How to Build AI Agent Audit Logs That Explain Every Action
An internal agent changes a payment record, sends a customer message, or disables an account. Later, the operator can see that a tool ran but cannot tell who requested it, which workflow version chose it, what policy allowed it, or whether a person approved it. A line that says tool completed does not answer those questions. AI agent audit logs have to connect the business request to the final outcome without copying raw prompts, credentials, or private documents into another sensitive database. This guide covers the event model, capture points, protection against edits, and the tests that prove an action can be reconstructed.
An audit record is not a diagnostic trace
A trace helps an engineer debug execution. It usually answers questions about duration, model calls, token usage, exceptions, and the path through a workflow. An audit record serves a different reader and a different retention policy. It must explain an accountable business action: who initiated it, what authority applied, what object was affected, which decision was made, and what happened.
Link the records instead of merging them. Put a trace identifier in the audit event, then keep detailed spans in the observability system, where access and retention can be tuned for diagnosis. The smaller accountability record belongs in the audit store. The OpenTelemetry generative AI agent span specification supplies a common vocabulary for agent operations and trace context. That vocabulary handles the diagnostic side of the link. It does not establish approval or business authorization by itself.
This separation also limits exposure. Debug telemetry may contain model input, output, or error details. An audit trail should favor stable identifiers and decision summaries. If an investigator needs deeper evidence, the trace ID points to the restricted diagnostic record while it still exists.
Begin with the questions an investigation must answer
Logging every field exposed by the agent framework produces noise quickly. Use a reconstruction exercise instead. Pick one consequential action, perhaps approving a refund or changing a supplier bank account, and work out what an investigator would need after someone disputes the result.
The investigation should be able to answer these questions:
- Which human, service, or scheduled job initiated the workflow?
- Which agent and workflow version processed the request?
- What business action was proposed, and which object was its target?
- Which authorization policy and approval step allowed or denied it?
- Which tool executed the action under which workload identity?
- Did the target system accept, reject, or partly apply the change?
- Which trace and request records contain deeper diagnostic evidence?
Each answer needs a field or a reliable link to another record. The exercise also exposes missing controls. If the system cannot identify the requesting user separately from the service identity that called the tool, adding more prompt text will not fix the gap.
The OWASP Logging Cheat Sheet recommends recording the actor, action, target, result status, time, and an interaction identifier for security events. It also warns against recording data that applications should exclude, including access tokens, passwords, and sensitive personal information. Those principles apply directly to an AI workflow, with a few extra fields for model and policy decisions.
Use one schema across every action boundary
Emit structured events. Free-text messages are difficult to search, validate, and compare after the wording changes. A compact event can describe an intent, an approval, a tool invocation, or a result while retaining the same envelope.
{
"event_id": "evt_01",
"occurred_at": "2026-07-30T05:42:11Z",
"event_type": "tool_action_completed",
"interaction_id": "int_4821",
"trace_id": "trc_7b2",
"requester": {
"subject_id": "usr_184",
"tenant_id": "org_27",
"auth_method": "work_sso"
},
"workflow": {
"agent_id": "invoice_review",
"version": "wf_31",
"policy_version": "pol_12"
},
"decision": {
"action": "invoice.mark_for_payment",
"target_type": "invoice",
"target_id": "inv_9031",
"authorization": "allowed",
"approval_id": "apr_611"
},
"execution": {
"tool": "accounts_payable_api",
"workload_identity": "svc_invoice_agent",
"attempt": 1,
"result": "succeeded",
"target_revision": "rev_88"
},
"data_classification": "confidential",
"redaction_profile": "audit_v3"
}
This contract is an example. Change the field names to fit the system, but keep the identities separate. subject_id names the person or system that asked for the work. workload_identity names the credential that called the downstream tool. agent_id and version identify the code and configuration that made the decision. One generic user field collapses different actors and makes later analysis unreliable.
Use stable action names such as invoice.mark_for_payment, not prose generated by the model. Record target identifiers rather than entire business objects. For a write, keep the target's resulting revision, transaction ID, or downstream receipt when the system provides one. That value helps an investigator prove that the action reached the system of record.
Capture events before and after the tool call
One completion event cannot describe a crash that happens between authorization and the downstream write. Capture the lifecycle at explicit boundaries:
- Record
action_proposedafter the workflow has normalized the model's suggestion into a deterministic action and target. - Record
authorization_decidedwhen the policy engine allows or denies that action. - Record
approval_recordedwhen a person approves, rejects, or edits a consequential request. - Record
tool_action_startedimmediately before the external call, including an attempt number and idempotency key reference. - Record
tool_action_completedortool_action_failedafter the target system responds.
Keep the audit writer outside the model's control. A model may suggest an action, but deterministic application code chooses the event type and fields. Prompt content must not be able to change the logging policy.
For operations that can partly succeed, add an explicit state rather than forcing every result into success or failure. A workflow might create a record but fail to attach a document. Record the completed side effect, the failed step, and the recovery state as separate facts. Do not overwrite the first event when a retry succeeds. Append another attempt so the sequence remains visible.
Redact at collection time
An audit database is often retained longer and accessed by more operational roles than a model trace. Copying prompts and outputs into it creates a second store for customer text, employee data, secrets, and retrieved documents. Avoid that default.
The OWASP logging guidance lists data that should normally be removed, masked, sanitized, hashed, or encrypted before it reaches a log. Apply that decision before serialization. A downstream cleanup job is too late if a credential has already entered the log pipeline, backups, and alerting system.
Prefer these representations:
- Store internal subject and object IDs instead of names, email addresses, or full records.
- Store a policy decision code instead of the policy input document.
- Store the prompt template version and input classification instead of raw prompt text.
- Store a content hash only when it has a defined verification use and cannot become a practical identifier for sensitive data.
- Store the downstream transaction or revision identifier instead of the full response body.
Redaction should be deterministic and versioned. Put the redaction profile name in each event so investigators know which rules applied at collection time. Test the profile with synthetic tokens, personal data, and prompt-injection text. The audit writer must treat all model-produced strings as untrusted input and encode them before storage or display.
Protect the record from the workflow it observes
An agent that can edit or delete its own audit events can hide a bad action, whether the cause is a compromised tool, a software defect, or an overly broad service account. Send events to a separate append-oriented sink through a narrowly scoped writer identity. The workflow should be able to create an event, but not update prior events or change retention settings.
Limit read access too. Audit records reveal user IDs, workflow structure, tool names, policy outcomes, and business-object references. Security staff may need broad search, while a product support role may need access only to one tenant or interaction. Log all administrative searches and exports.
OWASP's guidance covers verification, protection, monitoring, and disposal throughout the logging lifecycle. The NIST AI RMF Playbook organizes AI risk work around Govern, Map, Measure, and Manage actions. Put those ideas into an operating rule: name the audit-system owner, specify who reviews each event class, and define where findings go. A store that nobody reviews cannot provide much accountability.
Set retention by purpose and data class. The audit event may need to outlive its detailed trace, but it does not need to live forever. Document the retention period, legal basis where applicable, deletion process, and any hold mechanism. Keep the policy outside agent configuration so a prompt or workflow release cannot change it.
A log that stores no content cannot prove content
Minimizing what you record has a cost: retention duties can pull the other way. Article 19 of the EU AI Act requires providers of high-risk AI systems to keep the automatically generated logs for a period appropriate to the intended purpose, of at least six months unless other Union or national law says otherwise (Article 19). A content-free log, which records identifiers and field names but not values, satisfies a privacy review and may still fall short of what a regulator or an incident review needs. Decide per system which fields a retention duty actually requires, and put a short ceiling on everything else.
The consequence is that the log alone cannot answer "what exactly did the agent send?" That answer has to come from somewhere else: the target system's own record, the revision or receipt ID stored in the event, or a content digest you can compare against the target. If your regulator or customer will ask what was written, decide now which of those you rely on. Deciding after the dispute is too late, because a content-free log has nothing to recover.
Bind the approval to what the approver saw and to what ran
An approval event that says "approved" proves a click. It does not prove which content was approved. If the approval card shows a clipped preview, compute a SHA-256 digest over the canonical JSON of all stored arguments plus the resolved preview, store it with the approval. The approver never sees the digest. It binds the approval to the whole action, including text behind a "show more", not only the visible part.
Extend this across the lifecycle. Store the digest of the normalized action at proposal time, store it again at approval time, and store it a third time on the execution event. Any difference between the three is a finding. It catches the case where a model or a retry quietly changes an argument between approval and execution.
Detect edits, and anchor the chain outside the writer
Restricting the writer identity prevents the workflow from editing history. It does not tell you whether someone with storage access did. For that, make the record tamper-evident. A hash chain, where each event includes the hash of the previous one, shows that any change breaks every later hash. The weakness is that a person who can rewrite the whole table can recompute the whole chain, so the chain needs an anchor outside the system that writes it.
AWS CloudTrail's log file integrity validation is a concrete design to copy. It hashes each delivered log file with SHA-256, delivers an hourly digest file that lists those hashes, signs each digest with SHA-256 with RSA, and includes the signature of the previous digest in the next one. Digest files go to a separate folder from the logs, so different permissions can apply to each. The same ideas transfer to an agent audit store: a small signed checkpoint written on a schedule to a location the application cannot modify, containing the latest sequence number and chain hash. Verification then needs only the checkpoints.
Keep personal data out of the chained records. Chained records cannot be edited, so a deletion request or a retention limit then cannot be honoured on them. Store pseudonymous subject IDs in the chain and keep the mapping from ID to person in a separate table that can be deleted. Ask your counsel what you are legally required to retain, since the answer differs by jurisdiction and record type.
Walk through one invoice approval
Consider an invoice workflow that extracts payment details, checks a purchase order, asks an employee to approve the payment, and calls an accounts-payable API.
The first audit event identifies the employee who submitted the invoice, the tenant, and the workflow version. The agent proposes invoice.mark_for_payment against inv_9031. Application code checks whether the invoice value and supplier status require human approval. The policy event records the decision code and policy version without copying the invoice into the log.
The approver sees the normalized action, target, amount from the source system, and supplier identity. Their decision produces an approval ID tied to their own authenticated subject. The tool call then uses a dedicated workload identity. A completion event records the target system's transaction and resulting revision.
If the tool times out, the workflow records a failed attempt without claiming that no write occurred. It queries the target system by idempotency key or transaction reference before retrying. The next event records either the discovered prior success or a second attempt. An investigator can now follow the interaction ID from submission through policy, approval, execution, and recovery.
This trail also makes a denied action useful. If the agent proposes a target outside the requester's tenant, record the proposed stable identifiers, the denial code, and the workflow version. Do not call the tool. Repeated denials for the same agent version may indicate a mapping defect or an attack. The OWASP agentic AI threats and mitigations resource provides a threat-modeling frame for systems where generative AI can act through autonomous components and business tools.
Test reconstruction, not just event delivery
Delivery is only the first check. The useful test is whether someone can reconstruct a known scenario from the stored records.
Create a synthetic workflow run that includes an approval, one failed tool attempt, and a successful recovery. Give an investigator only the initial request ID. They should be able to identify the requester, tenant, workflow and policy versions, proposed action, approver, workload identity, target, both attempts, final result, and linked trace. If any answer depends on searching raw message text, the schema needs another stable field.
Run four additional checks:
- Submit synthetic credentials and personal data through every model-controlled field, then prove that none appears in the audit sink.
- Attempt to update and delete an existing event with the workflow's writer identity. Both operations should fail.
- Break the telemetry service and confirm the business action follows the documented policy. For a high-risk action, that may mean stopping rather than operating without an audit record.
- Compare the audit completion event with the system-of-record revision or transaction receipt. Detect any action that reached the target without a matching event.
Alert on missing sequences, not only explicit errors. An action_proposed event with no authorization decision may mean a crash. A started tool action with no completion needs reconciliation. An approval tied to a different tenant or target should be rejected before execution and flagged for review.
Common mistakes that weaken the trail
Logging the full chain of thought adds sensitive text without producing stable, testable decisions. Record the normalized action, policy outcome, evidence references, and result. Using one service identity for every field collapses the requester, approver, agent version, and executing workload into a single actor. Treating an immutable store as a substitute for minimization leaves a secret copied into the wrong place, protected perfectly. Redact before the event leaves the application boundary. Finally, a schema that exists only in documentation drifts. Define it as a versioned contract, validate required fields at the writer, reject unknown event types, and include audit-event tests in release checks.
Put the first action under audit
Pick one consequential tool action. Map the seven investigation questions to structured fields, add lifecycle events around authorization and execution, and run the failed-attempt reconstruction with an engineer who did not build the workflow. If they cannot show who authorized the action and what the target system did, fix the schema before adding another tool.
References
- OWASP Logging Cheat Sheet supports the event attributes, interaction identifiers, data-exclusion rules, verification steps, and log-protection guidance.
- AWS CloudTrail log file integrity validation supports the hashed, signed, chained digest-file design used as a model for tamper evidence.
- OWASP Agentic AI threats and mitigations supports threat modeling for generative AI connected to autonomous components and business tools.
- NIST AI RMF Playbook supports the Govern, Map, Measure, and Manage framing for AI risk ownership and operational controls.
- OpenTelemetry generative AI agent spans supports the trace context and semantic vocabulary used to link agent operations with diagnostic telemetry.
- EU AI Act, Article 19: Automatically generated logs: providers of high-risk systems keep logs for at least six months unless other law provides otherwise.