Since T2.4, every call the Kladde makes to a model writes a span: in the Aspire dashboard you see which model took how long and how many tokens it used. Since T7, though, the Kladde does more than make single calls: an agent calls the model several times, with tools in between, and a workflow strings steps together. When a quote looks wrong or a round takes too long, you want to see the whole round: which step, which tool, which call, how many tokens.

In this lesson, all AI features of the Kladde get traces. Every span says which feature it belongs to, content appears in it only while developing, and in the cloud you count the tokens per feature with a query in Application Insights. All examples run without a model, with small stand-ins that give fixed answers.

Package Version What it is for
Microsoft.Extensions.AI 10.11.0 (since T2.1) UseOpenTelemetry on the chat client
Microsoft.Agents.AI 1.28.0 (since T7.1) UseOpenTelemetry on the agent
Microsoft.Agents.AI.Workflows 1.28.0 (since T7.4) WithOpenTelemetry on the workflow

Trace, span, attribute

A trace is everything that belongs to one thing, for example to one request to the Kladde. It consists of spans: each span is a piece of work with a start, an end and a name. Spans hang together like branches: the span of the request is the root, the agent's span hangs below it, and the calls to the model below that. Every span carries attributes, pairs of a name and a value that say what happened.

For AI applications, OpenTelemetry has conventions of its own, the Semantic Conventions for Generative AI. They define what the spans are called and which attributes they carry. Because all libraries follow them, tools such as the Aspire dashboard or Application Insights understand the spans, no matter whether they come from Microsoft.Extensions.AI, from Agent Framework or from another language. The most important ones:

Span Created by Important attributes
chat <model> every call to a model gen_ai.request.model, gen_ai.response.model, gen_ai.provider.name, gen_ai.usage.input_tokens, gen_ai.usage.output_tokens, gen_ai.response.finish_reasons
invoke_agent <name> every round of an agent gen_ai.agent.name, gen_ai.agent.id, the sum of the round's tokens
execute_tool <tool> every call of a tool gen_ai.tool.name, gen_ai.tool.call.id

The conventions aren't stable yet. OpenTelemetry lists them as "Development", and attribute names have changed in the last few versions. Agent Framework 1.28.0 follows version 1.37. So write your queries in a way that lets you change a name in one place, and after updating the packages, check once whether your queries still find anything.