Hotdry.

Article

Agent 编排追踪:OpenTelemetry GenAI 与 MCP 语义约定的 span 层级、去重与内容采集

依据 open-telemetry/semantic-conventions-genai 中 inference、execute_tool 与 MCP client span 的 Development 约定,说明 Agent 网关如何把 LLM 调用、工具执行与 tools/call 挂成可查询的 trace,并给出 MCP/GemAI 去重、params._meta 传播与 Opt-In 内容采集的可落地参数。

2026-07-05ai-systems

Agent 平台排障时常见两类盲区:一是只看到「一次 chat 很慢」,看不出慢在模型首 token 还是某个 tools/call;二是 MCP 走 Streamable HTTP 时,底层 HTTP span 与 JSON-RPC 消息对不上号。OpenTelemetry 已将 GenAI 与 MCP 相关语义约定迁到独立仓库 open-telemetry/semantic-conventions-genai,状态均为 Development—— 字段与要求级别仍可能变更,但已经足够指导网关与编排运行时的埋点形状。本文只讨论客户端视角的 span 树与属性策略,不评价某一厂商 SDK 是否已完整实现。

问题背景:三类操作、两套约定、一条用户请求

典型 Agent 一轮循环包含:

  1. Inference:向模型提供商发起 chat /generate_content 等调用,span 类型为 gen_ai.inference.clientgen_ai.operation.namechat 等预定义值。
  2. Execute tool:编排器或工具宿主执行模型选中的工具(本地函数、HTTP、数据库、MCP tools/call),对应 gen_ai.execute_tool.internalgen_ai.operation.name SHOULDexecute_tool
  3. MCP 往返:若工具走 MCP,规范建议优先使用 MCP 语义约定 而非通用 RPC span,因为单条 HTTP 流上可承载多条 JSON-RPC 消息,且 streaming 内有多次 message 交换。

规范对 conversation id 有明确边界:gen_ai.conversation.id 仅在编排器已有会话标识时填写;不得用新 UUID、trace id 或请求内容哈希凑数。Agent 产品若用 run_id / thread_id 持久化会话,应在网关层通过 span processor 写入该字段,而不是在每次无状态调用时伪造。

MCP 与 GenAI 在 tools/call 上可能产生重复 span:MCP 约定写明,若外层 GenAI 插桩已在 trace 工具执行,MCP 插桩 SHOULD NOT 再建独立 span,而应把 mcp.method.namemcp.session.id 等属性合并到已有 execute_tool span(可配置开关)。未做去重时,同一工具调用会出现「execute_tool + mcp.client」双子节点,延迟与错误率统计会被双计。

跨传输的上下文传播也不能只依赖 HTTP traceparent:MCP 规范要求通过 JSON-RPC params._meta 注入 W3C Trace Context(traceparenttracestate;Baggage 时含 baggage),键名不加 DNS 前缀;这与 SEP-414_meta 的约定一致。stdio 与 Streamable HTTP 下,仅靠 HTTP 头无法关联流内的单条 tools/call

可落地实现:span 树、命名、属性与网关参数

1. 推荐 trace 骨架(一轮 tool-use)

编排运行时作为 trace 根(INTERNAL 或业务自定义 agent.run),子 span 建议顺序与父子关系如下:

agent.run (INTERNAL, 业务自定义)
├── chat gpt-4o-mini (CLIENT, gen_ai.inference.client)
│     gen_ai.operation.name=chat
│     gen_ai.provider.name=openai
│     gen_ai.request.model / gen_ai.usage.*
└── execute_tool search_docs (INTERNAL, gen_ai.execute_tool.internal)
      gen_ai.operation.name=execute_tool
      gen_ai.tool.name=search_docs
      gen_ai.tool.call.id=<与模型 tool_call id 一致>
      mcp.method.name=tools/call          # MCP 工具时合并到此 span
      mcp.session.id=...
      jsonrpc.request.id=...

Inference span 命名:规范要求 span name SHOULD{gen_ai.operation.name} {gen_ai.request.model},例如 chat gpt-4o-mini。kind SHOULDCLIENT(即使模型在同进程,仍推荐 CLIENT 以区分边界)。

Execute tool span 命名SHOULDexecute_tool {gen_ai.tool.name}。下列属性规范要求在 span 创建时 提供(若提供),以便采样器决策:gen_ai.operation.namegen_ai.tool.namegen_ai.tool.typefunction / extension / datastore)。

字段 要求级别(execute_tool) 网关落地建议
gen_ai.tool.name Required 与模型 tool_call.name、MCP params.name 一致,勿拼接租户 id
gen_ai.tool.call.id Recommended 必填:关联幂等 dedup、审计与模型上下文
gen_ai.agent.name Conditionally Required 多 Agent 产品时写编排配置名,单 Agent 可省略
error.type 工具失败时 Conditionally Required 用低基数枚举:timeouttool_deniedjsonrpc_-32603,避免堆栈字符串

重试:GenAI span SHOULD 覆盖「逻辑操作」全时长(含自动重试),不要在每次 HTTP 重试上各开一个 inference span,否则 token 用量与延迟分位会被拆碎。

2. MCP client span 与 GenAI 去重

配置项 生产建议 说明
mcp.span.dedupe_with_execute_tool true 对齐规范 SHOULD:单 span 承载 MCP 属性
MCP span name tools/call {gen_ai.tool.name} 无低基数 target 时用 tools/call
mcp.protocol.version initialize 协商值一致 例:2025-06-18
mcp.session.id Streamable HTTP 会话 id 关联 SSE resume、网关路由
gen_ai.operation.name on MCP span Recommended 填 execute_tool 便于与 GenAI 仪表盘统一过滤

params._meta 注入 trace context 时,网关 MUST 保留 MCP 规范已有键,且仅对出站 client 请求注入;Server 侧 span 应以 _meta 提取的 context 为 parent,并 SHOULD link 当前 ambient HTTP context(规范 NOTE:MCP 与 HTTP 上下文独立)。

反向 RPCsampling/createMessageelicitation/create):仍属 MCP client/server span 范畴,mcp.method.name 填完整方法名;不要误记为 inference span,除非 Client 侧采样确实调用了宿主 LLM(那时应再挂子 inference span)。

3. Token、流式与 conversation 属性

属性 用途 注意
gen_ai.request.stream 仅当流式时设置 未设置表示非流式
gen_ai.response.time_to_first_chunk 流式首 chunk 延迟 与网关 TTFB SLA 对齐
gen_ai.usage.input_tokens / output_tokens 成本与容量 有计费单位时优先报 billed count(规范对 Cohere 等有说明)
gen_ai.conversation.compacted 上下文压缩标记 仅在为 true 时设置;不要写 false

gen_ai.tool.definitionsgen_ai.input.messagesOpt-In,且 definitions NOT RECOMMENDED 默认填充(体积大)。编排器若每轮发送完整 tool schema,应依赖采样或外部存储,而非默认全量进 span。

4. 内容采集三档策略(规范默认 + 生产)

OpenTelemetry GenAI 约定:SHOULD NOT 默认记录 instructions /inputs/outputs;用户可 Opt-In。应用应三选一:

  1. 默认:不记录 gen_ai.input.messagesgen_ai.output.messagesgen_ai.tool.call.arguments/result
  2. 预发 / 调试:记录在 span 或 event 上;结构化优先,不支持时 span 上可 JSON 字符串(见 OTEP 4485 进展说明)。
  3. 生产:hook 或 Collector 管道上传外部对象存储,span 只留引用;hook SHOULD 在采样决策前仍可被调用以便合规归档(与 span 是否 export 解耦)。

网关推荐默认参数:

建议值
记录 gen_ai.tool.call.arguments 关闭;开启时单字段 ≤ 4 KiB,密钥字段 redact
记录 gen_ai.tool.call.result 关闭;错误时只写 error.type + 简短 message
记录 gen_ai.input.messages 仅非生产租户;单条 content truncate 2 KiB
Trace 采样 根 span 可按租户 10%;错误与超慢(如 execute_tool > 30s)强制 retain
Baggage tenant_idrun_id 低基数键;禁止把用户 email 放入 baggage

5. 与业务 id 的对齐(非规范字段)

下列字段不在 GenAI 必选集中,但建议作为 resource attribute 或 baggage(低基数)统一写入,便于与 Job、dedup 表 join:

  • agent.run_id(业务 run,可映射到 gen_ai.conversation.id 若语义等同)
  • agent.tenant_id
  • mcp.server_id(目录登记 id,非 URL 全文)

避免用 server.address 承载 MCP Server 的完整带密钥 URL。

6. 最小手动埋点示例(伪代码)

自动插桩未覆盖的自定义工具,规范鼓励手动 execute_tool span:

with tracer.start_as_current_span(
    "execute_tool run_sql",
    kind=SpanKind.INTERNAL,
    attributes={
        "gen_ai.operation.name": "execute_tool",
        "gen_ai.tool.name": "run_sql",
        "gen_ai.tool.type": "datastore",
        "gen_ai.tool.call.id": tool_call_id,
    },
) as span:
    try:
        result = execute(...)
        # 默认不写 gen_ai.tool.call.result
        span.set_status(Status(StatusCode.OK))
    except TimeoutError:
        span.set_attribute("error.type", "timeout")
        span.set_status(Status(StatusCode.ERROR))
        raise

MCP 出站时在 tools/callparams._meta 合并当前 context 后再发 JSON-RPC。

风险与边界

Development 稳定性:GenAI/MCP 约定尚未 Stable,升级 semantic-conventions-genai 版本时需做属性重命名与仪表盘迁移;主仓库中旧 mcp.* registry 条目可能标记 deprecated,应以 genai 仓库为准。

双 span 与双指标:未开启 MCP/GemAI 去重时,SLO 与告警会对同一操作重复计数;mcp.client.operation.duration 与 execute_tool 时长高度相关,勿在同一面板上做简单相加。

结构化属性支持不均:部分语言 / SDK 对 span 上的复杂类型支持不完整,强制结构化可能导致导出失败;fallback JSON 字符串会损失可查询性,需在 Collector 侧解析。

隐私与合规:Opt-In 的 arguments/result/messages 可能含 PII、密钥与受监管数据;默认关闭是规范立场,不是「可偷懒」—— 开启时必须配套 redaction、保留期与访问审计。

conversation.id 误用:把 trace id 写入 gen_ai.conversation.id 违反规范意图,会导致跨 run 会话统计失真,且与 LLM 提供商侧 thread 无法对齐。

HTTP 与 MCP 父链:仅传播 HTTP traceparent 时,流内第二条 tools/call 可能挂错父 span;stdio 传输无 HTTP 头,必须走 _meta

采样与排障矛盾:对 execute_tool 仅用尾采样可能丢掉失败前的 inference 上下文;建议对 error.type 存在或 rpc.response.status_code 为 JSON-RPC 错误的 trace 做「错误保证采样」。

参考来源

ai-systems

内容声明:本文无广告投放、无付费植入。

如有事实性问题,欢迎发送勘误至 i@hotdrydog.com