用 OpenTelemetry 看 AI Agent 的每一步:LoongSuite GenAI 语义规范实战

2026-06-04 14 预计阅读时间:1 分钟
来源:my.oschina.net AI 摘要 原文链接

免责声明:本文为 AI 摘要整理,建议结合原文阅读。摘要可能省略上下文、版本差异或边界条件,不作为官方说明。

预计阅读时间:12 分钟

AI Agent 跑起来之后,最难回答的问题不是"它能不能用",而是"它刚才到底做了什么"。一个多步推理的 Agent 可能调了三次模型、检索了两段文档、执行了一个工具调用——中间任何一步出错,最终结果就偏了。传统日志和指标很难把这些串联起来,而阿里云基于 OpenTelemetry 推出的 LoongSuite GenAI 可观测语义规范,正是要解决这个问题:让每一条 AI 运行轨迹都能被结构化地采集、关联和回溯。

三类 Agent,一套采集语言

LoongSuite GenAI 规范的核心设计思路是:用统一的语义属性描述不同形态的 Agent 运行过程。它覆盖了三类主流 Agent 形态:

  • 单轮对话 Agent:用户发一条消息,模型直接返回结果。典型场景是 ChatBot、问答系统。
  • 多步推理 Agent:模型自主规划执行步骤,可能串联多次模型调用、工具调用和外部检索。ReAct、Plan-and-Execute 等模式都属于这一类。
  • 多 Agent 协作:多个 Agent 各司其职,通过消息传递或任务分发协同完成复杂任务。Swarm、AutoGen 等框架对应这种形态。

三类形态的调用链复杂度差异很大,但 LoongSuite 的做法不是为每种形态定义不同的 schema,而是在 OTel GenAI 语义规范的基础上扩展一组通用属性,让同一个 Span 体系能描述从单轮到多 Agent 的所有场景。这意味着你用一套采集配置就能覆盖从简单到复杂的全部 Agent 应用,不需要在架构演进时重新埋点。

语义属性怎么把 AI 调用"说清楚"

OTel 的 Trace 体系用 Span 描述一次操作,Span 上挂的 Attributes 就是语义规范要定义的部分。LoongSuite GenAI 在 OTel GenAI Semantic Conventions 之上做了关键扩展,重点解决两个问题:

第一,把模型调用和业务动作关联起来。 原生 OTel GenAI 规范能描述"调了哪个模型、token 用了多少",但没法表达"这次模型调用是 Agent 第 2 步推理的一部分,目的是生成工具调用参数"。LoongSuite 增加了 gen_ai.agent.step.namegen_ai.agent.step.type 等属性,让每个 Span 不仅说明"做了什么技术操作",还说明"在 Agent 流程中处于什么位置"。

第二,区分不同来源的内容。 Agent 的输入可能来自用户、来自上一步推理结果、来自检索系统;输出可能是给用户的最终回复,也可能是给下一步的中间指令。LoongSuite 用 gen_ai.message.sourcegen_ai.message.role 等属性把这些区分开,避免在回溯时把中间指令误当成用户输入。

下面是一个多步推理 Agent 的典型 Trace 结构示意:

Trace: user_query_001
├── Span: agent.plan          (gen_ai.agent.step.type=planning)
   ├── Span: gen_ai.invoke   (model=gpt-4o, step=1)
   └── Span: tool.search     (tool.name=web_search, step=2)
├── Span: agent.execute       (gen_ai.agent.step.type=execution)
   ├── Span: gen_ai.invoke   (model=gpt-4o, step=3)
   └── Span: tool.code_run   (tool.name=python_executor, step=4)
└── Span: agent.respond       (gen_ai.agent.step.type=response)
    └── Span: gen_ai.invoke   (model=gpt-4o, step=5)

每个 Span 上的属性组合起来,就能完整还原 Agent 的决策路径和执行细节。

实战:用 OTel Python SDK 给 Agent 调用埋点

下面这段代码展示如何在一个多步推理 Agent 中,按照 LoongSuite GenAI 规范埋点。使用前需要安装依赖:

pip install opentelemetry-api opentelemetry-sdk opentelemetry-exporter-otlp

完整示例代码:

"""
LoongSuite GenAI 语义规范埋点示例
演示一个两步推理 Agent:先检索,再生成回复
运行前需启动 OTel Collector 接收数据
"""

from opentelemetry import trace
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor
from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter
from opentelemetry.sdk.resources import Resource

# 1. 初始化 Tracer — 标记服务身份
resource = Resource.create({
    "service.name": "my-agent-app",
    "service.version": "0.1.0",
})

provider = TracerProvider(resource=resource)
# 将 OTLP exporter 指向你的 Collector 地址
provider.add_span_processor(
    BatchSpanProcessor(OTLPSpanExporter(endpoint="localhost:4317"))
)
trace.set_tracer_provider(provider)

tracer = trace.get_tracer("my-agent-app", "0.1.0")


# 2. 模拟模型调用,按规范挂属性
def call_llm(prompt: str, step_name: str, step_index: int) -> str:
    """模拟一次 LLM 调用,埋入 LoongSuite GenAI 语义属性"""
    with tracer.start_as_current_span("gen_ai.invoke") as span:
        # OTel GenAI 基础属性
        span.set_attribute("gen_ai.system", "openai")
        span.set_attribute("gen_ai.request.model", "gpt-4o")
        span.set_attribute("gen_ai.request.max_tokens", 1024)
        # LoongSuite 扩展属性:关联 Agent 步骤
        span.set_attribute("gen_ai.agent.step.name", step_name)
        span.set_attribute("gen_ai.agent.step.type", "reasoning")
        span.set_attribute("gen_ai.agent.step.index", step_index)
        # 消息来源
        span.set_attribute("gen_ai.message.source", "agent")
        span.set_attribute("gen_ai.message.role", "assistant")
        # 模拟返回
        span.set_attribute("gen_ai.response.finish_reason", "stop")
        span.set_attribute("gen_ai.usage.input_tokens", len(prompt))
        span.set_attribute("gen_ai.usage.output_tokens", 150)
        return f"[模拟回复] 基于 {prompt[:20]}... 生成的回答"


# 3. 模拟工具调用
def search_tool(query: str, step_index: int) -> str:
    """模拟一次检索工具调用"""
    with tracer.start_as_current_span("tool.search") as span:
        span.set_attribute("tool.name", "web_search")
        span.set_attribute("tool.input", query)
        span.set_attribute("gen_ai.agent.step.name", "retrieve_context")
        span.set_attribute("gen_ai.agent.step.type", "tool_call")
        span.set_attribute("gen_ai.agent.step.index", step_index)
        return f"[检索结果] 与 {query} 相关的 3 条文档片段"


# 4. 组装 Agent 主流程
def run_agent(user_query: str) -> str:
    """两步推理 Agent:检索 → 生成"""
    with tracer.start_as_current_span("agent.run") as root_span:
        root_span.set_attribute("gen_ai.agent.type", "react")
        root_span.set_attribute("gen_ai.agent.input", user_query)

        # Step 1: 检索上下文
        context = search_tool(user_query, step_index=1)

        # Step 2: 基于上下文生成回复
        augmented_prompt = f"上下文:{context}\n问题:{user_query}"
        answer = call_llm(augmented_prompt, step_name="generate_answer", step_index=2)

        root_span.set_attribute("gen_ai.agent.output", answer)
        return answer


# 5. 运行
if __name__ == "__main__":
    result = run_agent("LoongSuite GenAI 规范和 OTel 有什么关系?")
    print(f"Agent 回复:{result}")

运行后,在 OTel Collector 后端(如 Jaeger、Grafana Tempo)中,你会看到一条完整的 Trace,每个 Span 上都挂了规范定义的属性,能直接回答"这次 Agent 跑了几步、每步调了什么、token 消耗多少"。

如果要对接阿里云 ARMS 可观测服务,只需把 exporter endpoint 改为 ARMS 提供的 OTLP gRPC 地址,并加上认证 header:

from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter

exporter = OTLPSpanExporter(
    endpoint="https://your-arms-endpoint:4317",
    headers={"Authentication": "your-arms-token"},
)

多 Agent 协作场景的属性设计

当多个 Agent 协作时,Trace 的根 Span 不再是单个 Agent 的 agent.run,而是一个编排层的 agent.orchestrate。LoongSuite 为此增加了区分 Agent 身份的属性:

属性 作用 示例值
gen_ai.agent.id 标识参与协作的具体 Agent planner-001
gen_ai.agent.role 该 Agent 在协作中的角色 planner / executor / critic
gen_ai.agent.message.target 消息发送的目标 Agent executor-002

这样在回溯时,你能看到"planner 把任务分发给了 executor,executor 调了工具后把结果回传给 critic"的完整流转路径,而不是一堆散落的模型调用记录。

采用建议与注意事项

先从单轮对话开始埋点。 即使你的 Agent 还不复杂,先按规范把 gen_ai.invoke Span 和基础属性埋好,后续演进到多步或多 Agent 时,只需要在已有 Span 上追加扩展属性,不需要重构采集架构。

属性粒度要匹配你的调试需求。 gen_ai.agent.step.index 在两步推理时可能显得多余,但当 Agent 跑了 8 步、中间有分支和重试时,这个属性就是定位问题的关键。建议一开始就按完整规范埋,宁可多几个属性,也不要在出问题时补埋点。

注意 token 计量的准确性。 gen_ai.usage.input_tokensgen_ai.usage.output_tokens 应尽量从模型 API 的实际响应中取值(如 OpenAI 返回的 usage 字段),而不是用字符串长度估算。这直接影响成本追踪的可信度。

敏感内容要脱敏。 gen_ai.agent.input / gen_ai.agent.output 可以记录 Agent 的输入输出,但如果业务数据包含用户隐私,应该在写入 Span 剂量前做脱敏处理,或者只记录摘要而非全文。OTel SDK 支持在 SpanProcessor 中统一做这件事。

Collector 配置要适配 GenAI 的 Span 密度。 一个 5 步推理的 Agent 可能产生 10+ 个 Span,多 Agent 协作更会翻倍。确保 Collector 的 batch_sizetimeout 能跟上这个吞吐量,避免 Trace 片段丢失。


LoongSuite GenAI 规范的价值不在于定义了多少属性,而在于它让 AI 系统的运行过程从黑盒变成了可追溯的链路。当你能清楚地看到 Agent 在第几步选了哪个工具、消耗了多少 token、为什么走向了某条分支时,优化和排错才有了扎实的数据基础。如果你已经在用 OTel 做服务可观测,接入这套规范的增量成本很低——换一个语义更精确的 Attributes 集合,换来的是对 AI 行为的真正理解。


相关推荐