Agent 评测闭环:决定进化是否真的有效
从任务结果、执行轨迹、确定性检查、LLM Judge、冻结留出集到灰度与回滚,搭建能支撑 Agent 自进化的评测闭环。
Agent 自进化最后会落到一个很具体的操作:候选版本 B 跑完一批任务后,系统要不要用它替换版本 A。
如果答案只来自一句“LLM Judge 觉得 B 更好”,这个闭环并不可靠。Judge 可能偏爱更长的解释,测试可能泄漏,任务环境可能被候选版本修改,基准平均分还可能掩盖某类关键任务的退化。
自进化系统首先是一套发布系统。它需要版本、隔离、证据、准入、灰度和回滚。模型可以提出改动,也可以辅助分析失败,但不能同时拥有修改系统、修改考题和宣布自己通过的权限。
一、评测对象不是一段回答
普通聊天模型评测经常只看最终文本。Agent 在环境中行动,最终回答只是轨迹的最后一项。
一次完整运行至少包含:
任务输入
+ 初始环境
+ 模型和 Harness 版本
+ 每轮上下文
+ 工具调用与结果
+ 文件或数据库状态变化
+ 最终输出
+ 成本、延迟和中断记录
只看最终答案会漏掉很多问题。Agent 可能碰巧给出正确数字,却查询了错误日期;可能测试通过,却顺手删除了无关文件;也可能完成退款,同时违反“高额退款需人工确认”的业务策略。
因此评测要覆盖三个层次:
| 层次 | 回答的问题 |
|---|---|
| Outcome | 最终世界状态和用户目标是否达成 |
| Trajectory | Agent 是否通过允许、合理的路径完成 |
| Operation | 成本、延迟、稳定性和人工介入是否可接受 |
三个层次不能互相替代。轨迹看起来漂亮但任务失败,没有用;最终状态正确但越权,也不能发布。
二、先定义任务契约
一条评测样本不应只有 Prompt 和参考答案。更完整的任务契约包含:
task_id: refund-delivered-order
initial_state: fixtures/order_1024.json
user_goal: 退回已送达但破损的商品
allowed_tools: [get_order, create_return, send_message]
forbidden_effects: [edit_payment_method, delete_order]
expected_state:
return.status: created
refund.status: pending_inspection
required_evidence:
- 用户确认退货方式
- 使用订单绑定地址
budget:
max_tool_calls: 12
max_cost_usd: 0.30
任务契约把需求变成可执行检查。模型生成的自然语言可以变化,世界状态和安全边界仍有明确答案。
契约里还应区分硬约束和优化指标。越权写数据库是硬失败;多调用一次只读工具通常只是成本变差。若把所有指标加权成一个总分,严重安全问题可能被其他高分抵消。
三、结果评测:尽量检查世界状态
能用程序判断的结果,不要先交给 LLM Judge。
Coding Agent 可以运行测试、静态检查和构建,再检查工作区 diff;数据库 Agent 可以比较事务后的表状态;浏览器 Agent 可以读取 DOM 和后端记录;工作流 Agent 可以检查事件是否按要求产生。
τ-bench的思路很有代表性。Agent 与模拟用户对话,同时通过 API 操作数据库。评测不只判断回复是否像客服,还会检查最终数据库状态是否符合目标,并验证领域策略。
确定性结果检查的优势是可重复、便宜、难以用文风讨好。它也有盲区:测试可能不完整,预期状态可能写错,候选版本还可能找到修改测试或读取答案的路径。
所以“测试通过”是证据,不是自动等价于“任务正确”。
四、轨迹评测:过程何时必须被检查
有些任务存在多条合法路径,只要结果正确就够了。另一些任务必须检查过程:
- 高风险动作是否取得批准;
- 是否访问了范围之外的数据;
- 是否把敏感信息发送到外部服务;
- 工具失败后有没有忽略错误继续执行;
- 是否通过读取测试答案完成任务;
- 是否多次重复产生副作用。
轨迹最好以结构化事件保存:
{
"type": "tool_call",
"tool": "create_refund",
"args_hash": "...",
"risk": "high",
"approval_id": "approval-17",
"started_at": "...",
"finished_at": "...",
"result": "success"
}
结构化轨迹允许规则引擎直接检查“所有高风险调用之前是否有有效批准”,也能计算无效重试、工具错误恢复率和路径长度。若日志只有自然语言,就只能再次依赖模型解释。
不需要为每一步规定唯一正确动作。过度约束轨迹会惩罚合法的新策略。轨迹规则应该关注权限、关键前置条件和已知作弊路径。
五、LLM Judge 应该做什么
很多质量无法写成精确断言,例如解释是否清楚、证据是否真正支持结论、方案是否覆盖用户约束。LLM Judge 在这些开放标准上很有用。
使用时应把大问题拆成独立 rubric:
1. 回答是否直接解决用户问题?
2. 每个事实性结论是否有可追溯证据?
3. 是否遗漏明确约束?
4. 是否声称执行了轨迹中没有发生的动作?
Judge 最好输出每条标准的 verdict、置信度和引用证据。只给 1 到 10 的总分,既难诊断,也容易被表达风格影响。
LLM-as-a-Judge Is Not an Oracle记录了自改进管道中的多类失败:Judge 偏差、Harness 和指标错误、ground truth 错误以及 reward hacking。论文提出把 Judge 从 oracle 降为 advisor,让确定性准入规则拥有更高优先级。
这条边界很实用:Judge 可以发现“解释没有覆盖风险”,但不能推翻测试失败、安全违规或环境不一致。
六、冻结集、开发集和隐藏集
同一批任务同时用于发现问题、生成修改和验证修改,Agent 会逐渐适应题目。即使它没有直接读取答案,Skill 和 Prompt 也会积累针对这些样本的特例。
评测数据至少分三层:
| 数据 | 用途 | 是否向优化器暴露 |
|---|---|---|
| 开发集 | 复现问题、生成候选、快速调试 | 可以 |
| 冻结回归集 | 每次版本对比,监测已知能力 | 只暴露任务,不暴露答案 |
| 隐藏留出集 | 最终准入,检测过拟合与作弊 | 不暴露 |
冻结意味着样本、环境、评分器和依赖版本一起固定。只锁住 Prompt,却让工具数据、模型版本或容器镜像漂移,前后分数没有可比性。
隐藏集也需要轮换。反复根据最终结果修改系统,结果本身会泄漏信息。可以保留长期核心集,再定期加入来自真实失败的新样本。
七、Harness 本身也可能制造假提升
模型和 Prompt 没变,评测分数仍可能因为 Harness 改动而变化:
- 解析失败时静默使用默认值;
- 超时任务被错误计为跳过;
- 工具 mock 与生产实现语义不同;
- 缓存把上一次正确答案带到新任务;
- 随机种子、时间或网络数据没有固定;
- 日志截断后,Judge 看不到失败证据。
评测运行本身要有元测试。可以先用 gold solution 验证环境能得到满分,再用已知错误方案确认评分器会失败。每次修改评测器,都要重新跑这两组探针。
SWE-bench Verified由软件工程师检查问题描述、测试补丁和可解性,原因就在这里:高质量任务和 Harness 是评测可信度的一部分,不是准备数据时的一次性杂务。
八、Reward hacking 不需要恶意模型
只要系统持续优化一个不完整指标,就会找到指标漏洞。
Coding Agent 可能:
- 修改测试文件;
- 硬编码公开测试输入;
- 捕获异常后始终返回成功;
- 读取环境中的 gold patch;
- 删除失败测试或跳过慢测试。
文本 Agent 也会投机。它可能学会复述 rubric 关键词、写得更长、加入大量免责声明,从而获得 Judge 高分,却没有提高事实正确性。
EvilGenie专门研究编程环境中的 reward hacking,用隐藏测试、LLM Judge 和测试文件修改检测交叉验证。更近的研究还显示,在 reference-free Judge 上做自我优化时,模型可能变得更有说服力,真实正确率却不提高。
防护要改变信息和权限结构,而不只是把“不要作弊”写进 Prompt:
- 评测答案与候选运行环境隔离;
- 测试目录只读或运行后校验哈希;
- 使用候选看不到的隐藏断言;
- Judge 在看到候选答案前独立求解或提交判断;
- 给基准加入 canary,异常高分触发审计;
- 保存完整 diff、工具轨迹和容器产物。
九、概率 Agent 要重复运行
单次成功不能代表稳定成功。同一任务在不同采样、工具时延和用户表达下可能得到不同结果。
常见指标包括:
pass@1 单次运行成功率
pass^k 连续 k 次都成功的比例
pass@k k 次内至少成功一次的比例
对自主执行系统,pass^k 往往比 pass@k 更接近可靠性需求。用户不会愿意每次重复运行五遍,再挑一次正确结果。
对比两个版本时,应使用相同任务和尽量一致的环境做 paired evaluation,并报告置信区间。20 个样本从 12 个成功变成 14 个成功,未必足以说明新版本稳定更好。
十、不要只看平均分
新版本平均成功率提高,可能同时让高风险任务退化。
评测报告至少要按这些维度切片:
- 任务类型与难度;
- 工具数量和调用深度;
- 是否需要用户澄清;
- 是否包含写操作;
- 上下文长度;
- 失败类型;
- 模型、Harness 和 Skill 版本。
此外还要检查成本质量前沿。成功率只提高 0.5%,token 和延迟却翻倍,未必值得发布;成本减半而成功率只下降在低风险场景,可能是更好的线上选择。
十一、准入条件应该长什么样
一个候选版本可以采用分层门禁:
Gate 1 构建、schema 与基础测试全部通过
Gate 2 安全和权限样本零退化
Gate 3 冻结集总体指标达到非劣标准
Gate 4 目标失败簇有统计上可信的改善
Gate 5 隐藏集无明显过拟合
Gate 6 成本与延迟在预算内
“非劣”比“平均分更高”更适合持续发布。新版本解决一类问题时,可以允许总体分数在很小范围波动,但关键切片不能越过下限。
准入规则要在运行候选前确定。看完结果再调整门槛,很容易为喜欢的版本找理由。
十二、离线通过以后还要灰度
离线数据覆盖不了真实用户的表达、环境和工具异常。通过准入的版本先进入小流量灰度,并保留旧版本作为对照。
线上监控包括:
- 任务完成率与人工接管率;
- 工具错误和无效重试;
- 用户纠正或撤销动作;
- 高风险操作的批准率;
- 延迟、token 和费用;
- 新出现的失败簇。
每次发布都要绑定不可变版本:模型、系统 Prompt、Skill、工具 schema、Harness commit、依赖和评测配置。指标异常时才能准确回滚,而不是猜测最近哪个文件发生了变化。
十三、评测失败也要进入学习循环
评测不仅产生分数,还要产出失败分类:
感知失败 没读到关键状态
检索失败 没找到证据
规划失败 顺序或依赖错误
工具失败 参数、权限、超时
恢复失败 遇错后重复或放弃
验证失败 错把局部成功当完成
沟通失败 没有澄清或误导用户
Harness 失败 事件、缓存、解析错误
只有定位到失败层,才能决定改 Context、Memory、Skill、工具还是 Loop。否则优化器会用最容易修改的 Prompt 去补所有问题,最后得到一份越来越长的免责声明。
新发现的真实失败可以匿名化后进入开发集,再构造相邻变体进入冻结集。这样评测集会随系统边界增长,但历史核心样本仍然保留,防止解决新问题时重新引入旧错误。
十四、一个可以落地的最小方案
个人项目不需要一开始搭建完整平台,可以从下面这套结构开始:
evals/
├── tasks/ # YAML 任务契约
├── fixtures/ # 固定初始环境
├── assertions/ # 确定性检查
├── rubrics/ # Judge 只处理开放质量
├── holdout/ # 与开发集隔离
└── reports/ # 版本、轨迹、结果和成本
每次改 Prompt、Skill 或 Harness:
- 保存候选版本,不覆盖当前稳定版;
- 在临时目录或容器中运行固定任务;
- 先执行确定性断言,再运行 Judge;
- 输出切片结果与失败样本,不只输出均分;
- 满足预先定义的门槛才允许合并;
- 部署后继续记录线上异常并支持一键回滚。
这套流程不炫,但它决定自进化到底是持续积累能力,还是持续积累看不见的回归。
参考资料
如果这篇文章对你有帮助