<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Google AI</title>
    <description>The latest articles on DEV Community by Google AI (googleai).</description>
    <link>https://dev.to/googleai</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Forganization%2Fprofile_image%2F11026%2F386b14d3-cc9a-4270-aba0-3e41cdfb9d85.jpg</url>
      <title>DEV Community: Google AI</title>
      <link>https://dev.to/googleai</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/googleai"/>
    <language>en</language>
    <item>
      <title>Gemini 3.6 Flash &amp; 3.5 Flash-Lite: Developer guide</title>
      <dc:creator>Patrick Loeber</dc:creator>
      <pubDate>Tue, 21 Jul 2026 16:08:54 +0000</pubDate>
      <link>https://dev.to/googleai/gemini-36-flash-35-flash-lite-developer-guide-i17</link>
      <guid>https://dev.to/googleai/gemini-36-flash-35-flash-lite-developer-guide-i17</guid>
      <description>&lt;p&gt;Gemini 3.6 Flash (&lt;code&gt;gemini-3.6-flash&lt;/code&gt;) and Gemini 3.5 Flash-Lite (&lt;code&gt;gemini-3.5-flash-lite&lt;/code&gt;) are generally available (GA) and ready for production use.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Gemini 3.6 Flash&lt;/strong&gt;: Stronger performance on complex agentic and multimodal tasks while reducing token usage, at a lower price point than 3.5 Flash.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Gemini 3.5 Flash-Lite&lt;/strong&gt;: The fastest, lowest-cost model in the 3.5 family. Outperforms prior Flash-Lite generations for high-throughput execution.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This guide explains what's new in each model, what API changes affect your code, and how to migrate.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tip&lt;/strong&gt;: You can automate this migration with a coding agent that supports &lt;a href="https://ai.google.dev/gemini-api/docs/coding-agents#gemini-interactions-api" rel="noopener noreferrer"&gt;skills&lt;/a&gt; (like Antigravity). Run &lt;code&gt;/gemini-interactions-api migrate my app to Gemini 3.6 Flash&lt;/code&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  New models
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Model&lt;/th&gt;
&lt;th&gt;Model ID&lt;/th&gt;
&lt;th&gt;Default thinking level&lt;/th&gt;
&lt;th&gt;Pricing&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Gemini 3.6 Flash&lt;/td&gt;
&lt;td&gt;&lt;code&gt;gemini-3.6-flash&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;medium&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;$1.50/1M input tokens and $7.50/1M output tokens&lt;/td&gt;
&lt;td&gt;Balances speed with intelligence for agentic and multimodal tasks.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Gemini 3.5 Flash-Lite&lt;/td&gt;
&lt;td&gt;&lt;code&gt;gemini-3.5-flash-lite&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;minimal&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;$0.30/1M input tokens and $2.50/1M output tokens&lt;/td&gt;
&lt;td&gt;The fastest, lowest-cost 3.5 model for high-throughput execution.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Both models support the 1M token context window, 64k max output tokens, thinking, and the full suite of built-in tools including &lt;a href="https://ai.google.dev/gemini-api/docs/computer-use" rel="noopener noreferrer"&gt;Computer Use&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;For complete specs, see the model pages:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://ai.google.dev/gemini-api/docs/models/gemini-3.6-flash" rel="noopener noreferrer"&gt;Gemini 3.6 Flash model page&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://ai.google.dev/gemini-api/docs/models/gemini-3.5-flash-lite" rel="noopener noreferrer"&gt;Gemini 3.5 Flash-Lite model page&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For detailed pricing, see the &lt;a href="https://ai.google.dev/gemini-api/docs/pricing" rel="noopener noreferrer"&gt;pricing page&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Quickstart
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;google&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;genai&lt;/span&gt;

&lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;genai&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Client&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="n"&gt;interaction&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;interactions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;gemini-3.6-flash&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nb"&gt;input&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Write a three.js script that renders an interactive 3D robot.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;interaction&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;output_text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  What's new in Gemini 3.6 Flash
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Token and turn reduction:&lt;/strong&gt; Completes multi-step workflows with fewer reasoning steps, conversational turns, and tool calls than Gemini 3.5. It also reduces execution loop spiraling.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Improved code generation:&lt;/strong&gt; Produces higher quality production-ready code with fewer unwanted edits and fewer debugging loops.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Better instruction following&lt;/strong&gt;: Reduces unwanted file changes during diagnostic tasks.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Strong multimodal and spatial reasoning:&lt;/strong&gt; Improved performance on chart interpretation, visual blueprint conversion, and multi-element web layout generation.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Upfront programmatic inspection:&lt;/strong&gt; Prefers running diagnostic code scripts before making changes more frequently than Gemini 3.5 Flash. This improves accuracy on complex tasks, but can add extra exploratory steps on simple frontend work.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Computer Use support:&lt;/strong&gt; Supported as native tool for agentic UI automation.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;UI styling preference&lt;/strong&gt;: Better at creating functional code, though human evaluators preferred earlier models for visual layout and styling. You can mitigate this by providing explicit design guidelines.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Default thinking effort (medium):&lt;/strong&gt; Uses the same &lt;code&gt;medium&lt;/code&gt; default thinking level as Gemini 3.5 Flash.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reduced pricing&lt;/strong&gt;: Lower output token costs ($7.50/1M vs. $9.00/1M for 3.5 Flash). Input tokens remain at $1.50/1M.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What's new in Gemini 3.5 Flash-Lite
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Reduced task execution latency:&lt;/strong&gt; Highest throughput in the 3.5 family for high-volume data parsing and document extraction.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Enhanced reasoning and multimodal performance:&lt;/strong&gt; Strong migration path from Gemini 2.5 Flash, with higher scores on reasoning tasks like HLE (18.0% vs. 11.0%) and multimodal benchmarks like CharXIV (74.5% vs. 63.7%).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Subagent orchestration and tool reliability:&lt;/strong&gt; Improves tool execution reliability for code execution, search, and MCP workflows. Increase the thinking level for autonomous planning and complex subagent tasks.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Improved document understanding:&lt;/strong&gt; Improves accuracy on document parsing and structured data extraction. Experiment with both minimal and high thinking levels depending on document complexity.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Interactive web coding and tabular data processing:&lt;/strong&gt; Performs strongly on frontend JavaScript and tabular data processing by planning via lightweight code execution.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Chatbot and persona persistence:&lt;/strong&gt; Stronger multi-turn instruction following and persona consistency over Gemini 3.1 Flash-Lite.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Computer Use support:&lt;/strong&gt; Supported as native tool for agentic UI automation.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Choosing the right Flash or Flash-Lite model
&lt;/h2&gt;

&lt;p&gt;Use this table to select the right model and migration path for your workloads.&lt;/p&gt;

&lt;p&gt;Both models require removing deprecated sampling parameters (&lt;code&gt;temperature&lt;/code&gt;, &lt;code&gt;top_p&lt;/code&gt;, &lt;code&gt;top_k&lt;/code&gt;) and prefilled model turns. See API changes for details.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Model&lt;/th&gt;
&lt;th&gt;Primary use cases&lt;/th&gt;
&lt;th&gt;Recommended migration target&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;Gemini 3.6 Flash&lt;/strong&gt;&lt;br&gt;&lt;code&gt;gemini-3.6-flash&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Code generation, spatial/multimodal reasoning, multi-step agentic workflows&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Gemini 3.5 Flash&lt;/strong&gt;, &lt;strong&gt;Gemini 3 Flash (Preview)&lt;/strong&gt;, or &lt;strong&gt;Gemini 3.1 Pro&lt;/strong&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;Gemini 3.5 Flash-Lite&lt;/strong&gt;&lt;br&gt;&lt;code&gt;gemini-3.5-flash-lite&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Autonomous subagent execution, high-volume data analysis and document extraction, structured JSON parsing&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Gemini 3.1 Flash-Lite&lt;/strong&gt; or &lt;strong&gt;Gemini 2.5 Flash&lt;/strong&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Updated Antigravity agent
&lt;/h2&gt;

&lt;p&gt;Due to its improved performance, Gemini 3.6 Flash is now the new default model powering the &lt;a href="https://ai.google.dev/gemini-api/docs/antigravity-agent" rel="noopener noreferrer"&gt;Antigravity agent&lt;/a&gt; in Gemini Managed Agents. This can be changed by setting a new field on the API.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;google&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;genai&lt;/span&gt;

&lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;genai&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Client&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="n"&gt;interaction&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;interactions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;agent&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;antigravity-preview-05-2026&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nb"&gt;input&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Read Hacker News, summarize the top 10 stories, and save the results as a PDF.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;environment&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;remote&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;interaction&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;output_text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  API changes and parameter updates
&lt;/h2&gt;

&lt;p&gt;Starting with Gemini 3.6 Flash and Gemini 3.5 Flash-Lite, the following API changes apply to these models and all future Gemini model releases.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Sampling parameter deprecation&lt;/strong&gt;: &lt;code&gt;temperature&lt;/code&gt;, &lt;code&gt;top_p&lt;/code&gt;, and &lt;code&gt;top_k&lt;/code&gt; are deprecated. The API ignores these parameters and returns an error in future model generations.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Prefilled model turn validation&lt;/strong&gt;: Prefilling model turns is no longer supported. If the last non-empty turn in the request is a &lt;code&gt;model&lt;/code&gt; turn, the API returns a &lt;code&gt;400&lt;/code&gt; error.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Below are detailed explanations and code samples for each API change.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Sampling parameter deprecation (&lt;code&gt;temperature&lt;/code&gt;, &lt;code&gt;top_p&lt;/code&gt;, &lt;code&gt;top_k&lt;/code&gt;)
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;temperature&lt;/code&gt;, &lt;code&gt;top_p&lt;/code&gt;, and &lt;code&gt;top_k&lt;/code&gt; are deprecated and ignored. In future model generations, supplying these parameters returns an HTTP 400 error. &lt;strong&gt;Remove these parameters from all requests.&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# ⚠️ Remove these parameters (deprecated)
&lt;/span&gt;&lt;span class="n"&gt;generation_config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;temperature&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;top_p&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.9&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;top_k&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To improve determinism, define a system instruction with explicit rules for your&lt;br&gt;
specific use case.&lt;/p&gt;
&lt;h3&gt;
  
  
  2. Prefilled model turn validation {:#prefilled-model-turn-validation}
&lt;/h3&gt;

&lt;p&gt;API requests ending with a non-empty model role turn are disallowed and return an &lt;strong&gt;HTTP 400 Error&lt;/strong&gt;.&lt;/p&gt;
&lt;h4&gt;
  
  
  ⚠️ Avoid
&lt;/h4&gt;

&lt;p&gt;In legacy &lt;code&gt;generateContent&lt;/code&gt; or raw REST payloads, ending with a model role turn&lt;br&gt;
is now disallowed:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="err"&gt;/*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;❌&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;DO&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;NOT:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;End&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;payload&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;contents&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;with&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;'model'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;role&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;turn&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;*/&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"contents"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"role"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"user"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"parts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="nl"&gt;"text"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Translate 'Hello world' to Spanish."&lt;/span&gt;&lt;span class="p"&gt;}]},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"role"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"model"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"parts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="nl"&gt;"text"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Translation:"&lt;/span&gt;&lt;span class="p"&gt;}]}&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="err"&gt;/*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;❌&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Returns&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;error&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;*/&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  ✅ Recommended Migration (Interactions API)
&lt;/h4&gt;

&lt;p&gt;In the Interactions API, model turns are not manually prefilled. If your application previously prefilled a model turn to suppress preambles or force JSON formatting, use system_instruction or &lt;a href="https://ai.google.dev/gemini-api/docs/structured-output" rel="noopener noreferrer"&gt;Structured outputs&lt;/a&gt; instead.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# ✅ RECOMMENDED: Use system_instruction in the Interactions API to specify output format
&lt;/span&gt;&lt;span class="n"&gt;interaction&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;interactions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;gemini-3.6-flash&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nb"&gt;input&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Translate &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Hello world&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt; to Spanish.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;system_instruction&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Output only the translation without introductory text.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Migration checklist
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Tip&lt;/strong&gt;: You can automate this migration with a coding agent that supports &lt;a href="https://ai.google.dev/gemini-api/docs/coding-agents#gemini-interactions-api" rel="noopener noreferrer"&gt;skills&lt;/a&gt; (like Antigravity). Run &lt;code&gt;/gemini-interactions-api migrate my app to Gemini 3.6 Flash&lt;/code&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Migrate to gemini-3.6-flash
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Update Model ID:&lt;/strong&gt; Change your target model string to &lt;code&gt;gemini-3.6-flash&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Remove deprecated sampling parameters:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;Strip &lt;code&gt;temperature&lt;/code&gt;, &lt;code&gt;top_p&lt;/code&gt;, and &lt;code&gt;top_k&lt;/code&gt; from generation configs.
&lt;/li&gt;
&lt;li&gt;Replace &lt;code&gt;thinking_budget&lt;/code&gt; with the string enum &lt;code&gt;thinking_level&lt;/code&gt; set to &lt;code&gt;"medium"&lt;/code&gt; or &lt;code&gt;"high"&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Remove &lt;code&gt;candidate_count&lt;/code&gt; (unsupported in Gemini 3.x).&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Enforce turn validation rules:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;Standardize multi-turn conversations on server-side &lt;code&gt;previous_interaction_id&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Remove prefilled model turns.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Audit function calling:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;Place multimodal assets inside the response payload.&lt;/li&gt;
&lt;li&gt;Format inline instructions using &lt;code&gt;\n\n&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;If you see &lt;code&gt;Malformed_Function_Call&lt;/code&gt; errors tied to pre-tool text, see &lt;a href="https://ai.google.dev/gemini-api/docs/function-calling#workarounds-for-pre-tool-text-requirements" rel="noopener noreferrer"&gt;Workarounds for pre-tool text requirements&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Only if using generateContent API: Ensure all &lt;code&gt;FunctionResponse&lt;/code&gt; objects include &lt;code&gt;call_id&lt;/code&gt; and &lt;code&gt;name&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Baseline Gemini 3.x requirements:&lt;/strong&gt; For SDK updates and thought signature preservation, see the &lt;a href="https://ai.google.dev/gemini-api/docs/whats-new-gemini-3.5#migration" rel="noopener noreferrer"&gt;Gemini 3.5 Migration Checklist&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Migrate to gemini-3.5-flash-lite
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Update Model ID:&lt;/strong&gt; Change your target model string to &lt;code&gt;gemini-3.5-flash-lite&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Configure thinking effort level:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;For high-volume extraction, routing, or classification: leave &lt;code&gt;thinking_level&lt;/code&gt; at &lt;code&gt;"minimal"&lt;/code&gt; (default) for maximum throughput.&lt;/li&gt;
&lt;li&gt;For autonomous subagents with tool calls, code execution, or multi-step reasoning: set &lt;code&gt;thinking_level&lt;/code&gt; to &lt;code&gt;"medium"&lt;/code&gt; or &lt;code&gt;"high"&lt;/code&gt; to prevent premature tool termination.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Remove deprecated parameters and validate function calling:&lt;/strong&gt; Apply the same rules as for 3.6 Flash above.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Baseline Gemini 3.x requirements:&lt;/strong&gt; Refer to the &lt;a href="https://ai.google.dev/gemini-api/docs/whats-new-gemini-3.5#migration" rel="noopener noreferrer"&gt;Gemini 3.5 Migration Checklist&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Next steps
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Review API specs on the &lt;a href="https://ai.google.dev/gemini-api/docs/models" rel="noopener noreferrer"&gt;Models Overview&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Explore multi-agent orchestration in the &lt;a href="https://ai.google.dev/gemini-api/docs/interactions" rel="noopener noreferrer"&gt;Interactions API Guide&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Test and refine prompts in &lt;a href="https://aistudio.google.com/" rel="noopener noreferrer"&gt;Google AI Studio&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Read the &lt;a href="https://blog.google/innovation-and-ai/models-and-research/gemini-models/gemini-3-6-flash-3-5-flash-lite-3-5-flash-cyber/" rel="noopener noreferrer"&gt;blog post&lt;/a&gt;, including info on the new Gemini 3.5 Flash Cyber model&lt;/li&gt;
&lt;/ul&gt;

</description>
    </item>
    <item>
      <title>Google Cloud Labs: Accelerate AI with Cloud Run</title>
      <dc:creator>Shir Meir Lador</dc:creator>
      <pubDate>Tue, 21 Jul 2026 13:00:00 +0000</pubDate>
      <link>https://dev.to/googleai/google-cloud-labs-accelerate-ai-with-cloud-run-md3</link>
      <guid>https://dev.to/googleai/google-cloud-labs-accelerate-ai-with-cloud-run-md3</guid>
      <description>&lt;h2&gt;
  
  
  Moving Beyond the Prototype
&lt;/h2&gt;

&lt;p&gt;The AI landscape has shifted. While "vibe coding" with tools like Antigravity and AI Studio lets you build and deploy complex agents in minutes, the real work begins on "Day 2". Moving from a magical prototype to a hardened, production-grade application requires professional AI engineering. We're excited to bring back the &lt;strong&gt;Accelerate AI with Cloud Run roadshow&lt;/strong&gt; for 2026. This year, we've updated our curriculum to focus on the full AI agent lifecycle, giving you the keys to productionizing and scaling agentic workloads on Google Cloud's serverless platform.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Coffee Shop Journey: A Hands-On Experience
&lt;/h2&gt;

&lt;p&gt;Experience the ease of building advanced AI agents on Cloud Run through 'The Coffee Shop Journey'. This interactive session is designed to guide you through the full lifecycle of an AI agent, moving beyond prototyping to focus on real business use cases. You will solve real-world business problems as you evolve from launching a simple cafe to building complex, intelligent assistants.&lt;/p&gt;

&lt;p&gt;Our curriculum covers the core pillars of modern AI development:  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The Basics:&lt;/strong&gt; Gain familiarity with Cloud Run by deploying a simple web app (a Coffee Shop launch scenario) to understand the platform fundamentals.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Build a Coffee Recommendation Agent:&lt;/strong&gt; Create a personalized AI assistant using Google's Agent Development Kit (ADK) and Retrieval-Augmented Generation (RAG).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Optimize Coffee Stand Locations:&lt;/strong&gt; Use Gemma 4 and the BigQuery MCP server to identify the most profitable locations for new coffee stands by analyzing popular bike routes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Personal Productivity Assistant for Store Managers:&lt;/strong&gt; Create a personal productivity assistant using Cloud Run to help a coffee shop manager with daily operational tasks and scheduling.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Master Advanced Features with Antigravity 2.0:&lt;/strong&gt; Learn how to use skills, context, rules, and hooks with Antigravity 2.0 to build new features for your Cloud Run applications.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Production-Grade AI on Cloud Run
&lt;/h2&gt;

&lt;p&gt;Get first-hand experience with the platform innovations that make Cloud Run the ideal home for production-grade agentic workloads. Through hands-on exercises, you will learn to build, scale, and orchestrate &lt;strong&gt;long-running agents&lt;/strong&gt; using Google's ADK and Antigravity 2.0. Additionally, you will utilize BigQuery MCP for automated, &lt;strong&gt;data-driven expansion strategies,&lt;/strong&gt; and experience low-latency inference for frontier models using &lt;strong&gt;Cloud Run's GPU offerings&lt;/strong&gt; without the traditional overhead of cluster management.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fi626y1vwpd7mkpgzicu9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fi626y1vwpd7mkpgzicu9.png" width="800" height="876"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Ready to Build for Scale? Join us in North America
&lt;/h2&gt;

&lt;p&gt;Don't just witness the AI revolution - build it. Find the workshop in your city and secure your spot today! Let's transform your AI journey from a simple prototype into a powerful, production reality.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;City&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Date&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Registration Link&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Atlanta, GA (as a part of Atlanta Tech week)&lt;/td&gt;
&lt;td&gt;August 12-13&lt;/td&gt;
&lt;td&gt;
&lt;a href="https://www.renderatl.com/tickets" rel="noopener noreferrer"&gt;https://www.renderatl.com/tickets&lt;/a&gt;&lt;br&gt;Event tickets grant access to the workshops on a first-come, first-served basis.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Sunnyvale, CA&lt;/td&gt;
&lt;td&gt;August 13&lt;/td&gt;
&lt;td&gt;&lt;a href="https://rsvp.withgoogle.com/events/google-cloud-labs-accelerate-ai-on-cloud-run-sunnyvale" rel="noopener noreferrer"&gt;Register now!&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Toronto, Canada&lt;/td&gt;
&lt;td&gt;August 27&lt;/td&gt;
&lt;td&gt;&lt;a href="https://rsvp.withgoogle.com/events/google-cloud-labs-accelerate-ai-on-cloud-run-toronto" rel="noopener noreferrer"&gt;Register now!&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Seattle, WA&lt;/td&gt;
&lt;td&gt;September&lt;/td&gt;
&lt;td&gt;Registration opens late July!&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;New York City, NY&lt;/td&gt;
&lt;td&gt;October&lt;/td&gt;
&lt;td&gt;Registration opens late July!&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Los Angeles, CA&lt;/td&gt;
&lt;td&gt;November&lt;/td&gt;
&lt;td&gt;Registration opens late July!&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Boston, MA&lt;/td&gt;
&lt;td&gt;October&lt;/td&gt;
&lt;td&gt;Registration opens late July!&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Washington D.C.&lt;/td&gt;
&lt;td&gt;October&lt;/td&gt;
&lt;td&gt;Registration opens late July!&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Registration Update:&lt;/strong&gt; Links for our &lt;strong&gt;September&lt;/strong&gt;, &lt;strong&gt;October&lt;/strong&gt;, and &lt;strong&gt;November&lt;/strong&gt; workshops will be added to this page in &lt;strong&gt;late July.&lt;/strong&gt; Stay tuned!&lt;/p&gt;

</description>
      <category>ai</category>
      <category>googlecloud</category>
      <category>agents</category>
      <category>antigravity</category>
    </item>
    <item>
      <title>What is an "agentic harness," actually?</title>
      <dc:creator>Tilde A. Thurium</dc:creator>
      <pubDate>Thu, 16 Jul 2026 21:19:20 +0000</pubDate>
      <link>https://dev.to/googleai/what-is-an-agentic-harness-actually-4oie</link>
      <guid>https://dev.to/googleai/what-is-an-agentic-harness-actually-4oie</guid>
      <description>&lt;p&gt;I've been hearing the word "harness" thrown around a lot lately. I assumed it just meant &lt;em&gt;"the IDE"&lt;/em&gt; or &lt;em&gt;"whatever app is running your agent."&lt;/em&gt; Turns out, it goes a little deeper than that.&lt;/p&gt;

&lt;p&gt;So I sat down with &lt;a class="mentioned-user" href="https://dev.to/greggyb"&gt;@greggyb&lt;/a&gt; and asked him to explain it from the ground up: what an LLM actually does, what turns it into an agent, and where the harness fits into all of it.&lt;/p&gt;


&lt;div class="crayons-card c-embed"&gt;

  
&lt;h2&gt;
  
  
  What's in the video
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Simon Willison's definition of an agent: an LLM with tools, running in a loop to accomplish a goal&lt;/li&gt;
&lt;li&gt;What "tools" really means under the hood, and how function calling lets an LLM pull in context it wasn't trained on&lt;/li&gt;
&lt;li&gt;The loop part: how an agent programmatically checks its own output to decide if it's actually done&lt;/li&gt;
&lt;li&gt;Why the harness is everything &lt;em&gt;after&lt;/em&gt; the LLM, and why that's a different thing than the interface&lt;/li&gt;
&lt;li&gt;Why you can swap interfaces without touching the underlying harness, and why some agents don't need a UI at all

&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;



&lt;p&gt;The point that stuck with me is the harness isn't the app you're looking at. It's the invisible plumbing deciding whether the agent keeps going or calls it done.&lt;/p&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/W9BX0jyzd2k"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;Did your mental model of "harness" match up? Or were you picturing something else too?&lt;/p&gt;

</description>
      <category>ai</category>
      <category>agents</category>
      <category>discuss</category>
      <category>llm</category>
    </item>
    <item>
      <title>DiffusionGemma: The Developer Guide</title>
      <dc:creator>Gemini Team</dc:creator>
      <pubDate>Thu, 16 Jul 2026 14:27:09 +0000</pubDate>
      <link>https://dev.to/googleai/diffusiongemma-the-developer-guide-5a3l</link>
      <guid>https://dev.to/googleai/diffusiongemma-the-developer-guide-5a3l</guid>
      <description>&lt;p&gt;Following our announcement in our &lt;a href="https://blog.google/innovation-and-ai/technology/developers-tools/diffusion-gemma-faster-text-generation/" rel="noopener noreferrer"&gt;launch blog post&lt;/a&gt;, we are sharing this developer guide to help you understand, serve and customize this experimental model.&lt;/p&gt;

&lt;p&gt;Built on the Gemma 4 backbone, &lt;strong&gt;DiffusionGemma&lt;/strong&gt; introduces several milestones for developer workflows:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Compute-bound parallel generation&lt;/strong&gt;: Bypasses memory-bandwidth limitations by shifting the bottleneck to compute, delivering up to 4x faster token generation on GPUs (up to 700+ tokens per second on NVIDIA GeForce RTX 5090 and 1000+ tokens per second on a single NVIDIA H100).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Bidirectional context &amp;amp; self-correction:&lt;/strong&gt; Uses bidirectional attention to evaluate the entire text block simultaneously during generation, enabling real-time error correction and parallel context propagation.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Developer-friendly sizes&lt;/strong&gt;: Designed as a 26B Mixture of Experts (MoE) model that activates only 3.8B parameters during inference, allowing quantized deployment within 18 GB VRAM limits.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  The Architecture
&lt;/h2&gt;

&lt;p&gt;For developers building with traditional LLMs on GPUs, the primary bottleneck is memory bandwidth. Autoregressive language models must repeatedly load model weights from memory to generate text one token at a time. DiffusionGemma bypasses this limitation by shifting the bottleneck from memory bandwidth to compute, generating and refining a &lt;strong&gt;256-token canvas&lt;/strong&gt; in parallel. By providing the GPU with a large parallel workload, it utilizes tensor cores that would otherwise sit idle during local serving.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Uniform State Diffusion:&lt;/strong&gt; Instead of predicting tokens sequentially, DiffusionGemma starts with a canvas of random placeholder tokens and iteratively refines them in parallel. Over multiple denoising passes, highly confident tokens help resolve adjacent positions, causing the entire sequence to snap into focus.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Block Autoregressive Diffusion for Variable Length Generation:&lt;/strong&gt; For sequences longer than 256 tokens, once a 256-token block is fully denoised, the model processes and commits it to the KV cache. The model then transitions to the next block, initializing a fresh 256-token canvas conditioned on the previously committed history. This combines parallel block speed with the sequential stability of autoregressive models.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Showcase: Solving Sudoku with Parallel Denoising&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Traditional autoregressive models struggle with strict, multivariable constrained problems like Sudoku. Because they generate text strictly from left to right, they cannot evaluate future placeholders or backtrack.&lt;/p&gt;

&lt;p&gt;To demonstrate customization of DiffusionGemma, we are releasing a &lt;a href="https://github.com/google-deepmind/gemma/tree/main/gemma/diffusion" rel="noopener noreferrer"&gt;fine-tuning recipe and results&lt;/a&gt; using &lt;a href="https://github.com/google/hackable_diffusion" rel="noopener noreferrer"&gt;Hackable Diffusion&lt;/a&gt;, a modular JAX research toolbox. This training setup focuses on a classic multi-variable grid task: &lt;strong&gt;the Sudoku Solver&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Why Sudoku is Interesting for Diffusion&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;In an 81-character Sudoku string representation (where empty cells are marked with periods), every digit is bound by strict intersecting horizontal, vertical, and 9x9 grid constraints.&lt;/p&gt;

&lt;p&gt;&amp;nbsp;&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F53ktq0b8t68564vuiy0m.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F53ktq0b8t68564vuiy0m.png" alt="DiffusionGemma-Sudoku-Comparison" width="800" height="203"&gt;&lt;/a&gt;&lt;br&gt;
&amp;nbsp;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Bidirectional Context Propagation:&lt;/strong&gt; Unlike autoregressive models, DiffusionGemma's denoising step allows every canvas query to attend to all positions in parallel. Information flows symmetrically across the board, resolving global dependencies in each step.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Error Correction via Re-Noising&lt;/strong&gt;: Under &lt;strong&gt;Uniform State Diffusion&lt;/strong&gt;, the model evaluates the entire board simultaneously. If confidence drops, the sampler replaces digits with random ones, allowing for continuous self-correction.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Efficient Early Stopping&lt;/strong&gt;: Fine-tuning on Sudoku shows that adapters enhance early stopping. The SFT-tuned model stabilizes faster than the base model, allowing the engine to halt sooner, reducing latency and compute costs.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&amp;nbsp;&lt;br&gt;
  
    controls &lt;br&gt;
    loop &lt;br&gt;
    muted &lt;br&gt;
    autoplay &lt;br&gt;
    playsinline &lt;br&gt;
    width="100%" &lt;br&gt;
    style="border-radius: 8px; display: block; margin-bottom: 0; padding-bottom: 0px;"&lt;br&gt;
    aria-label=""&amp;gt;&lt;br&gt;
    &lt;br&gt;
    Your browser does not support the video tag.&lt;br&gt;
  &lt;/p&gt;

&lt;center&gt;&lt;small&gt;Left: DiffusionGemma generating Sudoku output. The base model is unable to solve the Sudoku after 48 steps. Right: Fine-tuned (SFT) DiffusionGemma solves the puzzle after 12 steps. It is able to complete early thanks to adaptive stopping.&lt;/small&gt;&lt;/center&gt;

&lt;p&gt;&amp;nbsp;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Performance Impact:&lt;/strong&gt; While the base DiffusionGemma model is not specifically trained to solve Sudoku puzzles (~0% success rate), applying the simple JAX SFT recipe on a Sudoku dataset raises correctness to &lt;strong&gt;80% success&lt;/strong&gt;, while decreasing the overall inference step count.&lt;/p&gt;
&lt;h2&gt;
  
  
  &lt;strong&gt;Block Autoregressive Denoising&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;To enable block autoregressive denoising, DiffusionGemma alternates between incremental prefill and denoising during inference:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Prefill / Incremental Prefill (Causal):&lt;/strong&gt; Uses &lt;em&gt;causal attention&lt;/em&gt; to ingest the prompt context and write to the KV cache. This runs once to prefill the initial context and then once per block to append each finalized 256-token canvas to the KV cache before proceeding to denoising the next canvas.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Denoising (Bidirectional):&lt;/strong&gt; Uses &lt;em&gt;bidirectional attention&lt;/em&gt; to iteratively denoise the canvas. Query tokens at any position on the canvas can attend to all other canvas tokens (as well as KV cache), letting the model process context bidirectionally.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fq9d1m9tn6k14coe5agkj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fq9d1m9tn6k14coe5agkj.png" alt="diffusion_architecture" width="800" height="935"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This architectural choice makes the following possible:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Global Context Awareness&lt;/strong&gt;: Unlike autoregressive (AR) models that only "look backward," the Denoiser's bidirectional attention allows every token on the canvas to attend to every other token. This makes the model much more effective at solving non-sequential problems, such as Sudoku, where a digit in the first cell must respect constraints in the last cell.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Self-Correction&lt;/strong&gt;: Because the model iteratively refines the whole canvas, it can "fix" earlier mistakes. If a token's confidence drops during a pass, the sampler can re-noise and replace it. This is a capability AR models lack since they are "stuck" with a token once it is generated, especially during long output sequences.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Efficient Long-Context Scaling&lt;/strong&gt;: The "block-autoregressive" approach allows the model to handle long sequences. It combines the parallel speed of diffusion for blocks with the proven sequential stability of AR models for long-form text.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Simplified Deployment&lt;/strong&gt;: Using the same architecture as the Gemma 4 26B A4B model means developers only need to implement a denoising step, making it easier to integrate into existing serving frameworks like vLLM.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  &lt;strong&gt;Serving DiffusionGemma&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;To serve this experimental architecture efficiently, we worked with the vLLM team to implement DiffusionGemma into vLLM. This integration allows the engine to run the iterative parallel denoising loops efficiently across batched request streams.&lt;/p&gt;

&lt;p&gt;Developers can deploy DiffusionGemma out of the box using vLLM's standard OpenAI-compatible local server.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;vllm serve google/diffusiongemma-26B-A4B-it &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--max-model-len&lt;/span&gt; 262144 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--max-num-seqs&lt;/span&gt; 4 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--gpu-memory-utilization&lt;/span&gt; 0.85 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--attention-backend&lt;/span&gt; TRITON_ATTN &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--generation-config&lt;/span&gt; vllm &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--hf-overrides&lt;/span&gt; &lt;span class="s1"&gt;'{"diffusion_sampler": "entropy_bound", "diffusion_entropy_bound": 0.1}'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--diffusion-config&lt;/span&gt; &lt;span class="s1"&gt;'{"canvas_length": 256}'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--enable-chunked-prefill&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  &lt;strong&gt;Getting Started Today&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Ready to explore the frontier of non-autoregressive text generation? Take a look at the following resources to find out more:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Download the Weights:&lt;/strong&gt; Access the weights of the &lt;a href="https://huggingface.co/google/diffusiongemma-26B-A4B-it" rel="noopener noreferrer"&gt;experimental model&lt;/a&gt; (released under the Apache 2.0 license) directly on Hugging Face.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Integrate &amp;amp; Learn:&lt;/strong&gt; Review the &lt;a href="https://newsletter.maartengrootendorst.com/p/a-visual-guide-to-diffusiongemma" rel="noopener noreferrer"&gt;Visual Guide to DiffusionGemma&lt;/a&gt; to understand the mechanics of text-based diffusion models. Read more about DiffusionGemma in the &lt;a href="http://ai.google.dev/gemma/docs/diffusiongemma" rel="noopener noreferrer"&gt;Gemma documentation&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Use Your Favorite Inference Frameworks:&lt;/strong&gt; Run the model efficiently using &lt;a href="https://vllm-project.github.io/2026/06/10/diffusion-gemma" rel="noopener noreferrer"&gt;vLLM&lt;/a&gt;, &lt;a href="https://huggingface.co/google/diffusiongemma-26B-A4B-it" rel="noopener noreferrer"&gt;Hugging Face Transformers&lt;/a&gt;, SGLang, and &lt;a href="https://huggingface.co/collections/mlx-community/diffusiongemma" rel="noopener noreferrer"&gt;MLX&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Adapt &amp;amp; Fine-Tune:&lt;/strong&gt; For rapid experimentation, we are releasing the &lt;a href="https://github.com/google-deepmind/gemma/tree/main/gemma/diffusion" rel="noopener noreferrer"&gt;official training recipes&lt;/a&gt; using &lt;a href="https://github.com/google/hackable_diffusion" rel="noopener noreferrer"&gt;Hackable Diffusion&lt;/a&gt; You can also explore efficient fine-tuning using &lt;a href="https://unsloth.ai/docs/models/diffusiongemma" rel="noopener noreferrer"&gt;Unsloth&lt;/a&gt; or &lt;a href="https://github.com/NVIDIA-NeMo/Automodel/blob/main/docs/guides/dllm/diffusiongemma.md" rel="noopener noreferrer"&gt;NVIDIA NeMo&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Deploy Your Way:&lt;/strong&gt; Instantly deploy on Google Cloud using &lt;a href="https://console.cloud.google.com/agent-platform/publishers/google/model-garden/diffusiongemma" rel="noopener noreferrer"&gt;Model Garden&lt;/a&gt; or via &lt;a href="https://catalog.ngc.nvidia.com/orgs/nim/teams/google/containers/diffusiongemma-26b-a4b-it?version=latest" rel="noopener noreferrer"&gt;NVIDIA NIM&lt;/a&gt;. The model is optimized natively across the hardware stack from consumer RTX 4090 and 5090 cards to enterprise Hopper and Blackwell servers.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>gemma</category>
      <category>google</category>
      <category>ai</category>
      <category>llm</category>
    </item>
    <item>
      <title>DiffusionGemma: 4x faster text generation</title>
      <dc:creator>Gemini Team</dc:creator>
      <pubDate>Tue, 14 Jul 2026 15:06:56 +0000</pubDate>
      <link>https://dev.to/googleai/diffusiongemma-4x-faster-text-generation-fmd</link>
      <guid>https://dev.to/googleai/diffusiongemma-4x-faster-text-generation-fmd</guid>
      <description>&lt;h4&gt;
  
  
  Our newest open experimental model delivers up to 4x faster inference on dedicated GPUs and opens the door to exploring speed-critical, interactive local workflows.
&lt;/h4&gt;




&lt;p&gt;Introducing DiffusionGemma, an experimental open model that explores text diffusion, an exceptionally fast approach to text generation. Released under an Apache 2.0 license, this 26B Mixture of Experts (MoE) model moves beyond the sequential token-by-token processing of typical autoregressive Large Language Models (LLMs). Instead, it generates entire blocks of text simultaneously, delivering up to 4x faster text generation on GPUs.&lt;/p&gt;

&lt;p&gt;&amp;nbsp;&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F575rypju8jdcgm6nt103.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F575rypju8jdcgm6nt103.webp" alt="Intelligence vs. Latency" width="799" height="450"&gt;&lt;/a&gt;&lt;br&gt;
&amp;nbsp;&lt;/p&gt;

&lt;p&gt;Built upon the industry-leading intelligence-per-parameter of our Gemma 4 family and cutting-edge &lt;a href="https://deepmind.google/models/gemini-diffusion/" rel="noopener noreferrer"&gt;Gemini Diffusion research&lt;/a&gt;, DiffusionGemma integrates a novel diffusion head designed to maximize generation speed. While autoregressive Gemma 4 models remain the standard for high-quality production outputs, DiffusionGemma is designed for researchers and developers exploring speed-critical, interactive local workflows such as in-line editing, rapid iteration, and generating non-linear text structures.&lt;/p&gt;

&lt;h2&gt;
  
  
  Unlocking new value for developers
&lt;/h2&gt;

&lt;p&gt;Developers building real-time interactive AI applications often struggle with the latency bottlenecks of local inference. DiffusionGemma addresses these challenges directly, with some key trade-offs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Blazing fast inference:&lt;/strong&gt; By shifting the decode bottleneck from memory-bandwidth to compute, DiffusionGemma generates up to 4x faster token output on dedicated GPUs. (1000+ tokens per second on a single NVIDIA H100, 700+ tokens per second on NVIDIA GeForce RTX 5090).¹
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Accessible hardware footprint:&lt;/strong&gt; Operating as a 26B total Mixture of Experts (MoE) model that activates only 3.8B parameters during inference, DiffusionGemma fits comfortably within 18GB VRAM limits of high-end dedicated consumer GPUs when quantized.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Bi-directional attention&lt;/strong&gt;: Generating 256 tokens in parallel with each forward pass allows every token to attend to all others. This provides significant advantages for non-linear domains such as in-line editing, code infilling, amino acid sequences or mathematical graphs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Intelligent self-correction:&lt;/strong&gt; The model iteratively refines its own output, allowing it to evaluate the entire text block at once to fix mistakes in real-time.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Experimental status &amp;amp; production recommendations:&lt;/strong&gt; Because it prioritizes speed and parallel layout generation, DiffusionGemma’s overall output quality is lower than standard Gemma 4. For applications that demand maximum quality, we recommend deploying standard Gemma 4.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&amp;nbsp;&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fig2bjm5pobf8isaxk88c.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fig2bjm5pobf8isaxk88c.webp" alt="DiffusionGemma Benchmark" width="500" height="281"&gt;&lt;/a&gt;&lt;br&gt;
&amp;nbsp;&lt;/p&gt;

&lt;p&gt;You can improve DiffusionGemma's performance on specific tasks through fine-tuning. In the example below, &lt;a href="https://unsloth.ai/docs/models/diffusiongemma" rel="noopener noreferrer"&gt;Unsloth&lt;/a&gt; fine-tuned DiffusionGemma to play Sudoku — a task autoregressive models struggle with because each token depends on future tokens. DiffusionGemma's bi-directional attention makes this much easier.&lt;/p&gt;

&lt;p&gt;&amp;nbsp;&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fl1terwz875dm77pnmama.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fl1terwz875dm77pnmama.gif" alt="Fine-tuned DiffusionGemma solving Sudoku." width="760" height="454"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;center&gt;&lt;small&gt;Fine-tuned DiffusionGemma solving Sudoku.&lt;/small&gt;&lt;/center&gt;

&lt;p&gt;&amp;nbsp;&lt;/p&gt;

&lt;h2&gt;
  
  
  Why diffusion for text?
&lt;/h2&gt;

&lt;p&gt;While the AI research community has explored diffusion-based text generation for years, applying it to large models has remained a challenge. DiffusionGemma changes this by shifting how models use hardware.&lt;/p&gt;

&lt;h3&gt;
  
  
  The trade-off with traditional models
&lt;/h3&gt;

&lt;p&gt;Most language models act like a typewriter, generating one token at a time from left to right. In the cloud, this is efficient because servers can batch thousands of user requests together to share the hardware load. But when run locally for a single user, this word-by-word process leaves your dedicated GPU or TPU underutilized — it spends most of its time simply waiting for the next "keystroke."&lt;/p&gt;

&lt;p&gt;DiffusionGemma reverses this inefficiency. Instead of predicting words sequentially, it drafts an entire 256-token paragraph simultaneously. By giving the computer's processor a larger chunk of work at once, DiffusionGemma utilizes your hardware to its full potential. It upgrades your model inference from a single, sequential typewriter to a massive printing press that stamps the entire block of text simultaneously.&lt;/p&gt;

&lt;p&gt;&amp;nbsp;&lt;br&gt;
  
    controls &lt;br&gt;
    loop &lt;br&gt;
    muted &lt;br&gt;
    autoplay &lt;br&gt;
    playsinline &lt;br&gt;
    width="100%" &lt;br&gt;
    style="border-radius: 8px; display: block; margin-bottom: 0; padding-bottom: 0px;"&lt;br&gt;
    aria-label="DiffusionGemma text-to-3D SVG demo by Hugging Face. Step-by-step generation, slowed down for visualization."&amp;gt;&lt;br&gt;
    &lt;br&gt;
    Your browser does not support the video tag.&lt;br&gt;
  &lt;/p&gt;

&lt;center&gt;&lt;small&gt;DiffusionGemma text-to-3D SVG demo by Hugging Face. Step-by-step generation.&lt;/small&gt;&lt;/center&gt;

&lt;p&gt;&amp;nbsp;&lt;/p&gt;

&lt;p&gt;This means DiffusionGemma's speedup is designed for local and low-concurrency inference. In high-QPS cloud serving, autoregressive models can be deployed to saturate compute efficiently, so DiffusionGemma's parallel decoding offers diminishing returns and can result in higher serving costs. The throughput advantage is strongest at low-to-medium batch sizes on a single accelerator.&lt;/p&gt;

&lt;h3&gt;
  
  
  How text diffusion works
&lt;/h3&gt;

&lt;p&gt;Similar to AI image generators that &lt;a href="https://research.google/blog/on-device-diffusion-plugins-for-conditioned-text-to-image-generation/" rel="noopener noreferrer"&gt;start with visual static and iteratively refine it&lt;/a&gt; into a clear picture, DiffusionGemma applies this to text:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;The canvas:&lt;/strong&gt; The model starts with a canvas of random placeholder tokens.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Iterative refinement:&lt;/strong&gt; The model makes multiple passes, locking in correct tokens and using them as context clues to refine the rest.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Final polish:&lt;/strong&gt; The text converges into high-quality output.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&amp;nbsp;&lt;br&gt;
  
    controls &lt;br&gt;
    loop &lt;br&gt;
    muted &lt;br&gt;
    autoplay &lt;br&gt;
    playsinline &lt;br&gt;
    width="100%" &lt;br&gt;
    style="border-radius: 8px; display: block; margin-bottom: 0; padding-bottom: 0px;"&lt;br&gt;
    aria-label="DiffusionGemma Process"&amp;gt;&lt;br&gt;
    &lt;br&gt;
    Your browser does not support the video tag.&lt;br&gt;
  &lt;br&gt;
&amp;nbsp;&lt;/p&gt;

&lt;p&gt;Because the model can process the whole paragraph while generating, it unlocks new patterns of model behavior, like perfectly closing complex markdown formatting or generating and rendering code in near real-time.&lt;/p&gt;

&lt;h3&gt;
  
  
  Get started today
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Download the weights:&lt;/strong&gt; Access the experimental model weights (released under a permissive Apache 2.0 license) right now on Hugging Face.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Integrate &amp;amp; learn:&lt;/strong&gt; Learn more in our &lt;a href="https://developers.googleblog.com/en/diffusiongemma-the-developer-guide" rel="noopener noreferrer"&gt;DiffusionGemma developer guide&lt;/a&gt;. Or deep dive into &lt;a href="https://newsletter.maartengrootendorst.com/p/a-visual-guide-to-diffusiongemma" rel="noopener noreferrer"&gt;A Visual Guide to DiffusionGemma&lt;/a&gt; to understand the mechanics under the hood.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Use your favorite development tools:&lt;/strong&gt; Serve the model efficiently using &lt;a href="https://huggingface.co/collections/mlx-community/diffusiongemma" rel="noopener noreferrer"&gt;MLX&lt;/a&gt;, &lt;a href="https://vllm-project.github.io/2026/06/10/diffusion-gemma" rel="noopener noreferrer"&gt;vLLM&lt;/a&gt; (with integration supported by &lt;a href="https://huggingface.co/collections/RedHatAI/diffusiongemma-26b-a4b-it" rel="noopener noreferrer"&gt;Red Hat&lt;/a&gt;), and &lt;a href="https://huggingface.co/google/diffusiongemma-26B-A4B-it" rel="noopener noreferrer"&gt;Hugging Face Transformers&lt;/a&gt;. For rapid experimentation, we are releasing a fine-tuning tutorial using &lt;a href="https://github.com/google/hackable_diffusion" rel="noopener noreferrer"&gt;Hackable Diffusion&lt;/a&gt;, a modular JAX toolbox designed for composability. You can also explore fine-tuning with &lt;a href="https://unsloth.ai/docs/models/diffusiongemma" rel="noopener noreferrer"&gt;Unsloth&lt;/a&gt; and NVIDIA &lt;a href="https://github.com/NVIDIA-NeMo/Automodel/blob/main/docs/guides/dllm/diffusiongemma.md" rel="noopener noreferrer"&gt;NeMo&lt;/a&gt;. Additionally, official support for llama.cpp is arriving soon.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Experience optimized performance:&lt;/strong&gt; We worked with &lt;a href="https://blogs.nvidia.com/blog/rtx-ai-garage-local-gemma-diffusion" rel="noopener noreferrer"&gt;NVIDIA&lt;/a&gt; to optimize across their hardware stack, ensuring compatibility with consumer setups (quantized for GeForce RTX 5090 and 4090 GPUs) alongside high performance on enterprise systems (Hopper and Blackwell using advanced NVFP4 kernels), including NVIDIA DGX Spark and DGX Station for local deskside deployment, and RTX PRO for AI professionals. Native support for NVFP4 (4-bit floating-point) accelerates compute throughput, allowing the model to run at faster speeds with near-lossless accuracy.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Try your way:&lt;/strong&gt; Run on your desktop dedicated GPU or in the cloud through &lt;a href="https://console.cloud.google.com/agent-platform/publishers/google/model-garden/diffusiongemma" rel="noopener noreferrer"&gt;Gemini Enterprise Agent Platform Model Garden&lt;/a&gt; or &lt;a href="https://catalog.ngc.nvidia.com/orgs/nim/teams/google/containers/diffusiongemma-26b-a4b-it?version=latest" rel="noopener noreferrer"&gt;NVIDIA NIM&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>gemma</category>
      <category>google</category>
      <category>llm</category>
      <category>ai</category>
    </item>
    <item>
      <title>The (no longer) missing multi-agent pattern: triggering dynamic workflows from an agent</title>
      <dc:creator>Remigiusz Samborski</dc:creator>
      <pubDate>Tue, 14 Jul 2026 09:57:26 +0000</pubDate>
      <link>https://dev.to/googleai/the-no-longer-missing-multi-agent-pattern-triggering-dynamic-workflows-from-an-agent-ef3</link>
      <guid>https://dev.to/googleai/the-no-longer-missing-multi-agent-pattern-triggering-dynamic-workflows-from-an-agent-ef3</guid>
      <description>&lt;p&gt;When building multi-agent systems, rigid state graphs quickly fall apart in the face of dynamic user inputs. Imagine building a smart assistant: a user hands you a checklist of three household chores today, but tomorrow it might be a list of ten software debugging tasks. Because the number of tasks, their sequence, and their execution details are entirely runtime-dependent, you cannot hardcode this path at design time. Forcing dynamic lists of work into a static &lt;a href="https://adk.dev/graphs/" rel="noopener noreferrer"&gt;graph-based workflow&lt;/a&gt; can lead to fragile, over-engineered code. You need a workflow that adapts dynamically at runtime.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://adk.dev/" rel="noopener noreferrer"&gt;Google Agent Development Kit (ADK)&lt;/a&gt; provides a flexible programming model to define &lt;a href="https://adk.dev/graphs/dynamic/" rel="noopener noreferrer"&gt;dynamic workflows&lt;/a&gt;. With the release of &lt;a href="https://github.com/google/adk-python/releases/tag/v2.4.0" rel="noopener noreferrer"&gt;ADK 2.4.0&lt;/a&gt;, triggering these workflows has become even more seamless: you can register a &lt;code&gt;Workflow&lt;/code&gt; directly in an agent's &lt;code&gt;tools&lt;/code&gt; list, allowing the coordinator agent to execute it automatically as a first-class tool.&lt;/p&gt;

&lt;p&gt;In this article, you learn how to configure and trigger a dynamic workflow directly from a coordinator agent. This guide uses a task list coordination example, but you can adjust this pattern to other dynamic orchestration needs.&lt;/p&gt;

&lt;h2&gt;
  
  
  The architecture of a dynamic workflow
&lt;/h2&gt;

&lt;p&gt;Static workflows define the execution path at design time. Dynamic workflows, however, allow agents to invoke tools, spawn other nodes, and schedule sub-agents conditionally at runtime.&lt;/p&gt;

&lt;p&gt;The system consists of three main components:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Root agent (&lt;code&gt;root_agent&lt;/code&gt;)&lt;/strong&gt;: Gathers the list of tasks from the user, requests final approval, and directly calls the &lt;code&gt;tasks_workflow&lt;/code&gt; tool.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The workflow (&lt;code&gt;tasks_workflow&lt;/code&gt;)&lt;/strong&gt;: A &lt;code&gt;Workflow&lt;/code&gt; that iterates over the approved tasks.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sub-agent (&lt;code&gt;task_explainer&lt;/code&gt;)&lt;/strong&gt;: An &lt;code&gt;Agent&lt;/code&gt; tasked with generating a step-by-step execution plan for each task.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Here is the architectural diagram of the solution:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ft82yfq1pmg0g5rren7fd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ft82yfq1pmg0g5rren7fd.png" alt="Architecture diagram showing user interaction with the root coordinator agent, which directly calls the dynamic tasks_workflow tool that schedules task_explainer sub-agents" width="368" height="567"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Technical implementation
&lt;/h2&gt;

&lt;p&gt;Let's break down how to implement this solution using the Google ADK library in Python. The complete code resides in the &lt;a href="https://github.com/GoogleCloudPlatform/devrel-demos/tree/main/agents/workflow-dynamic" rel="noopener noreferrer"&gt;devrel-demos repository&lt;/a&gt; with core logic in the &lt;a href="https://github.com/GoogleCloudPlatform/devrel-demos/blob/main/agents/workflow-dynamic/app/agent.py" rel="noopener noreferrer"&gt;agent.py&lt;/a&gt; file.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Initialize the environment and model
&lt;/h3&gt;

&lt;p&gt;First, import the required ADK modules and set up the Gemini model. This example uses &lt;code&gt;gemini-3.5-flash&lt;/code&gt; with &lt;a href="https://cloud.google.com/products/gemini-enterprise-agent-platform?utm_campaign=CDR_0x87fa8d40_default_b532096501&amp;amp;utm_medium=external&amp;amp;utm_source=blog" rel="noopener noreferrer"&gt;Gemini Enterprise Agent Platform&lt;/a&gt; APIs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;google.auth&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;google.adk&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Agent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Event&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Workflow&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;google.adk.apps&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;App&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;google.adk.models&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Gemini&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;google.adk.workflow&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;node&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;google.genai&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;types&lt;/span&gt;

&lt;span class="c1"&gt;# ==============================================================================
# Initialize the environment
# ==============================================================================
&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;project_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;google&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;auth&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;default&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;project_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;GOOGLE_CLOUD_PROJECT&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;project_id&lt;/span&gt;
&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;GOOGLE_CLOUD_LOCATION&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;global&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;GOOGLE_GENAI_USE_VERTEXAI&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;True&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

&lt;span class="c1"&gt;# ==============================================================================
# Model Definition
# ==============================================================================
&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Gemini&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;gemini-3.5-flash&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;retry_options&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;types&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;HttpRetryOptions&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;attempts&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Define the sub-agent
&lt;/h3&gt;

&lt;p&gt;The sub-agent &lt;code&gt;task_explainer&lt;/code&gt; takes a task description and writes a step-by-step execution plan:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;task_explainer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Agent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;task_explainer&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;instruction&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
    You are a task execution planner subagent.
    Given a task description, write a short, step-by-step execution plan
    explaining how you would perform the task. Be concise and clear.
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Implement the dynamic workflow node
&lt;/h3&gt;

&lt;p&gt;To support dynamic execution, define two functions decorated with &lt;code&gt;@node&lt;/code&gt;. First node accepts the parent context &lt;code&gt;ctx&lt;/code&gt; and a list of task strings. This is the main node that iterates over the list of tasks and explains them:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="nd"&gt;@node&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rerun_on_resume&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;task_workflow_node&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;node_input&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;]):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Workflow that iterates over the list of tasks and explains them.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;task&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;node_input&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="c1"&gt;# Yield progress update
&lt;/span&gt;        &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="nc"&gt;Event&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;⏳ Starting task: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;task&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;...&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# type: ignore
&lt;/span&gt;
        &lt;span class="c1"&gt;# Dynamically trigger subagent
&lt;/span&gt;        &lt;span class="n"&gt;explanation&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run_node&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;task_explainer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;node_input&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;task&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;explanation_content&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getattr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;explanation&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;text&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;explanation&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="c1"&gt;# Mark as done
&lt;/span&gt;        &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="nc"&gt;Event&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;✅ Task Done: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;task&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
                &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;**Execution Explanation:**&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;explanation_content&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
            &lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# type: ignore
&lt;/span&gt;        &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The second node is just returning a message that all tasks were completed. It’s used here to demonstrate a sequential workflow execution:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="nd"&gt;@node&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;task_workflow_end&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="nc"&gt;Event&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;🎉🚀 All tasks executed successfully! ✨&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# type: ignore
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  4. Define the workflow
&lt;/h3&gt;

&lt;p&gt;Next, define the &lt;code&gt;Workflow&lt;/code&gt; object. A &lt;code&gt;Workflow&lt;/code&gt; consists of nodes and directed edges between them. Since ADK 2.4.0, a &lt;code&gt;Workflow&lt;/code&gt; can be registered directly as a first-class tool for an agent. To do so, make sure to define the &lt;code&gt;name&lt;/code&gt;, &lt;code&gt;description&lt;/code&gt;, and &lt;code&gt;input_schema&lt;/code&gt; so the parent agent knows how to call it. Whereas &lt;code&gt;edges&lt;/code&gt; describes the order of workflow steps execution:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;tasks_workflow&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Workflow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;tasks_workflow&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Iterates over the list of tasks and explains them.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;input_schema&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="n"&gt;edges&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;START&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;task_workflow_node&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;task_workflow_end&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  5. Define the root coordinator agent
&lt;/h3&gt;

&lt;p&gt;Finally, the &lt;code&gt;root_agent&lt;/code&gt; coordinator manages user interaction. The agent collects the list of tasks, asks for confirmation, and, once approved, executes the &lt;code&gt;tasks_workflow&lt;/code&gt; directly. Notice how we pass &lt;code&gt;tasks_workflow&lt;/code&gt; directly to the &lt;code&gt;tools&lt;/code&gt; array:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;root_agent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Agent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;root_agent&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;instruction&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
    You are a task coordinator agent.
    Your goal is to gather a list of tasks that the user wants to execute.
    Talk to the user to gather the list of tasks.
    Once you have a list of tasks, present them clearly to the user and ask
    for their final approval to execute them.
    Do NOT execute anything until the user explicitly approves.
    Once the user approves the list of tasks, call the tool `tasks_workflow` with
    the list of tasks.
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;tools&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;tasks_workflow&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Testing the flow locally
&lt;/h2&gt;

&lt;p&gt;You can run and test this agent locally using the &lt;code&gt;agents-cli&lt;/code&gt; playground.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;If you haven't already installed &lt;code&gt;agents-cli&lt;/code&gt; and its skills, run the setup command:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   uvx google-agents-cli setup
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Clone and enter the demo directory:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   npx &lt;span class="nt"&gt;-y&lt;/span&gt; giget@latest gh+git:google/adk-samples/python/agents/workflow-dynamic workflow-dynamic 
   &lt;span class="nb"&gt;cd &lt;/span&gt;workflow-dynamic
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Install required packages::
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   agents-cli &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Start the playground:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   agents-cli playground
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Interact with the Agent:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Open &lt;a href="http://localhost:8080" rel="noopener noreferrer"&gt;http://localhost:8080&lt;/a&gt; in your browser.&lt;/li&gt;
&lt;li&gt;Select &lt;strong&gt;app&lt;/strong&gt; from the dropdown list at the top.&lt;/li&gt;
&lt;li&gt;Type a list of tasks in the chat box. For example:
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; 1. Empty the trash.
 2. Feed the dog.
 3. Do the laundry.
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;The coordinator agent lists the tasks and asks for your approval.&lt;/li&gt;
&lt;li&gt;Once you reply with "Yes" or "Approved", the &lt;code&gt;tasks_workflow&lt;/code&gt; tool fires.&lt;/li&gt;
&lt;li&gt;The playground console streams live updates as &lt;code&gt;tasks_workflow&lt;/code&gt; iterates through each task and returns plans generated by the &lt;code&gt;task_explainer&lt;/code&gt; sub-agent.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Screencast demo
&lt;/h2&gt;

&lt;p&gt;The following screencast demonstrates the working solution:&lt;/p&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/umYYAmMcd8c"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;Dynamic workflows in Google ADK allow agents to perform complex, runtime-determined orchestrations. By leveraging &lt;code&gt;Workflow&lt;/code&gt; and the &lt;code&gt;@node&lt;/code&gt; decorator, you can build adaptable multi-agent applications that respond to dynamic requirements.&lt;/p&gt;

&lt;p&gt;Continue exploring:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://adk.dev/graphs/dynamic/" rel="noopener noreferrer"&gt;ADK 2.0 dynamic workflows documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developers.googleblog.com/why-we-built-adk-20/?utm_campaign=CDR_0x87fa8d40_default_b532096501&amp;amp;utm_medium=external&amp;amp;utm_source=blog" rel="noopener noreferrer"&gt;ADK 2.0 announcement&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/google/adk-python/commit/1263ed64e30805464fff3391554f65ebbf72746b" rel="noopener noreferrer"&gt;Workflow as Tool core feature&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/GoogleCloudPlatform/devrel-demos/tree/main/agents/workflow-dynamic" rel="noopener noreferrer"&gt;Sample code in GitHub&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Thanks for reading
&lt;/h2&gt;

&lt;p&gt;If you found this article helpful, please consider sharing it with your friends on socials.&lt;/p&gt;

&lt;p&gt;I'm always eager to share my learnings or chat with fellow developers and AI enthusiasts, so feel free to follow me on &lt;a href="https://www.linkedin.com/in/remigiusz-samborski/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;, &lt;a href="https://x.com/RemikSamborski" rel="noopener noreferrer"&gt;X&lt;/a&gt; or &lt;a href="https://bsky.app/profile/rsamborski.bsky.social" rel="noopener noreferrer"&gt;Bluesky&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>agents</category>
      <category>adk</category>
      <category>programming</category>
    </item>
    <item>
      <title>How your sample data impact vector tests in PostgreSQL and AlloyDB.</title>
      <dc:creator>Gleb Otochkin</dc:creator>
      <pubDate>Tue, 14 Jul 2026 04:44:34 +0000</pubDate>
      <link>https://dev.to/googleai/how-your-sample-data-impact-vector-tests-in-postgresql-and-alloydb-211l</link>
      <guid>https://dev.to/googleai/how-your-sample-data-impact-vector-tests-in-postgresql-and-alloydb-211l</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fahc6pinsn35a42xzk9no.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fahc6pinsn35a42xzk9no.png" width="800" height="447"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Introduction
&lt;/h3&gt;

&lt;p&gt;If you are using vector data in PostgreSQL on &lt;a href="https://cloud.google.com/sql?utm_campaign=CDR_0x370c34a8_default_b524180055&amp;amp;utm_medium=external&amp;amp;utm_source=blog" rel="noopener noreferrer"&gt;Cloud SQL&lt;/a&gt; or &lt;a href="https://cloud.google.com/products/alloydb?utm_campaign=CDR_0x370c34a8_default_b524180055&amp;amp;utm_medium=external&amp;amp;utm_source=blog" rel="noopener noreferrer"&gt;AlloyDB&lt;/a&gt;, you probably use some benchmark queries or functions to check your vector search performance. From time to time, I see people performing speed benchmarks or quality testing using generated, fully synthetic data. The main goal, after all, is to test, for example, performance with a particular data type and get some comparable numbers. Does it really matter what data I have in my vector column?&lt;/p&gt;

&lt;p&gt;It really depends, and things can indeed go very wrong when you try to evaluate the index’s performance. I use synthetic data all the time for my tests. But it depends on what I am testing. In some cases, real vs. synthetic data might have a significant impact on the results. Let me show you how it can change the outcome of some tests for the vector data type in PostgreSQL.&lt;/p&gt;

&lt;h3&gt;
  
  
  Two datasets
&lt;/h3&gt;

&lt;p&gt;Some tests can be done using fully synthetic data. For example, if you test inserts, updates, or deletes for vectors, then synthetic data works for you. In such cases, I usually create a fully synthetic dataset of totally random, normalized number arrays. Here is an example of a simple function and how to generate the dataset.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- Create a function &lt;/span&gt;
&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;OR&lt;/span&gt; &lt;span class="k"&gt;REPLACE&lt;/span&gt; &lt;span class="k"&gt;FUNCTION&lt;/span&gt; &lt;span class="n"&gt;generate_random_vector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dimensions&lt;/span&gt; &lt;span class="nb"&gt;integer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;RETURNS&lt;/span&gt; &lt;span class="n"&gt;vector&lt;/span&gt;
&lt;span class="k"&gt;LANGUAGE&lt;/span&gt; &lt;span class="k"&gt;sql&lt;/span&gt;
&lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="err"&gt;$$&lt;/span&gt;
    &lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;array_agg&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)::&lt;/span&gt;&lt;span class="n"&gt;vector&lt;/span&gt;
    &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;generate_series&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dimensions&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="err"&gt;$$&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;-- Creating the test table with vector data type 768 dimensions&lt;/span&gt;
&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;tvectr768&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="nb"&gt;BIGINT&lt;/span&gt; &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="n"&gt;d&lt;/span&gt; &lt;span class="n"&gt;VECTOR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;768&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;-- Populate the table with 1M or fows&lt;/span&gt;
&lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;tvectr768&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt;
    &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="c1"&gt;-- This function is now called for each row&lt;/span&gt;
    &lt;span class="n"&gt;generate_random_vector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;768&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;
    &lt;span class="n"&gt;generate_series&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1000000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the end, we have a table with 1 million vectors. Each vector dimension value is between -1 and 1. So, the vector is normalized and shows more or less the same behaviour for the DML operations as normally generated embedding for a piece of content.&lt;/p&gt;

&lt;p&gt;But when I test recall quality or performance for vector search, I use one of the publicly available datasets with vectors created based on real data. You can find multiple datasets for embeddings on Hugging Face or other sources. For example, you can check the Cohere Labs datasets here: &lt;a href="https://huggingface.co/CohereLabs/datasets." rel="noopener noreferrer"&gt;https://huggingface.co/CohereLabs/datasets.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here are my 1M rows with the same dimensions and structure, but all the vectors in those rows are based on real data.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;pgdata=#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\d&lt;/span&gt; tvreal768
&lt;span class="go"&gt;               Table "public.tvreal768"
 Column | Type | Collation | Nullable | Default
--------+-------------+-----------+----------+---------
 id | bigint | | |
 d | vector(768) | | |

&lt;/span&gt;&lt;span class="gp"&gt;pgdata=#&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Both tables have the same size and structure.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;pgdata=#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;SELECT
&lt;span class="gp"&gt;pgdata-#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;relname AS table_name,
&lt;span class="gp"&gt;pgdata-#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;pg_size_pretty&lt;span class="o"&gt;(&lt;/span&gt;pg_table_size&lt;span class="o"&gt;(&lt;/span&gt;relid&lt;span class="o"&gt;))&lt;/span&gt; AS table_size
&lt;span class="gp"&gt;pgdata-#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;FROM
&lt;span class="gp"&gt;pgdata-#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;pg_catalog.pg_statio_user_tables
&lt;span class="gp"&gt;pgdata-#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;WHERE
&lt;span class="gp"&gt;pgdata-#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;relname IN &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'tvectr768'&lt;/span&gt;, &lt;span class="s1"&gt;'tvreal768'&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="go"&gt; table_name | table_size
------------+------------
 tvectr768 | 4008 MB
 tvreal768 | 4008 MB
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you go a bit deeper, you can see that both tables are about 57 MB in heap size, plus 3950 MB in TOAST where the actual vector data is stored. It makes perfect sense, since the d column with vector(768) is larger than the default 2k threshold and is stored entirely in the TOAST segment.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;pgdata-#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;relname AS table_name,
&lt;span class="gp"&gt;pgdata-#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;pg_size_pretty&lt;span class="o"&gt;(&lt;/span&gt;pg_relation_size&lt;span class="o"&gt;(&lt;/span&gt;oid&lt;span class="o"&gt;))&lt;/span&gt; AS heap_size,
&lt;span class="gp"&gt;pgdata-#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;pg_size_pretty&lt;span class="o"&gt;(&lt;/span&gt;pg_table_size&lt;span class="o"&gt;(&lt;/span&gt;oid&lt;span class="o"&gt;)&lt;/span&gt; - pg_relation_size&lt;span class="o"&gt;(&lt;/span&gt;oid&lt;span class="o"&gt;))&lt;/span&gt; AS toast_size
&lt;span class="gp"&gt;pgdata-#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;FROM pg_class
&lt;span class="gp"&gt;pgdata-#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;WHERE relname &lt;span class="o"&gt;=&lt;/span&gt; ANY &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'{"tvectr768" ,"tvreal768"}'&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="gp"&gt;pgdata-#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;AND relkind &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'r'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="go"&gt; table_name | heap_size | toast_size
------------+-----------+------------
 tvectr768 | 57 MB | 3950 MB
 tvreal768 | 57 MB | 3950 MB
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let us do some performance tests and compare the results.&lt;/p&gt;

&lt;h3&gt;
  
  
  Building Vector Index
&lt;/h3&gt;

&lt;p&gt;For my tests, I was using Postgres 18 on a Debian VM in Google Cloud, and &lt;a href="https://cloud.google.com/alloydb/omni?utm_campaign=CDR_0x370c34a8_default_b524180055&amp;amp;utm_medium=external&amp;amp;utm_source=blog" rel="noopener noreferrer"&gt;AlloyDB Omni&lt;/a&gt; for &lt;a href="https://docs.cloud.google.com/alloydb/omni/containers/18.1.0/docs/ai/scann-vector-query-perf-overview?utm_campaign=CDR_0x370c34a8_default_b524180055&amp;amp;utm_medium=external&amp;amp;utm_source=blog" rel="noopener noreferrer"&gt;ScaNN index&lt;/a&gt; tests on a VM of the same size. We are mostly interested in the difference between “real” and synthetic vector values, but this varies depending on the index. The first test is to build an IVFFLAT index on our vectors.&lt;/p&gt;

&lt;p&gt;Here is an example for the IVFFLAT index on synthetic vector data.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;drop&lt;/span&gt; &lt;span class="k"&gt;index&lt;/span&gt; &lt;span class="n"&gt;if&lt;/span&gt; &lt;span class="k"&gt;exists&lt;/span&gt; &lt;span class="n"&gt;tvectr768_d_idx&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;INDEX&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;tvectr768&lt;/span&gt; &lt;span class="k"&gt;USING&lt;/span&gt; &lt;span class="n"&gt;ivfflat&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt; &lt;span class="n"&gt;vector_cosine_ops&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;WITH&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lists&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can see I was using default parameters, specifying only the number of lists. According to my tests, it takes about 271,068 ms to build the index on the synthetic tvectr768 table. And when it is built, the size of the index is roughly equal to the size of all the vectors (3912 MB), which makes total sense.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fr7lvllt1cbgjixcxt0cj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fr7lvllt1cbgjixcxt0cj.png" width="787" height="586"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When we run the same for the tvreal768 table with real vectors, we get similar results: 271,788 ms. So far, there is no difference. And it is probably expected if we think about how the IVFFLAT index is being built with splitting to buckets according to the number of lists and then simply moving each vector to one or another bucket.&lt;/p&gt;

&lt;p&gt;What about the &lt;a href="https://github.com/pgvector/pgvector#hnsw" rel="noopener noreferrer"&gt;HNSW&lt;/a&gt; index? Here is an example of a query used to build the HNSW index.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;drop&lt;/span&gt; &lt;span class="k"&gt;index&lt;/span&gt; &lt;span class="n"&gt;if&lt;/span&gt; &lt;span class="k"&gt;exists&lt;/span&gt; &lt;span class="n"&gt;tvectr768_d_idx&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;INDEX&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;tvectr768&lt;/span&gt; &lt;span class="k"&gt;USING&lt;/span&gt; &lt;span class="n"&gt;hnsw&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt; &lt;span class="n"&gt;vector_cosine_ops&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And here we start to see the difference in performance. The HNSW is a graph-based index and it is much harder to find node connections for a totally random array of numbers. You can play with different build parameters for the index and see how it can impact the results.&lt;/p&gt;

&lt;p&gt;For the building with default parameters it took on average 6601789 ms for synthetic data and 4174196 ms for the “real” vectors. That’s 1.5 faster for the real data and it is significant.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fcuwpjuocg1zwd09hvayg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fcuwpjuocg1zwd09hvayg.png" width="782" height="586"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So, why is it happening? How does the randomness in the numbers in the vector impact the performance? It probably deserves a separate article but I will try to give a short explanation. The HNSW builds its graph incrementally. When it adds another vector to the index it has to search for the right place in the graph. It means to find the closest neighbors to connect the new vector to. HNSW relies on the assumption that your data contains some underlying structure and it means clusters, patterns, and dense neighborhoods. The embedding vectors have but the random vector most likely not. Because of that the build process has to evaluate more candidates to find neighbors. That process is mathematically expensive for distances like cosine distance and takes time. That’s a short explanation but I hope it helps to understand to some extent the reasons behind the difference in built time.&lt;/p&gt;

&lt;p&gt;With the ScaNN index we have another case of striking difference between building time for index with fully synthetic vs “real” dataset. I was using the basic recommended parameters for the ScaNN index according to the &lt;a href="https://docs.cloud.google.com/alloydb/omni/containers/current/docs/ai/tune-indexes?resource=scann&amp;amp;utm_campaign=CDR_0x370c34a8_default_b524180055&amp;amp;utm_medium=external&amp;amp;utm_source=blog" rel="noopener noreferrer"&gt;guide&lt;/a&gt; for AlloyDB Omni.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;BEGIN&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="k"&gt;LOCAL&lt;/span&gt; &lt;span class="n"&gt;scann&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;num_leaves_to_search&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="k"&gt;LOCAL&lt;/span&gt; &lt;span class="n"&gt;scann&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pre_reordering_num_neighbors&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;drop&lt;/span&gt; &lt;span class="k"&gt;index&lt;/span&gt; &lt;span class="n"&gt;if&lt;/span&gt; &lt;span class="k"&gt;exists&lt;/span&gt; &lt;span class="n"&gt;tvreal768_d_idx&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;INDEX&lt;/span&gt; &lt;span class="n"&gt;tvreal768_d_idx&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;tvreal768&lt;/span&gt; &lt;span class="k"&gt;USING&lt;/span&gt; &lt;span class="n"&gt;scann&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt; &lt;span class="n"&gt;cosine&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;WITH&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;num_leaves&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;END&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It took 237094 ms to build the index on the synthetic data vs only 62673 ms on the “real” dataset. And the size of indexes was quite different because AlloyDB ScaNN was using internal index optimization for the real dataset. We have 1562 MB for the synthetic data index vs 275 MB for the “real” one.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fecnfu7z55b34k2lucbja.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fecnfu7z55b34k2lucbja.png" width="782" height="586"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So, when you start to evaluate performance in building indexes then you probably should use proper data or you can get misleading results. I would recommend using vectors built on your actual data.&lt;/p&gt;

&lt;h3&gt;
  
  
  Vector Search Performance
&lt;/h3&gt;

&lt;p&gt;Now that we have our ANN indexes built, we can evaluate how quickly you can find the top 5 similar vectors using the HNSW index. I am using a procedure that takes 100 samples from the table and uses some of those samples as predicates to find the 5 most similar vectors. I repeated this 11 times, discarding the first execution. This gives me the throughput in Queries Per Second (QPS) and the average execution time per query, along with the max and min times for all executions. You can check the procedure benchmark_vector_search in the Appendix chapter and the end of the article or &lt;a href="https://github.com/GoogleCloudPlatform/devrel-demos/tree/main/infrastructure/database-vectors-tests" rel="noopener noreferrer"&gt;download&lt;/a&gt; it from GitHub.&lt;/p&gt;

&lt;p&gt;Here is a sample output from the procedure.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;NOTICE: ------------------------------------------------------------------------
NOTICE: Benchmark finished successfully.
NOTICE: Total queries executed: 1000 (excluding warmup)
NOTICE: Total execution time: 2.145 seconds (excluding warmup)
NOTICE: Throughput (overall): 466.1 QPS (excluding warmup)
NOTICE: ------------------------------------------------------------------------
NOTICE: Latency Stats (excluding warmup):
NOTICE: Average: 2.125 ms
NOTICE: Minimum: 1.389 ms
NOTICE: Maximum: 3.888 ms
NOTICE: ------------------------------------------------------------------------
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When we run our tests, it shows 4.173 ms on average for synthetic data vs. 2.102 ms for our “real” vectors using HNSW index. Interestingly enough, for AlloyDB ScaNN, the difference is not so big — only 5.958 ms vs. 5.161 ms if we use 1,000 lists. But if we use an index with only 100 lists, we can see a significant difference in performance, where the response time for synthetic data is 7.239 ms vs. 4.249 ms for the “real” ones.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fpoxmz4ix3fndtbmjoiig.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fpoxmz4ix3fndtbmjoiig.png" width="782" height="586"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The reasons behind the difference in performance have the same nature but slightly different consequences. Since the random vector lacks clusters and dense neighborhoods it goes through more steps to find the closest neighbours. Instead, for example going through 32 nodes to find the closest neighbour it has to traverse 256 or more and even in such a case the quality might be still low.&lt;/p&gt;

&lt;p&gt;So, if you are testing HNSW, which is widely used in the community, the 2x difference is big enough and fully justifies the effort of getting a real dataset.&lt;/p&gt;

&lt;h3&gt;
  
  
  Vector Search Quality
&lt;/h3&gt;

&lt;p&gt;We already established that synthetic data can mislead you about build time or vector search speed for your vector indexes. What about quality?&lt;/p&gt;

&lt;p&gt;Usually, when we talk about vector search quality, we use the term “recall,” which represents the percentage of exact matches returned by an ANN index compared to the list of hits returned by a KNN exact search. For example, we do a KNN search returning the top 10 similar vectors. Then, we run exactly the same query with an ANN index and compare how many of the 10 vectors returned by the ANN search match the KNN results. If it is 9 out of 10, then it is 90% recall.&lt;/p&gt;

&lt;p&gt;In my procedure evaluate_vector_recall, I use 100 random samples from the dataset and then iterate on those samples to get the recall for each of them, averaging the results at the end. I tested it with the top 10 similar vectors (&lt;code&gt;k_param=&amp;gt;10&lt;/code&gt;). The procedure is provided in the Appendix and on &lt;a href="https://github.com/GoogleCloudPlatform/devrel-demos/tree/main/infrastructure/database-vectors-tests" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Here is a sample output of the procedure.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;pgdata=#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;call evaluate_vector_recall&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'tvreal768'&lt;/span&gt;,768,k_param&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;10&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="go"&gt;NOTICE: Starting vector similarity search recall evaluation...
NOTICE: Target table: tvreal768, Expected dimensions: 768
NOTICE: Parameters - Vector Column: d, ID Column: id, K: 10, Sample Size: 100
NOTICE: Selecting 100 random vectors for sampling from tvreal768...
NOTICE: ------------------------------------------------------------------------
NOTICE: Recall Evaluation Finished.
NOTICE: Total queries evaluated: 100
NOTICE: Average Recall@10: %91.80
NOTICE: Minimum Recall@10: %0.00
NOTICE: Maximum Recall@10: %100.00
NOTICE: ------------------------------------------------------------------------
CALL
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;According to my test results, the recall for synthetic data is quite low. On average for the 100 samples, I am getting only 3.8% recall using HNSW, while for “real” data, the recall is 91.8%. This means our search on synthetic data is not only slow but also hugely inaccurate. When we try it using the AlloyDB ScaNN index, it shows 16.4% recall for synthetic data vs. 91.4% for the “real” data. Only IVFFLAT didn’t show itself good with the real dataset giving only about 51.9% recall on average. But the quality with synthetic data was still much worse than that — only 10.6%&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fq5ecvr9bmd99b09zrktt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fq5ecvr9bmd99b09zrktt.png" width="787" height="586"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Of course, your test results can and most likely will be different, but the trend is clear. Even if ScaNN shows slightly better results for a synthetic vector search, it is still quite misleading and inaccurate.&lt;/p&gt;

&lt;p&gt;You also can opt-in to use the evaluate_query_recall procedure coming with the AlloyDB Omni and it gives you the option to measure recall. Read more in the official &lt;a href="https://docs.cloud.google.com/alloydb/omni/containers/current/docs/ai/measure-vector-query-recall?utm_campaign=CDR_0x370c34a8_default_b524180055&amp;amp;utm_medium=external&amp;amp;utm_source=blog" rel="noopener noreferrer"&gt;guide&lt;/a&gt; on how to use the procedure.&lt;/p&gt;

&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;Here is my short summary for all the tests and results&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Synthetic vectors can be used for pure performance DML tests where the vectors are not used as predicates and are only used to fill up space. For example, for inserts, updates, or deletes of rows filtered by non-vector column data.&lt;/li&gt;
&lt;li&gt;Synthetic vector data shows incorrect results for index build times and can mislead testers by giving inaccurate results. So far, in most of my tests, synthetic data required more time to build the index compared to realistic vector data.&lt;/li&gt;
&lt;li&gt;An ANN vector search executed against a synthetic dataset can be 1.5 times slower than for a similar dataset with realistic vectors.&lt;/li&gt;
&lt;li&gt;Recall quality for an ANN vector search cannot be measured against synthetic vectors. In my tests, the recall quality ranged from 3.8% to 16.4%, depending on the index type and index parameters. The real dataset showed more than 90% recall for most of the tests.&lt;/li&gt;
&lt;li&gt;Get a real dataset and use it for all your performance and quality testing for ANN indexes.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Appendix
&lt;/h3&gt;

&lt;p&gt;Here are some procedures I used for testing. I don’t pretend they are perfect in any way but should be clear enough for everyone who tries to use it. I used some hardcoded values to make it simpler to read. Both procedures are available on &lt;a href="https://github.com/GoogleCloudPlatform/devrel-demos/tree/main/infrastructure/database-vectors-tests" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Here is the procedure to check response time for vector search. It is using cosine distance for measurement and takes the first 100 vectors from the table as a sample to be used later.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;OR&lt;/span&gt; &lt;span class="k"&gt;REPLACE&lt;/span&gt; &lt;span class="k"&gt;PROCEDURE&lt;/span&gt; &lt;span class="n"&gt;benchmark_vector_search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;n_repetitions&lt;/span&gt; &lt;span class="nb"&gt;integer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;table_name_param&lt;/span&gt; &lt;span class="nb"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;vector_dimensions&lt;/span&gt; &lt;span class="nb"&gt;integer&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;LANGUAGE&lt;/span&gt; &lt;span class="n"&gt;plpgsql&lt;/span&gt;
&lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="err"&gt;$$&lt;/span&gt;
&lt;span class="k"&gt;DECLARE&lt;/span&gt;
    &lt;span class="c1"&gt;-- Array to hold the 100 vectors sampled for the benchmark run&lt;/span&gt;
    &lt;span class="n"&gt;sample_vectors&lt;/span&gt; &lt;span class="n"&gt;vector&lt;/span&gt;&lt;span class="p"&gt;[];&lt;/span&gt;
    &lt;span class="c1"&gt;-- Variable to hold the current vector being searched for&lt;/span&gt;
    &lt;span class="n"&gt;current_vector&lt;/span&gt; &lt;span class="n"&gt;vector&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="c1"&gt;-- Variables for timing&lt;/span&gt;
    &lt;span class="n"&gt;overall_start_time&lt;/span&gt; &lt;span class="n"&gt;timestamptz&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;overall_end_time&lt;/span&gt; &lt;span class="n"&gt;timestamptz&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;repetition_start_time&lt;/span&gt; &lt;span class="n"&gt;timestamptz&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;q_start&lt;/span&gt; &lt;span class="n"&gt;timestamptz&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;q_end&lt;/span&gt; &lt;span class="n"&gt;timestamptz&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;q_diff&lt;/span&gt; &lt;span class="nb"&gt;double&lt;/span&gt; &lt;span class="nb"&gt;precision&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;-- in milliseconds&lt;/span&gt;

    &lt;span class="c1"&gt;-- Metrics tracked across the entire benchmark&lt;/span&gt;
    &lt;span class="n"&gt;global_min_latency&lt;/span&gt; &lt;span class="nb"&gt;double&lt;/span&gt; &lt;span class="nb"&gt;precision&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="n"&gt;e9&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;global_max_latency&lt;/span&gt; &lt;span class="nb"&gt;double&lt;/span&gt; &lt;span class="nb"&gt;precision&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;global_sum_latency&lt;/span&gt; &lt;span class="nb"&gt;double&lt;/span&gt; &lt;span class="nb"&gt;precision&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;overall_total_queries&lt;/span&gt; &lt;span class="nb"&gt;integer&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;-- Variable for dimension validation&lt;/span&gt;
    &lt;span class="n"&gt;actual_dimensions&lt;/span&gt; &lt;span class="nb"&gt;integer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;actual_sample_size&lt;/span&gt; &lt;span class="nb"&gt;integer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;-- Loop counter&lt;/span&gt;
    &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;BEGIN&lt;/span&gt;
    &lt;span class="n"&gt;RAISE&lt;/span&gt; &lt;span class="n"&gt;NOTICE&lt;/span&gt; &lt;span class="s1"&gt;'Starting vector similarity search benchmark...'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;RAISE&lt;/span&gt; &lt;span class="n"&gt;NOTICE&lt;/span&gt; &lt;span class="s1"&gt;'Target table: %, Repetitions: %, Expected dimensions: %'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;table_name_param&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;n_repetitions&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;vector_dimensions&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;-- Validate that the vector column "d" exists and dimensions match&lt;/span&gt;
    &lt;span class="k"&gt;BEGIN&lt;/span&gt;
        &lt;span class="k"&gt;EXECUTE&lt;/span&gt; &lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'SELECT vector_dims(d) FROM %I WHERE d IS NOT NULL LIMIT 1'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;table_name_param&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;actual_dimensions&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="n"&gt;IF&lt;/span&gt; &lt;span class="n"&gt;actual_dimensions&lt;/span&gt; &lt;span class="k"&gt;IS&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt; &lt;span class="k"&gt;THEN&lt;/span&gt;
            &lt;span class="n"&gt;RAISE&lt;/span&gt; &lt;span class="n"&gt;EXCEPTION&lt;/span&gt; &lt;span class="s1"&gt;'Could not find any non-NULL vectors in table %. The table might be empty or column "d" contains only NULLs.'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;table_name_param&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;END&lt;/span&gt; &lt;span class="n"&gt;IF&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="n"&gt;IF&lt;/span&gt; &lt;span class="n"&gt;actual_dimensions&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="n"&gt;vector_dimensions&lt;/span&gt; &lt;span class="k"&gt;THEN&lt;/span&gt;
            &lt;span class="n"&gt;RAISE&lt;/span&gt; &lt;span class="n"&gt;EXCEPTION&lt;/span&gt; &lt;span class="s1"&gt;'Vector dimension mismatch. Expected dimension %, but found dimension % in table %.'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;vector_dimensions&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;actual_dimensions&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;table_name_param&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;END&lt;/span&gt; &lt;span class="n"&gt;IF&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;EXCEPTION&lt;/span&gt;
        &lt;span class="k"&gt;WHEN&lt;/span&gt; &lt;span class="n"&gt;undefined_table&lt;/span&gt; &lt;span class="k"&gt;THEN&lt;/span&gt;
            &lt;span class="n"&gt;RAISE&lt;/span&gt; &lt;span class="n"&gt;EXCEPTION&lt;/span&gt; &lt;span class="s1"&gt;'Table "%" does not exist.'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;table_name_param&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;WHEN&lt;/span&gt; &lt;span class="n"&gt;undefined_column&lt;/span&gt; &lt;span class="k"&gt;THEN&lt;/span&gt;
            &lt;span class="n"&gt;RAISE&lt;/span&gt; &lt;span class="n"&gt;EXCEPTION&lt;/span&gt; &lt;span class="s1"&gt;'Table "%" does not have a column named "d".'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;table_name_param&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;END&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="n"&gt;RAISE&lt;/span&gt; &lt;span class="n"&gt;NOTICE&lt;/span&gt; &lt;span class="s1"&gt;'Dimension validation successful.'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;-- Fetch 100 random vectors to use as search queries&lt;/span&gt;
    &lt;span class="n"&gt;RAISE&lt;/span&gt; &lt;span class="n"&gt;NOTICE&lt;/span&gt; &lt;span class="s1"&gt;'Selecting 100 random vectors for sampling from %...'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;table_name_param&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;EXECUTE&lt;/span&gt; &lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'SELECT array_agg(d) FROM (SELECT d FROM %I WHERE d IS NOT NULL ORDER BY random() LIMIT 100) AS random_sample'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;table_name_param&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;sample_vectors&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="n"&gt;IF&lt;/span&gt; &lt;span class="n"&gt;sample_vectors&lt;/span&gt; &lt;span class="k"&gt;IS&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt; &lt;span class="k"&gt;OR&lt;/span&gt; &lt;span class="n"&gt;COALESCE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;array_length&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sample_vectors&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="k"&gt;THEN&lt;/span&gt;
        &lt;span class="n"&gt;RAISE&lt;/span&gt; &lt;span class="n"&gt;EXCEPTION&lt;/span&gt; &lt;span class="s1"&gt;'Could not retrieve any sample vectors. The table % might be empty or all vectors are NULL.'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;table_name_param&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;END&lt;/span&gt; &lt;span class="n"&gt;IF&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="n"&gt;actual_sample_size&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;array_length&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sample_vectors&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;IF&lt;/span&gt; &lt;span class="n"&gt;actual_sample_size&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt; &lt;span class="k"&gt;THEN&lt;/span&gt;
        &lt;span class="n"&gt;RAISE&lt;/span&gt; &lt;span class="n"&gt;WARNING&lt;/span&gt; &lt;span class="s1"&gt;'Could not retrieve 100 sample vectors. Using % available sample vectors instead.'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;actual_sample_size&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;END&lt;/span&gt; &lt;span class="n"&gt;IF&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="n"&gt;RAISE&lt;/span&gt; &lt;span class="n"&gt;NOTICE&lt;/span&gt; &lt;span class="s1"&gt;'Starting main benchmark loop...'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;overall_start_time&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;clock_timestamp&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="c1"&gt;-- The main outer loop that repeats the entire test (warmup + N repetitions)&lt;/span&gt;
    &lt;span class="k"&gt;FOR&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="k"&gt;IN&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;..&lt;/span&gt;&lt;span class="n"&gt;n_repetitions&lt;/span&gt; &lt;span class="n"&gt;LOOP&lt;/span&gt;
        &lt;span class="n"&gt;repetition_start_time&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;clock_timestamp&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="c1"&gt;-- Reset overall_start_time to start of repetition 1 to exclude warmup duration from QPS/Totals&lt;/span&gt;
        &lt;span class="n"&gt;IF&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="k"&gt;THEN&lt;/span&gt;
            &lt;span class="n"&gt;overall_start_time&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;clock_timestamp&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="k"&gt;END&lt;/span&gt; &lt;span class="n"&gt;IF&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="k"&gt;DECLARE&lt;/span&gt;
            &lt;span class="n"&gt;rep_min_latency&lt;/span&gt; &lt;span class="nb"&gt;double&lt;/span&gt; &lt;span class="nb"&gt;precision&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="n"&gt;e9&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="n"&gt;rep_max_latency&lt;/span&gt; &lt;span class="nb"&gt;double&lt;/span&gt; &lt;span class="nb"&gt;precision&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="n"&gt;rep_sum_latency&lt;/span&gt; &lt;span class="nb"&gt;double&lt;/span&gt; &lt;span class="nb"&gt;precision&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="n"&gt;rep_queries&lt;/span&gt; &lt;span class="nb"&gt;integer&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="n"&gt;rep_duration&lt;/span&gt; &lt;span class="nb"&gt;double&lt;/span&gt; &lt;span class="nb"&gt;precision&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;BEGIN&lt;/span&gt;
            &lt;span class="c1"&gt;-- The inner loop that iterates through each of the sampled vectors&lt;/span&gt;
            &lt;span class="n"&gt;FOREACH&lt;/span&gt; &lt;span class="n"&gt;current_vector&lt;/span&gt; &lt;span class="k"&gt;IN&lt;/span&gt; &lt;span class="n"&gt;ARRAY&lt;/span&gt; &lt;span class="n"&gt;sample_vectors&lt;/span&gt;
            &lt;span class="n"&gt;LOOP&lt;/span&gt;
                &lt;span class="n"&gt;q_start&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;clock_timestamp&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
                &lt;span class="c1"&gt;-- Perform the core operation (limiting to top 5)&lt;/span&gt;
                &lt;span class="k"&gt;EXECUTE&lt;/span&gt; &lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'SELECT 1 FROM %I ORDER BY d &amp;lt;=&amp;gt; $1 LIMIT 5'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;table_name_param&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="k"&gt;USING&lt;/span&gt; &lt;span class="n"&gt;current_vector&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                &lt;span class="n"&gt;q_end&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;clock_timestamp&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

                &lt;span class="n"&gt;q_diff&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;extract&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;epoch&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;q_end&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;q_start&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;-- milliseconds&lt;/span&gt;

                &lt;span class="c1"&gt;-- Accumulate local statistics&lt;/span&gt;
                &lt;span class="n"&gt;IF&lt;/span&gt; &lt;span class="n"&gt;q_diff&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;rep_min_latency&lt;/span&gt; &lt;span class="k"&gt;THEN&lt;/span&gt; &lt;span class="n"&gt;rep_min_latency&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;q_diff&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;END&lt;/span&gt; &lt;span class="n"&gt;IF&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                &lt;span class="n"&gt;IF&lt;/span&gt; &lt;span class="n"&gt;q_diff&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;rep_max_latency&lt;/span&gt; &lt;span class="k"&gt;THEN&lt;/span&gt; &lt;span class="n"&gt;rep_max_latency&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;q_diff&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;END&lt;/span&gt; &lt;span class="n"&gt;IF&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                &lt;span class="n"&gt;rep_sum_latency&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;rep_sum_latency&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;q_diff&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                &lt;span class="n"&gt;rep_queries&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;rep_queries&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="k"&gt;END&lt;/span&gt; &lt;span class="n"&gt;LOOP&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

            &lt;span class="c1"&gt;-- Calculate total time for this repetition&lt;/span&gt;
            &lt;span class="n"&gt;rep_duration&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;extract&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;epoch&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;clock_timestamp&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;repetition_start_time&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

            &lt;span class="n"&gt;IF&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="k"&gt;THEN&lt;/span&gt;
                &lt;span class="c1"&gt;-- Warmup iteration: report it but do not add to global statistics&lt;/span&gt;
                &lt;span class="n"&gt;RAISE&lt;/span&gt; &lt;span class="n"&gt;NOTICE&lt;/span&gt; &lt;span class="s1"&gt;'Warmup Repetition: Avg Latency = % ms (Min: % ms, Max: % ms) | QPS: %'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
                             &lt;span class="n"&gt;round&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;rep_sum_latency&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;rep_queries&lt;/span&gt;&lt;span class="p"&gt;)::&lt;/span&gt;&lt;span class="nb"&gt;numeric&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; 
                             &lt;span class="n"&gt;round&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rep_min_latency&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nb"&gt;numeric&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; 
                             &lt;span class="n"&gt;round&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rep_max_latency&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nb"&gt;numeric&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                             &lt;span class="n"&gt;round&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;rep_queries&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;rep_duration&lt;/span&gt;&lt;span class="p"&gt;)::&lt;/span&gt;&lt;span class="nb"&gt;numeric&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="k"&gt;ELSE&lt;/span&gt;
                &lt;span class="c1"&gt;-- Accumulate global statistics&lt;/span&gt;
                &lt;span class="n"&gt;IF&lt;/span&gt; &lt;span class="n"&gt;rep_min_latency&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;global_min_latency&lt;/span&gt; &lt;span class="k"&gt;THEN&lt;/span&gt; &lt;span class="n"&gt;global_min_latency&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;rep_min_latency&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;END&lt;/span&gt; &lt;span class="n"&gt;IF&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                &lt;span class="n"&gt;IF&lt;/span&gt; &lt;span class="n"&gt;rep_max_latency&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;global_max_latency&lt;/span&gt; &lt;span class="k"&gt;THEN&lt;/span&gt; &lt;span class="n"&gt;global_max_latency&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;rep_max_latency&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;END&lt;/span&gt; &lt;span class="n"&gt;IF&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                &lt;span class="n"&gt;global_sum_latency&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;global_sum_latency&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;rep_sum_latency&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                &lt;span class="n"&gt;overall_total_queries&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;overall_total_queries&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;rep_queries&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

                &lt;span class="n"&gt;RAISE&lt;/span&gt; &lt;span class="n"&gt;NOTICE&lt;/span&gt; &lt;span class="s1"&gt;'Repetition %/%: Avg Latency = % ms (Min: % ms, Max: % ms) | QPS: %'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
                             &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;n_repetitions&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
                             &lt;span class="n"&gt;round&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;rep_sum_latency&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;rep_queries&lt;/span&gt;&lt;span class="p"&gt;)::&lt;/span&gt;&lt;span class="nb"&gt;numeric&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; 
                             &lt;span class="n"&gt;round&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rep_min_latency&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nb"&gt;numeric&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; 
                             &lt;span class="n"&gt;round&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rep_max_latency&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nb"&gt;numeric&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                             &lt;span class="n"&gt;round&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;rep_queries&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;rep_duration&lt;/span&gt;&lt;span class="p"&gt;)::&lt;/span&gt;&lt;span class="nb"&gt;numeric&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="k"&gt;END&lt;/span&gt; &lt;span class="n"&gt;IF&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;END&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;END&lt;/span&gt; &lt;span class="n"&gt;LOOP&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="n"&gt;overall_end_time&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;clock_timestamp&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="c1"&gt;-- Log overall summary metrics&lt;/span&gt;
    &lt;span class="k"&gt;DECLARE&lt;/span&gt;
        &lt;span class="n"&gt;total_benchmark_time&lt;/span&gt; &lt;span class="nb"&gt;double&lt;/span&gt; &lt;span class="nb"&gt;precision&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;overall_qps&lt;/span&gt; &lt;span class="nb"&gt;double&lt;/span&gt; &lt;span class="nb"&gt;precision&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;BEGIN&lt;/span&gt;
        &lt;span class="n"&gt;total_benchmark_time&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;extract&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;epoch&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;overall_end_time&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;overall_start_time&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
        &lt;span class="n"&gt;overall_qps&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;overall_total_queries&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;total_benchmark_time&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="n"&gt;RAISE&lt;/span&gt; &lt;span class="n"&gt;NOTICE&lt;/span&gt; &lt;span class="s1"&gt;'------------------------------------------------------------------------'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;RAISE&lt;/span&gt; &lt;span class="n"&gt;NOTICE&lt;/span&gt; &lt;span class="s1"&gt;'Benchmark finished successfully.'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;RAISE&lt;/span&gt; &lt;span class="n"&gt;NOTICE&lt;/span&gt; &lt;span class="s1"&gt;'Total queries executed: % (excluding warmup)'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;overall_total_queries&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;RAISE&lt;/span&gt; &lt;span class="n"&gt;NOTICE&lt;/span&gt; &lt;span class="s1"&gt;'Total execution time: % seconds (excluding warmup)'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;round&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;total_benchmark_time&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nb"&gt;numeric&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;RAISE&lt;/span&gt; &lt;span class="n"&gt;NOTICE&lt;/span&gt; &lt;span class="s1"&gt;'Throughput (overall): % QPS (excluding warmup)'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;round&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;overall_qps&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nb"&gt;numeric&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;RAISE&lt;/span&gt; &lt;span class="n"&gt;NOTICE&lt;/span&gt; &lt;span class="s1"&gt;'------------------------------------------------------------------------'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;RAISE&lt;/span&gt; &lt;span class="n"&gt;NOTICE&lt;/span&gt; &lt;span class="s1"&gt;'Latency Stats (excluding warmup):'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;RAISE&lt;/span&gt; &lt;span class="n"&gt;NOTICE&lt;/span&gt; &lt;span class="s1"&gt;' Average: % ms'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;round&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;global_sum_latency&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;overall_total_queries&lt;/span&gt;&lt;span class="p"&gt;)::&lt;/span&gt;&lt;span class="nb"&gt;numeric&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;RAISE&lt;/span&gt; &lt;span class="n"&gt;NOTICE&lt;/span&gt; &lt;span class="s1"&gt;' Minimum: % ms'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;round&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;global_min_latency&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nb"&gt;numeric&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;RAISE&lt;/span&gt; &lt;span class="n"&gt;NOTICE&lt;/span&gt; &lt;span class="s1"&gt;' Maximum: % ms'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;round&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;global_max_latency&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nb"&gt;numeric&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;RAISE&lt;/span&gt; &lt;span class="n"&gt;NOTICE&lt;/span&gt; &lt;span class="s1"&gt;'------------------------------------------------------------------------'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;END&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;END&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="err"&gt;$$&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And here is the procedure where I measure recall quality. It takes recall from each sample and then calculates an average recall quality for the entire execution.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;OR&lt;/span&gt; &lt;span class="k"&gt;REPLACE&lt;/span&gt; &lt;span class="k"&gt;PROCEDURE&lt;/span&gt; &lt;span class="n"&gt;evaluate_vector_recall&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;table_name_param&lt;/span&gt; &lt;span class="nb"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;vector_dimensions&lt;/span&gt; &lt;span class="nb"&gt;integer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;vector_col_param&lt;/span&gt; &lt;span class="nb"&gt;text&lt;/span&gt; &lt;span class="k"&gt;DEFAULT&lt;/span&gt; &lt;span class="s1"&gt;'d'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;id_col_param&lt;/span&gt; &lt;span class="nb"&gt;text&lt;/span&gt; &lt;span class="k"&gt;DEFAULT&lt;/span&gt; &lt;span class="s1"&gt;'id'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;k_param&lt;/span&gt; &lt;span class="nb"&gt;integer&lt;/span&gt; &lt;span class="k"&gt;DEFAULT&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;sample_size_param&lt;/span&gt; &lt;span class="nb"&gt;integer&lt;/span&gt; &lt;span class="k"&gt;DEFAULT&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;LANGUAGE&lt;/span&gt; &lt;span class="n"&gt;plpgsql&lt;/span&gt;
&lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="err"&gt;$$&lt;/span&gt;
&lt;span class="k"&gt;DECLARE&lt;/span&gt;
    &lt;span class="c1"&gt;-- Array to hold the vectors sampled for the recall evaluation&lt;/span&gt;
    &lt;span class="n"&gt;sample_vectors&lt;/span&gt; &lt;span class="n"&gt;vector&lt;/span&gt;&lt;span class="p"&gt;[];&lt;/span&gt;
    &lt;span class="c1"&gt;-- Variable to hold the current vector being searched for&lt;/span&gt;
    &lt;span class="n"&gt;current_vector&lt;/span&gt; &lt;span class="n"&gt;vector&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="c1"&gt;-- Arrays to hold the result IDs from exact and approximate searches&lt;/span&gt;
    &lt;span class="n"&gt;exact_ids&lt;/span&gt; &lt;span class="nb"&gt;text&lt;/span&gt;&lt;span class="p"&gt;[];&lt;/span&gt;
    &lt;span class="n"&gt;approx_ids&lt;/span&gt; &lt;span class="nb"&gt;text&lt;/span&gt;&lt;span class="p"&gt;[];&lt;/span&gt;
    &lt;span class="c1"&gt;-- Metrics variables&lt;/span&gt;
    &lt;span class="n"&gt;overlap_count&lt;/span&gt; &lt;span class="nb"&gt;integer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;vector_recall&lt;/span&gt; &lt;span class="nb"&gt;double&lt;/span&gt; &lt;span class="nb"&gt;precision&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;sum_recall&lt;/span&gt; &lt;span class="nb"&gt;double&lt;/span&gt; &lt;span class="nb"&gt;precision&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;min_recall&lt;/span&gt; &lt;span class="nb"&gt;double&lt;/span&gt; &lt;span class="nb"&gt;precision&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;max_recall&lt;/span&gt; &lt;span class="nb"&gt;double&lt;/span&gt; &lt;span class="nb"&gt;precision&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;actual_dimensions&lt;/span&gt; &lt;span class="nb"&gt;integer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;actual_sample_size&lt;/span&gt; &lt;span class="nb"&gt;integer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;total_queries&lt;/span&gt; &lt;span class="nb"&gt;integer&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;BEGIN&lt;/span&gt;
    &lt;span class="n"&gt;RAISE&lt;/span&gt; &lt;span class="n"&gt;NOTICE&lt;/span&gt; &lt;span class="s1"&gt;'Starting vector similarity search recall evaluation...'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;RAISE&lt;/span&gt; &lt;span class="n"&gt;NOTICE&lt;/span&gt; &lt;span class="s1"&gt;'Target table: %, Expected dimensions: %'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;table_name_param&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;vector_dimensions&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;RAISE&lt;/span&gt; &lt;span class="n"&gt;NOTICE&lt;/span&gt; &lt;span class="s1"&gt;'Parameters - Vector Column: %, ID Column: %, K: %, Sample Size: %'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
                 &lt;span class="n"&gt;vector_col_param&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;id_col_param&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;k_param&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sample_size_param&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;-- Validate vector column exists and dimension matches&lt;/span&gt;
    &lt;span class="k"&gt;BEGIN&lt;/span&gt;
        &lt;span class="k"&gt;EXECUTE&lt;/span&gt; &lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'SELECT vector_dims(%I) FROM %I WHERE %I IS NOT NULL LIMIT 1'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
                       &lt;span class="n"&gt;vector_col_param&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;table_name_param&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;vector_col_param&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;actual_dimensions&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="n"&gt;IF&lt;/span&gt; &lt;span class="n"&gt;actual_dimensions&lt;/span&gt; &lt;span class="k"&gt;IS&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt; &lt;span class="k"&gt;THEN&lt;/span&gt;
            &lt;span class="n"&gt;RAISE&lt;/span&gt; &lt;span class="n"&gt;EXCEPTION&lt;/span&gt; &lt;span class="s1"&gt;'Could not find any non-NULL vectors in column % of table %. The table might be empty or contains only NULLs.'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
                            &lt;span class="n"&gt;vector_col_param&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;table_name_param&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;END&lt;/span&gt; &lt;span class="n"&gt;IF&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="n"&gt;IF&lt;/span&gt; &lt;span class="n"&gt;actual_dimensions&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="n"&gt;vector_dimensions&lt;/span&gt; &lt;span class="k"&gt;THEN&lt;/span&gt;
            &lt;span class="n"&gt;RAISE&lt;/span&gt; &lt;span class="n"&gt;EXCEPTION&lt;/span&gt; &lt;span class="s1"&gt;'Vector dimension mismatch. Expected dimension %, but found dimension % in table %.'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
                            &lt;span class="n"&gt;vector_dimensions&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;actual_dimensions&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;table_name_param&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;END&lt;/span&gt; &lt;span class="n"&gt;IF&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;EXCEPTION&lt;/span&gt;
        &lt;span class="k"&gt;WHEN&lt;/span&gt; &lt;span class="n"&gt;undefined_table&lt;/span&gt; &lt;span class="k"&gt;THEN&lt;/span&gt;
            &lt;span class="n"&gt;RAISE&lt;/span&gt; &lt;span class="n"&gt;EXCEPTION&lt;/span&gt; &lt;span class="s1"&gt;'Table "%" does not exist.'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;table_name_param&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;WHEN&lt;/span&gt; &lt;span class="n"&gt;undefined_column&lt;/span&gt; &lt;span class="k"&gt;THEN&lt;/span&gt;
            &lt;span class="n"&gt;RAISE&lt;/span&gt; &lt;span class="n"&gt;EXCEPTION&lt;/span&gt; &lt;span class="s1"&gt;'Column "%" does not exist in table "%".'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;vector_col_param&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;table_name_param&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;END&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;-- Validate ID column exists&lt;/span&gt;
    &lt;span class="k"&gt;BEGIN&lt;/span&gt;
        &lt;span class="k"&gt;EXECUTE&lt;/span&gt; &lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'SELECT %I FROM %I LIMIT 1'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;id_col_param&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;table_name_param&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;EXCEPTION&lt;/span&gt;
        &lt;span class="k"&gt;WHEN&lt;/span&gt; &lt;span class="n"&gt;undefined_column&lt;/span&gt; &lt;span class="k"&gt;THEN&lt;/span&gt;
            &lt;span class="n"&gt;RAISE&lt;/span&gt; &lt;span class="n"&gt;EXCEPTION&lt;/span&gt; &lt;span class="s1"&gt;'ID column "%" does not exist in table "%". Cannot measure recall.'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
                            &lt;span class="n"&gt;id_col_param&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;table_name_param&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;END&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;-- Select random sample vectors (ignoring nulls)&lt;/span&gt;
    &lt;span class="n"&gt;RAISE&lt;/span&gt; &lt;span class="n"&gt;NOTICE&lt;/span&gt; &lt;span class="s1"&gt;'Selecting % random vectors for sampling from %...'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sample_size_param&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;table_name_param&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;EXECUTE&lt;/span&gt; &lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'SELECT array_agg(%I) FROM (SELECT %I FROM %I WHERE %I IS NOT NULL ORDER BY random() LIMIT $1) AS random_sample'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
                   &lt;span class="n"&gt;vector_col_param&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;vector_col_param&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;table_name_param&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;vector_col_param&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;sample_vectors&lt;/span&gt;
    &lt;span class="k"&gt;USING&lt;/span&gt; &lt;span class="n"&gt;sample_size_param&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="n"&gt;IF&lt;/span&gt; &lt;span class="n"&gt;sample_vectors&lt;/span&gt; &lt;span class="k"&gt;IS&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt; &lt;span class="k"&gt;OR&lt;/span&gt; &lt;span class="n"&gt;COALESCE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;array_length&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sample_vectors&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="k"&gt;THEN&lt;/span&gt;
        &lt;span class="n"&gt;RAISE&lt;/span&gt; &lt;span class="n"&gt;EXCEPTION&lt;/span&gt; &lt;span class="s1"&gt;'Could not retrieve any sample vectors. The table % might be empty or all vectors are NULL.'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;table_name_param&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;END&lt;/span&gt; &lt;span class="n"&gt;IF&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="n"&gt;actual_sample_size&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;array_length&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sample_vectors&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;IF&lt;/span&gt; &lt;span class="n"&gt;actual_sample_size&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;sample_size_param&lt;/span&gt; &lt;span class="k"&gt;THEN&lt;/span&gt;
        &lt;span class="n"&gt;RAISE&lt;/span&gt; &lt;span class="n"&gt;WARNING&lt;/span&gt; &lt;span class="s1"&gt;'Could not retrieve % sample vectors. Using % available sample vectors instead.'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
                      &lt;span class="n"&gt;sample_size_param&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;actual_sample_size&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;END&lt;/span&gt; &lt;span class="n"&gt;IF&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;-- Iterate through sampled vectors to calculate recall&lt;/span&gt;
    &lt;span class="n"&gt;FOREACH&lt;/span&gt; &lt;span class="n"&gt;current_vector&lt;/span&gt; &lt;span class="k"&gt;IN&lt;/span&gt; &lt;span class="n"&gt;ARRAY&lt;/span&gt; &lt;span class="n"&gt;sample_vectors&lt;/span&gt;
    &lt;span class="n"&gt;LOOP&lt;/span&gt;
        &lt;span class="c1"&gt;-- 1. Find exact nearest neighbors (ground truth) by forcing sequential scan&lt;/span&gt;
        &lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="k"&gt;LOCAL&lt;/span&gt; &lt;span class="n"&gt;enable_indexscan&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;off&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="k"&gt;LOCAL&lt;/span&gt; &lt;span class="n"&gt;enable_bitmapscan&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;off&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;EXECUTE&lt;/span&gt; &lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'SELECT array_agg(%I::text) FROM (SELECT %I FROM %I ORDER BY %I &amp;lt;=&amp;gt; $1 LIMIT $2) AS exact_search'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
                       &lt;span class="n"&gt;id_col_param&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;id_col_param&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;table_name_param&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;vector_col_param&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;exact_ids&lt;/span&gt;
        &lt;span class="k"&gt;USING&lt;/span&gt; &lt;span class="n"&gt;current_vector&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;k_param&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="c1"&gt;-- 2. Find approximate nearest neighbors (allowing index scan)&lt;/span&gt;
        &lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="k"&gt;LOCAL&lt;/span&gt; &lt;span class="n"&gt;enable_indexscan&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;on&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="k"&gt;LOCAL&lt;/span&gt; &lt;span class="n"&gt;enable_bitmapscan&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;on&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;EXECUTE&lt;/span&gt; &lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'SELECT array_agg(%I::text) FROM (SELECT %I FROM %I ORDER BY %I &amp;lt;=&amp;gt; $1 LIMIT $2) AS approx_search'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
                       &lt;span class="n"&gt;id_col_param&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;id_col_param&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;table_name_param&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;vector_col_param&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;approx_ids&lt;/span&gt;
        &lt;span class="k"&gt;USING&lt;/span&gt; &lt;span class="n"&gt;current_vector&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;k_param&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="c1"&gt;-- 3. Calculate overlap&lt;/span&gt;
        &lt;span class="n"&gt;IF&lt;/span&gt; &lt;span class="n"&gt;exact_ids&lt;/span&gt; &lt;span class="k"&gt;IS&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt; &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;approx_ids&lt;/span&gt; &lt;span class="k"&gt;IS&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt; &lt;span class="k"&gt;THEN&lt;/span&gt;
            &lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="k"&gt;COUNT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;overlap_count&lt;/span&gt;
            &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="k"&gt;unnest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;approx_ids&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;
            &lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="k"&gt;unnest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;exact_ids&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

            &lt;span class="n"&gt;vector_recall&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;overlap_count&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nb"&gt;double&lt;/span&gt; &lt;span class="nb"&gt;precision&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;k_param&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="n"&gt;sum_recall&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sum_recall&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;vector_recall&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

            &lt;span class="n"&gt;IF&lt;/span&gt; &lt;span class="n"&gt;vector_recall&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;min_recall&lt;/span&gt; &lt;span class="k"&gt;THEN&lt;/span&gt; &lt;span class="n"&gt;min_recall&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;vector_recall&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;END&lt;/span&gt; &lt;span class="n"&gt;IF&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="n"&gt;IF&lt;/span&gt; &lt;span class="n"&gt;vector_recall&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;max_recall&lt;/span&gt; &lt;span class="k"&gt;THEN&lt;/span&gt; &lt;span class="n"&gt;max_recall&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;vector_recall&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;END&lt;/span&gt; &lt;span class="n"&gt;IF&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

            &lt;span class="n"&gt;total_queries&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;total_queries&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;END&lt;/span&gt; &lt;span class="n"&gt;IF&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;END&lt;/span&gt; &lt;span class="n"&gt;LOOP&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;-- Print recall report&lt;/span&gt;
    &lt;span class="n"&gt;IF&lt;/span&gt; &lt;span class="n"&gt;total_queries&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="k"&gt;THEN&lt;/span&gt;
        &lt;span class="n"&gt;RAISE&lt;/span&gt; &lt;span class="n"&gt;NOTICE&lt;/span&gt; &lt;span class="s1"&gt;'------------------------------------------------------------------------'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;RAISE&lt;/span&gt; &lt;span class="n"&gt;NOTICE&lt;/span&gt; &lt;span class="s1"&gt;'Recall Evaluation Finished.'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;RAISE&lt;/span&gt; &lt;span class="n"&gt;NOTICE&lt;/span&gt; &lt;span class="s1"&gt;'Total queries evaluated: %'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;total_queries&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;RAISE&lt;/span&gt; &lt;span class="n"&gt;NOTICE&lt;/span&gt; &lt;span class="s1"&gt;'Average Recall@%: %%%'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;k_param&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;round&lt;/span&gt;&lt;span class="p"&gt;(((&lt;/span&gt;&lt;span class="n"&gt;sum_recall&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;total_queries&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)::&lt;/span&gt;&lt;span class="nb"&gt;numeric&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;RAISE&lt;/span&gt; &lt;span class="n"&gt;NOTICE&lt;/span&gt; &lt;span class="s1"&gt;'Minimum Recall@%: %%%'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;k_param&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;round&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;min_recall&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)::&lt;/span&gt;&lt;span class="nb"&gt;numeric&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;RAISE&lt;/span&gt; &lt;span class="n"&gt;NOTICE&lt;/span&gt; &lt;span class="s1"&gt;'Maximum Recall@%: %%%'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;k_param&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;round&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;max_recall&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)::&lt;/span&gt;&lt;span class="nb"&gt;numeric&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;RAISE&lt;/span&gt; &lt;span class="n"&gt;NOTICE&lt;/span&gt; &lt;span class="s1"&gt;'------------------------------------------------------------------------'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;ELSE&lt;/span&gt;
        &lt;span class="n"&gt;RAISE&lt;/span&gt; &lt;span class="n"&gt;WARNING&lt;/span&gt; &lt;span class="s1"&gt;'No recall queries were successfully evaluated.'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;END&lt;/span&gt; &lt;span class="n"&gt;IF&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;END&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="err"&gt;$$&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






</description>
      <category>data</category>
      <category>alloydb</category>
      <category>postgres</category>
      <category>vectorembeddings</category>
    </item>
    <item>
      <title>Context bankruptcy: The case for strategic forgetting for AI Agents</title>
      <dc:creator>James O'Reilly</dc:creator>
      <pubDate>Fri, 10 Jul 2026 14:12:20 +0000</pubDate>
      <link>https://dev.to/googleai/context-bankruptcy-the-case-for-strategic-forgetting-for-ai-agents-3c5</link>
      <guid>https://dev.to/googleai/context-bankruptcy-the-case-for-strategic-forgetting-for-ai-agents-3c5</guid>
      <description>&lt;p&gt;Most of us have seen a coding agent fail to complete a task we know it can do. We just don't understand the inconsistency in quality and lazily chalk it up to the "non-deterministic" nature of AI. What you might not realize is that the agent isn't failing because of intelligence, it's failing because its context is cluttered with noise.&lt;/p&gt;

&lt;p&gt;To fix this, we need to shift from context maximizing to deterministic pruning. Here is what you will get out of this deep dive to help you build those boundaries:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Why feeding an agent your entire repository causes agent reasoning failures.
&lt;/li&gt;
&lt;li&gt;How to navigate brownfield codebases by isolating subsystems for agents.
&lt;/li&gt;
&lt;li&gt;How to dynamically mask dead code before launching an agent turn.
&lt;/li&gt;
&lt;li&gt;The essentials for a reliable pruning harness.
&lt;/li&gt;
&lt;li&gt;Why unconstrained context bloats PRs and burns out reviewers.&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;In computer architecture, &lt;a href="https://en.wikipedia.org/wiki/Thrashing_(computer_science)" rel="noopener noreferrer"&gt;thrashing&lt;/a&gt; occurs when an executing process demands more memory than the caches can hold. The processor stops executing useful instructions and spends all its resources swapping pages of memory in and out of RAM.&lt;/p&gt;

&lt;p&gt;Coding agents have a similar bottleneck inside their prompt windows. With brownfield development, it might be tempting to dump the whole legacy codebase into context and prompt "refactor this to Next.js" (Read my article &lt;a href="https://javascript.plainenglish.io/migrating-express-to-next-js-using-ai-agents-antigravity-f48b4c206a8e" rel="noopener noreferrer"&gt;How to automate modernization with Antigravity and multi-agent orchestration&lt;/a&gt; to learn why this is a bad idea and what to do instead). However, if you do dump the whole codebase into context the agent is more likely to confuse dead code and old library usage as current intent.&lt;/p&gt;

&lt;p&gt;You've reached &lt;strong&gt;context bankruptcy&lt;/strong&gt; when the context actively degrades reasoning accuracy.&lt;/p&gt;

&lt;h2&gt;
  
  
  The visibility trap and why more context breaks reasoning
&lt;/h2&gt;

&lt;p&gt;Some developers treat complete repository ingestion as a headline feature of the large context windows of frontier models. They assume comprehensive visibility equals deep understanding. It doesn't.&lt;/p&gt;

&lt;p&gt;In clean greenfield projects, broad context ingestion works but in a multi-million-line legacy codebase, broad context ingestion adversely impacts an agent's ability to reason.&lt;/p&gt;

&lt;p&gt;Most of us don't work on greenfield apps and many work inside messy poorly documented legacy codebases they inherited. They typically contain gems like folders named "v1-alpha", temporary helper scripts that are no longer usable or reflect current code, and orphaned feature flags. When an agent sweeps this data into context via naive RAG sweeps or massive context windows, it treats outdated patterns as current engineering truth.&lt;/p&gt;

&lt;p&gt;The rule driving this failure is &lt;a href="https://www.hyrumslaw.com/" rel="noopener noreferrer"&gt;Hyrum's Law&lt;/a&gt; applied to AI context windows: &lt;em&gt;with sufficient ingestion breadth, every observable historical implementation detail will eventually be depended upon by an autonomous model&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Give an agent access to 1,000 files and it will reason over your hackiest workarounds. It anchors on deprecated helper scripts and replicates tech debt into new modules.&lt;/p&gt;




&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F39ptx1bw5nlvsiro3qzz.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F39ptx1bw5nlvsiro3qzz.jpg" alt="Selective ignorance and how to enforce the boundary" width="799" height="198"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Selective ignorance and how to enforce the boundary
&lt;/h2&gt;

&lt;p&gt;When I review a legacy monolith I'm not familiar with, I don't read every file.&lt;/p&gt;

&lt;p&gt;I practice &lt;a href="https://martinfowler.com/articles/bliki/BoundedContext.html" rel="noopener noreferrer"&gt;selective ignorance&lt;/a&gt; and deliberately ignore most of the repository when building a mental model around the module I want to modify. I do this by treating the surrounding subsystems as if they were black boxes bounded by strict contracts.&lt;/p&gt;

&lt;p&gt;The agent execution environments should model this behavior.&lt;/p&gt;

&lt;p&gt;Addy Osmani recently documented &lt;a href="https://addyo.substack.com/p/context-engineering-bringing-engineering" rel="noopener noreferrer"&gt;context rot&lt;/a&gt; (the measurable performance dip models experienced as prompt windows fill) and &lt;a href="https://addyosmani.com/blog/agentic-engine-optimization/" rel="noopener noreferrer"&gt;agentic engine optimization (AEO)&lt;/a&gt; (formatting codebase documentation). Our industry remains obsessed with ingestion pipelines and larger token limits. Nobody is building harness infrastructure designed to force strategic forgetting.&lt;/p&gt;

&lt;p&gt;To protect agent reasoning we must shift our engineering effort from context maximization to deterministic pruning. &lt;/p&gt;

&lt;p&gt;&lt;em&gt;If the agent sees the whole repository, you aren't scaling engineering velocity. You're scaling cognitive debt.&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fekpba7la5yzgdc3i6ypx.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fekpba7la5yzgdc3i6ypx.jpg" alt="Pruning harnesses versus context bankruptcy" width="799" height="198"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Pruning harnesses versus context bankruptcy
&lt;/h2&gt;

&lt;p&gt;I treat context budget allocation as an architectural constraint because &lt;strong&gt;strategic forgetting&lt;/strong&gt; requires replacing broad ingestion with deterministic pruning.&lt;/p&gt;

&lt;p&gt;The contrast between unbound ingestion and strategic forgetting is significant.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Architectural Dimension&lt;/th&gt;
&lt;th&gt;Unbound Ingestion (Context Bankruptcy)&lt;/th&gt;
&lt;th&gt;Strategic Forgetting (Pruning Harness)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Ingestion Strategy&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Global vector RAG sweeps or broad context dumping across &lt;code&gt;**/*&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Dynamic AST pruning scoped strictly to active dependency graphs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Legacy Conflict Resolution&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Ingests competing API wrappers; model hallucinates hybrid syntax&lt;/td&gt;
&lt;td&gt;Hides deprecated subsystems; enforces single active contract&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Prompt Capacity&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;85%+ context budget consumed by historical boilerplate and dead code&lt;/td&gt;
&lt;td&gt;&amp;lt;15% context budget consumed; working memory preserved for execution&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Reasoning Stability&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;High susceptibility to context rot; attention allocation dilutes over noise&lt;/td&gt;
&lt;td&gt;High attention concentration; deterministic diff generation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Verification Overhead&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;High cognitive load; engineers scrutinize massive diffs for side effects&lt;/td&gt;
&lt;td&gt;Minimal cognitive load; diffs match expected five-line boundary&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;em&gt;Architectural context pruning is not an optimization step. It is a baseline reliability prerequisite. If an autonomous agent can read deprecated internal modules, it will eventually depend on them.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Models are probabilistic which makes them unreliable at deterministic access controls. Simply refining a prompt to instruct the model to ignore sections of a large codebase will fail under complex reasoning loads. This is because models will attend to tokens inside their prompt windows regardless if those tokens are no longer relevant to the project.&lt;/p&gt;

&lt;p&gt;To guarantee selective ignorance, implement &lt;strong&gt;a muted repo pattern&lt;/strong&gt; which dynamically manipulates repository visibility before launching an agent turn. Here's how it works…&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Map active dependency graphs&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Run a static Abstract Syntax Tree (AST) parse starting from the target entry file. Generate a strict allowlist containing only immediate upstream imports and downstream consumers.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Dynamically create ignore masks&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Write an ephemeral &lt;code&gt;.antigravityignore&lt;/code&gt;, &lt;code&gt;.geminiignore&lt;/code&gt;, &lt;code&gt;.claudesignore&lt;/code&gt; or &lt;code&gt;.cursorignore&lt;/code&gt; file before the agent turns. Mask out &lt;code&gt;archive/&lt;/code&gt;, deprecated helpers, and unrelated domain packages.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Use interfaces as substitutes for implementation&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Replace complex external database wrappers and legacy integrations with clean interfaces or OpenAPI stubs. Force the model to code against these interfaces.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Launch scoped agent turns&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Execute the agent prompt inside the pruned sandbox. This enables the model to work with focused attention free of historical distraction.&lt;/p&gt;




&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F7jy0r9amn9m4e8g24crn.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F7jy0r9amn9m4e8g24crn.jpg" alt="Essential implementation components" width="799" height="198"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Essential implementation components
&lt;/h3&gt;

&lt;p&gt;Building a deterministic pruning harness relies on three core components.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Runtime CLI file gating&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Modern agentic IDEs maintain persistent workspace indexes so I avoid editing &lt;code&gt;.antigravityignore&lt;/code&gt;, &lt;code&gt;.geminiignore&lt;/code&gt;, &lt;code&gt;.claudesignore&lt;/code&gt; or &lt;code&gt;.cursorignore&lt;/code&gt; dynamically per agent turn because overwriting ignore manifests trigger file-watcher invalidation events. When that happens the machine rebuilds codebase indexes in a race condition against the model's first token.&lt;/p&gt;

&lt;p&gt;To avoid editing the ignored files each agent turn, one option is passing file allowlist flags directly into your terminal command when launching the agent (&lt;code&gt;agy --add-dir src/auth/dir&lt;/code&gt;). Or, if building a custom harness, pass allowed file arrays into your tool execution APIs in memory.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# .claudesignore .cursorignore .geminiignore .antigravityignore
# Force strategic forgetting in brownfield repositories

**/node_modules/
**/dist/
**/build/

# Gated Legacy Systems - Block the agent from historical noise
/legacy/v1-alpha/
/deprecated-soap-wrappers/
**/*.deprecated.ts

# Large static assets that trigger context thrashing
**/*.json
**/*.csv
package-lock.json
pnpm-lock.yaml

# Block the agent from analyzing un-verifiable dark matter code
/e2e/stale-tests-2022/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;2. Hybrid semantic dependency mapping&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Do not rely on naive vector embeddings to select codebase context. Vector similarity retrieves semantically similar code which can pick up unwanted legacy code.&lt;/p&gt;

&lt;p&gt;Pure static AST tree-shaking would likely fail in brownfield monoliths because legacy systems lie at compile time. I'm thinking about systems that run on Java reflection, dependency injection containers, and runtime module loaders. For example, a shallow AST transversal would miss dynamically injected resources leading to dependency pruning causing runtime exceptions.&lt;/p&gt;

&lt;p&gt;Instead, combine shallow AST parsing with recent observability outputs or runtime error logs to discover dynamic runtime dependencies before gating context windows. If runtime telemetry proves &lt;code&gt;verify.ts&lt;/code&gt; calls &lt;code&gt;legacy-sql-pool.js&lt;/code&gt; via DI reflection, include &lt;code&gt;legacy-sql-pool.js&lt;/code&gt; in the allowlist. If static analysis and runtime traces both show zero execution paths to &lt;code&gt;deprecated-auth.ts&lt;/code&gt;, keep it out of the allowlist.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Differential verification gating&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Brownfield architecture is typically handled by isolating complexity behind interfaces. For example, modifying a billing service doesn't require you to read connection pool source code.&lt;/p&gt;

&lt;p&gt;We can apply this thinking to agent prompt windows. By replacing deep internal library code with lightweight interface definitions, the model receives the correct contract specifications without ingesting implementation noise.&lt;/p&gt;

&lt;p&gt;I do want to point out however, that in legacy systems interfaces may lie. It's the runtime side effects that are the real source of truth. If you do decide to stub code to save tokens during generation, be sure to enforce a &lt;a href="https://en.wikipedia.org/wiki/Differential_testing" rel="noopener noreferrer"&gt;differential verification gate&lt;/a&gt;. That way, success can be measured by passing tests against the legacy runtime, not the contract stubs.&lt;/p&gt;

&lt;h3&gt;
  
  
  How Antigravity implements pruning
&lt;/h3&gt;

&lt;p&gt;Since I work at Google on Antigravity, I wanted to share my knowledge of how Antigravity implements isolation natively:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Subagent context isolation:&lt;/strong&gt; Antigravity separates exploration from execution. The main agent has the ability to dynamically generate subagents which will have their own isolated context buffers and instructions from the main agent. The main agent may decide to delegate token-heavy or codebase-wide operations to subagents and keep the primary execution memory clean.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Planning mode checkpoints:&lt;/strong&gt; Enforce explicit planning artifacts (&lt;code&gt;/plan&lt;/code&gt;) so reviewers verify implementation design decisions and verification strategy before code generation.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ff9ooryie85sbhnkq2o6g.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ff9ooryie85sbhnkq2o6g.jpg" alt="Vigilance fatigue" width="799" height="198"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Vigilance fatigue&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Unconstrained context ingestion doesn't just degrade model reasoning, it also has a toll on people. I know when I review lengthy and broad, machine-generated diffs it triggers vigilance fatigue.&lt;/p&gt;

&lt;p&gt;Reviewing code submitted by one of my colleagues is different from reviewing AI-generated code because I have less trust that basic architectural boundaries have been honored. I end up scrutinizing every line of code to verify the edit represents intentional business logic and not accidental side effects caused by historical noise.&lt;/p&gt;

&lt;p&gt;As reviews grow in size and number, my attention struggles. It becomes increasingly difficult to avoid rubber-stamping bloated pull requests. Strategic forgetting helps protect reviewers by limiting the agent's reach.&lt;/p&gt;




&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fxscm4fz1xson0twnlgio.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fxscm4fz1xson0twnlgio.jpg" alt="Tuesday morning checklist" width="799" height="198"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Tuesday morning checklist
&lt;/h2&gt;

&lt;p&gt;Most engineering organizations measure AI maturity by &lt;strong&gt;counting deployed subagents&lt;/strong&gt; or &lt;strong&gt;tracking token consumption volume&lt;/strong&gt;. In my opinion, both are vanity indicators.&lt;/p&gt;

&lt;p&gt;True agentic scaling requires shifting focus from ingestion maxing to isolation discipline. Audit your agent workflows against three deterministic pruning heuristics:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Enforce small diff boundaries:&lt;/strong&gt; If a localized bug fix generates PRs touching more than two files, halt execution. Your agent isn't being thorough, it's probably distracted by unrelated legacy code..&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Treat ignore manifests as dynamic build artifacts:&lt;/strong&gt; Don't maintain &lt;code&gt;.antigravityignore&lt;/code&gt; files manually. Compile ignore manifests dynamically before agent turns combining AST import traversal with runtime trace telemetry (observability/error logs) so dynamic dependency injection paths stay unmasked.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Sandbox dependencies behind contract stubs:&lt;/strong&gt; If an agent can read implementation source code for external SDKs or shared database wrappers, hide it. Use interface definitions or OpenAPI contract stubs instead.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Your agent headcount matters less than your isolation boundaries.&lt;/p&gt;

&lt;h2&gt;
  
  
  Additional resources
&lt;/h2&gt;

&lt;p&gt;Here are a few other agentic brownfield development resources:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Article&lt;/strong&gt;: &lt;a href="https://javascript.plainenglish.io/migrating-express-to-next-js-using-ai-agents-antigravity-f48b4c206a8e" rel="noopener noreferrer"&gt;How to automate modernization with Antigravity and multi-agent orchestration&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Codelab&lt;/strong&gt;: &lt;a href="https://codelabs.developers.google.com/automating-modernization-with-antigravity" rel="noopener noreferrer"&gt;Automating legacy modernization at scale using agentic pipelines and Antigravity&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Help others find this post.&lt;/strong&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Share this post with your friends on socials.
&lt;/li&gt;
&lt;li&gt;Follow me on &lt;a href="https://www.linkedin.com/in/jamesor/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt; or &lt;a href="https://x.com/JamesOR" rel="noopener noreferrer"&gt;X&lt;/a&gt; for more content like this.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Thanks for reading!&lt;/p&gt;

</description>
      <category>ai</category>
      <category>programming</category>
      <category>agentskills</category>
    </item>
    <item>
      <title>The Antigravity Loop: From Chat to Autonomous Engineering</title>
      <dc:creator>Romin Irani</dc:creator>
      <pubDate>Fri, 10 Jul 2026 07:18:37 +0000</pubDate>
      <link>https://dev.to/googleai/the-antigravity-loop-from-chat-to-autonomous-engineering-295g</link>
      <guid>https://dev.to/googleai/the-antigravity-loop-from-chat-to-autonomous-engineering-295g</guid>
      <description>&lt;p&gt;&lt;em&gt;How to go from pair-programming with AI to fully hands-off autonomous engineering and when each mode actually shines&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fp2uf7j5631vp6au4wo7l.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fp2uf7j5631vp6au4wo7l.png" width="800" height="447"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Infographic generated by NotebookLM&lt;/em&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Introduction
&lt;/h3&gt;

&lt;p&gt;Here’s a scenario most developers know: you’re building a feature, and you ask an AI assistant to help. You type a prompt, read the response, type another prompt, read, type, read. Twenty exchanges later, the feature works. Your fingers are tired, but you’re happy.&lt;/p&gt;

&lt;p&gt;Now imagine a different scenario: you write a single, detailed brief, like handing a spec to a senior engineer and walk away. You come back 30 minutes later to find the feature fully implemented, tested, deployed, and documented. Your fingers barely moved.&lt;/p&gt;

&lt;p&gt;Both of these are real ways to work with Google Antigravity. The first is &lt;strong&gt;Chat Mode&lt;/strong&gt; , which is conversational, interactive, real-time. The second is &lt;strong&gt;Goal Mode&lt;/strong&gt; , which is autonomous, hands-off, mission-driven.&lt;/p&gt;

&lt;p&gt;Most developers live exclusively in the first. This post will show you when, why, and how to use the second mode i.e. the &lt;strong&gt;Goal mode&lt;/strong&gt; and to get there with the right kind of requirements. This post is not about which mode is better than the other, rather it is an exploration of which modes are best suited to which scenarios.&lt;/p&gt;
&lt;h3&gt;
  
  
  What Exactly Are These Modes?
&lt;/h3&gt;

&lt;p&gt;Before diving into examples, let’s be precise about what each mode means in Antigravity:&lt;/p&gt;
&lt;h4&gt;
  
  
  Chat Mode (The Default)
&lt;/h4&gt;

&lt;p&gt;You type a message. The agent responds. You type again. The conversation proceeds turn by turn. The agent might edit files, run commands, or search the web, but it waits for your input after each step. You see every change as it happens and can redirect immediately.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Think of it like:&lt;/strong&gt; Pair programming with a colleague sitting next to you. You’re both looking at the screen, talking through every decision.&lt;/p&gt;
&lt;h4&gt;
  
  
  Planning Mode (The Middle Ground)
&lt;/h4&gt;

&lt;p&gt;When you ask for something architecturally significant, Antigravity automatically shifts into planning mode. It researches your codebase, creates a detailed implementation_plan.md artifact, and presents it with a "Proceed" button. Nothing changes in your code until you approve. After approval, it executes the plan and creates a &lt;strong&gt;walkthrough&lt;/strong&gt; artifact, summarizing what it did.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Think of it like:&lt;/strong&gt; Hiring a contractor. They survey the site, give you a detailed quote and plan, and only start work after you sign off.&lt;/p&gt;
&lt;h4&gt;
  
  
  Goal Mode (/goal)
&lt;/h4&gt;

&lt;p&gt;You prefix your prompt with /goal. The agent enters a persistent execution loop: research → plan → execute → verify → fix → repeat. It doesn't stop to ask you questions. It handles errors on its own. It may spawn sub-agents for parallel work. It only reports back when it's done (or when it's genuinely stuck and needs human input).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Think of it like:&lt;/strong&gt; Assigning a ticket to a senior engineer. You write the requirements, set the acceptance criteria, and check back when you get the “PR ready for review” notification.&lt;/p&gt;
&lt;h3&gt;
  
  
  The Spectrum of Autonomy
&lt;/h3&gt;

&lt;p&gt;These modes aren’t a binary switch, they’re a dial:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fmk63wap1oxxxs17jkutk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fmk63wap1oxxxs17jkutk.png" width="800" height="98"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For each of the above modes, let us look at Human control v/s Agent autonomy, as shown below:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fhgxbqes905pwfx8hcm1x.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fhgxbqes905pwfx8hcm1x.png" width="800" height="161"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Another way to understand these modes is to understand what the roles of a human v/s Agent are i.e. who is in control during a specific mode and what is each mode best suited for.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fv8n1d0fbln44ajwuk16x.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fv8n1d0fbln44ajwuk16x.png" width="800" height="193"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Example 1: Chat Mode — Quick Fixes and Exploration
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;When to use:&lt;/strong&gt; The task is trivial, you want to learn something, or you’re not sure what you want yet.&lt;/p&gt;
&lt;h4&gt;
  
  
  Scenario A: Fix a Bug
&lt;/h4&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Fix the typo in README.md — "recieve" should be "receive"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;What happens:&lt;/strong&gt; The agent makes the single edit and shows you the diff. Done in a few seconds (hopefully). No plan, no artifact, no ceremony.&lt;/p&gt;
&lt;h4&gt;
  
  
  Scenario B: Understand Your Code
&lt;/h4&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Explain how the authentication middleware works in server.js. 
Walk me through the flow step by step.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;What happens:&lt;/strong&gt; The agent reads the file, locates the middleware, and gives you a clear explanation with references to the relevant functions. It doesn’t change anything — it’s pure exploration. This is something Goal Mode may not be good at, since there is no “deliverable” to verify.&lt;/p&gt;
&lt;h4&gt;
  
  
  Scenario C: Brainstorm Ideas
&lt;/h4&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;What are some ways I could improve the onboarding flow in this app? 
The current sign-up takes 4 steps.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;What happens:&lt;/strong&gt; The agent analyzes your codebase, understands the current flow, and proposes improvements with rationale. This is inherently interactive, you want to react, ask follow-ups, and refine. Chat mode is perfect.&lt;/p&gt;
&lt;h4&gt;
  
  
  Why Chat Mode Is Right Here
&lt;/h4&gt;

&lt;p&gt;These tasks share a common trait: &lt;strong&gt;you can’t pre-specify the outcome&lt;/strong&gt;. You don’t know what the agent will say about your code. You don’t know which brainstormed ideas will resonate. You need to see intermediate results before deciding what’s next.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;Rule of thumb:&lt;/em&gt;&lt;/strong&gt; &lt;em&gt;If the next step depends on seeing this step’s output, use chat.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;
  
  
  Example 2: Guided Chat — Building Features Interactively
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;When to use:&lt;/strong&gt; You know the feature you want, but the implementation details should be driven by conversation.&lt;/p&gt;
&lt;h4&gt;
  
  
  Real-World Example: Adding Dark Mode
&lt;/h4&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Prompt 1: "Add a dark mode toggle to the header. Use CSS custom 
           properties for theming. Save the preference to 
           localStorage so it persists."

[Agent implements the toggle, CSS variables, localStorage logic]

Prompt 2: "That works, but the transition is jarring. Add a 300ms 
           ease on all color properties."

[Agent adds smooth transition CSS]

Prompt 3: "The icons in the sidebar aren't switching colors. They're 
           using hardcoded hex values."

[Agent finds and replaces the hardcoded colors with CSS variables]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;Total interaction:&lt;/strong&gt; 3 prompts, ~5 minutes of your attention. Each prompt built on seeing the result of the previous one.&lt;/p&gt;
&lt;h4&gt;
  
  
  Why Guided Chat Is Right Here
&lt;/h4&gt;

&lt;p&gt;You could have written all three requirements upfront as a goal. But you didn’t know about the jarring transition or the hardcoded icon colors until you saw the first implementation. The iterative nature of guided chat lets you &lt;strong&gt;discover requirements as you go&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;This is the most common mode for day-to-day feature development. You have a general direction, but the details emerge through interaction.&lt;/p&gt;
&lt;h3&gt;
  
  
  Example 3: Planning Mode — Architectural Changes
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;When to use:&lt;/strong&gt; The change is significant enough that you want to review the approach before any code changes happen.&lt;/p&gt;
&lt;h3&gt;
  
  
  Scenario: Adding Firebase Authentication
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Add Google Sign-In to the app using Firebase Authentication.
Only authenticated users should see the main content. 
Show a login screen with a Google sign-in button for unauthenticated users.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;What happens automatically:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Research phase:&lt;/strong&gt; The agent audits your codebase. It finds your existing HTML structure, identifies where the login gate should go, checks if any Firebase SDKs are already loaded, and reviews your current routing.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Plan creation:&lt;/strong&gt; The agent creates an implementation_plan.md covering:&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;Which Firebase SDKs to add&lt;/li&gt;
&lt;li&gt;Where to insert the auth state listener&lt;/li&gt;
&lt;li&gt;How to gate the main content behind onAuthStateChanged&lt;/li&gt;
&lt;li&gt;Security considerations (Firestore rules, protected routes)&lt;/li&gt;
&lt;li&gt;Proposed file changes with estimated line numbers&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;3. Review checkpoint:&lt;/strong&gt; The plan appears as an artifact with a “ &lt;strong&gt;Proceed&lt;/strong&gt; ” button. You read exactly what’s going to change before a single line of code is modified.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Execution:&lt;/strong&gt; After you click “ &lt;strong&gt;Proceed&lt;/strong&gt; ,” the agent executes all steps, verifies the login flow works, and creates a walkthrough artifact summarizing what changed.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F6eo5h4ej423b46n2gxp9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F6eo5h4ej423b46n2gxp9.png" width="800" height="216"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The critical difference from Goal Mode: &lt;strong&gt;the human review checkpoint&lt;/strong&gt; at step 3. Nothing changes in your code until you approve.&lt;/p&gt;
&lt;h4&gt;
  
  
  Why Planning Mode Is Right Here
&lt;/h4&gt;

&lt;p&gt;Authentication is a cross-cutting concern. It touches your HTML structure, JavaScript logic, security rules, and potentially your deployment config. If the agent had started editing without a plan, a wrong assumption about your routing could cascade into a broken app. The plan gives you a chance to catch issues before they become problems.&lt;/p&gt;
&lt;h4&gt;
  
  
  How Planning Mode Gets Triggered
&lt;/h4&gt;

&lt;p&gt;You don’t explicitly ask for it, though you could. Antigravity decides based on the scope of your request and this is what I have typically observed:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Simple fixes → direct execution&lt;/li&gt;
&lt;li&gt;New features → may or may not plan (depends on complexity)&lt;/li&gt;
&lt;li&gt;Architectural changes, migrations, multi-file refactors → almost always plans&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you want a plan for something the agent would normally just do, say: “Create a plan for this before implementing.”&lt;/p&gt;
&lt;h3&gt;
  
  
  Example 4: Goal Mode — The Power Move
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;When to use:&lt;/strong&gt; The task is well-defined, you’ve pre-answered every decision, and you want to walk away.&lt;/p&gt;
&lt;h4&gt;
  
  
  The Transition Point: Chat vs. Goal
&lt;/h4&gt;

&lt;p&gt;Let’s compare two approaches to the same task: migrating a REST API integration from fetch to a typed SDK with error handling.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Approach A: Chat Mode (interactive, ~30 minutes)&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Exchange 1: "I want to replace raw fetch calls with the 
              official SDK. Where are all the API calls?"
Exchange 2: [Reviews list] "Start with the user endpoints."
Exchange 3: [Reviews changes] "The error handling isn't matching 
              our pattern. Use the AppError class."
Exchange 4: [Reviews fix] "Good. Now do the products endpoints."
Exchange 5: [Reviews changes] "You missed the pagination helper."
Exchange 6: [Reviews fix] "Now the orders endpoints."
Exchange 7: [Reviews changes] "The types are wrong on the 
              response object."
Exchange 8: [Reviews fix] "Looks good. Run the tests."
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Total time:&lt;/strong&gt; ~30 minutes of continuous attention across 8 exchanges.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Approach B: Goal Mode (autonomous, ~5 minutes of your time)&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;**/goal**

Replace all raw fetch() API calls with the official TypeScript SDK.

## Current State
- API calls are in src/api/*.ts (users.ts, products.ts, orders.ts)
- Raw fetch with manual JSON parsing
- Error handling is inconsistent across files
- Types are defined in src/types/api.ts

## Requirements
1. Install @acme/sdk@^3.0 via npm
2. Replace every fetch() call with the equivalent SDK method
3. Use the existing AppError class (src/utils/errors.ts) for 
   all error handling
4. Ensure all response types match src/types/api.ts
5. Handle pagination using SDK's built-in paginator
6. Remove unused fetch utility functions from src/utils/http.ts

## Constraints
- Don't change any component code that calls these API functions
- Keep the same function signatures (callers shouldn't need to change)
- Don't modify existing types — adapt SDK responses to match them

## Verification
- npm run typecheck passes with zero errors
- npm run test passes (all existing tests should still work)
- grep 'fetch(' src/api/ → zero results
- grep 'AppError' src/api/ → at least one result per file
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Total time:&lt;/strong&gt; ~5 minutes to write the prompt. Walk away.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What the agent does autonomously:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1. Research → Reads all files in src/api/, catalogs every fetch call
2. Plan → Maps each fetch call to its SDK equivalent
3. Execute → Installs SDK, rewrites all three files
4. Verify → Runs typecheck → finds 2 type mismatches → fixes them
5. Re-verify → Runs tests → one test fails due to mock changes → 
                fixes the mock → all tests pass
6. Final grep → Confirms zero remaining fetch() calls
7. Report → Creates walkthrough summarizing all changes
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The key difference: in Goal Mode, the type mismatches and failing test would have been caught and fixed &lt;strong&gt;automatically&lt;/strong&gt; during the verify-fix loop. In Chat Mode, those same issues required two more exchanges of your time.&lt;/p&gt;

&lt;h3&gt;
  
  
  Example 5: Goal Mode — Multi-Component Feature
&lt;/h3&gt;

&lt;p&gt;Here’s a more complex goal prompt:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;**/goal**

Add a real-time admin dashboard to the web application.

## Requirements

### Navigation
- Add a "Dashboard" link in the sidebar, visible only to admin users
- Create a new route /admin/dashboard
- Include three tabs: "Users", "Activity", "Revenue"

### Users Tab
- Total registered users (count)
- New users this week (count with trend arrow)
- User growth chart (last 12 weeks, inline SVG bar chart)
- Top 10 most active users (sorted by login count)

### Activity Tab
- Actions per day chart (last 30 days, SVG line chart)
- Most common actions (table with percentages)
- Error rate (percentage with color indicator)

### Revenue Tab
- Monthly revenue chart (last 6 months, SVG bar chart)
- Average order value
- Top selling products (table)

### Design
- Use the existing design system (Tailwind CSS classes)
- Match the existing card component style
- Dark mode compatible
- Responsive — usable on tablet and desktop
- Animate chart elements on render

### Data
- Query existing Firestore collections: users, events, orders
- Use real-time onSnapshot listeners for live updates
- Admin check: user.role === 'admin'

### Constraints
- No external charting libraries (pure SVG)
- Admin-only: gate with role check, redirect non-admins
- Don't modify existing routes or components

### Verification
- Dashboard only accessible when user.role === 'admin'
- All three tabs render without console errors
- Charts display correctly with sample data
- Responsive at 768px viewport width
- npm run build succeeds
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;What makes this a good goal prompt:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Every decision is pre-made (no “should I use Chart.js?” ambiguity)&lt;/li&gt;
&lt;li&gt;Data sources are specified (which collections, which fields)&lt;/li&gt;
&lt;li&gt;Visual style is pinned to existing conventions&lt;/li&gt;
&lt;li&gt;Verification is concrete and testable&lt;/li&gt;
&lt;li&gt;Constraints prevent scope creep&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Anatomy of a Great Goal Prompt
&lt;/h3&gt;

&lt;p&gt;An effective /goal prompt should ideally have these sections:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;**/goal**  

[One-line summary of what you want]

## Current State
What exists today. Tech stack, file locations, relevant architecture.

## Requirements
Numbered list of specific, concrete deliverables.

## Constraints  
What NOT to do. Boundaries. Style rules. "Don't touch X."

## Verification
How the agent knows it's done. Tests to run, greps to check, 
behavior to validate.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  The Decision Pre-Answer Rule
&lt;/h4&gt;

&lt;p&gt;For every place where you’d normally say “yes, do that” or “no, use the other approach” in a chat conversation, &lt;strong&gt;pre-answer it in the goal prompt&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fg64okt40xjwddx537tda.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fg64okt40xjwddx537tda.png" width="799" height="274"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  What to Include in Verification
&lt;/h4&gt;

&lt;p&gt;Verification criteria are what separate a sub-optimal goal prompt from a good one. Think of them as acceptance tests:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fqdbv00flhgbnbdoxqpno.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fqdbv00flhgbnbdoxqpno.png" width="800" height="302"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Without verification criteria, the agent has no way to self-correct. It can’t enter the fix-verify loop because there’s no “verify” step defined.&lt;/p&gt;

&lt;h3&gt;
  
  
  Spec-Driven Development: The Bigger Idea
&lt;/h3&gt;

&lt;p&gt;If the structure of a goal prompt i.e. current state, requirements, constraints, verification looks familiar, it should. It’s a &lt;strong&gt;specification&lt;/strong&gt;. And what we’re really doing with /goal is something that we as software engineers have aspired to for decades now: &lt;strong&gt;writing a spec that executes itself&lt;/strong&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  The Old Way vs. The New Way
&lt;/h4&gt;

&lt;p&gt;Traditional software development has always involved specifications, but there was a gap between the spec and the code:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fx8zyeer62l9zrcz36bzo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fx8zyeer62l9zrcz36bzo.png" width="800" height="469"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the traditional flow, the spec is a &lt;strong&gt;communication artifact&lt;/strong&gt; , it tells a human what to build, and the human translates it into code. The translation step is where most bugs are born: misunderstandings, missed edge cases, things the spec assumed were obvious.&lt;/p&gt;

&lt;p&gt;In spec-driven development with Antigravity, the spec is an &lt;strong&gt;execution artifact&lt;/strong&gt;. It doesn’t just describe what to build, it directly drives the building. The “translation step” still exists (the agent interprets your spec), but the feedback loop is immediate: if the agent misinterprets something, the verification step catches it, and the agent self-corrects.&lt;/p&gt;

&lt;h4&gt;
  
  
  What Makes a Spec Executable?
&lt;/h4&gt;

&lt;p&gt;Not every spec works as a goal prompt. The difference between a traditional spec and an executable spec is &lt;strong&gt;verifiability&lt;/strong&gt;. Compare:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F6nlueb2c33vx3wrdcg4u.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F6nlueb2c33vx3wrdcg4u.png" width="800" height="202"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The left column is what humans interpret. The right column is what agents can verify. When every line in your spec has a corresponding check, you’ve written an executable specification.&lt;/p&gt;

&lt;h4&gt;
  
  
  The Spec as the Single Source of Truth
&lt;/h4&gt;

&lt;p&gt;Here’s where it gets powerful. In traditional development, the spec, the code, and the tests are three separate artifacts that can drift apart. The spec says one thing, the code does another, and the tests check a third.&lt;/p&gt;

&lt;p&gt;With a goal prompt:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The spec is the prompt&lt;/strong&gt; and it defines requirements, constraints, and verification.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The code is generated from the spec&lt;/strong&gt; by the agent, not by manual translation.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The tests are embedded in the spec&lt;/strong&gt; as &lt;strong&gt;verification criteria&lt;/strong&gt; that run automatically.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There’s one artifact instead of three. And when you need to change behavior, you update the spec and re-run it. You don’t separately update the requirements doc, then the code, then the tests.&lt;/p&gt;

&lt;h4&gt;
  
  
  Leveling Up: Composable Specs
&lt;/h4&gt;

&lt;p&gt;Once you’re comfortable with single goal prompts, you can compose them into multi-phase specifications. This is a possibility and you should try it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Phase 1 Spec: Authentication
  → Verification: users can sign in, auth state persists, 
    protected routes redirect

Phase 2 Spec: Data Layer (depends on Phase 1)
  → Verification: CRUD operations work, security rules enforce 
    auth, real-time updates sync

Phase 3 Spec: UI Dashboard (depends on Phase 2)
  → Verification: charts render, admin gate works, 
    responsive layout, no console errors
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each phase is a self-contained, verifiable spec. Phase 2 doesn’t start until Phase 1’s verification passes. This is dependency-ordered, verified, incremental development and each phase can run autonomously.&lt;/p&gt;

&lt;h4&gt;
  
  
  The Implication for Teams
&lt;/h4&gt;

&lt;p&gt;Spec-driven development changes how teams coordinate. Instead of:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Product manager writes a PRD&lt;/li&gt;
&lt;li&gt;Engineer reads the PRD and writes code&lt;/li&gt;
&lt;li&gt;QA reads the PRD and writes tests&lt;/li&gt;
&lt;li&gt;Everyone argues about what the PRD actually meant&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You get:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Product manager and engineer co-write an executable spec&lt;/li&gt;
&lt;li&gt;The spec runs and produces code, tests, and verification in one pass&lt;/li&gt;
&lt;li&gt;Review shifts from “does this code match the spec?” to “is this spec correct?”&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The bottleneck moves from &lt;strong&gt;implementation&lt;/strong&gt; to &lt;strong&gt;specification quality&lt;/strong&gt;. And that’s a much better bottleneck to have, because it’s a thinking bottleneck, not a typing bottleneck.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Slash Commands You Should Know
&lt;/h3&gt;

&lt;p&gt;Beyond /goal, Antigravity offers several slash commands that sit at different points on the autonomy spectrum:&lt;/p&gt;

&lt;h3&gt;
  
  
  /grill-me — The Bridge from Chat to Goal
&lt;/h3&gt;

&lt;p&gt;This is arguably the most important slash command to understand, because it solves the hardest problem in autonomous execution: &lt;strong&gt;how do you know what you don’t know?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;When you sit down to write a /goal prompt, you need to pre-answer every decision the agent would otherwise ask you about. But for complex tasks, you don't know what all those decisions are. You don't know what you haven't considered. That's where /grill-me comes in.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/grill-me I want to add multi-tenancy to my SaaS app
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The agent reads your codebase, understands the current architecture, and then &lt;strong&gt;interviews you&lt;/strong&gt; with a series of targeted questions — not generic ones, but questions specific to &lt;em&gt;your&lt;/em&gt; code and &lt;em&gt;your&lt;/em&gt; situation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Agent: I've reviewed your app. Here are the key decisions we need 
       to make before I can build this:

1. DATA ISOLATION: Your Firestore is currently a single database.
   Should each tenant get:
   a) Separate Firestore databases (strongest isolation, higher cost)
   b) Separate top-level collections per tenant (moderate isolation)  
   c) A tenantId field on every document (simplest, weakest isolation)

2. URL STRATEGY: Currently your app runs on app.example.com.
   Should tenants get:
   a) Subdomains (acme.app.example.com)
   b) Path-based routing (app.example.com/acme)
   c) Shared URL with tenant selected after login

3. AUTH BOUNDARY: Your Firebase Auth is project-wide. Should:
   a) Users be scoped to a single tenant
   b) Users be able to belong to multiple tenants
   c) Admin users have cross-tenant access

4. DATA ON DELETE: When a tenant is removed, should their data be:
   a) Hard deleted immediately
   b) Soft deleted with a 30-day recovery window
   c) Archived to Cloud Storage

5. EXISTING USERS: You have 340 users in production. How should 
   they be assigned to tenants during migration?
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You answer each question. The agent follows up on ambiguous answers. By the end of the interview — typically 5–10 minutes — you’ve surfaced every decision that would have been a blocking question during an autonomous run.&lt;/p&gt;

&lt;h4&gt;
  
  
  From Interview to Executable Spec
&lt;/h4&gt;

&lt;p&gt;Here’s the key: your answers from /grill-me &lt;strong&gt;become your goal prompt&lt;/strong&gt;. The interview output maps directly to the spec structure:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fl9fklbdx0zbgbzum7oc7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fl9fklbdx0zbgbzum7oc7.png" width="798" height="216"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Without /grill-me, you'd discover these decisions the hard way — either by writing an incomplete goal prompt that the agent guesses at (badly), or by having a long chat session where you gradually realize what you missed. /grill-me front-loads all of that discovery into a structured interview.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;Think of it this way:&lt;/em&gt;&lt;/strong&gt; &lt;em&gt;Chat is exploration.&lt;/em&gt; &lt;em&gt;/grill-me is structured discovery.&lt;/em&gt; &lt;em&gt;/goal is execution. The best specs come from running&lt;/em&gt; &lt;em&gt;/grill-me first.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  /learn — Persist Patterns
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/learn
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After a productive chat session, use /learn to capture successful patterns. The agent creates a persistent rule or skill so future sessions (including autonomous runs) benefit from what you discovered interactively. For example, after figuring out your project's error handling pattern through chat, /learn saves it so the agent follows the same pattern in future /goal runs.&lt;/p&gt;

&lt;h3&gt;
  
  
  /browser — Web Automation
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/browser Go to https://ai.google.dev and find the current pricing 
for Gemini 2.5 Flash. Summarize it in a table.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The agent launches a browser, navigates pages, reads content, clicks links, fills forms, and takes screenshots. Useful for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Verifying deployed web apps look correct&lt;/li&gt;
&lt;li&gt;Scraping documentation into structured formats&lt;/li&gt;
&lt;li&gt;Testing user flows end-to-end&lt;/li&gt;
&lt;li&gt;Filling out forms and capturing results&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  /schedule — Recurring Tasks
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/schedule Run the test suite every morning at 9am and report any failures
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Sets up cron-based agent execution. The agent runs autonomously on a schedule without any intervention. Useful for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Health checks on deployed services&lt;/li&gt;
&lt;li&gt;Periodic report generation&lt;/li&gt;
&lt;li&gt;Automated data syncs&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  The Self-Correction Loop: How Autonomous Runs Actually Work
&lt;/h3&gt;

&lt;p&gt;When you use /goal, the agent enters a persistent loop:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fqydnyoszr53kvye08zs8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fqydnyoszr53kvye08zs8.png" width="800" height="1052"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This isn’t just a flowchart, it’s the difference between chat and goal mode.&lt;/p&gt;

&lt;p&gt;Here’s a concrete example:&lt;/p&gt;

&lt;h4&gt;
  
  
  The “Remove Deprecated Feature” Scenario
&lt;/h4&gt;

&lt;p&gt;Imagine you ask the agent to remove a feature. Let’s say, an old notification system that’s been replaced. In &lt;strong&gt;Chat Mode&lt;/strong&gt; , you’d say:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Remove the notification bell feature from the app.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The agent removes the HTML, the JS functions, and the CSS. But let’s say that it &lt;strong&gt;misses&lt;/strong&gt; two stale references:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;initNotifications() is still called in the app's startup() function&lt;/li&gt;
&lt;li&gt;notificationCount is still referenced in the user profile component&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These cause runtime errors. You catch them by testing, report them back, and the agent fixes them one at a time across two more exchanges. Three total interactions to complete what should have been one task.&lt;/p&gt;

&lt;p&gt;In &lt;strong&gt;Goal Mode&lt;/strong&gt; with proper verification:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/goal
Remove the deprecated notification bell feature.

## Requirements
- Remove notification bell HTML from the header
- Remove all notification JS functions
- Remove notification CSS styles
- Remove notification-related state variables

## Verification
- grep -ri 'notification' src/ → zero results (excluding 
  this prompt and git history)
- npm run build succeeds
- No console errors on page load
- App loads normally for both regular and admin users
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The verify step catches the stale references. The fix step removes them. The re-verify step confirms a clean build. The two extra chat exchanges become zero — the loop handles them automatically.&lt;/p&gt;

&lt;h4&gt;
  
  
  What Self-Correction Looks Like
&lt;/h4&gt;

&lt;p&gt;During an autonomous run, the agent:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Runs a command&lt;/strong&gt; → command fails → &lt;strong&gt;reads the error&lt;/strong&gt; → &lt;strong&gt;fixes the code&lt;/strong&gt; →  &lt;strong&gt;retries&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Deploys&lt;/strong&gt; → deployment fails → &lt;strong&gt;checks the error log&lt;/strong&gt; → &lt;strong&gt;fixes the config&lt;/strong&gt; → &lt;strong&gt;redeploys&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Greps for leftover code&lt;/strong&gt; → finds stale references → &lt;strong&gt;removes them&lt;/strong&gt; →  &lt;strong&gt;re-greps&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Runs the test suite&lt;/strong&gt; → 2 tests fail → &lt;strong&gt;reads the assertion errors&lt;/strong&gt; → &lt;strong&gt;fixes the code&lt;/strong&gt; → &lt;strong&gt;re-runs tests&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is the core advantage of autonomous execution —  &lt;strong&gt;it doesn’t just run your code, it runs your tests and fixes its own mistakes&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  When Autonomy Fails: Anti-Patterns to Avoid
&lt;/h3&gt;

&lt;p&gt;Goal Mode isn’t magic. Here are real failure modes and how to avoid them. The suggestions given are with a best intention.&lt;/p&gt;

&lt;h4&gt;
  
  
  Anti-Pattern 1: The Vague Goal
&lt;/h4&gt;

&lt;p&gt;❌ &lt;strong&gt;Bad:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/goal Make the app better
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why it fails:&lt;/strong&gt; “Better” is not a verification criterion. The agent will make changes, but it has no way to know when to stop or whether its changes are improvements.&lt;/p&gt;

&lt;p&gt;✅ &lt;strong&gt;Good:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/goal Add loading spinners to all async buttons. Show a spinner 
icon and "Working..." text while operations run. Disable buttons 
during processing to prevent double-clicks.

## Verification
- All async buttons show spinners during their operations
- Buttons are disabled while processing
- Spinners disappear after completion or error
- No console errors
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Anti-Pattern 2: The Under-Constrained Goal
&lt;/h4&gt;

&lt;p&gt;❌ &lt;strong&gt;Bad:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/goal Add charts to the dashboard
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why it fails:&lt;/strong&gt; Should it use Chart.js, D3, or inline SVG? What data should the charts show? What colors? This forces the agent to make design decisions that you’ll likely want to override.&lt;/p&gt;

&lt;p&gt;✅ &lt;strong&gt;Good:&lt;/strong&gt; Pre-answer every design decision — specify the chart approach (inline SVG, no external libraries), data sources (which collections/arrays), visual style (match existing design system), and include concrete verification criteria.&lt;/p&gt;

&lt;h4&gt;
  
  
  Anti-Pattern 3: The Goal That Needs Discussion
&lt;/h4&gt;

&lt;p&gt;❌ &lt;strong&gt;Bad:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/goal Redesign the entire UI to be more modern
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why it fails:&lt;/strong&gt; “Modern” is subjective. The agent will make choices you disagree with, but since it’s running autonomously, it’ll make &lt;em&gt;all&lt;/em&gt; of them before you see any of them. Use chat or /grill-me first to align on the direction.&lt;/p&gt;

&lt;p&gt;✅ &lt;strong&gt;Better approach:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1. Start with chat: "What would a more modern UI look like for this app?"
2. Discuss options, agree on direction
3. Then use /goal with specific design tokens, colors, and patterns
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Anti-Pattern 4: Missing Verification
&lt;/h4&gt;

&lt;p&gt;❌ &lt;strong&gt;Bad:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/goal 

Remove the notification feature from the app

## Requirements
- Remove notification HTML
- Remove notification JS functions
- Remove notification variables
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why it fails:&lt;/strong&gt; No verification step. The agent removes what you listed, but doesn’t check for stale references, the very type of bug that’s easiest to catch with a simple grep.&lt;/p&gt;

&lt;p&gt;✅ &lt;strong&gt;Good:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;## Verification  
- grep -ri 'notification' src/ → zero results
- npm run build succeeds
- No console errors on page load
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Anti-Pattern 5: The Goal That’s Too Large
&lt;/h4&gt;

&lt;p&gt;❌ &lt;strong&gt;Bad:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/goal 

Build a complete e-commerce platform with user auth,
product catalog, shopping cart, checkout, payment processing, 
order management, admin dashboard, and email notifications
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why it fails:&lt;/strong&gt; Too many interacting components. If the auth system has a subtle bug, it cascades into everything else. The agent can’t isolate failures when 8 subsystems are being built simultaneously.&lt;/p&gt;

&lt;p&gt;✅ &lt;strong&gt;Better:&lt;/strong&gt; Break it into phases:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Phase 1: /goal Add user authentication with Google Sign-In
Phase 2: /goal Add product catalog with Firestore backend
Phase 3: /goal Add shopping cart with real-time sync
Phase 4: /goal Add checkout and payment processing
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each phase can be verified independently before moving to the next. You might want to even use /grill-me before coming up with the /goal at each of the phases.&lt;/p&gt;

&lt;h3&gt;
  
  
  When to Use Which Mode: The Decision Matrix
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F9qfrrrxj279oqrkd92qe.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F9qfrrrxj279oqrkd92qe.png" width="799" height="412"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  The Litmus Test
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;If you can write down every decision the agent would need to make, use&lt;/em&gt; &lt;strong&gt;&lt;em&gt;/goal&lt;/em&gt;&lt;/strong&gt; &lt;em&gt;. If you'd need to see intermediate results before deciding, use _ **_chat&lt;/em&gt;** &lt;em&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  The Hybrid Approach
&lt;/h3&gt;

&lt;p&gt;In practice, the best workflow is often:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fd8kn57licmwvr0b326qw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fd8kn57licmwvr0b326qw.png" width="795" height="73"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You &lt;strong&gt;chat&lt;/strong&gt; to understand your codebase’s auth patterns&lt;/li&gt;
&lt;li&gt;Use &lt;strong&gt;/grill-me&lt;/strong&gt; to surface edge cases you haven't considered ("what about expired tokens? rate limiting? session management?")&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Chat&lt;/strong&gt; a bit more to decide on the approach&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Write the spec&lt;/strong&gt; as a goal prompt, let &lt;strong&gt;/goal&lt;/strong&gt; execute it autonomously while you focus on something else, and then&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Review&lt;/strong&gt; the walkthrough and diff when it's done.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Practical Tips for Better Autonomous Runs
&lt;/h3&gt;

&lt;h4&gt;
  
  
  1. Start with Chat, Graduate to Goal
&lt;/h4&gt;

&lt;p&gt;Your first time building a feature? Use chat to understand the patterns. The second time you build something similar? Write a goal prompt based on what you learned.&lt;/p&gt;

&lt;h4&gt;
  
  
  2. Use /grill-me Before Writing Complex Goals
&lt;/h4&gt;

&lt;p&gt;Not sure if you’ve thought of everything? The agent interviews you with targeted questions to surface decisions you haven’t considered. Then use the answers to write a comprehensive goal prompt.&lt;/p&gt;

&lt;h4&gt;
  
  
  3. Include File Paths in Goal Prompts
&lt;/h4&gt;

&lt;p&gt;Don’t make the agent guess where things are:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;❌ "Update the main app file"
✅ "Update src/App.tsx and src/components/Header.tsx"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  4. Name Your Data Structures
&lt;/h4&gt;

&lt;p&gt;If the agent needs to work with existing code, tell it the variable names:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;❌ "Use the product list"
✅ "Use the productCatalog[] array from src/data/products.ts"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  5. Specify the Deploy Target
&lt;/h4&gt;

&lt;p&gt;If deployment is part of the goal, be explicit:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;❌ "Deploy it"
✅ "Deploy to Firebase Hosting project my-project-id"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  6. Use /learn After a Chat Session
&lt;/h4&gt;

&lt;p&gt;After a productive chat session, /learn persists successful patterns so future goal runs benefit from what you discovered interactively.&lt;/p&gt;

&lt;h4&gt;
  
  
  7. Set Permissions for Autonomous Runs
&lt;/h4&gt;

&lt;p&gt;For autonomous runs, pre-configure permissions so the agent doesn’t block waiting for approval:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Tool Execution Policy&lt;/strong&gt; : always-proceed (the agent runs commands without asking)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Trusted Workspaces&lt;/strong&gt; : Add your project directory so file operations proceed automatically&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;⚠️ Only use&lt;/em&gt; &lt;em&gt;always-proceed in trusted workspaces where you're comfortable with the agent running commands autonomously.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  The Bigger Picture: How Developer Roles Are Shifting
&lt;/h3&gt;

&lt;p&gt;The distinction between chat and autonomous execution isn’t just a UX feature, it reflects a fundamental shift in what it means to “write code.”&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;In the chat era&lt;/strong&gt; , the developer is a &lt;strong&gt;typist-thinker hybrid&lt;/strong&gt;. You think about what to build, type the code, think about what broke, type the fix. Your value is split between knowing what to do and doing it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;In the autonomous era&lt;/strong&gt; , the developer is a &lt;strong&gt;specification writer and reviewer&lt;/strong&gt;. You think about what to build, write a precise spec, and review the output. Your value shifts entirely to &lt;strong&gt;judgment,&lt;/strong&gt; knowing what to build, how to verify it, and how to catch subtle errors in AI-generated code.&lt;/p&gt;

&lt;p&gt;This isn’t a loss. It’s a liberation. The mental model shifts from:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Think → Type → Debug → Type → Test → Type → Deploy → Type
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Think → Specify → Review → Deploy
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The “typing” — the mechanical act of translating intent into code — is the part that gets automated. The thinking, specifying, and reviewing are the parts that remain uniquely human. And those are, arguably, the most intellectually rewarding parts. I hope we have consensus there though some would like to debate what is actually rewarding to them and I will respect that.&lt;/p&gt;

&lt;h3&gt;
  
  
  Summary
&lt;/h3&gt;

&lt;p&gt;The transition from chat to autonomous execution isn’t about trust — it’s about &lt;strong&gt;specification clarity&lt;/strong&gt;. When you can write down exactly what you want, including constraints and verification criteria, you’ve written a goal prompt. When you can’t yet, chat your way there first.&lt;/p&gt;

&lt;p&gt;Start small. Try a /goal on your next well-understood task. See what happens. Then gradually increase the scope as you build confidence in your prompt engineering.&lt;/p&gt;

&lt;p&gt;The dial goes from “fix this typo” to “build this entire feature.” Your job is to figure out, for each task, where to set it.&lt;/p&gt;

&lt;p&gt;A quick table summary to end this article:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fn5nsm17a18e84j57r9i1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fn5nsm17a18e84j57r9i1.png" width="800" height="302"&gt;&lt;/a&gt;&lt;/p&gt;




</description>
      <category>specdrivendevelopmen</category>
      <category>googleantigravity</category>
      <category>autonomouscodingagen</category>
    </item>
    <item>
      <title>The future of autonomous software maintenance: a dependency update agent</title>
      <dc:creator>Karl Weinmeister</dc:creator>
      <pubDate>Thu, 09 Jul 2026 21:14:10 +0000</pubDate>
      <link>https://dev.to/googleai/the-future-of-autonomous-software-maintenance-a-dependency-update-agent-1pk4</link>
      <guid>https://dev.to/googleai/the-future-of-autonomous-software-maintenance-a-dependency-update-agent-1pk4</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fek0nypuf1qigljuyvarm.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fek0nypuf1qigljuyvarm.jpeg" width="800" height="447"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Most AI tools focus on writing new code, but &lt;a href="https://www.researchgate.net/publication/243503924_Facts_and_Fallacies_of_Software_Engineering" rel="noopener noreferrer"&gt;up to 80%&lt;/a&gt; of the cost of software comes from maintenance. What if an intelligent agent could handle that burden for you?&lt;/p&gt;

&lt;p&gt;In this article, I’ll walk through an autonomous agent called &lt;a href="https://github.com/kweinmeister/dependency-director" rel="noopener noreferrer"&gt;dependency-director&lt;/a&gt; that handles one of the most common maintenance scenarios: dependency updates. It goes beyond merging dependency updates that are passing tests; it upgrades your code to match changes in the dependency. This provides a sneak peek into the future of automated engineering workflows.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F0%2AkUITR-z_lNlTzWDK" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F0%2AkUITR-z_lNlTzWDK" width="1024" height="547"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Note: dependency-director is an experimental proof of concept and is not intended for production environments. Although it uses security guardrails like sandboxing and command filters, running LLM-generated code dynamically always involves security trade-offs. If you experiment with the code on GitHub, please do so in an isolated testing environment, start with the &lt;code&gt;--dry-run&lt;/code&gt; flag, and use highly restricted API tokens.&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Loop engineering for software updates
&lt;/h3&gt;

&lt;p&gt;A continuous triage loop is at the center of the process. The agent scans repositories and maps every open pull request to a state and triggers an action.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F0%2AOd_8ROeFstum8dzW" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F0%2AOd_8ROeFstum8dzW" width="1024" height="913"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When a dependency update breaks a test suite, the agent automatically clones the repository and updates the code to align with the updated dependency.&lt;/p&gt;

&lt;p&gt;Dependency Director runs on the &lt;a href="https://antigravity.google?utm_campaign=CDR_0x2b6f3004_default_b533079536&amp;amp;utm_medium=external&amp;amp;utm_source=blog" rel="noopener noreferrer"&gt;Google Antigravity SDK&lt;/a&gt;, with &lt;a href="https://ai.google.dev/gemini-api/docs?utm_campaign=CDR_0x2b6f3004_default_b533079536&amp;amp;utm_medium=external&amp;amp;utm_source=blog" rel="noopener noreferrer"&gt;Gemini&lt;/a&gt; taking care of the reasoning. You can configure the agent like this (full version in &lt;a href="https://github.com/kweinmeister/dependency-director/blob/main/src/dependency_director/main.py" rel="noopener noreferrer"&gt;main.py&lt;/a&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;LocalAgentConfig&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;gemini-3.5-flash&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;system_instructions&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;system_instructions&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;policies&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;policies&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;tools&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;agent_tools&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;# 12 GitHub API tools, plus a sandboxed shell
&lt;/span&gt;    &lt;span class="n"&gt;workspaces&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;project_root&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;workspace_tmp&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="n"&gt;capabilities&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;types&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;CapabilitiesConfig&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;enable_subagents&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;disabled_tools&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;types&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;BuiltinTools&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RUN_COMMAND&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two lines do most of the security work. &lt;code&gt;disabled_tools=[types.BuiltinTools.RUN_COMMAND]&lt;/code&gt; turns off the SDK’s built-in shell, so the only way the agent can run a command is through my sandboxed wrapper. And &lt;code&gt;workspaces&lt;/code&gt; pins file operations to the project directory and a per-repo temp workspace, so the agent can’t wander through the filesystem.&lt;/p&gt;

&lt;h3&gt;
  
  
  Guardrails live in the tools, not the prompt
&lt;/h3&gt;

&lt;p&gt;A common mistake in agent design is writing rules into the system prompt and hoping the model follows them. All important constraints are enforced in the tool functions themselves.&lt;/p&gt;

&lt;p&gt;For example, the author of the pull request must be one of the allowed bots. The defaults are &lt;code&gt;dependabot[bot]&lt;/code&gt; and &lt;code&gt;renovate[bot]&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;_check_bot_author&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;author&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;bots&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;BotConfig&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;BotConfig&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;allowed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;author&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;bots&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;author&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;allowed&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;PermissionError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Security Block: Only pull requests authored by &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;allowed&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
            &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;can be processed. This PR was authored by &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;author&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;next&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;bots&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;author&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;author&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There’s also a &lt;code&gt;--dry-run&lt;/code&gt; parameter, so that you can simulate the behavior locally, without updating the pull request.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;merge_bot_pr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;owner&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;repo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pr_number&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;author&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_pr_author&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;owner&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;repo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pr_number&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;_check_bot_author&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;author&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;bots&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;dry_run&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;[DRY-RUN] Would have merged PR #&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;pr_number&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; in &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;owner&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;repo&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;All fixes are held up as an open PR for your review by default, unless you use &lt;code&gt;--automerge&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Patching a red pull request
&lt;/h3&gt;

&lt;p&gt;When a PR has a conflict, the agent follows a procedure in its system instructions located in &lt;a href="https://github.com/kweinmeister/dependency-director/blob/main/src/dependency_director/instructions.py" rel="noopener noreferrer"&gt;instructions.py&lt;/a&gt;. It uses the &lt;a href="https://github.com/addyosmani/agent-skills/blob/main/skills/code-review-and-quality/SKILL.md" rel="noopener noreferrer"&gt;code-review-and-quality&lt;/a&gt; skill from &lt;a href="https://x.com/addyosmani" rel="noopener noreferrer"&gt;Addy Osmani&lt;/a&gt; to go beyond just making the test pass, and ensure a code review and quality standards are enforced:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;RED: Retrieve logs via 'get_pr_workflow_run_logs'. Clone into a subdirectory,
then check out the PR branch:
  git fetch origin pull/&amp;lt;pr_number&amp;gt;/head:pr-&amp;lt;pr_number&amp;gt; &amp;amp;&amp;amp; git checkout pr-&amp;lt;pr_number&amp;gt;
Otherwise install deps, test, fix, verify, and push to the remote branch.
Max 3 fix attempts per RED PR before skipping.
Run 'code-review-and-quality' self-review before committing.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The agent is also very clear about what counts as a fix. It’s told to consider what’s changed in the API documentation, and not just chase a green checkmark:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;- Fix root causes (types, signatures, API changes) using changelogs.
- NEVER suppress errors (type: ignore, noqa, Any) unless upstream bugs
  leave no alternative (require comment).
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Running untrusted code in a sandbox
&lt;/h3&gt;

&lt;p&gt;To restrict filesystem and network operations, Dependency Director uses &lt;a href="https://github.com/anthropic-experimental/sandbox-runtime" rel="noopener noreferrer"&gt;sandbox-runtime&lt;/a&gt;. It’s an open-source OS-level sandbox. Every shell command the agent runs is wrapped in it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;process&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;asyncio&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create_subprocess_exec&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;srt&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;--settings&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;active_config_path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-c&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;command_line&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;cwd&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;target_cwd&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;stdout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;subprocess&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PIPE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;stderr&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;subprocess&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PIPE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can configure the settings file where the sandbox is configured. The default settings allow network access to package registry domains such as PyPI, npm, crates.io. The agent is blocked from reading the home directory and writing to the .env environment variable file and .git/hooks. Here’s the bundled &lt;a href="https://github.com/kweinmeister/dependency-director/blob/main/src/dependency_director/srt-settings.json" rel="noopener noreferrer"&gt;srt-settings.json&lt;/a&gt;, trimmed to a few representative entries:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"network"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"allowedDomains"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"github.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"pypi.org"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"registry.npmjs.org"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"deniedDomains"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"*.ngrok.io"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"*.webhook.site"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"*.requestbin.com"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"filesystem"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"denyRead"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"~"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"allowWrite"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"/tmp"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"denyWrite"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;".env"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;".git/hooks"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"allowGitConfig"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Trying it out yourself
&lt;/h3&gt;

&lt;p&gt;Solid test coverage is essential for updating dependencies. Tests give you the confidence to know that your code still functions. When you use Dependency Director, they’re absolutely required so that the agent can safely patch your code to work with the updated dependencies. For tips on effective tests, see my blog post on &lt;a href="https://medium.com/google-cloud/five-essential-testing-patterns-for-ai-agent-development-002a746378ce" rel="noopener noreferrer"&gt;five essential testing patterns&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Using an AI agent does consume tokens, of course. I performed a trial run on a repo with 6 open pull requests using gemini-3.5-flash. It used about 200k tokens. At the current &lt;a href="https://ai.google.dev/gemini-api/docs/pricing?utm_campaign=CDR_0x2b6f3004_default_b533079536&amp;amp;utm_medium=external&amp;amp;utm_source=blog#gemini-3.5-flash" rel="noopener noreferrer"&gt;published rate&lt;/a&gt;, that’s about $0.30 USD for the run, or roughly 6 cents per PR.&lt;/p&gt;

&lt;p&gt;The code to try out dependency-director is on &lt;a href="https://github.com/kweinmeister/dependency-director" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt; for you to try. Start with &lt;code&gt;--dry-run&lt;/code&gt; to see how it works for you!&lt;/p&gt;

&lt;p&gt;What maintenance work would you hand to an agent first? Tell me on &lt;a href="https://x.com/kweinmeister" rel="noopener noreferrer"&gt;X&lt;/a&gt;, &lt;a href="https://www.linkedin.com/in/karlweinmeister/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;, or &lt;a href="https://bsky.app/profile/kweinmeister.bsky.social" rel="noopener noreferrer"&gt;Bluesky&lt;/a&gt;.&lt;/p&gt;




</description>
      <category>agents</category>
      <category>googlegemini</category>
      <category>dependencymanagement</category>
      <category>googleantigravity</category>
    </item>
    <item>
      <title>Agent Factory Recap: 100X engineering with AI agents in Google Antigravity 2.0</title>
      <dc:creator>Shir Meir Lador</dc:creator>
      <pubDate>Tue, 07 Jul 2026 17:28:25 +0000</pubDate>
      <link>https://dev.to/googleai/agent-factory-recap-100x-engineering-with-ai-agents-in-google-antigravity-20-4p4j</link>
      <guid>https://dev.to/googleai/agent-factory-recap-100x-engineering-with-ai-agents-in-google-antigravity-20-4p4j</guid>
      <description>&lt;p&gt;In this episode of the Agent Factory, I sat down with Rody Davis, one of Google's top agentic engineers. We dive into the massive shift from traditional IDEs to agent-first platforms, the reality of code reviews in an AI-driven world, and how to use "skills" to perform at a 100X level.&lt;/p&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/Dk4MD6TNiWE"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;This post guides you through the key ideas from our conversation. Use it to quickly recap topics or dive deeper into specific segments with links and timestamps.&lt;/p&gt;

&lt;h2&gt;
  
  
  Google Antigravity 2.0 - What is it?
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://antigravity.google/" rel="noopener noreferrer"&gt;Antigravity 2.0&lt;/a&gt; has evolved from a simple agentic IDE into a full-scale agent-first platform. It now consists of four core pillars: a standalone desktop &lt;strong&gt;Agent Manager&lt;/strong&gt; for orchestration, a robust &lt;strong&gt;CLI&lt;/strong&gt; for server-side work, an &lt;strong&gt;SDK&lt;/strong&gt; for custom Python-based workflows, and a specialized &lt;strong&gt;IDE&lt;/strong&gt;. This unbundled approach allows developers to compose their own environment, managing multiple folders and complex project structures without being forced into a single-workspace layout.&lt;/p&gt;

&lt;h2&gt;
  
  
  Rody Davis on 100X Engineering
&lt;/h2&gt;

&lt;p&gt;We explored the strategies elite engineers use to scale their impact and reduce the "cognitive toil" of daily development.&lt;/p&gt;

&lt;h3&gt;
  
  
  Scaling Impact and Reducing Toil
&lt;/h3&gt;

&lt;p&gt;Timestamp: &lt;a href="https://www.youtube.com/watch?v=Dk4MD6TNiWE&amp;amp;t=115s" rel="noopener noreferrer"&gt;01:55&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Rody explains that AI isn't just about writing code; it's about accelerating the entire lifecycle. He uses agents to write richer test suites and prototype multiple versions of an app before committing to a framework. By offloading "toil", like building marketing sites, he can focus on high-level architecture and problem-solving.&lt;/p&gt;

&lt;h3&gt;
  
  
  Skills as "Context Cheat Sheets"
&lt;/h3&gt;

&lt;p&gt;Timestamp: &lt;a href="https://www.youtube.com/watch?v=Dk4MD6TNiWE&amp;amp;t=185s" rel="noopener noreferrer"&gt;03:05&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A core philosophy in Rody's workflow is the use of "Skills." He views skills as a way to compress context for the model. "It's literally a cheat sheet for the agent," Rody notes. By providing the agent with specific design systems or API documentation, the model becomes significantly faster and more accurate, avoiding the latency of searching through massive, unorganized docs.&lt;/p&gt;

&lt;h3&gt;
  
  
  Customizations, Skills, and MCP Servers
&lt;/h3&gt;

&lt;p&gt;Timestamp: &lt;a href="https://www.youtube.com/watch?v=Dk4MD6TNiWE&amp;amp;list=PLIivdWyY5sqLXR1eSkiM5bE6pFlXC-OSs&amp;amp;index=1&amp;amp;t=257s" rel="noopener noreferrer"&gt;04:17&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fc8fwr93yc18wdpos5fy7.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fc8fwr93yc18wdpos5fy7.jpg" alt="A list of agent skills in the Antigravity 2.0 customizations tab." width="800" height="656"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Rody walks us through the customizations tab in Antigravity 2.0, showing how to extend an agent's capabilities:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Android CLI:&lt;/strong&gt; Building and deploying mobile apps directly from the command line.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Modern Web Guidance:&lt;/strong&gt; Grounding the agent in the latest CSS and accessibility standards.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;MCP Servers:&lt;/strong&gt; Using the Model Context Protocol to enable features like hot reloading for Flutter and Dart.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  The Bonsai Approach to Code Review
&lt;/h3&gt;

&lt;p&gt;Timestamp: &lt;a href="https://www.youtube.com/watch?v=Dk4MD6TNiWE&amp;amp;t=327s" rel="noopener noreferrer"&gt;05:27&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Rody compares maintaining a codebase to being a Bonsai artist: constantly pruning to keep things simple. He advocates for flat architectures where state, UI, and data are strictly separated. This makes it easier for a human to "steer" the agent; if the agent starts putting files in the wrong place, the architectural violation is immediately obvious.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fw7t9wjvxfxb7a2my9zh3.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fw7t9wjvxfxb7a2my9zh3.jpg" alt="Infographic titled " width="799" height="436"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Do you review 100% of agent-generated code?
&lt;/h3&gt;

&lt;p&gt;Timestamp: &lt;a href="https://www.youtube.com/watch?v=Dk4MD6TNiWE&amp;amp;t=431s" rel="noopener noreferrer"&gt;07:11&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Rody's answer depends on the task. For a marketing site, he focuses on the visual output rather than the code. However, for backend logic, he cares deeply about API contracts and schemas. He recommends writing the first example yourself so the agent can simply "copy the pattern" for the rest of the codebase.&lt;/p&gt;

&lt;h3&gt;
  
  
  Building Extensions to Solve Daily Friction
&lt;/h3&gt;

&lt;p&gt;Timestamp: &lt;a href="https://www.youtube.com/watch?v=Dk4MD6TNiWE&amp;amp;t=545s" rel="noopener noreferrer"&gt;09:05&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To solve the problem of managing files across multiple Git projects, Rody used Antigravity to build a custom macOS Finder extension in Swift. This tool allows him to filter files by time boxes (today, last week, etc.), demonstrating how agents can build specialized utilities that reduce daily friction.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fie080486a0vqcb65ivfe.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fie080486a0vqcb65ivfe.jpg" alt="Screenshot of an Antigravity IDE session titled " width="800" height="738"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Do AI engineers still write code by hand?
&lt;/h3&gt;

&lt;p&gt;Timestamp: &lt;a href="https://www.youtube.com/watch?v=Dk4MD6TNiWE&amp;amp;t=622s" rel="noopener noreferrer"&gt;10:22&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;"Oh yeah," Rody says. He still loves the syntax of languages like Go and the challenge of controlling computers. He believes it's vital to understand the building blocks deeply so that when you face a problem two years down the road, you know exactly which "old project" to reach back for.&lt;/p&gt;

&lt;h3&gt;
  
  
  Powering Personal Websites with Gemma 4
&lt;/h3&gt;

&lt;p&gt;Timestamp: &lt;a href="https://www.youtube.com/watch?v=Dk4MD6TNiWE&amp;amp;t=702s" rel="noopener noreferrer"&gt;11:42&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Rody showcases his personal website, which uses &lt;a href="https://cloud.google.com/blog/topics/developers-practitioners/agent-factory-recap-how-gemma-4-taught-itself-physics?e=48754805" rel="noopener noreferrer"&gt;Gemma 4&lt;/a&gt; and Embedding Gemma to provide dynamic content recommendations offline. By vectorizing post summaries at compile time, the site can suggest related content via a local vector database without needing a live backend server.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fjzfjovtxrnre9jv5yi93.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fjzfjovtxrnre9jv5yi93.jpg" alt="Screenshot of Rody Davis's personal website rendered in an editor-style layout, showing three dynamic focus-area cards raw `" width="800" height="647"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The Factory Floor
&lt;/h2&gt;

&lt;p&gt;The Factory Floor is our segment for getting hands-on. Here, we moved from high-level concepts to practical code with live demos.&lt;/p&gt;

&lt;h3&gt;
  
  
  Multi-Agent Parallelism in Action
&lt;/h3&gt;

&lt;p&gt;Timestamp: &lt;a href="https://www.youtube.com/watch?v=Dk4MD6TNiWE&amp;amp;t=842s" rel="noopener noreferrer"&gt;14:02&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In this demo, Rody uses a single stream-of-thought voice prompt to build a full-stack application. We watched as Antigravity:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Spun up parallel sub-agents, including a dedicated DevOps and QA engineer. (see &lt;a href="https://www.youtube.com/watch?v=Dk4MD6TNiWE&amp;amp;t=1188s" rel="noopener noreferrer"&gt;19:48&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;Built a multilingual note-taking app using Vite, Go, and SQLite.&lt;/li&gt;
&lt;li&gt;Orchestrated the entire stack via Docker Compose.&lt;/li&gt;
&lt;li&gt;Localized the app into five different languages simultaneously.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fsmpmze8mm4cpi8iizdqw.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fsmpmze8mm4cpi8iizdqw.jpg" alt="` endraw Screenshot of the Antigravity Agent Manager during a " width="800" height="650"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Unbundling the IDE Ecosystem
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fccuo918brl3fmr0c51bg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fccuo918brl3fmr0c51bg.png" alt="" width="800" height="231"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Timestamp: &lt;a href="https://www.youtube.com/watch?v=Dk4MD6TNiWE&amp;amp;t=935s" rel="noopener noreferrer"&gt;15:35&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We discussed why Google separated the IDE from the Agent Manager. Rody highlights that this unlocks different workflows: the CLI is perfect for SSH sessions on a Raspberry Pi, while the Agent Manager handles general knowledge work and orchestration across multiple folders.&lt;/p&gt;

&lt;h3&gt;
  
  
  Turning Documentation into Reusable Skills
&lt;/h3&gt;

&lt;p&gt;Timestamp: &lt;a href="https://www.youtube.com/watch?v=Dk4MD6TNiWE&amp;amp;t=1541s" rel="noopener noreferrer"&gt;25:41&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Rody shares his process for turning documentation into skills. He wrote a Go CLI that parses websites into markdown, allowing him to install hundreds of skills for the sites he visits frequently. This ensures the agent always has access to the specific version of the docs he is using.&lt;/p&gt;

&lt;h3&gt;
  
  
  Rapid Fire: Future Tech Predictions
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fc4ehwam7n46npmx1qxsz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fc4ehwam7n46npmx1qxsz.png" alt="Split-screen video call screenshot of two podcast hosts, one clean-shaven with short dark hair on the left and the other smiling with long curly blonde hair on the right. An on-screen caption reads " width="800" height="507"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Timestamp: &lt;a href="https://www.youtube.com/watch?v=Dk4MD6TNiWE&amp;amp;t=1655s" rel="noopener noreferrer"&gt;27:35&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We put Rody on the spot with some controversial takes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Vibe Coding:&lt;/strong&gt; Rody believes a non-technical founder will launch a company using only vibe coding by 2026, but the real test will be maintaining it in years 2 through 5.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Production Failures:&lt;/strong&gt; Rody agrees that vibe coding will cause significant production failures, leading to a new hot job for software engineers: consulting to solve those failures.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Codebase Health:&lt;/strong&gt; Rody argues that poor codebase health, not context windows, is the biggest bottleneck in AI speed.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Grounding Yourself in a Changing Landscape
&lt;/h3&gt;

&lt;p&gt;Timestamp: &lt;a href="https://www.youtube.com/watch?v=Dk4MD6TNiWE&amp;amp;t=1870s" rel="noopener noreferrer"&gt;31:10&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Rody advises engineers to focus on why they were hired: to solve problems and engineer things that didn't exist before. He suggests using AI to provide better communication handoffs between colleagues, making artifacts so easy to approve that they are "ready to sign off" the moment they are handed over.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;The era of agentic engineering is here, but as Rody Davis demonstrated, it requires more architectural discipline, not less. By treating your codebase like a Bonsai tree and your agents like an orchestra, you can move past the "toil" and focus on building the frameworks of the future.&lt;/p&gt;

&lt;h2&gt;
  
  
  Your turn to build
&lt;/h2&gt;

&lt;p&gt;Are you ready to build anything? We've officially launched the &lt;strong&gt;#NapkinChallenge&lt;/strong&gt;. Take a handwritten sketch of an app idea, use Antigravity 2.0 to build it, and share your creation on social media.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Try Antigravity 2.0:&lt;/strong&gt; &lt;a href="https://goo.gle/4fnXilj" rel="noopener noreferrer"&gt;antigravity.google&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Join the Challenge:&lt;/strong&gt; &lt;a href="https://goo.gle/4e0AGF6" rel="noopener noreferrer"&gt;Napkin Challenge Details&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rody's personal&lt;/strong&gt; &lt;a href="https://rodydavis.com/" rel="noopener noreferrer"&gt;&lt;strong&gt;website&lt;/strong&gt;&lt;/a&gt;, &lt;a href="https://github.com/rodydavis/rodydavis" rel="noopener noreferrer"&gt;&lt;strong&gt;github repo&lt;/strong&gt;&lt;/a&gt; and &lt;a href="https://github.com/rodydavis/skills" rel="noopener noreferrer"&gt;skills&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Connect with us
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Rody Davis&lt;/strong&gt; → &lt;a href="https://goo.gle/Rody-on-X" rel="noopener noreferrer"&gt;X&lt;/a&gt;, &lt;a href="https://goo.gle/Rody-on-LinkedIn" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Shir Meir Lador&lt;/strong&gt; → &lt;a href="https://goo.gle/Shir-on-X" rel="noopener noreferrer"&gt;X&lt;/a&gt;, &lt;a href="https://goo.gle/Shir-on-LinkedIn" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>agents</category>
      <category>ai</category>
    </item>
    <item>
      <title>Master Local Fine-Tuning with "gemma-trainer"</title>
      <dc:creator>bebechien</dc:creator>
      <pubDate>Tue, 07 Jul 2026 01:44:15 +0000</pubDate>
      <link>https://dev.to/googleai/master-local-fine-tuning-with-gemma-trainer-3ipp</link>
      <guid>https://dev.to/googleai/master-local-fine-tuning-with-gemma-trainer-3ipp</guid>
      <description>&lt;p&gt;Remember back in May when I introduced the &lt;a href="https://github.com/google-gemma/gemma-skills" rel="noopener noreferrer"&gt;&lt;code&gt;gemma-skills&lt;/code&gt;&lt;/a&gt; repository? It's been rewarding to see how many of you have used &lt;a href="https://dev.to/googleai/a-warm-welcome-to-gemma-skills-4466"&gt;my previous post&lt;/a&gt; to streamline your workflows. (And hey, even if we aren't swimming in GitHub stars yet, I think we're off to a great start!😉)&lt;/p&gt;

&lt;p&gt;But as I built more custom applications, I kept hitting the same roadblock: &lt;strong&gt;how to take a great base model and adapt it to my specific needs.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Fine-tuning a model usually requires wading through complex setups and confusing guides. To make this process straightforward and quick, we created our newest skill: &lt;code&gt;gemma-trainer&lt;/code&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  What is &lt;code&gt;gemma-trainer&lt;/code&gt;?
&lt;/h1&gt;

&lt;p&gt;&lt;code&gt;gemma-trainer&lt;/code&gt; is your blueprint for training and adapting Gemma models on your local hardware. It handles the "how-to" so you can focus on your specific project goals, whether you are teaching a model a new domain or aligning its behavior to your preferences.&lt;/p&gt;

&lt;h1&gt;
  
  
  Why You'll Use It
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Faster, Lighter Training&lt;/strong&gt;: We recommend using &lt;strong&gt;&lt;a href="http://unsloth.ai/" rel="noopener noreferrer"&gt;Unsloth&lt;/a&gt;&lt;/strong&gt; for single-GPU training, making it fast and using less memory so it runs easily on personal hardware.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Three Key Methods&lt;/strong&gt;: It guides you through Supervised Fine-Tuning (SFT) to teach new info, Direct Preference Optimization (DPO) to align with preferences, and Reward Modeling (RM) to rate responses.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Teach Models to See and Hear&lt;/strong&gt;: It includes clear instructions for training models with images and audio (multimodal learning) alongside text.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Run Anywhere&lt;/strong&gt;: Quickly convert your models to lightweight formats (like GGUF) and run them on mobile or smart devices (IoT) using &lt;strong&gt;&lt;a href="https://developers.google.com/edge/litert-lm/models/gemma-4" rel="noopener noreferrer"&gt;LiteRT-LM&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Up-to-Date Best Practices&lt;/strong&gt;: The skill is continuously updated with the latest optimized settings and training techniques, ensuring you're always using the best methods.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Practical Use Case
&lt;/h1&gt;

&lt;p&gt;To see this in action, recall how we turned Gemma 4 into an expert translator for Classical Korean literature in &lt;a href="https://dev.to/googleai/turning-gemma-4-into-an-old-korean-translator-hop"&gt;my previous post&lt;/a&gt;. With &lt;code&gt;gemma-trainer&lt;/code&gt;, you don't need to manually piece together a pipeline. You can simply ask your agent:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;"Fine-tune Gemma 4 E2B on the dataset bebechien/HongGildongJeon."&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;With the &lt;code&gt;gemma-trainer&lt;/code&gt; skill, your agent will partner with you to:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Verify your data&lt;/strong&gt;: Use the validation script to ensure your training data matches template requirements.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Set up parameters&lt;/strong&gt;: Select the best LoRA settings to teach the model linguistic nuances without running out of video memory (VRAM).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Run the training&lt;/strong&gt;: Launch the training session using optimized, resource-efficient defaults.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Evaluate and iterate&lt;/strong&gt;: Review the model's performance and adjust settings to get the exact results you need.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Here is an example showing the agent starting a fine-tuning run on a Gemma 4 12B model for audio tasks:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F64x4o59iwo59z0ebqwt9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F64x4o59iwo59z0ebqwt9.png" alt="audio-tuning start" width="800" height="331"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once configured, the agent kicks off the training process using your designated dataset:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fg63rixy56tuq8jwccfl3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fg63rixy56tuq8jwccfl3.png" alt="audio-tuning training" width="800" height="331"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Even if you make a mistake, the agent has your back. For instance, when I accidentally requested training a Gemma 4 31B model (which is a text-and-vision model and has no audio capability), it suggested using Gemma 4 E2B or 12B for audio tuning instead:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fbz88cd28ixbrvmn8ukgr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fbz88cd28ixbrvmn8ukgr.png" alt="audio-tuning fix" width="800" height="251"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once training is complete, the agent presents the results and outlines the next steps:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fuahbr5bcax7x7k3sjxzg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fuahbr5bcax7x7k3sjxzg.png" alt="audio-tuning finish" width="800" height="331"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can also ask your agent to write a custom evaluation script based on your specific requirements. In this case, I asked the agent to create a script that checks transcription similarity:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fwvzx3eaiv16adfr9ox32.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fwvzx3eaiv16adfr9ox32.png" alt="audio-tuning eval" width="800" height="785"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Finally, you will receive a comprehensive report summarizing the training performance, making it clear where you can make improvements in the next run:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fexcuwsosep197wy61coz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fexcuwsosep197wy61coz.png" alt="audio-tuning report" width="800" height="368"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Let's try!
&lt;/h1&gt;

&lt;p&gt;&lt;code&gt;gemma-trainer&lt;/code&gt; is a living, structured document. Drop it into your agent's skills directory, and your AI assistant will immediately know how to guide you through the process.&lt;/p&gt;

&lt;p&gt;Check out the &lt;a href="https://github.com/google-gemma/gemma-skills" rel="noopener noreferrer"&gt;repository&lt;/a&gt;, add the skill to your toolbox, and let's build something amazing!&lt;/p&gt;

&lt;p&gt;Thanks for reading and happy training!&lt;/p&gt;

</description>
      <category>gemma</category>
      <category>finetuning</category>
      <category>ai</category>
      <category>agents</category>
    </item>
  </channel>
</rss>
