← Back to insights
Field note6 min readAgentify

Building Audit Trails for AI Agent Decisions: A Technical Guide

Your AI agent just approved a ₹50 lakh purchase order. A week later, someone asks why. You open the logs and find a timestamp, a model name, and the word "approved."

Your AI agent just approved a ₹50 lakh purchase order. A week later, someone asks why. You open the logs and find a timestamp, a model name, and the word "approved."

That's not an audit trail. That's a liability.


I used to think of logging as a checkbox — something you bolt on after the core logic works. I was wrong. For AI agents, audit trails are architecture. They're the difference between "our agent made a mistake and we fixed it in 20 minutes" and "our agent made a mistake and we spent three days guessing what happened."

ISACA's 2025 guidance on auditing agentic AI puts it bluntly: every action taken by an AI system should be logged with who initiated it — human, application, or AI agent — and the reason for it.

Here's how to build it properly.


Why agent logs ≠ application logs

Regular application logs capture request/response pairs, error codes, and latency. Agent audit trails are a fundamentally different animal, for three reasons:

Same input, different output. LLMs are non-deterministic. The same prompt can produce different responses depending on temperature, context, and the phase of the moon (kidding, but only slightly). You need to capture the full context — system prompt, user input, retrieved documents, model version, temperature — to understand why a particular decision happened.

Agents don't just respond, they reason. A purchase approval agent might retrieve a budget document, check a policy database, run a calculation, and then make a decision. If you only log the final output, you've lost the entire decision chain. Galileo's compliance guide calls this "decision lineage" — and without it, you can't debug, audit, or improve.

Agents act on behalf of others. When an agent takes an action, it's usually doing so on behalf of a user or another system. LoginRadius's engineering team points out that audit systems need to track delegation — who authorised the agent, what scope it was given, and whether any permission escalation happened.


The five layers you need to capture

Fast.io's audit trail guide has the best mental model I've found. They break it into five layers, and they point out that most observability tools only cover layers 2 and 5, leaving massive gaps:

1. Identity — Who acted? Agent ID, model version, the user it's acting for, auth token.

2. Input — What triggered it? User prompt, webhook, scheduled event, file change.

3. Reasoning — Why did it decide this? Chain-of-thought, retrieved documents, tool selection, confidence scores. This is the layer most teams miss.

4. Action — What did it actually do? API calls, database writes, files created, notifications sent. Include the full request/response payloads.

5. Outcome — What happened? Success or failure, downstream effects, cost.

Fast.io makes an important point: the most common gap is between Reasoning and Action. An agent might log its chain-of-thought but not the actual API call it made. So you know why it decided to send a notification, but you can't verify that it actually sent one. That's a compliance nightmare.


What a good audit record looks like

Here's the structure I use. Every single agent invocation gets one of these:

{
  "trace_id": "abc-123-def-456",
  "timestamp": "2026-01-15T10:30:00Z",
  "agent": {
    "id": "purchase-approval-agent-v2",
    "model": "gpt-4o-2025-08-06",
    "temperature": 0.1,
    "system_prompt_hash": "sha256:a1b2c3..."
  },
  "identity": {
    "triggered_by": "user:priya@company.com",
    "auth_method": "OAuth_google",
    "session_id": "sess-456"
  },
  "input": {
    "user_prompt": "Approve PO-2026-0142 for server hardware",
    "retrieved_context": [
      {"doc_id": "policy-procurement-v3", "chunk_id": "chunk-42", "relevance": 0.92},
      {"doc_id": "budget-q1-2026", "chunk_id": "chunk-17", "relevance": 0.87}
    ]
  },
  "reasoning": {
    "chain_of_thought": "PO amount ₹50L within Q1 budget (₹1.2Cr remaining). Policy requires VP approval above ₹25L. Routing to VP Engineering.",
    "tool_calls": [
      {"tool": "check_budget", "input": {"dept": "engineering"}, "output": {"remaining": 12000000}},
      {"tool": "get_approval_chain", "input": {"amount": 5000000}, "output": {"approver": "vp_engineering"}}
    ]
  },
  "output": {
    "decision": "route_for_approval",
    "response": "PO routed to VP Engineering for approval.",
    "actions_taken": [{"action": "send_notification", "target": "vp@company.com", "status": "sent"}]
  },
  "guardrails": {
    "pii_detected": false,
    "policy_violations": [],
    "guardrail_triggers": []
  },
  "cost": {
    "tokens_input": 1847,
    "tokens_output": 312,
    "cost_usd": 0.067,
    "latency_ms": 3420
  }
}

Notice what's captured: not just what the agent said, but what documents it read, what tools it called, what reasoning it followed, and what actions it took. That's what makes this auditable.


Use OpenTelemetry traces — seriously

OpenTelemetry's GenAI observability project is building standardised semantic conventions for agent tracing. If you're not already using OTel, now's the time.

The core idea: treat each agent invocation as a trace, with individual steps (LLM calls, tool invocations, retrieval operations) as spans within that trace. Spans form a hierarchy, so you can visualise the full decision tree of any action.

This matters because it lets you answer questions like: "Why did this request take 12 seconds?" (the retrieval span took 8 seconds), "Why did the agent give a wrong answer?" (the wrong document was retrieved in span 3), "How much did this invocation cost?" (sum the token counts across all LLM spans).

Tetrate's MCP audit logging guide makes a critical point: never sample away audit logs for high-stakes decisions. In regular observability, sampling 10% of traces is fine. For agent audit trails, regulated operations should be traced at 100%.


Where to store all this

Agent audit data is high-volume. A single invocation can generate several kilobytes of structured logs. At scale, this adds up fast.

Galileo's compliance guide notes that financial regulators treat missing traces as a books-and-records violation — you need to retain this data for years, not days.

Here's the tiered storage approach I use:

Hot (0–30 days): Elasticsearch or ClickHouse. Full-fidelity, queryable by any field. This is your debugging and monitoring window.

Warm (30 days – 1 year): Compressed object storage (S3/GCS) in Parquet format. Still queryable via Athena or BigQuery, but much cheaper. Monthly compliance reports pull from here.

Cold (1–7+ years): Glacier-class storage. Rarely accessed, but must be available for regulatory requests and legal holds.

And make it immutable. Audit logs you can edit aren't audit logs. Use S3 Object Lock, WORM storage, or hash chains where each record includes a hash of the previous one. If anyone modifies a record, the chain breaks and you know.


Beyond compliance: why audit trails make you faster

Compliance is the floor. The real value of good audit trails is operational:

Debugging goes from hours to minutes. When an agent gives a wrong answer, you trace back through retrieval, reasoning, and tool calls. You don't guess — you see.

You can optimise costs with precision. With token counts per invocation, you know which prompts are expensive, which retrievals are wasteful, and where caching would save the most.

You catch drift before users do. Track guardrail trigger rates and hallucination scores over time. When they trend up, you know something changed — a new model version, a data quality issue, or a shift in user behaviour.

Production data becomes your eval dataset. Extract (input, output, human_feedback) triples from audit trails and use them to improve prompts, retrieval, and evaluator models. Your best test data comes from real usage.


Start here

If you're building from scratch, here's the order:

Week 1: Instrument every LLM call with input, output, model version, and latency. Assign trace IDs to every request. Log to structured JSON, ship to Elasticsearch.

Week 2–3: Add tool call logging, retrieved document IDs, reasoning steps, and guardrail results.

Week 3–4: Implement immutability (hash chains or write-once storage). Set up retention policies. Build export for audit queries.

Ongoing: Cost dashboards, drift detection alerts, eval dataset extraction.

The earlier you do this, the easier everything else gets. Debugging, evaluation, compliance, cost optimisation — they all depend on having a clean, complete record of what your agent did and why.