On this page
What agent observability is
Agent observability is the recording, storage and replay of everything an agent saw, decided and did on a given run, together with the exact configuration it was running under, so the run can be reconstructed and explained after the fact. It differs from ordinary application monitoring because an agent run can return a status of success and still be wrong, which means the useful signal is the decision rather than the response code.
Four questions have to be answerable from a trace alone: what did the agent see, what did it decide, what did it do, and what was it at the time. The fourth is the one teams leave out, and it is the one that ruins the other three. A trace without the prompt version, the model version and the threshold values is an anecdote, because you cannot tell whether the behaviour you are looking at still exists.
This is unglamorous infrastructure that becomes the most important thing you own during an incident. What to log so future you can debug it covers the field-level detail; this page covers the design and the trade-offs.
- Trace
- The complete record of one agent run, from trigger to terminal state, structured as a tree of spans that share a trace identifier.
- Span
- One unit of work inside a run, with a start time, an end time, inputs, outputs and a parent. A model call, a tool call and a decision are each a span.
- Replay
- Re-executing a recorded run offline against its recorded tool responses and pinned configuration, to produce the same trace again without touching production.
- Reason code
- A label drawn from a closed vocabulary that the agent attaches to each decision. Reason codes make traces countable, where free text only makes them readable.
- Config snapshot
- The prompt template identifiers and versions, model alias and pinned version, temperature, tool registry version and threshold values in force for a run.
- Four questionsWhat it saw, what it decided, what it did, what it was. A trace that cannot answer all four cannot be interpreted later.
- Grade 3Deterministic replay, which is the target for every step that writes to a system of record.
- 2 to 6 weeksOur window to retrofit observability onto an agent already running in production.
- Never sample failuresSuccesses can be sampled, failures and writes are captured in full. Uniform sampling throws away the runs you built this for.
- Redact at write timePersonal data is masked before a span leaves the process, because masking later means it was already stored.
Who it is for, and who it is not for
This is for agents that take actions with consequences, and for teams who have already had the conversation that starts with somebody asking why the system did something last Tuesday. It is premature for a prototype that does not yet work, where the money is better spent on an evaluation set.
| Your situation | What it means | What we would do |
|---|---|---|
| The agent writes to a system of record | Every write may need explaining to somebody who was not there | Deterministic replay on the whole write path |
| The agent drafts text a human sends | The human is the control, so the trace is for improvement rather than defence | Full capture on failures, sampled capture on the rest |
| An auditor or regulator may ask | The evidence requirement is legal, not technical | Agree retention, redaction and access with counsel before capture starts |
| A reported bug cannot be reproduced | You are at grade 0 or 1 and debugging is guesswork | This is the highest-value retrofit available, start here |
| Pre-launch, one prototype, no users | There is nothing yet to explain | Build the evaluation set first, see evaluation and guardrails |
| Traces exist and nobody reads them | A review surface problem, not a capture problem | Build the reason-code rollup and attach it to a weekly ritual |
| High volume, low consequence per run | Full capture will dominate the running cost | Sample successes hard, keep every failure and every write |
The moment you record everything the model saw, you have copied customer data into a second system with different access controls, a different retention policy and a different backup regime. Decide redaction and retention before the first span is written. Retrofitting redaction means the unredacted copies already exist, in backups you cannot selectively edit.
The replay grades
Observability is not a switch, it is a ladder, and different parts of the same agent belong on different rungs. Grading each path honestly stops the two common outcomes: capturing far too little on the paths that matter, and capturing far too much on the paths that do not.
The ChatGPTalker Replay Grades
Five grades, from having an output to being able to ask what would have happened. Grade each path in your agent separately, then decide where you actually need to be.
You have the final answer and nothing else. Debugging means asking the user what they typed and hoping they remember. Almost every agent starts here, and it is survivable only while nobody is depending on the output.
You know which tools were called in what order, with timestamps and statuses. You can see the shape of a run and spot the step that hung. You cannot see what was passed or returned, so you can see that it went wrong without seeing why.
Every prompt, tool argument, response and decision stored against the trace. You can read exactly what happened. You still cannot rerun it, so a fix is verified by pushing it to production and waiting, which is the slowest possible feedback loop.
The run can be re-executed offline against recorded tool responses, with the prompt version, model version, temperature and tool registry pinned. The same inputs produce the same trace. This is the rung at which regression testing becomes possible at all, and it is the target for any path that writes.
You can change one variable, a prompt revision, a model version, a threshold, a tool response, and rerun the recorded inputs to see what would have happened instead. This turns an incident into a measurement. It costs real engineering, so put it on the two or three decisions with the highest consequence rather than everywhere.
The sensible default is grade 3 on everything that writes, grade 2 on everything else, and grade 4 on a shortlist you can name in one sentence. Teams that try to reach grade 4 across a whole agent usually stall somewhere around grade 1 with a large unfinished framework, which is worse than grade 2 with a boring one that works.
What we actually build
Five components. The first one decides whether the other four have anything to work with.
The span emitter
A thin wrapper around the model client and the tool registry, so every call is recorded without a developer choosing to record it. Instrumentation that depends on people remembering produces partial coverage, and during an incident the span you need is reliably the one nobody wrapped. Wrapping the client rather than the call sites is the whole trick, and it usually takes two days.
The trace store
Append-only, keyed by trace identifier, with field-level redaction applied on the way in and tiered retention on the way through. Payloads above a size threshold go to object storage with a reference left in the span, because storing a 60,000 token context inline in a queryable index is how observability bills get frightening.
The replay harness
Takes a trace identifier and re-executes the run offline against the recorded tool responses under the recorded configuration. Two uses: reconstructing an incident, and running the regression suite. A harness nobody exercises does not work, so we schedule an automatic replay of a random production trace and fail the build when the result diverges.
The review surface
Not log search. A run view showing the decision tree, the reason code and confidence at each decision, the retrieved context, and where a human later disagreed. Filters by reason code and outcome, because the useful question is almost never about one run. It is about which of eleven reason codes accounts for most of last week.
The rollups and alarms
Reason code distribution, confidence distribution, tool error rates, cost per run by step, and drift alarms that fire when a distribution shifts rather than when a request errors. Silent degradation is the characteristic agent failure, and nothing in ordinary uptime monitoring will catch it. Monitoring an AI system in production goes deeper on the alarm design.
How it works technically
One trace per run, a tree of spans underneath it, and a configuration snapshot stamped at the top. Eight things happen.
- The run starts. The orchestrator issues a trace identifier and stamps the config snapshot: prompt template identifiers and versions, model alias and pinned version, temperature, tool registry version, threshold values.
- Each model call opens a span carrying the rendered prompt hash, token counts, and the prompt itself where the redaction policy allows, or a redacted copy where it does not.
- Each tool call opens a child span with its arguments, the response, the latency and the attempt number, so a retry storm is visible as a shape rather than inferred from a bill.
- Each decision opens a span with the choice, the confidence, the threshold that applied, and a reason code from a closed vocabulary. The free-text explanation is stored beside the code, never instead of it.
- Writes carry the idempotency key and the confirmation read, so the trace records what actually landed rather than what was attempted.
- Redaction runs in-process. Named personal fields are hashed or masked before the span leaves the application, which means the unredacted values never reach the store or its backups.
- Spans append to the trace store. Payloads above the size threshold are written to object storage and referenced by pointer.
- The run closes with a terminal status, a reason code, and the replay grade achieved, so you know at a glance what you can and cannot reconstruct from it.
- One span shape, used everywhere, is worth more than a rich schema per component.
- Reason codes are versioned alongside the prompts, because adding one silently breaks last month's rollup.
- Every span carries the trace identifier, so a support ticket quoting one identifier is enough to reconstruct everything.
{
"trace_id": "run_20260826_a41f9c",
"span_id": "sp_07",
"parent_span_id": "sp_03",
"name": "decision.route_refund",
"kind": "decision",
"started_at": "2026-08-26T09:14:02.118Z",
"ended_at": "2026-08-26T09:14:03.902Z",
"config": {
"agent": "refunds-triage",
"agent_version": "4.2.1",
"prompt_template_id": "refund_triage",
"prompt_template_version": 11,
"rendered_prompt_sha256": "3f9c...a71b",
"model_alias": "triage-primary",
"model_version_pinned": "vendor-model-2026-06-01",
"temperature": 0,
"tool_registry_version": 19,
"thresholds": { "auto_approve": 0.92, "escalate": 0.55 }
},
"saw": {
"input_ref": "blob://traces/run_20260826_a41f9c/sp_07/input.json",
"retrieved_doc_ids": ["policy-refunds-v6#s3", "order-88213"],
"context_tokens": 5812,
"redacted_fields": ["customer.email", "customer.card_last4"]
},
"decided": {
"choice": "escalate_to_human",
"confidence": 0.61,
"threshold_applied": 0.92,
"reason_code": "policy_ambiguous_partial_shipment",
"reason_text": "order shipped in two parts, policy covers full shipments only",
"alternatives_considered": ["auto_approve", "reject"]
},
"did": {
"writes": [],
"queue": "refunds_review",
"idempotency_key": "run_20260826_a41f9c:sp_07",
"confirmation_read": null
},
"cost": {
"input_tokens": 5812,
"cached_input_tokens": 4900,
"output_tokens": 214,
"price_book_ref": "pricebook/2026-08"
},
"attempt": { "n": 1, "of": 3, "previous_error": null },
"error": null,
"replay": { "tool_responses_recorded": true, "deterministic": true, "grade": "G3" }
}The config block is the part people cut and the part that makes the rest usable. Without prompt_template_version and model_version_pinned, a trace from six weeks ago cannot tell you whether the behaviour you are staring at still exists, because somebody edited a template in between and nothing recorded it. The alternatives_considered field costs almost nothing and answers the question everybody asks second, which is what else it nearly did.
The build process, stage by stage
- Write down the questions
Five questions the trace must answer and the person who asks each one. Ours usually start as: what did it decide and why, what did it see, what did it change, what did it cost, and who could have stopped it. Everything about the schema follows from this list, and skipping it produces a store full of data that answers nothing.
- Redaction and retention policy
Which fields are masked, which are hashed, which are kept whole, how long each tier lives, and who can read the store. Agreed with whoever owns data protection before capture starts, because this is far harder to change after the first month of traces exists.
- Instrument the spine
Wrap the model client and tool registry so every call emits a span automatically. Add the config snapshot at run start. This is the stage that produces coverage, and coverage is the property that matters most when something goes wrong at an inconvenient hour.
- Agree the reason code vocabulary
A closed list, deliberately short, named by people who understand the domain rather than by the engineer nearest the keyboard. Ten to twenty codes covers most agents. The moment reason codes exist, traces become countable and the weekly review has an agenda.
- Trace store and tiering
Append-only store, size threshold for payload offloading, hot and warm and cold tiers, and a deletion job that genuinely runs. A retention policy nobody enforces is worse than no policy, because it is a promise you are quietly breaking.
- Replay harness
Recorded tool responses, pinned configuration, offline execution, trace comparison. Then the scheduled replay of a random production trace, wired into the build, so the harness is exercised weekly rather than discovered to be broken during the incident it was built for.
- Review surface and rollups
The run view, the filters, the reason code distribution, the cost per run by step, and the drift alarms. Reviewed with the team that will use it, because a surface built without them becomes a dashboard nobody opens.
- Backfill the evaluation set
Traces where a human overrode the agent are the most valuable labelled data you will ever get, and they are free. Pull them into a regression set and the observability build has paid for a large part of the next quality improvement.
Retrofits onto a running agent land at the shorter end of that range when the tool layer is already centralised, and at the longer end when every tool call was written by hand in a different style. The instrumentation work is proportional to how disciplined the original build was, which is usually the first honest thing we can tell a client.
What you get at handover
Everything below sits in your repositories and your cloud accounts. Nothing in the pack depends on us being reachable.
A trace store with no obligation attached to it decays into storage cost. We hand over a fifteen minute weekly review with three fixed questions: which reason code grew, which decisions did humans overturn, and what did cost per run do. Teams that keep that meeting keep the system healthy. Teams that skip it are back at grade 1 within a quarter, with a large bill.
Where these projects go wrong
Six patterns, and five of them are decisions rather than bugs.
Instrumentation was left to individual developers
Voluntary logging produces coverage that looks fine in review and fails in production, because the path that breaks is the one written in a hurry by somebody who has since moved teams. Wrap the client and the tool registry so a call cannot happen without a span. Coverage is a property of the architecture, never of anybody's diligence.
The prompt version was never recorded
You have three months of traces and no way to tell which prompt produced any of them, because templates were edited in place. Every conclusion drawn from that archive is unsafe. Pin the template identifier, the version and a hash of the rendered prompt on every span, and treat every prompt edit as a versioned change with an identifier, the same way you would treat a schema migration.
Everything was captured, including what you cannot keep
Full fidelity capture of customer messages, identity documents and payment details into a store with looser access control than the source system. This is not a bug, it is a disclosure waiting for an audit to find it. Decide the redaction list first, apply it in-process, and accept that a few debugging sessions will be harder as a result.
Reasons were free text instead of codes
Ask a model to explain itself and it writes prose, which reads well and cannot be counted. Two hundred runs of prose is not a report. Constrain the reason to a closed vocabulary and keep the prose in a second field. The distribution of reason codes is the single most useful artifact the whole system produces.
Sampling was applied uniformly
A flat sampling rate of one in twenty sounds prudent and means you keep one in twenty failures, which are the runs the system exists to explain. Sample successes aggressively, capture every failure, every escalation and every write in full. The cost difference is small because failures are the minority, which is exactly why uniform sampling looks affordable and is not.
The replay harness was never exercised
It was built, it worked once, and then tool responses changed shape, a recording format drifted, and nobody noticed for four months. During the incident it fails, and by then the trace you wanted has aged out of the hot tier. Replay a random production trace on a schedule, compare against the original, and fail the build on divergence. How agents fail covers the failure shapes this catches.
Most observability builds succeed technically and fail organisationally. The traces are there, the rollups are correct, and no one has an obligation to look. Attach the review to a named person, a fixed time and three questions with answers that get written down. Without that, the first anyone knows about a degraded agent is a customer explaining it to them.
What it costs to run once live
Two running costs: storing traces, and reviewing them. Storage is arithmetic on your own volumes and it is usually smaller than people fear once payloads are offloaded and tiers are configured. Automated review, where a second model scores a sample of traces, is the line that grows quietly, because it scales with volume rather than with incidents.
Use your own volumes and your provider's current storage and model prices. Span size is the average across a run, including offloaded payloads.
Work the defaults once. Two thousand runs a day at fourteen spans is 28,000 spans a day, 840,000 a month. At 8 KB each and 180 days retention that is roughly 38 GB held, which at a blended two cents a gigabyte-month is under a dollar. Automated review at five percent of runs and a cent per trace is 100 runs a day, roughly 30 a month. Both figures are assumptions you should replace, and the point stands after you do: at moderate volume, observability is cheap, and the expensive version is the one where raw contexts are stored inline in a search index instead of offloaded.
Full fidelity, indexed, queryable in seconds. Incident work happens here, which is why the tier boundary should sit beyond your worst realistic time to notice a problem rather than at a round number.
Payloads live in object storage, spans keep metadata and pointers. Queries take minutes instead of seconds. This is where trend work and evaluation set backfilling happen.
Compressed archives, restored before use. Kept for audit, disputes and the occasional argument about what the system did in a quarter that has already been reported on.
A scheduled job that runs, logs what it removed, and can be shown to somebody who asks. A retention policy nobody enforces is a commitment you are breaking without noticing.
How to tell whether you need this
Seven questions about an agent you already run. Any two answered badly is enough to justify the retrofit, and the first two on their own usually settle it.
- Can you explain a run from three months ago without rerunning it? If reconstruction means rerunning against today's prompt and today's model, you are not reconstructing anything.
- If a customer disputes an action, can you show what the agent saw? Not what it did, what it saw. The retrieved context is the part that explains the decision.
- When a prompt changes, can you tell whether quality moved? Without pinned versions and a regression set, prompt changes are edits made on faith.
- Do you know your cost per run, broken down by step? A single monthly invoice tells you the total and hides which step is responsible for it.
- Can you count failures by cause? Reading them one at a time is not counting, and a distribution is what tells you where to spend the next fortnight.
- Do you know which model version produced last month's output? Providers deprecate and revise. A trace without a pin cannot answer this.
- If the agent degraded quietly, what would alert you? If the only answer is a customer complaint, that is the gap this service exists to close.
There is a version of this work that is one engineer for three weeks and gives you full capture, reason codes and a run view. There is another version that takes a quarter and delivers counterfactual replay across every path. The first one is the right starting point for almost everyone, and the second is worth doing only for decisions you can name.
How to start
Bring one agent that already runs in production and one incident you could not explain. In an hour we can usually tell you which replay grade you are actually at, which is often a grade lower than the team believes, and what the shortest path to grade 3 on the write path looks like.
The retrofit is two to six weeks depending on how centralised your tool layer already is. If the codebase has a single model client and a tool registry, instrumentation is days. If every tool call was written by hand in its own style, that is the work, and we will say so before quoting rather than after. Making an agent's decisions auditable is a reasonable thing to read while you decide.
ChatGPTalker, "Agent observability, so a run can be explained months later", chatgptalker.com/services/agent-observability/