Hotdry.

线上 Agent 一次用户任务往往跨越:API 网关 → 编排运行时 → 多次 chat/generate_content → 本地或远程 execute_tool → MCP tools/call。当 P95 延迟升到数分钟、且中间夹杂队列 Worker 重投时,仅靠应用日志里的 request_id 很难把「哪一轮模型决策触发了哪次工具超时」对齐到同一条因果链。分布式追踪的目标是把上述 hop 收敛到同一 trace-id,并在追踪后端按父子 Span 展开时间线。

W3C Trace Context 定义了 traceparent / tracestate 的 HTTP 头格式;OpenTelemetry 在独立的 semantic-conventions-genai 仓库中维护 GenAI 与 MCP 的 Span 命名与属性(当前文档状态为 Development,落地时应锁定约定版本并关注变更)。本文只讨论可在代码与网关配置中实现的传播与建模,不假设某一厂商托管 Agent 的专有字段。

invoke_agent 为根 Span,其下交替出现 chat 与 execute_tool,MCP 工具调用通过 params._meta 携带 traceparent
推荐的最小 Span 树:一次用户任务一个 invoke_agent(INTERNAL),每轮模型与每次工具各建子 Span;经 MCP 出站时在 _meta 注入 W3C 头字段。

问题背景:HTTP 头够不到 JSON-RPC 流内的单次 tools/call

与三层微服务 RPC 相比,Agent 链路有三个常见断点:

因此,Agent 平台的可观测性设计需要同时回答:入口如何进 trace进程内如何分层MCP 消息级如何续链

可落地实现:Span 分层、网关头、MCP _meta 与采样参数

1. Span 命名与关键属性(GenAI Agent 约定)

下列名称来自 OpenTelemetry GenAI Agent spans 文档,实现时应固定 gen_ai.operation.name 与 Span kind:

场景Span 名(SHOULD)Span kindgen_ai.operation.name
本进程编排一次用户任务invoke_agent {gen_ai.agent.name}INTERNALinvoke_agent
调用托管 Agent API(Bedrock 等)invoke_agent {gen_ai.agent.name}CLIENTinvoke_agent
执行工具(应用代码)execute_tool {gen_ai.tool.name}INTERNALexecute_tool
单次 LLM 请求gen-ai-spans.mdchat多为 CLIENTchat / generate_content

建议在创建 invoke_agent 时写入低基数、可用于采样的属性(约定中标注为 span 创建时应提供):gen_ai.agent.namegen_ai.provider.namegen_ai.request.model(若已知)。会话维度使用 gen_ai.conversation.id 与业务侧 thread id 对齐,但不要把该字段当作 W3C 的 trace-id 替代品——一次会话可包含多次独立 trace(例如用户点击「重试」)。

2. 入口网关:注入 traceparent

traceparent 格式为四段:{version}-{trace-id}-{parent-id}-{trace-flags},其中 trace-id 为 32 位十六进制、parent-id 为 16 位十六进制(见 W3C HTTP 头绑定文档)。若客户端未带头,网关可为每个新的用户任务生成根上下文并向下游传递;若客户端已带有效 traceparent,网关应原样转发并在下游 HTTP 客户端 span 上延续同一 trace-id

# 网关侧检查(伪逻辑)
# - 校验 traceparent 版本与十六进制长度
# - 非法则丢弃并生成新 trace-id(避免污染后端)
# - 禁止把 traceparent 写入响应体或 SSE 事件明文(仅请求链路透传)

incoming = request.headers.get("traceparent")
if not valid_traceparent(incoming):
    incoming = tracer.start_span("ingress").get_span_context().traceparent
forward_to_agent_runtime(traceparent=incoming)

编排服务收到请求后,应使用提取到的上下文作为 invoke_agent 的 parent,而不是再新建根 trace。

3. MCP:经 params._meta 注入 W3C 键(非 DNS 前缀)

MCP 约定要求:instrumentation 使用已配置的 OpenTelemetry propagator,在创建 JSON-RPC 请求或通知时,把上下文注入 params._meta;接收方从 params._meta 提取并作为 remote parent。尽管 MCP 规范通常期望 _meta 键为 DNS 前缀形式,传播用的 traceparenttracestatebaggage不加前缀SEP-414 对此有专门说明。

{
  "jsonrpc": "2.0",
  "method": "tools/call",
  "params": {
    "name": "search_docs",
    "arguments": { "query": "visibility timeout" },
    "_meta": {
      "traceparent": "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01",
      "tracestate": ""
    }
  },
  "id": 42
}

Agent 运行时在为某次 execute_tool 调用 MCP 客户端前,应在当前 execute_tool Span 的 context 下执行 inject,使 MCP 服务端 tools/call Span 的 parent 指向该工具 Span,而不是仅指向外层 HTTP POST。

4. 属性与事件:默认少写、按需 Opt-In

gen_ai.tool.call.argumentsgen_ai.tool.call.result 在约定中为 Opt-In。生产环境默认应关闭或做脱敏/截断,否则追踪后端会重复存储 prompt、PII 与密钥。优先记录:gen_ai.tool.namegen_ai.tool.call.id(若模型返回)、error.type(失败时)。

5. 推荐初始参数(可按流量调整)

参数建议初值说明
根采样率5%–10%(成功路径)Agent 单次 trace Span 数量多,全量采集成本极高;错误 trace 建议 tail sampling 保留
ParentBased 采样开启保证入口已采样的用户任务,子 Span(含 MCP)一致跟随
Baggage 键tenant_iddeploymentW3C Baggage 会跨 hop 传播;禁止放 token、API key
导出批延迟1–5sOTel SDK 批处理;长任务可在 invoke_agent 结束 force flush
Span 事件上限按后端配额勿把每个 SSE chunk 记为事件;记录首个 token / 首个 tool delta 时间戳即可
MCP vs execute_tool二选一为主 Span约定允许重叠;选一个作为 SLO 统计来源并在另一处降采样

6. 与异步 Worker 的衔接

若用户任务经 PostgreSQL 队列(见仓库内 SKIP LOCKED 队列文)异步执行,入队时应把当前 traceparent 序列化进任务 payload 或独立列;Worker claim 后先 extract 再创建 invoke_agent,否则队列延迟段在追踪上会与用户点击断开。注意:租约重投会产生新的 Worker 尝试,是否沿用同一 trace-id 属于产品选择——沿用便于端到端,新建可避免把多次失败叠在同一条瀑布图上。

风险与边界

验收清单

参考来源