代理评测应补上四项过程指标
文章建议从运行轨迹分别检查约束满足、工具调用正确性、循环终止和人工接管情况。先声明约束再逐项核验,避免最终答案分数掩盖预算超限、参数错误等过程问题。
AI 深度解读
Agent 最后答对了,执行过程就可靠吗?原文的结论是:最终准确率会遮住四类问题,分别是违反约束、工具调用出错、循环不停止,以及需要人工接管。评估时,要从执行轨迹里分别计算它们。
这套做法对 AI 编程也有用:一次任务即使交出了可运行的代码,中间也可能调用了不存在的工具、反复试错,或者最终靠人收拾残局。把这些过程拆开记录,才能看清一次模型或提示词更新究竟改善了什么。
答案看着不错,为什么仍然算失败
原文引用了两组讨论:Chip Huyen 和 NVIDIA 的 Vivienne Zhang 在 2024 年 12 月的 Chain of Thought 节目中谈到 Agent 的失败点;Snorkel AI 联合创始人兼 CEO Alex Ratner 在 2026 年 4 月补充了评估复杂任务的困难。
第一项检查是约束满足情况。做法很直接:运行前列出要求,运行后逐条判定是否通过。
Chip Huyen 举的是旅行规划:行程必须覆盖两周,预算不能超过 5,000。安排写得再流畅,只要超预算,就失败。这里不能让表达质量冲淡硬性条件,也不能等结果出来后再决定哪些要求重要。
复杂输出更需要这样的评分表。Alex Ratner 提到,一些法律深度研究任务可能需要人工花五、10 或 15 小时,涉及多个工具和多处引用。数学题可以拿一个数字与标准答案比较,但一份带有 50 条引用的 30 页 PDF,无法靠一次相等判断完成评估。
原文建议把每个评分项存成布尔值,再汇总成 constraints_met。只有事先声明的全部条件都通过,这次运行才算满足约束。
约束检查的价值,是把「感觉完成得不错」变成可追溯的逐项判断。
不过,布尔值只是记录方式,不会自动解决评分标准的质量问题。原文没有详细说明复杂文档中的每项要求由谁判、怎样处理模糊情况,这部分仍需团队自行设计。
工具名正确,只过了半关
第二项是工具调用正确性。每次调用都要检查两件事:工具是否有效,参数是否有效。
模型可能调用一个根本没有提供的工具,也可能选对工具,却传入错误参数。原文用天气查询举例:模型需要提取有效的地点和时间。工具名对了,地点不合法,这次调用照样失败。
因此,每次调用都记录 valid_tool 和 valid_params。一次运行中的所有调用都通过这两项检查,才算通过工具调用检查。
这里有个容易漏掉的边界:没有调用任何工具,也可能得到通过结果。因为代码使用 all(),空列表会返回 True。如果任务明确要求使用某个工具,就必须把「调用该工具」写进约束清单,否则 Agent 可以绕开这项要求。
对 Node 工具封装或 AI 编程流程来说,这个区分很实用。工具是否存在、参数能否通过校验,与任务是否完成,是不同层次的问题。原文这项指标衡量的是调用有效性,并没有证明「有效参数一定能获得正确结果」,也没有覆盖工具自身返回错误内容的情况。
能结束、何时交给人,也要分别记录
第三项检查循环是否终止。只有在预先设定的步数上限内到达停止状态,terminated 才为真;耗尽上限却没有停止状态,应判为失败。
同时保存 steps,把终止率和步数放在一起看。否则,模型更新后虽然仍能完成任务,但执行路径明显变长,单看成功率就察觉不到。
Alex Ratner 用自主执行跨度(autonomy horizon)讨论任务需要的步骤数量,其中也包括执行途中目标发生变化的情况。一道以单元测试判定结果的编程题处在较短的一端,多轮交互、规格不断变化的任务则更长。原文认为,能覆盖真实任务长度的评估仍处在前沿,公开基准很少,多数仍有较大提升空间;但文中没有给出具体基准名单或对照数据。
第四项是人工接管。Vivienne Zhang 的例子是客服机器人:持续处理问题,直到复杂度超出能力,再把任务交给人。记录 escalated,才能看出一次提示词修改是否让人工接管突然增多。
下面这段代码解决的是把执行日志汇总成指标的问题,原文明确说它只是需要自行适配的最小示意:
def score_agent_runs(runs):
if not runs:
return {}
n = len(runs)
def tool_calls_ok(run):
return all(
c["valid_tool"] and c["valid_params"]
for c in run["tool_calls"]
)
return {
"constraint_satisfaction": sum(r["constraints_met"] for r in runs) / n,
"tool_call_correctness": sum(tool_calls_ok(r) for r in runs) / n,
"loop_termination": sum(r["terminated"] for r in runs) / n,
"mean_steps": sum(r["steps"] for r in runs) / n,
"human_escalation": sum(r["escalated"] for r in runs) / n,
}
要重点看 for c in run["tool_calls"] 这一行所在的检查:它要求一次运行里的调用全部正确,最后得到的是「全部调用都合格的运行占比」,不是逐次调用的正确率。长任务调用次数更多,解读这个比例时也要结合任务长度。
人工接管率则没有统一的理想值。该交给人时及时交接,可能是产品希望得到的行为。原文明确把目标值留给具体任务决定。
我的判断
我认同这套拆法,因为它把最终结果背后的不同失败原因分开了。相比只看测试通过率或一个综合分数,它更容易帮助团队定位问题:到底是漏了要求、参数生成有误,还是执行过程失控。
但它提供的是评估框架,文中没有团队实验、样本规模,也没有「采用后提升多少」的对照数据。相关建议应视为实践经验,不能当成已经验证过收益的方案。
另外,全部通过才算成功的规则适合硬约束,却会损失一些诊断信息。同样失败,一次可能只漏掉一个条件,另一次可能几乎全部不合格。因此,既然原文已经建议保留逐项布尔值,就应该在汇总比例之外继续查看这些明细。
平均步数也有局限。它能提示执行路径变长,却无法单独解释增加的步骤是在有效检查,还是无意义重试。人工接管同样需要回看轨迹,不能直接认定越低越好。
这四项适合作为最终结果评分的补充,帮助解释成功与失败是怎样发生的。
能拿来做什么
- 给 AI 编程任务先写一份可检查的约束清单,例如修改范围、必须通过的检查,以及必须调用的工具,并在运行后逐项保存结果。
- 在 Node 的工具调用入口记录工具标识和参数校验结果,分别生成
valid_tool、valid_params,避免把参数失败混进笼统的任务失败里。 - 为同一批评估任务预先固定步数上限,同时记录停止状态与实际步数,比较提示词或模型更新前后的终止率和平均步数。
- 把人工接管单独记下来;当接管率明显变化时,回看对应轨迹,判断是能力退化、任务更复杂,还是交接策略发生了变化。