Skip to main content
INS // Insights

AI Agent Audit Logging: Building SOC 2-Ready Evidence

Updated August 2026 · 7 min read

An AI agent that calls tools, reads data, and takes actions on a company's behalf is, from a compliance standpoint, a new class of identity with the same audit-trail expectations as a human user or a service account — and most production AI agent deployments have far weaker logging than either, because the logging that exists was built for debugging the agent's behavior, not for satisfying an auditor asking who did what and why.

Debug Logs and Audit Evidence Are Different Things

Most AI agent frameworks log liberally by default — prompts, model responses, tool call arguments, intermediate reasoning steps. This is genuinely useful for debugging agent behavior and is not, on its own, audit evidence, for a specific reason: it's typically mutable, often stored in an application log aggregator with standard retention and access controls, not treated as an immutable compliance artifact with its own access restrictions and retention policy.

The distinction that matters for SOC 2 is whether the log can answer, with confidence it hasn't been altered: which identity (the specific agent instance, the user who triggered it, or both) took which action, against which system, with what authorization, and what was the outcome.

# agent_audit_logger.py — structured, append-only audit record per tool call
import hashlib
import json
import boto3
from datetime import datetime

dynamodb = boto3.resource("dynamodb")
audit_table = dynamodb.Table("agent-audit-log")

def log_tool_call(agent_id: str, triggered_by_user: str, tool_name: str,
                   arguments: dict, authorized_scope: list[str], result: str):
    record = {
        "agent_id": agent_id,
        "triggered_by_user": triggered_by_user,
        "tool_name": tool_name,
        "arguments": arguments,
        "authorized_scope": authorized_scope,
        "in_scope": tool_name in authorized_scope,
        "result_summary": result[:500],
        "timestamp": datetime.utcnow().isoformat(),
    }
    # integrity hash lets a later review detect if the record was tampered with
    record["integrity_hash"] = hashlib.sha256(
        json.dumps(record, sort_keys=True).encode()
    ).hexdigest()

    audit_table.put_item(Item=record)
    return record

The in_scope field is doing real compliance work here — it's checking and recording, at the moment of the call, whether the tool the agent invoked was actually within its authorized permission set, not just logging that a call happened. This is the difference between a log that shows activity and a log that demonstrates the least-privilege control was actually enforced, which is what a reviewer testing an AI agent's access controls actually wants to see.

What an AI Agent Audit Trail Needs to Answer

A complete audit trail entry for an agent action should support reconstructing five things without ambiguity: the specific agent instance and version that acted, the human user or system event that triggered the action (agents acting fully autonomously on a schedule still need this — "scheduled trigger" is a valid answer, "unknown" is not), the exact tool and parameters invoked, whether that invocation was within the agent's authorized scope at the time, and the outcome, including whether it required human approval and whether that approval was actually obtained before the action executed.

# human_in_the_loop_gate.py — logs the approval decision as part of the audit chain
def request_human_approval(agent_id: str, proposed_action: dict, approver_id: str) -> bool:
    approval_record = {
        "agent_id": agent_id,
        "proposed_action": proposed_action,
        "approver_id": approver_id,
        "decision": None,  # populated when the approver responds
        "requested_at": datetime.utcnow().isoformat(),
    }
    # ... present to approver via Slack/dashboard, block agent execution until response ...
    return approval_record  # decision field populated before returning to caller

For any action class deemed high-risk enough to require human-in-the-loop approval — a production database write, an external email send, a financial transaction — the approval request and decision need to be part of the same immutable audit chain as the action itself, not a separate, disconnected notification that leaves no durable link between "approval was requested" and "approval was granted before execution."

Retention and Access Control for the Log Itself

An audit log that anyone with database access can modify isn't meaningfully more trustworthy than no log at all for compliance purposes. The audit table needs its own restricted write access (ideally append-only, enforced at the IAM policy level with no update/delete permission granted to any application role), and a retention policy that matches whatever compliance framework's requirements apply — commonly a minimum of one year for SOC 2-relevant logs, longer for some regulated industries.

Frequently Asked Questions

Does every AI agent tool call need to go through human approval?

No — human-in-the-loop approval should be reserved for genuinely high-risk action classes (irreversible actions, financial transactions, production data writes, external communications), not applied universally, which would defeat the purpose of agent automation. Lower-risk, reversible, or read-only actions can execute autonomously with full logging but no approval gate.

How is this different from standard application logging?

Standard application logs are typically mutable, stored with standard access controls, and optimized for debugging rather than compliance. Audit-grade logging for AI agents needs integrity protection (tamper-evidence), restricted write access, explicit scope-checking recorded at time of action, and a retention policy aligned to compliance requirements rather than typical log-retention defaults.

What happens if an agent attempts an out-of-scope tool call?

The scope check should block the call before it executes, not just log it as a finding after the fact — the in_scope flag in the audit record should reflect a check performed and enforced at call time, with the blocked attempt still logged as its own audit event for review, since a blocked attempt is itself a useful signal about whether the agent's permission scoping needs adjustment.

Do we need a separate audit system for each AI agent, or can this be centralized?

Centralizing audit logging across all agents into one structured store is strongly preferable — it makes cross-agent review and anomaly detection possible, and it avoids re-solving the integrity, retention, and access-control problem separately for every agent deployment.

Is this level of logging required by SOC 2 explicitly, or is it inferred?

SOC 2's criteria don't name "AI agents" specifically — they're technology-neutral. The evidence requirements (CC6 access controls, CC7 monitoring, CC8 change management) apply to any system component including AI agents, and an auditor evaluating an AI-agent-integrated environment will expect the same class of access control and monitoring evidence they'd expect from any other automated system with production access.


Rutagon designs AI agent permission models and audit logging that satisfy SOC 2 evidence requirements — scoped tools, human-in-the-loop gates, and tamper-evident audit trails.

Discuss securing your AI agent build → rutagon.com/contact | 907-841-8407 | contact@rutagon.com

Related reading: AI Agent Permissions: Best Practices for Production · AI Agent Least Privilege Controls · Security Automation Capability

External reference: NIST AI Risk Management Framework