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.name、gen_ai.agent.step.type 等属性,让每个 Span 不仅说明"做了什么技术操作",还说明"在 Agent 流程中处于什么位置"。
第二,区分不同来源的内容。 Agent 的输入可能来自用户、来自上一步推理结果、来自检索系统;输出可能是给用户的最终回复,也可能是给下一步的中间指令。LoongSuite 用 gen_ai.message.source、gen_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_tokens 和 gen_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_size 和 timeout 能跟上这个吞吐量,避免 Trace 片段丢失。
LoongSuite GenAI 规范的价值不在于定义了多少属性,而在于它让 AI 系统的运行过程从黑盒变成了可追溯的链路。当你能清楚地看到 Agent 在第几步选了哪个工具、消耗了多少 token、为什么走向了某条分支时,优化和排错才有了扎实的数据基础。如果你已经在用 OTel 做服务可观测,接入这套规范的增量成本很低——换一个语义更精确的 Attributes 集合,换来的是对 AI 行为的真正理解。