<?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: Sireesha Pulipati</title>
    <description>The latest articles on DEV Community by Sireesha Pulipati (@sireesha_pulipati_842f9f1).</description>
    <link>https://dev.to/sireesha_pulipati_842f9f1</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%2Fuser%2Fprofile_image%2F3700959%2Fc5a26c43-fde4-4168-815c-5c770abb0d01.png</url>
      <title>DEV Community: Sireesha Pulipati</title>
      <link>https://dev.to/sireesha_pulipati_842f9f1</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/sireesha_pulipati_842f9f1"/>
    <language>en</language>
    <item>
      <title>Vibecoding EventMatch, built with Antigravity, ADK, and Gemini</title>
      <dc:creator>Sireesha Pulipati</dc:creator>
      <pubDate>Thu, 13 Aug 2026 04:18:51 +0000</pubDate>
      <link>https://dev.to/gde/vibecoding-eventmatch-built-with-antigravity-adk-and-gemini-2gc7</link>
      <guid>https://dev.to/gde/vibecoding-eventmatch-built-with-antigravity-adk-and-gemini-2gc7</guid>
      <description>&lt;p&gt;Every week I get invited to more AI events than I can attend. Bond AI, Founders Bay, AI Collective, half a dozen other newsletters, each one dropping a handful of events into my inbox with no way to tell, at a glance, which ones I’m actually free for. Public aggregators like Luma and Eventbrite don’t help either. The interesting events, the ones from niche community newsletters, never show up there in the first place. If you live in a city like New York or San Francisco, this is a familiar kind of overload, too many good options, scattered across too many places, with no single view of what actually fits your week.&lt;/p&gt;

&lt;p&gt;So I built EventMatch. And I vibecoded it, using Antigravity, Gemini 3.5 Flash, and Google’s Agent Development Kit.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/sireeshapulipati/event-match" rel="noopener noreferrer"&gt;Code&lt;/a&gt; · &lt;a href="https://www.kaggle.com/competitions/vibecoding-agents-capstone-project/writeups/eventmatch-fight-your-event-fomo" rel="noopener noreferrer"&gt;Kaggle writeup&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here’s the walkthrough, what I wanted the app to do, how the agents are put together, and what building it actually taught me.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I wanted the app to do
&lt;/h2&gt;

&lt;p&gt;The core ask was simple to state and harder to deliver. Pull events from public platforms and from the private newsletters sitting in my inbox, check every one of them against my real calendar, and only show me what’s actually conflict free.&lt;/p&gt;

&lt;p&gt;A few things went beyond that baseline. Preferences needed to be conversational, not a set of rigid tags, something like “a mix of tech networking, AI workshops, creative art showcases, and relaxed social gatherings,” not a checkbox list. And those preferences needed to stay stable. If I ask a one off question like “show me free events this weekend,” that shouldn’t silently overwrite my standing preferences. Only an explicit request to update them should.&lt;/p&gt;

&lt;p&gt;The rest was polish that ended up counting for more than I expected. Price shown as a tier, free, , $ , $$$, instead of raw text pulled from the source. One register button per event, not two competing ones in the same modal.&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%2Foattizkc1w4z3h3nu4z4.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%2Foattizkc1w4z3h3nu4z4.png" alt="Event Match UI with top 10 events matching your vibes on the right and a concierge agent on the left" width="800" height="559"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The multi agent architecture
&lt;/h2&gt;

&lt;p&gt;EventMatch runs on ADK 2.0, and the workflow alternates between plain Python nodes and LLM agents. That alternation is the whole design.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;prepare_extractor (Python)&lt;/strong&gt; sanitizes the incoming message and reads the current session state.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;preference_extractor (LlmAgent)&lt;/strong&gt; parses the user’s message to pull out location, and decides whether this is a persistent preference change or a one off search.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;filter_events_node (Python)&lt;/strong&gt; pulls public data from Luma and Eventbrite, triggers background fetches from Gmail and Google Calendar, and merges everything. This is where the actual grounding happens. It runs a millisecond level time overlap check against the live calendar and flags conflicts before anything reaches the model, and converts events to plain text descriptions to keep the token count down for the next step.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;vibe_ranker (LlmAgent)&lt;/strong&gt; takes the conflict free events and scores them against the user’s preferences, 0 to 100, with a short explanation for each one.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;formatter_node (Python)&lt;/strong&gt; closes the loop, and it’s the piece that holds the whole system together. The vibe_ranker only ever sees plain text descriptions and returns ranked IDs. formatter_node takes those IDs and re-joins them against the original database records. Registration URLs, host names, event locations, none of it passes through the model twice.&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%2Ffkn6daetnrrw7a39iq0k.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%2Ffkn6daetnrrw7a39iq0k.png" alt="Deterministic code and probabilistic llm nodes" width="800" height="103"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Python does the things Python is reliable at, filtering, matching, deduplicating. The LLM does the one thing it’s actually needed for, judging fit against a vague preference. An LLM asked to both evaluate and format a result will, often enough, paraphrase a URL wrong or invent a detail that sounds plausible. Keeping those two jobs separate removes that failure mode instead of trying to prompt it away.&lt;/p&gt;

&lt;h2&gt;
  
  
  Integrations
&lt;/h2&gt;

&lt;p&gt;Underneath the agents is a fairly ordinary web app, FastAPI on the backend, Python 3.11, React on the frontend, built with Vite and Tailwind.&lt;/p&gt;

&lt;p&gt;The integration that makes everything else possible is OAuth. EventMatch requests read only scopes for Gmail and Calendar through Google’s official client libraries. Once authorized, the backend caches the credentials and the UI shows the connected account in the navbar. Everything downstream, the newsletter parsing, the conflict checking, only works because that connection is live.&lt;/p&gt;

&lt;p&gt;Public event data comes from Luma and Eventbrite, which meant working around scraping restrictions rather than clean APIs. Deployment is Docker on Cloud Run, provisioned with Terraform.&lt;/p&gt;

&lt;h2&gt;
  
  
  Building it, and what it taught me
&lt;/h2&gt;

&lt;p&gt;I built this in Antigravity, following a spec driven process rather than jumping straight into code. Start with a product requirements document. Turn that into a high level design. Turn the design into a spec detailed enough for an agent to build from. Only then does actual development start.&lt;/p&gt;

&lt;p&gt;Coding a working app over a weekend is genuinely exciting. It’s also where that process got tested, and where it broke down. The pattern showed up early and kept showing up. Fix one bug, move to the next, fix that one, and the first bug resurfaces. Back and forth, the same category of issue reappearing in a slightly different shape each time.&lt;/p&gt;

&lt;p&gt;The spec was the actual problem. When the requirements document and the spec are vague about what the app needs to do and what it needs to be tested against, the agent has room to solve the same problem two different ways in two different places, and neither implementation knows about the other. Every fix becomes local. Nothing stays fixed.&lt;/p&gt;

&lt;p&gt;The fix is spending more time upfront, at the PRD stage and the spec stage, reviewing what the agent actually produced there before writing a line of application code. Get the spec and the test plan tight enough that they cover what “done” actually means, and the back and forth during development drops off. Skip that step, and you pay for it later, one resurfacing bug at a time.&lt;/p&gt;

</description>
      <category>gemini</category>
      <category>vibecoding</category>
      <category>antigravity</category>
    </item>
    <item>
      <title>Google's AI tools for developers and enterprise, and when to actually reach for each</title>
      <dc:creator>Sireesha Pulipati</dc:creator>
      <pubDate>Mon, 03 Aug 2026 21:51:04 +0000</pubDate>
      <link>https://dev.to/gde/googles-ai-tools-for-developers-and-enterprise-and-when-to-actually-reach-for-each-5816</link>
      <guid>https://dev.to/gde/googles-ai-tools-for-developers-and-enterprise-and-when-to-actually-reach-for-each-5816</guid>
      <description>&lt;p&gt;Beyond the consumer surfaces most people know, the Gemini app and Gemini Notebook (formerly NotebookLM), Google offers a whole set of AI tools and interfaces built for developers and enterprise teams. At first glance, it's a lot to wrap your head around. AI Studio, Gemini Enterprise Agent Platform, the Gemini Enterprise app, Antigravity in its various forms. The names overlap, the marketing language overlaps, and it's not obvious where one tool's job ends and another's begins.&lt;/p&gt;

&lt;h2&gt;
  
  
  AI Studio is for prototyping and building web and Android&amp;nbsp;apps
&lt;/h2&gt;

&lt;p&gt;AI Studio is where you test a prompt, try a multimodal input, or compare models before committing to anything. It also builds full applications straight from a prompt, complete with backend infrastructure. When your app needs to store data or handle sign-ins, AI Studio provisions a database and authentication for you, either Cloud Firestore or Cloud SQL for a relational setup, and Firebase Authentication, without you configuring any of that by hand. It also connects to Google Workspace APIs, so an app you build there can work with a user's real Gmail, Docs, Sheets, or Calendar data. It supports mobile app development as well, for native Android apps.&lt;/p&gt;

&lt;p&gt;Use AI Studio when you want to validate an idea or ship a lightweight web or Android app fast, with real backend support, and without provisioning any Google Cloud infrastructure yourself.&lt;/p&gt;

&lt;h2&gt;
  
  
  Gemini Enterprise Agent Platform is where agents actually get&amp;nbsp;built
&lt;/h2&gt;

&lt;p&gt;This is the next generation of Vertex AI. Google folded Vertex AI's entire service catalog, model access, custom training, evaluation, pipelines, all of it, into what's now called the Agent Platform. If you go looking for Vertex AI in the console today, it redirects you here.&lt;/p&gt;

&lt;p&gt;This is the one-stop shop for the full agent development lifecycle. You build with Agent Studio, the low-code visual builder, or the Agent Development Kit for code-first development in Python, Go, Java, or TypeScript. Agent Engine handles the managed runtime, Model Garden gives you access to over 200 models including Gemini and Claude, and the governance layer, IAM, VPC-SC, compliance controls, gives a production agent what it needs to survive a security review.&lt;/p&gt;

&lt;p&gt;Use this when what you're building needs to act with real autonomy inside your enterprise environment, holding state and making decisions across steps.&lt;/p&gt;

&lt;h2&gt;
  
  
  Gemini Enterprise app is for business users and knowledge workers
&lt;/h2&gt;

&lt;p&gt;This is the surface your non-technical colleagues will actually touch. A SaaS chat interface, grounded search across Drive, SharePoint, and Jira, no-code agent creation for business users, all without opening the Agent Platform directly. If your job is to register, govern, and hand off an agent so people across the org can use it, this is the distribution layer.&lt;/p&gt;

&lt;p&gt;The Agent Platform builds the agent. The Enterprise app is how it reaches an employee, packaged for their actual job.&lt;/p&gt;

&lt;h2&gt;
  
  
  Antigravity is a bundle of&amp;nbsp;tools
&lt;/h2&gt;

&lt;p&gt;Each piece in the bundle enables a different way of working with the same underlying coding agent.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Antigravity IDE is for interactive, local, visual development. It's VS Code-style, with the agent chat sitting next to your code and inline diffs as it works. This is for engineers doing day-to-day, hands-on development with an AI partner sitting next to them.&lt;/li&gt;
&lt;li&gt;Antigravity 2.0 is a standalone desktop app. It runs multiple agents in parallel across different projects, handles scheduled or cron-based tasks, and produces reviewable output. This is for delegation at a higher level.&lt;/li&gt;
&lt;li&gt;Antigravity CLI is the terminal-native surface. Scriptable, headless, built for CI/CD pipelines and shell-level workflows.&lt;/li&gt;
&lt;li&gt;Antigravity SDK is what you use when you want the agent loop itself embedded inside your own code, particularly Python-based systems. You call it from your own code, the same way you'd call any other library.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Underneath all of them sits the same agent harness, the actual execution engine that runs code, executes shell commands, browses the web, and does the multi-step work autonomously. It powers the IDE, the CLI, and the SDK from underneath, so improvements to the core agent land on all three surfaces at once.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where this leaves&amp;nbsp;you
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Need&lt;/th&gt;
&lt;th&gt;Tool&lt;/th&gt;
&lt;th&gt;Why&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Validate an idea or ship a lightweight web or Android app fast&lt;/td&gt;
&lt;td&gt;AI Studio&lt;/td&gt;
&lt;td&gt;Full-stack app building with automatic backend and auth, no infra setup&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Build a governed, autonomous agent for production&lt;/td&gt;
&lt;td&gt;Agent Platform&lt;/td&gt;
&lt;td&gt;Full agent development lifecycle, IAM, VPC-SC, compliance&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Roll an agent out to non-technical staff&lt;/td&gt;
&lt;td&gt;Gemini Enterprise app&lt;/td&gt;
&lt;td&gt;No-code distribution, grounded search&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Hands-on coding with an AI partner&lt;/td&gt;
&lt;td&gt;Antigravity IDE&lt;/td&gt;
&lt;td&gt;Interactive, visual, local development&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Delegate multiple background tasks&lt;/td&gt;
&lt;td&gt;Antigravity 2.0&lt;/td&gt;
&lt;td&gt;Async, parallel, scheduled work&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Terminal or CI/CD automation&lt;/td&gt;
&lt;td&gt;Antigravity CLI&lt;/td&gt;
&lt;td&gt;Scriptable, headless&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Embed the agent loop in your own code&lt;/td&gt;
&lt;td&gt;Antigravity SDK&lt;/td&gt;
&lt;td&gt;Programmatic, especially Python systems&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Once you know what each of these actually does, the overlapping names stop being confusing. AI Studio for prototyping and lightweight apps, the Agent Platform for production agents, the Enterprise app for distribution, Antigravity for the coding work itself. Pick based on what you're building and who it's for.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>google</category>
    </item>
    <item>
      <title>Guarding the till while autonomous data agents do the digging</title>
      <dc:creator>Sireesha Pulipati</dc:creator>
      <pubDate>Thu, 09 Jul 2026 22:24:23 +0000</pubDate>
      <link>https://dev.to/gde/guarding-the-till-while-autonomous-data-agents-do-the-digging-3nmi</link>
      <guid>https://dev.to/gde/guarding-the-till-while-autonomous-data-agents-do-the-digging-3nmi</guid>
      <description>&lt;p&gt;Autonomous agents are genuinely good at answering messy business questions. Give one an LLM and a set of tools, and it can query a database, dig through files, and build a chart to investigate why a metric moved. No fixed script required.&lt;/p&gt;

&lt;p&gt;The problem is what happens when nobody’s watching the meter.&lt;/p&gt;

&lt;p&gt;If a question is simple, you don’t need an agent. You write one SQL query and you’re done. But most real analytical questions aren’t simple. They’re ambiguous, open ended, and take a few rounds of trial and error before they resolve. An agent handles that the way a human analyst would if you turned them loose on a data warehouse with no supervision. It tries a query, gets a weird result, tries a different angle, hits a dead end, and tries again.&lt;/p&gt;

&lt;p&gt;On a warehouse the size of BigQuery, that trial-and-error loop gets expensive fast. Add in the token costs of a long-context LLM reasoning through fifteen turns, and a single agent run can blow past your budget before anyone notices.&lt;/p&gt;

&lt;p&gt;In this post, I’ll walk through how I built a cost-safe version of this kind of agent, using the Google Antigravity SDK and the Data Agent Kit (DAK) extension. Two guardrails do the work.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A BigQuery scan cost guardrail. It dry-runs every query before it executes and blocks anything that would scan too much data.&lt;/li&gt;
&lt;li&gt;An LLM token spend guardrail. It pauses the agent for human approval once a session crosses a token budget.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Why autonomous analysis runs amok at scale
&lt;/h2&gt;

&lt;p&gt;To see why this matters, it helps to look at how BigQuery bills, how agents actually reason, and where the two collide.&lt;/p&gt;

&lt;h3&gt;
  
  
  The columnar trap
&lt;/h3&gt;

&lt;p&gt;A lot of people assume that adding a &lt;code&gt;LIMIT 10&lt;/code&gt; or a &lt;code&gt;WHERE&lt;/code&gt; clause keeps a query cheap. In a columnar database like BigQuery, that assumption will bite you.&lt;/p&gt;

&lt;p&gt;BigQuery charges by the bytes it reads from the columns you reference, across the whole table, not the rows you end up seeing. Run something like &lt;code&gt;SELECT body FROM comments WHERE body LIKE '%pandas%'&lt;/code&gt; and it scans the entire body column for every row before it even gets to filtering. There's no row-level index to jump to matching rows, so evaluating that filter means reading every value in the column first. The &lt;code&gt;LIMIT&lt;/code&gt; clause runs after that scan is already paid for, so it doesn't save you anything.&lt;/p&gt;

&lt;p&gt;The only real lever is filtering on a partitioned column, which restricts the query to specific physical blocks like a date range, or a clustered column, which prunes blocks based on sorted keys.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why agentic exploration is so inefficient
&lt;/h3&gt;

&lt;p&gt;A human analyst scopes a query carefully before running it. An agent doesn’t work that way. It loops.&lt;/p&gt;

&lt;p&gt;For an open-ended problem, the path usually looks like this. It runs a few queries to discover table structures and columns. It tries different combinations of joins, filters, and aggregations to test its hypotheses, and when it hits an empty result or a syntax error, it rewrites and tries again. It runs heavy string scans, LIKE '%error%' across a text column, to pull out logs or comments. And when it needs to merge sources, it runs wide multi-key joins that shuffle data across worker slots.&lt;/p&gt;

&lt;p&gt;Here’s where it gets real. In a production environment with tables in the hundreds of terabytes, a single on-demand query scanning 100 TB of one column costs $625 at standard $6.25/TB pricing. Stack a few window functions and nested joins on top and you’re triggering shuffle operations that move gigabytes between worker slots. If you’re on capacity-based billing instead of on-demand, those same queries burn through slot-hours and degrade everyone else’s performance.&lt;/p&gt;

&lt;p&gt;An agent running fifteen turns of unoptimized exploratory queries on TB-scale tables can rack up thousands of dollars in a single run. That’s not a hypothetical. That’s what happens the first time you point one of these things at a real warehouse without a guardrail.&lt;/p&gt;

&lt;h2&gt;
  
  
  Investigating dev sentiment and tech stack health
&lt;/h2&gt;

&lt;p&gt;To build and test this, I used the Stack Overflow dataset from BigQuery Public datasets.&lt;/p&gt;

&lt;p&gt;Here’s the question I tried to get the answer to:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Developer engagement metrics for Python data science libraries (pandas, numpy) on Stack Overflow dropped in late 2023. Answer rates and comment volumes fell. Did that correlate with a specific library release? Was it a boycott, or something more transient?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;That’s a genuinely open-ended problem, and answering it takes a few steps. First, verify the trend actually happened, and check whether it was unique to &lt;code&gt;pandas&lt;/code&gt; and &lt;code&gt;numpy&lt;/code&gt; or part of a broader Stack Overflow slowdown (a seasonal dip, or the rise of ChatGPT, would show up across all topics). Second, cross-reference the comment trend with PyPI download stats to see whether people actually stopped installing the library, or just stopped needing to ask about it, say, after pandas 2.1 shipped in September 2023. Third, run text searches on the comments themselves to find the specific error messages or breaking changes people were complaining about, like the PyArrow dependency changes.&lt;/p&gt;

&lt;p&gt;To run this without setting up a bunch of infrastructure, the agent queries across two systems. BigQuery holds the Stack Overflow public dataset (&lt;code&gt;bigquery-public-data.stackoverflow&lt;/code&gt;), hundreds of gigabytes of partitioned post and comment data. A local CSV holds monthly PyPI download rates for &lt;code&gt;pandas&lt;/code&gt; and &lt;code&gt;numpy&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wiring up Data Agent Kit and Antigravity
&lt;/h2&gt;

&lt;p&gt;Data Agent Kit (DAK) runs as an extension inside the Antigravity IDE or CLI, and it handles the data-connection plumbing so you don’t have to.&lt;/p&gt;

&lt;p&gt;It exposes database tools as MCP servers, bigquery_remote being the one I used here, which run remotely in GCP and talk directly to your data in BigQuery. It also ships filesystem-based Agent Skills for performing various data operations, such as data cleaning, building data pipelines, querying data etc., using Google Cloud services and standard tools such as Python and Notebooks. Skills are pre-packaged directories with standard SKILL.md instruction files and references.&lt;/p&gt;

&lt;p&gt;Once you install DAK in your IDE, you configure it with the Google Cloud Project you want to work with. The MCP tools and skills get injected into the agent’s workspace automatically. I didn’t have to write connection pooling, manage GCP credentials by hand, or wire up custom Python imports for BigQuery access.&lt;/p&gt;

&lt;p&gt;The Antigravity SDK sits on top of that as the agent runtime. It’s where I write the governance logic, the lifecycle hooks that intercept and control whatever tools DAK injects. That’s where the guardrails live.&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.antigravity.hooks&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;policy&lt;/span&gt;

&lt;span class="c1"&gt;# 2. Config is clean: the IDE automatically injects DAK tools and skills
&lt;/span&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;system_instructions&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 an autonomous data investigator...&lt;/span&gt;&lt;span class="sh"&gt;"&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="p"&gt;[&lt;/span&gt;
        &lt;span class="n"&gt;policy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;deny&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;search_web&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;           &lt;span class="c1"&gt;# Force the agent to use BigQuery instead of search shortcuts
&lt;/span&gt;    &lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="n"&gt;hooks&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="n"&gt;bq_cost_guardrail_hook&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;             &lt;span class="c1"&gt;# Intercepts the injected BQ tool for dry-run check
&lt;/span&gt;        &lt;span class="n"&gt;check_token_budget_hook&lt;/span&gt;             &lt;span class="c1"&gt;# Intercepts chat turns for budget validation
&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;Splitting the data platform layer (DAK) from the agent harness layer (Antigravity SDK) is what makes this clean. The guardrails live as harness hooks, and DAK just handles the database connections underneath.&lt;/p&gt;

&lt;h2&gt;
  
  
  Guardrail 1: the BigQuery scan cost guardrail
&lt;/h2&gt;

&lt;p&gt;This one intercepts SQL queries with the Antigravity SDK’s &lt;code&gt;@hooks.pre_tool_call_decide&lt;/code&gt; hook.&lt;/p&gt;

&lt;p&gt;Before a query runs, I do a dry run. It’s a free operation, and it tells you exactly how many bytes the query will scan. If that number blows past the budget, the call gets rejected and the agent gets a message back telling it why, so it can rewrite the query with a proper partition filter.&lt;/p&gt;

&lt;p&gt;One thing worth saying up front. The 10 GB limit below is small on purpose, small enough that you’ll actually see the guardrail trigger and self-correct within a couple of turns. Your real limit depends on your table sizes, your pricing tier, and what you’re actually willing to spend on a single exploratory query. Treat it as a placeholder and set your own before you point this at a real workload.&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.cloud&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;bigquery&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;google.antigravity&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;types&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;google.antigravity.hooks&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;hooks&lt;/span&gt;

&lt;span class="c1"&gt;# Cost settings
&lt;/span&gt;&lt;span class="n"&gt;SINGLE_QUERY_SCAN_LIMIT_GB&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;10.0&lt;/span&gt;  &lt;span class="c1"&gt;# 10 GB limit per query
&lt;/span&gt;&lt;span class="n"&gt;BQ_PRICE_PER_TB&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;6.25&lt;/span&gt;             &lt;span class="c1"&gt;# Standard BigQuery on-demand pricing
&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;estimate_bq_query_cost&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sql&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="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;tuple&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;float&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;float&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Perform a dry run to estimate the query scan size and cost.&lt;/span&gt;&lt;span class="sh"&gt;"""&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;bigquery&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;job_config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;bigquery&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;QueryJobConfig&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dry_run&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="n"&gt;use_query_cache&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="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;query_job&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="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sql&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;job_config&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;job_config&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;bytes_scanned&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;query_job&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;total_bytes_processed&lt;/span&gt;
        &lt;span class="n"&gt;gb_scanned&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;bytes_scanned&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1024&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="n"&gt;cost&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bytes_scanned&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1024&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;BQ_PRICE_PER_TB&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;gb_scanned&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cost&lt;/span&gt;
    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;Exception&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="c1"&gt;# If the dry run fails (e.g. syntax error), let the actual query run fail naturally
&lt;/span&gt;        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mf"&gt;0.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.0&lt;/span&gt;

&lt;span class="nd"&gt;@hooks.pre_tool_call_decide&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;bq_cost_guardrail_hook&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&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;ToolCall&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;types&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HookResult&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="c1"&gt;# Intercept BigQuery tool execution from DAK MCP tools
&lt;/span&gt;    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;execute_sql&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;execute_sql_readonly&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;query_bigquery&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
        &lt;span class="n"&gt;sql&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;sql&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;query&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;sql&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;return&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;HookResult&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;allow&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="n"&gt;gb_scanned&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cost&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;estimate_bq_query_cost&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sql&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="c1"&gt;# Print status so users running this in a terminal can see the guardrail in action!
&lt;/span&gt;        &lt;span class="nf"&gt;print&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="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;[Guardrail Check] Intercepted Query: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;sql&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;120&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;()&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="nf"&gt;print&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;[Guardrail Check] Estimated Scan: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;gb_scanned&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; GB (Estimated Cost: ~$&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;cost&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="n"&gt;f&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="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;gb_scanned&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;SINGLE_QUERY_SCAN_LIMIT_GB&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;error_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;Query rejected by Cost Guardrail! &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;This query is estimated to scan &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;gb_scanned&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; GB of data (costing ~$&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;cost&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="n"&gt;f&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;which exceeds your safety limit of &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;SINGLE_QUERY_SCAN_LIMIT_GB&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; GB per query. &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;Please rewrite your query to limit scan costs by filtering on partitioned/clustered &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;columns (e.g., creation_date) and avoiding SELECT *.&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="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;❌ [Guardrail Triggered] BLOCKING query scan (&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;gb_scanned&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; GB &amp;gt; &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;SINGLE_QUERY_SCAN_LIMIT_GB&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; GB).&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="c1"&gt;# Block the tool execution and send the guidance back to the agent's context
&lt;/span&gt;            &lt;span class="k"&gt;return&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;HookResult&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;allow&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;message&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;error_message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;else&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="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;✅ [Guardrail Approved] Query scan approved (&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;gb_scanned&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; GB &amp;lt;= &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;SINGLE_QUERY_SCAN_LIMIT_GB&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; GB).&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="n"&gt;types&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;HookResult&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;allow&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here is the visual flow of how this cost interceptor and the agent self-correction loop function:&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%2Fjiumd75ew2srua4436xh.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%2Fjiumd75ew2srua4436xh.png" alt=" " width="800" height="1025"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  The Agent Self-Correction Loop
&lt;/h3&gt;

&lt;p&gt;When the agent receives this rejection as the “tool result,” its LLM reads the guidance, understands the partition requirement, and automatically rewrites the query. Here is what the self-correction log looks like when the agent attempts a raw scan:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[Agent Turn 1]
Thinking: I need to analyze comment text containing "pandas". Let me query the comments table.
Tool Call: execute_sql_readonly(sql="SELECT text FROM `bigquery-public-data.stackoverflow.comments` WHERE text LIKE '%pandas%'")

[Guardrail Triggered]
System Hook: Query rejected by Cost Guardrail! This query is estimated to scan 14.09 GB of data (costing ~$0.09), which exceeds your safety limit of 10.0 GB. Please rewrite your query to filter on partitioned columns (e.g., creation_date).

[Agent Turn 2]
Thinking: Ah, my query was blocked because it scans the entire comments table. I should restrict the scan size by adding a date filter. The 'comments' table is partitioned by 'creation_date'.
Tool Call: execute_sql_readonly(sql="SELECT text FROM `bigquery-public-data.stackoverflow.comments` WHERE creation_date &amp;gt;= '2022-01-01' AND creation_date &amp;lt; '2022-02-01' AND text LIKE '%pandas%'")

[Guardrail Approved]
System Hook: Query estimated to scan 1.8 GB. Approved.

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Guardrail 2: the LLM token spend guardrail
&lt;/h2&gt;

&lt;p&gt;The BigQuery guardrail watches the data layer. This one watches the reasoning budget.&lt;/p&gt;

&lt;p&gt;Autonomous agents run in a loop, and every turn the conversation history grows, more prompts, more tool calls, more results, more reasoning steps. Token usage climbs with it, and so does your API bill.&lt;/p&gt;

&lt;p&gt;Because the Antigravity SDK manages the agent’s internal reasoning loop asynchronously inside a single &lt;code&gt;await agent.chat()&lt;/code&gt; call, you can't just check usage in a manual loop. You need an SDK hook. I built a stateful manager that checks the conversation's &lt;code&gt;total_usage&lt;/code&gt; before each turn and asks a human for confirmation if the budget is breached.&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;asyncio&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;google.antigravity&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;LocalAgentConfig&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;types&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;google.antigravity.hooks&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;hooks&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SessionGuardrail&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;token_budget&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="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;token_budget&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;token_budget&lt;/span&gt;
        &lt;span class="n"&gt;self&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="bp"&gt;None&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&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;Agent&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Bind the agent reference to the guardrail manager.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
        &lt;span class="n"&gt;self&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="n"&gt;agent&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;check_token_budget&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&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="o"&gt;-&amp;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;HookResult&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Intercept before a chat turn starts to enforce the token budget.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;agent&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&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;conversation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;usage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&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;conversation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;total_usage&lt;/span&gt;
            &lt;span class="c1"&gt;# Show active token monitoring in the terminal on every turn
&lt;/span&gt;            &lt;span class="nf"&gt;print&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;[Token Monitor] Session token count: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;usage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;total_token_count&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;,&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;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;token_budget&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;,&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="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;usage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;total_token_count&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;token_budget&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="c1"&gt;# Prompt the user asynchronously for authorization
&lt;/span&gt;                &lt;span class="nf"&gt;print&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="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;[Budget Alert] Cumulative token usage is &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;usage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;total_token_count&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;. Budget is &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;token_budget&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="n"&gt;confirm&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;to_thread&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;input&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Authorize additional tokens? (y/n): &lt;/span&gt;&lt;span class="sh"&gt;"&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;confirm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;lower&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;y&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="n"&gt;types&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;HookResult&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                        &lt;span class="n"&gt;allow&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;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;Session suspended: Token budget of &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;token_budget&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; exceeded.&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="n"&gt;types&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;HookResult&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;allow&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One thing I want to call out here:&lt;/p&gt;

&lt;p&gt;Using &lt;code&gt;input()&lt;/code&gt; wrapped in &lt;code&gt;asyncio.to_thread&lt;/code&gt; is a fast way to demonstrate human-in-the-loop validation from a local terminal, but it won't hold up in production. Blocking stdin reads can hang the event loop or throw an &lt;code&gt;EOFError&lt;/code&gt; in a web backend, a CI runner, or a notebook. For anything real, swap the CLI prompt for an async request to a Slack webhook, a PagerDuty trigger, or a queue that resolves when someone clicks approve.&lt;/p&gt;

&lt;h2&gt;
  
  
  Running the safe agent loop
&lt;/h2&gt;

&lt;p&gt;Now it all comes together in one config. The token budget here is another demo value. 150,000 tokens is enough to watch the guardrail trigger without waiting forever, not a number to carry into production. Set yours based on your actual session lengths and what a runaway session would cost you.&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;asyncio&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;google.antigravity&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;LocalAgentConfig&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;google.antigravity.hooks&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;hooks&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;policy&lt;/span&gt;

&lt;span class="c1"&gt;# Global instance of the guardrail manager to hold state
&lt;/span&gt;&lt;span class="n"&gt;guardrail&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;SessionGuardrail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;token_budget&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;150_000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nd"&gt;@hooks.pre_turn&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;check_token_budget_hook&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&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="o"&gt;-&amp;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;HookResult&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;guardrail&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;check_token_budget&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&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;run_safe_investigation&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="c1"&gt;# 1. Register cost guardrail hooks — DAK tools and skills are injected automatically by the IDE
&lt;/span&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;system_instructions&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;You are an autonomous data investigator. &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;When outputting your reasoning, steps, or planned actions, you MUST format them clearly &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;with newlines, bullet points, and spacing so they are clean and readable in a terminal console. &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Do not merge your planned actions into a single dense paragraph.&lt;/span&gt;&lt;span class="sh"&gt;"&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="p"&gt;[&lt;/span&gt;
            &lt;span class="n"&gt;policy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;deny&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;search_web&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;     &lt;span class="c1"&gt;# Force the agent to use BigQuery instead of search shortcuts
&lt;/span&gt;        &lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="n"&gt;hooks&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="n"&gt;bq_cost_guardrail_hook&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;       &lt;span class="c1"&gt;# Intercepts SQL queries
&lt;/span&gt;            &lt;span class="n"&gt;check_token_budget_hook&lt;/span&gt;       &lt;span class="c1"&gt;# Intercepts chat turns
&lt;/span&gt;        &lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# 3. Execute the agent
&lt;/span&gt;    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;with&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;config&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;agent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;guardrail&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;agent&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# Give the manager access to the running agent
&lt;/span&gt;
        &lt;span class="n"&gt;user_query&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;You MUST run a live SQL query (using execute_sql or query_bigquery) to search the Stack Overflow &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;comments table for pandas comments in Q4 2023, even if you think the table is unpartitioned. &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Use the standard file writing tools (like write_file) to save the final markdown report as &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
            &lt;span class="sh"&gt;"'&lt;/span&gt;&lt;span class="s"&gt;output/analysis_results.md&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt; and the script as &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;output/analyze_pandas.py&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt; in the workspace. &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Do not use the save_artifact tool.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;response&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;agent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;chat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_query&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="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;text&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;__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;__main__&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&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;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;run_safe_investigation&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Getting your hands dirty
&lt;/h2&gt;

&lt;p&gt;Ready to run this yourself? Here’s the walkthrough.&lt;/p&gt;

&lt;h4&gt;
  
  
  Step 1: clone the repo and look around
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/sireeshapulipati/guarding-the-till.git
&lt;span class="nb"&gt;cd &lt;/span&gt;guarding-the-till
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The directory splits into two clean categories. The inputs, what you actually need to run it, are &lt;code&gt;agent.py&lt;/code&gt; (setup, hooks, and the execution loop), &lt;code&gt;requirements.txt&lt;/code&gt;, &lt;code&gt;data/pypi_downloads.csv&lt;/code&gt; (monthly PyPI download stats for pandas and numpy), and &lt;code&gt;.env.example&lt;/code&gt;. The outputs are pre-generated reference examples. &lt;code&gt;output/analysis_results_example.md&lt;/code&gt; is the full markdown report from my run, and &lt;code&gt;output/analyze_pandas_example.py&lt;/code&gt; is a standalone cost-guarded script the agent wrote to automate this in the future.&lt;/p&gt;

&lt;h4&gt;
  
  
  Step 2: sort out the GCP prerequisites
&lt;/h4&gt;

&lt;p&gt;Before you boot the agent, make sure your terminal has access to Google Cloud. You need a project with the BigQuery API enabled, and you need to authenticate with Application Default Credentials.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gcloud auth application-default login

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You’ve got two options here, and the &lt;code&gt;.env.example&lt;/code&gt; file lays both out. Authenticate with ADC like above and the Antigravity SDK picks up your active GCP context automatically, hooking straight into Vertex AI's Gemini models, as long as the Gemini for Google Cloud API is enabled on your project. Or skip GCP auth entirely and drop a Gemini API key straight into &lt;code&gt;.env&lt;/code&gt; instead. Either path works, pick whichever fits how you're already set up.&lt;/p&gt;

&lt;h4&gt;
  
  
  Step 3: spin up your Python environment
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python &lt;span class="nt"&gt;-m&lt;/span&gt; venv .venv
&lt;span class="nb"&gt;source&lt;/span&gt; .venv/bin/activate  &lt;span class="c"&gt;# On Windows, use: .venv\Scripts\activate&lt;/span&gt;
pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; requirements.txt

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Step 4: configure your environment variables
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cp&lt;/span&gt; .env.example .env

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Open &lt;code&gt;.env&lt;/code&gt; and swap &lt;code&gt;your_gcp_project_id_here&lt;/code&gt; for your actual GCP project ID.&lt;/p&gt;

&lt;h4&gt;
  
  
  Step 5: let it run
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python agent.py

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Watch the logs. The agent starts up, formulates a plan, and immediately goes for the comments table. Agents are non-deterministic, so your exact run will vary depending on the prompt, but here’s roughly what happens.&lt;/p&gt;

&lt;p&gt;The session opens with the token monitor reporting the starting count, something like &lt;code&gt;[Token Monitor] Session token count: 0 / 150,000&lt;/code&gt;. This demo wraps in a single turn, so that's the only time you'll see it print. In a longer run with several self-correction turns, it fires at the start of each one, printing the cumulative count and pausing for approval once you cross budget. From there the agent reads the local files and narrates its plan, inspect the schema, then query Stack Overflow comments in BigQuery.&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%2Fzqz6gy4rbp34c19bu3oz.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%2Fzqz6gy4rbp34c19bu3oz.png" alt=" " width="800" height="586"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Then it tries the raw query, and the guardrail catches it before it runs. A dry run against the unpartitioned comments table comes back at 14.09 GB, over the 10 GB limit, so the hook blocks it and hands the rejection back as feedback. The agent reads that, realizes it needs the &lt;code&gt;creation_date&lt;/code&gt; partition filter, rewrites the query, and resubmits. This time the scan comes in at 1.8 GB and gets approved.&lt;/p&gt;

&lt;p&gt;Once the analysis wraps, the agent reaches for its file tools and writes the results straight into &lt;code&gt;./output/&lt;/code&gt;, a markdown report as &lt;code&gt;analysis_results.md&lt;/code&gt; and a standalone script as &lt;code&gt;analyze_pandas.py&lt;/code&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%2F8rbolgtpkeldt5ai1c1o.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%2F8rbolgtpkeldt5ai1c1o.png" alt=" " width="800" height="453"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Interpreting the results
&lt;/h3&gt;

&lt;p&gt;Here is what the agent found:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Month&lt;/th&gt;
&lt;th&gt;Pandas PyPI Downloads&lt;/th&gt;
&lt;th&gt;Download MoM %&lt;/th&gt;
&lt;th&gt;Pandas SO Comment Count&lt;/th&gt;
&lt;th&gt;SO MoM %&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Oct 2023&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;150,354,240&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Baseline&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;6,210&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Baseline&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Nov 2023&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;147,923,892&lt;/td&gt;
&lt;td&gt;-1.62%&lt;/td&gt;
&lt;td&gt;5,845&lt;/td&gt;
&lt;td&gt;-5.88%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Dec 2023&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;151,553,549&lt;/td&gt;
&lt;td&gt;+2.45%&lt;/td&gt;
&lt;td&gt;5,420&lt;/td&gt;
&lt;td&gt;-7.27%&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The agent calculated a Pearson Correlation Coefficient of $r \approx -0.3654$.&lt;/p&gt;

&lt;p&gt;This is a weak-to-moderate negative correlation. Under normal circumstances, you would expect forum questions to rise as library installations rise. Instead, we see:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Installations increased: PyPI downloads reached their highest volume of the quarter in December (151.5M downloads).&lt;/li&gt;
&lt;li&gt;Stack Overflow comments decreased: Stack Overflow comments mentioning pandas steadily declined month-over-month, dropping from 6,210 in October to 5,420 in December.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This divergence is likely driven by seasonality. In December, human developers take vacations and stop posting questions or answers on public forums, leading to a dip in active comments. Meanwhile, automated build systems, CI/CD runners, and deployment scripts continue pulling the package continuously, keeping the download numbers high.&lt;/p&gt;

&lt;h3&gt;
  
  
  What’s the standalone script for?
&lt;/h3&gt;

&lt;p&gt;The agent also wrote &lt;code&gt;output/analyze_pandas_example.py&lt;/code&gt; as part of its delivery. If you want to rerun this analysis every month in a production pipeline, an Airflow DAG, a cron job, you don't need to run the slow, non-deterministic agent loop again. Just run this.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python output/analyze_pandas_example.py

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It’s a fast, deterministic ETL pipeline. It queries BigQuery, calculates the correlation, and spits out a fresh markdown report, no LLM reasoning layer involved, while keeping the exact same dry-run safety guardrails.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I took away from this
&lt;/h2&gt;

&lt;p&gt;These two guardrails do two different jobs. The BigQuery dry-run watches the data layer and stops runaway scan bills before they happen. The session token guardrail watches the reasoning layer and caps what the LLM itself costs you. Together they give the agent a bounded sandbox to actually explore in.&lt;/p&gt;

&lt;p&gt;The semantic error feedback is what actually makes this useful. Instead of a fatal exception that kills the run, the scan guardrail hands the LLM a plain-language explanation of what went wrong. That turns a runtime constraint into something the agent can actually act on, and it rewrites its own query instead of just failing.&lt;/p&gt;

&lt;p&gt;And wrapping the token budget check in a non-blocking async hook means the human-in-the-loop step doesn’t freeze the rest of the system while it waits for someone to click approve.&lt;/p&gt;

&lt;p&gt;If you’re building agents that need to explore large, cross-system datasets, this is the difference between an agent you can trust with a warehouse and one you have to babysit.&lt;/p&gt;

</description>
      <category>agenticarchitect</category>
      <category>antigravity</category>
      <category>googlecloud</category>
    </item>
    <item>
      <title>I use AI to write. The fear: everyone starts sounding the same. Real risk, but only if you skip making it yours. Where do you draw the line? More here: https://sireeshap.substack.com/p/there-is-nothing-formulaic-about?r=3alo35</title>
      <dc:creator>Sireesha Pulipati</dc:creator>
      <pubDate>Sat, 20 Jun 2026 00:04:39 +0000</pubDate>
      <link>https://dev.to/sireesha_pulipati_842f9f1/i-use-ai-to-write-the-fear-everyone-starts-sounding-the-same-real-risk-but-only-if-you-skip-4n5h</link>
      <guid>https://dev.to/sireesha_pulipati_842f9f1/i-use-ai-to-write-the-fear-everyone-starts-sounding-the-same-real-risk-but-only-if-you-skip-4n5h</guid>
      <description>&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
    &lt;div class="c-embed__content"&gt;
        &lt;div class="c-embed__cover"&gt;
          &lt;a href="https://sireeshap.substack.com/p/there-is-nothing-formulaic-about?r=3alo35" class="c-link align-middle" rel="noopener noreferrer"&gt;
            &lt;img alt="" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fsubstackcdn.com%2Fimage%2Ffetch%2F%24s_%21jrm_%21%2Cw_1200%2Ch_675%2Cc_fill%2Cf_jpg%2Cq_auto%3Agood%2Cfl_progressive%3Asteep%2Cg_auto%2Fhttps%253A%252F%252Fsubstack-post-media.s3.amazonaws.com%252Fpublic%252Fimages%252F8cde63d4-0f2f-4bd2-b159-ec4d3fa96e2f_2816x1536.png" height="450" class="m-0" width="800"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="c-embed__body"&gt;
        &lt;h2 class="fs-xl lh-tight"&gt;
          &lt;a href="https://sireeshap.substack.com/p/there-is-nothing-formulaic-about?r=3alo35" rel="noopener noreferrer" class="c-link"&gt;
            There is nothing formulaic about how I write
          &lt;/a&gt;
        &lt;/h2&gt;
          &lt;p class="truncate-at-3"&gt;
            On patterns, tells, and what voice actually is
          &lt;/p&gt;
        &lt;div class="color-secondary fs-s flex items-center"&gt;
            &lt;img alt="favicon" class="c-embed__favicon m-0 mr-2 radius-0" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fsubstackcdn.com%2Ficons%2Fsubstack%2Ffavicon.ico" width="32" height="32"&gt;
          sireeshap.substack.com
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;


</description>
    </item>
    <item>
      <title>Agents are building their own UIs now. Here's when that's worth doing.</title>
      <dc:creator>Sireesha Pulipati</dc:creator>
      <pubDate>Wed, 29 Apr 2026 21:02:06 +0000</pubDate>
      <link>https://dev.to/gde/agents-are-building-their-own-uis-now-heres-when-thats-worth-doing-o9d</link>
      <guid>https://dev.to/gde/agents-are-building-their-own-uis-now-heres-when-thats-worth-doing-o9d</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/google-cloud-next-2026-04-22"&gt;Google Cloud NEXT Writing Challenge&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;During the developer keynote at Google Cloud NEXT '26, Google built an entire multi-agent system live on stage: a marathon planner that simulates routing a race through Las Vegas. The demo wasn’t just about the agents coordinating route planning, logistics, and runner simulation. It was about the interfaces those agents generated on the fly.&lt;/p&gt;

&lt;p&gt;The route planning view showed a map with the proposed path, landmarks, and distance markers. The evaluation view displayed score breakdowns with both deterministic criteria (exactly 26.2 miles) and nondeterministic ones (community impact). The simulation view tracked live runner positions, traffic patterns, hydration stations, and Port-A-Potties. Each phase needed a different interface, and the agent built what it needed in real time using A2UI and the GenUI SDK for Flutter.&lt;/p&gt;

&lt;p&gt;The keynote also highlighted FinnishIt, an AI-powered Finnish language tutor built with GenUI. Give it a topic and it generates custom flashcard decks specific to that context. Role-play scenarios shift from text exercises to tap-and-drag word puzzles to fill-in-the-blank modules depending on what the AI assesses you need right now.&lt;/p&gt;

&lt;p&gt;Every session produces a different interface.&lt;/p&gt;

&lt;p&gt;That's the point where I stopped treating A2UI as conference noise and started paying attention.&lt;/p&gt;

&lt;p&gt;A2UI is an open standard Google donated to the community at NEXT '26. It lets agents generate UI dynamically at runtime. The GenUI SDK for Flutter is the developer-facing layer that makes it practical to build with. Most coverage either skipped it or described it without asking the more useful question: when does this actually make sense to use?&lt;/p&gt;

&lt;h2&gt;
  
  
  Where GenUI earns it
&lt;/h2&gt;

&lt;p&gt;The marathon planner works because the interface IS the orchestration experience. Different workflow phases need different visualizations: route planning requires a map, evaluation needs scoring charts, simulation shows a live timeline. &lt;/p&gt;

&lt;p&gt;FinnishIt works the same way for adaptive learning. There's no predetermined layout that serves a user practicing spoken conversational Finnish the same as one drilling grammar for the YKI citizenship test. &lt;/p&gt;

&lt;p&gt;The right exercise type, difficulty, and interaction pattern depend on what the AI assesses the user needs right now. Hardcoding any of that produces a worse product.&lt;/p&gt;

&lt;p&gt;The dynamic generation isn't a feature on top of the app. It is the app. &lt;/p&gt;

&lt;p&gt;The same logic applies to onboarding flows, and this is where I think GenUI has untapped potential.&lt;/p&gt;

&lt;p&gt;Most onboarding flows are static decision trees in disguise. You collect preferences on screen one, goals on screen two, then route users down one of two or three predetermined paths. The result feels personalized but is just filtered content behind a fixed interface.&lt;/p&gt;

&lt;p&gt;Consider a personal finance app. Someone who opens it saying "I want to stop overspending" has a completely different mental model than someone who says "I want to start investing" or "I have irregular income and need to plan around it." Those aren't just different content buckets. They're different journeys, with different concepts to introduce, different decisions to make up front, and a different definition of what "getting to value" even means.&lt;/p&gt;

&lt;p&gt;A GenUI-powered onboarding flow could read what a user brings to that first session and generate the next step as a direct response: not a static screen two, but a computed one.&lt;/p&gt;

&lt;p&gt;A personal style app makes the case even more clearly, because here the interaction type itself changes, not just the content.&lt;/p&gt;

&lt;p&gt;Someone who opens a style app saying "I have a job interview next week" needs an occasion-specific outfit construction flow: clear goal, tight timeline, specific constraints. Someone who says "I'm trying to figure out my personal style" needs a discovery experience: visual-first, exploratory, maybe swipe-on-images or mood board style. Someone who says "I want to build a capsule wardrobe on a budget" might need a wardrobe audit flow that starts with photographing what they already own.&lt;/p&gt;

&lt;p&gt;These are not variations on the same form. They require different interface primitives: camera, swipe cards, visual grids, checklists. GenUI earns it here because you genuinely cannot know which one to show until the user tells you what they're trying to do.&lt;/p&gt;

&lt;p&gt;The right interaction depends on the context. The context arrives at runtime.&lt;/p&gt;

&lt;h2&gt;
  
  
  A decision filter
&lt;/h2&gt;

&lt;p&gt;Before reaching for GenUI, three questions:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Is the interface the experience, or is it a container for a fixed one?&lt;/strong&gt;&lt;br&gt;
In FinnishIt, the dynamically generated exercise is the product. That's different from a news reader or a task manager, where content arrives through a stable interface. Not every app benefits from a layout that changes each session.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Does the user need to find the same thing in the same place next time?&lt;/strong&gt;&lt;br&gt;
Adaptive learning, personalized onboarding, style discovery: each session is meant to feel different. An e-commerce checkout, a settings screen, a navigation menu: users build trust and speed through repetition. Those interfaces earn nothing from variation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Is this an exploratory action, or one that requires confident understanding of what's about to happen?&lt;/strong&gt;&lt;br&gt;
Payment confirmation, account deletion, anything irreversible: users need to know exactly what they're looking at. Dynamic layout introduces uncertainty at exactly the wrong moment.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where it doesn't fit
&lt;/h2&gt;

&lt;p&gt;The failure cases aren't about regulation or compliance. They're about what users need from an interface to trust it.&lt;/p&gt;

&lt;p&gt;A checkout flow that looks different each time isn't personalization. It's friction.&lt;/p&gt;

&lt;p&gt;High-frequency task interfaces derive part of their value from the fact that users can operate them without thinking. Email, task management, booking flows: variability works against that entirely.&lt;br&gt;
There's also a quieter design system concern. Most product teams ship against a component library: specific tokens, spacing rules, interaction patterns. An agent that approximately matches those patterns is not the same as one that respects the contract. That gap shows up in production in ways that are hard to articulate and easy to notice.&lt;/p&gt;

&lt;h2&gt;
  
  
  The open bet
&lt;/h2&gt;

&lt;p&gt;A2UI and GenUI aren't solutions looking for a problem. There's a real category of app where static UI has always been the wrong answer: the kind where the right interaction depends on context that only arrives at runtime.&lt;/p&gt;

&lt;p&gt;FinnishIt is an early, polished example of what that looks like when it's done well. Personalized onboarding, adaptive learning, style discovery: same category.&lt;/p&gt;

&lt;p&gt;What I'm watching is whether developers build intuition for where this pattern belongs, or whether the next few years surface a wave of apps that introduced variability in exactly the places their users needed stability.&lt;/p&gt;

&lt;p&gt;If you've seen agent-generated UI get it right, or quietly get in the way, I'd like to hear about it.&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>googlecloud</category>
      <category>cloudnextchallenge</category>
      <category>genui</category>
    </item>
    <item>
      <title>Why the Google Cloud Professional Data Engineer Certification Still Matters in an AI-First World</title>
      <dc:creator>Sireesha Pulipati</dc:creator>
      <pubDate>Wed, 28 Jan 2026 05:57:23 +0000</pubDate>
      <link>https://dev.to/gde/why-the-google-cloud-professional-data-engineer-certification-still-matters-in-an-ai-first-world-4emk</link>
      <guid>https://dev.to/gde/why-the-google-cloud-professional-data-engineer-certification-still-matters-in-an-ai-first-world-4emk</guid>
      <description>&lt;p&gt;Have you found yourself wondering recently whether cloud certifications still matter, especially now that AI can write SQL, generate pipelines, and suggest architectures?&lt;/p&gt;

&lt;p&gt;Short answer: yes, arguably more than ever.&lt;br&gt;&lt;br&gt;
But the value just is not the credential. It is the depth of understanding the preparation forces you to develop.&lt;/p&gt;

&lt;p&gt;The Google Cloud Professional Data Engineer certification was never about memorizing services or APIs. At its core, it has always been a structured way to internalize how data systems should be designed, operated, and evolved in real production environments.&lt;/p&gt;

&lt;p&gt;In an AI-enabled world, that foundation matters more, not less.&lt;/p&gt;

&lt;h2&gt;
  
  
  Certification prep is about judgment
&lt;/h2&gt;

&lt;p&gt;AI tools are powerful accelerators. They can draft code, propose architectures, and help debug issues. What they cannot reliably do is exercise engineering judgment in complex, real-world constraints.&lt;/p&gt;

&lt;p&gt;Preparing seriously for the PDE exam pushes you to reason through questions such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;When does streaming actually make sense versus batch?&lt;/li&gt;
&lt;li&gt;How should reliability, cost, security, and governance be balanced?&lt;/li&gt;
&lt;li&gt;What tends to fail first at scale, and how should systems be designed for that?&lt;/li&gt;
&lt;li&gt;When is a simpler design the correct decision?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These are not trivia questions. They reflect the decisions practicing data engineers make every day. The exam tests whether you can reason through trade-offs, not whether you recognize product names.&lt;/p&gt;

&lt;h2&gt;
  
  
  How the exam has evolved
&lt;/h2&gt;

&lt;p&gt;The Professional Data Engineer exam has evolved alongside the platform and the role itself.&lt;/p&gt;

&lt;p&gt;Six or seven years ago, the exam covered a much broader surface area. It included significant emphasis on databases, analytics, and machine learning concepts. That breadth made sense at the time. Cloud data roles were still forming, and boundaries between responsibilities were less clear.&lt;/p&gt;

&lt;p&gt;What has changed since then is not the philosophy of the exam, but its focus.&lt;/p&gt;

&lt;p&gt;As Google Cloud introduced more specialized certifications, such as Associate Data Analyst, Professional Machine Learning Engineer, and Professional Database Engineer, those adjacent concerns moved into their own lanes. The PDE exam responded by narrowing its scope and going deeper.&lt;/p&gt;

&lt;p&gt;Today, it is firmly focused on core data engineering responsibilities:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Designing secure and reliable data systems
&lt;/li&gt;
&lt;li&gt;Building and operating batch and streaming pipelines
&lt;/li&gt;
&lt;li&gt;Modeling, storing, and querying data at scale
&lt;/li&gt;
&lt;li&gt;Managing cost, automation, and operational reliability
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That emphasis has always been present. What is different now is the level of concentration. With peripheral topics handled elsewhere, the exam prioritizes depth over breadth.&lt;/p&gt;

&lt;h2&gt;
  
  
  The quiet but meaningful 2025 exam guide update
&lt;/h2&gt;

&lt;p&gt;The most recent exam guide update made this focus even clearer.&lt;/p&gt;

&lt;h3&gt;
  
  
  Data engineering for AI is now explicit
&lt;/h3&gt;

&lt;p&gt;For the first time, the guide explicitly calls out responsibilities such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;AI data enrichment within pipelines
&lt;/li&gt;
&lt;li&gt;Preparing unstructured data for embeddings
&lt;/li&gt;
&lt;li&gt;Supporting retrieval-augmented generation (RAG) workflows
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These additions reflect how data engineering shows up in practice today.&lt;/p&gt;

&lt;p&gt;Most data engineers are not building models end to end. They are enabling AI systems by ensuring data is reliable, enriched, governed, and retrievable at inference time. This includes handling unstructured data, managing feature pipelines, and supporting retrieval patterns that AI applications depend on.&lt;/p&gt;

&lt;p&gt;Importantly, this does not turn the Professional Data Engineer exam into a machine learning exam. Model training and tuning remain the responsibility of ML engineers. What the PDE exam reinforces is a more fundamental truth:&lt;/p&gt;

&lt;p&gt;AI systems succeed or fail based on data engineering quality.&lt;/p&gt;

&lt;p&gt;The underlying competencies remain the same. Data modeling, pipeline design, reliability, cost control, and governance are still central. The difference is that the exam now names these AI-adjacent use cases explicitly, instead of assuming them implicitly.&lt;/p&gt;

&lt;h3&gt;
  
  
  “Data mesh” quietly disappeared
&lt;/h3&gt;

&lt;p&gt;Another notable change is the removal of the explicit term “data mesh” from the guide.&lt;/p&gt;

&lt;p&gt;This does not mean decentralization or domain ownership disappeared. It signals a shift away from buzzwords toward practical platform design, governance, and enablement. The exam now frames this work as building data platforms, not adhering to a specific architectural label.&lt;/p&gt;

&lt;p&gt;That is a healthy evolution.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to prepare today
&lt;/h2&gt;

&lt;p&gt;If you are preparing for the PDE exam now, one principle matters more than any resource.&lt;/p&gt;

&lt;p&gt;Do not study by service. Study by responsibility.&lt;/p&gt;

&lt;p&gt;Map your preparation directly to the exam domains:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Designing data systems
&lt;/li&gt;
&lt;li&gt;Ingesting and processing data
&lt;/li&gt;
&lt;li&gt;Storing data
&lt;/li&gt;
&lt;li&gt;Preparing data for analytics and AI
&lt;/li&gt;
&lt;li&gt;Maintaining and automating workloads
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For each domain, focus on trade-offs. Be able to explain why one approach is better than another in a given scenario. Anchor your understanding in production constraints such as cost, reliability, scale, and security. That is what the exam evaluates.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final thought
&lt;/h2&gt;

&lt;p&gt;The Professional Data Engineer certification is most valuable when treated as a learning framework, not a finish line. In a world where AI can generate solutions instantly, the differentiator is not speed. It is the ability to choose the right solution under real constraints. That is exactly what this certification, when approached thoughtfully, helps develop.&lt;/p&gt;

</description>
      <category>googlecloud</category>
      <category>certification</category>
      <category>dataengineering</category>
    </item>
  </channel>
</rss>
