◉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.

antigravity461agents144explainabilitylogging3production71design-patterns

✦ Premium Article

Since I started building apps on my own, 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.

What I want to set down here are the design patterns I use for decision logging in Antigravity agents, and how I turn that logging into a quality improvement cycle. After years of running several apps on my own, I can say with some 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 $15 for lifetime access
View Membership →

Related Articles

◈ Agents & Manager2026-05-27
Record & Replay for Antigravity Agents
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.
◈ Agents & Manager2026-05-11
Canary Deployment with Auto-Rollback for AI Agents — Protecting Production with Antigravity and Burn-Rate SLOs
A practical playbook for shipping new AI agent versions through canary deployment on Antigravity, with automatic rollback driven by burn-rate SLOs. Includes a lightweight setup that solo developers can sustain.
◈ Agents & Manager2026-05-10
Giving Your Antigravity AI Agents a 'Time Budget' — A Production Scheduling Design That Unifies Timeouts, Priorities, and Deadlines
Your AI agent's response time creeps up, users drop off, and unexpected costs pile on. Here is how I give Antigravity agents a single 'Time Budget' object that unifies timeouts, priority, and deadlines, plus the traps I hit after half a year in production.
📚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