在上一篇文章中,我们为 Agent 装上了安全护栏——输入净化、身份隔离、工具权限和输出过滤。但安全护栏只能“拦住”已知的威胁,真正要回答“Agent 为什么失败”以及“它到底变好了还是变坏了”,需要的是可观测性与评估。传统 APM 追踪的是确定性代码路径——同样的输入可靠地产生同样的输出。但 LLM 系统打破了这一假设:同样的提示词产生不同的输出,你无法在没有捕获精确输入、模型参数和温度的情况下复现问题;延迟和成本与 Token 数量相关而非请求数量,一个“慢”请求可能消耗 10 倍的预算;一个 Agent 处理一次用户请求可能发出 5 次 LLM 调用、3 次工具调用和 2 次向量数据库查询,每一个都是潜在的故障点,而单一的错误指标无法告诉你哪一步失败了。 本文从传统 APM 的局限讲起,深入讲解 OpenTelemetry GenAI 语义约定、Agent 评估的四支柱框架,以及 LangSmith Engine 如何把“追踪→诊断→修复”的循环自动化。
一、传统 APM 为什么不够用?
传统 APM 的信号与 LLM/Agent 系统的信号存在结构性差异:
数据来源:Uptrace(2026)
核心差异:传统 APM 告诉你“请求失败了”,Agent 可观测性需要告诉你“推理链的哪一步、因为什么原因、导致了什么结果”。一个真实的 Agent 栈中,单次用户请求现在会产生 10 到 50 个 span,跨越 LLM 调用、检索查询、工具调用、护栏检查和子 Agent 分发。
二、OpenTelemetry GenAI 语义约定:让 Agent 用“同一种语言”说话
OpenTelemetry 的 GenAI 语义约定为 LLM 和 Agent 定义了标准化的 gen_ai.* 属性。对于 Agent,每一次工具调用、LLM 调用和检索步骤都成为一个子 span,产生推理链的完整追踪。
2.1 三个起步必备属性
Agent Timeline 将多个 trace、多个 Agent 归并到一个对话视图中。要让这一点工作,执行链中的每一个 span 都必须携带三个属性:
一个容易被忽视的关键点:“GenAI span”不只是一次 LLM 调用。它是 Agent 触发的执行链上的任何 span,包括下游数据库查询、第三方 API 调用、或因为 Agent 决定调用工具而运行的后台任务。如果一个 span 因为 Agent 做了某件事而存在,它就应该携带 conversation.id。这就是 Timeline 能端到端工作的原因。
2.2 Span 设计:每个有意义的操作一个 span
最佳实践:一个 span 对应一个有意义的操作。一个真实的 Agent 栈中,单次请求产生 10-50 个 span 是正常的,但如果没有父子结构,追踪就是一堆扁平、不可搜索的 span。
工具调用的 span 设计(工具调用是大多数 Agent 故障的高发区):
withtracer.start_as_current_span(f"execute_tool{tool_name}")asspan:span.set_attribute("gen_ai.conversation.id",conversation_id)span.set_attribute("gen_ai.agent.name","support_agent")span.set_attribute("gen_ai.operation.name","execute_tool")span.set_attribute("gen_ai.tool.name",tool_name)span.set_attribute("gen_ai.tool.call.id",tool_call_id)span.set_attribute("gen_ai.tool.call.arguments",json.dumps(args))try:result=execute(tool_name,args)span.set_attribute("gen_ai.tool.call.result",json.dumps(result))span.set_attribute("gen_ai.response.finish_reasons",json.dumps(["stop"]))returnresultexceptExceptionase:span.set_attribute("error.type",type(e).__name__)span.set_status(Status(StatusCode.ERROR,str(e)))raise关键细节:工具失败时,设置 error.type 并把错误状态传播到父 span。Timeline 的“只看失败”模式和对话级失败计数都依赖于此。
2.3 LLM 调用的 Span 设计
withtracer.start_as_current_span("chat gpt-4o")asspan:span.set_attribute("gen_ai.conversation.id",conversation_id)span.set_attribute("gen_ai.agent.name","support_agent")span.set_attribute("gen_ai.operation.name","chat")span.set_attribute("gen_ai.request.model","gpt-4o")result=llm.invoke(...)span.set_attribute("gen_ai.response.model",result.model)# 实际使用的版本span.set_attribute("gen_ai.usage.input_tokens",result.usage.input_tokens)span.set_attribute("gen_ai.usage.output_tokens",result.usage.output_tokens)# 缓存 token 也必须记录,否则成本仪表盘会低估span.set_attribute("gen_ai.usage.cache_read.input_tokens",result.usage.cache_read)为什么 request.model 和 response.model 都要记录? 你请求的是 gpt-4o,实际得到的可能是 gpt-4o-2024-08-06 这样的带日期版本。捕获两者,才能在提供商静默升级模型后调试行为变化。
三、Agent 评估:超越“任务完成率”
任务完成率是二元的——完成或未完成。但 Agent 的非确定性引入了执行过程中的行为不确定性,这是二元指标无法捕获的。一项 2026 年的研究提出了一个端到端的 Agent 评估框架,包含四个评估支柱:LLM、Memory、Tools、Environment。
3.1 四支柱与关键指标
数据来源:Akshathala et al.,ACM 2026
这个框架的价值:它把评估从“只看结果”推进到“看过程”。一个 Agent 可能“完成了任务”,但它在过程中选错了工具、参数传错了、或者在错误发生后没有正确恢复——这些偏差在纯任务完成率指标下是隐形的。
3.2 评估的三种模式
该框架整合了静态、动态和 Judge 评估三种模式,以捕获超越任务成功率的行为失败:
静态评估:检查输出是否符合预定义的 Schema、格式、安全策略。
动态评估:在执行过程中监控行为(工具选择、参数映射、错误恢复)。
Judge 评估:用 LLM-as-Judge 对开放式输出进行质量评分。
四、LangSmith Engine:把“追踪→诊断→修复”自动化
可观测性让你看到问题,评估让你衡量问题,但传统流程中,“读 trace → 找模式 → 写评估 → 创建修复”仍然是一个手工循环。LangSmith Engine(2026 年 5 月进入公开测试)的目标是自动化这个循环。
4.1 Engine 的自动化闭环
Engine 是一个运行在 LangSmith 内部的 Agent,它执行以下循环:
监视生产 trace:持续分析你的生产追踪数据。
将失败聚类为命名问题:不是给你一堆散落的错误,而是把相似失败归并为“命名 Issue”。
针对代码库诊断根因:读取你的源代码,定位问题发生在哪一行、哪个工具描述、哪段提示词。
提出修复 PR:生成针对性的代码或提示词修复。
创建在线评估器:为这个具体问题生成一个自定义的在线评估器,让复发问题自动重新浮出水面。
将失败 trace 加入离线评估集:作为 ground truth 示例,防止回归。
你只需要 review 和 merge,而不是自己 hunting for problems。
4.2 Engine 背后的基础设施:SmithDB
Agent trace 的体量和形态——深度嵌套的 span、长时间运行的操作、分片到达的事件——对传统数据库是严峻挑战。LangSmith 构建了 SmithDB,一个专为 Agent 可观测性设计的数据库,基于 Rust、Apache DataFusion 和 Vortex,使用对象存储作为持久化层。
关键性能数据:核心 LangSmith 体验最高快 15 倍,P50 trace 树加载 92ms,P50 单次运行加载 71ms。它是无状态的、对象存储后端的,因此自托管和多云部署比传统数据库集群容易得多。
五、实战:为 LangGraph Agent 接入 OTel GenAI 追踪与评估
以下代码展示如何在 LangGraph 节点中注入标准化的 GenAI span 属性,并将评估分数附加到 span 上。
fromopentelemetryimporttracefromopentelemetry.traceimportStatus,StatusCode tracer=trace.get_tracer("customer-agent")definstrumented_agent_node(state:dict)->dict:conversation_id=state["conversation_id"]withtracer.start_as_current_span("invoke_agent customer_service")asspan:span.set_attribute("gen_ai.conversation.id",conversation_id)span.set_attribute("gen_ai.agent.name","customer_service")span.set_attribute("gen_ai.operation.name","invoke_agent")# 路由决策(对应 LLM 支柱的“指令遵循”评估)category=classify_query(state["query"])span.set_attribute("agent.routing.category",category)# 工具调用(对应 Tools 支柱)ifcategory=="order":withtracer.start_as_current_span("execute_tool check_order")astool_span:tool_span.set_attribute("gen_ai.conversation.id",conversation_id)tool_span.set_attribute("gen_ai.agent.name","customer_service")tool_span.set_attribute("gen_ai.operation.name","execute_tool")tool_span.set_attribute("gen_ai.tool.name","check_order")try:result=check_order(state["order_id"])tool_span.set_attribute("gen_ai.tool.call.result",str(result))exceptExceptionase:tool_span.set_attribute("error.type",type(e).__name__)tool_span.set_status(Status(StatusCode.ERROR,str(e)))raise# 评估分数附加到 span(让漂移检测运行在分数流上)eval_score=run_evaluator(state["query"],category)# 0.0-1.0span.set_attribute("agent.eval.routing_accuracy",eval_score)return{"response":"...","category":category}关键设计:
conversation.id 在所有 span 中传递,形成完整的对话视图。
工具失败时设置 error.type 和错误状态,让失败可被聚合分析。
评估分数直接附加到 span 上——这是让“漂移检测”能够运行在分数流上的关键。没有 span 附着的评估分数,只是孤立的数字。
六、可观测性与评估的最佳实践清单
数据来源:FutureAGI(2026)
如果只看一条:在每个 LLM span 上标记提示词版本。 没有它,生产调试循环在最常见的入口处就是断的。
七、小结
传统 APM 的局限:Agent 是非确定性的、多步骤的、不透明的。传统 APM 追踪确定性代码路径,无法回答“推理链的哪一步失败了”。
OTel GenAI 语义约定:用 gen_ai.* 属性标准化 Agent 追踪。三个起步属性:conversation.id、agent.name、operation.name。一个用户请求产生 10-50 个 span。
评估四支柱:LLM(指令遵循、安全)、Memory(存储、检索)、Tools(选择、参数、顺序、错误恢复)、Environment(护栏、授权)。
LangSmith Engine:自动化“监视 trace → 聚类失败 → 诊断根因 → 提出修复 PR → 创建在线评估器”的闭环。SmithDB 为 Agent trace 提供 15 倍加速和 92ms P50 树加载。
关键实践:工具失败必须设置 error.type;评估分数必须附着到 span;提示词版本必须标记在每个 LLM span 上。