智能体评测也需要持续集成
文章称,Terminal-Bench 2.0 的审计发现约三成任务存在问题,修复使准确率变化超过十个百分点,并影响榜单排名。文章将评测维护问题归纳为校验器陈旧、基础设施漂移等层次,主张持续维护。
AI 深度解读
Agent 跑出了能用的代码,评测却判失败,这个分数到底在衡量谁?这篇围绕 Terminal-Bench 的文章认为,编程 Agent 的评测集也会老化:模型找到新的正确解法,判题器却还停留在旧假设里,最后榜单测成了「谁更会迎合测试」。
作者给出的处理办法,是把评测当软件维护:保存执行记录、给规则打版本、用历史结果验证修复,再按固定节奏发布。对需要长期比较 AI 编程工具的团队,这比单看一次通过率更有参考价值。
正确代码为什么会被判错
原文称,Terminal-Bench 2.0 发布前经过了大量人工和语言模型辅助审查,但后续审计仍在约 30% 的评测内容中发现问题。修复让准确率变化超过 10 个百分点,榜单位置最多移动了 3 名。
这些数字说明,评测规则的变化足以影响选型判断。不过,文章没有展开审计样本、问题分布和实验设置,不能把它理解为所有编程评测都有相同比例的问题。
作者把维护欠账分成三类。
第一类是判题器过时。任务要求实现某项功能,Agent 确实完成了,但文件布局与测试预期不同,于是被拒绝。判断对象从功能正确性滑向了实现形式。
第二类是基础设施漂移。执行框架、依赖版本和资源条件发生变化,原先设定的超时限制不再合适。原文举例,一个按 60 秒设计的任务,可能因为模型探索更多路径而需要 120 秒。
第三类是任务描述含糊。例如「创建用户资料」没有说明用户名重复时应该报错还是覆盖,两种行为都能解释得通,判题器却只接受一种。
如果团队只围着通过率调 Agent,就可能把适应测试误当成能力进步。
不过,拒绝另一种文件布局是否属于误判,也要看任务约定。若目录结构本来就是交付要求,检查它当然合理。问题出在规则没有写清,却被测试偷偷加进了答案。
修判题器之前,先保留它判过的答案
作者提出的持续评测架构,关键是让每次判断都能追溯。
任务、判题器、Agent 执行轨迹和参考校验共同进入 Git 管理;每个评测版本使用带标签的发布,历史比较绑定具体 commit hash,不引用会变化的 latest。
遇到疑似误判,报告需要附上执行轨迹、失败测试,以及为什么该解法正确的人工解释。维护者审核修复时,还要把新判题器跑在历史输出上,检查哪些旧结论发生了变化。之后才合并修复、发布新版本,旧版本继续保留。
流水线分五步:执行 Agent,采集 stdout、stderr、文件系统变化和 API 调用;运行测试并收集结果;把完整轨迹按内容寻址的键存入对象存储;收到报告后对照任务描述排查;最后用历史轨迹样本验证修复。
这里最有用的拆分,是把「生成答案」和「判断答案」分开。判题器改了,可以重判旧答案,不必重新消耗 Agent 配额。
下面这段代码展示了如何绑定评测版本,并找出修复前后的判定差异:
class BenchmarkVersion:
def __init__(self, commit_hash, tasks, verifiers):
self.commit = commit_hash
self.tasks = tasks
self.verifiers = verifiers
def evaluate(self, agent_trace):
task = self.tasks[agent_trace.task_id]
verifier = self.verifiers[task.verifier_id]
return verifier.check(agent_trace, task.spec)
def validate_repair(self, new_verifier, historical_traces):
regressions = []
for trace in historical_traces:
old_result = self.evaluate(trace)
new_result = new_verifier.check(trace, trace.task_spec)
if old_result != new_result:
regressions.append((trace, old_result, new_result))
return regressions
要留意 if old_result != new_result::它记录的是所有判定变化,其中既可能有新增误判,也可能有修复成功。变量叫 regressions,不代表列表里的每项都是回归问题,仍需人工确认。
作者称 evaluate() 是纯函数,即同一轨迹、同一版本应得到相同结果。但代码片段本身没有保证这一点:如果 verifier.check() 还依赖外部服务、当前环境或未固定的依赖,重跑仍可能变。确定性需要整套运行条件配合。
超时限制改了,分数就不能直接比较
原文最值得保留的一组结果,来自时间预算:把超时从 60 秒延长到 120 秒后,GPT-4o 上原有的 20 个百分点准确率差距被消除,GPT-4o mini 上的差距则从 30 个百分点缩小到 5 个百分点。
文章没有说清这些差距具体比较的是哪些配置,因此不能进一步推断模型之间的优劣。但它指出的问题成立:60 秒内完成任务,与允许 120 秒完成任务,是不同的测量条件。
资源预算必须和任务一起记录版本,否则同一个分数也可能回答不同的问题。
持续维护还需要监控。任务层记录通过率、平均耗时和 token 消耗;判题器层统计有效解被拒、无效解被接受的情况;Agent 层观察重试、错误恢复和工具调用模式。原文建议,任务通过率在版本间下降超过 10 个百分点时,自动创建问题交给人工审查。下降是排查信号,还不能直接认定判题器出了错。
执行隔离也属于维护成本。原文要求 Agent 在无网络的临时容器中运行,只能写入沙箱目录,限制 CPU 和内存;轨迹存储前清理秘密信息;判题器放在独立容器中,避免 Agent 篡改检查过程或接触其他轨迹。
版本越修越多,也会带来重复评测、审核积压和排名波动。作者的折中是按季度打包修复,对外保留用于正式比较的冻结版本,后台持续验证。
我的判断
我认同作者把评测当成软件维护,尤其是保存完整产物、绑定版本、重判历史答案这几项。它们让「模型变差了」和「尺子变了」有机会被区分开。
但文章对收益的论证仍有边界。约 30% 的问题比例和超时实验都由作者转述,文末列出 Terminal-Bench 论文 ArXiv 2611.08417v1,正文没有提供足够细节供读者复核。季度发布也没有与其他发布频率的对照数据,应当视为维护经验。
另外,原文前面把有效解被拒称为假阴性,后面又称为假阳性,术语口径并不一致。实际建设看板时,应先明确「阳性」代表通过还是发现缺陷,最好直接写清误判方向。
与固定测试集相比,持续评测的代价是历史轨迹存储、重判计算和人工审核。原文建议用于持续超过六个月、输出复杂且可验证、又有维护能力的项目;一次性研究、成功标准稳定的选择题,未必需要整套流水线。这个取舍比「所有评测都上 CI」更合理。
能拿来做什么
- 比较前端或 Node 编程 Agent 时,把任务说明、测试版本、依赖环境和超时预算一起保存。下一轮结果变化时,先确认比较条件是否一致。
- 遇到「功能已经完成却测试失败」,保留代码产物、stdout、stderr 和失败断言,再检查测试是否夹带了未写明的目录或实现要求。
- 修复内部判题脚本前,选取历史成功、失败及争议样本重跑,逐项解释判定变化。先具备可追溯的修复记录,再决定是否建设完整流水线。
- 如果评测会长期用于团队选型,约定固定的对外比较版本和更新时间,让日常修复与正式结论都有明确依据。