Trace 和 Span:Agent 执行的可观测性
难度:中级 分类:Evaluation
简短回答
Trace 和 Span 是分布式追踪(Distributed Tracing)的核心概念,被引入 LLM/Agent 系统用于实现执行可观测性——理解 Agent "做了什么、花了多久、哪里出了问题"。Trace 代表一次完整的 Agent 执行(从接收任务到返回结果),Span 代表 Trace 中的一个操作单元(如一次 LLM 调用、一次工具调用、一次检索)。Span 之间有父子关系,形成树形结构,清晰展示 Agent 的决策链。OpenTelemetry (OTel) 是 LLM 可观测性的主流方向——OTel GenAI Special Interest Group 维护的 Semantic Conventions for Generative AI 截至 2026-05 仍处于 Development 阶段(尚未 Stable),定义了 GenAI 相关的语义约定(gen_ai.system、gen_ai.request.model 等属性)。主流工具(Langfuse、LangSmith 2026-01 起 end-to-end native OTel、Arize Phoenix)都已支持,但 API 名字可能在 GA 前微调,生产接入建议固定到具体语义版本。
详细解析
Trace 和 Span 的结构

一个 Trace 就是一棵 Span 树:每次 LLM / 工具调用都是可独立计时、可标错的子节点。
OpenTelemetry 集成
代码块
Python
from opentelemetry import trace
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor
# 初始化 OTel
provider = TracerProvider()
provider.add_span_processor(
BatchSpanProcessor(OTLPSpanExporter(endpoint="http://collector:4318"))
)
trace.set_tracer_provider(provider)
tracer = trace.get_tracer("agent-service")
class ObservableAgent:
"""带完整可观测性的 Agent"""
async def execute(self, task: str):
# 创建 Trace(顶层 Span)
with tracer.start_as_current_span("agent_execution") as root_span:
root_span.set_attribute("task", task)
root_span.set_attribute("agent.name", "research-agent")
# Step 1: 规划
plan = await self.plan(task)
# Step 2: 执行每个步骤
results = []
for step in plan.steps:
result = await self.execute_step(step)
results.append(result)
# 记录总体指标
root_span.set_attribute("total_steps", len(results))
root_span.set_attribute("task_success", True)
return self.synthesize(results)
async def plan(self, task):
with tracer.start_as_current_span("llm_call") as span:
span.set_attribute("gen_ai.system", "openai")
span.set_attribute("gen_ai.request.model", "gpt-4o")
span.set_attribute("gen_ai.operation.name", "planning")
response = await self.llm.invoke(task)
# 记录 token 使用
span.set_attribute("gen_ai.usage.input_tokens", response.usage.prompt_tokens)
span.set_attribute("gen_ai.usage.output_tokens", response.usage.completion_tokens)
return response
async def execute_step(self, step):
with tracer.start_as_current_span("agent_step") as span:
span.set_attribute("step.name", step.name)
if step.requires_tool:
# 工具调用子 Span
with tracer.start_as_current_span("tool_call") as tool_span:
tool_span.set_attribute("tool.name", step.tool_name)
tool_span.set_attribute("tool.input", str(step.tool_params))
result = await self.call_tool(step.tool_name, step.tool_params)
tool_span.set_attribute("tool.output_size", len(str(result)))
if result.error:
tool_span.set_status(trace.Status(trace.StatusCode.ERROR))
tool_span.record_exception(result.error)
return result
GenAI 语义约定(OTel 标准)
代码块
Python
# OTel 为 GenAI 定义的标准属性
otel_genai_attributes = {
# 系统信息
"gen_ai.system": "openai / anthropic / google",
"gen_ai.request.model": "gpt-4o / claude-sonnet-4-5",
# 请求参数
"gen_ai.request.temperature": 0.7,
"gen_ai.request.max_tokens": 4096,
"gen_ai.request.top_p": 1.0,
可观测性的三大支柱在 Agent 中的应用
代码块
JSON
observability_pillars = {
"Traces(追踪)": {
实际调试场景
代码块
Python
# 场景:Agent 在某个任务上失败了,如何用 Trace 定位问题?
debug_workflow = """
1. 找到失败的 Trace
→ 根据 request_id 或 error 状态筛选
2. 查看 Span 树结构
→ 定位哪个 Span 出错(红色标记)
3. 检查出错 Span 的详情
→ LLM 调用:看输入 Prompt 和输出
→ 工具调用:看参数和返回值
→ 错误信息:看异常类型和堆栈
4. 分析上下文
→ 前序 Span 的输出是否正常
主流工具对比
常见误区 / 面试追问
误区:"有日志就不需要 Trace" — 日志是离散的事件记录,Trace 是结构化的因果链。日志告诉你"发生了什么",Trace 告诉你"为什么发生"以及"事件之间的关系"。在多步 Agent 中,没有 Trace 几乎无法定位问题。
误区:"Trace 只在出问题时有用" — Trace 在日常监控中同样重要:发现性能瓶颈(哪个 Span 最慢)、优化成本(哪个 LLM 调用 token 消耗最多)、理解用户行为(Agent 通常走什么路径)。
追问:"如何控制 Trace 数据的存储成本?" — (1) 采样——不是每个请求都记录完整 Trace(如 10% 采样率);(2) 按需详细度——正常请求只记录关键 Span,错误请求记录全部细节;(3) 数据保留策略——Trace 数据保留 7-30 天。
追问:"OpenTelemetry 的优势是什么?" — 供应商无关性。用 OTel 标准化的数据可以发送到任何后端(Jaeger、Datadog、Langfuse)。换监控工具不需要改代码,只改 exporter 配置。这避免了供应商锁定。