AI Agents

AI Agent Observability: Tracing, Metrics, and Evals Explained

AI agent observability is tracing and evaluating the full span tree behind one agent request — every nested model call, tool call, retrieval step, and sub-agent handoff — not just a request/response timing the way classic APM does. It runs on OpenTelemetry's GenAI semantic conventions, which standardize spans like invoke_agent, chat, and execute_tool so tools like Langfuse, LangSmith, Arize Phoenix, and MLflow can all read the same traces. Here's how the tracing actually works, how the major tools compare, why output-only evals miss real failures, and how to instrument a multi-agent system correctly.

Gurram Poorna Prudhvi

Lead AI Engineer

Technical Guide
Nov 9, 2026
12 min read
AI AGENT OBSERVABILITY: ONE REQUEST, A SPAN TREEOpenTelemetry GenAI semantic conventions — invoke_agent → chat / execute_tool / handoff, not one HTTP timinginvoke_agent · gen_ai.agent.name=researcherchat · gen_ai.request.model=claude-opusexecute_tool · gen_ai.tool.name=web_searchinvoke_agent · gen_ai.agent.name=writer (handoff)chat · gen_ai.usage.output_tokens=842execute_tool · gen_ai.tool.name=file_writeA SINGLE REQUEST DURATION HIDES ALL OF THISagent observability reconstructs the full tree — which model call, tool call, or sub-agent handoff caused the slow or wrong answeraiengineerinsights.com

TL;DR

  • • Agent observability traces the nested span tree of one request, not a single HTTP timing.
  • • OpenTelemetry's GenAI semantic conventions (gen_ai.*) are the emerging standard, but still "Development" status, not frozen.
  • • Langfuse, LangSmith, Arize Phoenix, and MLflow differ mainly on license, self-hosting, and what they're strongest at (RAG eval vs multi-agent view vs LangChain-native).
  • • Trajectory evaluation (did it use the right tools, in the right order?) catches failures output-only checks miss.
  • • In multi-agent systems, the calling agent emits the invoke_agent span — not the agent being called.

Explore this article with

What is AI agent observability, and how is it different from APM?

AI agent observability is the practice of capturing, tracing, and evaluating everything an AI agent does while handling one request — not just whether it returned a 200 in 800ms. Classic application performance monitoring (APM) treats a request as one span with a duration and an error flag, which works when the server does one deterministic thing. An agent is different: a single user request can fan out into a planning step, several nested model calls, multiple tool calls, a retrieval step, and a handoff to another sub-agent — and any one of those can be the reason the response was slow or wrong.

Elastic's engineering team and Honeycomb both make the same point from different angles: you need the whole span tree — the parent-child relationship between invoke_agent, chat, and execute_tool spans — to debug an agent, because the aggregate request duration hides which exact step caused the problem.

Why agent observability matters now

Once an agent has more than one tool, or hands work off between sub-agents, a plain error log stops being enough. A request that "worked" in the sense of returning an answer can still have silently called the wrong tool, burned 10x the tokens it needed to, or lost context across a multi-turn session — none of which shows up as an HTTP error. Without a trace of every step, you're debugging by guessing which of the five things that happened inside the request actually went wrong.

This is also why the industry converged on a shared standard rather than every vendor shipping a proprietary tracing format: the OpenTelemetry GenAI Semantic Conventions mean a trace produced by one SDK can be read by Langfuse, Phoenix, MLflow, or a generic OTel backend like Honeycomb or Elastic, instead of locking you into one vendor's trace format.

How agent tracing actually works (OpenTelemetry GenAI semconv)

The OpenTelemetry GenAI semantic conventions define standard span names for the operations an agent performs — invoke_agent, chat, execute_tool, and plan among them — plus a common set of attributes carried on every span:

  • • gen_ai.operation.name — which operation this span represents (chat, execute_tool, invoke_agent…).
  • • gen_ai.request.model / gen_ai.response.model — the model requested vs. the one that actually served the call.
  • • gen_ai.usage.input_tokens / gen_ai.usage.output_tokens — token accounting per span, so cost is attributable to the exact call that spent it.
  • • gen_ai.agent.name — which named agent emitted the span, essential once you have more than one.
  • • gen_ai.tool.name — which tool an execute_tool span invoked.
  • • gen_ai.conversation.id — ties spans across turns of the same session together.

A simplified trace for "research a topic, then write it up" looks like a tree: a root invoke_agent span for the researcher agent, with child spans for a chat call to the model and an execute_tool call to a search tool, then a sibling invoke_agent span for a writer agent it handed off to, which itself nests its own chat and execute_tool spans — exactly what the hero diagram above shows.

with tracer.start_as_current_span(
    "invoke_agent", attributes={"gen_ai.agent.name": "researcher"}
) as agent_span:
    with tracer.start_as_current_span(
        "chat", attributes={"gen_ai.request.model": "claude-opus-4"}
    ):
        response = model.chat(messages)

    with tracer.start_as_current_span(
        "execute_tool", attributes={"gen_ai.tool.name": "web_search"}
    ):
        results = web_search(query)

One caveat worth flagging: TrueFoundry's writeup notes the spec is still marked "Development" status as of mid/late 2026 — not yet frozen or stable — so attribute names can still shift between releases. Pin a version and expect to update instrumentation as the spec matures, the same way you'd treat any pre-1.0 standard. MLflow's docs on the convention are a good place to track what's actually stabilized.

Langfuse vs LangSmith vs Arize Phoenix vs MLflow

All four read and/or emit OpenTelemetry-shaped traces, but they differ on license, self-hosting difficulty, and what they're actually strongest at:

ToolLicenseSelf-hostingOTel-nativeBest forApprox. cost at 1M events/mo
LangfuseMIT (open source)Yes — Docker Compose (ClickHouse + Postgres + Redis)Yes, OTel-nativeMulti-agent tracing (Agent Graph view), teams that want to self-host~$101/mo Cloud Core at 1M events
LangSmithProprietary (closed source)Enterprise tier onlyPartial — strongest inside LangChain/LangGraphTeams already standardized on LangChain/LangGraph~$2,514/mo Plus at 1M events
Arize PhoenixElastic License 2.0 (open source)Yes — single Docker containerYes, OpenInference/OTel-nativeRAG eval (RAGAS-style faithfulness/relevance) alongside tracingSelf-host free; cloud tiers available
MLflowApache 2.0 (open source)YesYes — native GenAI semconv export since MLflow 3.11Teams already on MLflow for experiment tracking / model registryFree, self-hosted; 30M+/mo PyPI downloads

Per MLflow's own comparison and Arize's evaluation-platform breakdown, the practical default is: Langfuse if you want an OTel-native, self-hostable option with a dedicated multi-agent "Agent Graph" view; LangSmith only if you're already committed to LangChain/LangGraph and fine with a closed-source, cloud-first tool; Arize Phoenix when RAG and retrieval-quality metrics (faithfulness, relevance) matter as much as tracing; and MLflow if you already run it for experiment tracking and want tracing in the same place — it's exported native GenAI semconv support since MLflow 3.11. A fifth option, Braintrust, is worth a mention for eval-driven workflows but has no self-hosting path, per ZenML's Langfuse vs Phoenix comparison.

If you're weighing Langfuse against Phoenix specifically, Arize's own FAQ on the two is a reasonably balanced starting point even though it's written by one of the vendors.

Get the weekly AI engineering brief

Agents, RAG, and the tools worth using — one practical email a week. Plus the free roadmap PDF.

By subscribing you agree to receive emails from AI Engineer Insights. Unsubscribe anytime. See our Privacy Policy.

Trajectory evals vs output-only evals

Tracing tells you what happened; evaluation tells you whether it was good. The mistake teams make is scoring only the final output — does the answer look right? — because that check misses an entire class of agent failures. Per Arize's research on agent evaluation, trajectory evaluation — scoring the sequence of tool calls and decisions the agent took, not just the end state — catches failures an output-only check can't:

  • • The agent called the wrong tool but still produced a final answer that happens to look correct.
  • • It ran an unsafe sequence of actions that nonetheless landed on a correct-looking end state.
  • • Context was lost across a multi-turn session, so a later turn silently contradicts an earlier one.
  • • The agent reports success, but the underlying system state never actually changed (a write that silently failed).

In practice this means scoring traces, not just outputs: given the full span tree for a request, check whether the tool call sequence matches what a correct run should look like, in addition to whether the final text response reads fine. If you're building retrieval into the agent, the same principle applies — faithfulness and context-precision metrics score the retrieval step, not just whatever the model wrote afterward.

How do you observe a multi-agent system specifically?

Once you're running multiple agents coordinating on one task, one rule matters more than any tool choice: Honeycomb's instrumentation guide specifies that each agent should carry its own gen_ai.agent.name attribute, and the calling agent — not the callee — is responsible for emitting the invoke_agent span for that handoff.

Get that backwards and your tracing UI shows two unconnected traces instead of one agent handing off to another — which defeats the purpose of tracing a multi-agent run in the first place. This is also where a tool's dedicated "agent graph" view (Langfuse has one) earns its keep: it renders the handoff chain visually instead of making you reconstruct it from a flat span list.

How to get started with agent observability

  1. Instrument with OpenTelemetry's GenAI semconv — use an SDK or integration that already emits gen_ai.* attributes rather than hand-rolling your own span schema.
  2. Pick one backend to start. Langfuse (self-hosted via Docker Compose) is the lowest-friction way to see your first traces without a procurement cycle.
  3. Name every agent. Set gen_ai.agent.name on every agent before you have more than one — retrofitting it later means re-tracing old incidents blind.
  4. Add trajectory checks, not just output checks, to your eval set — at minimum, assert the expected tool was called with reasonable arguments.
  5. Watch token usage per span, not just per request — gen_ai.usage.* attributes let you see exactly which model call is driving cost.

This also matters for the operational side of agents more broadly — see our MLOps best practices for how tracing fits into the rest of the production lifecycle, and our piece on AI agent memory if context loss across sessions (one of the trajectory-eval failure modes above) is what's biting you. If your agent runs untrusted code or tools, pair observability with proper sandboxing — tracing tells you what happened, sandboxing limits the damage when it's something you didn't want.

Go deeper on AI agent observability with AI

Ask your next question with this article as context. Opens in a new tab; you can edit the question before sending.

  • Which agent observability tool should I use — Langfuse, LangSmith, Phoenix, or MLflow?
  • How do I instrument my multi-agent system with OpenTelemetry GenAI spans?
  • How do I write a trajectory eval instead of just checking the final output?
  • What should I trace first if I only have an hour to add observability?

Frequently Asked Questions

What is AI agent observability?

AI agent observability is the practice of capturing, tracing, and evaluating everything an AI agent does during a request — not just a single request/response timing, but the full nested tree of model calls, tool calls, retrieval steps, and hand-offs to other agents. It's built on OpenTelemetry's GenAI semantic conventions, which standardize spans like invoke_agent, chat, and execute_tool so different tools can read the same traces.

How is AI agent observability different from traditional APM?

Traditional APM measures a request's duration and error rate as one span, which is fine when the server does one deterministic thing. An agent's single request can fan out into dozens of nested spans — a planning step, several model calls, multiple tool calls, and handoffs to sub-agents — and a slow or wrong answer could come from any one of them. APM gives you the total time; agent observability gives you the span tree that explains it.

What is the OpenTelemetry GenAI semantic convention?

It's a set of standard span names and attributes (gen_ai.operation.name, gen_ai.request.model, gen_ai.response.model, gen_ai.usage.input_tokens/output_tokens, gen_ai.agent.name, gen_ai.tool.name, gen_ai.conversation.id) that different tools emit and read the same way, covering operations like invoke_agent, chat, execute_tool, and plan. As of mid/late 2026 it's still marked 'Development' status in the OpenTelemetry spec, not yet frozen, so attribute names can still shift between releases.

Should I use Langfuse, LangSmith, Arize Phoenix, or MLflow for agent observability?

It depends mostly on your stack and hosting needs. Langfuse is OTel-native and MIT-licensed, a strong default if you want to self-host and need a dedicated multi-agent view. LangSmith is the obvious pick if you're already deep in LangChain/LangGraph, but it's closed-source and self-hosting is Enterprise-only. Arize Phoenix is the strongest choice if RAG/retrieval evaluation metrics matter as much as tracing. MLflow fits teams that already use it for experiment tracking and want tracing in the same tool, now with native GenAI semconv export.

What is trajectory evaluation for AI agents?

Trajectory evaluation scores the sequence of steps an agent took — which tools it called, in what order, with what arguments — rather than just checking whether the final output looks correct. It catches failures an output-only check misses: the agent picked the wrong tool but still produced a plausible-looking answer, it ran an unsafe sequence that happened to land on a correct-looking end state, or it claims success while the underlying system state never actually changed.

How do you instrument a multi-agent system for observability?

Give each agent its own gen_ai.agent.name attribute, and make sure the calling agent — not the agent being called — emits the invoke_agent span for that handoff. That convention is what lets a tracing UI reconstruct the full call graph between agents instead of a flat list of disconnected spans, which is the difference between seeing 'agent A handed off to agent B' and just seeing two unrelated traces.

Want 1:1 help? Book a session

5.0 · 18 reviews

Career guidance, resume & interview prep, or tech consulting — 1:1 with a Lead AI Engineer. Start with a free 30-min quick chat.

References

Found this useful? Share it.

Share:

Related Articles