系列导航:上一篇《你的 Agent "行不行",到底在问什么》|下一篇《读懂别人的 Benchmark:GAIA、AgentBench、WebArena、τ-bench、SWE-bench 各自在测什么》|合集目录见 T0
LLM 评测 ≠ Agent 评测:多出来的四样东西

周报助手把上周的汇总邮件发给了全公司,抄送栏里还挂着一个外部合作方的邮箱。
它写的正文,一个字都没错。数据是从三个内部文档里交叉核对过的,结论写得比我来写还像样。
按 LLM 评测(LLM Evaluation,详见 TA 篇)的规矩,这道题该判满分:输入一个需求,输出一段内容,内容准确、格式合格、引用齐全。可这道题实际上是我职业生涯里最想钻进地缝的一次------那封邮件撤不回来。
这就是两件事的分界线。评 LLM 的那套方法搬到 Agent 身上会漏掉四样东西,每一样漏掉都会让你得出一个"看起来挺好"的结论。
一、多步骤:中间错一步,最后怎么都是错
LLM 评测的基本单元是一来一回:一条 prompt 进去,一段文本出来。
Agent 不是。周报助手收到一句"帮我写周报并发给张三",它内部大概是这么一段路:先判断要不要搜、搜什么词、点开哪三个链接、从每篇里抽哪几段、发现其中两个来源数据打架时怎么处理、按什么结构写、发给谁、要不要抄送。
每一步都可能单独出错。
而这四步如果是串行的,单步 95% 的成功率叠十步就只剩 60%(0.95 的十次方约等于 0.599)。单步 90% 叠十步,35%。这就是为什么你手上明明是个"挺强的模型",装进 Agent 里却经常干不成事------不是模型变笨了,是它现在要连着对十次。
更要命的是只看最后一次输出会漏掉大部分失败。举两个真实的漏法:
- 蒙对的:要求查三个来源,它只查了一个就开写。偏偏那一个里数据齐全,最终答案全对。你按终态判,满分。可下次那个来源没更新,它就瞎了。
- 过程对、收尾崩的:三个来源都查了,中间某一步的 JSON 解析失败,它拿半个结果凑出了结论,正文里少了一个关键数字。你按文本判,扣分。可问题根本不在收尾,在解析那一步。
这两种失败,只看最终文本一个都抓不到。
二、工具副作用:错了撤不回来
LLM 的输出是一段文本,你不满意就扔掉,代价为零。
Agent 会真的动手。发邮件、下订单、改数据库、删文件、提交代码。这些动作改变的是外部世界,而且相当一部分是不可逆的(Irreversible Side Effect,详见 TA 篇)。
我最早栽的那次就是这样。答案得分是满分,副作用那一栏是负分,而且那一栏没法撤销。
所以 Agent 评测有一条硬规矩:不可逆操作必须在受控环境里跑。邮件用 mock 的假邮箱、订单用沙箱、数据库用评测专用的副本。你不这么做,评测本身就会变成生产事故。
顺带说一个反直觉的:mock 掉以后,你的分数会变好看,但那是好事------因为你终于能把"它做对了没有"和"它真的做了没有"分开看。
三、环境不确定:同一个输入,两次两个结果
这是最阴的一关。
同一个任务,早上跑成了,下午跑失败,代码一行没改。原因可能是:它要抓的那个网页中午改版了、搜索接口 503 了、接口还在但返回的 JSON 少了个字段、模型服务端那天更新了一版。
LLM 评测里,这类问题也存在(温度不是 0 的时候输出会变),但影响有限,因为输入是自足的,模型闭着眼睛就能答。Agent 的外部世界不受你控制,它每跑一步都在跟一个会变的外部环境打交道。
我周三那次失败,第一反应是"模型今天变笨了"。翻执行轨迹(Trace,详见 TA 篇)才发现,是第二步调用的搜索接口 503 了,模型拿到空结果,自己做主编了两段看起来挺像的上周数据填了进去。
先错后对,修正一下:问题不在模型,在它没有能力区分"查到了空"和"查不到"。
这条直接决定了后面两篇为什么存在------为什么两次跑出来不一样(T9),以及为什么跑一次不算数(T10)。
四、评价对象变了:从一段文本,变成过程 + 终态 + 副作用
前三条汇总起来,是第四条。
评 LLM 时,你手上能被验证的东西只有一段文本。拿它跟参考答案比对(Exact Match / 语义相似),或者交给裁判打分,完事。
评 Agent 时,你手上能被验证的是三样:
- 一段过程:它到底走了哪几步,在哪一步掉的链子
- 一个终态(End State,详见 TA 篇):事情办完以后,世界变成了什么样
- 一堆副作用:除了目标动作,它还顺手做了什么
三样得一起看。只看终态,你会漏掉蒙对的;只看过程,你会奖励那种"步骤规范但没办成事"的;不看副作用,你会给那封发给全公司的邮件打满分。
下面这段代码是"判定落在终态上"的最小示范,也是我后来给周报助手做的第一版判定:
python
# 判定要看终态,不是看它嘴上说做了没有
import json, time
def llm_client(messages, **kw):
"""可替换的模型入口,别绑死某家 SDK"""
raise NotImplementedError("接上你自己的模型:YOUR_LLM_ENDPOINT")
class MockMailbox:
"""邮件一律走假邮箱,绝不真发"""
def __init__(self):
self.sent = []
def send(self, to, subject, body):
self.sent.append({"to": to, "subject": subject, "body": body,
"at": time.time()})
return {"ok": True, "mode": "mock"}
def run_agent(task, mailbox):
trace = {"task_id": task["id"], "steps": [], "t0": time.time()}
messages = [{"role": "user", "content": task["input"]}]
out = llm_client(messages) # → YOUR_LLM_ENDPOINT
trace["steps"].append({"type": "llm", "out": out})
for to in task.get("recipients", []):
mailbox.send(to, task["subject"], out) # mock,不真发
trace["steps"].append({"type": "tool", "name": "send_email",
"args": {"to": to}})
trace["elapsed"] = time.time() - trace["t0"]
return trace
def judge(task, trace, mailbox):
# 终态:假邮箱里现在躺着几封信
want = set(task["expected"]["recipients"])
got = {m["to"] for m in mailbox.sent}
# 过程:有没有先查过资料
searched = any(s["type"] == "tool" and s.get("name") == "search"
for s in trace["steps"])
# 副作用:多发了没有
extra = got - want
return {
"end_state_ok": want == got, # 该发的都发了
"process_ok": searched, # 该查的查了没有
"side_effect_clean": not extra, # 没多发人
}
三个字段分开返回,别合成一个总分。合成一个总分,你就又回到"看着还行"了。
一张表看清楚
| 评 LLM | 评 Agent | |
|---|---|---|
| 输入 | 一条 prompt | 一个任务 + 一套工具 + 一个环境 |
| 输出 | 一段文本 | 一段过程 + 一个终态 + 一堆副作用 |
| 能验证的东西 | 文本本身(比对参考答案 / 裁判打分) | 终态(查状态)、过程(看 Trace)、副作用(看工具调用记录) |
| 失败长什么样 | 答错、答偏、废话多 | 中间某步错、做了不该做的、环境一变就崩 |
| 重跑一次 | 大致一样 | 可能完全不一样 |
| 搞砸的代价 | 文本扔掉即可 | 可能撤不回来 |
| 判定难点 | 开放式输出不好判 | 终态也不好判,而且还有三层要分开判 |
顺带算一笔账
这四样不只是让评测变复杂,它们还直接把评测的价格抬了上去。
- 多步骤 → 光留最终输出不够,每一步都得存下来,Trace 的存储、检索、可视化全是要花钱的活
- 工具副作用 → 你没法在真实环境里评,得先搭一套 mock 或沙箱。这是一次性投入,但通常是整个评测里最贵的一块,而且是纯粹的新增成本,评 LLM 时根本不用付
- 环境不确定 → 单次结果不可信,同一道题得跑 k 次才有底气,成本直接乘 k
- 评价对象三层 → 判定要写三份,不是一份;三份还得分别能解释,不然汇报时讲不清
合起来,一次评测的开销大概是:单次运行成本 × 重复次数,再加一次性环境建设。
这也就是 T1 里那句"精度是按场景买的"的真正意思------精度不是免费的,你要的每一个百分点背后都是重复次数和人工。
这四样,就是后面二十多篇存在的理由
说实话,如果 Agent 跟 LLM 一样只吐文本,这个系列写到 T2 就可以收工了。后面所有篇幅都是被这四样逼出来的:
- 多步骤 → 得能回看它走过的路,所以有 T19(看 Trace 破案);得单独看它工具调得对不对,所以有 T21(工具调用评测)
- 工具副作用 → 得划清哪些事不许它自己做,所以有 T24(安全边界);得让工具能被假装弄坏,所以有 T22(故障注入)
- 环境不确定 → 得控制变量、得重跑、得算方差,所以有 T9 和 T10
- 评价对象变成三层 → 成功的定义得写成"结果对、过程对、没出事",所以有 T4(《答案对了,不等于事办成了》)
这张表还有个用法:拿它去审别人的评测结论。下次有人跟你说"我们 Agent 的准确率 90%",先问一句这 90% 是按左边那列算的,还是按右边那列算的。按左边算出来的 90%,多半只覆盖了"文本写得对不对",剩下三样它压根没测------那不是 90 分的 Agent,那是 90 分的作文。
别急,这些不用现在全懂。你只要先接受一件事:评 Agent 的麻烦大多不在"打分",在于你根本没留下可供打分的东西。
一句话结论:LLM 评测评的是一段文本,Agent 评测评的是"它做了什么、做成了没有、还顺手干了什么"------这四样东西叠在一起,才让评测从"打个分"变成一整套工程。
下一篇要解决的那个麻烦:道理讲完了,可具体怎么出题、怎么判定,我心里还是没谱。这时候最划算的一步是先去看别人花了几个月做出来的题长什么样------下一篇把 GAIA、AgentBench、WebArena、τ-bench、SWE-bench 五个 benchmark 逐个拆开,看它们各自在测什么。
踩坑征集:你的 Agent 干过最离谱的"不可逆操作"是什么?真发邮件、真删表、真下单都算。报过来,最惨的那几个我放进 T24 讲安全边界那篇当反面素材。