Guarding the till while autonomous data agents do the…
    Neura MarketNeura Market/Stable Diffusion
    ChatGPTChatGPTClaudeClaudeGeminiGeminiCursorCursorGrokGrokPerplexityPerplexityStable DiffusionStable Diffusion
    DeepSeekDeepSeekCoPilotCoPilotMidjourneyMidjourney
    View All Directories
    OverviewPromptsBlogVideosGuidesCoursesCommunityModelsLoRAsComfyUI WorkflowsTrending
    Stable DiffusionBlogGuarding the till while autonomous data agents do the digging
    Back to Blog
    Guarding the till while autonomous data agents do the digging
    agenticarchitect

    Guarding the till while autonomous data agents do the digging

    Sireesha Pulipati July 9, 2026
    0 views

    Autonomous agents are genuinely good at answering messy business questions. Give one an LLM and a set...

    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.

    The problem is what happens when nobody’s watching the meter.

    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.

    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.

    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.

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

    Why autonomous analysis runs amok at scale

    To see why this matters, it helps to look at how BigQuery bills, how agents actually reason, and where the two collide.

    The columnar trap

    A lot of people assume that adding a LIMIT 10 or a WHERE clause keeps a query cheap. In a columnar database like BigQuery, that assumption will bite you.

    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 SELECT body FROM comments WHERE body LIKE '%pandas%' 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 LIMIT clause runs after that scan is already paid for, so it doesn't save you anything.

    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.

    Why agentic exploration is so inefficient

    A human analyst scopes a query carefully before running it. An agent doesn’t work that way. It loops.

    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.

    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.

    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.

    Investigating dev sentiment and tech stack health

    To build and test this, I used the Stack Overflow dataset from BigQuery Public datasets.

    Here’s the question I tried to get the answer to:

    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?

    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 pandas and numpy 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.

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

    Wiring up Data Agent Kit and Antigravity

    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.

    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.

    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.

    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.

    from google.antigravity.hooks import policy
    
    # 2. Config is clean: the IDE automatically injects DAK tools and skills
    config = LocalAgentConfig(
        system_instructions="You are an autonomous data investigator...",
        policies=[
            policy.deny("search_web")           # Force the agent to use BigQuery instead of search shortcuts
        ],
        hooks=[
            bq_cost_guardrail_hook,             # Intercepts the injected BQ tool for dry-run check
            check_token_budget_hook             # Intercepts chat turns for budget validation
        ]
    )
    

    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.

    Guardrail 1: the BigQuery scan cost guardrail

    This one intercepts SQL queries with the Antigravity SDK’s @hooks.pre_tool_call_decide hook.

    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.

    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.

    from google.cloud import bigquery
    from google.antigravity import types
    from google.antigravity.hooks import hooks
    
    # Cost settings
    SINGLE_QUERY_SCAN_LIMIT_GB = 10.0  # 10 GB limit per query
    BQ_PRICE_PER_TB = 6.25             # Standard BigQuery on-demand pricing
    
    def estimate_bq_query_cost(sql: str) -> tuple[float, float]:
        """Perform a dry run to estimate the query scan size and cost."""
        client = bigquery.Client()
        job_config = bigquery.QueryJobConfig(dry_run=True, use_query_cache=False)
        try:
            query_job = client.query(sql, job_config=job_config)
            bytes_scanned = query_job.total_bytes_processed
            gb_scanned = bytes_scanned / (1024 ** 3)
            cost = (bytes_scanned / (1024 ** 4)) * BQ_PRICE_PER_TB
            return gb_scanned, cost
        except Exception:
            # If the dry run fails (e.g. syntax error), let the actual query run fail naturally
            return 0.0, 0.0
    
    @hooks.pre_tool_call_decide
    async def bq_cost_guardrail_hook(data: types.ToolCall) -> types.HookResult:
        # Intercept BigQuery tool execution from DAK MCP tools
        if data.name in ["execute_sql", "execute_sql_readonly", "query_bigquery"]:
            sql = data.args.get("sql") or data.args.get("query")
            if not sql:
                return types.HookResult(allow=True)
            
            gb_scanned, cost = estimate_bq_query_cost(sql)
            # Print status so users running this in a terminal can see the guardrail in action!
            print(f"\n[Guardrail Check] Intercepted Query: {sql[:120].strip()}...")
            print(f"[Guardrail Check] Estimated Scan: {gb_scanned:.2f} GB (Estimated Cost: ~${cost:.4f})")
            
            if gb_scanned > SINGLE_QUERY_SCAN_LIMIT_GB:
                error_message = (
                    f"Query rejected by Cost Guardrail! "
                    f"This query is estimated to scan {gb_scanned:.2f} GB of data (costing ~${cost:.4f}), "
                    f"which exceeds your safety limit of {SINGLE_QUERY_SCAN_LIMIT_GB} GB per query. "
                    f"Please rewrite your query to limit scan costs by filtering on partitioned/clustered "
                    f"columns (e.g., creation_date) and avoiding SELECT *."
                )
                print(f"❌ [Guardrail Triggered] BLOCKING query scan ({gb_scanned:.2f} GB > {SINGLE_QUERY_SCAN_LIMIT_GB} GB).")
                # Block the tool execution and send the guidance back to the agent's context
                return types.HookResult(allow=False, message=error_message)
            else:
                print(f"✅ [Guardrail Approved] Query scan approved ({gb_scanned:.2f} GB <= {SINGLE_QUERY_SCAN_LIMIT_GB} GB).")
                
        return types.HookResult(allow=True)
    
    

    Here is the visual flow of how this cost interceptor and the agent self-correction loop function:

    Image description

    The Agent Self-Correction Loop

    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:

    [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 >= '2022-01-01' AND creation_date < '2022-02-01' AND text LIKE '%pandas%'")
    
    [Guardrail Approved]
    System Hook: Query estimated to scan 1.8 GB. Approved.
    
    

    Guardrail 2: the LLM token spend guardrail

    The BigQuery guardrail watches the data layer. This one watches the reasoning budget.

    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.

    Because the Antigravity SDK manages the agent’s internal reasoning loop asynchronously inside a single await agent.chat() 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 total_usage before each turn and asks a human for confirmation if the budget is breached.

    import asyncio
    from google.antigravity import Agent, LocalAgentConfig, types
    from google.antigravity.hooks import hooks
    
    class SessionGuardrail:
        def __init__(self, token_budget: int):
            self.token_budget = token_budget
            self.agent = None
    
        def register(self, agent: Agent):
            """Bind the agent reference to the guardrail manager."""
            self.agent = agent
    
        async def check_token_budget(self, data: str) -> types.HookResult:
            """Intercept before a chat turn starts to enforce the token budget."""
            if self.agent and self.agent.conversation:
                usage = self.agent.conversation.total_usage
                # Show active token monitoring in the terminal on every turn
                print(f"[Token Monitor] Session token count: {usage.total_token_count:,} / {self.token_budget:,}")
                
                if usage.total_token_count > self.token_budget:
                    # Prompt the user asynchronously for authorization
                    print(f"\n[Budget Alert] Cumulative token usage is {usage.total_token_count}. Budget is {self.token_budget}.")
                    confirm = await asyncio.to_thread(input, "Authorize additional tokens? (y/n): ")
                    
                    if confirm.strip().lower() != 'y':
                        return types.HookResult(
                            allow=False,
                            message=f"Session suspended: Token budget of {self.token_budget} exceeded."
                        )
            return types.HookResult(allow=True)
    

    One thing I want to call out here:

    Using input() wrapped in asyncio.to_thread 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 EOFError 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.

    Running the safe agent loop

    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.

    import asyncio
    from google.antigravity import Agent, LocalAgentConfig
    from google.antigravity.hooks import hooks, policy
    
    # Global instance of the guardrail manager to hold state
    guardrail = SessionGuardrail(token_budget=150_000)
    
    @hooks.pre_turn
    async def check_token_budget_hook(data: str) -> types.HookResult:
        return await guardrail.check_token_budget(data)
    
    async def run_safe_investigation():
        # 1. Register cost guardrail hooks — DAK tools and skills are injected automatically by the IDE
        config = LocalAgentConfig(
            system_instructions=(
                "You are an autonomous data investigator. "
                "When outputting your reasoning, steps, or planned actions, you MUST format them clearly "
                "with newlines, bullet points, and spacing so they are clean and readable in a terminal console. "
                "Do not merge your planned actions into a single dense paragraph."
            ),
            policies=[
                policy.deny("search_web")     # Force the agent to use BigQuery instead of search shortcuts
            ],
            hooks=[
                bq_cost_guardrail_hook,       # Intercepts SQL queries
                check_token_budget_hook       # Intercepts chat turns
            ]
        )
        
        # 3. Execute the agent
        async with Agent(config) as agent:
            guardrail.register(agent)  # Give the manager access to the running agent
            
            user_query = (
                "You MUST run a live SQL query (using execute_sql or query_bigquery) to search the Stack Overflow "
                "comments table for pandas comments in Q4 2023, even if you think the table is unpartitioned. "
                "Use the standard file writing tools (like write_file) to save the final markdown report as "
                "'output/analysis_results.md' and the script as 'output/analyze_pandas.py' in the workspace. "
                "Do not use the save_artifact tool."
            )
            response = await agent.chat(user_query)
            print(await response.text())
    
    if __name__ == "__main__":
        asyncio.run(run_safe_investigation())
    

    Getting your hands dirty

    Ready to run this yourself? Here’s the walkthrough.

    Step 1: clone the repo and look around

    git clone https://github.com/sireeshapulipati/guarding-the-till.git
    cd guarding-the-till
    

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

    Step 2: sort out the GCP prerequisites

    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.

    gcloud auth application-default login
    
    

    You’ve got two options here, and the .env.example 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 .env instead. Either path works, pick whichever fits how you're already set up.

    Step 3: spin up your Python environment

    python -m venv .venv
    source .venv/bin/activate  # On Windows, use: .venv\Scripts\activate
    pip install -r requirements.txt
    
    

    Step 4: configure your environment variables

    cp .env.example .env
    
    

    Open .env and swap your_gcp_project_id_here for your actual GCP project ID.

    Step 5: let it run

    python agent.py
    
    

    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.

    The session opens with the token monitor reporting the starting count, something like [Token Monitor] Session token count: 0 / 150,000. 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.

    Image description

    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 creation_date partition filter, rewrites the query, and resubmits. This time the scan comes in at 1.8 GB and gets approved.

    Once the analysis wraps, the agent reaches for its file tools and writes the results straight into ./output/, a markdown report as analysis_results.md and a standalone script as analyze_pandas.py

    Image description

    Interpreting the results

    Here is what the agent found:

    MonthPandas PyPI DownloadsDownload MoM %Pandas SO Comment CountSO MoM %
    Oct 2023150,354,240Baseline6,210Baseline
    Nov 2023147,923,892-1.62%5,845-5.88%
    Dec 2023151,553,549+2.45%5,420-7.27%

    The agent calculated a Pearson Correlation Coefficient of $r \approx -0.3654$.

    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:

    • Installations increased: PyPI downloads reached their highest volume of the quarter in December (151.5M downloads).
    • 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.

    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.

    What’s the standalone script for?

    The agent also wrote output/analyze_pandas_example.py 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.

    python output/analyze_pandas_example.py
    
    

    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.

    What I took away from this

    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.

    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.

    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.

    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.

    Tags

    agenticarchitectantigravitygooglecloud

    Comments

    More Blog

    View all
    Context bankruptcy: The case for strategic forgetting for AI Agentsai

    Context bankruptcy: The case for strategic forgetting for AI Agents

    Most of us have seen a coding agent fail to complete a task we know it can do. We just don't...

    J
    James O'Reilly
    Parallel Compliance Engine: Drive-to-Sheets Multi-Agent Orchestrationgooglecloud

    Parallel Compliance Engine: Drive-to-Sheets Multi-Agent Orchestration

    When building Generative AI applications, developers often encounter a massive bottleneck: sequential...

    A
    Aryan Irani
    Is It Ethical to Post and Ask About Circuits on Dev.to?discuss

    Is It Ethical to Post and Ask About Circuits on Dev.to?

    I’ve been thinking about sharing some electronic circuit posts on Dev.to — small circuits, DIY...

    C
    codebunny20
    The One-Click Exporter: AI Studio Antigravity, Probed to Its Limitsagents

    The One-Click Exporter: AI Studio Antigravity, Probed to Its Limits

    What nobody tells you about exporting your multi-agent prototype to a local workspace. Every...

    L
    leslysandra
    Return on Attention: Why AI Code Reviews Are Wearing Us Outai

    Return on Attention: Why AI Code Reviews Are Wearing Us Out

    PR volume went up, ticket quality didn't, and the gap got filled with LLMs on both sides of the review: bots reviewing, bots replying, bots occasionally arguing with bots about priorities that only existed in a teammate's head. Our CEO named the actual problem, and it's bigger than code review.

    C
    christine
    Debugging Deployments with Gemma 4B, TPU v6e-1, MCP, and Antigravity CLIantigravitycli

    Debugging Deployments with Gemma 4B, TPU v6e-1, MCP, and Antigravity CLI

    This article provides a step by step debugging guide for deploying Gemma 4 to a Google Cloud TPU...

    X
    xbill

    Stay up to date

    Get the latest Stable Diffusion prompts, rules, and resources delivered to your inbox weekly.

    Neura Market LogoNeura Market

    Discover the best AI prompts, plugins, and resources for Stable Diffusion and more.

    Content Types

    • Rules
    • Prompts
    • MCPs
    • Agents
    • Guides

    Platforms

    • ChatGPT Directory
    • Claude Directory
    • Gemini Directory
    • Cursor Directory
    • Grok Directory
    • Perplexity Directory
    • DeepSeek Directory
    • CoPilot Directory
    • Stable Diffusion Directory
    • Midjourney Directory
    • All Directories

    Resources

    • Blog
    • Documentation
    • Help Center
    • Marketplace

    Legal

    • Privacy Policy
    • Terms of Service

    © 2026 Neura Market. All rights reserved.

    |

    Not affiliated with any AI platform vendors.

    Ready-made automations for this

    Workflows from the Neura Market marketplace related to this Stable Diffusion resource

    • Autonomous Vehicle Log Summarizer: Streamlining Vehicle Data Managementn8n · $4.91 · Related topic
    • Autonomous AI Crawler for Financial Insightsn8n · $4.76 · Related topic
    • AI-Powered Autonomous Research Workflow for Content Monetizationn8n · $4.52 · Related topic
    • Autonomous YouTube to SEO-Optimized WordPress Blog Publishern8n · $24.99 · Related topic
    Browse all workflows