# Beta Telemetry

AG2 Beta includes a `TelemetryMiddleware` that emits [OpenTelemetry](https://opentelemetry.io/) spans for agent turns, LLM calls, tool executions, and human-in-the-loop interactions.

The middleware follows the [OpenTelemetry GenAI Semantic Conventions](https://opentelemetry.io/docs/specs/semconv/gen-ai/gen-ai-agent-spans/), so traces can be exported to **any compatible backend** \-- Jaeger, Grafana Tempo, Datadog, Honeycomb, Langfuse, and others.

## Installation

```
pip install "ag2[openai,tracing]"
```

## Quick Start

```
from opentelemetry import trace
from opentelemetry.sdk.resources import Resource
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import SimpleSpanProcessor, ConsoleSpanExporter

from autogen.beta import Agent
from autogen.beta.config import OpenAIConfig
from autogen.beta.middleware.builtin import TelemetryMiddleware

# 1. Configure OpenTelemetry
resource = Resource.create(attributes={"service.name": "ag2-beta-quickstart"})
tracer_provider = TracerProvider(resource=resource)
tracer_provider.add_span_processor(SimpleSpanProcessor(ConsoleSpanExporter()))
trace.set_tracer_provider(tracer_provider)

# 2. Create agent with telemetry middleware
agent = Agent(
    "assistant",
    prompt="You are a helpful assistant.",
    config=OpenAIConfig(model="gpt-4o-mini"),
    middleware=[\
        TelemetryMiddleware(\
            tracer_provider=tracer_provider,\
            agent_name="assistant",\
        ),\
    ],
)

# 3. Run -- spans are emitted automatically
import asyncio
reply = asyncio.run(agent.ask("What is the capital of France?"))
```

## Trace Hierarchy

Each `ask()` call produces a root span with child spans for LLM calls, tool executions, and human input:

```
invoke_agent assistant
  |-- chat gpt-4o-mini              # LLM API call
  |-- execute_tool get_weather      # tool execution
  |-- chat gpt-4o-mini              # LLM call after tool result
  +-- await_human_input assistant   # human-in-the-loop
```

## Span Types

Every span includes an `ag2.span.type` attribute:

| `ag2.span.type` | Operation name | Triggered by |
| --- | --- | --- |
| `agent` | `invoke_agent` | `on_turn` \- wraps the full agent turn |
| `llm` | `chat` | `on_llm_call` \- each LLM API call |
| `tool` | `execute_tool` | `on_tool_execution` \- each tool invocation |
| `human_input` | `await_human_input` | `on_human_input` \- human-in-the-loop |

## Semantic Attributes

Spans carry standard [OpenTelemetry GenAI attributes](https://opentelemetry.io/docs/specs/semconv/gen-ai/):

| Attribute | Span types | Description |
| --- | --- | --- |
| `gen_ai.operation.name` | All | Operation: `invoke_agent`, `chat`, `execute_tool`, `await_human_input` |
| `gen_ai.agent.name` | agent, human\_input | Agent name |
| `gen_ai.provider.name` | agent, llm | LLM provider (e.g. `openai`, `anthropic`) \- auto-detected |
| `gen_ai.request.model` | agent, llm | Model name (e.g. `gpt-4o-mini`) \- auto-detected |
| `gen_ai.response.model` | llm | Resolved model name from response |
| `gen_ai.response.finish_reasons` | llm | Finish reasons (e.g. `["stop"]`, `["tool_calls"]`) |
| `gen_ai.usage.input_tokens` | llm | Prompt token count |
| `gen_ai.usage.output_tokens` | llm | Completion token count |
| `gen_ai.usage.cache_creation_input_tokens` | llm | Tokens used to create prompt cache (Anthropic) |
| `gen_ai.usage.cache_read_input_tokens` | llm | Tokens read from prompt cache (Anthropic, OpenAI, Gemini) |
| `gen_ai.tool.name` | tool | Tool function name |
| `gen_ai.tool.call.id` | tool | Tool call ID |
| `gen_ai.tool.type` | tool | Tool type (always `function`) |

## Content Capture

By default, message content, tool arguments, and results **are** included in spans. To disable content capture for privacy-sensitive environments:

```
TelemetryMiddleware(
    tracer_provider=tracer_provider,
    agent_name="assistant",
    capture_content=False,  # omits messages, tool args, and results
)
```

When content capture is enabled (the default), spans include these additional attributes:

| Attribute | Span type | Content |
| --- | --- | --- |
| `gen_ai.input.messages` | llm | JSON request messages |
| `gen_ai.output.messages` | llm | JSON response messages |
| `gen_ai.tool.call.arguments` | tool | Tool call arguments (JSON) |
| `gen_ai.tool.call.result` | tool | Tool execution result |
| `ag2.human_input.prompt` | human\_input | Prompt shown to human |
| `ag2.human_input.response` | human\_input | Human's response |

Warning

With `capture_content=True`, message content, tool arguments, and human input will appear in your tracing backend. Ensure your backend has appropriate access controls.

## Custom Span Attributes

Use `span_attributes` to stamp custom key-value pairs onto spans the middleware emits. This is useful for routing or filtering traces by tenant, environment, deployment, or any other label your backend supports. Every span will carry these attributes.

```
TelemetryMiddleware(
    tracer_provider=tracer_provider,
    agent_name="assistant",
    span_attributes={
        "deployment": "production",
        "ag2.org.id": "org-abc123",
    },
)
```

Tip

This is the right place to add tenant or organization identifiers when your tracing backend filters traces by span-level labels (for example, Google Cloud Trace label filters or Datadog tags).

Note

If a key in `span_attributes` collides with an intrinsic attribute set by the middleware (such as `ag2.span.type` or `gen_ai.usage.input_tokens`), the middleware's value always wins.

## Configuration

`TelemetryMiddleware` accepts:

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `tracer_provider` | `TracerProvider | None` | Global provider | OpenTelemetry TracerProvider |
| `capture_content` | `bool` | `True` | Include message/tool content in spans |
| `agent_name` | `str | None` | `"unknown"` | Agent name for span attributes |
| `provider_name` | `str | None` | `None` | LLM provider name (auto-detected from response if not set) |
| `model_name` | `str | None` | `None` | Model name (auto-detected from response if not set) |
| `span_attributes` | `dict[str, str] | None` | `None` | Extra key-value pairs stamped onto every span (tenant IDs, environment tags, etc.) |

## Tool Execution Example

```
from autogen.beta import Agent
from autogen.beta.config import OpenAIConfig
from autogen.beta.middleware.builtin import TelemetryMiddleware
from autogen.beta.tools import tool

@tool
def get_weather(city: str) -> str:
    """Get weather information for a city."""
    return f"Sunny, 72F in {city}"

agent = Agent(
    "weather_agent",
    prompt="Use the get_weather tool to answer weather questions.",
    config=OpenAIConfig(model="gpt-4o-mini"),
    tools=[get_weather],
    middleware=[\
        TelemetryMiddleware(\
            tracer_provider=tracer_provider,\
            agent_name="weather_agent",\
        ),\
    ],
)
```

## Backend Integration

Since `TelemetryMiddleware` uses standard OpenTelemetry, any OTLP-compatible backend works. See the [V1 tracing documentation](https://docs.ag2.ai/latest/docs/user-guide/tracing/opentelemetry/) for setup guides for Grafana Tempo, Jaeger, Langfuse, and other backends. The setup is identical -- only the agent instrumentation method differs.
