- Set up the exporter as on Send traces from any agent.
- Wrap each turn in your own
agent.turnspan with theflow.*attributes, as in step 2. Setflow.agent_replyfrom the text you actually send, not from the framework’s output. - Get the framework’s spans into that trace, so Observe shows the model calls, tools and errors under the turn. This is the only part that differs per framework.
agent.turn span alone is enough for conversations and grading. If the framework’s spans end up in traces of their own, they are kept but not shown under any conversation. Do not copy the flow.* attributes onto those traces: Observe reads every trace with a flow.conversation_id as a turn of its own.
Frameworks record prompts and outputs in their spans by default. Observe stores them encrypted and does not show them today. If you would rather not send them, each section says how to turn recording off.
OpenAI Agents SDK
The SDK has its own tracing, which by default sends traces to OpenAI’s traces dashboard. It does not export OpenTelemetry itself. The OpenInference instrumentation turns its traces into OpenTelemetry spans, nested under your current span, and by default replaces the export to OpenAI.- OpenInference names spans its own way, so Observe shows these spans with their names, timings and errors, but leaves the model and token columns empty for them today.
- Do not turn the SDK’s tracing off (
OPENAI_AGENTS_DISABLE_TRACING,set_tracing_disabled): that stops the instrumentation too. To keep sending to OpenAI as well, passexclusive_processor=False(Python) ormanuallyInstrument(agents, { exclusiveProcessor: false })(TypeScript). - To leave prompts and outputs out of the spans:
RunConfig(trace_include_sensitive_data=False)(Python) ortraceIncludeSensitiveData: false(TypeScript).
LangChain and LangGraph
Use the OpenInference instrumentation. It sends LangChain’s and LangGraph’s runs as OpenTelemetry spans through your provider, nested under your current span, and never contacts LangSmith as long asLANGSMITH_TRACING (and the older LANGCHAIN_TRACING_V2) stay unset.
pip install "langsmith[otel]", then set LANGSMITH_TRACING=true, LANGSMITH_OTEL_ENABLED=true and LANGSMITH_OTEL_ONLY=true (without the last one, traces go to LangSmith too), with the provider from step 1 set before LangChain starts.
Vercel AI SDK
The AI SDK’s spans use the global OpenTelemetry provider and nest under your current span. Telemetry must be turned on, and the option’s name depends on your version:TypeScript
instrumentation.ts instead of step 1:
instrumentation.ts
- With
streamText, end theagent.turnspan only after the stream has been read to the end and sent. - Do not also set
OTEL_EXPORTER_OTLP_ENDPOINTwhen you pass atraceExporter:@vercel/otelwould export through both and send every span twice. - The AI SDK records prompts and outputs by default. To leave them out:
recordInputs: false, recordOutputs: falsein the telemetry options. - On AI SDK 5 and 6 you can also pass the ids as telemetry
metadata(metadata: { "flow.conversation_id": threadId }); Observe reads them there. AI SDK 7 removedmetadata, so theagent.turnspan is the way that works on every version.
Claude Agent SDK
The SDK runs Claude Code, which exports its own spans over OpenTelemetry when you turn it on through the SDK’senv option (a beta feature). Run query() inside your agent.turn span and the SDK passes your trace context on, so its spans join your trace. Needs claude-agent-sdk 0.1.60 or later installed as claude-agent-sdk[otel] (Python), or @anthropic-ai/claude-agent-sdk 0.2.113 or later (TypeScript).
- Its spans are named
claude_code.interaction,claude_code.llm_requestandclaude_code.tool. Observe shows them with their timings and errors; they use Claude Code’s own attribute names, so model and token columns stay empty for them today. - Prompts and responses are left out of these spans unless you set
OTEL_LOG_USER_PROMPTS=1orOTEL_LOG_ASSISTANT_RESPONSES=1. You do not need them:flow.user_messageandflow.agent_replycarry the text. - Claude Code batches spans and fails quietly. Set
CLAUDE_CODE_OTEL_DIAG_STDERR=1to see export errors. Never use theconsoleexporter: standard output is the SDK’s message channel.
Mastra
Mastra’s tracing has its own OpenTelemetry exporter,@mastra/otel-exporter. It does not join your active span by itself: pass your turn span’s trace id and span id in tracingOptions, and Mastra’s spans nest under it. Your agent.turn span is still exported by your own setup from step 1.
TypeScript
- Put the
flow.*attributes on youragent.turnspan. Observe also reads them from Mastra’smetadata(exported asmastra.metadata.flow.*), but Mastra exports its spans apart from yours, so keep the ids on the turn span with the texts. signals: { logs: false }keeps the exporter from also posting logs.- In a serverless function,
await mastra.observability.flush()before returning, besides flushing your own provider. - To leave prompts and outputs out of Mastra’s spans:
tracingOptions: { hideInput: true, hideOutput: true }.
Another framework
Any framework that emits OpenTelemetry spans through the global provider works like the Vercel AI SDK above: run it inside youragent.turn span. One that has its own tracing system needs a bridge to OpenTelemetry, as the OpenAI Agents SDK and LangChain do. If you cannot get its spans into your trace, your agent.turn span is still enough for conversations and grading.