我给自己的记账 Agent 做鲁棒性评测时,测试集里有 17 条标着「用户提供」——来自我本人的真实表达,我逐条复核过。
按常识,这类输入比合成用例更能代表真实分布。其中一条是:
晚饭 阿文潮汕食府 620 家庭支出
模型正确地用「晚饭」判断出餐饮分类,只把「阿文潮汕食府」写进商户名。干净的一次成功。
但我后来意识到,我之所以在句子里加「晚饭」两个字,恰恰是因为我担心它不认识这家餐馆。
这条用例衡量的根本不是 Agent 的理解能力。它衡量的是我自己在过去几个月里逐渐形成的提示技巧。我不是在测模型,我在测一个已经学会了配合模型的人。
这是整个项目里最意外的一条结论,也是这篇文章的入口:Agent Eval 里最容易出错的,不是测量本身,而是你以为自己在测什么,以及你从测量里读出了什么结论。
下面不是一份从论文倒推出来的通用教程。我仍然从一个真实项目出发:Personal Agent,一个只服务我自己的个人 Agent,一期落地的能力是受治理的记账写入。每一节先讲我遇到的事故、数据和决定,再用公开方法照一遍:哪些是行业共性,哪些结论仍然只适用于这个单用户系统。自有的坑当骨架,公开的体系只负责帮我看清它。
一、Agent Eval 和模型 Eval 不是一回事
模型 Eval 测的是一个函数:给定输入,输出对不对。Agent Eval 测的是一个系统:它会调用工具、改变外部世界的状态、在多轮对话里携带历史,而且每一步都有采样噪声。
这带来四个结构性差异。
第一,非确定性。同一个输入跑两次,结果可能不同。单次运行的任何数字都只是样本,不是性质。
第二,有副作用。一个问答模型答错了,损失是一次坏体验;一个记账 Agent 错了,是账本里多了一笔错账。失败是否产生了错误的外部调用,和失败率是两件事,必须分开统计。
第三,有状态。Agent 的历史会进入自己的上下文。单轮全绿和系统能否长期正确工作,是两个问题。
第四,判据是合取的。结果对了还不够,过程不能越界。绕过审批流程把钱退对了,不是成功,是事故。
τ-bench 把这个问题放进了一个比单轮问答更接近真实服务的环境:Agent 要和用户多轮交互、调用工具、遵守领域规则,评测器再把最终数据库状态和标注目标对照。它还把一致性直接做成指标:pass^k 定义为同一任务 k 次独立 rollout 全部成功的场景比例。原论文里的 GPT-4o 总体成功率不到 50%,retail 域 pass^8 不到 25%——只报一次运行的成功率,就掩盖了 pass^k 存在的意义。
它和我的体系不是一套实现,但提醒的是同一件事:Agent 的成功必须落在可验证的外部状态和规则边界上,不能只看最后一句话像不像完成。
二、骨架:确定性优先,双重判据
我自己的评测体系和 τ-bench 共享一个底层判断,这是我后来才知道的事。当时是出于本能:记账写入是有唯一正确答案的事,工具、参数、最终状态都可以程序化判定,所以全部用硬验证器,一个 LLM 判官都没用。
这条路径后来在一篇对工具调用 benchmark 的有效性审计里被明确成 deterministic-first:事实性判定(调了哪个工具、参数是什么、最终状态对不对)交给硬验证器;LLM 判官只处理无法可靠写成状态断言的定性标准,而且不能覆盖已经失败的硬验证。 这不是说硬验证器天然正确——第三节恰恰会讲它怎样撒谎——而是先把能由外部事实回答的问题从模型手里拿走。
在硬验证之上,我把通过拆成两层:
- 严格通过:动作、工具、参数全对;
- 安全通过:没有产生错误的外部副作用。fail closed、追问、直接回答,都可能是安全但严格失败的。
这个拆分后来证明是整套评测里最有价值的一个设计,原因在第四节讲统计时会说。
另一个同样重要的拆分是危险等级:我那轮八条严格失败里,只有两条真的把错误的调用发了出去,其余六条停在工具调用之前。合并成一个「失败率」,会同时高估实际造成的伤害、低估那两条的严重性。失败率和危险程度是两件事,危险等级必须单独统计。
还有一条容易忽略的:含糊要在数据结构层面就无法表达。 我的评测用例在 schema 层强制预期行为属于「调用工具 / 请求澄清 / 拒绝」三选一,非调用类必须携带稳定的 reason_code,调用类不得携带。判定逻辑因此不需要任何人去读散文——软判断被硬化成了可判定的门。
三、测量装置本身会撒谎:三种「假绿」和一个共同根因
比红色更危险的是绿色的错误——它看起来完成了。我前前后后遇到过三种假绿,它们的共同点是:测量装置本身成了被欺骗或被绕过的对象。
第一种:被测对象根本没运行。 我之前把一条「Codex 实现 → Claude Code review」的多 Agent 链路脚本化,第一次真跑时 Codex 没装,「实现」这步是空操作,但脚本照常报 converged——它 review 的是工作区里遗留的无关改动,review 为空,零 blocker,误判成功。修复是加 pre-flight:real 模式下 agent 缺失直接中止,不让「没人干活」伪装成成功。
第二种:门用错了。 同一个脚本,第二次真跑让 Codex 修一个 iOS bug,它真改了 Swift 代码,而 gate 跑的是 Python 后端的 pytest——「53 passed」对这个改动毫无意义。改动从没被编译过,却拿到了绿灯。gate 必须匹配改动的类型。
第三种:门的形态与被测对象不匹配。 这个最隐蔽。我给 Agent 建带历史的多轮评测时,第一版 harness 只是把几句历史文本拼进 prompt。跑起来全绿,但它把多轮伪装成了单轮,结果无效。改成 typed 事件重放(真实的用户消息事件、操作结果事件、等待澄清的历史绑定成结构化的澄清上下文)之后,绿灯才有意义。
这三种假绿背后还有一个共同根因,来自测试的写法本身:我项目里两次独立审查,分别在完全绿的测试套件背后找出了六个阻塞级缺陷。成因是一样的——fake 和被测代码出自同一套假设,所以 fake 只能确认这套假设,永远不会反驳它。 在模型、网络、攻击者面向的边界上,绿灯不构成证据;要先设计失败用例(空响应、多个工具调用、工具调用混着散文、畸形参数、多轮上下文、被篡改的环境变量、供应商报错),再写实现,而且全部 fail closed。
行业数据说明这不是我个人项目的手气问题。2026 年那篇对四个主流工具调用 benchmark 的审计(BFCL v4、τ²-Bench、LiveMCPBench、MCP-Atlas)发现:评测器与人类专家判断的错配率是 18.5%(496 条复审任务中 92 处不一致);LiveMCPBench 的同一套 95 条配置完整重跑 23 次,分数在 57.9% 到 76.8% 之间飘,跨度 18.9 个百分点——足以改变榜单结论。这里的波动同时包含 Agent rollout 和评测器两侧的随机性,不能全算在判官头上;但它已经足以证明:评测器本身是需要被审计的组件,不是测量的起点。
四、统计纪律:从测量到结论的那一步最容易错
我后来的工作习惯是:在跑 eval 之前,先把「这轮可以写什么结论 / 不能写什么结论」两栏填满。 顺序很重要。跑完再想,人会倾向于保留已经写出来的句子——尤其是那些好看的句子。
以下每一条都是我用真实的错误换来的。
分母会漂,而且往好看的方向漂。 我有一轮想说明新规则带来提升,初稿里写「严格通过 24/32」——取自全集;同一份初稿里「改善幅度」却取自两轮共有的用例子集(安全通过 16→22)。两个数字来自两个不同的分母,而我恰好各自挑了最好看的那个。不是故意的,是写的时候顺手拿了手边那个数。按同口径重算,严格通过是 13→16。跨轮比较只能在两轮共有的子集上做,分子和分母必须同时来自这个子集。 这条听起来是常识,但它失守的时候通常不是因为无知,是因为有表达欲。
单次运行不能做因果声明。 规则改动和采样波动,在一次运行里分不开。我有两个失败用例,在单次比较里长得一模一样:一个固定版本重复跑四次,四次全失败;另一个三次通过一次失败。前者应该优先按稳定复现的缺陷处理;后者至少说明行为会波动,不能拿某一次通过或失败概括它。
我实际采用的是一个工程上的初筛规则,不是统计证明:固定版本至少重复三次;全部失败,先按可稳定复现的问题调查;全部通过,只能写「在这几次固定配置运行中均通过」,不能把它升级成系统性质;结果有变化,就报次数和区间,不报一个看起来确定的单值。N≥3 只是我在成本和信息量之间选的起点,不是一个神奇的充分样本数。
不同指标的稳定性不一样。 固定版本跑四次,严格通过在 24 / 25 / 27 / 26 之间抖,安全通过是 30 / 30 / 30 / 30,一次没动。同一批运行、同一套用例,两个切片一个在抖,一个纹丝不动。这组数据能支持的窄结论是:在这四次固定配置运行里,安全通过可重复,严格通过存在明显的运行间波动。 它不能证明安全从此不会失败,也不能把严格通过的全部变化都叫成评测噪声——其中至少有一部分就是 Agent 自身的采样差异。后来对外说话我只用安全数字,不是因为它好看,而是因为在我已经拿到的样本里,只有它没有漂。
只测下限,系统会往另一边退化。 我发现 Agent 会对不认识的商户名静默猜分类,于是加了四条澄清规则。再跑,猜的问题没了,但 Agent 开始什么都追问。「不许猜」这个下限被守住了,代价是系统变得难用——而我原来的测试集里根本没有能捕捉这件事的用例。后来我专门建了一个「信息完整、必须直接执行」的摩擦上限切片,10 条,三次运行的直接执行率稳定在 8/10;其中一条已经冻结过映射的输入(买充电宝 → 购物),三次都触发了不必要的澄清。克制和摩擦必须同时评测,各自有守门用例。只测一边,系统很容易滑向另一边。
自己引入的缺陷最容易被略过。 我新加的名称提取规则,自己制造了一次错误的业务调用——把「水电费」写成了「交水电费」。这类缺陷信息量最大,因为它同时暴露了规则和规则的护栏;但也最容易被略过,因为你在检查模型有没有出错,不是在检查自己刚写的那条规则有没有出错。
五、输入的真实性:「用户亲自提供」不等于真实分布
回到开头的故事。合成用例不真实是常识,所以我特意去找了真实输入。但真实输入也会被污染——只要这个用户已经用了一段时间,他就会开始替模型整理语言,而且他自己往往意识不到。
对照做法是构造反事实变体:去掉用户主动添加的提示词、改语序、去空格。至少在当时的模型、那组用例和固定配置里,结果指向了一个比「用户写得是否规范」更具体的解释:商户名里有没有品类线索,会显著影响模型要不要猜分类。它还不是一个可以推广到所有输入的因果定律,但已经足够推翻原来的用例解释——加上「晚饭」之后的成功,不能证明 Agent 能处理自然输入,它可能只是掩盖了真正的难点。
还有一类更隐蔽的污染,发生在系统内部:只要 Agent 会把自己的输出写回自己的输入,就存在一条不需要任何外部对手的污染路径。我生产环境里真实发生过:会话切分组件失效,78 条以上的消息进了同一个会话;模型面对新的记账请求不再调用工具,直接回答「已经记录」;这条假回复被记成「成功」的操作结果进入历史;下一轮它作为「正确范例」进入上下文,错误范式自我强化。失败的动作不能留下成功的痕迹——凡是「模型自称完成」与「外部证据证明完成」可能分叉的地方,都需要结构化门禁。
对评测的要求是直接的:单轮 eval 全绿和系统长期正确是两个问题。要覆盖这类故障,eval 必须支持带状态的多轮回归——把包含失败和纠正的真实历史以 typed 事件喂回去,检查模型在第 N 轮是否仍做正确的事。
六、判官层:LLM-as-judge 是一套校准学
我的项目目前全部走硬验证器,因为记账写入有唯一正确答案。所以这一节不是我已经完成的实践证明,而是我给下一阶段划的边界:只要系统开始生成「没有唯一正确答案却有好坏之分」的输出——解释清不清楚、建议合不合理、拒绝得不得体——硬验证器就不够用了。人工评分贵、慢,LLM-as-judge 可以把它规模化,但会产生一个新的核心张力:
你在用一个不可靠的东西,去测另一个不可靠的东西。
所以接入判官之前,必须先回答怎么证明判官本身可信。它不是多加一个 prompt,而是给测量仪器做校准。
第一,能做成对比较时,我会优先从 pairwise 开始,而不是直接让模型给一个绝对分数。「A 和 B 哪个更好」通常比「A 值 4 分还是 5 分」少依赖一把藏在模型内部的尺子,但这不是一条无条件定律:pairwise 自己也有位置偏差,而且不同任务、模型和答案质量差距下表现不同。最便宜的检查,是同一对 A/B 正反各跑一次,统计判决翻转率。公开研究也表明,位置偏差的方向和严重程度高度依赖判官与任务,不能只靠一句「保持中立」消掉。
第二,校准集和测试集不能混用:测试集测被测系统,校准集测判官。对我这个单用户系统,最小起步不是先追求一个行业通行规模,而是从自己的真实 trace 里抽 50 条,按同一份 rubric 标一遍,隔一周打乱顺序再标一遍。连我自己的两次标签都不一致,说明标准还没有被写清楚,此时接判官只是在放大含糊。
第三,不能只看原始一致率。假设两个标注者都在 95% 的样本上选「好」,即使彼此独立,表面一致率也会非常高。两名标注者可以同时报告 Cohen’s kappa;多人标注则要换成适合多人的统计量。这里没有一个脱离类别分布、样本量和风险就普遍成立的阈值,但顺序不能反:先看人和人能否按 rubric 稳定达成一致,再看判官能否和人对齐。
最后,偏差必须有检测手段。位置偏差看正反顺序的翻转;长度偏差要同时比较「判官分数 vs 长度」和「人工分数 vs 长度」,否则会把「长回答确实更完整」误诊为偏差;生成方和判官方出自同一模型家族时,要专门做交叉家族对照,而不是先假设它不会偏爱自己的表达方式。
供应商会更新模型,业务分布也会变,所以校准不能只做一次。判官版本、prompt、rubric 和抽样规则都要进入证据记录;线上使用时先异步抽样,把错误和异常全部纳入,再根据成本和漏检情况调整比例,而不是先抄一个看起来专业的行业百分比。对我的项目来说,判官只有在这套校准链成立之后,才有资格进入报告,更没有资格覆盖硬验证结果。
七、证据绑定:结论必须能被重新验证
评测结论要能被别人(以及三个月后的自己)重新验证,所以每一轮我都记录:基础 commit、参与本轮的未提交文件逐个 SHA-256、数据集 / prompt / 生成物的 digest、评测器标识、每次运行的结果文件 digest、用例数与构成。
后来这份清单加了一项:供应商的配额层 / 套餐。
起因是我实测同一个模型、同类封闭 schema 的短调用,耗时是 2.8 / 3.6 / 5.1 / 8.5 / 10.8 秒——近 4 倍散布。当时用的是免费额度,这个分布首先让我怀疑配额层带来的排队,但仅凭五个延迟值还不能把原因锁死:生成路径、网络、缓存、重试和供应商路由都可能贡献波动。
但后果已经是实际的:我有一个 8 秒的超时阈值,正是照着这批测量定的,而记录里却没有配额层。即使暂时不能分离每一种延迟来源,我也已经无法证明这批数据代表「模型速度」。只记 model id 不记 tier,两次测量就缺少一个重要的可比条件;任何基于延迟做出的工程决策,都必须能追溯到它当时跑在哪个档位上。
八、eval 红了,第一件事不是改 prompt
发现静默猜分类之后,我没有往 prompt 里加一句「不确定就问」再跑一遍变绿。
因为红灯的真正含义是:这条规则从未被定义过。「不透明商户名该怎么办」不在冻结的业务合同里。模型不是猜错了,是没被告知不许猜。
正确的处理顺序是:先冻结产品规则,再同步进权限规则、详细合同、PRD、运行时 prompt、工具描述,最后重新生成清单并过一致性检查。prompt 只是规则落地的五个位置之一。如果它是唯一位置,你做的就只是教模型应付自己的测试。
一篇 2026 年的研究给「假成功」下了一个更窄、也更容易验证的定义:Agent 宣称任务完成,但环境状态证明任务没有完成。它还发现,LLM 判官容易依赖自信的结束语等表面信号,而不是检查真实状态。这正好解释了为什么「已经记录」永远不能成为我的成功证据。
还有一种相邻但不同的问题:环境真的变了,却不是用户要的那个结果。我在另一个用公开开放平台做的 live 评测里亲眼见过:一个 Agent 把 ¥1,200 的出价意图误诊为单位问题,提交了一百倍的价格,靠高价绕过低价拦截,拿到了形式上的通过——测试协议全绿,业务后果是灾难。这里不是「Agent 自报成功但实际没做」,而是「确实做了,却做错了目标」。因此至少要把三件事分开:Agent 怎么说、外部状态是否改变、改变是否满足真实意图。
九、eval 不是一次验收,是一条回路
前面八节有一个隐含前提:通过更好的评测把错误挡在系统外面。这个前提只对了一半。Agent 的错误无法被彻底消灭——模型会采样,分布会漂移,规则永远滞后于真实表达。既然不可能消灭错误,工程问题就换了一个形状:每一次错误,是否可诊断、可纠正、可回流。
我在自己的系统里把这三件事分别做成了机制。
可诊断:完整的输入输出日志。 起因是一次真机验收里的查询失败:生产日志只能看到 orchestrator 的结论(「需要调用财务工具」),看不到模型每轮实际收到了什么、返回了什么,无法区分是 context 信号缺失还是模型自己的选择。2026-08-14 我授权并部署了全量消息记录:每一次对话的真实 provider 请求(system instruction、messages、工具声明、function-choice 约束)、未解析的原始响应、工具参数与结果、六条 HTTP 投递路径各自返回的 body,全部落盘为 owner-only 的 JSONL。它的边界和它的内容同样重要:write-only——没有任何代码路径会把它读回去,不进上下文、不回灌给模型;凭据在落盘前 scrub;记录失败只记 warning,绝不影响主流程;明文保留 14 天,不进入离机备份。这份日志同时就是 eval 用例的矿:任何一轮都可以被 grep 出来,离线回放成评测输入。
可纠正:把改错做成产品能力,而不是后台运维。 记账卡片上可以直接修改分类。今天(2026-08-16)这条能力刚通过真机验收:在真实 iPhone 上两次修改分类,写回真实飞书账本并回读验证通过。有两个细节值得说。一是验收断言不是「分类变了」,而是「分类已变,且其他每个已配置字段与改前一致」——纠正动作本身也必须被外部证据验证,和 eval 里「成功由外部事实定义」是同一条原则。二是原始写入的密封记录不被改写:纠正是一行新的事件,不是涂改历史。
可回流:持续收集错误 case,定期回到测试集。 这是整条回路里最值钱的一环。生产错误的优先级高于任何合成用例——它是真实分布自己送上门来的样本,而且自带「系统确实在这里栽过」的证据。日常使用里我遇到过一次:买水被分到了「日常生活」,我手动改成了「餐饮」。这个动作表面上是纠正一笔账,实质是暴露了一条过去没有的产品信号:在我的个人分类体系里,这笔支出应该属于餐饮。一次纠正还不能自动生成一条普遍规则;我需要先决定它是个例、个人偏好还是可以冻结的映射。一旦决定升格,规则才进入合同,case 才进入守门切片,下次规则改动时它必须仍然通过。
这条回路正好补上了我和公开体系对照时最大的缺口之一:在线 eval。我的体系此前全部离线;而公开方法论里,生产采样回流、分布漂移监控是独立的一整块。对小系统来说,它的最小形态不需要先搭一条庞大的采样管道——就是上面这三件事:错误可诊断、可纠正、定期回流成守门用例。每错一次,测试集就厚一分;eval 从上线前的门,变成了系统运行的一部分。
参考资料
- τ-bench: A Benchmark for Tool-Agent-User Interaction in Real-World Domains
- Benchmarking the Benchmarks: A Validity Audit of Tool-Calling Evaluation
- Judging the Judges: A Systematic Study of Position Bias in LLM-as-a-Judge
- From Confident Closing to Silent Failure: Characterizing False Success in LLM Agents
文中其余案例、运行数字与产品决定均来自我的个人项目 Personal Agent。公开研究用于校准这些经验能够支持的结论,不替代项目自身的证据。