“Why did the agent reimburse that expense claim at 3 a.m.?” With classic code, you’d read the logs and the stack trace. With an agent, the “why” lives elsewhere: in the conversation — which tools it called, with which arguments, what it read, what the model answered. A non-deterministic system can’t be debugged with printf.

The industry’s answer is called OpenTelemetry — and the Agent Framework article promised it in one line: “you replay the tape instead of guessing”. Today, we set up the screening room. You’ll see: it’s not rocket science.

OpenTelemetry in 60 seconds

OTel is the open standard for observability — three signals: logs (events), metrics (counters), and above all traces: the complete story of a request, cut into nested spans. A span = one operation with a start, an end, and attributes. Stacked together, they form the timeline of what actually happened — who called what, how long, with what result.

If you do modern ASP.NET, you’re already doing this without knowing: every HTTP request traces its span. What’s new is that the AI world has standardized its own spans.

The GenAI conventions: the shared vocabulary

OpenTelemetry defines GenAI semantic conventions: standard span names and attributes for AI operations. The three spans that tell the whole story:

Span What it tells Its key attributes
invoke_agent one full agent turn agent name, model
chat one model call model, input/output tokens, finish reason
execute_tool one tool call tool name, arguments

The practical consequence is huge: since everyone speaks this language — Agent Framework, the SDKs, the backends — your observability tool understands your agents without custom configuration. And you recognize the attributes: the tokens from the bill, the tool calls from the loop — the series’ theory becomes columns in a dashboard.

Enabling it in Agent Framework: three lines (or zero)

Agent Framework instruments everything — agents, tools, workflows — as soon as you ask:

// At startup: wire OTel and export via OTLP (to Aspire, App Insights…)
builder.Services.AddOpenTelemetry()
    .WithTracing(t => t.AddSource("*Microsoft.Agents.AI").AddOtlpExporter())
    .WithMetrics(m => m.AddMeter("*Microsoft.Agents.AI").AddOtlpExporter());

And the “zero-code” version exists: environment variables are enough to enable instrumentation and export — precious for instrumenting without redeploying. One conscious choice remains: by default, traces contain the mechanics (which calls, which tokens, which durations) but not the content of prompts and responses — enabling that is an explicit opt-in. Keep the reflex straight from the memory article: sensitive data travels in those conversations; in production, trace the mechanics, sample the content, and govern who accesses it.

The local screening room: the Aspire dashboard

For development, no cloud backend needed: the Aspire dashboard is a standalone, free screening room that runs in one container:

docker run --rm -p 18888:18888 -p 4317:18889 \
  mcr.microsoft.com/dotnet/aspire-dashboard

Point your OTLP export at it (http://localhost:4317), run your agent, open localhost:18888 — and the movie appears: each request’s timeline, the invoke_agent span wrapping three chats and five execute_tools, tokens per call, durations, errors in red. The question “why did it do that?” becomes: click the span and read. (And if your project already runs on Aspire — wink at the base prompt — the dashboard is already there, for free.)

In production, same mechanics, different screen: Application Insights, or any OTLP backend — the GenAI conventions mean the “AI” views light up on their own.

What you’ll see (and stop guessing)

The classic discoveries of the first hours of agent observability — lived:

  • The silent tool loop: the agent calls the same tool eight times with argument variations — invisible in the final answer, glaring in the timeline. (Often a mute error message that doesn’t help it self-correct.)
  • The swelling context: input tokens climbing turn after turn — dilution made measurable, and the cache rate (yesterday’s cached_tokens) collapsing after a compaction.
  • The slow subagent: 80% of the latency in a single nested invoke_agent — an immediate candidate for a smaller model.

And the final virtuous loop, promised by the evals article: production traces feed the golden dataset. The case that derailed last night, exported from the dashboard, becomes the eval that prevents the regression. Observe → understand → test → redeploy: the full cycle of a grown-up AI application.

In summary

  • An agent isn’t debugged with logs: it’s replayed — OTel traces and spans tell who called what, with which tokens and what result.
  • The GenAI conventions standardize the vocabulary (invoke_agent, chat, execute_tool): your tools understand your agents without custom work.
  • Agent Framework instruments itself in three lines (or via environment variables); conversation content stays an opt-in to govern.
  • The Aspire dashboard is the free local screening room; App Insights or any OTLP backend takes over in production.
  • And traces close the quality loop: last night’s incident becomes tomorrow’s eval.

The full movie of every agent decision, one click away: that’s what separates “we hope it works” from “we know what’s happening”. And that, honestly… is not rocket science.