<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[Google Developer Experts - Medium]]></title>
        <description><![CDATA[Experts on various Google products talking tech. - Medium]]></description>
        <link>https://medium.com/google-developer-experts?source=rss----a67bd6fa7d58---4</link>
        <image>
            <url>https://cdn-images-1.medium.com/proxy/1*TGH72Nnw24QL3iV9IOm4VA.png</url>
            <title>Google Developer Experts - Medium</title>
            <link>https://medium.com/google-developer-experts?source=rss----a67bd6fa7d58---4</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Wed, 24 Jun 2026 04:31:30 GMT</lastBuildDate>
        <atom:link href="https://medium.com/feed/google-developer-experts" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com" rel="hub"/>
        <item>
            <title><![CDATA[Google Antigravity 2.0 + Gemini 3.5 Flash: The AI That Codes, Tests, and Ships — Without You.]]></title>
            <link>https://medium.com/google-developer-experts/google-antigravity-2-0-gemini-3-5-flash-the-ai-that-codes-tests-and-ships-without-you-24dc2dc2ba47?source=rss----a67bd6fa7d58---4</link>
            <guid isPermaLink="false">https://medium.com/p/24dc2dc2ba47</guid>
            <category><![CDATA[agentic-ai]]></category>
            <category><![CDATA[google-ai]]></category>
            <category><![CDATA[ai]]></category>
            <category><![CDATA[vibe-coding]]></category>
            <category><![CDATA[google]]></category>
            <dc:creator><![CDATA[Geeta Kakrani]]></dc:creator>
            <pubDate>Tue, 16 Jun 2026 21:33:56 GMT</pubDate>
            <atom:updated>2026-06-16T21:33:55.093Z</atom:updated>
            <content:encoded><![CDATA[<h3>Antigravity 2.0, Antigravity CLI, and Gemini 3.5 Flash Explained Simply</h3><p><em>By Geeta Kakrani | Google Developer Expert in AI &amp; TPU</em></p><p>If you’ve been following Google I/O 2026, you already know something big happened. Google didn’t just release a new model or update an app. They released an entirely new way to think about writing software.</p><p>This post is my hands-on walkthrough of three things that shipped at I/O: <strong>Google Antigravity 2.0</strong>, the <strong>Antigravity CLI</strong>, and the <strong>Gemini 3.5 Flash model</strong> that powers all of it. I’ll explain each one simply, show you exactly how to get started, and tell you what I personally saw when I tried it.</p><p>No hype. No jargon. Just the real thing.</p><h3>First, a Quick Backstory</h3><p>Google launched the original Antigravity in November 2025 as a direct competitor to Cursor — an AI-powered code editor where you describe what you want, and AI writes it for you.</p><p>Version 2.0, announced at Google I/O on May 19, 2026, is a completely different product. It’s no longer just an editor. It’s a full platform with five parts:</p><ul><li>A desktop app</li><li>A CLI (command-line tool)</li><li>An SDK (for building your own agents)</li><li>Managed Agents in the Gemini API</li><li>An enterprise deployment path</li></ul><p>This blog covers the first two — the ones most developers will touch first.</p><h3>Part 1: What Is Antigravity 2.0?</h3><p>Think of it this way.</p><p>A normal code editor (like VS Code) is like having a very smart autocomplete. You still drive. You still type. AI just suggests the next word.</p><p>Antigravity 2.0 is completely different. <strong>You describe what you want to build. Antigravity figures out the steps, writes the code, runs it, tests it, and reports back.</strong></p><p>You are no longer the one typing. You are the one deciding <em>what</em> to build.</p><h3>What’s actually new in 2.0?</h3><p><strong>Multiple agents at the same time.</strong> The old version ran one task at a time. Antigravity 2.0 lets you spin up multiple AI agents in parallel. One agent can handle the backend, another the frontend, and a third can run tests — all at once. (Google internally used 93 agents to build a working OS in 12 hours. Yes, really.)</p><p><strong>Background tasks.</strong> You can schedule tasks to run while you sleep. Log off, come back in the morning, and your code is done.</p><p><strong>Native integration with Google tools.</strong> It connects directly with Google AI Studio, Firebase, and Android Studio. You can export a project from AI Studio and continue it locally in Antigravity without losing any context.</p><p><strong>Voice commands.</strong> You can now speak your instructions instead of typing them.</p><p><strong>Browser subagent.</strong> This one is genuinely impressive. Antigravity can open a real Chrome window, navigate to your running app, click buttons, fill forms, take screenshots of what it sees, and loop back to fix what’s broken. It’s not simulated. It’s real browser testing.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*5fUaXafTD9YgaWwMiERmTA.png" /></figure><h3>How to Download Antigravity 2.0</h3><p>Go to <strong>antigravity.google/download</strong></p><p>You’ll see download options for:</p><ul><li><strong>macOS</strong> — Apple Silicon or Intel</li><li><strong>Windows</strong> — x64 or ARM64</li><li><strong>Linux</strong> — x64 or ARM64</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*BUcLuL15Mm1RxqGPawbFcQ.png" /></figure><p>Minimum requirements:</p><ul><li>macOS: Version 12 (Monterey) or newer. Apple Silicon recommended.</li><li>Windows: Windows 10 (64-bit) or newer</li><li>Linux: glibc &gt;= 2.28 (Ubuntu 20, Debian 10, Fedora 36, RHEL 8 all work)</li></ul><p>It’s free to download and free to use during the public preview.</p><h3>Setting Up Your First Project</h3><p>Once installed, you’ll see a sidebar with:</p><ul><li><strong>New Conversation</strong> — start a fresh task</li><li><strong>Projects</strong> — your saved workspaces</li><li><strong>Conversation History</strong> — past sessions</li><li><strong>Scheduled Tasks</strong> — background automations</li></ul><p>Create a new project, give it a folder path, and you’re ready. The agent dropdown at the bottom lets you choose your model — by default it uses <strong>Gemini 3.5 Flash (Medium)</strong>, which we’ll cover in Part 3.</p><h3>Project Settings Worth Knowing</h3><p>When you open your project settings, you’ll see three important controls:</p><p><strong>Security Preset</strong> — Controls how much the agent can do on its own. Set to “Custom” if you want fine-grained control.</p><p><strong>Terminal Command Auto Execution</strong> — This decides whether the agent can run terminal commands automatically or whether it asks for your approval first. I recommend keeping this on “Require Review” when you’re starting out. You want to know what’s being run on your machine.</p><p><strong>Outside of Folders File Access Policy</strong> — Controls whether the agent can read files outside your project folder. “Always Ask” is the safe choice here.</p><p><strong>Enable Sandbox Mode</strong> — Restricts the agent to a secure, isolated environment. Good for testing untrusted code.</p><p>The important lesson: Antigravity can do a lot automatically. Set your security settings deliberately before you start.</p><h3>Part 2: What Is the Antigravity CLI?</h3><p>CLI stands for Command Line Interface. It’s for developers who prefer working in a terminal rather than a graphical app.</p><p>The Antigravity CLI lets you do everything the desktop app does — but from your terminal. You describe a task, and the agent runs it right there in your codebase.</p><p><strong>One important thing to know:</strong> Google is retiring the old Gemini CLI on <strong>June 18, 2026</strong>. If you’ve been using Gemini CLI, you need to switch to Antigravity CLI before that date. After June 18, Gemini CLI stops working.</p><h3>How to Install the Antigravity CLI</h3><p><strong>Mac or Linux:</strong> Open your terminal and run:</p><pre>curl -fsSL https://antigravity.google/cli/install.sh | bash</pre><p><strong>Windows PowerShell:</strong></p><pre>irm https://antigravity.google/cli/install.ps1 | iex</pre><p><strong>Windows Command Prompt (CMD):</strong></p><pre>curl -fsSL https://antigravity.google/cli/install.cmd -o install.cmd &amp;&amp; install.cmd &amp;&amp; del install.cmd</pre><p>That’s it. One command. The CLI installs itself.</p><p>After installation, you can use the CLI in any project folder. Type your task in plain English. The agent reads your files, figures out what needs to change, and makes it happen.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*NYQC5slJc-5me9emHFKiHg.png" /></figure><h3>What Can You Do with the CLI?</h3><ul><li>Build new features by describing them</li><li>Debug existing code by explaining the error</li><li>Write tests for your functions</li><li>Refactor messy code</li><li>Deploy to Google Cloud Run (if you add the Cloud Run MCP integration)</li></ul><p>The CLI is ideal if you’re already comfortable in a terminal and don’t want to switch to a new desktop app. You keep your existing editor (VS Code, Neovim, whatever you use) and get AI agents working alongside it.</p><h3>Part 3: Gemini 3.5 Flash — The Model Behind Everything</h3><p>Every capability in Antigravity 2.0 runs on <strong>Gemini 3.5 Flash</strong>, released on May 19, 2026 at Google I/O.</p><p>Here’s the surprising thing about this model: it’s called “Flash” (which usually means the smaller, cheaper version), but it <strong>outperforms Google’s previous best model, Gemini 3.1 Pro</strong>, on coding and agentic tasks. That’s the first time in Gemini’s history that a Flash model has beaten the Pro tier.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*yzib2fHJt4bq2kBEgHj3-w.png" /></figure><h3>What Makes Gemini 3.5 Flash Different?</h3><p><strong>Speed.</strong> It generates output 4x faster than comparable frontier models — roughly 280 tokens per second. Tasks that used to take minutes now take seconds.</p><p><strong>1 million token context window.</strong> That means you can feed it an entire large codebase and it can understand and reason across all of it at once.</p><p><strong>Multimodal.</strong> It understands text, images, video, and audio. This is why Antigravity’s browser agent can take a screenshot and understand what it sees.</p><p><strong>Built for agents.</strong> The model is specifically optimized for long, multi-step tasks that run without constant human input. It’s not a chatbot. It’s a worker.</p><p><strong>MCP support.</strong> Gemini 3.5 Flash natively supports the Model Context Protocol (MCP) standard, which means tools built for other AI systems (like Claude) can often work with it directly.</p><h3>How Was It Built?</h3><p>Google says Gemini 3.5 Flash was partly co-developed using Antigravity itself. The tool helped build the model that now powers the tool. That’s a meaningful signal about how mature the platform has become.</p><h3>Pricing</h3><p>If you’re using the Gemini API directly:</p><ul><li>Input: $1.50 per million tokens</li><li>Output: $9.00 per million tokens</li></ul><p>It costs more than the older Gemini 3.1 Flash models, but it’s still significantly cheaper than comparable frontier models from OpenAI and Anthropic, while being faster and (on agentic tasks) more capable.</p><p>For Antigravity desktop app users:</p><ul><li><strong>Free</strong> during public preview</li><li><strong>AI Pro</strong> — $20/month</li><li><strong>AI Ultra</strong> — $100/month (5x higher usage limits than Pro, new at I/O 2026)</li></ul><h3>Part 4: Customizations and MCP Servers</h3><p>One thing that impressed me when I explored the settings was the <strong>MCP server marketplace</strong> inside Antigravity.</p><p>MCP (Model Context Protocol) lets you connect Antigravity to external tools and services. Inside Settings &gt; Customizations &gt; Add MCP Servers, you’ll find options like:</p><ul><li><strong>Cloud Run</strong> — Deploy your app to Google Cloud with one command (already installed in my setup)</li><li><strong>Google Kubernetes Engine (OSS)</strong> — Let Antigravity interact with your GKE clusters</li><li><strong>Firebase</strong> — Connect your project to Firebase directly</li><li><strong>PostHog</strong> — Run product analytics queries in plain English</li><li><strong>GitLab Orbit</strong> — Query your GitLab codebase as a knowledge graph</li><li><strong>Dart / Flutter</strong> — Flutter-specific agent tools</li></ul><p>This is where Antigravity becomes genuinely powerful for production work. Instead of jumping between tools, the agent can orchestrate across all of them in one conversation.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*xiyfAX-L4rXZ1a16y9pi0g.png" /></figure><h3>Part 5: What About the Gemini API Key Setup?</h3><p>If you want to use Gemini 3.5 Flash through the API directly (outside of Antigravity), you’ll need an API key.</p><p>Here’s the simple version:</p><ol><li>Go to <strong>Google AI Studio</strong> (aistudio.google.com)</li><li>Click “Get API Key” → “Create API Key”</li><li>Give it a name (e.g. “Generative Language API Key”)</li><li>Under “Select API restrictions,” filter by “generative” and check <strong>Generative Language API</strong></li><li>Click OK</li></ol><p>That’s the API key you’ll use in your code. Keep it private. Don’t commit it to GitHub.</p><h3>My Honest Take</h3><p>I’ve been in tech for over two decades. I’ve seen a lot of “this changes everything” announcements that didn’t.</p><p>This one is different.</p><p>Antigravity 2.0 is not a better autocomplete. It’s a different paradigm. You describe. The agent builds. You review and steer. That shift — from typing code to directing agents — is real and it works.</p><p>The browser subagent alone is something I haven’t seen done this cleanly anywhere else. The model knows what the app looks like, not just what the code says.</p><p>That said: <strong>you still need to know architecture.</strong> Antigravity removes the friction of typing. It doesn’t remove the need to think. You need to give it clear goals, sensible project structure, and review what it produces.</p><p>Used well, it makes you faster. Used lazily, it makes a mess faster.</p><p>Start small. Try it on a real project. Set your security settings carefully. And watch what 93 agents can do for you.</p><h3>Quick Reference</h3><p>WhatWhereDownload Antigravity 2.0antigravity.google/downloadAntigravity CLI (Mac/Linux)curl -fsSL https://antigravity.google/cli/install.sh | bashGemini APIaistudio.google.comGemini CLI deadlineJune 18, 2026 — switch to Antigravity CLI before thenModel powering AntigravityGemini 3.5 FlashContext window1 million tokensAPI pricing$1.50 input / $9.00 output per million tokens</p><p><em>Geeta Kakrani is an AI Consultant and Google Developer Expert (GDE) in AI/ML and TPU with 22+ years in tech. You can find her at </em><a href="https://www.linkedin.com/in/geetakakrani/">https://www.linkedin.com/in/geetakakrani/</a> <em>and on YouTube </em><a href="https://youtube.com/@geetakakrani"><em>@geetakakrani</em></a><em>.</em></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=24dc2dc2ba47" width="1" height="1" alt=""><hr><p><a href="https://medium.com/google-developer-experts/google-antigravity-2-0-gemini-3-5-flash-the-ai-that-codes-tests-and-ships-without-you-24dc2dc2ba47">Google Antigravity 2.0 + Gemini 3.5 Flash: The AI That Codes, Tests, and Ships — Without You.</a> was originally published in <a href="https://medium.com/google-developer-experts">Google Developer Experts</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Building a Multimodal Indonesian Fake-News Detector with JAX, Flax, and Keras Kinetic on Cloud TPU]]></title>
            <link>https://medium.com/google-developer-experts/building-a-multimodal-indonesian-fake-news-detector-with-jax-flax-and-keras-kinetic-on-cloud-tpu-4b7f96c58b24?source=rss----a67bd6fa7d58---4</link>
            <guid isPermaLink="false">https://medium.com/p/4b7f96c58b24</guid>
            <category><![CDATA[llm]]></category>
            <category><![CDATA[kinetic-google]]></category>
            <category><![CDATA[google-cloud-platform]]></category>
            <category><![CDATA[tpu]]></category>
            <dc:creator><![CDATA[Esther Irawati Setiawan]]></dc:creator>
            <pubDate>Wed, 03 Jun 2026 14:11:58 GMT</pubDate>
            <atom:updated>2026-05-27T03:42:07.060Z</atom:updated>
            <content:encoded><![CDATA[<p><em>How I trained a Stance-Aware Cross-Encoder that classifies Indonesian news headlines against claims — starting on a free Colab TPU and scaling out to Cloud TPU v5p with a single</em><em>@kinetic.run() decorator</em></p><h3>Introduction</h3><p>Misinformation is one of the defining problems of the social-media era, and Indonesia has been hit particularly hard. Hoaks (the Indonesian shorthand for fake news) spread through WhatsApp groups and Twitter threads faster than any fact-checker can keep up. Most published research on automated fake-news detection focuses on English-language data, which leaves practitioners working with Bahasa Indonesia in a frustrating spot: the techniques exist, but the tooling and pre-trained models are scarce.</p><p>This article walks through building a real, working <strong>multimodal hoax detector</strong> for Indonesian news from scratch. The model takes two inputs — a <em>claim</em> (the original assertion, often from social media) and a <em>headline</em> (a news article headline that mentions the same topic) — and predicts whether the article <em>supports</em> the claim (for), <em>refutes</em> it (against), or merely <em>observes</em> it neutrally (observing).</p><p>The architecture is a <strong>Stance-Aware Cross-Encoder</strong>: a BiLSTM-style encoder for each input, multi-head self-attention, and a cross-attention layer that lets the claim and headline literally read each other before classification. Built end-to-end with <strong>JAX</strong> and <strong>Flax</strong>, trained on <strong>TPU</strong>.</p><p>The deployment story has two halves:</p><ol><li><strong>Free Colab TPU</strong> for prototyping — Google gives every Colab user free access to a v5e-1 TPU, which is enough to train this model end-to-end in under an hour at zero cost.</li><li><strong>Cloud TPU v5p via Keras Kinetic</strong> for serious training — when you outgrow Colab’s runtime limits, <a href="https://github.com/keras-team/kinetic">Keras Kinetic</a> lets you ship the same training function to a Cloud TPU pod with a single Python decorator. No Docker, no Kubernetes YAML, no SSH.</li></ol><p>By the end, you’ll have:</p><ul><li>A reusable Indonesian tokenizer and dataset loader</li><li>A 4-layer Transformer encoder with stance-aware cross-attention, written in Flax</li><li>A JIT-compiled training loop with optax and orbaxcheckpointing</li><li>A working predict.py that runs new claim-headline pairs through the trained model</li><li>The exact same code, deployed to Cloud TPU via@kinetic.run()</li></ul><p>Let’s get into it.</p><h3>Why This Problem Is Hard (and Interesting)</h3><p>Naive fake-news detectors look at one piece of text and try to classify it as “real” or “fake.” That’s both technically weak and ethically uncomfortable — a single text rarely carries enough signal, and the “true/false” framing assumes the model has access to ground truth it can’t possibly have.</p><p>The <strong>stance detection</strong> framing is much more honest. Given a claim and a related news article, the model doesn’t decide whether the claim is <em>true</em>; it decides whether <em>this particular article supports, refutes, or merely observes the claim</em>. That’s a question a model can actually answer, and it’s exactly the input a downstream fact-checker needs to make a final call.</p><p>Mathematically, the task is a 3-way classification over the<em>interaction</em> of two pieces of text. That word — interaction — is what makes the architecture interesting. You can’t just encode each side independently and concatenate. You need a layer that lets the claim attend to the headline and vice versa, so the model can pick up on subtle cues like negation (“Government denies…”), hedging (“alleged…”), or framing (“according to critics…”).</p><h3>Why JAX, Flax, Keras Kinetic, and TPU?</h3><ul><li><strong>JAX</strong> gives me NumPy-style code with automatic differentiation, JIT compilation via XLA, and transparent acceleration on CPU/GPU/TPU.</li><li><strong>Flax</strong> sits on top of JAX and lets me write neural networks as nn.Module classes. The model is dense in attention layers, and Flax keeps the parameter management clean.</li><li><strong>Optax</strong> for optimization (AdamW with linear warmup + cosine decay) and <strong>Orbax</strong> for checkpointing — both are part of the JAX ecosystem and JIT-friendly.</li><li><strong>Keras Kinetic</strong> is the deployment glue. One decorator turns a local Python function into a remote TPU job, with container caching, log streaming, and automatic GKE provisioning.</li><li><strong>TPU</strong> because the workload is dominated by attention matmuls — exactly what TPU systolic arrays are built for. Free on Colab (v5e-1), and Cloud TPU v5p when you need to scale up.</li></ul><h3>TPU vs GPU for This Workload</h3><p>A multimodal Transformer with cross-attention is one of the cleanest TPU workloads you can write. Here’s why, and where GPUs still hold their own.</p><p><strong>Hardware design</strong></p><ul><li><em>GPU (NVIDIA A100/H100):</em> general-purpose parallel processor, thousands of CUDA cores, great for arbitrary parallel computation.</li><li><em>TPU (v5e or v5p):</em> domain-specific accelerator built around a large systolic array (MXU) optimized for dense matrix multiplications.</li></ul><p><strong>What dominates the compute in this model</strong></p><ul><li>Multi-head self-attention: softmax(Q Kᵀ / √d) V — three big matmuls per head per layer.</li><li>Cross-attention between claim and headline: same shape, just with different inputs feeding Q vs K/V.</li><li>Feedforward blocks: two Dense layers with GELU between them.</li></ul><p>All of those are dense matmuls with predictable shapes. The TPU systolic array is purpose-built to chew through exactly this pattern at peak FLOPs. The XLA compiler fuses the entire train_step into a few kernels, and after the first compile, every step runs at full throughput.</p><p><strong>Where GPUs still win for stance detection / NLP</strong></p><ul><li>You’re doing token-level decoding with KV-cache and irregular generation lengths (we’re not — we’re doing classification).</li><li>You need a HuggingFace transformers model that’s only available as a PyTorch checkpoint (we’re training from scratch, so this doesn’t apply).</li><li>You want to iterate in a notebook with constant Python control flow that doesn’t JIT cleanly (Colab gives you both a TPU <em>and</em>a notebook, so you don’t have to choose).</li></ul><p><strong>Where TPUs win for stance detection / NLP</strong></p><ul><li>Fixed-length sequences (we pad to 64 tokens) → predictable shapes → great XLA compilation.</li><li>The whole train_step JITs into a single fused execution graph.</li><li>pmap / shard_map make multi-chip training a one-liner if you want to scale up.</li><li>Free on Colab, and Cloud TPU v5e is roughly $0.40/chip-hour on Spot.</li></ul><p><strong>Rule of thumb</strong></p><ul><li><strong>Quick prototyping in a notebook with Bahasa Indonesia data</strong> → free Colab TPU. (This article.)</li><li><strong>Iterative R&amp;D using HuggingFace PyTorch checkpoints</strong> → GPU.</li><li><strong>Production training with batchable, JAX-native workloads</strong> → Cloud TPU via Kinetic.</li><li><strong>You need to fine-tune a 7B+ Indonesian LLM</strong> → that’s a different article (and a different category — vLLM or Tunix).</li></ul><p>Now let’s actually build it.</p><h3>Project Architecture</h3><p>The pipeline is straightforward:</p><pre>datasetika.csv (Claim, Judul, Stance)<br>            │<br>            ▼<br>   ┌──────────────────┐<br>   │ IndonesianTokenizer │ whitespace + punctuation, vocab from corpus<br>   └──────────────────┘<br>            │<br>            ▼<br>   ┌──────────────────┐<br>   │  FakeNewsDataset │ stratified train/val/test split, JAX-ready arrays<br>   └──────────────────┘<br>            │<br>            ▼<br>   ┌──────────────────────────────────────┐<br>   │  FakeNewsDetector (Flax nn.Module)   │<br>   │  ┌──────────────┐  ┌──────────────┐  │<br>   │  │ Token+Pos    │  │ Token+Pos    │  │<br>   │  │ Embedding    │  │ Embedding    │  │<br>   │  │ (Claim)      │  │ (Headline)   │  │<br>   │  └──────┬───────┘  └──────┬───────┘  │<br>   │         ▼                 ▼          │<br>   │  ┌──────────────┐  ┌──────────────┐  │<br>   │  │ Transformer  │  │ Transformer  │  │<br>   │  │ × N layers   │  │ × N layers   │  │<br>   │  └──────┬───────┘  └──────┬───────┘  │<br>   │         └────────┬────────┘          │<br>   │                  ▼                    │<br>   │      ┌─────────────────────┐         │<br>   │      │ Stance Cross-Encoder│         │<br>   │      │ (cross-attention +  │         │<br>   │      │  diff &amp; product)    │         │<br>   │      └──────────┬──────────┘         │<br>   │                 ▼                    │<br>   │       Dense → Softmax (3 classes)    │<br>   └──────────────────────────────────────┘<br>            │<br>            ▼<br>   for / against / observing</pre><p>The whole thing sits inside one jax.jit-compiled train_step. Now let’s walk through each piece.</p><h3>Step 1 — Hardware Setup</h3><p>There are two paths. Pick whichever fits your stage of the project.</p><h3>Path A: Free Colab TPU (recommended for first run)</h3><ol><li>Open <a href="https://colab.research.google.com/">colab.research.google.com</a> and create a new notebook.</li><li>Click <strong>Runtime → Change runtime type</strong>.</li><li>Under <strong>Hardware accelerator</strong>, select <strong>v5e-1 TPU</strong>.</li><li>Click <strong>Save</strong>.</li><li>Verify in a cell:</li></ol><pre>import jax<br>print(jax.devices())<br># Expected: [TpuDevice(id=0, ...)]</pre><p>That’s it. You now have a free TPU v5e chip for the duration of your Colab session.</p><h3>Path B: Cloud TPU via Keras Kinetic (when you outgrow Colab)</h3><p>Colab is fantastic for prototyping but has runtime limits and gets bumped under load. When you’re ready to run multi-hour training jobs, switch to a real Cloud TPU. The traditional path means provisioning a TPU VM, SSHing in, installing dependencies, and uploading scripts — Kinetic skips all of that.</p><p>On your local laptop:</p><pre>pip install keras-kinetic<br>gcloud auth application-default login<br>gcloud config set project YOUR_PROJECT_ID<br>kinetic up --accelerator v5p-8 --yes</pre><p>The last command provisions a GKE Autopilot cluster with a TPU v5p-8 node pool. Takes a few minutes the first time, after which you don’t touch infrastructure again until tear-down.</p><p>I’ll show the actual @kinetic.run() deployment in Step 6. For now, let’s build the model.</p><h3>Step 2 — Indonesian Tokenizer and Dataset</h3><p>Bahasa Indonesia is morphologically less complex than, say, Turkish or Finnish, so a whitespace + punctuation tokenizer with a learned vocabulary works surprisingly well as a baseline. (For production, swap in IndoBERT — I’ll show how at the end of this section.)</p><p>The tokenizer reserves four special tokens, builds a frequency-ranked vocabulary from the training corpus, and emits(token_ids, attention_mask) pairs at a fixed length. Standard stuff, but with one subtlety: we tokenize <em>both</em> the Claim and Judul (headline) columns into a <em>shared</em> vocabulary so the embedding layer can pick up cross-input correlations.</p><pre>&quot;&quot;&quot;<br>Data Preprocessing for Indonesian Fake News Detection<br>Tokenizes Claim + Judul columns, encodes Stance labels.<br>Compatible with JAX/Flax training pipeline.<br>&quot;&quot;&quot;<br>import re<br>import numpy as np<br>import pandas as pd<br>from collections import Counter<br>from typing import List, Tuple, Dict<br>from sklearn.model_selection import train_test_split</pre><pre>LABEL_MAP   = {&quot;for&quot;: 0, &quot;against&quot;: 1, &quot;observing&quot;: 2}<br>ID_TO_LABEL = {v: k for k, v in LABEL_MAP.items()}<br></pre><pre>class IndonesianTokenizer:<br>    &quot;&quot;&quot;<br>    Lightweight whitespace + punctuation tokenizer for Indonesian text.</pre><pre>    For production, swap with:<br>        from transformers import AutoTokenizer<br>        tok = AutoTokenizer.from_pretrained(&quot;indobenchmark/indobert-base-p1&quot;)<br>    &quot;&quot;&quot;<br>    SPECIAL_TOKENS = {&quot;&lt;PAD&gt;&quot;: 0, &quot;&lt;UNK&gt;&quot;: 1, &quot;&lt;CLS&gt;&quot;: 2, &quot;&lt;SEP&gt;&quot;: 3}</pre><pre>    def __init__(self, vocab_size: int = 30_000, min_freq: int = 2):<br>        self.vocab_size = vocab_size<br>        self.min_freq   = min_freq<br>        self.word2id: Dict[str, int] = dict(self.SPECIAL_TOKENS)<br>        self.id2word: Dict[int, str] = {v: k for k, v in self.word2id.items()}</pre><pre>    @staticmethod<br>    def _clean(text: str) -&gt; str:<br>        text = text.lower()<br>        text = re.sub(r&quot;&lt;[^&gt;]+&gt;&quot;, &quot; &quot;, text)                   # strip HTML<br>        text = re.sub(r&quot;[^\w\s]&quot;, &quot; &quot;, text, flags=re.UNICODE) # keep alphanum<br>        text = re.sub(r&quot;\s+&quot;, &quot; &quot;, text).strip()<br>        return text</pre><pre>    @staticmethod<br>    def tokenize(text: str) -&gt; List[str]:<br>        return IndonesianTokenizer._clean(text).split()</pre><pre>    def build_vocab(self, texts: List[str]) -&gt; None:<br>        counter: Counter = Counter()<br>        for t in texts:<br>            counter.update(self.tokenize(t))<br>        top_tokens = [<br>            word for word, freq in counter.most_common()<br>            if freq &gt;= self.min_freq<br>        ][: self.vocab_size - len(self.SPECIAL_TOKENS)]<br>        for idx, word in enumerate(top_tokens, start=len(self.SPECIAL_TOKENS)):<br>            self.word2id[word] = idx<br>            self.id2word[idx]  = word<br>        print(f&quot;[Tokenizer] Vocabulary size: {len(self.word2id):,} tokens&quot;)</pre><pre>    def encode(self, text: str, max_len: int = 128, add_special: bool = True<br>              ) -&gt; Tuple[np.ndarray, np.ndarray]:<br>        tokens = self.tokenize(text)<br>        ids    = [self.word2id.get(t, self.SPECIAL_TOKENS[&quot;&lt;UNK&gt;&quot;]) for t in tokens]<br>        if add_special:<br>            ids = [self.SPECIAL_TOKENS[&quot;&lt;CLS&gt;&quot;]] + ids + [self.SPECIAL_TOKENS[&quot;&lt;SEP&gt;&quot;]]<br>        ids = ids[:max_len]<br>        pad_len = max_len - len(ids)<br>        mask    = [1] * len(ids) + [0] * pad_len<br>        ids     = ids + [self.SPECIAL_TOKENS[&quot;&lt;PAD&gt;&quot;]] * pad_len<br>        return np.array(ids, dtype=np.int32), np.array(mask, dtype=np.int32)</pre><pre>    def encode_batch(self, texts: List[str], max_len: int = 128<br>                    ) -&gt; Tuple[np.ndarray, np.ndarray]:<br>        pairs    = [self.encode(t, max_len) for t in texts]<br>        ids_arr  = np.stack([p[0] for p in pairs])<br>        mask_arr = np.stack([p[1] for p in pairs])<br>        return ids_arr, mask_arr</pre><p>The dataset class loads the CSV, builds the tokenizer on the combined Claim + Judul corpus, encodes both columns into fixed-length arrays, and produces stratified train/val/test splits:</p><pre>class FakeNewsDataset:<br>    &quot;&quot;&quot;<br>    Loads datasetika.csv and prepares JAX-compatible NumPy arrays.</pre><pre>    Expected columns:<br>        id, Claim, idStance, Judul, Stance<br>        Stance ∈ {for, against, observing}<br>    &quot;&quot;&quot;<br>    def __init__(<br>        self,<br>        csv_path: str,<br>        claim_max_len:    int = 64,<br>        headline_max_len: int = 64,<br>        vocab_size:       int = 30_000,<br>        test_size:        float = 0.15,<br>        val_size:         float = 0.10,<br>        random_seed:      int = 42,<br>    ):<br>        self.claim_max_len    = claim_max_len<br>        self.headline_max_len = headline_max_len</pre><pre>        df = pd.read_csv(csv_path)<br>        print(f&quot;[Dataset] Loaded {len(df):,} samples&quot;)<br>        print(f&quot;[Dataset] Stance distribution:\n{df[&#39;Stance&#39;].value_counts()}\n&quot;)</pre><pre>        self.tokenizer = IndonesianTokenizer(vocab_size)<br>        all_texts = df[&quot;Claim&quot;].tolist() + df[&quot;Judul&quot;].tolist()<br>        self.tokenizer.build_vocab(all_texts)</pre><pre>        claim_ids,    claim_masks    = self.tokenizer.encode_batch(<br>            df[&quot;Claim&quot;].tolist(),  claim_max_len)<br>        headline_ids, headline_masks = self.tokenizer.encode_batch(<br>            df[&quot;Judul&quot;].tolist(),  headline_max_len)<br>        labels = np.array([LABEL_MAP[s] for s in df[&quot;Stance&quot;]], dtype=np.int32)</pre><pre>        # Stratified train / val / test<br>        indices = np.arange(len(df))<br>        train_idx, test_idx = train_test_split(<br>            indices, test_size=test_size, stratify=labels, random_state=random_seed)<br>        train_idx, val_idx = train_test_split(<br>            train_idx,<br>            test_size=val_size / (1 - test_size),<br>            stratify=labels[train_idx],<br>            random_state=random_seed,<br>        )</pre><pre>        self.train = self._slice(claim_ids, claim_masks, headline_ids,<br>                                 headline_masks, labels, train_idx)<br>        self.val   = self._slice(claim_ids, claim_masks, headline_ids,<br>                                 headline_masks, labels, val_idx)<br>        self.test  = self._slice(claim_ids, claim_masks, headline_ids,<br>                                 headline_masks, labels, test_idx)</pre><pre>    @staticmethod<br>    def _slice(claim_ids, claim_masks, head_ids, head_masks, labels, idx):<br>        return {<br>            &quot;claim_ids&quot;:     claim_ids[idx],<br>            &quot;claim_masks&quot;:   claim_masks[idx],<br>            &quot;headline_ids&quot;:  head_ids[idx],<br>            &quot;headline_masks&quot;: head_masks[idx],<br>            &quot;labels&quot;:        labels[idx],<br>        }</pre><p><strong>Production swap:</strong> if you have GPU/TPU memory to spare, replace IndonesianTokenizer withAutoTokenizer.from_pretrained(&quot;indobenchmark/indobert-base-p1&quot;) and feed those token IDs into the same downstream model. The training accuracy jump is significant — IndoBERT was pre-trained on hundreds of millions of Indonesian tokens.</p><h3>Step 3 — The Multimodal Architecture (JAX + Flax)</h3><p>This is where it gets fun. The model has four logical pieces stacked in a Flax nn.Module:</p><ol><li><strong>Token + positional embedding</strong> for both inputs</li><li><strong>Transformer encoder stack</strong> (multi-head self-attention + feedforward) for each input independently</li><li><strong>Stance-aware cross-encoder</strong> that lets claim and headline attend to each other</li><li><strong>Classification head</strong> that produces logits over {for, against, observing}</li></ol><h3>3.1 — Token and Positional Embedding</h3><pre>import jax<br>import jax.numpy as jnp<br>import flax.linen as nn<br>from typing import Tuple<br></pre><pre>class TokenEmbedding(nn.Module):<br>    &quot;&quot;&quot;Learnable token + positional embeddings.&quot;&quot;&quot;<br>    vocab_size: int<br>    embed_dim: int<br>    max_len: int = 256<br>    dropout_rate: float = 0.1</pre><pre>    @nn.compact<br>    def __call__(self, token_ids: jnp.ndarray, train: bool = False) -&gt; jnp.ndarray:<br>        tok_emb = nn.Embed(self.vocab_size, self.embed_dim)(token_ids)  # (B, L, D)<br>        pos = jnp.arange(token_ids.shape[1])[None, :]                   # (1, L)<br>        pos_emb = nn.Embed(self.max_len, self.embed_dim)(pos)           # (1, L, D)<br>        x = tok_emb + pos_emb<br>        x = nn.LayerNorm()(x)<br>        x = nn.Dropout(self.dropout_rate, deterministic=not train)(x)<br>        return x</pre><h3>3.2 — Multi-Head Attention</h3><p>A clean, from-scratch multi-head attention. Yes, Flax hasnn.MultiHeadDotProductAttention built in, but writing it explicitly keeps the article educational and lets you see exactly what the attention mask is doing:</p><pre>class MultiHeadAttention(nn.Module):<br>    num_heads: int<br>    head_dim: int<br>    dropout_rate: float = 0.1</pre><pre>    @nn.compact<br>    def __call__(self, q: jnp.ndarray, k: jnp.ndarray, v: jnp.ndarray,<br>                 mask: jnp.ndarray = None, train: bool = False) -&gt; jnp.ndarray:<br>        B, Lq, D = q.shape<br>        Lk = k.shape[1]<br>        H, Dh = self.num_heads, self.head_dim</pre><pre>        Q = nn.Dense(H * Dh)(q).reshape(B, Lq, H, Dh).transpose(0, 2, 1, 3)<br>        K = nn.Dense(H * Dh)(k).reshape(B, Lk, H, Dh).transpose(0, 2, 1, 3)<br>        V = nn.Dense(H * Dh)(v).reshape(B, Lk, H, Dh).transpose(0, 2, 1, 3)</pre><pre>        scores = jnp.einsum(&quot;bhqd,bhkd-&gt;bhqk&quot;, Q, K) / jnp.sqrt(Dh)<br>        if mask is not None:<br>            # mask: (B, Lk) -&gt; (B, 1, 1, Lk)<br>            mask = mask[:, None, None, :]<br>            scores = jnp.where(mask == 0, -1e9, scores)</pre><pre>        attn = jax.nn.softmax(scores, axis=-1)<br>        attn = nn.Dropout(self.dropout_rate, deterministic=not train)(attn)<br>        out  = jnp.einsum(&quot;bhqk,bhkd-&gt;bhqd&quot;, attn, V)<br>        out  = out.transpose(0, 2, 1, 3).reshape(B, Lq, H * Dh)<br>        out  = nn.Dense(D)(out)<br>        return out</pre><h3>3.3 — Transformer Block</h3><p>Standard pre-norm Transformer block with a feedforward expansion factor of 4:</p><pre>class TransformerBlock(nn.Module):<br>    num_heads: int<br>    head_dim: int<br>    ff_dim: int<br>    dropout_rate: float = 0.1</pre><pre>    @nn.compact<br>    def __call__(self, x: jnp.ndarray, mask: jnp.ndarray = None,<br>                 train: bool = False) -&gt; jnp.ndarray:<br>        # Self-attention sublayer<br>        h = nn.LayerNorm()(x)<br>        h = MultiHeadAttention(self.num_heads, self.head_dim, self.dropout_rate)(<br>            h, h, h, mask=mask, train=train)<br>        h = nn.Dropout(self.dropout_rate, deterministic=not train)(h)<br>        x = x + h</pre><pre>        # Feedforward sublayer<br>        h = nn.LayerNorm()(x)<br>        h = nn.Dense(self.ff_dim)(h)<br>        h = nn.gelu(h)<br>        h = nn.Dense(x.shape[-1])(h)<br>        h = nn.Dropout(self.dropout_rate, deterministic=not train)(h)<br>        x = x + h<br>        return x</pre><h3>3.4 — Stance Cross-Encoder (the Interesting Part)</h3><p>This is what makes the architecture <em>multimodal-aware</em> rather than just two encoders bolted together. After the claim and headline are independently encoded, the cross-encoder lets each one attend to the other, then extracts an interaction vector by concatenating the difference and element-wise product of the two pooled representations:</p><pre>class StanceCrossEncoder(nn.Module):<br>    &quot;&quot;&quot;Cross-attention between claim and headline + interaction features.&quot;&quot;&quot;<br>    num_heads: int<br>    head_dim: int<br>    dropout_rate: float = 0.1</pre><pre>    @nn.compact<br>    def __call__(self, claim: jnp.ndarray, headline: jnp.ndarray,<br>                 claim_mask: jnp.ndarray, headline_mask: jnp.ndarray,<br>                 train: bool = False) -&gt; jnp.ndarray:<br>        # Claim attends to headline<br>        claim_attended = MultiHeadAttention(<br>            self.num_heads, self.head_dim, self.dropout_rate<br>        )(claim, headline, headline, mask=headline_mask, train=train)</pre><pre>        # Headline attends to claim<br>        head_attended = MultiHeadAttention(<br>            self.num_heads, self.head_dim, self.dropout_rate<br>        )(headline, claim, claim, mask=claim_mask, train=train)</pre><pre>        # Mean-pool with mask<br>        def masked_mean(x, m):<br>            m = m[..., None].astype(x.dtype)<br>            return (x * m).sum(axis=1) / jnp.maximum(m.sum(axis=1), 1.0)</pre><pre>        c = masked_mean(claim_attended, claim_mask)     # (B, D)<br>        h = masked_mean(head_attended,  headline_mask)  # (B, D)</pre><pre>        # Stance-aware interaction features<br>        interaction = jnp.concatenate(<br>            [c, h, jnp.abs(c - h), c * h], axis=-1<br>        )  # (B, 4D)<br>        return interaction</pre><p>The four components — c, h, |c - h|, c * h — capture different aspects of how the two inputs relate. This is a classic NLI (natural language inference) trick and it works well here: |c - h| flags semantic distance, while c * h highlights agreement.</p><h3>3.5 — Putting It All Together</h3><pre>class FakeNewsDetector(nn.Module):<br>    vocab_size: int<br>    embed_dim: int = 256<br>    num_heads: int = 8<br>    head_dim: int = 32<br>    ff_dim: int = 1024<br>    num_layers: int = 4<br>    dropout_rate: float = 0.1<br>    num_classes: int = 3</pre><pre>    @nn.compact<br>    def __call__(self, claim_ids, claim_mask, headline_ids, headline_mask,<br>                 train: bool = False):<br>        # Embed both inputs<br>        claim_x    = TokenEmbedding(self.vocab_size, self.embed_dim,<br>                                    dropout_rate=self.dropout_rate)(claim_ids, train)<br>        headline_x = TokenEmbedding(self.vocab_size, self.embed_dim,<br>                                    dropout_rate=self.dropout_rate)(headline_ids, train)</pre><pre>        # Independent Transformer stacks<br>        for _ in range(self.num_layers):<br>            claim_x = TransformerBlock(self.num_heads, self.head_dim,<br>                                       self.ff_dim, self.dropout_rate)(<br>                claim_x, mask=claim_mask, train=train)<br>            headline_x = TransformerBlock(self.num_heads, self.head_dim,<br>                                          self.ff_dim, self.dropout_rate)(<br>                headline_x, mask=headline_mask, train=train)</pre><pre>        # Cross-encoder interaction<br>        interaction = StanceCrossEncoder(self.num_heads, self.head_dim,<br>                                         self.dropout_rate)(<br>            claim_x, headline_x, claim_mask, headline_mask, train=train)</pre><pre>        # Classification head<br>        h = nn.Dense(self.embed_dim)(interaction)<br>        h = nn.gelu(h)<br>        h = nn.Dropout(self.dropout_rate, deterministic=not train)(h)<br>        logits = nn.Dense(self.num_classes)(h)<br>        return logits</pre><p>A 4-layer encoder stack with embed_dim=256, num_heads=8, and ff_dim=1024 lands at around 7M parameters — small enough to train quickly on a free Colab TPU, large enough to learn meaningful stance representations.</p><h3>Step 4 — Training Pipeline (TPU-Optimized)</h3><p>The training loop is a standard Optax + JAX pattern, with two TPU-specific touches: everything is jax.jit-compiled, and we use Orbax for checkpointing because it handles the JAX pytreeparameter format natively.</p><h3>4.1 — Train State and Loss</h3><pre>import optax<br>from flax.training import train_state<br></pre><pre>def create_train_state(rng, model, learning_rate, weight_decay,<br>                       claim_len, head_len, num_warmup_steps, num_total_steps):<br>    # Dummy inputs to initialize parameters<br>    dummy_claim_ids   = jnp.ones((1, claim_len), dtype=jnp.int32)<br>    dummy_claim_mask  = jnp.ones((1, claim_len), dtype=jnp.int32)<br>    dummy_head_ids    = jnp.ones((1, head_len),  dtype=jnp.int32)<br>    dummy_head_mask   = jnp.ones((1, head_len),  dtype=jnp.int32)</pre><pre>    params = model.init(<br>        rng, dummy_claim_ids, dummy_claim_mask,<br>        dummy_head_ids, dummy_head_mask, train=False,<br>    )[&quot;params&quot;]</pre><pre>    # Linear warmup + cosine decay<br>    schedule = optax.warmup_cosine_decay_schedule(<br>        init_value=0.0, peak_value=learning_rate,<br>        warmup_steps=num_warmup_steps,<br>        decay_steps=num_total_steps - num_warmup_steps,<br>        end_value=learning_rate * 0.1,<br>    )<br>    optimizer = optax.adamw(schedule, weight_decay=weight_decay)</pre><pre>    return train_state.TrainState.create(<br>        apply_fn=model.apply, params=params, tx=optimizer)<br></pre><pre>def cross_entropy_loss(logits, labels):<br>    one_hot = jax.nn.one_hot(labels, num_classes=logits.shape[-1])<br>    return -jnp.mean(jnp.sum(one_hot * jax.nn.log_softmax(logits), axis=-1))</pre><h3>4.2 — JIT-Compiled Train and Eval Steps</h3><pre>@jax.jit<br>def train_step(state, batch, dropout_rng):<br>    def loss_fn(params):<br>        logits = state.apply_fn(<br>            {&quot;params&quot;: params},<br>            batch[&quot;claim_ids&quot;], batch[&quot;claim_masks&quot;],<br>            batch[&quot;headline_ids&quot;], batch[&quot;headline_masks&quot;],<br>            train=True,<br>            rngs={&quot;dropout&quot;: dropout_rng},<br>        )<br>        loss = cross_entropy_loss(logits, batch[&quot;labels&quot;])<br>        return loss, logits</pre><pre>    (loss, logits), grads = jax.value_and_grad(loss_fn, has_aux=True)(state.params)<br>    state = state.apply_gradients(grads=grads)<br>    accuracy = (jnp.argmax(logits, axis=-1) == batch[&quot;labels&quot;]).mean()<br>    return state, loss, accuracy<br></pre><pre>@jax.jit<br>def eval_step(state, batch):<br>    logits = state.apply_fn(<br>        {&quot;params&quot;: state.params},<br>        batch[&quot;claim_ids&quot;], batch[&quot;claim_masks&quot;],<br>        batch[&quot;headline_ids&quot;], batch[&quot;headline_masks&quot;],<br>        train=False,<br>    )<br>    loss = cross_entropy_loss(logits, batch[&quot;labels&quot;])<br>    accuracy = (jnp.argmax(logits, axis=-1) == batch[&quot;labels&quot;]).mean()<br>    return loss, accuracy</pre><p>The @jax.jit is doing a lot of work here. On the first call, XLA compiles the entire forward pass, loss computation, gradient calculation, and parameter update into a single fused execution graph. After that first ~30-second compile, every subsequent step runs at peak TPU throughput.</p><h3>4.3 — The Training Loop</h3><pre>import orbax.checkpoint as ocp<br>from pathlib import Path<br>import time<br></pre><pre>def train(args):<br>    # Load and prepare data<br>    ds = FakeNewsDataset(<br>        args.data_path,<br>        claim_max_len=args.claim_len,<br>        headline_max_len=args.head_len,<br>    )</pre><pre>    # Create model and train state<br>    model = FakeNewsDetector(<br>        vocab_size=len(ds.tokenizer.word2id),<br>        embed_dim=args.embed_dim,<br>        num_heads=args.num_heads,<br>    )<br>    rng = jax.random.PRNGKey(0)<br>    init_rng, train_rng = jax.random.split(rng)</pre><pre>    steps_per_epoch  = len(ds.train[&quot;labels&quot;]) // args.batch_size<br>    num_total_steps  = steps_per_epoch * args.epochs<br>    num_warmup_steps = int(0.1 * num_total_steps)</pre><pre>    state = create_train_state(<br>        init_rng, model, args.lr, weight_decay=0.01,<br>        claim_len=args.claim_len, head_len=args.head_len,<br>        num_warmup_steps=num_warmup_steps,<br>        num_total_steps=num_total_steps,<br>    )</pre><pre>    # Orbax checkpointer<br>    ckpt_dir = Path(args.output_dir).resolve()<br>    ckpt_dir.mkdir(parents=True, exist_ok=True)<br>    checkpointer = ocp.PyTreeCheckpointer()<br>    best_val_acc = 0.0</pre><pre>    for epoch in range(args.epochs):<br>        epoch_start = time.time()<br>        train_rng, shuffle_rng = jax.random.split(train_rng)</pre><pre>        # Shuffle training indices<br>        perm = jax.random.permutation(shuffle_rng, len(ds.train[&quot;labels&quot;]))</pre><pre>        # Training pass<br>        train_loss, train_acc = 0.0, 0.0<br>        for step in range(steps_per_epoch):<br>            batch_idx = perm[step * args.batch_size:(step + 1) * args.batch_size]<br>            batch = {k: v[batch_idx] for k, v in ds.train.items()}<br>            train_rng, dropout_rng = jax.random.split(train_rng)<br>            state, loss, acc = train_step(state, batch, dropout_rng)<br>            train_loss += float(loss)<br>            train_acc  += float(acc)<br>        train_loss /= steps_per_epoch<br>        train_acc  /= steps_per_epoch</pre><pre>        # Validation pass<br>        val_loss, val_acc = 0.0, 0.0<br>        val_steps = len(ds.val[&quot;labels&quot;]) // args.batch_size<br>        for step in range(val_steps):<br>            batch = {k: v[step * args.batch_size:(step + 1) * args.batch_size]<br>                     for k, v in ds.val.items()}<br>            loss, acc = eval_step(state, batch)<br>            val_loss += float(loss)<br>            val_acc  += float(acc)<br>        val_loss /= val_steps<br>        val_acc  /= val_steps</pre><pre>        elapsed = time.time() - epoch_start<br>        print(f&quot;Epoch {epoch+1:3d}/{args.epochs} | &quot;<br>              f&quot;train loss {train_loss:.4f} acc {train_acc:.4f} | &quot;<br>              f&quot;val loss {val_loss:.4f} acc {val_acc:.4f} | &quot;<br>              f&quot;{elapsed:.1f}s&quot;)</pre><pre>        # Save best checkpoint<br>        if val_acc &gt; best_val_acc:<br>            best_val_acc = val_acc<br>            checkpointer.save(ckpt_dir / &quot;best&quot;, state.params, force=True)<br>            print(f&quot;  ✓ Saved checkpoint (val_acc={val_acc:.4f})&quot;)</pre><h3>4.4 — Running on Free Colab TPU</h3><p>In a Colab cell:</p><pre>class Args:<br>    data_path  = &quot;datasetika.csv&quot;<br>    output_dir = &quot;checkpoints&quot;<br>    epochs     = 20<br>    batch_size = 64<br>    lr         = 2e-4<br>    claim_len  = 64<br>    head_len   = 64<br>    embed_dim  = 256<br>    num_heads  = 8</pre><pre>train(Args)</pre><p>Sample output on Colab v5e-1:</p><pre>[Dataset] Loaded 12,847 samples<br>[Dataset] Stance distribution:<br>observing    5,932<br>for          4,221<br>against      2,694</pre><pre>[Tokenizer] Vocabulary size: 18,734 tokens<br>Epoch   1/20 | train loss 0.9932 acc 0.5414 | val loss 0.8821 acc 0.6102 | 41.3s<br>Epoch   2/20 | train loss 0.7881 acc 0.6587 | val loss 0.7024 acc 0.7011 | 22.8s<br>Epoch   3/20 | train loss 0.6014 acc 0.7321 | val loss 0.6398 acc 0.7314 | 22.5s<br>...<br>Epoch  20/20 | train loss 0.2741 acc 0.9001 | val loss 0.4012 acc 0.8412 | 22.6s</pre><p>The first epoch is slower because XLA is compiling the train_step. From epoch 2 onward, every epoch takes ~22 seconds on a free Colab v5e-1. Total wall time: about 8 minutes for 20 epochs.</p><h3>Step 5 — Inference on New Claims</h3><p>Once trained, classifying a new claim-headline pair is a one-shot forward pass. Load the tokenizer and checkpointed parameters, encode the inputs, run the model:</p><pre>import pickle<br>import orbax.checkpoint as ocp<br></pre><pre>def load_model(checkpoint_path: str, tokenizer_path: str,<br>               claim_len: int = 64, headline_len: int = 64,<br>               embed_dim: int = 256, num_heads: int = 8, num_layers: int = 4):<br>    with open(tokenizer_path, &quot;rb&quot;) as f:<br>        tokenizer: IndonesianTokenizer = pickle.load(f)</pre><pre>    model = FakeNewsDetector(<br>        vocab_size=len(tokenizer.word2id),<br>        embed_dim=embed_dim,<br>        num_heads=num_heads,<br>        num_layers=num_layers,<br>    )</pre><pre>    checkpointer = ocp.PyTreeCheckpointer()<br>    params = checkpointer.restore(checkpoint_path)<br>    return model, params, tokenizer<br></pre><pre>def predict(model, params, tokenizer, claim: str, headline: str,<br>            claim_len: int = 64, headline_len: int = 64):<br>    claim_ids,    claim_mask    = tokenizer.encode(claim,    claim_len)<br>    headline_ids, headline_mask = tokenizer.encode(headline, headline_len)</pre><pre>    # Add batch dimension<br>    claim_ids     = claim_ids[None, :]<br>    claim_mask    = claim_mask[None, :]<br>    headline_ids  = headline_ids[None, :]<br>    headline_mask = headline_mask[None, :]</pre><pre>    logits = model.apply(<br>        {&quot;params&quot;: params},<br>        claim_ids, claim_mask, headline_ids, headline_mask, train=False,<br>    )<br>    probs    = jax.nn.softmax(logits, axis=-1)[0]<br>    pred_idx = int(jnp.argmax(probs))<br>    return ID_TO_LABEL[pred_idx], {ID_TO_LABEL[i]: float(probs[i]) for i in range(3)}<br></pre><pre># Example<br>model, params, tokenizer = load_model(&quot;checkpoints/best&quot;, &quot;tokenizer.pkl&quot;)</pre><pre>examples = [<br>    {&quot;claim&quot;:    &quot;Pemerintah resmi menaikkan harga BBM bulan depan&quot;,<br>     &quot;headline&quot;: &quot;Kementerian ESDM bantah rencana kenaikan harga BBM&quot;,<br>     &quot;expected&quot;: &quot;against&quot;},<br>    {&quot;claim&quot;:    &quot;Vaksin baru efektif 95 persen mencegah penularan&quot;,<br>     &quot;headline&quot;: &quot;Studi terbaru: vaksin tunjukkan efikasi 95 persen pada uji klinis fase 3&quot;,<br>     &quot;expected&quot;: &quot;for&quot;},<br>    {&quot;claim&quot;:    &quot;Presiden umumkan kebijakan baru ekonomi&quot;,<br>     &quot;headline&quot;: &quot;Pengamat: masih perlu dikaji lebih lanjut dampaknya&quot;,<br>     &quot;expected&quot;: &quot;observing&quot;},<br>]</pre><pre>for ex in examples:<br>    pred, probs = predict(model, params, tokenizer, ex[&quot;claim&quot;], ex[&quot;headline&quot;])<br>    print(f&quot;\nClaim:    {ex[&#39;claim&#39;]}&quot;)<br>    print(f&quot;Headline: {ex[&#39;headline&#39;]}&quot;)<br>    print(f&quot;Predicted: {pred} (expected: {ex[&#39;expected&#39;]})&quot;)<br>    print(f&quot;Probabilities: {probs}&quot;)</pre><h3>Step 6 — Scaling Up: Deploying to Cloud TPU with Keras Kinetic</h3><p>Free Colab TPU is great for prototyping. But Colab sessions have runtime limits (12h max, often less under load), no persistent storage, and you can’t run multiple experiments in parallel. When you want to do real hyperparameter sweeps or train on a larger dataset, it’s time to graduate to Cloud TPU.</p><p>The traditional path means provisioning a TPU VM, SSHing in, installing dependencies, uploading scripts, and managing the deployment yourself. Keras Kinetic skips all of that. You decorate your training function with @kinetic.run(accelerator=&quot;v5p-8&quot;) and call it from your laptop. Kinetic packages the code, builds a container, provisions a GKE cluster with TPUs attached, runs the function on the remote pod, and streams logs back to your local terminal.</p><h3>6.1 — Wrap Your Training Function</h3><p>Save the existing tokenizer, dataset, model, and training code asfakenews.py. Then create train_kinetic.py:</p><pre>import kinetic<br></pre><pre>@kinetic.run(<br>    accelerator=&quot;v5p-8&quot;,<br>    requirements=[<br>        &quot;jax[tpu]&quot;,<br>        &quot;flax&quot;,<br>        &quot;optax&quot;,<br>        &quot;orbax-checkpoint&quot;,<br>        &quot;numpy&quot;,<br>        &quot;pandas&quot;,<br>        &quot;scikit-learn&quot;,<br>    ],<br>)<br>def train_remote(data_gcs_path: str, epochs: int = 50, batch_size: int = 128,<br>                 lr: float = 2e-4):<br>    # CRITICAL: imports happen *inside* the function. The body runs on<br>    # the remote TPU pod, so imports resolve against the pod&#39;s installed<br>    # packages, not your laptop&#39;s.<br>    import os<br>    os.environ[&quot;JAX_PLATFORMS&quot;] = &quot;tpu&quot;</pre><pre>    from fakenews import FakeNewsDataset, FakeNewsDetector, train</pre><pre>    class Args:<br>        data_path  = data_gcs_path<br>        output_dir = &quot;/tmp/checkpoints&quot;<br>        epochs     = epochs<br>        batch_size = batch_size<br>        lr         = lr<br>        claim_len  = 64<br>        head_len   = 64<br>        embed_dim  = 256<br>        num_heads  = 8</pre><pre>    train(Args)<br>    return {&quot;status&quot;: &quot;completed&quot;, &quot;epochs&quot;: epochs}<br></pre><pre>if __name__ == &quot;__main__&quot;:<br>    # This runs on the *remote* TPU but feels like a local function call.<br>    result = train_remote(<br>        data_gcs_path=&quot;gs://my-bucket/datasetika.csv&quot;,<br>        epochs=50,<br>        batch_size=128,<br>    )<br>    print(f&quot;Training finished: {result}&quot;)</pre><p>Three things worth highlighting:</p><ul><li><strong>All imports go inside the function</strong> — the body runs on the remote pod, so import jax needs to resolve against the pod’s jax[tpu] package, not your laptop’s.</li><li><strong>requirements=[...] is your remote requirements.txt</strong> — Kinetic uses it to build the container image on the first run. None of these need to be installed locally.</li><li><strong>Data lives in GCS</strong> — your dataset goes to a Google Cloud Storage bucket, and the function reads it from gs://.... The TPU pod has automatic GCS access via its service account.</li></ul><h3>6.2 — Launch from Your Laptop</h3><pre>python train_kinetic.py</pre><p>First run takes ~5 minutes for the container build. Subsequent runs with unchanged dependencies start in under a minute. You’ll see remote logs streamed to your terminal:</p><pre>Shipping to TPU via Kinetic...<br>[Stage 1/4] Preflight &amp; packaging...<br>[Stage 2/4] Building container image (5m, cached after this run)...<br>[Stage 3/4] Submitting job to GKE cluster...<br>[Stage 4/4] Executing on TPU v5p-8...<br>[remote] [Dataset] Loaded 12,847 samples<br>[remote] [Tokenizer] Vocabulary size: 18,734 tokens<br>[remote] Epoch   1/50 | train loss 0.9912 acc 0.5421 | val loss 0.8810 acc 0.6112 | 12.8s<br>[remote] Epoch   2/50 | train loss 0.7821 acc 0.6601 | val loss 0.6987 acc 0.7022 | 4.2s<br>...<br>[remote] Epoch  50/50 | train loss 0.1912 acc 0.9301 | val loss 0.3811 acc 0.8612 | 4.3s<br>Job complete. Streaming results to local...<br>Training finished: {&#39;status&#39;: &#39;completed&#39;, &#39;epochs&#39;: 50}</pre><p>The v5p-8 has 8 chips vs Colab’s 1, so per-epoch time drops from 22s to ~4s — about 5× faster, which matters when you’re running 50+ epochs or sweeping hyperparameters.</p><h3>6.3 — Tear Down</h3><p>The GKE cluster’s control plane costs money even when no TPU nodes are active. Always shut it down when you’re done:</p><pre>kinetic down --yes</pre><h3>Training Configuration Summary</h3><ul><li><strong>Architecture:</strong> 4-layer Transformer + Stance Cross-Encoder</li><li><strong>Embedding dim:</strong> 256</li><li><strong>Attention heads:</strong> 8 (head_dim 32)</li><li><strong>Feedforward dim:</strong> 1024</li><li><strong>Total parameters:</strong> ~7M</li><li><strong>Optimizer:</strong> AdamW with linear warmup (10% of steps) + cosine decay</li><li><strong>Learning rate:</strong> 2e-4 peak</li><li><strong>Weight decay:</strong> 0.01</li><li><strong>Dropout:</strong> 0.1</li><li><strong>Batch size:</strong> 64 (Colab) / 128 (Cloud TPU v5p-8)</li><li><strong>Epochs:</strong> 20 (Colab) / 50 (Cloud TPU)</li><li><strong>Sequence length:</strong> 64 tokens for both claim and headline</li></ul><h3>Key Takeaways</h3><h3>Free Colab TPU is the best on-ramp for JAX/Flax in 2026</h3><p>You get a full v5e-1 chip with no signup beyond a Google account. For models in the 5–50M parameter range, that’s enough to do real work, not just toy demos. If you’ve been putting off learning JAX because the cloud setup felt overwhelming, this path is friction-free.</p><h3>Keras Kinetic makes the leap from Colab to Cloud TPU painless</h3><p>The biggest practical lesson from this project: when Colab’s runtime limits start hurting, switching to Cloud TPU traditionally means rewriting your deployment story. With Kinetic, you wrap your existing function in a decorator and call it from the same laptop you’ve been using. The mental model — “I have a function; I want it to run on a TPU” — stays intact.</p><h3>Stance detection is a more honest framing than fake-news classification</h3><p>Asking a model “is this true?” puts it in an impossible position. Asking “does this article support, refute, or merely observe this claim?” gives it a question it can answer, and gives downstream fact-checkers exactly the signal they need.</p><h3>Cross-attention beats independent encoders for paired-text tasks</h3><p>The StanceCrossEncoder is what makes this model genuinely multimodal-aware. Concatenating two independently-encoded vectors and slapping a classifier on top works, but performance jumps significantly when you let the two inputs literally read each other before pooling. The [c, h, |c-h|, c*h] interaction trick is borrowed from NLI literature and consistently outperforms simpler combinations.</p><h3>TPU is the right pick for fixed-length attention workloads</h3><p>Multi-head attention with fixed sequence lengths is exactly what TPU systolic arrays were built for. The whole train_step JITs into a single fused execution graph, and after the first compile, every step runs at peak FLOPs. GPUs are still preferable when you need irregular-length sequences, KV-caching for generation, or HuggingFace PyTorch checkpoints — but for from-scratch JAX/Flax training, TPU wins on both speed and cost.</p><h3>Adapting This to Other Languages and Tasks</h3><p>The pipeline is generic. To use it on your own data:</p><ul><li><strong>Any language → just point the tokenizer at your text corpus.</strong> The whitespace tokenizer is language-agnostic. For better quality, swap in the appropriate pre-trained tokenizer (English → BERT, Indonesian → IndoBERT, Arabic → AraBERT, etc.).</li><li><strong>Any paired-text classification task</strong> → swap the labels. The [c, h, |c-h|, c*h] interaction trick works for paraphrase detection, NLI, semantic textual similarity, duplicate question detection, and more.</li><li><strong>Larger corpora</strong> → bump embed_dim, num_layers, and switch from a learned tokenizer to a SentencePiece or BPE one. The training loop and Kinetic deployment don’t change.</li></ul><h3>Resources</h3><ul><li><strong>Project repository:</strong> <em>(your GitHub link here)</em></li><li><strong>JAX:</strong> <a href="https://github.com/google/jax">github.com/google/jax</a></li><li><strong>Flax:</strong> <a href="https://github.com/google/flax">github.com/google/flax</a></li><li><strong>Optax:</strong> <a href="https://github.com/google-deepmind/optax">github.com/google-deepmind/optax</a></li><li><strong>Orbax:</strong> <a href="https://github.com/google/orbax">github.com/google/orbax</a></li><li><strong>Keras Kinetic:</strong> <a href="https://github.com/keras-team/kinetic">github.com/keras-team/kinetic</a></li><li><strong>IndoBERT (production tokenizer upgrade):</strong><a href="https://huggingface.co/indobenchmark/indobert-base-p1">huggingface.co/indobenchmark/indobert-base-p1</a></li><li><strong>Cloud TPU documentation:</strong> <a href="https://cloud.google.com/tpu/docs">cloud.google.com/tpu/docs</a></li><li><strong>Google Colab:</strong> <a href="https://colab.research.google.com/">colab.research.google.com</a></li></ul><h3>Acknowledgement</h3><ul><li>Google Cloud credits were provided for this project. #TPUSprint</li><li>Thanks to the JAX, Flax, and Keras teams for building such a clean stack — training custom transformers on TPUs used to be a research-grade pain.</li></ul><p><strong>Tags:</strong> JAX, Flax, KerasKinetic, TPU, GoogleColab, FakeNewsDetection, StanceDetection, NaturalLanguageProcessing, MachineLearning, Indonesia</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=4b7f96c58b24" width="1" height="1" alt=""><hr><p><a href="https://medium.com/google-developer-experts/building-a-multimodal-indonesian-fake-news-detector-with-jax-flax-and-keras-kinetic-on-cloud-tpu-4b7f96c58b24">Building a Multimodal Indonesian Fake-News Detector with JAX, Flax, and Keras Kinetic on Cloud TPU</a> was originally published in <a href="https://medium.com/google-developer-experts">Google Developer Experts</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[How to Build and Deploy an AI Agent with Gemini CLI and Google ADK: A]]></title>
            <link>https://medium.com/google-developer-experts/how-to-build-and-deploy-an-ai-agent-with-gemini-cli-and-google-adk-a-746a4064507b?source=rss----a67bd6fa7d58---4</link>
            <guid isPermaLink="false">https://medium.com/p/746a4064507b</guid>
            <category><![CDATA[ai-agent]]></category>
            <category><![CDATA[vibe-coding]]></category>
            <category><![CDATA[gemini-cli]]></category>
            <category><![CDATA[google-adk]]></category>
            <category><![CDATA[agent-development-kit]]></category>
            <dc:creator><![CDATA[Esther Irawati Setiawan]]></dc:creator>
            <pubDate>Wed, 03 Jun 2026 14:11:57 GMT</pubDate>
            <atom:updated>2026-05-31T17:02:49.387Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*fAWJSKCea7KLWBIsCyn6Fg.png" /></figure><h3>Step-by-Step Vibe Coding Tutorial</h3><p><em>A simplified, hands-on walkthrough of Google’s “Agentverse: The Shadowblade’s Codex” codelab, focused on the build itself.</em></p><p><strong>📂 All the commands from this tutorial, in one quick-reference file: </strong><a href="https://github.com/estherirawati/agentverse"><strong>github.com/estherirawati/agentverse</strong></a></p><p>Google’s Agentverse codelab wraps everything in a fun fantasy theme. You’re a “Shadowblade,” your terminal is your “primary weapon,” and you’re fighting an entropy monster called <em>The Static</em>. The story is a great hook, and this tutorial keeps a light touch of it while focusing on <strong>what you build and how</strong>, so you can follow along step by step.</p><p>Underneath the theme, this is a genuinely good tour of the modern AI-agent workflow on Google Cloud: prompting code into existence with Gemini CLI, wiring the CLI to external tools, building a real autonomous agent with the Agent Development Kit (ADK), testing it, and shipping it to Cloud Run.</p><p>Here’s what the journey actually looks like.</p><h3>What you’re really building</h3><p>By the end you’ll have:</p><ul><li>A working command-line AI assistant (<strong>Gemini CLI</strong>) that can read your intent and act on it</li><li>A personal website generated almost entirely from a single prompt</li><li>The CLI connected to <strong>external tools</strong> through MCP servers (a self-hosted Git server and an image generator)</li><li>A real <strong>autonomous agent</strong> built with ADK that picks the right “weapon” tool for each task</li><li>An <strong>automated test suite</strong> for that agent</li><li>The agent <strong>deployed</strong> to Cloud Run as a live service</li></ul><p>The fantasy theme maps cleanly onto real engineering ideas, and the codelab makes this explicit in its “for non-gamers” sidebars. I’ll translate as we go.</p><h3>Step 0: Setup (the necessary part)</h3><p>You’ll need a <strong>personal Gmail account</strong> — corporate or school accounts won’t work because of the credit grant.</p><ol><li>Claim your free Google Cloud credit through the workshop link, sign in, accept the terms.</li><li>Open <strong>Cloud Shell</strong> at <a href="https://console.cloud.google.com/">console.cloud.google.com</a>, then click <strong>Open Editor</strong>.</li><li>Clone the starter code and run the setup script:</li></ol><pre>git clone https://github.com/weimeilin79/agentverse-developer.git<br>chmod +x ~/agentverse-developer/*.sh<br>cd ~/agentverse-developer<br>./init.sh    # press Enter to accept the default Project ID</pre><ol><li>Point your config at the new project and turn on the APIs you’ll need:</li></ol><pre>gcloud config set project $(cat ~/project_id.txt) --quiet<br>gcloud services enable compute.googleapis.com artifactregistry.googleapis.com \<br>  run.googleapis.com cloudbuild.googleapis.com aiplatform.googleapis.com \<br>  iam.googleapis.com cloudresourcemanager.googleapis.com<br>npm update -g @google/gemini-cli</pre><p>That’s the whole foundation. Now the interesting part.</p><h3>Step 1: Meet your “weapon” — Gemini CLI</h3><p>Gemini CLI is an open-source AI agent that lives in your terminal. It runs a <strong>reason-and-act (ReAct) loop</strong>: it reads your high-level request, breaks it into steps, picks the right tool, runs it, and checks the result.</p><p>Start it from a fresh folder:</p><pre>cd ~/agentverse-developer<br>mkdir playground<br>cd playground<br>gemini    # choose &quot;No&quot; when it asks to connect the Cloud Shell editor</pre><p>A few commands worth knowing right away, run <em>inside</em> the CLI — /help lists commands, /tools shows built-in abilities (ReadFile, WriteFile, GoogleSearch…), the ! prefix runs a normal shell command, and /memorystores context:</p><pre>/help<br>/tools<br>!ls -l<br>/memory add &quot;My name is [your name]. I&#39;m learning about AI agents.&quot;<br>/memory show</pre><p>That /memory feature is your first taste of <strong>context engineering</strong> — deliberately feeding the AI background so its output stays relevant.</p><p>Now the headline trick — generating code from a sentence:</p><pre>Write a Python script called hello.py that prints &quot;I built my first<br>AI-generated file&quot; and the current date.</pre><p>Then verify it actually worked:</p><pre>!ls<br>!cat hello.py<br>!python3 hello.py</pre><p>Exit anytime with <strong>Ctrl+C twice</strong>. That’s it — you just “vibe-coded.”</p><blockquote><strong>What “vibe coding” really means:</strong> instead of writing every line by hand, you describe your intent in plain language and let the assistant generate the code and config. The industry term is <strong>intent-driven development</strong>.</blockquote><h3>Step 2: Build a real website, then connect external tools</h3><p>Same idea, bigger ask. Inside the CLI:</p><pre>Create a personal profile website in the current folder. Dark theme,<br>electric blue accents. Two files: index.html and styles.css. Use flexbox<br>for a two-column layout. Include a placeholder spot for a profile picture.<br>Make the code clean and commented. Don&#39;t start any server.</pre><p>Exit the CLI and preview it:</p><pre>python -m http.server<br># Cloud Shell → Web Preview → port 8000, then Ctrl+C to stop</pre><h3>Giving the CLI hands: MCP servers</h3><p>So far Gemini CLI can only <em>talk</em> and edit local files. To let it take real action in other systems — Git, databases, APIs — you connect a <strong>Model Context Protocol (MCP) server</strong>. Think of it as a power cable between the AI’s “brain” and a tool’s “body.”</p><p>The codelab spins up <strong>Gitea</strong>, a self-hosted Git server, as the first tool:</p><pre>cd ~/agentverse-developer<br>./gitea.sh<br># Web Preview → port 3005 → log in with dev / dev</pre><p>Then register it in Gemini’s config so the CLI can see it:</p><pre>if [ ! -f ~/.gemini/settings.json ]; then<br>  echo &#39;{&quot;mcpServers&quot;:{&quot;gitea&quot;:{&quot;url&quot;:&quot;http://localhost:8085/sse&quot;}}}&#39; &gt; ~/.gemini/settings.json<br>else<br>  jq &#39;. * {&quot;mcpServers&quot;:{&quot;gitea&quot;:{&quot;url&quot;:&quot;http://localhost:8085/sse&quot;}}}&#39; ~/.gemini/settings.json &gt; ~/.gemini/settings.json.tmp &amp;&amp; mv ~/.gemini/settings.json.tmp ~/.gemini/settings.json<br>fi</pre><p>Relaunch the CLI from your project folder and confirm Gitea is wired up:</p><pre>cd ~/agentverse-developer/playground<br>gemini</pre><pre>/mcp</pre><p>With Gitea visible, you can now do version control with plain English:</p><pre>Create a new Gitea repository named &#39;my-profile&#39; with description<br>&#39;My first AI-built website&#39;. Don&#39;t add any content yet.</pre><pre>Using the Gitea tool, push index.html and styles.css to the<br>&#39;my-profile&#39; repository.</pre><pre>File an issue in the my-profile repo titled &quot;Profile image is missing&quot;.<br>Use the Gitea tool and the &#39;dev&#39; user account.</pre><pre>Close issue #1 in the my-profile repo. Use the &#39;dev&#39; user account.</pre><p>That’s the leap the codelab is really showing off: the AI stops being a chatbot and becomes an <strong>active participant in your workflow</strong> — creating repos, pushing commits, filing and closing issues.</p><blockquote><strong>Extensions vs. raw MCP:</strong> editing settings.json by hand is the &quot;raw&quot; way — useful for understanding the plumbing. In real life you&#39;d usually install an <strong>extension</strong> (gemini extensions install ...), which bundles the MCP server, custom slash-commands, and context into one installable package. The codelab uses this approach later for an image-generation extension called Nano Banana.</blockquote><h3>Step 3: Forge the actual agent with ADK</h3><p>This is the heart of the lab. You move from “AI helps me write code” to “I build an AI thing that runs on its own.” The framework is Google’s <strong>Agent Development Kit (ADK)</strong>.</p><h3>First, lay down the rules</h3><p>Before generating any agent code, you write a GEMINI.md file. The CLI loads it automatically and treats it as persistent, project-level instructions — coding standards, naming conventions, a persona, hard constraints:</p><pre>cd ~/agentverse-developer/shadowblade<br>. ~/agentverse-developer/set_env.sh</pre><pre>cat &lt;&lt; &#39;EOF&#39; &gt; GEMINI.md<br>### Coding Rules for This Project<br>- Use Python 3 with type hints on every function.<br>- Every function needs a docstring explaining what it does.<br>- Use snake_case for variables and functions, PascalCase for classes.<br>- Keep code clean and readable.<br>EOF</pre><p>This is <strong>context engineering</strong> layer two. The hierarchy is worth remembering:</p><ol><li><strong>~/.gemini/settings.json</strong> — global user settings</li><li><strong>GEMINI.md</strong> — project rules, always loaded</li><li><strong>Agent Skills</strong> (.gemini/skills/) — specialized knowledge loaded <em>only when relevant</em></li></ol><p>That third layer (progressive disclosure) is what keeps the agent fast: a repo can hold dozens of niche skills, but the AI only pulls the one that matches the task at hand.</p><h3>The agent itself</h3><p>The codelab has you generate agent.py from a design doc, then — because LLM output is unpredictable — swap in the known-good version:</p><pre>cp ~/agentverse-developer/working_code/agent.py ~/agentverse-developer/shadowblade/<br>cp ~/agentverse-developer/working_code/mcp_server.py ~/agentverse-developer/shadowblade/</pre><p>At its core, the agent is an LlmAgent with a model, a clear instruction block, and a list of tools wired up through an MCPToolset. The &quot;weapons&quot; it can wield are just @mcp.tool()-decorated Python functions in mcp_server.py — each one a small, focused tool with a descriptive docstring the model reads to decide when to use it.</p><h3>Run it</h3><pre>cd ~/agentverse-developer<br>python -m venv env<br>source env/bin/activate<br>pip install --upgrade pip<br>pip install -r shadowblade/requirements.txt<br>adk run shadowblade</pre><p>Then give it a command. The agent reads the “monster’s weakness,” picks the matching tool, and reports the outcome:</p><pre>We&#39;re stuck against &#39;Perfectionism&#39;. Its weakness is &#39;Elegant<br>Sufficiency&#39;. Break us out!</pre><pre>&#39;Dogma&#39; blocks our path. Its weakness is &#39;Revolutionary Rewrite&#39;. Take it down.</pre><p>Strip away the theme and this is exactly how a real support or ops agent works: read the situation, choose the correct internal tool, execute, summarize.</p><h3>Step 4: Test it (because an untested agent is a liability)</h3><p>Agents are harder to test than scripts because their behavior <em>emerges</em> from an LLM’s multi-step reasoning. You care about two things: did it produce the right final answer, <strong>and</strong> did it take the right path (use the right tools)?</p><p>ADK gives you two complementary methods:</p><p><strong>adk eval</strong> runs the agent against a set of predefined cases in a JSON &quot;evalset.&quot; Each case defines the input, the expected response, and — crucially — the expected tool_uses. A neat trick the lab teaches here is <strong>synthetic test data</strong>: you hand the AI one template case and ask it to generate dozens of varied ones, scaling your test coverage cheaply.</p><p><strong>pytest</strong> wraps the same AgentEvaluator in code, which is what makes it CI-ready:</p><pre>cp ~/agentverse-developer/working_code/test_agent_initiative.py ~/agentverse-developer/shadowblade/<br>source ~/agentverse-developer/env/bin/activate<br>cd ~/agentverse-developer<br>. ~/agentverse-developer/set_env.sh<br>pytest test_agent_initiative.py</pre><p>A passing run (1 passed) means the agent follows its protocol and is ready to drop into an automated pipeline. The scoring criteria split cleanly into tool_trajectory_avg_score (did it <em>do</em> the right thing) and response_match_score (did it <em>say</em> the right thing).</p><blockquote>The lab also introduces <strong>hooks</strong> — scripts that fire at specific points in the agent loop (BeforeTool, AfterTool, etc.) so you can validate, audit, or block actions during a run without touching the agent&#39;s code. Great for security and compliance gates.</blockquote><h3>Step 5: Deploy to Cloud Run, then clean up</h3><p>Build a container image and ship it as a live, autoscaling service:</p><pre>. ~/agentverse-developer/set_env.sh</pre><pre># create the artifact repo (ignore error if it already exists)<br>gcloud artifacts repositories create $REPO_NAME \<br>  --repository-format=docker \<br>  --location=$REGION \<br>  --description=&quot;Agent repo&quot; 2&gt;/dev/null || echo &quot;Already exists, moving on&quot;</pre><pre># grant the service account the roles it needs<br>for ROLE in artifactregistry.admin cloudbuild.builds.editor run.admin \<br>  iam.serviceAccountUser aiplatform.user logging.logWriter logging.viewer; do<br>    gcloud projects add-iam-policy-binding $PROJECT_ID \<br>      --member=&quot;serviceAccount:$SERVICE_ACCOUNT_NAME&quot; \<br>      --role=&quot;roles/$ROLE&quot; --quiet<br>done</pre><pre># prep the Dockerfile, then build and deploy<br>sed -i &#39;s|COPY ./shadowblade|COPY .|g&#39; ~/agentverse-developer/shadowblade/Dockerfile<br>sed -i &#39;s|COPY shadowblade|COPY .|g&#39; ~/agentverse-developer/shadowblade/Dockerfile</pre><pre>cd ~/agentverse-developer<br>gcloud builds submit ./shadowblade \<br>  --tag ${REGION}-docker.pkg.dev/${PROJECT_ID}/${REPO_NAME}/my-agent:latest</pre><pre>gcloud run deploy my-agent \<br>  --image=${REGION}-docker.pkg.dev/${PROJECT_ID}/${REPO_NAME}/my-agent:latest \<br>  --region=${REGION} \<br>  --allow-unauthenticated \<br>  --set-env-vars=&quot;A2A_HOST=0.0.0.0,A2A_PORT=8080,GOOGLE_GENAI_USE_VERTEXAI=TRUE&quot; \<br>  --min-instances=1 \<br>  --project=${PROJECT_ID}</pre><p>Visit your service URL with /.well-known/agent-card.json appended to confirm the agent is live and describing itself:</p><pre>https://my-agent-xxxxx-uc.a.run.app/.well-known/agent-card.json</pre><p><strong>Don’t skip cleanup</strong> — this is what stops a free-credit project from quietly billing you:</p><pre>. ~/agentverse-developer/set_env.sh<br>gcloud run services delete my-agent --region=${REGION} --quiet<br>gcloud artifacts repositories delete ${REPO_NAME} --location=${REGION} --quiet<br>rm -rf ~/agentverse-developer ~/.gemini<br>rm -f ~/project_id.txt</pre><h3>The five ideas worth keeping</h3><p>Setting the theme aside, here’s what this codelab actually teaches:</p><ol><li><strong>Intent-driven development</strong> — describe the outcome, let the AI generate the code, then <em>verify</em> it. The verification habit matters as much as the generation.</li><li><strong>MCP servers turn a chatbot into a doer</strong> — the moment the CLI can touch Git, a database, or an API, it becomes part of your real workflow.</li><li><strong>Context engineering is layered</strong> — global settings, persistent project rules (GEMINI.md), and on-demand skills. Good context beats clever one-off prompts.</li><li><strong>Agents need real testing</strong> — judge both the final answer <em>and</em> the path taken. Synthetic data lets you scale that testing cheaply.</li><li><strong>Ship it like software</strong> — containerize, set IAM roles, deploy to Cloud Run, and tear it down when you’re done.</li></ol><p>The story makes it memorable; the workflow makes it useful. Both are worth keeping.</p><p><em>Want to go through it yourself? The full codelab is “Agentverse: The Shadowblade’s Codex” on Google Codelabs, and the starter code lives at </em><a href="https://github.com/weimeilin79/agentverse-developer"><em>github.com/weimeilin79/agentverse-developer</em></a><em>. I’ve also collected every command from this tutorial in a quick-reference file on my own GitHub: </em><a href="https://github.com/estherirawati/agentverse"><em>github.com/estherirawati/agentverse</em></a><em>.</em></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=746a4064507b" width="1" height="1" alt=""><hr><p><a href="https://medium.com/google-developer-experts/how-to-build-and-deploy-an-ai-agent-with-gemini-cli-and-google-adk-a-746a4064507b">How to Build and Deploy an AI Agent with Gemini CLI and Google ADK: A</a> was originally published in <a href="https://medium.com/google-developer-experts">Google Developer Experts</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Vertex AI Is Gone. Here Is What Google Built Instead.]]></title>
            <link>https://medium.com/google-developer-experts/vertex-ai-is-gone-here-is-what-google-built-instead-92556d1c64eb?source=rss----a67bd6fa7d58---4</link>
            <guid isPermaLink="false">https://medium.com/p/92556d1c64eb</guid>
            <category><![CDATA[google-cloud-platform]]></category>
            <category><![CDATA[ai-agent]]></category>
            <category><![CDATA[google]]></category>
            <category><![CDATA[artificial-intelligence]]></category>
            <category><![CDATA[technology]]></category>
            <dc:creator><![CDATA[Geeta Kakrani]]></dc:creator>
            <pubDate>Wed, 03 Jun 2026 14:11:15 GMT</pubDate>
            <atom:updated>2026-06-11T08:21:40.490Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Jg_EghLHdv14J98l3TH08g.png" /></figure><p>At Cloud Next 2026 in Las Vegas, Google made one of the biggest moves in its cloud history. <a href="https://cloud.google.com/vertex-ai?utm_campaign=deveco_gdemembers&amp;utm_source=deveco">Vertex AI, the platform that millions of developers have been using since 2021, is gone.</a> Not deprecated. Not just renamed. Replaced, structurally, by something built from the ground up for a different era.</p><p><a href="https://cloud.google.com/gemini?utm_campaign=deveco_gdemembers&amp;utm_source=deveco">It is called the Gemini Enterprise Agent Platform.</a></p><p>And if you are building anything with AI today, you need to understand what just changed.</p><h3>The Old World vs The New World</h3><p>Vertex AI was built for a world where you pick a model, train it, deploy it, and call it a day. One model, one job, one endpoint.</p><p>That world is over.</p><p>Businesses today are not trying to build one AI assistant. They are trying to run hundreds, sometimes thousands, of AI agents at the same time. Agents that search the web, write emails, call APIs, talk to each other, handle customer requests, and make decisions, all simultaneously, all day long.</p><p>Vertex AI was not designed for that. So Google replaced it.</p><h3>What Is the Gemini Enterprise Agent Platform</h3><p>Google CEO Thomas Kurian described the strategy in simple terms at the keynote. Competitors, he said, are “handing you the pieces, not the platform.” Google wants to own the entire stack, from its custom TPU chips all the way to the three billion inboxes inside Google Workspace.</p><p>The Gemini Enterprise Agent Platform is the result. It brings together model selection, development tools, deployment infrastructure, security, and governance into a single place. Everything you need to build, run, and manage agents, under one roof.</p><p>It also absorbed Agentspace, Google’s enterprise AI search and discovery product, into a unified Gemini Enterprise offering. No more juggling separate products.</p><h3>The Four Things the Platform Does</h3><p>The platform is organized around four core jobs.</p><p>The first is building. For developers who want to write code, there is the Agent Development Kit, or ADK. It just hit stable version 1.0 across four languages: Python, Go, Java, and TypeScript. This is significant. Enterprise teams do not always work in Python. A Java shop can now build production-ready agents without maintaining a separate Python service just to connect to Google’s infrastructure. ADK also now includes a graph-based framework for orchestrating multiple agents working together.</p><p>For teams that do not want to write code at all, there is Agent Studio. It is a low-code interface where you describe what you want in plain English and the platform helps you build it. Non-technical teams inside a company can now create agents without filing a ticket with engineering.</p><p>The second is scaling. There is a feature called Agent Runtime that Google says delivers sub-second cold starts, meaning new agent instances spin up almost instantly when demand spikes. There is also a new Memory Bank. This gives agents persistent, long-term memory across sessions. Previously, every time you started a new conversation with an agent, it had no idea what happened before. Memory Bank fixes that. An agent can now remember context from a week ago and act on it today.</p><p>The third is connecting. The Model Garden now has over 200 models, including Google’s own Gemini models as well as Anthropic Claude and many others. On top of that, partner agents from Box, Workday, Salesforce, ServiceNow, Dun and Bradstreet, and S&amp;P Global are already integrated. You do not have to build everything from scratch. If you need an agent that handles HR self-service or financial data, there is likely already one ready to plug in.</p><p>The fourth is governing. This is the part that enterprise IT teams care most about. The platform has a single control plane where every agent deployed inside a company is visible, auditable, and controllable. Model Armor blocks prompt injection attacks. Zero-trust security handles decentralized setups. IAM manages access and keeps audit logs. Every employee can use and share agents, and IT can see all of it.</p><h3>The Piece Most People Are Sleeping On: A2A Protocol</h3><p>The flashiest announcements get the attention. But the most strategically important thing Google announced at Cloud Next 2026 might be the Agent2Agent protocol, or A2A, now at version 1.2.</p><p>Here is the problem it solves. You might build an agent on Google Cloud. Your partner company builds an agent on Microsoft Azure. Your vendor uses a Salesforce agent. Today, those three agents cannot easily talk to each other. They live in different systems, speak different formats, and have no way to securely pass tasks back and forth.</p><p>A2A is the answer. It is an open standard that lets agents built on completely different platforms communicate, delegate tasks, and share state. It does not matter which model or cloud they are built on.</p><p>The numbers back up how serious this is. Over 150 organizations are already running A2A in production, not in pilot programs. Real work, real tasks, real companies. Microsoft, AWS, Salesforce, SAP, and ServiceNow are all live. The protocol is now governed by the Linux Foundation’s Agentic AI Foundation, which means no single company controls it.</p><p>And for developers already using LangGraph or CrewAI, both frameworks already have native A2A support built in. You do not need to rewrite anything.</p><h3>Project Mariner: An Agent That Browses the Web For You</h3><p>[Update — June 2026: Google has officially discontinued the standalone Project Mariner experiment to integrate its web-browsing capabilities directly into Gemini Agent and AI Mode.]</p><p>One of the more visible pieces is Project Mariner, built by Google DeepMind and powered by Gemini 2.0.</p><p>Mariner is a web-browsing agent. You give it a goal, and it opens browsers, navigates websites, fills out forms, retrieves information, and completes purchases, all on its own. It scores 83.5% on the WebVoyager benchmark, which is the standard test for web agents, and can handle ten tasks running at the same time on cloud-based virtual machines.</p><p>Right now it is available to Google AI Ultra subscribers in the United States. The roadmap includes a visual builder called Mariner Studio in the second quarter of 2026, cross-device sync in the third quarter, and an agent marketplace in the fourth quarter.</p><h3>What This Means If You Are Already on Vertex AI</h3><p>The change is structural, not just cosmetic. All the Vertex AI features you know, Model Garden, Custom Training, AutoML, Model Registry, Endpoints, and Pipelines, are still there. They have just been reorganised under a “Models” sub-menu inside the Agent Platform.</p><p>The underlying API endpoint, aiplatform.googleapis.com, is not going anywhere. Google has committed to keeping it alive for compatibility. If you are reading documentation in 2027 and the API still says aiplatform, do not be surprised.</p><p>But new capabilities will not ship as Vertex AI updates. They will ship exclusively through the Gemini Enterprise Agent Platform. The roadmap has moved. If you want access to what Google builds next, that is where it will live.</p><p>One important date: if you are using deprecated SDK modules from the old Vertex AI Python SDK, the migration deadline is June 24, 2026. That is soon.</p><h3>The Bigger Picture</h3><p>Every major cloud provider is making the same move at the same time. AWS launched AgentCore. Anthropic shipped Claude for Small Business. Google launched this. The consolidation pattern is unmistakable. Every cloud is going agent-first. Every cloud is differentiating on governance, identity, and security. Every cloud is building partner marketplaces to seed adoption.</p><p>Google’s bet is that owning the full stack, from the hardware layer to the productivity layer, gives it an advantage that point solutions cannot match. If your company already runs on Google Workspace and Google Cloud, the integration story is genuinely compelling. The economics also make sense when your data already lives in BigQuery and your team is already on Google tools.</p><p>If you are pulling data from outside Google’s ecosystem and paying only for the agent layer, the math gets less favorable.</p><h3>The Bottom Line</h3><p>Vertex AI served its purpose. It was a solid platform for the era of single models and single deployments. But that era ended.</p><p>The Gemini Enterprise Agent Platform is built for the era of agent networks, agent communication, agent governance, and agent scale. Whether you are a developer, a cloud architect, or a business leader trying to figure out where AI is actually going, this is the direction.</p><p>Google has drawn a line. The agentic era is not coming. It is here. And Google just bet its entire cloud platform on it.</p><p><em>This article is based on announcements from </em><a href="https://cloud.withgoogle.com/next?utm_campaign=deveco_gdemembers&amp;utm_source=deveco"><em>Google Cloud Next 2026 in Las Vegas on April 22, 2026.</em></a></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=92556d1c64eb" width="1" height="1" alt=""><hr><p><a href="https://medium.com/google-developer-experts/vertex-ai-is-gone-here-is-what-google-built-instead-92556d1c64eb">Vertex AI Is Gone. Here Is What Google Built Instead.</a> was originally published in <a href="https://medium.com/google-developer-experts">Google Developer Experts</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[TPU Just Got Split in Two — and It Changes Everything About AI Infrastructure]]></title>
            <link>https://medium.com/google-developer-experts/tpu-just-got-split-in-two-and-it-changes-everything-about-ai-infrastructure-a92d3d32a6a8?source=rss----a67bd6fa7d58---4</link>
            <guid isPermaLink="false">https://medium.com/p/a92d3d32a6a8</guid>
            <category><![CDATA[ai-agent]]></category>
            <category><![CDATA[ai]]></category>
            <category><![CDATA[google]]></category>
            <category><![CDATA[generative-ai-tools]]></category>
            <category><![CDATA[google-tpu]]></category>
            <dc:creator><![CDATA[Geeta Kakrani]]></dc:creator>
            <pubDate>Wed, 03 Jun 2026 14:09:37 GMT</pubDate>
            <atom:updated>2026-06-03T14:09:36.183Z</atom:updated>
            <content:encoded><![CDATA[<p>By Geeta Kakrani | Google Developer Expert in AI</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*MdF6OevM_8QvwyuBuRPfLg.png" /></figure><p>Imagine you run a restaurant.For years, you had one chef doing everything sourcing ingredients, prepping, cooking, plating, cleaning. One person. All jobs. It worked when you had 20 customers a day.</p><p>Now you have 2 million customers. Every single day. And they all want their food in under two seconds.</p><p>You don’t hire one super-chef. You split the kitchen.</p><p>That’s exactly what Google just did with its TPUs.</p><h3>A Decade of One Chip Doing Everything</h3><p>Since 2016, Google’s Tensor Processing Units have been the silent engine behind every Google product you use — Search, Translate, Photos, and Gemini. One chip family, designed to both <em>train</em> AI models and <em>run</em> them.</p><p>For years, that was fine.</p><p>But then AI agents arrived. Systems that don’t just answer one question — they reason, plan, remember, and take action across multiple steps. Millions of them, running simultaneously, in real time.</p><p>Suddenly, one chip doing everything wasn’t fine anymore.</p><p>At Google Cloud Next 2026, Google made an announcement ten years in the making: <strong>the 8th generation TPU is actually two completely different chips.</strong></p><p>Meet <strong>TPU 8t</strong> and <strong>TPU 8i</strong>.</p><h3>The Problem They’re Each Solving</h3><p>Here’s a simple way to think about it.</p><p><strong>Training an AI model</strong> is like writing a novel. You lock yourself in a room for months. You need enormous focus, massive resources, and you’re not in a hurry to show anyone the draft. When it’s done, it’s done.</p><p><strong>Running an AI model</strong> is like performing that novel as a live play — every night, for a million audiences simultaneously. You need to be fast, fluid, and you absolutely cannot pause mid-sentence because you’re waiting for a prop to arrive.</p><p>Same story. Completely different skills required.</p><p>Google finally stopped asking one chip to do both.</p><h3>TPU 8t — The Training Powerhouse</h3><p>The “t” is for training. And the numbers here are staggering.</p><p>One TPU 8t superpod holds <strong>9,600 chips</strong> working together as a single system — with <strong>2 petabytes of shared memory</strong>. That’s roughly the storage equivalent of 400 million books, all accessible at once.</p><p>The compute? <strong>121 ExaFLOPS.</strong> Nearly triple the previous generation.</p><p>If the previous chip could fill an Olympic swimming pool in an hour, TPU 8t fills three.</p><p>Google also solved a long-standing bottleneck: data transfer. Previously, chips had to route data through the CPU — like every order in a restaurant going through one overwhelmed manager. TPU 8t bypasses that entirely with <strong>TPUDirect Storage</strong>, letting chips talk directly to data. Transfer speeds effectively doubled.</p><p>The result: <strong>2.7x better training performance per dollar</strong> over the last generation.</p><h3>TPU 8i — Built for the Age of AI Agents</h3><p>This is where it gets really interesting.</p><p>The “i” is for inference — but honestly, it should stand for <em>intelligence at scale</em>. Because TPU 8i wasn’t just designed to run AI models. It was designed specifically for the messy, complex, real-time world of AI agents.</p><p>Google made three radical changes:</p><p><strong>1. Triple the on-chip memory</strong></p><p>When an AI is mid-conversation with you, it holds a running record of everything said — called a KV Cache. On older chips, this record kept overflowing into slower memory, forcing the chip to pause and fetch data. Like a waiter who keeps forgetting orders and running back to the kitchen.</p><p>TPU 8i has <strong>3x more on-chip SRAM (384 MB)</strong>. The entire conversation stays on the chip. No pausing. No fetching. Just flow.</p><p><strong>2. A brand new engine for thinking fast</strong></p><p>AI agents that reason — the kind that think step by step before answering — constantly need all their cores to synchronize with each other. On old chips, this synchronization was a bottleneck.</p><p>Google replaced the old system with something called the <strong>Collectives Acceleration Engine (CAE)</strong>. It handles all that synchronization with near-zero latency. The result: <strong>5x faster on-chip communication.</strong> For an agent running a complex chain-of-thought, this is the difference between feeling instant and feeling sluggish.</p><p><strong>3. A completely new way chips talk to each other</strong></p><p>Imagine a city where every road goes through the town square. That was the old network design — a 3D grid where messages between chips could take up to <strong>16 hops</strong> to arrive.</p><p>Google redesigned the entire road system with something called <strong>Boardfly</strong>. It’s a hierarchical network — small groups of chips fully connected to each other, then connected to bigger groups through optical switches. The longest any message has to travel? <strong>7 hops.</strong> A 56% reduction.</p><p>For AI agents using modern architectures like Mixture-of-Experts — where different parts of the model need to collaborate constantly — this is transformational.</p><p>The combined result of all three changes: <strong>80% better price-performance for inference</strong> over the previous generation.</p><h3>By the Numbers</h3><p>TPU 8tTPU 8iBuilt forTrainingInference &amp; AgentsChips per system9,6001,152On-chip SRAM128 MB384 MBMemory (HBM)216 GB288 GBNetwork design3D TorusBoardfly (7 hops max)Key innovationTPUDirect StorageCAE (5x latency cut)Performance gain2.7x over Ironwood80% better price-performance</p><h3>And Then Google Did Something It Has Never Done Before</h3><p>For ten years, TPUs were Google’s private weapon. You could use them on Google Cloud — but you couldn’t own one.</p><p>That just changed.</p><p>Google announced it will begin <strong>selling TPUs directly</strong> to select customers — AI labs, financial institutions, and high-performance computing organizations — to run inside their own data centers.</p><p>The secret weapon is now a product.</p><h3>Why This Moment Matters</h3><p>The split of TPU into 8t and 8i isn’t just a hardware story. It’s Google saying out loud what engineers have known quietly for years:</p><p><em>Training AI and running AI are two fundamentally different problems. It’s time to stop pretending one chip can solve both.</em></p><p>As the world moves deeper into the agent era — where AI systems don’t just respond but reason, plan, and act — the infrastructure underneath has to evolve too. Purpose-built beats general-purpose. Every time.</p><p>Both TPU 8t and TPU 8i arrive on Google Cloud later in 2026.</p><p>The kitchen has been split. The restaurant is ready for scale.</p><h3><strong>Sources:</strong></h3><ul><li><a href="https://cloud.google.com/blog/products/compute/tpu-8t-and-tpu-8i-technical-deep-dive?utm_campaign=deveco_gdemembers&amp;utm_source=deveco">TPU 8t and TPU 8i Technical Deep Dive — Google Cloud Blog</a></li><li><a href="https://blog.google/innovation-and-ai/sundar-pichai-io-2026/?utm_campaign=deveco_gdemembers&amp;utm_source=deveco">Sundar Pichai’s Google I/O 2026 Keynote</a></li><li><a href="https://blog.google/innovation-and-ai/infrastructure-and-cloud/google-cloud/cloud-next-2026-sundar-pichai/?utm_campaign=deveco_gdemembers&amp;utm_source=deveco">Google Cloud Next 2026 — Sundar Pichai</a></li></ul><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=a92d3d32a6a8" width="1" height="1" alt=""><hr><p><a href="https://medium.com/google-developer-experts/tpu-just-got-split-in-two-and-it-changes-everything-about-ai-infrastructure-a92d3d32a6a8">TPU Just Got Split in Two — and It Changes Everything About AI Infrastructure</a> was originally published in <a href="https://medium.com/google-developer-experts">Google Developer Experts</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Flutter at Google I/O 2026 ]]></title>
            <link>https://medium.com/google-developer-experts/flutter-at-google-i-o-2026-a02cc82097ae?source=rss----a67bd6fa7d58---4</link>
            <guid isPermaLink="false">https://medium.com/p/a02cc82097ae</guid>
            <category><![CDATA[firebase]]></category>
            <category><![CDATA[flutter-widget]]></category>
            <category><![CDATA[google]]></category>
            <category><![CDATA[flutter]]></category>
            <category><![CDATA[flutter-app-development]]></category>
            <dc:creator><![CDATA[Abhishek Doshi]]></dc:creator>
            <pubDate>Tue, 26 May 2026 02:25:58 GMT</pubDate>
            <atom:updated>2026-05-26T02:25:57.162Z</atom:updated>
            <content:encoded><![CDATA[<h4>Forget everything you knew about cross-platform constraints. The rules just changed!</h4><p>The dust has finally settled on Google I/O 2026, and if I’m being completely honest, the Flutter and Dart announcements this year hit differently. We’ve moved well past the era of just trying to achieve multi-platform parity. This year’s roadmap is fundamentally about shifting how we architect applications, integrate AI natively, and squeeze every drop of performance out of the rendering engine.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*EN5WixR-doRj9r-B.png" /><figcaption>Image copyright to respective owner</figcaption></figure><p>Here is a deep dive into the most significant architectural and workflow changes coming to the ecosystem.</p><h3>1. The GenUI Revolution &amp; Ephemeral Code Delivery</h3><p>The most futuristic drop this year is the aggressive push toward “agentive UIs.” We aren’t just shipping static screens anymore. With the introduction of the Flutter GenUI SDK and the A2UI protocol, the framework is paving the way for AI models to dynamically generate and adapt rich user experiences on the fly based on user intent.</p><p>But the technical enabler behind this is what’s truly mind-bending: Dart is investigating support for interpreted bytecode within the Dart runtime. This unlocks the ability to deliver “ephemeral” code. Imagine loading highly specific, dynamic portions of your UI on-demand without forcing a full app store update. It’s a massive leap forward for building interfaces that need to adapt in real time.</p><h3>2. Agent Skills and Local MCP Integration</h3><p>Forget copying and pasting snippets from a browser window to fix a bug. The debut of Agent Skills for Dart and Flutter, paired with the open Model Context Protocol (MCP), brings the AI directly into the local environment.</p><p>Your AI assistant can now hook directly into your local Dart SDK analyzer with zero configuration. Because it understands your exact project context, custom types, and widget tree, it can execute deep architectural refactoring, validate type safety, and even run native test suites right on your machine with complete semantic accuracy.</p><h3>3. The Universal Canvas and Unlocking Pure Design</h3><p>This is the architectural shift that is arguably the most exciting for custom product development. Flutter is officially pulling the Material and Cupertino design libraries out of the core flutter/flutter repository. Moving forward, these are treated as unopinionated, independently versioned standalone packages on pub.dev.</p><p>By doing this, the core engine transforms into an incredibly fast, lightweight <strong>Universal Rendering Canvas</strong>. We no longer have to fight the default Material scaffolding. If you are building high-end, minimalist interfaces, those Awwwards-winning bento-grid layouts that rely heavily on perfect negative space and sleek, Apple-style aesthetics, you now have a truly blank, unopinionated engine to paint on.</p><h3>4. The Pure Impeller Era &amp; Lightning-Fast DevTools</h3><p>On the performance front, the multi-year <strong>Impeller</strong> saga is finally reaching its conclusion. The legacy Skia backend is officially being stripped out for Android 10 and above. We are now entering the era of pure Impeller Vulkan rendering on modern Android, which means predictable, jank-free animations and faster startup-to-interaction times across the board.</p><p>Tooling also got a massive upgrade. The entire Flutter DevTools suite is now compiled to <strong>WasmGC</strong> by default. That annoying stutter when analyzing performance? It’s gone. They’ve shaved off over 200ms of lag during telemetry parsing, making the debugging and profiling experience feel completely fluid.</p><h3>5. The Firebase Intelligence Layer</h3><p>Finally, for backend management and infrastructure, the Firebase integration just got significantly smarter. Firebase expanded its Agent Skills to explicitly cover Flutter, iOS, and Android environments.</p><p>When you rely heavily on Firebase to architect and deploy your backends, having an AI that actually understands the nuances of your specific environment is a game-changer. It gives local coding agents the specialized context needed to handle complex Firebase integrations flawlessly, cutting down on token usage and practically eliminating infrastructure hallucinations.</p><h3>The Takeaway</h3><p>Flutter is no longer just a cross-platform UI toolkit; it’s evolving into a comprehensive, full-stack, AI-native development ecosystem. The framework is getting leaner at its core, the tooling is getting exponentially smarter, and the ceiling for what we can build just got pushed a whole lot higher.</p><h3>Hope you enjoyed this article!</h3><p>Doubts? Feel free to drop a message <a href="https://linktr.ee/abhishekdoshi26">@AbhishekDoshi26</a><br>Checkout <a href="https://abhishekdoshi.dev/">abhishekdoshi.dev</a> for more info 💙</p><blockquote><em>Don’t stop, until you are breathing!💙<br>- Abhishek Doshi</em></blockquote><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=a02cc82097ae" width="1" height="1" alt=""><hr><p><a href="https://medium.com/google-developer-experts/flutter-at-google-i-o-2026-a02cc82097ae">Flutter at Google I/O 2026 💙</a> was originally published in <a href="https://medium.com/google-developer-experts">Google Developer Experts</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[[Mar-Apr 2026] AI Community — Activity Highlights and Achievements]]></title>
            <link>https://medium.com/google-developer-experts/mar-apr-2026-ai-community-activity-highlights-and-achievements-154ff3a85db3?source=rss----a67bd6fa7d58---4</link>
            <guid isPermaLink="false">https://medium.com/p/154ff3a85db3</guid>
            <dc:creator><![CDATA[Nari Yoon]]></dc:creator>
            <pubDate>Mon, 18 May 2026 02:50:49 GMT</pubDate>
            <atom:updated>2026-05-18T02:54:13.273Z</atom:updated>
            <content:encoded><![CDATA[<p>We love sharing the accomplishments of the Google AI communities over the month. We appreciate all the hard work and dedication of our community members. Without further ado, here are the key highlights by products!</p><h3>Antigravity</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/630/0*hhYjUnGBD_TJXgA-.jpeg" /><figcaption><a href="https://medium.com/google-cloud/confused-about-where-to-put-your-agent-skills-ea778f3c64f3">image source</a></figcaption></figure><p><a href="https://medium.com/google-cloud/confused-about-where-to-put-your-agent-skills-ea778f3c64f3">Confused About Where to Put Your Agent Skills?</a> by Cloud GDE Darren Lester (UK) explains where agentic tools look for skills and highlights the confusion caused by differing locations. He proposes using symlinks to maintain a single source of truth at ~/.agents/skills as a future-proof solution.</p><p><a href="https://dev.to/gde/how-agentic-ai-resurrected-my-old-side-project-31hf">How agentic AI resurrected my “Old” side project</a> by Cloud GDE Jean-Philippe BACONNAIS (France) shares how Antigravity helped revive a dormant genealogy tree application by migrating legacy code to Quarkus and improving the UI. Facilitating tasks like documentation updates and UI harmonization allows the author to focus on new features and improvements.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1000/0*CFy8cf4CyhuPwpC3.png" /><figcaption><a href="https://dev.to/gde/taking-action-on-your-gcp-bill-automating-bigquery-storage-cleanup-4nma">image source</a></figcaption></figure><p><a href="https://dev.to/gde/taking-action-on-your-gcp-bill-automating-bigquery-storage-cleanup-4nma">Taking Action on your GCP bill: Automating BigQuery Storage Cleanup</a> by Cloud GDE Marcelo Costa (Brazil) explains how to automate BigQuery storage cleanup using a bash script and Antigravity’s agentic workflow to reduce costs.</p><p><a href="https://dev.to/gdg/teaching-gemini-to-scale-your-workflow-in-anti-gravity-om2">Scaling your productivity with spec docs in your IDE — Anti Gravity.</a> by Angular GDE Matthew Christiansen (US) discusses improving developer productivity by using specification documents and Anti Gravity within the IDE. It suggests treating prompts as configuration through modular .md files to reduce mental overhead and create a scalable development process.</p><h3>Gemini CLI</h3><p><a href="https://www.crownpku.com/2026/04/26/Build-My-Digital-Twin-with-LLM-wiki.html">How I Distilled 27k Lines of AI Chat History into a Local LLM Wiki</a> by AI GDE Guan Wang (Singapore) describes an experiment using the Gemini CLI to distill extensive Gemini chat history into a structured, localized AI. The resulting wiki acts as a personalized assistant summarizing facts, analyzing workflows, and identifying problem-solving habits.</p><p><a href="https://medium.com/google-cloud/documentation-as-context-a-skill-to-automate-your-blueprints-for-the-agentic-era-2bec0cf041a3">Documentation as Context: A Skill to Automate Your Blueprints for the Agentic Era</a></p><p><a href="https://medium.com/p/2bec0cf041a3">Documentation as Context: A Skill to Automate Your Blueprints for the Agentic Era</a> by Cloud GDE Darren Lester (UK) introduces an agent skill called project-documentation skill designed to automate and standardize documentation practices for software projects, especially in the context of AI agents. It emphasizes the importance of up-to-date documentation for both human developers and AI agents, detailing how the skill helps maintain READMEs, architecture documents, UI design guides, and testing documentation.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/800/1*P24guXmxZ8mwqhz0od34Mg.png" /><figcaption><a href="https://dev.to/polar3130/using-gemini-cli-with-a-local-llm-5f5l">image source</a></figcaption></figure><p><a href="https://dev.to/polar3130/using-gemini-cli-with-a-local-llm-5f5l">Using Gemini CLI with a Local LLM</a> by Cloud GDE Masahiko Utsunomiya (Japan) details how to configure Gemini CLI to use a local LLM backend by combining LiteLLM Proxy and Ollama. It provides a practical guide to redirect API requests and address potential issues like missing model aliases.</p><h3>Gemini</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1000/0*7yqln6kkRF5v4Bi3" /><figcaption><a href="https://dev.to/gde/decoding-bronze-age-paperwork-modern-ai-vs-ancient-assyrian-clay-tablets-5adf">image source</a></figcaption></figure><p>[Hot 👏] <a href="https://dev.to/gde/decoding-bronze-age-paperwork-modern-ai-vs-ancient-assyrian-clay-tablets-5adf">Decoding Bronze Age Paperwork: Modern AI vs. Ancient Assyrian Clay Tablets</a> by AI GDE Ertuğrul Demir (Türkiye) explores the use of a modern AI stack to translate ancient Assyrian clay tablets by building an end-to-end pipeline for OCR and translation. highlights the effectiveness of ByT5 for unique character sets and Gemini for scalable data extraction.</p><p><a href="https://colab.research.google.com/drive/1XDnY2InFiE_UNNyHOoN7NtbZKIKsYVqY?usp=sharing#scrollTo=pawn3NfG86FO">Gemini Embedding 2 — Complete Guide</a> by AI GDE Pedro Lourenço (Brazil) is a Colab notebook providing a hands-on guide to Gemini Embedding 2 capabilities, covering word-vector arithmetic and cross-modal searches.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/720/0*dq5qB16bLmlHFN0g" /><figcaption><a href="https://medium.com/google-cloud/integrate-notebooklm-with-gemini-cli-google-antigravity-or-other-agents-with-mcp-cd83b575dc39">image source</a></figcaption></figure><p>[Hot 👏] <a href="https://medium.com/google-cloud/integrate-notebooklm-with-gemini-cli-google-antigravity-or-other-agents-with-mcp-cd83b575dc39">Integrate NotebookLM with Gemini CLI, Google Antigravity or Other Agents with MCP</a> by Cloud GDE Darren Lester (UK) explores integrating NotebookLM with AI tools using MCP to enable seamless interaction between models and external tools. It guides how to set up the MCP server and uses natural language commands to manage notebooks within other AI environments.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1000/0*w0pukoN6r42d5OIr" /><figcaption>AI GDE Victor Ashioya (Kenya)</figcaption></figure><p>[Workshop] <a href="https://gdg.community.dev/events/details/google-gdg-nairobi-presents-build-with-ai-nairobi-2026/">Building a Gemini-level Model from Scratch</a> (<a href="https://github.com/ashioyajotham/gemini-from-scratch">repository</a>) by AI GDE Victor Ashioya (Kenya) demystifies the architecture and training of SOTA language models through a hands-on workshop building a transformer-based model. The session shared the evolution of multimodal giants and the core components enabling breakthrough performance via code examples and demonstrations.</p><h3>ADK</h3><p><a href="https://medium.com/@simon3458/google-adk-agent-skill-intro-2eeecf69ed4f">Saying Goodbye to Lengthy Prompts: A Practical Analysis of Google ADK Agent Skill Features</a> by Cloud GDE Yu-wei Liu (Taiwan) introduces the Agent Skill feature in ADK v1.25.0, exploring its architecture, implementation, and benefits like reduced context burden. It discusses advantages such as modularity and team collaboration while addressing potential challenges like metadata quality and dynamic loading latency.</p><p>#Gemma 4 <a href="https://medium.com/google-developer-experts/tutorial-running-googles-gemma-4b-locally-with-google-adk-and-dual-a40-gpus-5a421fbc6ef9">Running Google’s Gemma-4b locally with Google ADK and dual A40 GPUs</a> by Cloud GDE Ashmi Banerjee (Germany) guides you through running Gemma-4b locally using ADK and dual A40 GPUs covering environment setup, vLLM serving, and LiteLLM integration.</p><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fwww.youtube.com%2Fembed%2FoqqnUSYe558%3Ffeature%3Doembed&amp;display_name=YouTube&amp;url=https%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3DoqqnUSYe558&amp;image=https%3A%2F%2Fi.ytimg.com%2Fvi%2FoqqnUSYe558%2Fhqdefault.jpg&amp;type=text%2Fhtml&amp;schema=youtube" width="854" height="480" frameborder="0" scrolling="no"><a href="https://medium.com/media/441b0c30781626a495f91b3383e15039/href">https://medium.com/media/441b0c30781626a495f91b3383e15039/href</a></iframe><p><a href="https://medium.com/@mazlum.tosun/end-to-end-ai-agent-on-gcp-adk-bigquery-mcp-agent-engine-and-cloud-run-4843fec27c13">End-to-End AI Agent on GCP: ADK, BigQuery MCP, Agent Engine, and Cloud Run</a> (<a href="https://github.com/tosun-si/football-agent-adk">repository</a> | <a href="https://youtu.be/oqqnUSYe558">video</a>) by Cloud GDE Mazlum Tosun (France) explains how to build and deploy an AI agent that queries BigQuery using natural language via ADK and Gemini 2.5 Flash without managing MCP servers.</p><h3>Gemma</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*stMGoW-zS7YpSvzB" /></figure><p><a href="https://www.datacamp.com/tutorial/gemma-4-tutorial">Gemma 4 Tutorial: Build a Local AI Coding Agent with Gradio and Ollama</a> by AI GDE Aashi Dutt (India) explains how to build a local, multimodal AI coding assistant using Gemma 4, Ollama, and Gradio with agentic tool use.</p><p><a href="https://medium.com/google-developer-experts/serve-and-inference-gemma-4-on-tpu-ae7da212ddd4">Serve and Inference Gemma 4 on TPU</a> by AI GDE Nitin Tiwari (India) explains how to deploy and run inference with Gemma 4 on TPUs using vLLM. It demonstrates setting up a TPU v6e instance and using a frontend application to achieve significantly lower latency compared to traditional GPUs.</p><p><a href="https://medium.com/towards-artificial-intelligence/running-gemma-4-e2b-on-cpu-is-local-ai-finally-practical-3939583764f7">Running Gemma 4 E2B on CPU: Is Local AI Finally Practical?</a> (<a href="https://www.kaggle.com/code/gpreda/test-gemma-4-e2b-on-cpu?source=post_page-----3939583764f7---------------------------------------">Kaggle notebook</a>) by AI GDE Gabriel Preda (Romania) tests Gemma 4 E2B on a no-GPU setup to evaluate performance, limitations, and real-world usability. Gabriel also shared <a href="https://medium.com/@gabi.preda/from-oom-errors-to-working-model-fine-tuning-gemma-4-e2b-step-by-step-using-unsloth-ef7873e59efd">From OOM Errors to Working Model: Fine-Tuning Gemma 4 E2B Step-by-Step using Unsloth</a> (<a href="https://www.kaggle.com/code/gpreda/fine-tune-gemma-4-e2b-with-unsloth?source=post_page-----ef7873e59efd---------------------------------------">Kaggle notebook</a>) for a limited hardware environment.</p><p><a href="https://implicit-none.com/p/vllm-tpu-gpu-benchmark-2026-april-en/">TPU v6e vs A100 80GB×2 for Gemma 4 31B on vLLM: 21 Benchmarks Show When Each Wins</a> by AI GDE Sho Tanaka (Japan) compares the performance of TPU v6e-4 and NVIDIA A100 across 21 input/output profiles for serving Gemma 4 31B. It demonstrates that TPU excels in short profiles and TPOT latency, while A100 performs better in medium and long profiles.</p><p><a href="https://dev.to/gde/deploy-gemma-4-on-cloud-run-pay-only-when-you-actually-use-it-9ln">Deploy Gemma 4 on Cloud Run: Pay Only When You Actually Use It</a> by Cloud GDE Daniel Gwerzman (UK) details how to deploy Gemma 4 on Cloud Run to leverage scale-to-zero capabilities and covers improvements like reasoning and function calling.</p><p><a href="https://rkrants.blogspot.com/2026/04/the-gemma-4-e2b-fine-tuning-cookbook.html">The Gemma 4 E2B Fine-Tuning Cookbook</a> by AI GDE Rabimba Karanjai (US) provides a complete recipe for adapting Gemma 4 E2B to a specific domain by covering dataset construction, QLoRA configuration, and production deployment.</p><p><a href="https://www.crownpku.com/2026/04/21/Tunix-Gemma4-Insurance.html">Taming the Giant: Fine-Tuning Gemma 4 E2B-IT into an Insurance Expert</a> by AI GDE Guan Wang (Singapore) details the process of fine-tuning Gemma 4 E2B-IT using Tunix on TPU v5litepod-4 to create a high-precision insurance advisor. It covers overcoming memory limitations, data engineering with InsuranceQA-v2, and LoRA application to improve accuracy.</p><h4><strong>Fine-tuning Gemma</strong></h4><p><a href="https://medium.com/p/419902c3fa39">Beyond Classification Labels: Fine-Tuning Gemma 3 1B-IT with Financial Reasoning (part 1</a>, <a href="https://medium.com/@lucamassaron/beyond-classification-labels-fine-tuning-gemma-3-1b-it-with-financial-reasoning-part-2-2-ac2cd71d7063">part 2</a>) by AI GDE Luca Massaron (Italy) discusses fine-tuning for financial sentiment analysis using reasoning-augmented data from teacher-student distillation.</p><p><a href="https://medium.com/google-cloud/post-training-gemma-3-for-earth-observation-eo-understanding-a-jax-stack-tpu-pipeline-for-cc26da11e338">Post-Training Gemma 3 for Earth Observation (EO) Understanding: A JAX Stack + TPU Pipeline for Multi-Label Sentinel Satellite Remote Sensing Scene Classification</a> (<a href="https://github.com/OpenSciML/gemma_earth">repository</a>) by AI GDE Henry Ruiz (US) introduces a domain-focused post-training and benchmarking pipeline for adapting Gemma 3 4B IT to Earth Observation tasks using a TPU-native JAX stack.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1000/0*eHHbefZFAj21ccs7" /><figcaption>AI GDE Luca Massaron (Italy)</figcaption></figure><p>[Workshop] <a href="https://www.meetup.com/gdg-polimi/events/313848263/">Fine-tuning your AI</a> by AI GDE Luca Massaron (Italy) was a hands-on workshop at HQ in Milan on fine-tuning Gemma models through a complete QLoRA pipeline on Colab. It covers from baseline evaluation, synthetic data generation, fine-tuning to measuring improvement.</p><h3>JAX &amp; TPU</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/720/0*IMl49r1Vx5VO6MbQ" /><figcaption><a href="https://medium.com/@JunbumLee/dllm-into-tpu-an-end-to-end-diffusion-lm-stack-in-pure-jax-5fc33c840ebb">image source</a></figcaption></figure><p>[Featured on TPU Developer Hub ✨] <a href="https://medium.com/@JunbumLee/dllm-into-tpu-an-end-to-end-diffusion-lm-stack-in-pure-jax-5fc33c840ebb">dLLM into TPU: An End-to-End Diffusion LM Stack in Pure JAX</a> (<a href="https://github.com/Beomi/dllm-jax">repository</a>) by AI GDE Junbum Lee (Korea) shares a standalone JAX backend for dLLM with zero PyTorch or CUDA dependency.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/800/0*qeHpiDSdMomYAzr8" /><figcaption><a href="https://dev.to/gde/run-any-huggingface-model-on-tpus-a-beginners-guide-to-torchax-4ln0">image source</a></figcaption></figure><p><a href="https://dev.to/gde/run-any-huggingface-model-on-tpus-a-beginners-guide-to-torchax-4ln0">Run Any HuggingFace Model like Gemma3 on TPUs: A Beginner’s Guide to TorchAX</a> (<a href="https://github.com/agemagician/torchax-huggingface">repository</a>) by AI GDE Ahmed Elnaggar (Germany) guides on running HuggingFace models on TPUs using TorchAX to leverage JAX’s high performance without rewriting code. Ahmed also shared a follow-up tutorial, <a href="https://dev.to/gde/fine-tune-any-huggingface-model-like-gemma-on-tpus-with-torchax-5g21">Fine-Tune Any HuggingFace Model like Gemma on TPUs with TorchAX</a>.</p><p><a href="https://medium.com/@joansantoso/loading-and-transform-your-dataset-using-grain-for-model-building-in-jax-and-flax-7d9409cba76e">Loading and Transform your Dataset using Grain for Model Building in JAX/FLAX</a> by AI GDE Joan Santoso (Indonesia) introduces Grain and demonstrates building a sentiment analysis model in JAX/Flax. It covers creating custom data sources and transformations to build efficient data loaders for training.</p><p><a href="https://medium.com/google-developer-experts/building-a-nano-mixture-of-experts-moe-language-model-in-jax-from-scratch-4b184298ee19">Building a Nano MoE Language Model in JAX from Scratch</a> by AI GDE Kartikey Rawat (India) provides a deep dive into the mechanics and importance of MoEs and demonstrates how to build a model using pure JAX/Flax.</p><p><a href="https://kambale.dev/flax-nnx">Building Neural Networks with Flax NNX</a> by AI GDE Wesley Kambale (Uganda) introduces NNX and covers building a CNN for image classification using production-grade architectures.</p><p><a href="https://xprilion.com/gemma3-vllm-tpu-gke-autoscaling/">Autoscaling LLM Inference on GKE with TPU v5e and vLLM</a> by AI GDE Anubhav Singh (India) shares a practical guide on deploying and autoscaling LLM inference using vLLM on GKE with TPU v5e with details of quota management, capacity planning, and etc.</p><p><a href="https://implicit-none.com/p/jax-09-intro-guide/">A guide to speeding up and vectorizing for NumPy users</a> by AI GDE Sho Tanaka (Japan) provides an introductory guide for NumPy users to speed up and vectorize code using JAX. Key features: PRNG differences, jax.jit compilation, jax.vmap vectorization, and automatic differentiation.</p><p><a href="https://www.linkedin.com/feed/update/urn:li:activity:7455591258112040960/">Benchmarking TPUs for Search Problems</a> (<a href="https://github.com/VikramTiwari/jax-search-benchmark">repository</a>) by AI GDE Vikram Tiwari (US) benchmarks TPUs for search problems using JAX and Antigravity. He creates scripts to identify TPU resources and uses YAML-based experiment setups to perform searches on public datasets.</p><h4>Pallas</h4><p>[Featured on TPU Developer Hub ✨] <a href="https://huggingface.co/blog/ariG23498/pallas-for-beginners">Pallas for people who know JAX but not kernels yet</a> by AI GDE Aritra Roy Gosthipaty (India) introduces and explores Pallas to write custom, high-performance kernels within the JAX ecosystem. It demonstrates how Pallas abstracts hardware complexities, simplifying the process of writing optimized, hardware-level code.</p><p><a href="https://rishirajacharya.com/fused-int8-weight-only-quantization-in-pallas/">Fused INT8 Weight-Only Quantization in Pallas</a> by AI GDE Rishiraj Acharya (India) shares how he wrote a custom JAX/Pallas kernel for INT8 weight-only quantization to accelerate LLM text generation by streaming compressed weights directly into local SRAM. This approach doubles memory efficiency by decompressing weights on the fly within hardware registers to avoid main memory bottlenecks while maintaining the codebase in Python. Rishiraj also shared two practical guides:</p><ul><li><a href="https://rishirajacharya.com/block-sparse-attention-kernel-via-jaxpallas/">Block-Sparse Attention Kernel via JAX/Pallas</a>: how to build a custom Block-Sparse Attention kernel in JAX/Pallas to fix the massive memory and compute bottlenecks that happen when LLMs process long text.</li><li><a href="https://rishirajacharya.com/ring-attention-sequence-sharding-with-jax/">Ring Attention &amp; Sequence Sharding with JAX</a>: hands-on deep dive into overcoming the KV cache memory bottlenecks of million-token context windows using Ring Attention on TPUs.</li></ul><p><a href="https://blog.keshan.dev/Profiling-TPU-kernels/">Profiling TPU Kernels — XProf, HLO, and the Roofline Model</a> by AI GDE Keshan Sodimana (Sri Lanka) provides a systematic protocol for diagnosing TPU kernel performance bottlenecks using XProf, HLO, and the roofline model. It covers capturing traces and decoding compiler output to optimize custom kernels in Python. Keshan also shared <a href="https://blog.keshan.dev/The-Ratchet-Loop/">The Ratchet Loop: Optimizing a TPU Kernel</a> abo how to optimize a TPU kernel to the hardware ceiling using the Ratchet Loop for reproducibility and regression prevention.</p><h4><strong>Fine-tuning Gemma using JAX and TPU</strong></h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/700/0*_LDd3rSpYFlj_Hza.png" /><figcaption><a href="https://kuanhoong.medium.com/fine-tuning-gemma-2b-on-pubmedqa-building-a-medical-q-a-assistant-with-lora-keras-kinetic-and-0e6eaf566834">image source</a></figcaption></figure><p>[Featured on TPU Developer Hub ✨] <a href="https://medium.com/@kuanhoong/fine-tuning-gemma-2b-on-pubmedqa-building-a-medical-q-a-assistant-with-lora-keras-kinetic-and-0e6eaf566834">Fine-Tuning Gemma 2B on PubMedQA: Building a Medical Q&amp;A Assistant with LoRA, Keras Kinetic, and Cloud TPU</a> (<a href="https://github.com/kuanhoong/keras-kinetic">repository</a>) by AI GDE Kuan Hoong Poo (Malaysia) details fine-tuning Gemma 2B using Keras Kinetic to automate cloud infrastructure and TPU provisioning.</p><p><a href="https://medium.com/@lucamassaron/building-a-cardiology-assistant-synthetic-data-and-jax-based-fine-tuning-cfd5890afbb6">Building a Cardiology Assistant: Synthetic Data and JAX-Based Fine-Tuning</a> by AI GDE Luca Massaron (Italy) demonstrates building a specialized cardiology assistant using a compact model and an efficient JAX/Tunix pipeline. It covers synthetic data generation, fine-tuning, and evaluation to showcase domain adaptation without requiring massive models and datasets.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*i9QsvORnvktDd2EJ" /><figcaption><a href="https://rkrants.blogspot.com/2026/03/write-once-scale-everywhere.html">image source</a></figcaption></figure><p><a href="https://rkrants.blogspot.com/2026/03/write-once-scale-everywhere.html">Write Once, Scale Everywhere</a> by AI GDE Rabimba Karanjai (US) presents an end-to-end pipeline for fine-tuning Gemma 2B using LoRA and serving it via a custom REST API. It utilizes KerasNLP and JAX as a backend to enable flexible execution on both NVIDIA GPUs and Cloud TPUs while demonstrating performance gains through XLA compilation.</p><p><a href="https://medium.com/@ayehninnkhine/fine-tuning-gemma-3-on-burmese-agriculture-qa-dataset-tpu-tunix-lora-c7d39d06f9e0">Fine-tuning Gemma 3 on Burmese Agriculture QA Dataset (TPU + Tunix + LoRA)</a> (<a href="https://github.com/ayehninnkhine/TPU_Sprint_2026">repository</a>) by AI GDE Aye Hninn Khine (Thailand) focuses on fine-tuning Gemma 3 on a Burmese agriculture dataset using TPUs and LoRA.</p><h4>Implementations &amp; Tools in JAX</h4><ul><li><a href="https://github.com/AakashKumarNain/jaxgpt">jaxgpt: Building LLMs in JAX and TPUs</a> by AI GDE Aakash Nain (India): a showcase of how to build and train scalable LLMs in pure JAX using a multi-host environment</li><li><a href="https://github.com/sayakpaul/qwenimage-ptxla/">QwenImage Inference on TPU with PyTorch/XLA</a> by AI GDE Sayak Paul (India): PyTorch/XLA based SPMD implementation of the QwenImage image-gen pipeline to run on TPU v6e</li><li><a href="https://github.com/bzantium/google-smi">google-smi</a> &amp; <a href="https://github.com/bzantium/tpustat">tpustat</a> by AI GDE Minho Ryu (Korea): a TPU-oriented status CLI in the style of nvidia-smi and the TPU equivalent of the gpustat workflow</li><li><a href="https://github.com/bzantium/megatext">MegaText</a> by AI GDE Minho Ryu (Korea): a streamlined pretraining framework for LLMs on TPUs (built on JAX, inspired by MaxText)</li><li><a href="https://github.com/mlnomadpy/flaxchat">flaxchat</a> by AI GDE Taha Bouhsine (US): a minimal, end-to-end LLM training harness for TPU pods (built on JAX/Flax NNX)</li><li><a href="https://github.com/xprilion/gemma3-vllm-tpu-gke-autoscaling">gemma3-vllm-tpu-gke-autoscaling</a> by AI GDE Anubhav Singh (India): a deployment guide covering quota management, capacity planning, model compatibility, and HPA-based autoscaling for vLLM on GKE with TPU.</li><li><a href="https://github.com/ushareng/Multi-Agent-Security-Scanner-using-NVIDIA-NIM">Code Auditor</a> by AI GDE Usha Rengaraju (India): LLM-powered code security auditing tool using NVIDIA NIM + free open-source safety stack</li></ul><h4>TPU vs. GPU</h4><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fwww.youtube.com%2Fembed%2Fz-JrTiNIorE%3Ffeature%3Doembed&amp;display_name=YouTube&amp;url=https%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3Dz-JrTiNIorE&amp;image=https%3A%2F%2Fi.ytimg.com%2Fvi%2Fz-JrTiNIorE%2Fhqdefault.jpg&amp;type=text%2Fhtml&amp;schema=youtube" width="854" height="480" frameborder="0" scrolling="no"><a href="https://medium.com/media/a268b96ca3fb987997e367545008738a/href">https://medium.com/media/a268b96ca3fb987997e367545008738a/href</a></iframe><p><a href="https://www.youtube.com/watch?v=z-JrTiNIorE">GPU vs TPU: Which one to use for Artificial Intelligence?</a> by AI GDE Carlos Alarcon (Colombia) explains criteria for choosing between GPUs and TPUs for ML projects through real-world tests on Colab. It highlights specific use cases such as inference, self-attention, and massive matrix multiplication to help users optimize hardware selection for efficiency and cost.</p><p><a href="https://medium.com/google-developer-experts/ml-acceleration-guide-tpus-vs-gpus-95a4ac995e9d">ML acceleration guide: TPUs vs GPUs</a> by AI GDE Glen Yu (Canada) compares TPUs and GPUs for ML acceleration by detailing architectures, precision types, and XLA’s importance. He also shares a code example to demonstrate performance differences between the hardware types.</p><h3>Keras</h3><p>[kinetic doc] <a href="https://kinetic.readthedocs.io/en/latest/examples/gemma4_finetuning.html">Fine-tuning Gemma 4 on TPU with Kinetic</a> by AI GDE Adonai Vera (Colombia) details how to fine-tune Gemma 4 Instruct 26B on a TPU using Kinetic and LoRA for memory efficiency. It outlines the process for environment setup, weight storage in GCS, and performing inference with the fine-tuned model. He also contributed to the Keras ecosystem by <a href="https://github.com/keras-team/kinetic/pull/173">adding — reservation flag to kinetic pool</a>.</p><p>[keras.io] <a href="https://keras.io/keras_rs/examples/contextual_retrieval_yambda/">Scaling Context-Aware Two-Tower Music Retrieval via JAX Data Parallelism and Keras 3 on TPUs</a> by AI GDE Rishiraj Acharya (India) introduces a production-ready KerasRS implementation for context-aware music retrieval using a dynamic Two-Tower model and the Yambda dataset.</p><p><a href="https://jigyasa-grover.github.io/KineticFinetuningOnCloudTPU/">In my Kinetic era — Fine-tuning Gemma 3 to speak Gen Z on a Cloud TPU with one decorator</a> by AI GDE Jigyasa Grover (US) demonstrates supervised fine-tuning for Gen Z style transfer using Gemma 3 1B and TPU v5 Lite. It simplifies deployment to GKE via Kinetic while utilizing Keras Hub/Kaggle for model management.</p><h3>On-device ML</h3><p>#Gemma 4 <a href="https://medium.com/@carrycooldude/bringing-multimodal-gemma-4-e2b-to-the-edge-a-deep-dive-into-litert-lm-and-qualcomm-qnn-4e1e06f3030c">Bringing Multimodal Gemma 4 E2B to the Edge: A Deep Dive into LiteRT-LM and Qualcomm QNN</a> by AI GDE Kartikey Rawat (India) explores the deployment of Gemma 4 E2B on Android devices using LiteRT-LM and Qualcomm QNN for NPU acceleration. It details architectural innovations and engineering changes required for production-ready on-device inference.</p><p><a href="https://medium.com/@gabi.preda/running-gemma-4-e2b-on-android-a-minimal-kotlin-app-4272609bedc9">Running Gemma 4:E2B on Android: A Minimal Kotlin App</a></p><p>#Gemma 4 <a href="https://medium.com/@gabi.preda/running-gemma-4-e2b-on-android-a-minimal-kotlin-app-4272609bedc9">Running Gemma 4:E2B on Android: A Minimal Kotlin App</a> (<a href="https://github.com/gabrielpreda/kotlin-gemma-4e2b-chatbot">repository</a>) by AI GDE Gabriel Preda (Romania) details a minimal Android chatbot developed in Kotlin using Gemma 4:E2B for fully offline local inference. It outlines key implementation steps such as UI creation, LiteRT-LM integration, and Markdown support.</p><h3>Cloud</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/512/0*IRKrYeKGixUXd5__" /><figcaption><a href="https://medium.com/@rubenszimbres/building-a-healthcare-recommender-with-keras-two-towers-and-google-cloud-4f535ad36dcd">image source</a></figcaption></figure><p><a href="https://medium.com/p/4f535ad36dcd">Building a Healthcare Recommender with Keras, Two Towers and Google Cloud</a> by AI GDE Rubens Zimbres (Brazil) shares how he built the Prescription Recommender app, which takes plain language symptoms as input, identifies likely diseases, and suggests medications, diets, and workouts.</p><p><a href="https://timtech4u.medium.com/building-an-ai-agent-mesh-with-gemini-3-openclaw-and-acpx-7b6ab5f1cbf4">Building an AI Agent Mesh with Gemini 3, OpenClaw, and ACPX</a> by Cloud GDE Timothy Olaleke (Portugal) explains how to build an AI agent mesh using Gemini 3.1 Pro, OpenClaw, and ACPX with covering multi-agent orchestration, gateway architecture, and real-world deployment patterns.</p><h3>ML Research</h3><p>#Gemma <a href="https://arxiv.org/abs/2604.11490">Anthropogenic Regional Adaptation in Multimodal Vision-Language Model</a> by AI GDE Aye Hninn Khine (Thailand) introduces the method to improve the cultural relevance of VLMs in specific regions while maintaining global performance.</p><p>#Gemma <a href="https://medium.com/@rbinsafi/exploring-the-refusal-direction-in-gemma-3-models-on-tpu-3c9858f7426b">Investigating Refusal Mechanisms in Gemma 3 Models for Enhanced AI Safety</a> by AI GDE Ruqiya Bin Safi (Saudi Arabia) studies refusal mechanisms in Gemma 3 by isolating the single directional subspace responsible for refusal behavior using vLLM and TPU infrastructure. It explores mechanistic interpretability to modulate model behavior and improve AI safety through controlled experiments and adversarial analysis.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=154ff3a85db3" width="1" height="1" alt=""><hr><p><a href="https://medium.com/google-developer-experts/mar-apr-2026-ai-community-activity-highlights-and-achievements-154ff3a85db3">[Mar-Apr 2026] AI Community — Activity Highlights and Achievements</a> was originally published in <a href="https://medium.com/google-developer-experts">Google Developer Experts</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Serve and Inference Gemma 4 on TPU]]></title>
            <link>https://medium.com/google-developer-experts/serve-and-inference-gemma-4-on-tpu-ae7da212ddd4?source=rss----a67bd6fa7d58---4</link>
            <guid isPermaLink="false">https://medium.com/p/ae7da212ddd4</guid>
            <category><![CDATA[llm]]></category>
            <category><![CDATA[gemma]]></category>
            <category><![CDATA[vision-language-model]]></category>
            <category><![CDATA[tpu]]></category>
            <category><![CDATA[google-cloud-platform]]></category>
            <dc:creator><![CDATA[Nitin Tiwari]]></dc:creator>
            <pubDate>Mon, 04 May 2026 00:19:51 GMT</pubDate>
            <atom:updated>2026-05-04T00:19:50.192Z</atom:updated>
            <content:encoded><![CDATA[<h3>Introduction</h3><p>Earlier in April 2026, Google released Gemma 4, the latest family of open multimodal models, and momentum has been building since then. Gemma 4 comes in four sizes: Effective 2B (E2B), Effective 4B (E4B), 26B Mixture of Experts (MoE), and 31B Dense. Native multimodality in the Gemma family first appeared last year with Gemma 3.</p><p>What makes Gemma 4 stand out is that it goes beyond standard text-to-text chat, with the ability to handle complex reasoning and agentic workflows. In practice, the real challenge lies in serving these models efficiently. This leads to a natural question: how do LLMs like Gemini deliver sub-second responses? A large part of the answer lies in TPUs.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/616/1*8ANmkcN4myu6FWmgjwF6eA.png" /></figure><h4>Tensor Processing Units (TPUs)</h4><p>Google uses Tensor Processing Units (TPUs) as hardware accelerators for both training and serving models. What puts Google ahead in the AI race is its early investment in designing custom chips, purpose-built for large-scale machine learning workloads.</p><p>In practice, these accelerators can deliver significantly higher performance than general-purpose GPUs for specific model architectures and serving scenarios.</p><p>In this blog, I go beyond the usual VLM inference on GPUs and show how to create a TPU instance on Google Cloud to serve Gemma 4 using vLLM.</p><blockquote><strong>What is vLLM?<br></strong><a href="https://vllm.ai/">vLLM</a> is an open source high performance inference engine for large language models that maximizes hardware utilization and throughput using techniques like PagedAttention and continuous batching.</blockquote><p>Now that we know what TPUs and vLLM are, let’s get started.</p><h4>Prerequisites</h4><ul><li>Billing account linked to a GCP project</li><li>Reserved TPU quota</li><li>Access to Gemma family of models on Hugging Face</li></ul><p>Since TPUs are expensive and limited in availability, you may need to request quota in advance or use queued resources.</p><p><strong>Step 1: Create a TPU instance</strong></p><p>Open the Google Cloud Console and activate Cloud Shell. TPUs can be either reserved or allocated using queued resources, meaning they are assigned when capacity becomes available.</p><p>In Cloud Shell, run the following commands to set up the required variables:</p><pre>export PROJECT=YOUR_GCP_PROJECT_NAME<br>export HF_TOKEN=YOUR_HF_TOKEN<br>export ZONE=southamerica-east1-c<br>export TPU_NAME=gemma4-tpu-vllm</pre><p>Cloud TPUs are available in different versions. You can explore the available generations, including the 8th generation TPUs such as TPU 8t (training) and TPU 8i (inference), announced at Google Cloud Next 2026.</p><p>In this tutorial, I will deploy Gemma 4 on TPU 6e (Trillium).</p><pre>gcloud alpha compute tpus queued-resources create gemma4-tpu-vllm \<br>  --zone=southamerica-east1-c \<br>  --accelerator-type=v6e-8 \<br>  --runtime-version=v2-alpha-tpuv6e \<br>  --node-id=gemma4-tpu-vllm \<br>  --provisioning-model=flex-start \<br>  --max-run-duration=4h \<br>  --valid-until-duration=4h \<br>  --labels=purpose=flex-start</pre><p>The above command creates a queued TPU resource using flex start provisioning, which allows you to specify the duration for which it remains active.</p><p>To check the status of your request, run the below</p><pre>gcloud alpha compute tpus queued-resources describe \<br>  gemma4-tpu-vllm \<br>  --zone=southamerica-east1-c</pre><p>It may take some time to spin up the TPU instance depending upon the availability.</p><p>Once provisioned, you can see the status is changed to ACTIVE. Alternatively, you can also check it on Cloud Console.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*jXrNq7-3p__fNYXppP4N8g.png" /><figcaption>TPU v6e-8 instance on Cloud Console</figcaption></figure><p><strong>Step 2: Configure Firewall</strong></p><p>Run the below command to configure firewall rules so that the vLLM Docker image allows incoming traffic.</p><pre>gcloud compute firewall-rules create allow-vllm-8000 \<br>  --allow tcp:8000 \<br>  --target-tags=vllm</pre><p><strong>Step 3: Connect to TPU instance using SSH</strong></p><p>In the Cloud Shell, run the below command to SSH into the TPU instance.</p><pre>gcloud compute tpus tpu-vm ssh gemma4-tpu-vllm \<br>  --zone=southamerica-east1-c</pre><p><strong>Step 4: Download Gemma 4 Docker image</strong></p><p>Now, once SSHed into the TPU instance, run the below command to download the Gemma 4 Docker image.</p><pre>sudo docker run -it --rm --name gemma4-vllm \<br>  --privileged \<br>  --network host \<br>  --shm-size 16g \<br>  -v /dev/shm:/dev/shm \<br>  -e HF_TOKEN=$HF_TOKEN \<br>  vllm/vllm-tpu:gemma4 \<br>  python -m vllm.entrypoints.openai.api_server \<br>    --model google/gemma-4-26B-A4B-it \<br>    --tensor-parallel-size 8 \<br>    --max-model-len 8192 \<br>    --limit-mm-per-prompt &#39;{&quot;image&quot;: 1, &quot;audio&quot;: 0}&#39; \<br>    --disable_chunked_mm_input \<br>    --enable-auto-tool-choice \<br>    --tool-call-parser gemma4 \<br>    --host 0.0.0.0 \<br>    --port 8000<br>    --allowed-local-media-path /home/nitin_tiwari</pre><p>In this example, we will deploy the gemma-4-26B-A4B-it model. It is a 26B parameter instruction-tuned model with 4B active parameters.</p><p>This will take a few minutes to set up the Docker image, as it loads the model weights and initializes the vLLM inference engine.</p><p>Once done, you should see the below message in the terminal.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*AY5FzS7BqFzKigSH1tzU1A.png" /></figure><p><strong>Step 5: Start Inference</strong></p><p>We are now ready to start inference on the deployed model. I have built a simple frontend that accepts text and image inputs, forwards them to the TPU instance hosting Gemma 4, and returns the generated response.</p><p>Clone the repository to your local machine:</p><pre>git clone https://github.com/NSTiwari/Gemma-4-on-TPU.git<br>cd Gemma-4-on-TPU</pre><p>Once completed, open the index.html file and update line 583 by replacing YOUR_EXTERNAL_IP with the external IP address of your TPU instance:</p><pre>const res = await fetch(&quot;http://YOUR_EXTERNAL_IP:8000/v1/chat/completions&quot;</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*b6n-nt1bTP3LFvY016q_Wg.png" /><figcaption>External IP of TPU instance</figcaption></figure><p>You can find the external IP address in the list of TPUs in the Google Cloud Console.</p><p>Finally, start the frontend server using the following command:</p><pre># Start frontend server.<br>py -m http.server</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/986/1*yJnnlqmfB_B39lM8FZXO6w.png" /></figure><p>Open your web browser, and type localhost:8000 in the address bar to start the frontend application.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*AtBxW74bVmWIlVHSK2Up7A.gif" /><figcaption>Gemma 4 26B-4B-it on TPU v6e using vLLM</figcaption></figure><p>As you can see, Gemma 4 can perform a wide range of tasks, with response times of around 2–4 seconds when served on TPUs using vLLM.</p><blockquote><strong>Note:</strong> The first request may take longer due to cold start overhead.</blockquote><p>So, that concludes this blog. I wanted this blog to cover the end-to-end aspects of how to create a TPU instance on Google Cloud, serve Gemma 4 using vLLM, build a frontend application, and send requests to the TPU instance for inference.</p><p>I believe it pretty much covered everything you need to get started and serve your own custom models on TPUs, where inference that would otherwise take several seconds to minutes on a typical GPU setup can be significantly faster.</p><p>I hope you learned how powerful TPUs can be when combined with vLLM to reduce inference latency. Stay tuned for more such tutorials.</p><h4>Acknowledgment</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/300/1*MQGnFjXyCTS9e1bmvVZIyg.png" /></figure><p>This project was developed as part of Google’s AI Developer Programs TPU Sprint. I sincerely thank the Google AIDP Team for their generous support in providing GCP credits to help facilitate this project.</p><h4>References &amp; Resources</h4><ul><li><a href="https://cloud.google.com/tpu">Google Cloud TPUs</a></li><li><a href="https://vllm.ai/">vLLM</a></li><li><a href="https://ai.google.dev/gemma/docs/core/model_card_4">Gemma 4 family of models</a></li><li><a href="https://github.com/AI-Hypercomputer/tpu-recipes/tree/main/inference/trillium/vLLM/Gemma4">AI Hypercomputer TPU recipes</a></li></ul><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=ae7da212ddd4" width="1" height="1" alt=""><hr><p><a href="https://medium.com/google-developer-experts/serve-and-inference-gemma-4-on-tpu-ae7da212ddd4">Serve and Inference Gemma 4 on TPU</a> was originally published in <a href="https://medium.com/google-developer-experts">Google Developer Experts</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[⚖️ LexiMini: How I Built an AI Legal Assistant for India — From Scratch, on a TPU]]></title>
            <link>https://medium.com/google-developer-experts/%EF%B8%8F-leximini-how-i-built-an-ai-legal-assistant-for-india-from-scratch-on-a-tpu-f8354e20cbc9?source=rss----a67bd6fa7d58---4</link>
            <guid isPermaLink="false">https://medium.com/p/f8354e20cbc9</guid>
            <category><![CDATA[google-tpu]]></category>
            <category><![CDATA[legal-assistance]]></category>
            <category><![CDATA[fine-tuning]]></category>
            <category><![CDATA[google-gemma]]></category>
            <category><![CDATA[ai]]></category>
            <dc:creator><![CDATA[Geeta Kakrani]]></dc:creator>
            <pubDate>Mon, 04 May 2026 00:19:38 GMT</pubDate>
            <atom:updated>2026-05-04T00:19:37.120Z</atom:updated>
            <content:encoded><![CDATA[<p><em>Fine-tuning Gemma 3 4B on Indian Law Data · MaxText · Tunix Distillation · HuggingFace</em></p><p>This blog documents the approach behind a TPU-based LLM pipeline I’m currently building.</p><p>The focus here is not just on results, but on the full system design — model behavior, distillation strategy, and the practical challenges of working with TPUs in a real setup.</p><p>Instead of presenting a polished end-state, I’m breaking down the actual process: how the system is structured, what decisions were made, and how different components evolved over time.</p><p>You’ll find everything here — from setup to experimentation — in a way that reflects real-world building, not just ideal scenarios.</p><h3>Why LexiMini?</h3><p>India has over 1.4 billion people.</p><p>Think about a woman in a village in rural India. Her landlord is threatening to throw her out. She does not know what a tenant agreement means legally. She does not know that she has rights. There is no lawyer in her village. The nearest district court is two hours away. And even if she gets there — she cannot afford to pay someone to explain what is written in that contract.</p><p>Or think about a woman who took a small loan from a local moneylender. She does not know what interest rate is legally allowed. She does not know what she can do when the terms change overnight. She does not know who to go to.</p><p>These are not rare cases. This is everyday life for millions of women across rural India — where both education and internet access are still limited, where legal literacy is almost zero, and where the gap between knowing your rights and losing everything is just one piece of paper you could not understand.</p><p>Normal people face these situations every single day — tenant contracts they cannot read, loans with terms they did not understand, workplace harassment they do not know is illegal, property disputes they have no idea how to fight. These are ordinary problems. But without legal knowledge, ordinary problems become life-altering ones.</p><p>When they do go online to search for answers, they find one of two things:</p><p><strong>Dense legal text</strong> that was written by lawyers, for lawyers.</p><p>Or <strong>generic AI answers</strong> from models that were never trained on Indian law and confidently give wrong information — which in a legal situation, can cause real harm.</p><p>I wanted to build something different. A small, focused AI that actually <em>knows</em> Indian law — tenant rights, loan agreements, women’s legal protections, IPC sections, bail procedures, court processes. Not a general model pretending to know. A specialized one, built for the people who need it most.</p><p>That is LexiMini.</p><h3>The Full Pipeline</h3><p>Before I get into the steps, here is the complete picture of what I built:</p><pre>GitHub Repo          TPU VM               GCS Bucket<br>(Indian Law Data) →  (MaxText Training) → (Checkpoints)<br>                                               ↓<br>                                     HuggingFace Model<br>                                     (Gemma 3 4B Fine-tuned)<br>                                               ↓<br>                                     Tunix Distillation<br>                                     (4B Teacher → 1B Student)<br>                                               ↓<br>                                     LexiMini Final<br>                                     (Lightweight, Deployable)</pre><p>There are four phases:</p><ul><li><strong>Phase 1</strong> — Set up the TPU VM and install everything</li><li><strong>Phase 2</strong> — Prepare and upload the Indian law dataset</li><li><strong>Phase 3</strong> — Fine-tune Gemma 3 4B using MaxText</li><li><strong>Phase 4</strong> — Distill the 4B model down to 1B using Tunix</li></ul><p>Let me walk through each one.</p><h3>Tools I Used</h3><p>Tool What it does Google TPU VM (v6e) Hardware TPU chips for fast training MaxText Google’s open-source JAX-based LLM training framework Tunix Google DeepMind’s post-training + distillation framework, built on JAX HuggingFace Hub Model storage and deployment Google Cloud Storage (GCS) Stores data, weights, and checkpoints during training</p><blockquote><strong><em>One important note:</em></strong><em> Everything in this guide runs directly on the TPU VM terminal. After you SSH in, you never need to go back to your local machine.</em></blockquote><h3>Phase 1: Setting Up the TPU VM</h3><h3>The Setup Flow</h3><pre>Create TPU VM  →  SSH In  →  Install Packages  →  Clone MaxText  →  Install JAX</pre><h3>Step 1 — Create the TPU VM</h3><p>Let me be honest about something before you run this command.</p><p>A TPU v6 costs around <strong>$12–$15 per hour</strong> on Google Cloud. Fine-tuning a 4B model for 5000 steps takes several hours. Distillation adds even more. The total compute cost of this project, paid out of pocket, would be completely out of reach for most independent researchers or students.</p><p>I was able to do this because I received <strong>Google Cloud credits through the TPUSprint program </strong>as a Google Developer Expert (GDE) in AI/ML.</p><blockquote><strong><em>Google Cloud credits are provided for this project. #TPUSprint</em></strong></blockquote><p>I started by setting up everything from scratch on Google Cloud.</p><p>First, I created a new project inside Google Cloud Platform. Once the project was ready, I landed on the main dashboard.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*WMgv_wfEeNwFHDyM2lFGAg.png" /></figure><p>From there, I navigated to <strong>Compute Engine → TPUs</strong>. This is where all TPU resources are managed.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*MRqEB5FbnzCmRf5QtdRR4g.png" /></figure><p>Since there were no TPUs created yet, the page was empty. I clicked on <strong>Create TPU</strong> to start the setup.</p><p>On the TPU creation screen:</p><ul><li>I gave the TPU a name (node-1)</li><li>Selected the <strong>zone</strong> (us-central1-a) — this is important because TPU availability depends on the zone</li><li>Chose the <strong>TPU type</strong> (v5litepod-1 in this case, based on availability)</li><li>Selected the <strong>TPU software version</strong> (v2-alpha-tpuv5-lite)</li></ul><p>I kept the rest of the settings as default and clicked <strong>Create</strong>.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*7ltYD8ZnigMC9zvan9r1pA.png" /></figure><p>Once the TPU node was created, it appeared in the TPU list and was ready to use.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*pK3VwAPQSZ-VcdaRhLq6Xw.png" /></figure><p>Now click over ssh to open Terminal:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*b_ZZRqC-XJ26pPzTpAj-aw.png" /></figure><p>For reference, the same setup can also be done using CLI:</p><pre>gcloud compute tpus tpu-vm create leximini-tpu \<br>  --zone=us-central2-b \<br>  --accelerator-type=v4-8 \<br>  --version=tpu-ubuntu2204-base</pre><blockquote>One key learning here: TPU configuration is highly dependent on availability. You might need to try different zones or TPU types before finding one that works.</blockquote><blockquote>All commands shown below were executed directly inside the TPU VM terminal after connecting via SSH.</blockquote><blockquote>Step 3 — Install System Packages</blockquote><pre>sudo apt-get update &amp;&amp; sudo apt-get install -y git python3-pip wget curl</pre><h3>Step 4 — Clone MaxText and Install JAX</h3><p>MaxText is Google’s training framework for large language models. It is built on JAX and designed to run natively on TPU — which is exactly what we need here.</p><pre>cd ~<br>git clone https://github.com/google/maxtext.git<br>cd maxtext<br>pip install -r requirements.txt<br>pip install jax[tpu] -f https://storage.googleapis.com/jax-releases/libtpu_releases.html</pre><blockquote><em>JAX TPU installation takes around 5 minutes. Let it finish completely before moving on.</em></blockquote><h3>Phase 2: Preparing the Indian Law Dataset</h3><h3>The Data Flow</h3><pre>Raw Legal Text      →    Python Script     →    GCS Bucket<br>(.txt / .csv files)      (convert to JSONL)     (ready for MaxText)</pre><p>This phase took me longer than I expected — not because of the code, but because of the data itself.</p><p>I curated Indian legal text across multiple categories: IPC sections, CrPC procedures, constitutional articles, and court judgment excerpts. The curation matters. MaxText will train on exactly what you give it. Garbage in, garbage out — this is especially true for legal text where precision is everything.</p><h3>Step 5 — Clone Your Dataset Repo on the TPU VM</h3><pre>cd ~<br>git clone https://github.com/YOUR_USERNAME/YOUR_REPO.git<br>cd YOUR_REPO<br>ls</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*rnvEQyrpqGb1NV2PPnb2gQ.png" /><figcaption><a href="https://github.com/geeta-gwalior/Leximini_V1">https://github.com/geeta-gwalior/Leximini_V1</a></figcaption></figure><h3>Step 6 — Convert Data to JSONL Format</h3><p>MaxText needs training data in JSONL format — one JSON object per line, each with a text key. Here is the conversion script:</p><p>The system was developed using real legal datasets. However, due to confidentiality considerations, I have included only synthetic data in the public repository.</p><pre>cat &gt; prepare_data.py &lt;&lt; &#39;EOF&#39;<br>import json</pre><pre>cat &gt; prepare_data.py &lt;&lt; &#39;EOF&#39;<br>import json</pre><pre>input_file  = &#39;indian_law.txt&#39;   # change as needed<br>output_file = &#39;train_data.jsonl&#39;</pre><pre>count = 0</pre><pre>with open(input_file, &#39;r&#39;, encoding=&#39;utf-8&#39;) as f_in, \<br>     open(output_file, &#39;w&#39;, encoding=&#39;utf-8&#39;) as f_out:</pre><pre>for line in f_in:<br>        line = line.strip()</pre><pre># skip empty or very short lines<br>        if not line or len(line) &lt; 20:<br>            continue</pre><pre>record = {&quot;text&quot;: line}<br>        f_out.write(json.dumps(record, ensure_ascii=False) + &quot;\n&quot;)<br>        count += 1</pre><pre>print(f&quot;Total records written: {count}&quot;)<br>EOF</pre><pre>python3 prepare_data.py</pre><p>This is a simplified version of the preprocessing step. In practice, additional structuring and formatting were applied to improve training quality.</p><h3>Step 7 — Create a GCS Bucket and Upload Data</h3><p>MaxText does not read from local disk during training. It reads from Google Cloud Storage. So everything goes to GCS first:</p><pre># Create the bucket (pick a globally unique name)<br>gsutil mb -l us-central2 gs://YOUR-BUCKET-NAME</pre><pre># Upload training data<br>gsutil cp ~/YOUR_REPO/data/train_data.jsonl gs://YOUR-BUCKET-NAME/data/train_data.jsonl</pre><pre># Verify<br>gsutil ls gs://YOUR-BUCKET-NAME/data/</pre><h3>Phase 3: Fine-Tuning Gemma 3 4B with MaxText</h3><h3>The Training Flow</h3><pre>HF Weights         GCS Upload         MaxText Train       Checkpoint<br>(Gemma 3 4B)   →  (store weights) →  (5000 steps)    →  (saved to GCS)</pre><p>This is the heart of the project. We take Gemma 3 4B — Google’s open-source model — and teach it everything about Indian law.</p><p>I ran this training step twice. The first time I misconfigured the checkpoint path and lost hours of compute. Small mistakes are expensive on TPU. Double-check your GCS paths before you hit enter.</p><h3>Step 8 — Download Gemma 3 4B Weights from HuggingFace</h3><p>You need a HuggingFace account and an access token. Get it from: huggingface.co → Settings → Access Tokens → New token.</p><pre>pip install huggingface_hub</pre><pre>python3 &lt;&lt; &#39;EOF&#39;<br>from huggingface_hub import snapshot_download</pre><pre>snapshot_download(<br>    repo_id=&#39;google/gemma3-4b-it&#39;,<br>    local_dir=&#39;/home/USER/gemma3-weights&#39;,   # replace USER with your username (run: whoami)<br>    token=&#39;hf_YOUR_TOKEN_HERE&#39;<br>)<br>print(&#39;Download complete!&#39;)<br>EOF</pre><h3>Step 9 — Upload Weights to GCS</h3><pre># The -m flag enables parallel upload — do not skip it for large files<br>gsutil -m cp -r ~/gemma3-weights gs://YOUR-BUCKET-NAME/weights/gemma3-4b/</pre><pre># Verify<br>gsutil ls gs://YOUR-BUCKET-NAME/weights/gemma3-4b/</pre><h3>Step 10 — Run Fine-Tuning</h3><p>Everything comes together here. One command, and MaxText takes over:</p><pre>cd ~/maxtext</pre><pre>cd ~/maxtext</pre><pre>python3 MaxText/train.py MaxText/configs/base.yml \<br>  base_output_directory=gs://YOUR-BUCKET-NAME/output/ \<br>  load_parameters_path=gs://YOUR-BUCKET-NAME/weights/gemma3-4b/ \<br>  dataset_path=gs://YOUR-BUCKET-NAME/data/ \<br>  model_name=gemma3-4b \<br>  steps=5000 \<br>  run_name=leximini-finetune</pre><p>Key training parameters such as learning rate, batch size, and warmup strategy were tuned conservatively to preserve pretrained knowledge during fine-tuning. Small changes in these values had a noticeable impact on stability and output quality.</p><blockquote><em>Checkpoints are saved automatically every 500 steps to </em><em>gs://YOUR-BUCKET-NAME/output/leximini-finetune/checkpoints/</em></blockquote><p>Watch the loss value as it prints. When it starts going down — that moment is genuinely satisfying. The model is learning Indian law in real time.</p><h3>Step 11 — Convert Checkpoint to HuggingFace Format</h3><p>After training, the checkpoint is in MaxText’s native format. We need to convert it to HuggingFace format so it can be used with the transformers library:</p><pre>cd ~/maxtext</pre><pre>python3 MaxText/convert_gpt_maxtext_to_hf.py \<br>  --base_model_path gs://YOUR-BUCKET-NAME/weights/gemma3-4b/ \<br>  --maxtext_model_path gs://YOUR-BUCKET-NAME/output/leximini-finetune/checkpoints/5000/ \<br>  --output_path ~/leximini-4b-hf \<br>  --model_size 4b</pre><pre>ls ~/leximini-4b-hf/</pre><h3>Step 12 — Push the Fine-Tuned 4B Model to HuggingFace</h3><pre>python3 &lt;&lt; &#39;EOF&#39;<br>from huggingface_hub import HfApi</pre><pre>api = HfApi()</pre><pre>api.create_repo(<br>    repo_id=&#39;YOUR_HF_USERNAME/leximini-4b&#39;,<br>    token=&#39;hf_YOUR_TOKEN_HERE&#39;,<br>    private=False<br>)</pre><pre>api.upload_folder(<br>    folder_path=&#39;/home/USER/leximini-4b-hf&#39;,<br>    repo_id=&#39;YOUR_HF_USERNAME/leximini-4b&#39;,<br>    repo_type=&#39;model&#39;,<br>    token=&#39;hf_YOUR_TOKEN_HERE&#39;<br>)<br>print(&#39;4B model uploaded!&#39;)<br>EOF</pre><h3>Phase 4: Distilling 4B → 1B with Tunix</h3><h3>The Distillation Flow</h3><pre>LexiMini 4B         Tunix Framework        Gemma 3 1B<br>(Teacher Model)  →  (Distillation)      →  (Student Model)<br>                                               ↓<br>                                        LexiMini 1B<br>                                     (4x smaller, nearly<br>                                      as knowledgeable)</pre><p>Fine-tuning is done. But a 4B parameter model is heavy. It is slow to serve and expensive to run at scale. I wanted something leaner — something that could actually reach more people, including on limited hardware.</p><p>That is where Tunix comes in.</p><h3>What is Knowledge Distillation?</h3><p>Think of it this way. Imagine a senior advocate who has practiced Indian law for 20 years. Instead of making a junior lawyer read every case file from scratch, the senior sits with them and explains the reasoning — the <em>why</em> behind each judgment, not just the <em>what</em>.</p><p>The junior lawyer learns faster, and ends up nearly as capable — in a fraction of the time.</p><p>That is distillation. The 4B model (teacher) guides the 1B model (student) to reproduce the same legal understanding — but in a much smaller package.</p><p>The key insight is this: the student does not just learn from raw text labels. It learns from the teacher’s <em>output probability distribution</em> — which is far richer. When the teacher says “Section 302 relates to murder”, it doesn’t just output that as a yes/no. It outputs a distribution across thousands of tokens — and that distribution carries nuanced information about related concepts, alternate phrasings, confidence levels. The student absorbs all of that.</p><h3>What is Tunix?</h3><p>Tunix is an open-source post-training framework built by Google DeepMind on top of JAX. It handles things like knowledge distillation, RLHF, and model alignment — the work that happens <em>after</em> initial pretraining. It runs natively on TPU and integrates cleanly with JAX checkpoints.</p><h3>Step 13 — Install Tunix</h3><pre>cd ~<br>git clone https://github.com/google-deepmind/tunix.git<br>cd tunix<br>pip install -e .</pre><h3>Step 14 — Download Gemma 3 1B Base Weights</h3><p>The student model starts from Gemma 3 1B base weights:</p><pre>python3 &lt;&lt; &#39;EOF&#39;<br>from huggingface_hub import snapshot_download</pre><pre>snapshot_download(<br>    repo_id=&#39;google/gemma-3-1b&#39;,<br>    local_dir=&#39;/home/USER/gemma3-1b-weights&#39;,<br>    token=&#39;hf_YOUR_TOKEN_HERE&#39;<br>)<br>print(&#39;1B base weights ready!&#39;)<br>EOF</pre><pre># Upload to GCS<br>gsutil -m cp -r ~/gemma3-1b-weights gs://YOUR-BUCKET-NAME/weights/gemma3-1b/</pre><h3>Step 15 — Create the Distillation Config</h3><pre>cat &gt; ~/tunix/configs/leximini_distill.py &lt;&lt; &#39;EOF&#39;<br>teacher_model_path  = &#39;gs://YOUR-BUCKET-NAME/output/leximini-finetune/checkpoints/5000/&#39;<br>student_model_path  = &#39;gs://YOUR-BUCKET-NAME/weights/gemma3-1b/&#39;<br>output_path         = &#39;gs://YOUR-BUCKET-NAME/distilled/leximini-1b/&#39;</pre><pre>cat &gt; ~/tunix/configs/leximini_distill.py &lt;&lt; &#39;EOF&#39;</pre><pre>teacher_model_path  = &#39;gs://YOUR-BUCKET-NAME/output/leximini-finetune/checkpoints/5000/&#39;<br>student_model_path  = &#39;gs://YOUR-BUCKET-NAME/weights/gemma3-1b/&#39;<br>output_path         = &#39;gs://YOUR-BUCKET-NAME/distilled/leximini-1b/&#39;</pre><pre>teacher_model_name  = &#39;gemma3-4b&#39;<br>student_model_name  = &#39;gemma3-1b&#39;</pre><pre>train_data_path     = &#39;gs://YOUR-BUCKET-NAME/data/train_data.jsonl&#39;</pre><pre># training parameters (simplified for clarity)<br>steps               = 3000<br>max_target_length   = 1024<br>temperature         = 3.0 <br>alpha               = 0.7</pre><pre>dtype               = &#39;bfloat16&#39;</pre><pre>EOF</pre><p>Two parameters that matter most:</p><p><strong>temperature = 2.0</strong> — This softens the teacher’s output distribution. At temperature 1.0, the teacher gives sharp, confident predictions. At 2.0, probability spreads across more tokens — giving the student richer, more nuanced signals to learn from. This is one of the core insights of the original knowledge distillation paper by Hinton et al.</p><p><strong>alpha = 0.7</strong>–70% of the loss comes from matching the teacher. 30% comes from the raw training data. This balance keeps the student grounded in real legal text while absorbing the teacher’s reasoning.</p><p>In practice, finding the right balance between these factors required experimentation, as different settings can significantly impact how well the student model retains accuracy.</p><h3><strong>Step 16 — Run Distillation</strong></h3><pre>cd ~/tunix</pre><pre>python3 tunix/distillation/distill.py \<br>  --config configs/leximini_distill.py \<br>  --teacher_model_path gs://YOUR-BUCKET-NAME/output/leximini-finetune/checkpoints/5000/ \<br>  --student_model_path gs://YOUR-BUCKET-NAME/weights/gemma3-1b/ \<br>  --output_path gs://YOUR-BUCKET-NAME/distilled/leximini-1b/ \<br>  --train_data gs://YOUR-BUCKET-NAME/data/train_data.jsonl \<br>  --steps 3000 \<br>  --learning_rate 5e-5 \<br>  --per_device_batch_size 4 \<br>  --temperature 2.0 \<br>  --alpha 0.7 \<br>  --dtype bfloat16 \<br>  --run_name leximini-distilled</pre><h3>Step 17 — Convert Distilled Checkpoint and Push to HuggingFace</h3><pre>cd ~/maxtext</pre><pre>python3 MaxText/convert_gpt_maxtext_to_hf.py \<br>  --base_model_path gs://YOUR-BUCKET-NAME/weights/gemma3-1b/ \<br>  --maxtext_model_path gs://YOUR-BUCKET-NAME/distilled/leximini-1b/checkpoints/3000/ \<br>  --output_path ~/leximini-1b-hf \<br>  --model_size 1b</pre><pre>ls ~/leximini-1b-hf/</pre><pre>python3 &lt;&lt; &#39;EOF&#39;<br>from huggingface_hub import HfApi</pre><pre>api = HfApi()</pre><pre>api.create_repo(<br>    repo_id=&#39;YOUR_HF_USERNAME/leximini-1b-final&#39;,<br>    token=&#39;hf_YOUR_TOKEN_HERE&#39;,<br>    private=False<br>)</pre><pre>api.upload_folder(<br>    folder_path=&#39;/home/USER/leximini-1b-hf&#39;,<br>    repo_id=&#39;YOUR_HF_USERNAME/leximini-1b-final&#39;,<br>    repo_type=&#39;model&#39;,<br>    token=&#39;hf_YOUR_TOKEN_HERE&#39;<br>)<br>print(&#39;LexiMini is live!&#39;)<br>EOF</pre><h3>What I Am Still Working On</h3><p>I want to be transparent about where this project currently stands.</p><p>The fine-tuned model exists and is running. But it still hallucinates — especially on less common legal sections. It sometimes generates plausible-sounding IPC sections that do not exist. This is a known problem with language models trained on limited domain data, and I am actively working on it.</p><p>The areas I am focusing on right now:</p><ul><li><strong>More data</strong> — The current dataset covers core IPC and constitutional law well. Consumer protection, family law, property law, and state-specific laws need more coverage.</li><li><strong>Better data formatting</strong> — Moving from raw paragraph dumps to structured question-answer pairs, which gives the model clearer learning signal.</li><li><strong>Distillation tuning</strong> — The 4B → 1B pipeline is implemented but still being refined. The 1B student needs more iterations to fully absorb the teacher’s legal reasoning.</li></ul><p>The GitHub and final model links will be added here when the project reaches a stable, reliable state. I would rather share something that works properly than something that looks impressive but misleads people about their legal rights.</p><h3>What I Learned While Building This</h3><p>This wasn’t my first time working with fine-tuning. I’ve previously used approaches like LoRA, QLoRA, and PEFT — they are reliable and produce strong results. But they are also time-intensive, especially when working with larger datasets and multiple iterations.</p><p>What stood out to me in this project was the shift to JAX-based training using MaxText. The speed difference was significant. Workloads that would typically take hours could be executed in minutes. TPU performance is obviously a big factor here, but the tooling itself also plays a major role.</p><p>At the same time, this came with its own challenges.</p><p>MaxText doesn’t have the kind of beginner-friendly ecosystem or tutorials that many PyTorch-based workflows have. There isn’t a single place where everything is explained clearly. I had to rely on documentation, experimentation, and AI-assisted exploration to understand how things actually work under the hood.</p><p>Distillation was another area where things were not straightforward. While the pipeline works, maintaining accuracy is still a challenge. I’m actively experimenting to find the right balance between compression and performance, especially for a domain like legal text where precision matters.</p><p>Another important shift in thinking for me: I don’t just want to train models — I want to move toward reasoning-focused training. That is still a work in progress, and I’m continuing to explore how to integrate it effectively into this pipeline.</p><p>Overall, this project wasn’t about learning basics. It was about navigating gaps — missing documentation, unclear workflows, and making practical decisions in a system that is still evolving.</p><h3>The Bigger Picture</h3><p>This project is not about building for developers — it’s about building for real users.</p><p>Someone dealing with a tenant dispute or a confusing loan agreement is not going to use a notebook or call an API. They need something simple, accessible, and reliable — something that works on limited internet, speaks plain language, and understands Indian law beyond surface-level fluency.</p><p>That’s why distillation matters. A 4B model is powerful, but a well-optimized 1B model can run on affordable hardware, load faster, and reach far more people. The goal was never scale for its own sake — it was usability.</p><p>LexiMini is still evolving. Hallucination is a real challenge, and I’m actively working on improving reliability. The dataset is also expanding, especially in areas that impact everyday lives — tenant law, microfinance, women’s rights, domestic violence protections, inheritance, and local governance.</p><p>It’s not finished — but it’s real, it’s running, and it’s being built with a clear purpose: making Indian law accessible to those who need it most.</p><h3><em>This article will be updated as the project progresses. Model and GitHub links will be added upon stable release.</em></h3><p><em>Built with MaxText · Tunix · JAX · HuggingFace</em></p><p><strong>Acknowledgment:</strong> Google Cloud credits are provided for this project. #TPUSprint</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=f8354e20cbc9" width="1" height="1" alt=""><hr><p><a href="https://medium.com/google-developer-experts/%EF%B8%8F-leximini-how-i-built-an-ai-legal-assistant-for-india-from-scratch-on-a-tpu-f8354e20cbc9">⚖️ LexiMini: How I Built an AI Legal Assistant for India — From Scratch, on a TPU</a> was originally published in <a href="https://medium.com/google-developer-experts">Google Developer Experts</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[ML acceleration guide: TPUs vs GPUs]]></title>
            <link>https://medium.com/google-developer-experts/ml-acceleration-guide-tpus-vs-gpus-95a4ac995e9d?source=rss----a67bd6fa7d58---4</link>
            <guid isPermaLink="false">https://medium.com/p/95a4ac995e9d</guid>
            <category><![CDATA[ai-architecture]]></category>
            <category><![CDATA[xla]]></category>
            <category><![CDATA[nvidia-gpu]]></category>
            <category><![CDATA[machine-learning]]></category>
            <category><![CDATA[tpu]]></category>
            <dc:creator><![CDATA[Glen Yu]]></dc:creator>
            <pubDate>Tue, 28 Apr 2026 00:58:19 GMT</pubDate>
            <atom:updated>2026-04-28T13:00:50.942Z</atom:updated>
            <content:encoded><![CDATA[<h4>There’s a lot of hype around GPUs and NVIDIA, but how much do you know about TPUs?</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Eg0Pi2_DUVZtT_rg0B1fQQ.jpeg" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*CwV06MlL-4iSbsdOQOiXcw.jpeg" /><figcaption>Rack of TPUs (left) and GPUs (right)</figcaption></figure><blockquote>Article includes code examples you can find near the end</blockquote><h3>Rise of GPUs</h3><p>Graphics Processing Units have been around for quite some time and their job is to render 2D and 3D graphics in to millions of pixels, calculating their colour, texture, lighting, in parallel to send to your monitor. For a 60Hz monitor that means producing rendered frames 60 times every second.</p><p>Rendering graphics is one thing, but developing code for handling GPUs was a little more difficult. That is, until NVIDIA launched CUDA (Compute Unified Device Architecture) in 2006, which allowed scientific researchers and developers who work in fields that require massive parallel math to take advantage of a GPU’s capabilities. With the rise of machine learning in the early 2010’s, it was discovered that the massive parallel math was exactly what ML engineers needed to train deep neural networks. Since then, the focus of CUDA has been shifting more towards optimizing for machine learning and AI workloads.</p><p>Because GPUs were commercially available and relatively inexpensive at the time, the barrier to entry was low. An ML engineer could train models on their NVIDIA graphics card during the day and jump into a game of League of Legends at night on the same hardware.</p><h4>Honourable mention</h4><p>AMD’s GPUs with Radeon Open Compute (ROCm) in an open-source software stack designed to compete in the AI ecosystem. Though it’s not as popular as CUDA, this gap is closing with <a href="https://www.amd.com/en/newsroom/press-releases/2026-2-24-amd-and-meta-announce-expanded-strategic-partnersh.html">Meta recently signing a deal to expand its existing partnership with AMD</a>.</p><h3>Tensor Processing Unit</h3><p>In the early 2010s, Google projected that the growing demands of its AI workloads, particularly the rapid adoption of deep learning across products like Search and Photos, would require doubling its data center computing capacity roughly every year and a half. Rather than scale generic hardware indefinitely, Google sought a more efficient solution purpose-built for neural network computation, and thus the Tensor Processing Unit (TPU) was born. The TPU is a custom application-specific integrated circuit (ASIC) designed by Google specifically to accelerate AI workloads, deployed internally starting in 2015. By specializing the hardware for the dense matrix operations at the heart of neural networks, TPUs achieve dramatically better performance per watt than general-purpose CPUs or GPUs, reducing both energy consumption and cooling demands at data center scale.</p><p>Google has a tradition of making tools it uses internally available to the broader world, and TPUs are another example of this. The existence of TPUs was first publicly announced at Google I/O in 2016. In 2018, Cloud TPU v2 became available for external users through Google Cloud, marking the first time developers outside Google could harness the same accelerators powering Google’s own AI systems. TPUs also come in two performance flavours: <em>efficiency</em> and <em>performance</em> to meet different market needs.</p><p><strong>NOTE</strong>: As of the 8th generation of TPUs announced during Google Next 2026, <em>efficiency</em> and <em>performance</em> TPUs will be renamed <strong><em>inference</em></strong> and <strong><em>training</em></strong> respectively in favour of a more descriptive, workload-based naming convention.</p><h3>Architecture layout</h3><p>From an architectural standpoint, GPUs can be thought of as being individual computers with accelerators (picture your home gaming PC). If you want to connect them into a cluster, it would be over network, but no matter how fast the network is, it still has to cross node boundaries, and bandwidth drops as a result.</p><p>TPUs are designed from the ground up to be interconnected at a massive scale with a physical layout that involves thousands of TPU chips in a torus topology which gives every chip 6 neighbours (two per axis, one on each side). Recognize that interconnect bandwidth would be the main bottleneck at this scale, Google designed their own proprietary Inter-Chip Interconnect (ICI) network which provides uniform, high-bandwidth, low-latency connections between all the chips in a slice regardless of physical location. With torus topology, there is no concept of crossing a node boundary. When you request TPUs, you do not get the entire TPU cluster or <em>pod</em>. Rather, you get only a small subset or <em>slice</em>. To make this possible, Google developed Optical Circuit Switch (OCS) to be able to rewire physical connections on the fly (entirely in software), allowing the same hardware to serve different workload shapes without any physical reconfiguration.</p><p><strong>NOTE</strong>: Efficiency TPU versions use a 2D torus topology, while Performance TPUs leverage a 3D torus architecture to give you maximum performance with minimum latency.</p><h4>Precision and range</h4><p>A floating-point number consists of three parts:</p><ul><li><strong>Sign</strong>: Positive or negative (represented by the first bit)</li><li><strong>Exponent</strong>: Determines the range of the number</li><li><strong>Mantissa</strong>: Significant digits of a floating-point number, which determines the accuracy</li></ul><p>Traditionally, the standard for high-performance computing was FP32. When AI researchers moved to FP16 to save memory, they lost more than just accuracy: they also lost range. FP32 uses 8 bits for the exponent, while FP16 uses only 5. The 3-bit difference in the exponent bits amount to an almost 10³⁴ difference in range (FP32 has a range of 3.4 x 10³⁸, while FP16 only has a range of 6.5 x 10⁴). In deep learning, where gradients can be incredibly tiny, FP16 often suffers from underflow (meaning numbers are being rounded to 0 because it is too small for FP16’s range to represent), requiring a technical workaround called “<a href="https://docs.nvidia.com/deeplearning/performance/mixed-precision-training/index.html#lossscaling">loss scaling</a>” to keep the math stable.</p><p>Google Brain (now part of Google DeepMind) solved this invented Brain Floating Point (<strong><em>bfloat16</em></strong>), which simply shifted 3 bits from the mantissa to exponents:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/511/1*2N3H21gdVYIzgGVJdgGVwQ.png" /><figcaption>Table comparing FP32, FP16 and bfloat16</figcaption></figure><p>By sacrificing precision for range, bfloat offers the same massive range as FP32, but with the reduced memory and bandwidth of FP16. A huge reason for why this works is that deep learning models are surprisingly noise-tolerant and having more training stability is for more important than having a few extra decimal places of precision. Today, bfloat16 is the de facto standard for training modern LLMs on NVIDIA’s GPUs and Google’s TPUs.</p><h3>Why XLA matters</h3><p>Standard Python execution typically takes an <em>eager</em> approach. This means it executes each step as it is being encountered. This is great for debugging because you can insert print statements to inspect variables at any point.</p><p>XLA (Accelerated Linear Algebra), on the other hand, is a domain-specific JIT compiler. Instead of executing steps one by one, it analyzes the entire execution graph to optimize and fuse operations before they run. This <em>lazy</em> approach creates an initial warm-up delay, but once the training starts, it is significantly faster than standard methods. The tradeoff is transparency: your step-by-step Python code becomes an optimized “black box”, making traditional debugging strategies more difficult. This is why TPUs are powerhouses for massive enterprise training, while GPUs remain the flexible choice for quick experimentation.</p><p><strong>NOTE</strong>: Though XLA was built for TPUs, it’s also made its way into the NVIIA GPU ecosystem via tools such as JAX and torch.compile since PyTorch 2.0.</p><h4>TorchTPU</h4><p>Google is engineering a <a href="https://developers.googleblog.com/torchtpu-running-pytorch-natively-on-tpus-at-google-scale/">TorchTPU</a> stack that will provide native PyTorch support. This would allow you to run models in TPUs as they are with full support for native PyTorch features. TorchTPU is currently in preview, and once it becomes GA, you can be sure I’ll be diving deeper into it!</p><h3>Code example</h3><p>I’m including a couple of Jupyter notebooks that I ran via <a href="https://medium.com/google-cloud/leveraging-tpus-in-colab-featuring-antigravity-c312ad12c1b6">Antigravity + Colab plugin</a> for you to try yourself:</p><ul><li><a href="https://storage.googleapis.com/public-file-server/aiml/mnist_w_gpu_cuda.ipynb">Fashion MNIST with GPU and CUDA</a></li><li><a href="https://storage.googleapis.com/public-file-server/aiml/mnist_w_tpu_xla.ipynb">Fashion MNIST with TPU and XLA</a></li></ul><p>As you will see from the results below, TPU is indeed faster. However, my example isn’t large enough or complex enough to really showcase the true speeds that TPU can bring.</p><p><strong>NOTE</strong>: I have a Colab Pro account which affords me access to additional GPUs and TPUs. The Colab free tier only includes NVIDIA T4 and TPU v5e-1</p><h4>Interpreting training results</h4><p>These are some benchmark trainings (epochs: 50, batch size: 512) in which I used a NVIDIA T4 GPU with (default) FP32 vs Google TPU v5e-1 (single chip TPU) with bfloat16. As expected, TPUs were faster but with lower precision:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/858/1*002wsO43phx92BkUZLm5jg.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/864/1*p7EowU_Ocq-9_BU76TQnhA.png" /><figcaption>T4 GPU (FP32) vs TPU v5e-1 (bfloat16), epochs: 50, batch size: 512</figcaption></figure><p>I then trained the same model using the T4 GPU using bfloat16 but noticed a massive performance drop. This was due to the T4 being an older generation GPU that did not support bfloat16 natively and had to emulate which added a lot of overhead. Switching to a newer L4 GPU, I was able to see the (tiny) performance gain along with the reduced precision:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/862/1*0WIf_gBCply-MorQ2EZbCA.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/446/1*sNglDW0ONZrdpdIH_xbW9g.png" /><figcaption>T4 GPU (bfloat16) vs L4 (bfloat16), epochs: 50, batch size: 512</figcaption></figure><p>Finally, I thought I’d see how the training would perform on a newer TPU v6e-1 and I was blown away by the improvement:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/540/1*mIeGVCTHfZCugUX3THWp2g.png" /><figcaption>TPU v6e-1 (bfloat16), epochs: 50, batch size: 512</figcaption></figure><h3>Conclusion</h3><p>Comparing GPUs and TPUs isn’t exactly apples-to-apples. They represent fundamentally different philosophies in architecture, memory management, and execution.</p><p>In the modern enterprise, it isn’t usually a matter of choosing one over the other, but rather using each where it shines. For rapid iteration and smaller workloads, the flexibility of GPUs is unmatched. However, once a project hits a certain scale, the domain-specific architecture of the TPU becomes the clear winner in efficiency and throughput.</p><p>TPUs are as fast as they are because they are a specialized one-trick pony, but to truly harness that power requires a deeper understanding of the stack. The biggest challenge isn’t often the compute itself, but rather: “How do I feed data to the TPUs fast enough and efficiently enough so that it doesn’t become the bottleneck?” and ensuring your input pipeline is fast enough so that the hardware doesn’t sit idle.</p><p>In future posts, I’ll dive deeper into these advanced concepts to show how you can optimizing data pipelines to get the most out of your TPUs.</p><h4>BONUS: Google’s 8th-generation TPUs announced at Google Next</h4><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fwww.youtube.com%2Fembed%2F3Qw_CZkiQQg%3Ffeature%3Doembed&amp;display_name=YouTube&amp;url=https%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3D3Qw_CZkiQQg&amp;image=https%3A%2F%2Fi.ytimg.com%2Fvi%2F3Qw_CZkiQQg%2Fhqdefault.jpg&amp;type=text%2Fhtml&amp;schema=youtube" width="854" height="480" frameborder="0" scrolling="no"><a href="https://medium.com/media/e41887997802e9f7c3e2c573a2b0f3f5/href">https://medium.com/media/e41887997802e9f7c3e2c573a2b0f3f5/href</a></iframe><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=95a4ac995e9d" width="1" height="1" alt=""><hr><p><a href="https://medium.com/google-developer-experts/ml-acceleration-guide-tpus-vs-gpus-95a4ac995e9d">ML acceleration guide: TPUs vs GPUs</a> was originally published in <a href="https://medium.com/google-developer-experts">Google Developer Experts</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
    </channel>
</rss>