Agent observability: what to trace in every AI agent run
Trace each AI agent run as one tree of spans: every model call, tool call and approval wait, with inputs, results, errors, tokens, cost and who it acted for.
Trace every AI agent run as a single tree of spans: one parent span for the run, and a child span for each model call, tool call and wait for a person. Each span should record what went in, what came out, whether it failed, how long it took, how many tokens it used and who the run was acting for. With that, any bad outcome can be followed back to the step that caused it, which is the whole point of agent observability.
The rest of this article is about which fields earn their place, where the content of prompts should live, and what to do with the traces once you have them.
A run that looked fine
Here is an illustration. A support agent receives the message "please refund my last order" from a customer called Priya Nair. It looks up the customer by name, fetches her recent orders, picks the latest one, asks a supervisor for approval and issues a ₹2,340 refund. The final reply is polite and correct in tone. The supervisor clicked approve. Every individual call returned a 200.
Two days later a different Priya Nair asks why she received a refund she did not request.
If all you logged was the final reply and the approval, you have nothing to go on. If you have a trace, the answer is visible in about a minute: the customer lookup returned two matches, the model picked the first, and every later step was correct for the wrong person. The bug is in step two, the damage happens in step six, and the approval screen showed the order but not the ambiguity.
That gap between where a mistake starts and where it shows up is why agents need tracing more than ordinary services do. A web request follows a code path someone wrote. An agent picks its own path at run time, and Anthropic's guide to building agents puts the risk plainly: "The autonomous nature of agents means higher costs, and the potential for compounding errors."
Logs record events, traces record causes
A log line says something happened. A trace says how things that happened relate to each other: this tool call was made by this model turn, inside this run, triggered by this message, on behalf of this user.
The mechanics come from distributed tracing. Each run gets a trace ID. Each unit of work inside it is a span with its own ID, a parent ID, a start time, an end time, a status and a set of attributes. Nest the spans and you get a waterfall you can read top to bottom.
You do not have to invent the vocabulary. OpenTelemetry, the open observability project, publishes semantic conventions for generative AI. Its agent conventions define spans named invoke_agent, execute_tool, plan, create_agent and invoke_workflow, and its model conventions cover inference, embeddings, retrieval and memory operations. The span name for a tool call should be execute_tool {gen_ai.tool.name}, so a waterfall reads like the one above without any custom work.
One caution: every one of these conventions is marked Development status, which means names and requirements can still change, and the documents themselves now live in a dedicated OpenTelemetry repository rather than the main semantic conventions site. Use them, because a shared shape beats a private one, but pin the version your instrumentation follows and expect to update it. The OpenTelemetry project makes the same argument for standards, saying the aim is to "establish standards around the shape of the telemetry generated by agent apps to avoid lock-in".
The fields worth recording
Most of what you need fits in a short list per span type. The attribute names below are from the OpenTelemetry GenAI conventions where one exists; the rest are fields I would add myself.
| Span | Record | Why it matters |
|---|---|---|
Agent run (invoke_agent) |
Agent name and version, trigger, user or system acted for, conversation ID, final status, total tokens and cost | Answers "who asked for this, and did it finish?" |
| Model call (inference) | Requested and returned model, gen_ai.usage.input_tokens, gen_ai.usage.output_tokens, cached tokens, finish reason, duration |
Explains cost, truncation and slow steps |
Tool call (execute_tool) |
gen_ai.tool.name, gen_ai.tool.call.id, arguments, result or error.type, duration, retries |
This is where an agent touches real systems |
| Retrieval | Query, data source ID, number and IDs of documents returned | Shows whether the answer had the right material |
Planning (plan) |
The plan the model produced before acting | Makes later steps readable |
| Human wait | Who was asked, what they were shown, decision, how long it took | Approvals are part of the run, and often the slowest part |
Version deserves a special mention. If a run can't tell you which prompt version, tool definitions and model it used, you can't compare last week's failures with this week's fix. The gen_ai.agent.version attribute exists for this; set it from your deployment, not by hand.
The finish reason is the cheapest early warning you have. A model call that stopped because it hit the token limit produced a cut-off answer, and an agent will happily act on half a plan. Count those per day.
Prompts and outputs are the awkward part
Everything in the table above is operational data: names, numbers, IDs and timings. The awkward fields are the content: the system instructions, the messages sent to the model, what it said back, and the arguments and results of tool calls. They are the most useful fields for debugging, and also the ones most likely to hold a customer's name, phone number or account details.
The OpenTelemetry conventions are clear on the default. Model instructions, user messages and outputs "are considered sensitive", and instrumentations "SHOULD NOT capture them by default". Content capture, which also covers tool call arguments and results, retrieval queries and memory records, is controlled by an explicit setting, OTEL_INSTRUMENTATION_GENAI_CAPTURE_CONTENT, whose default value is NO_CONTENT.
The conventions then describe three patterns. Record nothing. Record content directly on the spans, which they suggest for situations like pre-production where volume is manageable and privacy rules don't apply or are met. Or store content externally and put a reference on the span, which they recommend for production "where telemetry volume is a concern or sensitive data needs to be handled securely", because external storage allows separate access controls.
I would go with the third pattern from day one, even in a pilot. The people who need latency graphs and error rates (most of engineering) are not the same people who need to read a customer's messages (a small group, for a specific incident). Splitting the two stores lets you give each group what it needs. It also means a retention rule for personal data can apply to the content store without deleting the operational history you use for capacity planning. If you operate in India, our article on AI governance and the DPDP Act covers why that separation is worth having.
This is also where tenancy matters. If you run agents for several business units or clients, a trace from one must never be readable by another. CloudOEngine includes tenant isolation, role-based access and audit logs as part of the platform's governance, so the same roles that limit what an agent can touch also limit who can open its traces.
Tool calls deserve the most attention
A model call that goes wrong produces bad text. A tool call that goes wrong changes something: a ticket, a record, a payment, an email in someone's inbox. If you only have time to instrument one thing well, make it the tool calls.
For each one, record the tool name, the arguments the model chose, the result or the error, how long it took and how many retries there were. OpenTelemetry's gen_ai.tool.call.id links the tool span back to the specific request in the model's response, which matters when a single model turn asks for three tools at once. Its error.type attribute should hold a low-cardinality class such as timeout or 500, so you can group failures without reading every one.
Also record what the tool reported back to the model, as well as what your code saw. Anthropic's guidance is that agents need to gain "ground truth" from the environment at each step. If a tool returns an empty list on a database timeout instead of an error, the model's next move is reasonable and wrong, and only the tool span shows why. In the refund illustration, the lookup returned two customers without any flag saying the match was ambiguous. A trace makes that visible; a better tool contract (return an error when more than one customer matches) fixes it.
CloudOEngine's AI runtime is built around this: agents run real tools, for example filing a ticket, and every step and tool call is traced and inspectable, so a run like the refund above can be opened and read step by step.
Waiting for people, and other systems
Two kinds of step tend to fall out of traces: time spent waiting for a human, and work done in another service.
Approvals belong in the trace. In the waterfall above, the longest span is the supervisor's wait, and that is common for agents with approval rules. If you don't record it, your latency numbers blame the model for what is really a queue. Record who was asked, what they were shown and when they answered. The "what they were shown" part is the one people skip, and it is the one that would have saved the second Priya Nair: the approval screen listed the order, not the fact that the customer match was a guess. CloudOEngine's Autopilot runs triggered agents with approval rules, where a person approves the action in the app or through an email approve link before it runs; that decision is a step in the run like any other. Our article on human-in-the-loop AI covers what an approver should see.
Other services need the trace ID passed to them. The W3C Trace Context Recommendation defines a traceparent HTTP header for this, carrying a version, a 32-character trace ID, a 16-character parent span ID and flags. When an agent calls your order service and that service is also instrumented, the order service's spans attach to the agent's run instead of floating loose. Check that your tool layer actually sends it: a hand-rolled HTTP client won't, unless someone adds it.
Turn traces into numbers
Individual traces are for debugging. Aggregated, they tell you whether the agent is healthy.
The OpenTelemetry conventions define agent metrics including gen_ai.invoke_agent.duration, gen_ai.invoke_agent.inference_calls and gen_ai.invoke_agent.tool_calls. Those three, plus cost per run and tool error rate by tool, cover most of what a weekly review needs. Watch the distribution as well as the average: a run that normally takes four model calls and suddenly takes forty is usually looping, and an average over a thousand runs will hide it.
This is also a cost and abuse control. OWASP's entry on unbounded consumption in its 2025 LLM risk list recommends that teams "continuously monitor resource usage and implement logging to detect and respond to unusual patterns", alongside timeouts and rate limits. Per-run step counts and token totals from your traces are exactly that signal. Anthropic's guide makes the same point from the design side, suggesting stopping conditions such as a maximum number of iterations. A hard limit stops the runaway; the trace tells you why it ran away. For the money side, see our guide to LLM cost control.
From a bad trace to a test case
The best use of a failed trace is to make sure the same failure can't ship again. Take the inputs from the bad run (the message, the tool results it saw, the data state) and add them to your evaluation set as a case with the correct expected outcome. In the refund illustration, the expected outcome is "ask which customer" rather than "refund the first match".
Then fix the cause: the prompt, the tool contract, or the data. Rerun the evaluation set before deploying. Our article on evaluating LLM applications covers how to build and run that set. Teams that do this weekly end up with a test suite made of their own real failures, which is worth more than any generic benchmark.
What I would instrument in the first week
If an agent is already running without tracing, this order gets the most value soonest:
- Give every run a trace ID and record the trigger, the user or system it acted for, the agent version and the final status.
- Wrap every tool call in a span with name, arguments, result or error class, duration and retries.
- Add model call spans with model name, token counts, finish reason and duration, and compute cost per run.
- Decide where prompt and response content lives, and keep it in a separate store with its own access rules and retention.
- Record approval waits as spans, including what the approver was shown.
- Propagate
traceparentinto the services your tools call. - Build a weekly view of steps per run, cost per run and tool error rate, and turn the worst traces into test cases.
Steps one and two alone would have explained the refund to the wrong Priya Nair on the day it happened.
How CloudOEngine helps
CloudOEngine is one platform and console for connecting data (databases, files, camera streams), pipelines, an AI runtime (LLM gateway, agents with tool access, RAG), Vision AI and live video. It all sits under one permission model and one audit trail, so the record of what an agent did lives alongside the record of what your people did.
In the AI runtime, agents run real tools, for example filing a ticket, and every step and tool call is traced and inspectable. When a run goes wrong you can open it and follow each model decision, tool input and result to the step where it went off course, rather than reconstructing it from logs.
Autopilot runs triggered agents with approval rules. A person approves an action, in the app or through an email approve link, before it runs. That keeps the risky step in a person's hands, and the approval is part of the run's history.
Governance is included, not upsold: tenant isolation, role-based access, audit logs and per-tenant encrypted secrets. The roles that decide what an agent may do also decide who may read its traces, and tool credentials stay scoped to the tenant that owns them.
CloudOEngine is built in Bengaluru, India.
Book a demo or see the AI runtime.
Frequently asked questions
What is agent observability?
Agent observability is the ability to see what an AI agent did during a run and why: which model calls it made, which tools it chose, with what arguments, what came back, how long each step took and what it cost. In practice it means recording each run as a trace of nested spans.
What should you trace in an AI agent run?
At minimum: the trigger and the user or system the run acted for, every model call with its model name, token counts and finish reason, every tool call with its name, arguments, result or error and duration, any time spent waiting for a person, and the final outcome. Tie them together with one trace ID.
Is there a standard for tracing AI agents?
OpenTelemetry publishes semantic conventions for generative AI that define spans such as invoke_agent, execute_tool and plan, plus attributes like gen_ai.usage.input_tokens and gen_ai.tool.name. They are still marked as Development status, so names can change between versions.
Should agent traces include the full prompts and responses?
Only with care. The OpenTelemetry conventions say instrumentations should not capture prompts, outputs or tool arguments by default, and recommend storing that content externally in production so it can have separate access controls. Keep operational fields on the span and put the content where fewer people can read it.
How is agent tracing different from normal application logging?
A log line records an event; a trace records how events relate, so you can see that a refund failed because a lookup three steps earlier returned two customers. Agents choose their own steps at run time, so the relationships between steps matter more than for fixed code paths.
How do traces help improve an agent?
A failed trace shows the exact step that went wrong and the inputs that caused it. Turn that case into a test in your evaluation set, fix the prompt, tool or data, and rerun the set before shipping the change.
Sources
- Semantic Conventions for GenAI agent and framework spans (OpenTelemetry)
- Semantic conventions for generative client AI spans (OpenTelemetry)
- Semantic conventions for generative AI metrics (OpenTelemetry)
- AI Agent Observability: Evolving Standards and Best Practices (OpenTelemetry blog, March 2025)
- Trace Context (W3C Recommendation)
- LLM10:2025 Unbounded Consumption (OWASP GenAI Security Project)
- Building effective agents (Anthropic, December 2024)
