OpenTelemetry GenAI 语义约定


文档摘要

OpenTelemetry GenAI 语义约定 本节摘要:每家厂商都自创 span 名,运维团队最后要为每个框架单独搭仪表盘——Agent 的追踪在 Datadog、Grafana、Jaeger、Honeycomb 里长得都不一样,根本无法横向比较。OpenTelemetry 的 GenAI SIG(2024 年 4 月成立)给出了一份标准模式,让整个生态都向它对齐。三类 span:模型/客户端 span(覆盖裸 LLM 调用,由提供者 SDK 与框架适配器发射)、Agent span( 构造时、 运行时)、工具 span(每次工具调用一个,经父子关系挂到 Agent span 上)。

OpenTelemetry GenAI 语义约定

本节摘要:每家厂商都自创 span 名,运维团队最后要为每个框架单独搭仪表盘——Agent 的追踪在 Datadog、Grafana、Jaeger、Honeycomb 里长得都不一样,根本无法横向比较。OpenTelemetry 的 GenAI SIG(2024 年 4 月成立)给出了一份标准模式,让整个生态都向它对齐。三类 span:模型/客户端 span(覆盖裸 LLM 调用,由提供者 SDK 与框架适配器发射)、Agent span(create_agent 构造时、invoke_agent 运行时)、工具 span(每次工具调用一个,经父子关系挂到 Agent span 上)。invoke_agent 又分两种 span kind:CLIENT(远程 Agent 服务,如 OpenAI Assistants、Bedrock Agents)与 INTERNAL(进程内 Agent 框架,如 LangChain、CrewAI、本地 ReAct)。关键属性:gen_ai.provider.name(anthropic/openai/aws.bedrock/google.vertex)、gen_ai.request.modelgen_ai.response.modelgen_ai.agent.namegen_ai.operation.namegen_ai.data_source.id(RAG 命中哪个语料库)。内容捕获(content capture)是本节最重要的安全契约:默认不捕获输入/输出(防止 PII、密钥、客户数据进可被运维读取的追踪);要捕获必须显式 opt-in(gen_ai.system_instructionsgen_ai.input.messagesgen_ai.output.messages),且推荐生产里内容外部存储(S3/日志库)、span 上只记指针 ID——这正是第 27 节内容投毒防御在可观测性层的接线。本节吃透这套约定,并用标准库实现一个对齐 GenAI 约定的 span 发射器。

对应原课程:Phase 14 · Lesson 23 · otel-genai-conventions(原英文 phases/14-agent-engineering/23-otel-genai-conventions/docs/en.md)。前置:第 13 节(LangGraph)、第 24 节(可观测性平台)。

学习目标

阅读完本节,你应当能够:

  1. 说出 GenAI 的三类 span:模型/客户端、Agent、工具
  2. 区分 invoke_agentCLIENT 与 INTERNAL 两种 span kind,以及各自适用场景。
  3. 列出顶层 GenAI 属性:provider name、request model、data-source ID 等。
  4. 解释内容捕获契约:opt-in、OTEL_SEMCONV_STABILITY_OPT_IN、外部引用推荐。
  5. 识别四种失败:在 span 里捕完整提示、缺 provider name、span 无父链、未设稳定性 opt-in。

一、问题与直觉

每个 Agent 框架都自己定义遥测:CrewAI 的 span 名和 LangGraph 的不一样,OpenAI 的又和 Claude 的不一样。结果:运维团队为每个框架单独搭仪表盘,跨框架的告警无法共享,「这个工具错误是不是只发生在某个模型上」这种基本问题答不出来。

OpenTelemetry 的 GenAI SIG 解决的就是这件事——一份标准模式,整个生态都对齐。这样,无论你的 Agent 用什么框架,它的追踪在 Datadog、Grafana、Jaeger、Honeycomb 里长得一样、能横向比较

三类 span

  1. 模型/客户端 span(Model/client spans) —— 覆盖裸 LLM 调用。由提供者 SDK(Anthropic、OpenAI、Bedrock)与框架模型适配器发射。
  2. Agent span —— create_agent(Agent 构造时)与 invoke_agent(Agent 运行时)。
  3. 工具 span(Tool spans) —— 每次工具调用一个;经父子关系连到 Agent span 上。

Agent span 命名

  • span 名:invoke_agent {gen_ai.agent.name}(若有命名);否则回退 invoke_agent
  • span kind:
    • CLIENT —— 远程 Agent 服务(OpenAI Assistants API、Bedrock Agents)。
    • INTERNAL —— 进程内 Agent 框架(LangChain、CrewAI、本地 ReAct)。

关键属性

  • gen_ai.provider.name —— anthropicopenaiaws.bedrockgoogle.vertex
  • gen_ai.request.model —— 请求的模型 ID。
  • gen_ai.response.model —— 实际解析到的模型(可能因路由与请求不同)。
  • gen_ai.agent.name —— Agent 标识符。
  • gen_ai.operation.name —— chatcompletioninvoke_agenttool_call
  • gen_ai.data_source.id —— RAG 用:查的是哪个语料库或存储。

针对 Anthropic、Azure AI Inference、AWS Bedrock、OpenAI 各有技术专用约定。

内容捕获

默认规则:仪表化**不应(SHOULD NOT)**默认捕获输入/输出。捕获是显式 opt-in:

  • gen_ai.system_instructions
  • gen_ai.input.messages
  • gen_ai.output.messages

推荐的生产模式:内容外部存储(S3、你的日志库),在 span 上记录引用(指针 ID,而非原文)。这是第 27 节内容投毒防御在可观测性层的接线——避免把 PII、密钥、客户数据写进运维可读的追踪。

稳定性

截至 2026 年 3 月,大多数约定仍是实验性的。用稳定预览 opt-in:

OTEL_SEMCONV_STABILITY_OPT_IN=gen_ai_latest_experimental

Datadog v1.37+ 把 GenAI 属性原生映射进其 LLM 可观测性模式;其他后端(Grafana、Honeycomb、Jaeger)支持原始属性。

⚠️ 四种失败模式:① 在 span 里捕完整提示——PII、密钥、客户数据进可被运维读取的追踪;应外部存储。② gen_ai.provider.name——多提供者仪表盘在归因缺失时崩。③ span 无父链——孤立的工具 span;永远要传播上下文(第 17 节的 W3C)。④ 未设稳定性 opt-in——你的属性可能在后端升级时被重命名。

二、从零实现

原课程 code/main.py 实现一个对齐 GenAI 约定的标准库 span 发射器:

  • Span,带 GenAI 属性模式。
  • Tracer,带 start_span、嵌套上下文。
  • 一个脚本化 Agent 运行,发射:create_agentinvoke_agent(INTERNAL)、每工具 span、LLM 调用的 chat span。
  • 一个内容捕获模式:提示外部存储、span 上只记 ID。

Step 1:Span 与 GenAI 属性

@dataclass class Span: name: str kind: str # CLIENT / INTERNAL parent: "Span" = None attrs: dict = field(default_factory=dict) def agent_span(tracer, agent_name, kind="INTERNAL"): return tracer.start_span( name=f"invoke_agent {agent_name}", kind=kind, attrs={"gen_ai.agent.name": agent_name, "gen_ai.operation.name": "invoke_agent"})

Step 2:工具 span 与模型 span(父子挂接)

def tool_span(tracer, parent, tool_name): return tracer.start_span( name=f"tool {tool_name}", parent=parent, attrs={"gen_ai.operation.name": "tool_call"}) def chat_span(tracer, parent, provider, model): return tracer.start_span( name="chat", parent=parent, attrs={"gen_ai.provider.name": provider, "gen_ai.request.model": model, "gen_ai.operation.name": "chat"})

Step 3:内容捕获(外部存储 + 指针 ID)

class ExternalStore: def __init__(self): self.rows = {} def put(self, content): rid = uuid4().hex; self.rows[rid] = content; return rid # 生产模式:span 上只记引用 ID,原文留外部存储 span.attrs["gen_ai.input.messages"] = f"ref://{store.put(prompt)}"

运行 python3 code/main.py 会输出:一棵带全部必需 GenAI 属性的 span 树,以及一个展示 opt-in 内容引用的「外部存储」。

💡 设计要点:内容捕获「默认关」是安全契约而非偷懒——Agent 处理的提示里常有 PII、密钥、客户数据,默认捕获等于把它们写进任何能读追踪的人手里。把内容推到访问受控的外部存储、span 上只留指针 ID,是「可观测性」与「数据安全」的平衡点,也是第 27 节防御在追踪层的落点。

三、框架对比

后端/平台 GenAI 支持程度
Datadog LLM Observability(v1.37+) 原生映射 GenAI 属性
Langfuse / Phoenix / Opik(第 24 节) 自动仪表化生态
Jaeger / Honeycomb / Grafana Tempo 原始 OTel 追踪,按 GenAI 属性建仪表盘
自托管 OTel Collector + GenAI 处理器

四、可复用产物

原课程 outputs/skill-otel-genai.md:把 OTel GenAI span 接进一个已有 Agent,带内容捕获默认关、外部引用存储、稳定性 opt-in 配置。

五、练习

  1. (Easy) 给你的第 01 节 ReAct 循环加 invoke_agent(INTERNAL)+ 每工具 span,发到一个 Jaeger 实例。
  2. (Medium) 加「仅引用」模式的内容捕获:提示存 SQLite,span 属性只带行 ID。
  3. (Medium)gen_ai.data_source.id 规范,把它接进你第 09 节的 Mem0 搜索。
  4. (Hard)OTEL_SEMCONV_STABILITY_OPT_IN=gen_ai_latest_experimental,验证你的属性没被 collector 重命名。
  5. (Hard) 建一个仪表盘:「哪个工具错误与哪个模型相关」,只用 GenAI 属性。

本节要点回顾

  1. GenAI SIG 统一模式:2024-04 成立,让 Agent 追踪跨框架、跨后端长得一样、可横向比较。
  2. 三类 span:模型/客户端(裸 LLM 调用)、Agent(create_agent / invoke_agent)、工具(每次一个,父子挂接)。
  3. invoke_agent 两种 kind:CLIENT(远程服务如 Assistants/Bedrock)、INTERNAL(进程内如 LangChain/CrewAI/ReAct)。
  4. 关键属性:provider.name、request/response.model、agent.name、operation.name、data_source.id(RAG 语料库)。
  5. 内容捕获默认关:SHOULD NOT 默认捕输入/输出;opt-in 经 system_instructions / input.messages / output.messages。
  6. 生产外部存储:内容推 S3/日志库,span 上只记指针 ID——可观测性与数据安全的平衡,第 27 节防御在追踪层的落点。
  7. 稳定性 opt-in:OTEL_SEMCONV_STABILITY_OPT_IN=gen_ai_latest_experimental 钉实验约定,防升级重命名。
  8. 后端支持:Datadog v1.37+ 原生映射;Langfuse/Phoenix/Opik 自动仪表化;Jaeger/Honeycomb/Tempo 走原始属性。
  9. 四种失败:捕完整提示(PII 泄露)、缺 provider.name(多提供者仪表盘崩)、span 无父链(孤立)、未设 opt-in(被重命名)。
  10. 跨进程汇成一条 trace:第 17 节的 W3C 传播让子进程 span 挂到调用方 trace 上,GenAI 约定保证形状一致。

下一节,我们站在这些约定之上,看 Agent 可观测性平台——Langfuse、Phoenix、Opik、Arize 等如何用这套标准 span 形状,提供会话回放、成本归因、评估闭环与告警。


发布者: 作者: Rohit Gupta 转发
评论区 (0)
U