Tracing Standards for Agents: What Fits and What's Missing
A run is a trace and a turn is a span — the mapping is cleaner than expected. Two things break: payloads far too big for span attributes, and sampling that discards exactly the runs you need.
Distributed tracing is a solved problem with mature tooling, and an agent run maps onto it more naturally than you'd expect: a run is a trace, each model call and tool call is a span, and the parent-child structure falls out of the loop.
Using existing tracing infrastructure gets you propagation, sampling, storage, querying, and integration with the rest of your system's observability for free. It's the right foundation. But two things about agents don't fit the standard model, and knowing which is which saves you from either fighting the tooling or building a parallel stack.
The natural mapping
trace: run_8821
├── span: agent.turn (1)
│ ├── span: llm.call model, tokens.in, tokens.cached, tokens.out
│ └── span: tool.call tool.name, args.hash, result.size, ok
├── span: agent.turn (2)
│ ├── span: llm.call
│ ├── span: tool.call (parallel)
│ └── span: tool.call
└── span: agent.finish outcome, total_turns, total_cost
Everything a normal trace gives you applies: latency breakdown per span, error propagation, correlation with the rest of your infrastructure. When a tool call is slow because a downstream database is slow, one trace shows both — which is a real advantage over any agent-specific tool that only sees the agent.
Attributes worth standardizing across your services: model name and version, prompt version, token counts split by cached and uncached, tool name, outcome, and the run's tenant and user.
What doesn't fit: payload size
Standard tracing assumes small attributes. The agent-specific thing you most need to inspect is the rendered message array, which can be tens of thousands of tokens — orders of magnitude beyond what a span attribute should hold.
The pattern that works: store payloads separately, put a reference on the span.
llm.call
prompt.hash: sha256:9f2c...
prompt.ref: s3://traces/2026-08/run_8821/turn_3.json
tokens.in: 18422
The trace stays light and queryable; the heavy content sits in object storage with its own retention policy. Your trace viewer links out to it. ⚠️ Trying to embed full contexts in spans produces a tracing backend that is expensive, slow, and eventually sampled — which drops exactly the traces you'd want.
What doesn't fit: sampling
Standard tracing samples, typically a small percentage, because traces are high-volume and mostly uninteresting.
Agent runs are the opposite: low volume, high value, each expensive and each potentially the one someone asks about. And the failures you need are precisely the ones a random sample discards.
- Sample agent traces at or near 100%, at least the span structure. The volume is manageable because runs are counted in thousands, not millions.
- Sample the payloads, not the spans. Keep the structure for everything; keep full contexts for a percentage plus every anomalous run.
- Never downsample the anomalies — errors, escalations, budget exhaustion, long runs, high cost. These have to be exempt by rule, and getting that exemption right is most of the work.
The agent-specific layer on top
Some things standard tracing has no concept of, which you add as attributes and derive at query time:
- Run outcome as a first-class dimension. Completed, escalated, halted, budget-exhausted. Not an error, not a success — the tracing model has no vocabulary for it.
- Turn count as a metric derived per trace. Your most useful single number, and nothing standard computes it.
- Tool sequence. The ordered list of tools called, as a queryable field. This is what lets you spot behavior changes and stuck loops, and it doesn't exist unless you record it.
- Cost, computed from token attributes at ingest and attached to the trace root.
✅ These four turn a generic trace store into something that answers agent questions. They're a small amount of work at ingest, and they're what determines whether the standard tooling is sufficient.
The pragmatic recommendation
Use standard tracing as the substrate — you get the ecosystem, and correlation with the rest of your stack that no agent-specific tool provides. Add the four agent dimensions as attributes. Keep payloads out of spans, referenced from object storage. Sample structure at 100%, payloads by policy with anomalies always retained.
Add a specialized agent-observability tool if you want trace viewing built for conversations, or an eval integration you don't want to build. But keep the underlying data in your own store, because that's what lets you answer questions nobody's product anticipated.
The takeaway
An agent run maps cleanly onto trace-and-spans, and using standard infrastructure is right. The two mismatches are payload size — reference it, don't embed it — and sampling, which needs to be near-total for structure with anomalies always kept. Add outcome, turn count, tool sequence, and cost as attributes, and the generic tooling answers agent-shaped questions.