ANTIGRAVITY LABJP
Articles/Agents & Manager
Agents & Manager/2026-05-12Advanced

Tracking AI Agent Decisions in Antigravity: Implementing Decision Logs and Explainability

Learn how to design decision logging systems that capture why your AI agents make specific choices. Includes working Python code examples, Before/After patterns, and a quality improvement cycle for production Antigravity agents.

antigravity429agents123explainabilitylogging2production71design-patterns2

Premium Article

I've been building apps on my own since 2014, and the phrase I've repeated most often is: "How did it end up like this?"

For the first several years, the answer was always in the code. Logic I'd written hitting an unexpected state. Run the debugger, trace through the steps, find the cause. Code is honest — it only does what you tell it to do. Bugs come from human assumptions, but the source is always somewhere in the code.

Working with AI agents changed the nature of that honesty. The reasoning behind an agent's decision isn't written anywhere in the code. The "why" dissolves into hundreds of lines of internal state, conversational context, and tool call history.

Last year, an agent handling one of my tasks overwrote a file I hadn't intended it to touch. I had backups, so the damage was zero — but it took two hours to figure out why it happened. The culprit turned out to be a single line in the prompt: "replace with the latest file." I'd written it as an instruction about a specific file, but the agent applied it to a broader scope.

That experience is what pushed me to make explainability design a formal part of my development process.

In this article, I'll walk through the design patterns for decision logging in Antigravity agents, and how to turn that logging into a quality improvement cycle. After building apps that have reached over 50 million cumulative downloads, I can say with confidence: any system you can't understand will eventually surprise you — and the larger the system, the more expensive that surprise.

Observability vs. Explainability — Knowing What You're Designing

Before getting into implementation, let me clarify two concepts that seem similar but serve different purposes.

Observability is the ability to understand the current state of a system from the outside. Response times, error rates, resource usage — this is the domain of SRE and infrastructure, typically covered by tools like OpenTelemetry or Langfuse.

Explainability is the ability to describe why a specific output was produced in a way a human can understand. Why the agent chose a particular tool, what intermediate reasoning steps occurred, which contextual factors influenced the decision — standard monitoring tools can't capture this.

Observability tells you "the agent called write_file." Explainability tells you "the agent considered three files and selected foo.ts because the task description mentioned updating the configuration."

The trap is confusing these two. You can have rich observability and still have no idea why an agent made a bad decision. If you've ever stared at agent traces and thought "I can see what happened but I don't understand why," that's the gap.

Standard observability pipelines record agent behavior. The structured "why it made that decision" log is something you need to design yourself. In Antigravity, treating these as two separate layers is the practical approach.

Building Stable AI Agents with Harness Engineering covers the stability side in depth. This article focuses on recording why stability breaks down.

The Core Design: DecisionLogger

The simplest useful decision log records each reasoning step in a structured way. The key design principle is separating "what happened" from "why it happened."

Three design decisions I've come to rely on:

Per-step recording — create an entry for each decision step, not just the overall task. This lets you pinpoint "step 3 was the problem" when something goes wrong.

Automatic confidence estimation — rather than asking the agent to self-rate its confidence (which tends toward optimism), estimate it from the richness of the reason field in tool inputs. External metrics over self-assessment.

Context factor recording — note which contextual elements seem to have influenced the decision. Over time, this reveals patterns like "confidence drops when files have non-standard names."

import json
import time
from datetime import datetime
from typing import Any
 
class DecisionLogger:
    """Records structured decision steps from AI agent execution"""
 
    def __init__(self, session_id: str):
        self.session_id = session_id
        self.entries = []
        self.start_time = time.time()
 
    def log_decision(
        self,
        step: int,
        reasoning: str,
        action: str,
        tool_name: str | None = None,
        tool_input: dict | None = None,
        confidence: str = "medium",  # high / medium / low
        context_factors: list[str] | None = None
    ) -> None:
        entry = {
            "session_id": self.session_id,
            "step": step,
            "timestamp": datetime.utcnow().isoformat(),
            "elapsed_sec": round(time.time() - self.start_time, 2),
            "reasoning": reasoning,
            "action": action,
            "tool": {
                "name": tool_name,
                "input": tool_input
            } if tool_name else None,
            "confidence": confidence,
            "context_factors": context_factors or []
        }
        self.entries.append(entry)
        print(f"[Step {step}] {action} → confidence: {confidence}")
 
    def export(self) -> str:
        return json.dumps(self.entries, ensure_ascii=False, indent=2)
 
    def summary(self) -> dict:
        tool_calls = [e for e in self.entries if e.get("tool")]
        confidence_dist = {}
        for e in self.entries:
            c = e["confidence"]
            confidence_dist[c] = confidence_dist.get(c, 0) + 1
        return {
            "total_steps": len(self.entries),
            "tool_calls": len(tool_calls),
            "tools_used": list({e["tool"]["name"] for e in tool_calls}),
            "confidence_distribution": confidence_dist,
            "elapsed_total_sec": round(time.time() - self.start_time, 2)
        }

Here's the agent loop that incorporates the logger. The key detail: the system prompt makes reasoning explicit, and confidence is estimated automatically from the richness of the reason field — not from asking the agent to rate itself.

from anthropic import Anthropic
 
client = Anthropic()
 
def run_agent_with_logging(task: str, session_id: str) -> dict:
    """Run an agent with decision logging enabled"""
    logger = DecisionLogger(session_id)
 
    tools = [
        {
            "name": "read_file",
            "description": "Read the contents of a file",
            "input_schema": {
                "type": "object",
                "properties": {
                    "path": {"type": "string", "description": "File path"}
                },
                "required": ["path"]
            }
        },
        {
            "name": "write_file",
            "description": "Write content to a file. Has side effects — reason is required",
            "input_schema": {
                "type": "object",
                "properties": {
                    "path": {"type": "string"},
                    "content": {"type": "string"},
                    "reason": {
                        "type": "string",
                        "description": "Explain why this file is being modified (50+ characters)"
                    }
                },
                "required": ["path", "content", "reason"]
            }
        }
    ]
 
    system = """You are a file operations agent.
 
Rules:
- Before calling any tool, articulate internally why you're choosing it
- For write_file, the reason field must contain a specific explanation (50+ characters)
- For ambiguous instructions, choose the most conservative (narrowest scope) interpretation
- When uncertain, use read_file to gather information before making destructive changes"""
 
    messages = [{"role": "user", "content": task}]
    step = 0
 
    while True:
        step += 1
        response = client.messages.create(
            model="claude-opus-4-6",
            max_tokens=4096,
            system=system,
            tools=tools,
            messages=messages
        )
 
        if response.stop_reason == "tool_use":
            for block in response.content:
                if block.type == "tool_use":
                    # Estimate confidence from reason field richness
                    reason_text = block.input.get("reason", "")
                    if len(reason_text) < 20:
                        confidence = "low"
                    elif len(reason_text) < 50:
                        confidence = "medium"
                    else:
                        confidence = "high"
 
                    logger.log_decision(
                        step=step,
                        reasoning=reason_text or "(no reason recorded)",
                        action=f"calling {block.name}",
                        tool_name=block.name,
                        tool_input=block.input,
                        confidence=confidence
                    )
 
                    tool_result = execute_tool(block.name, block.input)
                    messages.append({"role": "assistant", "content": response.content})
                    messages.append({
                        "role": "user",
                        "content": [{
                            "type": "tool_result",
                            "tool_use_id": block.id,
                            "content": str(tool_result)
                        }]
                    })
        else:
            logger.log_decision(
                step=step,
                reasoning="All steps completed",
                action="task complete",
                confidence="high"
            )
            break
 
        if step > 20:
            break
 
    return {
        "result": response.content[-1].text if response.content else "",
        "decision_log": logger.export(),
        "summary": logger.summary()
    }

Thank you for reading this far.

Continue Reading

What follows includes implementation code, benchmarks, and practical content we hope you'll find useful. This site runs without ads — server and development costs are supported entirely by members like you. If it's been helpful, we'd be truly grateful for your support.

WHAT YOU'LL LEARN
Working code for a two-layer JSONL + SQLite log store that stays manageable at solo-developer scale
A three-tier delegation framework using low_confidence_rate and uncertainty density to decide what agents may do unsupervised
Improvement-cycle designs proven over six months of operation, including a specificity score that catches perfunctory reason fields
Secure payment via Stripe · Cancel anytime

Unlock This Article

Get full access to the rest of this article. Buy once, read anytime. This site is ad-free — your support goes directly toward keeping it running.

or
Unlock all articles with Membership →
Share

Thank You for Reading

Antigravity Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.

  • Copy-paste ready implementation code
  • New advanced guides published daily
  • $5/mo or $10 for lifetime access
View Membership →

Related Articles

Agents & Manager2026-07-05
Protecting Your Agent Stack's Known-Good State with a Single Lockfile — Change-Budget Design for an Era of Simultaneously Moving Parts
When the IDE build, CLI, model, and dependencies all move at once, you can no longer tell which one caused a regression. Here is a change-budget design that pins your known-good state to one lockfile, with working code and operational logs.
Agents & Manager2026-05-29
Supervising Long-Running Antigravity Agents — Watchdog and Tiered Recovery
Eight weeks of running AdMob revenue optimization on Antigravity background agents revealed three quiet failure modes. Here is the watchdog plus tiered recovery design I landed on.
Agents & Manager2026-05-27
Record & Replay for Antigravity Agents — A Production Pattern to Reproduce Failures in 3 Minutes
How to deterministically replay a failed Antigravity Agent run offline, drawn from a month of running it across four production sites. Covers boundary recording, R2 + KV storage costs, PII masking, and a working TypeScript harness.
📚RECOMMENDED BOOKS
Build a Large Language Model (From Scratch)
Sebastian Raschka
LLM Dev
Prompt Engineering for LLMs
Berryman & Ziegler
Prompting
AI Engineering
Chip Huyen
AI Eng
* Contains affiliate links
See all →