mcpField
A
ag2-telemetry

ag2-telemetry

github.com/ag2aiAgent workflows1264 installsUpdated 13w ago
Repository

About ag2-telemetry

ag2-telemetry is an OpenTelemetry instrumentation skill for AG2 agents. It helps developers trace agent turns, LLM calls, tools, and human input, with configurable content capture and standard GenAI attributes.

Key features

  1. 01Hierarchical spans for agent operations
  2. 02Token and model attributes
  3. 03Optional message and tool content capture
  4. 04OpenTelemetry exporter support

Use cases

  1. 01Break down latency across an agent turn
  2. 02Attribute token use to LLM calls
  3. 03Trace tool execution and human input
  4. 04Send traces to an OTLP-compatible backend

Install

Copy the command and run it in a terminal.

$npx skills add https://github.com/ag2ai/ag2-skills --skill ag2-telemetry

After installing, type/ag2-telemetryin Claude Code to confirm it loaded.

Details

From the project README

Telemetry — OpenTelemetry instrumentation

When to use

The user wants to:

  • See per-turn / per-call latency breakdowns
  • Attribute token usage across operations
  • Push traces to Jaeger, Grafana Tempo, Datadog, Honeycomb, Langfuse, etc.
  • Debug a slow agent end-to-end with structured spans rather than print statements

If they just want quick stdout debugging, point them at LoggingMiddleware instead (see ag2-middleware).

Installation

pip install "ag2[openai,tracing]"

Required. Run this install before delivering the code. If you cannot run commands, state the exact pip install command.

60-second recipe

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 ag2 import Agent
from ag2.config import OpenAIConfig
from ag2.middleware.builtin import TelemetryMiddleware

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

# 2. Wire the 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 emit automatically
import asyncio
asyncio.run(agent.ask("What is the capital of France?"))

For production, swap ConsoleSpanExporter for OTLPSpanExporter (or your backend's exporter) and SimpleSpanProcessor for BatchSpanProcessor.

Span hierarchy

Each ask() produces a root span with children:

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 has an ag2.span.type attribute:

ag2.span.typeOperation nameHook
agentinvoke_agenton_turn — full turn
llmchaton_llm_call — each LLM call
toolexecute_toolon_tool_execution — each tool
human_inputawait_human_inputon_human_input — HITL

Semantic attributes (GenAI semconv)

Spans carry standard OpenTelemetry GenAI attributes:

AttributeSpansDescription
gen_ai.operation.nameAllinvoke_agent / chat / execute_tool / await_human_input
gen_ai.agent.nameagent, human_inputAgent name
gen_ai.provider.nameagent, llmAuto-detected (openai, anthropic, …)
gen_ai.request.modelagent, llme.g. gpt-4o-mini
gen_ai.response.modelllmResolved from response
gen_ai.response.finish_reasonsllme.g. ["stop"], ["tool_calls"]
gen_ai.usage.input_tokensllmPrompt tokens
gen_ai.usage.output_tokensllmCompletion tokens
gen_ai.usage.cache_creation_input_tokensllmPrompt-cache writes (Anthropic)
gen_ai.usage.cache_read_input_tokensllmPrompt-cache reads (Anthropic, OpenAI, Gemini)
gen_ai.tool.nametoolTool function name
gen_ai.tool.call.idtoolTool call ID
gen_ai.tool.typetoolAlways function

Content capture (default ON)

By default, message content, tool args, and results are included on spans. Useful for debugging but can leak sensitive data:

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

When enabled, additional attributes appear:

AttributeSpanContent
gen_ai.input.messagesllmJSON request messages
gen_ai.output.messagesllmJSON response messages
gen_ai.tool.call.argumentstoolTool args (JSON)
gen_ai.tool.call.resulttoolTool result
ag2.human_input.prompthuman_inputPrompt shown to human
ag2.human_input.responsehuman_inputHuman's response

For privacy-sensitive backends (or anywhere telemetry leaves your infra), set capture_content=False.

Constructor reference

ParameterTypeDefaultDescription
tracer_providerTracerProvider | NoneGlobal providerOpenTelemetry TracerProvider
capture_contentboolTrueInclude message/tool content in spans
agent_namestr | None"unknown"Agent name for span attributes
provider_namestr | NoneNoneProvider override (auto-detected if unset)
model_namestr | NoneNoneModel override (auto-detected if unset)

Backend integration

TelemetryMiddleware uses standard OpenTelemetry, so any OTLP-compatible backend works:

  • Jaeger — OTLPSpanExporter(endpoint="http://localhost:4318/v1/traces")
  • Grafana Tempo — same OTLP exporter, point at the Tempo gateway
  • Langfuse, Honeycomb, Datadog — vendor-specific exporters; the agent-side setup is identical

For container-orchestrated stacks, this repo includes a tracing/ directory with Docker-Compose for otel-collector + Grafana Tempo.

Going deeper

  • website/docs/user-guide/telemetry.mdx — full attribute table, configuration, example.
  • tracing/ — Docker setup for local otel-collector + Tempo + Grafana.
  • For sibling middleware (logging, retry, history limits), see ag2-middleware.

Common pitfalls

  • SimpleSpanProcessor + ConsoleSpanExporter in production — synchronous, blocks every span emit. Use BatchSpanProcessor and a real exporter (OTLP / Jaeger / vendor) outside of dev.
  • Leaking content into telemetry — capture_content=True is the default. For privacy-sensitive prompts (PII, credentials), set capture_content=False and audit what your backend retains.
  • Forgetting trace.set_tracer_provider(...) — without it, tracer_provider you pass to the middleware is fine, but third-party libraries that auto-instrument may use a different provider.
  • Token usage missing — gen_ai.usage.* requires the provider client to surface usage in the response. Streaming providers may emit usage only at the end; if you don't see them, check the provider's response shape.
  • Span hierarchy doesn't show parent-child — your exporter or backend may need the OTLP/HTTP path enabled, not just OTLP/gRPC. Check both.
  • Comparing to AG2 Classic tracing docs — the semantic-attribute format is the same; only the agent instrumentation method differs (AG2 Classic uses instrument_agent() / instrument_llm_wrapper() / instrument_pattern(); AG2 uses TelemetryMiddleware).

Comments

Markdown supported