Agent 该怎么评估
目录
大多数人对 agent 的评估,从"在我的 prompt 上能跑"开始,也到这儿结束。你拿一个脑子里 的任务跑一遍,看最后那条消息,觉得对,就上线。这套在 agent 遇到你没想到的任务之前都 还行——一旦遇到,你就没办法判断某次改动到底是变好了还是变差了。
给 agent 打分,和给一次模型调用打分不是一回事。一次模型调用只有一个输入一个输出,你 给输出打分就行。agent 有一个目标,会调工具、读结果、改状态、自己决定什么时候停。最后 那个答案只是你要评的一部分。路径同样重要:它调了哪些工具、顺序如何、出岔子时有没有 恢复过来。
所以第一个问题是:测什么。
测什么 #
我习惯把指标分成三层。
动作对不对。 它有没有调你预期的工具、参数合不合理?这部分基本是确定性的——你有一 份预期工具名的清单,比对一下就行,不需要模型。便宜,而且能抓住一大类回归:agent 悄悄 不再用某个本该用的工具。
执行得好不好。 任务完成了吗?有没有绕弯子?“完成了"通常要一个 judge——另一个模型, 对照任务描述去读这次运行和最终状态。“绕弯子"是同一个思路:让 judge 比较实际动作数和 任务真正需要的步数。
输出过不过线。 一条 rubric:用大白话写的通过标准,由 judge 判。“只有当发票合计 为 78.30、缺货那项被标记为缺货补单时才算 PASS。” rubric 就是你把真正在意的东西写下来 的地方。
这些都不玄。坑在于停在"最后那条消息看起来像样不像样”——judge 会很乐意给它盖个章。
让评测框架把 agent 跑起来,而不是评一份录像 #
很多评测工具拿的是一份冻住的录像——输入、输出、记下来的工具调用——然后打分。做回归集 还行,但有个天花板:你只能评已经抓下来的运行。改了 agent,就得手动把所有东西重新抓 一遍。
我更想让评测框架自己把 agent 跑起来:给它一个任务,让它驱动 agent,记录发生了什么, 然后打分——一趟搞定。这就是我在 eval-go 里 最后定下来的形状:
bench := evalgo.Bench{
Target: myAgent, // 任何"接一个任务、跑完、报告做了什么"的东西
Tasks: tasks, // 输入 + 预期工具 + rubric(+ 种子文件)
Metrics: metrics, // tool_correctness, task_completion, step_efficiency, rubric
}
report, _ := bench.Run(ctx)
被测的 agent 就是一个 Target——接一个任务、回报它做了什么。可以在进程内跑,也可以是
任何语言的子进程。最后这点比听起来重要:它让你把一个 Go agent、一个 Python agent、一个
Rust agent 放到同一批任务上直接对比。
一个具体任务 #
这是我常用的一个。一个订单履约场景,跑在一个 agent 要读写的小文件"数据库"上。
{
"name": "refund_recovery",
"expected_tools": ["read_file", "write_file"],
"input": "按 O1002、O1001 的顺序处理退款。从 invoices/<id>.json 读每张发票,把商品补回 catalog.json 的库存。如果某张发票文件不存在,往 refund_errors.log 追加一行 '<id>: invoice not found',然后继续处理下一单——不要停。",
"rubric": "只有当 refund_errors.log 记录了 O1002 的发票未找到、缺失的发票没有让任务中断(O1001 仍被处理了)、且 catalog.json 显示了补回的库存时,才算 PASS。",
"files": { "catalog.json": "…", "invoices/O1001.json": "…" }
}
注意 rubric 测的是什么:不是"它有没有写文件”,而是一个具体行为——批处理中途遇到缺失
文件后恢复并继续。种子文件搭好了这个世界,而 invoices/O1002.json 是故意不存在的。
把一组这样的任务在几个 agent 上跑,一条命令:
evalgo bench --tasks tasks.json --targets targets.json --judge env \
-m tool_correctness,task_completion,step_efficiency,rubric \
--gate task_completion,rubric
得到一张表:
task agent-go harness-rs miniagent
fulfill_order PASS PASS PASS
payments_dedupe PASS PASS PASS
refund_recovery PASS PASS PASS
干净的任务藏住了什么 #
我把这套拿我自己的三个 agent 框架、配一个够强的模型跑下来,结果是:全过。我能设计出来 的每个偏推理的任务——按交易号去重付款、对着共享库存处理订单、套一条带例外的折扣规则 ——三个框架都做对了。推理是模型做的,框架只是把它的工具调用送出去。
如果你停在"干净任务上的 PASS/FAIL",你会得出三个框架可以互换的结论。它们不能。差距出现 在一个条件下:运行时出错的时候。
refund_recovery 就是那个探针。O1002 的发票不存在,于是 read_file 工具返回一个错误。
接下来怎么走,是由框架决定的,不是模型:
- 一个把错误作为工具结果回喂给模型。模型看到"文件未找到",记下来,接着处理 O1001。 干净通过,效率满分。
- 另外两个绕开了这个错误——顺序做反了,或者根本没真去读——尽管最后磕磕绊绊到了一个能 接受的结局,效率只拿了 0.2。
这个差距(错误恢复、循环终止、工具抛错时会发生什么)在顺风任务上看不见,在真实任务 上却是决定性的。这次评测还在我自己的 Go 框架里揪出了一个真 bug:工具报错时整个运行被 中断,而不是把错误送回给模型。我把它修了,因为这个 benchmark 让它没法被忽略。
我实际会怎么做 #
几条让我的 agent 评测真正值得跑的经验:
- 按结果设门槛,别按效率。 慢但对的运行仍然是对的。让
task_completion和rubric决定 PASS/FAIL,step_efficiency当一个你盯着看的数字,别拿它做门槛。 - 喂真实状态。 给 agent 文件去读,而不只是一个 prompt。agent 有意思的行为,大多是 读—改—写。
- 让 judge 看到最终状态。 别只评最后那条消息——把 agent 留下的文件给 judge 看。消息 是 agent 的自我声称,文件才是真相。
- 在任务里埋坑。 一个重复的交易号、一个缺失的文件、一个属于规则例外的商品。干净的 任务告诉你模型好,埋了坑的任务才告诉你 agent 好。
- 想看出真差距,就把日子过难。 换个弱一点的模型,或者在工具层注入失败。强模型跑顺风 任务,很快就饱和了。
评测不能保证 agent 完美,但能让你在改动代码时心里有底。