藏书里「组件级评测」一直是概念,这篇给出它的工程地基:Trace/Span 结构化轨迹、两大 tracing 标准选型(OTel GenAI vs OpenInference)、失败全量采的采样策略、以及 trace 回流成评测用例的闭环。没有 tracing,组件级评测是空中楼阁。
先修:a19(Harness 拆解)。准备做组件级评测或线上监控的团队:先把 tracing 按标准接起来,它是后续一切下钻与回流的地基。与 a26(线上评测)连读。
先合上原文在心里作答,再点开对照。全对 = 本篇真正通关。
Trace 是一次完整运行(= 一条 trial);Span 是运行里的一个动作(LLM 调用、工具调用、检索、子 Agent 交接),带父子关系、输入输出、状态、成本耗时。一次运行 = 一棵 span 树。
大技术栈统一遥测(含大量非 LLM 服务)选 OpenTelemetry GenAI semantic conventions;RAG 重、要深度调试 LLM 细节选 OpenInference。多数平台两者都支持。
成功请求按比例抽样(如 10%)留大盘样本;失败、报错、护栏触发、延迟/成本超阈值的请求 100% 全量采——平顺的少留几条够统计,出事的一条不能漏。
上方为本站原创导读,下方为原文完整收录,便于对照阅读。
上一讲,评测能跑起来了。但它现在还是个黑箱——一个 case 失败了,你只知道"分数低",不知道到底是哪一步坏的。这一讲,我们给它装上眼睛:可观测性与 Tracing。
一句话说清这一讲的价值:
没有 tracing,评测只能告诉你"病了";有了 tracing,你才能下钻到"病在哪一步"。
传统软件监控看什么?成功率、延迟、错误码——看"最终结果"。这对单轮服务够用,但对 Agent 远远不够。
因为 Agent 是多步、长程、会调工具和子 Agent 的——一次运行内部,藏着一长串推理、调用、决策。传统监控只能告诉你"这次任务失败了",可 Agent 的失败往往发生在第 7 步的某次工具调用、或某一步检索没召回、或推理链的中间跳了步——这些,看最终结果是根本看不见的。
tracing 干的事,就是把一次运行拆成一串可见的记录,让 Agent 内部那条原本黑箱的执行路径,变得透明、可查、可下钻。对 Agent 来说,tracing 不是锦上添花,是能不能定位问题的分水岭。
tracing 的世界里就两个核心概念:
一次 Agent 运行,就是一棵由 span 组成的树。这正是 transcript 的结构化形式:transcript 是"记录",而 trace 是"结构化、可查询、可下钻的记录"。评测 harness 里那个"轨迹采集器",采的就是这棵 span 树。
一条退款 trace 大致长这样:
Trace: 退款请求 #1234 [失败] → llm.plan "先查订单状态" 120ms → tool.getorder args{id:#1234} → paid,未发货 80ms ✅ → llm.decide "满足条件,退款" 90ms → tool.refund args{id:#1234} → ERROR: GATEWAYTIMEOUT 30s ❌ → llm.respond "退款已处理"(← 没看错误!)110ms → tool.send_email "已成功退款" → ok 200ms。Outcome: order #1234 = paid(未退款)。
看这棵树,那条"谎报成功"的 bug 一眼就定位到了:tool.refund 那个 span 明明返回了 GATEWAY_TIMEOUT,可下一个 llm.respond 却无视它、直接说"已处理"。没有 trace,你只看到"这单没退成";有了 trace,你精确知道是"退款超时 + Agent 没处理错误"。
一个 span 里该记什么?给你一份最小清单,每个 span 至少带上:
记全了,你才能既下钻定位(靠输入输出和状态)、又做非功能评测(靠成本耗时)。记漏了中间步骤,等于给自己留了盲区。
tracing 不只是调试工具,它是评测体系的地基材料。三个用途:
用途一:下钻定位。一个失败分数 → 调出这条 trace → 看是哪个 span 坏的:工具返回了错误?检索没召回?推理跳了步?这就是把"端到端失败"下钻到"组件级根因"的操作,而 trace 是唯一的下钻依据。
用途二:组件级评分的落地。"工具调用评测"、"单步检索质量"——这些组件级的评测,靠的就是能拿到单个 span。有了结构化的 trace,你才能对"某一次工具调用的参数"、"某一步检索的召回"分别打分,而不是只能评最终答案。这种"直接给 trace 里的 span 打分"的做法,业界叫 trace-based evals(基于轨迹的评测):评分器不只看最终 output,而是遍历 span 树,对每个感兴趣的环节各打一分。反过来说,你的 trace 记得多细,你能做的组件级评测就有多细;trace 是天花板。
用途三:生产轨迹回流成评测用例。线上的 trace,就是现成的真实用例。把线上失败的 trace 捞出来、固化成评测集里的新 task——这就是"评测集生长飞轮"的燃料来源。production traces become datasets(生产轨迹变成数据集),是这个飞轮转起来的关键。
单条 trace 让你看清"这一次"发生了什么;但要看"整体健康度",你得把成千上万条 trace 聚合起来。(顺带正名:传统可观测性的"三支柱"指 Metrics / Logs / Traces;这里借"三层"说法,讲 Agent 评测里层层递进的三件事——)
三者的关系是一条链:监控发现大盘异动 → 追踪下钻到具体的坏 trace → 评测给这些 trace 打分、并把典型失败固化成用例。tracing 是中间那根轴——没有它,监控只是一堆没有细节的数字,评测也无从定位。
很多团队一开始自己打日志记轨迹,格式五花八门。问题是:换个可观测平台、换个 Agent 框架,就得重写一遍采集和解析。现在这件事有标准了,直接用,别重复造轮子。两个主流标准:
OpenTelemetry GenAI semantic conventions。由 OpenTelemetry 的 GenAI SIG 制定,是一套标准化的 span 属性名、指标名、事件 schema——统一了 LLM 调用、agent 步骤、向量检索、token 用量、成本等的命名。它最大的价值是统一词汇:一个 LangChain agent 产生的 span,和一个裸 OpenAI 调用产生的 span,长得一模一样。适合"要在一个包含大量非 LLM 服务的大技术栈里统一遥测"的团队。
OpenInference(Arize/Phoenix)。更偏 LLM 专用的深度——完整捕获 prompt/completion、把检索作为一等公民的 span、更细粒度的 span 类型,并对 LangChain、LlamaIndex、CrewAI 等广泛自动埋点。适合"RAG 重、要马上能调试 LLM 细节"的生产 Agent。
怎么选?一句话:大栈统一遥测 → OpenTelemetry GenAI;要 LLM/RAG 的深度调试 → OpenInference。很多可观测平台两者都支持,你也不必二选一到底。
为什么一定要用标准而不是自己编?三个理由:可移植(换平台/框架不用重写采集)、可复用生态(现成的可视化、告警、分析都认这套格式)、面向未来(标准在演进,你搭个私有格式迟早要还技术债)。这就是这一讲要你记住的一个具体决策:tracing 从第一天起就按标准来。
tracing 有个特别重要的角色——它同时服务离线评测和线上监控,是连接两者的公共语言:
于是,线下评测和线上监控用的是同一种"语言"看 Agent——同样的 span 结构、同样的属性。这带来一个巨大的好处:线上发现的失败 trace,可以无缝地回流成线下的评测用例,因为它们本就是同一种格式。反之,如果线上线下各记各的格式,这个回流就处处是转换和摩擦。
记住这个定位:tracing 不只是调试工具,它是打通"离线评测 ⇄ 线上监控"的那个公共底座。
落地路径其实很短,三步:
从 MVP 角度,第一版哪怕只把"工具调用"和"最终输出"两类 span 记全,就已经能解决大半的定位难题——先接起来,再逐步记细。
这些和"评分器也是代码"是一个道理——tracing 基础设施本身也要被认真对待,它记漏了、记错了,你后面所有的下钻和组件级评测都是空中楼阁。