PromptABCD
FeaturesLearnHow it worksUse casesFAQGuideBlogContext Blocks
Sign inGet started free
Sign inSign up
PromptABCD

A calm home for your best AI prompts. Save them once, find them in seconds, reuse them forever.

Product

  • Features
  • Chrome Extension
  • Free Courses
  • How it works
  • Use cases
  • Blog
  • Context Blocks
  • Export Anywhere
  • FAQ

Resources

  • User guide
  • Learn prompting
  • Sign in
  • Get started free

© 2026 PromptABCD. All rights reserved.

Privacy PolicyTerms and Conditions
Home/Blog/AI Harness/Building an Audit Log Into Your Harness
AI Harness

Building an Audit Log Into Your Harness

Can you reconstruct exactly what your agent did, tamper-proof? An agent harness audit log turns 'we think it did X' into an unforgeable, queryable record.

September 8, 2026·8 min read
ShareShare
⚡Featured Prompt— copy and use right now
import json, time, uuid

class AuditLog:
    def __init__(self, sink):
        self.sink = sink              # append-only store (file, DB, S3)

    def record(self, run_id, event_type, **fields):
        entry = {
            "id": str(uuid.uuid4()),
            "run_id": run_id,
            "ts": time.time(),
            "event": event_type,
            **fields,
        }
        self.sink.append(json.dumps(entry, sort_keys=True))
        return entry

When something goes wrong with your agent — a bad action, a leaked record, an angry customer — can you reconstruct exactly what happened, step by step, in a way you'd trust in front of a regulator or a court? For most teams the honest answer is no, and they find that out at the worst possible moment. An agent harness audit log is what turns "we think the agent did X" into "here is the exact, tamper-evident record of every decision it made and why." This guide gives you a working one you can drop in today, then explains how to make it something you can actually rely on.

The question at the top isn't rhetorical. Autonomous agents take actions with real consequences, and "the model decided to" is not an acceptable answer when those consequences land on a person. An audit log is the difference between accountability and hand-waving.

Quick-Start (Copy This Right Now)

Here's a structured, append-only audit logger. Every meaningful event — model call, tool call, permission decision, approval — becomes one immutable record.

hljs python
[object Object], json, time, uuid

,[object Object], ,[object Object],:
    ,[object Object], ,[object Object],(,[object Object],):
        ,[object Object],.sink = sink              ,[object Object],

    ,[object Object], ,[object Object],(,[object Object],):
        entry = {
            ,[object Object],: ,[object Object],(uuid.uuid4()),
            ,[object Object],: run_id,
            ,[object Object],: time.time(),
            ,[object Object],: event_type,
            **fields,
        }
        ,[object Object],.sink.append(json.dumps(entry, sort_keys=,[object Object],))
        ,[object Object], entry

What this does: Writes one structured JSON record per event, stamped with a unique ID, the run it belongs to, and a timestamp, to an append-only sink. Structured means you can query it later ("show every

tool_call
that touched
delete
"); append-only means nobody edits history after the fact. This alone puts you ahead of most agent deployments.

⚡ Pro tip: Log the inputs and the decision, never a summary written after the fact. "Agent deleted 3 records" is a story; "tool_call

delete_records
args={count: 3, table: 'temp'} decision=allow rule=
under_ten
" is evidence. The evidence version is queryable, attributable, and doesn't rely on anyone's after-the-fact interpretation of what happened.

What Belongs in an Agent Harness Audit Log

Three choices shape how useful your audit log ends up being, and they all come down to deciding what an agent harness audit log is actually for: reconstructing intent and action after the fact, not just recording that something occurred.

What you log. At minimum: every model request and response (or a hash of them if they're large), every tool call with its arguments and result, every permission and approval decision with the rule and human involved, and every run's start and end. If an event changed what the agent did, it belongs in the log. When in doubt, log it — storage is cheap and missing evidence is expensive.

What you never log. Secrets, raw credentials, and full personal data. This is where the audit log and secrets management intersect: the log is one of the most-read stores in your system, so a credential in it is a credential leaked. Run the same scrubber you use for the model context over audit entries too.

Where it lives. The sink must be append-only and separately permissioned from the agent itself. If the agent's own credentials can rewrite the audit log, the log proves nothing — a compromised agent would simply erase its tracks. Write access to history is the one thing the agent must never have.

Step-by-Step: Making the Log Tamper-Evident

A log that can be silently edited isn't an audit log; it's a suggestion. The fix is a hash chain — each entry includes the hash of the previous one, so any alteration anywhere breaks every hash after it.

Step one: Have each entry carry the previous entry's hash.

hljs python
[object Object], hashlib

,[object Object], ,[object Object],(,[object Object],):
    entry = {,[object Object],: run_id, ,[object Object],: time.time(),
             ,[object Object],: event_type, ,[object Object],: prev_hash, **fields}
    body = json.dumps(entry, sort_keys=,[object Object],)
    entry[,[object Object],] = hashlib.sha256((prev_hash + body).encode()).hexdigest()
    ,[object Object],.sink.append(json.dumps(entry, sort_keys=,[object Object],))
    ,[object Object], entry[,[object Object],]

What this does: Computes each entry's hash from the previous hash plus its own contents, forming a chain. Change any past entry and its hash changes, which breaks the

prev
link in the next entry, and so on to the end. You can't quietly rewrite history without rewriting everything after it — which a verifier immediately notices.

Step two: Verify the chain on read.

hljs python
[object Object], ,[object Object],(,[object Object],):
    prev = ,[object Object],
    ,[object Object], e ,[object Object], entries:
        body = {k: v ,[object Object], k, v ,[object Object], e.items() ,[object Object], k != ,[object Object],}
        expect = hashlib.sha256((prev + json.dumps(body, sort_keys=,[object Object],)).encode()).hexdigest()
        ,[object Object], e[,[object Object],] != expect:
            ,[object Object], TamperError(,[object Object],)
        prev = e[,[object Object],]

What this does: Walks the chain recomputing each hash and raising the moment one doesn't match. Running this before you trust the log — during an investigation, or on a schedule — tells you whether the record is intact. A clean verification is what lets you stand behind the log's contents.

⚡ Pro tip: Periodically write the latest chain hash somewhere you don't control — a managed timestamping service, a separate append-only store, even a git commit. That external anchor means even someone who could rewrite your entire log can't match the hash you published elsewhere, which closes the one gap a self-contained chain leaves open.

Pro-Level Variations

For high-volume agents, logging every full model request is expensive. Log a content hash of the request and response plus the metadata, and store the full bodies separately with a lifecycle policy. You keep the ability to prove what was sent without paying to keep every token forever.

For multi-service systems, thread a single correlation ID through every service an agent run touches, and stamp it on every audit entry. When a run spans your harness, a tool service, and a downstream API, that ID is what stitches the fragments into one coherent timeline.

For compliance-heavy domains, the audit log doubles as your data-processing record. Structure entries so you can answer "every action taken on subject X" with a query, because eventually someone will ask exactly that, and grepping unstructured logs at 2 a.m. is not where you want to be.

Troubleshooting Common Issues

The log slows down every run. Synchronous writes to a remote sink add latency to the critical path. Buffer entries and flush asynchronously — but flush before the action they describe completes for anything irreversible, so you never take an action you failed to record.

Entries arrive out of order. With async writes and concurrent runs, timestamps alone won't order events within a run. Add a per-run sequence number so you can reconstruct the exact order regardless of when each write landed.

The chain breaks and you don't know why. Usually it's non-deterministic serialization — a dict that serializes in different key orders. Always serialize with

sort_keys=True
(as above) so the same logical entry always produces the same bytes and the same hash.

⚠️ Common mistake: Building the audit log as an afterthought that the agent writes to with its own permissions. If the agent can write the log, a compromised or buggy agent can also corrupt it, and a corruptible audit log gives you false confidence — arguably worse than none, because you'll trust it when you shouldn't. The log must be write-once from the agent's side and editable by no one.

Making the Log Answer Real Questions

A log you can't query is just a very long file. The point of structuring every entry is that months later you can ask precise questions and get precise answers — and the questions that come up are surprisingly consistent across teams.

"What did run

abc-123
do, in order?" is the debugging question, answered by filtering on
run_id
and sorting by sequence. "Every action taken against customer 5589?" is the compliance and data-subject question, which is why you stamp subject identifiers on entries that touch a person. "Which tool calls were denied in the last hour?" is the security question, and a spike in the answer is often your first sign of an attack in progress. Design your entry fields around those three questions and the log earns its keep the first time any of them gets asked in anger.

Retention is the other half. Full model transcripts are large and sometimes sensitive, so keep the metadata and hashes for as long as your compliance rules require — often years — while aging out the heavy bodies on a shorter clock. The hash chain still proves the record is intact even after the bulky content is gone, because the hash was computed over content you can re-supply or attest to separately. You keep provability cheaply and pay for full fidelity only as long as you actually need it.

⚡ Pro tip: Decide your retention policy before you start logging, not after your storage bill spikes. Retrofitting a policy onto a log that already mixes years of full transcripts with the metadata you actually needed is painful and risky. Tag each entry with a retention class at write time and let a lifecycle job enforce it, so the log prunes itself correctly without anyone deciding, under pressure, what's safe to delete.

Your Turn

Add the append-only logger to your harness today and start recording tool calls and decisions — even without the hash chain, structured records put you far ahead. Then layer in chaining when you need tamper-evidence, and an external anchor when the stakes justify it. Build it early: an audit log you add after an incident can't tell you what happened during the incident. That single constraint — that the log has to exist before you need it — is why it's the piece teams most regret deferring.

Keep your audit schema — which events, which fields, what's scrubbed — versioned alongside your prompts and tool definitions in a library like PromptABCD, so every agent you ship logs the same way and your incident response never depends on remembering which service happened to log which field. Consistency across agents is what lets one investigation query span your whole fleet instead of stopping at each service's idiosyncratic format.

ai-harnessaudit-logobservabilitycompliancehash-chaintamper-evidence

Continue Reading

Managing Prompt Templates Across a Harness Codebase
AI Harness

Managing Prompt Templates Across a Harness Codebase

Four divergent copies of one prompt caused a two-day bug. Harness prompt templates management makes prompts versioned, tested, single-source artifacts instead of scattered strings.

September 10, 2026·8 min read
How to Open-Source Your Agent Harness
AI Harness

How to Open-Source Your Agent Harness

An agent harness isn't an ordinary library — it's security-sensitive infra tangled with your secrets. Release an open source agent harness without leaking a key or shipping unusable code.

September 10, 2026·8 min read
Error Taxonomy: Classifying Harness Failures
AI Harness

Error Taxonomy: Classifying Harness Failures

When every failure looks the same, you can't retry, route, or alert correctly. Agent harness error classification gives failures types that drive real behavior.

September 10, 2026·8 min read

Save the prompts from this post

PromptABCD is a free prompt manager. Paste, organize, and reuse your best AI prompts — no more hunting through chat history.

Start free →
← PreviousHow to Handle Secrets and API Keys in a HarnessNext →Structured Tool Schemas in the Harness
Share this post:
ShareShare