Skip to main content
Microsoft Agent Framework hero Microsoft Agent Framework (MAF) is an open-source SDK from Microsoft for building AI agents and multi-agent workflows. It succeeds Semantic Kernel and AutoGen, both now in maintenance mode, and ships with built-in OpenTelemetry instrumentation.
Not to be confused with the Microsoft 365 Agents SDK: Agent Framework (microsoft/agent-framework) is the orchestration layer, the M365 Agents SDK (microsoft/Agents) is the channel and hosting layer for Teams and Copilot. They compose. Agent Framework’s .NET package is Microsoft.Agents.AI, which despite the name is not part of the M365 Agents SDK.

Configuration

The integration works by sending trace data to Openlayer’s OpenTelemetry endpoint. The steps below use the Python SDK; the .NET and Go SDKs emit the same spans, but the setup calls differ.
1

Install the dependencies

Agent Framework depends on the OpenTelemetry API only and ships no exporter, so install one:
2

Set the environment variables

Two details are specific to Agent Framework:
  • Set the protocol explicitly. It defaults to OTLP over gRPC, and Openlayer’s endpoint speaks OTLP over HTTP — without this variable, no traces arrive.
  • Use the traces-specific variables. They create only a trace exporter. The generic OTEL_EXPORTER_OTLP_ENDPOINT also builds metric and log exporters, pointed at endpoints Openlayer does not serve.
3

Configure the OpenTelemetry providers

Call this before creating your agents:
If you already configure OpenTelemetry yourself — Azure Monitor distro, an OTel Collector, your own TracerProvider — skip that call. Agent Framework’s instrumentation is on by default and emits into whatever providers are globally registered; you only need to opt in to message content:
4

Use agents as usual

Trace data is captured and exported automatically. The example below is a travel concierge with several function tools, a retrieval tool that embeds its query, and a specialist sub-agent exposed as a tool:

Capturing prompts and responses

Agent Framework captures no message content by default. Without opting in, prompts, completions and tool arguments/results are all omitted, and traces arrive looking healthy but carry nothing to evaluate. Opt in with ENABLE_SENSITIVE_DATA=true, or programmatically:
ENABLE_SENSITIVE_DATA gates the agent, chat and tool code paths only. It does not apply to workflow spans, which carry structural metadata either way — see Workflow tracing.

Resulting traces

Your Agent Framework interactions are published to Openlayer as:
  • Agent invocationsinvoke_agent spans, with agent names and instructions. Sub-agents invoked through as_tool() nest inside the caller’s execute_tool span.
  • LLM callschat spans, with messages, model parameters, token usage, and cost.
  • Tool callsexecute_tool spans, with function names, arguments, and results.
  • Embedding callsembeddings spans, with model and token usage. Calling get_embeddings() inside a tool keeps the span in the same trace.
The agent above produces all four step types:
The “Data” page of your data source shows the full trace, with per-span latency and tokens: Microsoft Agent Framework trace in Openlayer
Do not wrap your agent run in a root span. Agent Framework’s own samples open a manual parent span to print a trace ID for Application Insights. Openlayer builds a record from the root span, and a plain span carries no output — so the record arrives empty even though the content sits one level down. Let invoke_agent be the root span.
You can then create tests that run at a regular cadence on top of these records.

Workflow tracing

Agent Framework’s graph workflow engine emits its own spans outside the GenAI semantic conventions: workflow.build, workflow.run, executor.process, edge_group.process and message.send. Executor and edge-group spans nest under workflow.run, and are stored as generic steps with their attributes preserved under “Other fields.” Because Agent Framework opens a separate root span per top-level operation and never wraps them in a shared parent, an agent run followed by a workflow run arrives as three records: workflow.build fires when the graph is constructed, not when it runs — so a real application that builds once at startup gets one such record per process, not one per request. Routing decisions are the useful part: every edge evaluation is recorded, including messages silently dropped because a condition did not match.
edge_group.delivery_status is one of delivered, buffered, exception, dropped type mismatch, dropped target mismatch, or dropped condition evaluated to false. Two limitations:
  • Executor inputs and outputs are not on the spans. Agent Framework exposes them as workflow events, so no telemetry setting surfaces them. Read the executor_invoked and executor_completed events off workflow.run(..., stream=True) and record them yourself.
  • Causality between executors uses span links. A message.send span and the executor.process span it feeds are connected by an OpenTelemetry link rather than nesting, since executors do not nest. The tree still renders correctly — every span is a child of workflow.run — but the link edges are not shown.

Caveats

  • Duplicate content. Instrumenting the chat client and the agent captures prompts and responses twice.
  • MCP trace propagation. Agent Framework injects traceparent into tools/call for MCP sessions your process opens (MCPStreamableHTTPTool, MCPStdioTool, MCPWebsocketTool), but not for hosted connectors like FoundryChatClient.get_mcp_tool(...), where the provider runtime issues the call. Traces stop at that boundary — use a client-opened transport for end-to-end tracing.
  • No .env auto-loading. Call load_dotenv() before configuring the providers, or pass configure_otel_providers(env_file_path=".env").

See full Python example