
做 Agent 开发的人应该都有过这种经历测试报告里一排绿色 PASS你兴冲冲把它交到业务那边结果对方跑了一个真实场景回来劈头盖脸一句这什么东西绕了一圈还答错了。问题出在哪出在绝大多数评估只看 Agent 的最终输出而真正决定 Agent 质量的是它走完的那条路径。最近我用 Jev 把 Agent 运行时的 Trace 翻出来一个 Span 一个 Span 地评估算是把PASS 但路径不一样这件事彻底看清楚了。这个思路其实很简单把 Agent 一次任务中所有工具调用、推理步骤、分支选择记录成标准化的 Trace再用 Jev 对 Trace 里的每个 Span 单独打分最后汇总成路径质量分。它不是简单地问结果对不对而是追问这段路走得值不值。这篇文章适合正在搭 Agent 评测体系的开发者、做 Agent 应用落地的工程师以及所有被测试通过但线上翻车折磨过的人。1. 为什么同样 PASS是个假信号1.1 Agent 评估的常见误区传统的评估方式是把 Agent 当黑盒输入一个测试用例拿到最终回答然后用字符串匹配、规则判断或者大模型打分能对上就给 PASS。这种方式在单轮问答场景下够用但放到 Agent 场景里问题就大了。Agent 的本质是一个会行动的模型。它不只生成文字还会调用工具、读取数据库、发起 HTTP 请求、根据结果决定下一步动作。一次任务执行下来可能经历十几个步骤中途还可能走错分支、触发重试、跑进死循环又绕出来。所有这些过程信息在最终答案里几乎完全不可见。我见过最典型的例子一个客服 Agent用户问我的订单为什么还没到。路径 A 只调用了一次get_order_status1.2 秒拿到结果干净利落。路径 B 先调用search_orders搜出一堆候选再调用get_customer_info确认身份又调用get_recent_orders缩小范围最后才调了get_order_status总共花了 8.7 秒Token 消耗是路径 A 的五倍。但两者最后给出的答复都是您的订单正在运输中预计明天到达按传统评估标准两个都是 PASS。问题就在这里测试报告根本看不出路径 A 和路径 B 的区别。如果你只看最终结果你甚至不知道 Agent 每次都在走哪条路也不知道它是不是每次都在走那条更差的路。1.2 路径质量才是 Agent 的真正分水岭同样是 PASS背后的路径差异意味着什么往小了说是延迟和成本的区别。路径 B 多调了三个工具多消耗了大量 Token如果这类请求量大成本可能直接翻倍。往大了说路径差异暴露的是 Agent 的策略问题。为什么路径 B 需要先搜索、再确认身份、再缩小范围很可能是 Agent 没有正确提取用户上下文里的 order_id导致第一轮搜索无法精确定位。这类问题用常规的最终答案评估完全检测不到但它恰恰是 Agent看起来聪明、用起来笨的根源。我后来把评估思路换成了路径感知每次任务不仅记录最终输出还完整记录执行轨迹形成标准化的 Trace。然后对 Trace 里每一个 Span 单独做质量评估。这样一来Agent 的每个决策都会被摊开来看——这个工具调用是否必要这个分支选择是否合理这次重试是否真的需要。路径好坏一目了然同一个 PASS 背后的差异也无处遁形。2. Jev 是什么可编程的 Agent 评估框架2.1 Jev 的工作机制Jev 是一个偏工程向的 LLM 评估框架核心特点是评估逻辑可以用代码精确控制。也就是说你不再依赖单一的让模型打个分这种模糊指令而是可以把指标拆成一个个可运行的断言、可量化的规则甚至把 LLM 判断和规则判断混合在一起用。在实际使用中Jev 的工作流大致是这样你先准备一组测试用例每条用例里包含输入、期望结果以及搭配的可选参考信息然后在配置里定义评估指标每个指标可以是一段脚本、一个提示词模板、或者两者组合最后运行评估Jev 会逐条执行用例、逐项计算指标最后输出结构化报告。逐 Span 评估 Agent Trace这件事之所以能用 Jev 落地是因为 Jev 的数据输入足够灵活。你可以把一条 Trace 当作一个测试用例的附带数据传进去然后在指标定义里按 Span 遍历、按 Span 打分。这比传统评估工具输入一段文本、输出一个分数的单向模式要强很多。2.2 为什么选择 Jev 而不是自己写脚本很多团队会选择自己写评估脚本理由是不就遍历一下调用记录吗。没错如果只是统计工具调用次数、计算平均延迟自己写确实够了。但一旦涉及语义判断事情就变复杂了。比如你想判断这次工具调用是否真的必要规则层面很难定义。什么是必要如果 Agent 在没有 order_id 的情况下先调用搜索接口这算必要还是算绕路这类判断需要结合上下文语义得靠 LLM 来给结论。而 Jev 的价值恰恰在于它把规则判断 语义判断揉在了一起先用代码做结构化处理再按需触发 LLM 评估两者可以自由组合。另外Jev 的评估结果天然是结构化的。每个指标、每个用例、每个 Span 的分数都会被记录下来方便后续聚合、对比、回归分析。这比自己在脚本里拼字符串、攒 JSON 要省事得多。我现在的做法是把 Jev 的评估输出接进 CI 流程每次 Agent 版本更新自动跑一遍路径质量分数一变就能立刻发现。3. Trace 与 Span读懂 Agent 的每一步3.1 从一次用户问题到一串 Span在聊逐 Span 评估之前必须先讲清楚 Trace 和 Span 到底是什么关系。用一句大白话说Trace 是整条调用链Span 是调用链上的每一个环节。一次完整任务是一个 Trace里面每次函数调用、每个工具请求、每次数据库查询都可以是一个 Span。举个例子Agent 处理帮我查一下北京的天气这个问题Trace 大致长这样最外层是一个根 Span代表整个任务下面挂着几个子 Span可能有调用天气查询工具解析返回结果生成最终回答。每一个 Span 都有自己的名称、开始时间、结束时间、状态码、属性标签还可以带上父 Span 的引用用来表达调用层级关系。这个模型最早是为分布式系统设计的但用在 Agent 上特别合适。因为 Agent 的执行过程本质上也是一个多方协作的过程模型要调工具工具要访问外部服务外部服务可能还要再调其他接口。整条链路拉出来哪里慢、哪里出错、哪里重复劳动都清清楚楚。3.2 一个好 Trace 应该包含什么我之前踩过一个坑Trace 倒是记录了但记录的内容太稀薄导致后续评估根本没法做。现在我会要求每个 Span 至少包含以下几类信息阶段标识这个 Span 属于哪个阶段比如planning、tool_call、tool_result、generation方便按阶段过滤统计。输入输出摘要工具调用的入参和返回结果的截断内容不一定完整保存但关键字段必须有否则没法判断这次调用是否合理。状态信息成功还是失败、错误码是什么、有没有走重试分支这些直接影响路径质量分。时间与开销耗时、Token 消耗、模型调用次数这些是效率评估的硬指标。父子关系通过parent_span_id关联上下文还原完整的调用树。有些团队会觉得记录这么多信息太啰嗦但我的实际感受是评估方案的设计往往受限于 Trace 数据能提供什么。你前期多记一个字段后面做路径分析时就多了一种可能性。如果数据本身是残缺的再好的评估框架也白搭。4. 实操用 Jev 逐 Span 评估 Agent Trace4.1 先跑通 Trace 导出在做逐 Span 评估之前得先解决数据来源问题。现在主流的 Agent 框架基本都内置了可观测性支持我自己的项目用的是 OpenTelemetry 规范那一套直接把 Agent 的每次工具调用映射成 Span最终导出成 JSON 格式的 Trace 文件。导出的数据大概长这样{ trace_id: a3f9c1e2, spans: [ { span_id: s1, parent_span_id: null, name: handle_query, stage: root, start_time: 2024-06-01T10:00:00.000Z, end_time: 2024-06-01T10:00:09.200Z, status: ok, attributes: { query: 为什么我的订单还没到 } }, { span_id: s2, parent_span_id: s1, name: search_orders, stage: tool_call, start_time: 2024-06-01T10:00:01.000Z, end_time: 2024-06-01T10:00:01.800Z, status: ok, attributes: { input: {customer_id: C10086}, output: {count: 3} } } ] }这是一个简化示例真实场景里 Span 数量会比这多得多。关键是把stage、status、input、output这几个字段留好它们是后续评估的主要原料。如果你用的 Agent 框架不支持自动导出 Trace也可以自己埋点。我试过最省事的办法是写一个装饰器包在工具函数外面每次调用自动记录入参、出参、耗时和状态再攒成统一的 Span 结构。前置工作一周左右就能搞定之后所有 Agent 任务都会自动留下足迹。4.2 设计 Span 级评估指标拿到 Trace 之后下一步是定义指标。这里的核心原则是不同 Span 应该用不同的评估标准来审视因为根 Span、工具调用 Span、生成 Span 承担的职责完全不一样。我目前常用的指标组合大概是这几类指标适用 Span判断方式说明tool_relevance工具调用类LLM 判断这个工具是否当前步骤真正需要step_efficiency全链路规则统计达到同样结果是否用了过多步骤loop_detection全链路规则匹配是否存在重复调用同一工具的死循环input_sufficiency工具调用类LLM 判断调用工具时是否带齐了必要参数error_recovery失败分支LLM 判断失败后是合理重试还是无脑重试cost_control全链路规则统计Token 消耗是否在合理区间这里需要特别说明的是tool_relevance和input_sufficiency的差别。前者问的是这个工具该不该调后者问的是既然要调参数带对了没有。路径 B 的问题主要出在后者它调用search_orders时没有传入 order_id导致无法精确检索。这类问题用传统评估方式几乎不可能发现但逐 Span 评估可以精准定位。4.3 编写 Jev 评估配置在 Jev 里做逐 Span 评估我习惯先把 Trace 数据加载为用例集然后写一段遍历 Span 的评估逻辑。配置文件的形态大致是这样eval: dataset: file: ./agent-traces.jsonl metrics: - name: step_efficiency type: rule threshold: 8 params: max_tool_spans: 6 - name: tool_relevance type: llm provider: model: gpt-4o-mini prompt: | 判断这次工具调用是否在当前上下文中是必要且合理的。 上下文 - 用户意图{{query}} - 当前步骤{{span.name}} - 输入参数{{span.attributes.input}} - 执行结果{{span.attributes.output}} 回答格式只输出 PASS 或 FAIL不要输出其他内容。这套配置跑起来后step_efficiency会先按规则过滤掉明显冗余的长路径tool_relevance再对每个工具调用 Span 做语义判断。两类指标一硬一软互相补充比单独使用任何一种都可靠。写 LLM 判断类指标时有个小技巧Prompt 一定要给足上下文。如果只给一个孤立的 Span模型根本不知道这个调用在全链路中处于什么位置判断自然不准。我会把当前 Span 的父 Span 摘要、前置步骤结果一并放进 Prompt让它带着上下文做裁决。4.4 同一个 PASS两条路径的分数差在哪用这套方案跑完之前说的订单查询案例后结果非常直观。路径 A 的 Trace 只有一个工具调用 Spanstep_efficiency和tool_relevance全部通过几乎满分。路径 B 的 Trace 里有五个工具调用 Span虽然在最终答复上也是 PASS但逐 Span 评估时问题全暴露了search_orders调用被标记为 FAIL因为当时上下文里已经有明确的 order_id没必要做无差别搜索。get_customer_info被判为可疑属于身份确认动作但在该场景下不是必要步骤。链路总步数超过阈值step_efficiency直接给了低分。Token 消耗统计显示路径 B 是路径 A 的 4.7 倍。更有意思的是我把几十条历史 Trace 全部用 Jev 跑了一遍后发现同一条业务线里长路径和短路径的比例大约是三七开。也就是说三成请求在走低效的绕路方案但最终评估全部是 PASS。这个数据放到评估体系里做回归基线之后后续每次 Agent 版本更新我都会盯着路径质量分的变化低于基线的改动直接打回。5. 常见问题与排查技巧实录5.1 数据层面的坑Trace 数据不完整是最大的坑。有段时间我这里的 Agent 会在部分场景下丢 Span排查后发现是异步工具调用的父子关系没传对导致很多 Span 变成了孤儿节点失掉了层级信息。没有父节点后面的归因分析就全部对不上LLM 评估也无法理解上下文。解决办法是在埋点代码里强制校验parent_span_id缺失的直接标记为异常数据宁可少一条也不往里混。输入输出摘要要设置上限。工具返回结果可能很大尤其是查数据库类的工具动不动就是几百行数据。全都存进 Trace存储开销和评估时长都受不了。我现在对摘要做了截断默认只保留前 500 个字符关键字段优先保留。如果某个 Span 因为截断导致评估依据不足我会在指标 Prompt 里注明部分信息已被截断请基于现有信息判断。这个提示很管用能明显降低模型的误判率。5.2 评估结果层面的坑LLM 判断类指标容易过宽。刚开始我用默认 Prompt 让模型判断工具调用是否必要发现它几乎给所有 Span 都放行了。原因很简单模型倾向于理解 Agent 的选择而不是质疑 Agent 的选择。后来我在 Prompt 里加了一句如果这个步骤可以通过已有上下文直接完成那么额外调用工具就应该判为 FAIL。加了这条约束之后评估尺度明显收紧和人工抽检结果的对齐率提升了很多。规则类指标别拍脑袋定值。比如step_efficiency的阈值一开始我设成 5结果一堆正常的多步任务被误伤。后面我统计了过往 200 条 Trace 的步骤数分布发现 90 分位线在 7 步左右于是把阈值定在了 8。更重要的是这个数值不是一成不变的随着 Agent 策略优化步骤数的分布会整体前移阈值要跟着迭代。建议把阈值定义放在配置文件里随代码一起走版本方便回溯调整。还有一个容易被忽略的成本问题逐 Span 评估如果用 LLM 判断Token 消耗不小。我现在做了分级策略先用规则指标过滤掉明显不合格或明显优质的 Trace只有灰色地带才花钱请 LLM 来判。这样评估成本可以压缩到现在成本的三分之一左右准确率并没有明显下降。我个人在实际操作中的体会是Agent 评估这件事最怕的就是用战术勤奋掩盖战略懒惰。你花了很多精力设计测试用例、跑回归、调参数结果评估维度永远只有最终答案对不对那很多深层质量问题永远都发现不了。把 Trace 引入评估体系之后再看 Agent视角会完全不一样——你不再关心它说了什么而是关心它做了什么、为什么这么做、有没有更聪明的做法。最后再分享一个小技巧如果你的 Agent 框架还没有埋点体系不要等以后再说现在就开始记录。哪怕只是简单地把每次工具调用的入参、出参、耗时打到一个本地日志文件里等你想做路径评估时这些日志就是最宝贵的原始资产。数据攒够了Jev 这种框架才能真正发挥威力。否则工具再好也只是巧妇难为无米之炊。