An audit trail is the record that answers, weeks later and under unfriendly questioning, three plain questions: what did the agent do, why was it allowed to, and who is answerable for it. Screenshots and chat transcripts do not answer those questions. A structured, tamper-evident log of actions and decisions does. This tutorial builds one from the ground up: what counts as an auditable event, the fields every record needs, how to store the trail so it cannot be quietly rewritten, and how to review it while review still matters.
What an audit trail is — and is not¶
It is not the model's reasoning trace. Reasoning is verbose, often wrong about its own motives, and useless as evidence of what actually happened. It is not general application logging either — debug logs record what the code did internally, rotate quickly, and mix a thousand unimportant lines around every significant one. An audit trail is narrower and more deliberate: one durable record per action the agent took in the world, plus one per decision that allowed or blocked it. If a record cannot help reconstruct a specific action, it probably does not belong here; if an action has no record, the trail has a hole exactly where an investigator will look first.
Step 1: Define an auditable event¶
Log at the boundary where the agent affects something outside itself:
- Every tool call that reads sensitive data or changes any state.
- Every message sent, file written or deleted, permission changed, payment made.
- Every approval requested, granted, denied, or expired.
- Every block: the actions the system refused, with the reason.
- Every policy or configuration change that alters what the agent may do next.
Blocks and denials are first-class entries, not errors to be swallowed. A trail that only shows successes will claim the agent never tried anything it shouldn't — which is precisely the claim nobody will believe after an incident.
Step 2: Give every record the same fields¶
Consistency is what makes a trail reviewable. Every record, whatever the action, should carry:
- Timestamp in UTC, from a clock the agent does not control.
- Agent identity — a stable, named identity, plus the version or configuration it ran under.
- Acting for whom — the person or system whose task the agent was executing.
- Action and target — what was attempted, and the specific record, recipient, or resource it touched.
- Inputs in summary — the parameters or payload reference. Store a pointer or a hash where the full payload is large or sensitive; never log secrets or credentials themselves.
- Decision — allowed, blocked, or approved, with the policy rule or approval request that produced it.
- Outcome — succeeded, failed, or partially applied, with the resulting state or error.
- Cost — spend, tokens, or external calls consumed, where the action has any.
A record in that shape looks like this:
{
"at": "2026-10-25T14:03:11Z",
"agent": "agent:ops/summarizer",
"acting_for": "ops-team",
"action": "email.send",
"target": "customer-success-list@example.com",
"decision": "approved",
"approval_ref": "appr-2026-10-25-014",
"outcome": "sent",
"cost": {"external_calls": 1, "spend_usd": 0},
"prev_hash": "9f2c…",
"hash": "b71a…"
}
Step 3: Make the trail tamper-evident¶
A log the agent — or anyone — can edit after the fact proves nothing. Three measures, in increasing order of strength:
- Append-only storage. Write records to a store where the agent's credentials can create entries but never modify or delete them. Separation matters more than sophistication: the writer of the action must not be the keeper of the record.
- Hash chaining. Include the previous record's hash in each new record, as in the example above. Editing or removing any past entry breaks every hash after it, so tampering announces itself.
- Periodic external anchoring. On a schedule, copy a digest of the trail somewhere outside the system's control entirely. This bounds the window in which even a privileged insider could rewrite history undetected.
Also log the trail's own gaps honestly: if logging was down for an hour, that outage gets a record when service resumes. A declared gap is survivable in review; a silent one is not.
Step 4: Decide retention before you need it¶
Keep audit records at least as long as the decisions they document can be questioned — by a customer dispute, an internal review, or a regulator. Some regimes set floors: deployers of high-risk AI systems under the EU AI Act, for example, must keep the logs under their control for at least six months, and contracts with enterprise customers often demand more. Set the period per data class, write it into policy, and make deletion of audit records itself an audited, approval-gated action. Retaining everything forever is its own liability where records contain personal data — the point of a retention policy is that the decision was made deliberately, in advance, not improvised after a request arrives.
Step 5: Review on a cadence, not on a crisis¶
A trail nobody reads is a write-only expense. Make review cheap and routine:
- Searchable by the obvious keys — agent, person affected, target, action type, time window. If answering "what did this agent do last Tuesday" takes an engineer an afternoon, the schema is wrong.
- A short recurring review — scan the week's blocks, denials, and high-cost actions. Denials clustering on one rule usually means the rule or the agent's scope needs adjusting, not that the agent needs scolding.
- Sampling of the routine — pull a handful of ordinary approved actions and verify the record alone lets a reviewer reconstruct them. If it doesn't, add the missing field now, not during an incident.
The mistakes that hollow out a trail¶
Four recur. Logging outcomes without decisions — you know the email was sent but not who or what allowed it. Letting the agent hold write access to its own logs. Logging payloads wholesale, secrets included, which turns the trail into a second breach waiting to happen. And over-claiming: a record should state what the system verified, not what it hopes — "payload hash recorded" is a claim the trail can back; "message was appropriate" is not something a log can know. Build the trail to declare exactly what it proves, keep it out of the agent's reach, and read it while times are calm. When something goes wrong, it becomes the difference between reconstructing an incident and guessing at one.