Agent Observability Without Logging Chain-of-Thought

When an agent fails, the first request from an engineer is often:

Show me what it was thinking.

That request confuses two different needs.

Researchers may study model reasoning. Operators need to know which version ran, what state it saw, which evidence it used, what action it proposed, why policy allowed or denied it, what the tool actually did, how much budget remained, and why the run ended.

Logging hidden chain-of-thought does not reliably answer those questions. It can expose sensitive data, create a false sense of explanation, and still omit the deterministic code path that produced the external effect.

Agent observability should explain execution, not attempt to read a mind.

Start With Three Different Records

Do not use one giant “agent log.”

Trace

A trace connects operations with duration:

  • model calls;
  • retrieval;
  • policy checks;
  • tool calls;
  • approvals;
  • checkpoint reads and writes.

Events

Events record point-in-time state changes:

RUN_ADMITTED
MODEL_PROPOSED_TOOL
POLICY_DENIED
TOOL_STARTED
TOOL_RECEIPT_RECORDED
APPROVAL_GRANTED
CHECKPOINT_WRITTEN
RUN_COMPLETED

OpenTelemetry’s event guidance specifically positions events for checkpoints, state changes, lifecycle moments, and outcomes. That maps naturally to agent runtimes.

Audit receipts

Receipts prove high-impact decisions and effects:

  • canonical request hash;
  • actor and tenant;
  • policy decision and version;
  • approval identity and scope;
  • external transaction or object ID;
  • committed outcome;
  • integrity metadata.

Traces help diagnose. Events explain progression. Receipts support accountability. They can share correlation IDs without becoming the same data store.

Model the Run as a State Machine

Observability starts with explicit states:

ADMITTED
PLANNING
WAITING_MODEL
WAITING_TOOL
WAITING_APPROVAL
RUNNABLE
COMPLETED
FAILED
CANCELED
EXPIRED

Every transition records:

{
  "event_name": "agent.state.transition",
  "run_id": "run_...",
  "task_id": "task_...",
  "from": "WAITING_TOOL",
  "to": "RUNNABLE",
  "reason_code": "TOOL_RECEIPT_CONFIRMED",
  "graph_version": "support-31",
  "attempt": 2,
  "budget_delta": {"tool_calls": -1, "milliseconds": -842},
  "trace_id": "trace_..."
}

This record does not need the model’s internal prose. It says what the runtime did and why, using a controlled reason code.

Now an operator can ask:

  • which state accumulates the most wait time?
  • which transition causes retries?
  • which graph version cancels most often?
  • which tool returns after its lease expires?
  • where does cost grow without improving success?

Those are operational questions with operational answers.

Capture Model Calls as Versioned Dependencies

A model span should record:

  • provider and model identifier;
  • endpoint or deployment version;
  • prompt-template ID and hash;
  • sampling configuration;
  • input/output token counts;
  • cached tokens where available;
  • finish reason;
  • latency, queue time if exposed, and errors;
  • safe content-capture policy version.

Do not assume the model name fully identifies behavior. Hosted deployments can change; prompts, tools, retrieval, and policies also influence the result. Record every version needed to reproduce the task.

Content should be opt-in and risk-scoped. The OpenTelemetry GenAI attribute registry warns that messages, tool arguments, and tool results may contain sensitive information. Telemetry is not exempt from privacy, retention, or access control.

Default to hashes, length buckets, classifications, and controlled references. Capture raw content only in a restricted debug lane with purpose, expiry, and audit.

Treat Evidence as a First-Class Object

“The agent used RAG” is not enough.

For each retrieval operation, record:

  • query ID and safe hash;
  • corpus and index version;
  • embedding and reranker versions;
  • filters and tenant scope;
  • returned document/chunk IDs;
  • scores and rank;
  • document version and integrity hash;
  • whether the final response cited or relied on the evidence.

Large content stays in governed storage. The trace keeps references.

This answers the important counterfactual: did the model fail despite receiving the right evidence, or did retrieval fail before generation?

It also distinguishes a model regression from a corpus update.

Separate Proposal, Policy, and Execution

For a tool call, record three different facts.

Proposal

What the model requested, after safe structural parsing:

tool=issue_refund, argument_hash=...

Decision

What deterministic policy concluded:

decision=DENY, reason=APPROVAL_REQUIRED, policy_version=31

Execution

What the tool gateway actually sent and what receipt returned:

effect_id=..., canonical_request_hash=..., status=CONFIRMED

Never collapse them into “agent called refund.”

A model may propose a forbidden action that policy correctly blocks. That is a quality signal, not a security breach. If execution occurs despite denial, that is a control failure. The observability model must let incident responders tell the difference immediately.

Use Controlled Reasons, Not Generated Explanations

The model can produce a user-facing explanation, but dashboards need enumerable codes:

  • INVALID_TOOL_SCHEMA;
  • POLICY_DENIED;
  • APPROVAL_EXPIRED;
  • DEPENDENCY_TIMEOUT_UNKNOWN;
  • BUDGET_EXHAUSTED;
  • EVIDENCE_INSUFFICIENT;
  • USER_CANCELED;
  • LEASE_LOST;
  • POSTCONDITION_FAILED.

Codes support aggregation and alerts. Attach a concise redacted summary for humans, but do not let free-form model text become the authoritative root cause.

The true cause may be outside the model: queue saturation, stale credentials, an index rollout, a policy bug, or a duplicate callback.

Make Cost and Budgets Traceable

Agents amplify work. One user request can generate multiple model calls, retrievals, and tools.

Record budget at admission and delta per step:

  • model calls;
  • input/output tokens;
  • retrieval queries and documents;
  • tool calls;
  • retries;
  • wall-clock time;
  • estimated and actual cost.

Then report cost per successful terminal outcome, not cost per model call.

A cheaper model that retries and calls more tools can be more expensive. An agent that succeeds only after five attempts may look strong under pass@5 and weak as a customer-facing service.

Sampling Must Preserve Failures

High-volume telemetry creates pressure to sample. Uniform sampling can discard the rare runs you need most.

Keep or heavily sample:

  • all high-risk effects;
  • all policy denials;
  • unknown external outcomes;
  • security detections;
  • failed, canceled, and expired runs;
  • lease loss and duplicate execution;
  • cost or latency outliers;
  • novel tool/graph versions during rollout.

For successful low-risk runs, tail-based or cohort sampling can reduce volume. Preserve counts and metrics even when detailed traces are not retained.

Separate operational retention from audit retention. An audit receipt may need a longer, more controlled lifecycle than a debug span. Legal and regulatory requirements depend on context; do not reuse one blanket period.

Build Dashboards Around Questions

Useful dashboards answer:

  • What percentage of admitted runs reach each terminal state?
  • Where does wall time accumulate?
  • Which model or tool version changes success?
  • How often does policy block a dangerous proposal?
  • Which evidence sources correlate with grounded outcomes?
  • What is cost per success by task and tenant tier?
  • How many external outcomes remain unknown?
  • Which retries duplicate work?
  • How quickly can operators cancel active runs?

Avoid a vanity “agent reasoning” page filled with transcripts. It is expensive to store and difficult to aggregate.

The Minimal Observability Contract

Require these fields before launch:

run_id, task_id, parent_id, trace_id
actor and tenant references
agent, graph, model, prompt, tool, and policy versions
state transition and reason code
safe evidence references
model/tool operation timings
proposal, policy decision, and execution receipt IDs
budget deltas
terminal state and outcome
content-capture policy version

This is enough to reconstruct what the system did.

Hidden chain-of-thought would still not prove that the right policy ran, the right resource was authorized, or the payment committed once. Execution evidence does.

The operational goal is not to make the agent narrate itself more vividly. It is to make every meaningful transition, dependency, and side effect observable as a fact.

Leave a Comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.