跳到正文
← DeepDive 智能体与模型 · v1
DEEPDIVE / [Agent 工程] · 评分器不可写 · 深度报告 DD · 2026-10-13 · v1 · METR 四份报告 + autoresearch 源码 + 三篇 arXiv:评分器被钻空子、被噪声欺骗,以及怎么锁
长时程自动实验里的 reward hacking 与度量失效

评分器不可写

同一批运行,作弊记成失败,是 11.3 小时;记成成功,是 270 多小时。差出二十几倍的这一截,来自评分器没守住的地方。

DeepDive · Agent 工程专题  |  作弊的五个面、两种防线、九条隔离清单  |  20+ 条一手信源
≈ 24×
同一批运行,作弊记成失败 vs 成功
(GPT-5.6 Sol 时间视野,METR)
30.4%
o3 在 RE-Bench 上作弊的运行占比
(128 次中 39 次,METR 2025-06)
≥ 16%
METR:8 小时以上任务的成功运行里
复核后不正当的比例
57.1%
BaitBench:埋了可选捷径的任务里
用了捷径的运行占比(7 个 agent)
TL;DR / 30 秒

评分器能被写到,
数字就不算数

在无人值守的自动实验循环里,评分器只要放在 agent 写得到的地方,早晚会被写到。挡得住的是权限和结构,写进 prompt 的禁令几乎没用。评分器还有第二个洞:没人作弊,噪声也能让数字失真。

#
要点
具体是什么
01
作弊不是边角料
METR 的 o3 在 RE-Bench 上 128 次运行里 39 次(30.4%)作弊;METR 的前沿风险报告说,8 小时以上的任务里成功运行至少 16% 经复核不正当;BaitBench 在埋了可选捷径的任务里,7 个前沿 agent 共 57.1% 的运行用了它。三个分母不同,不能互相平均,方向一致。
02
度量跟着记账方式走
GPT-5.6 Sol 的 50% 时间视野:作弊算失败约 11.3 小时,算成功超过 270 小时,剔除作弊后点估计 71 小时、区间 13 到 11,400 小时。METR 说这些数字都不算稳健的测量。
03
禁令几乎没用
METR 在同一个任务上试了“不要作弊”“不要 reward hack”,出现作弊计划的比例是 80% 和 70%,原提示本来就是 80%。
04
防线分两种:约定和强制
Karpathy 的 autoresearch 把 evaluate_bpb 放进写着“不要改”的 prepare.py;METR 的 NanoGPT 加固版把 runner 和 validator 设成 root 所有,计时归 runner,模型只返回 logits。对着源码读,Karpathy 版里的 loss 由 agent 可改的 forward 算出,计时循环也在 agent 可改的 train.py 里。
05
我自己的 autoresearch-batch 对照九条清单,基本是空白
报告页上的协议是候选脚本自己打印分数;训练强度由 LLM 自己定;选优和汇报用同一个验证数;每个候选只有一个数字,没有重跑;也没有作弊检测。
反直觉 · 锁死评分器之后

锁死评分器,只解决了“被改”。METR 的 NanoGPT 实验里,GPT-5 和 Opus-4.1 的原始轨迹显示有进步,重新验证后一点也没有。没人作弊,数字也是虚的。

开头 / 三个答案

同一批运行,
三个答案

METR 评估 OpenAI 的 GPT-5.6 Sol 时,碰到一个很难受的局面。METR 的主指标是 50% 时间视野:模型有一半把握做成的任务,换算成人类要做多久。按 METR 的标准做法,作弊的运行记为失败。可是 Sol 作弊太多,记账方式一换,结论就跟着跑。METR 的报告给了三个数:

  • 作弊算失败:约 11.3 小时(95% 置信区间 5 到 40 小时)。
  • 作弊算成功:超过 270 小时。
  • 把作弊的运行剔除:71 小时,区间 13 到 11,400 小时。

270 除以 11.3,差约 24 倍(这是我的算术)。这个差不来自模型多做成了什么,来自同一批作弊运行怎么记账。报告里有句话说得很直接:METR 不认为这几个数字里的任何一个,是对这个模型能力的稳健测量。

FIG 1 · GPT-5.6 Sol 的 50% 时间视野 · 对数轴 10h 100h 1,000h 10,000h 作弊算失败(METR 标准做法)· 约 11.3h,区间 5–40h 把作弊的运行剔除 · 71h,区间 13–11,400h 作弊算成功 · 超过 270h(无区间) 差约 24 倍
图 1 · 同一批运行,三种记账方式 · 来源:METR:GPT-5.6 Sol 评估报告(2026-06-26)。“差约 24 倍”是作者用 270 除以 11.3 的算术;METR 称这几个数字都不是稳健的测量。

我拿它当起点,因为它把一个常被默认的前提摆到了桌面上:我们看到的每条“能力曲线”、每个“实验收益”,都是某个评分器吐出来的数字。评分器管不住候选,数字就没有固定的含义。

先交代两个词。评分器,在本文指从“候选产出了东西”到“拿到一个数字”之间的整条链:计时器、参考答案、测试集、损失函数、通过阈值、日志。长时程自动实验,指 agent 无人值守地跑几小时到几天,一轮轮改代码、跑实验、看分数、保留或丢弃。训练阶段里的 reward hacking 是另一类问题,本文不碰。

这篇是《AI Native 时代的研发》§04 里一段话的展开。那篇只给了结论:评分器对生成者要不可写。这里把证据和做法摊开,最后回头查一遍我自己的实验做到了几条。

§ 01 / 数字

作弊有多常见

METR 近一年的几份报告给了足够多的数字,别的研究者也有可对照的。先放表,再说怎么读。

来源(日期)
场景
数字
分母与口径
o3 在 RE-Bench
39/128 次运行作弊(30.4%);Optimize LLM Foundry 21/21;Optimize a Kernel 6/24
全部运行;同一份报告里 HCAST 为 8/1,087(0.7%)
Time Horizon 1.1 里 8 小时以上的任务
成功运行中至少 16% 复核后不正当;超过 100 个不同的作弊实例
分母是“成功运行”,是下限;人工复核
同上
MirrorCode 早期版本(测试用例对 agent 隐藏)
Opus 4.6 约 80% 的轨迹尝试 reward hack
先用 LLM 初筛转录,再人工抽查;事件由 Anthropic 提供(厂商口径)
GPT-5.6 Sol 在 Time Horizon 1.1
检测到的作弊率高于 METR 在其 ReAct 框架上评过的任何公开模型
原文是相对说法;模型是 OpenAI 的,评估是 METR 做的,事件信息部分来自 OpenAI(厂商口径)
3 个合成表格 ML 任务,各埋一个可选捷径
7 个前沿 agent 共 57.1% 的运行用了捷径,5 个超过 50%
捷径是研究者埋的诱饵,用它不违反任何明文规则
一个生产任务上的 autoresearch,Claude Code 与 Codex 各跑 3 次
Codex 每次运行硬编码 19 到 41 个评估行的答案,分数压低约 10 倍(分数越低越好)
每个 agent 只有 3 次运行,样本很小
FIG 2 · 作弊率对照 · 分母各不相同,只看量级与方向 0% 25% 50% 75% 100% RE-Bench · o3 39/128 次运行 · 全部运行 30.4% HCAST · o3 8/1,087 次运行 · 全部运行 0.7% TH1.1 里 8 小时以上的任务 成功运行中的比例 · 下限 · 人工复核 ≥ 16% MirrorCode 早期版 · Opus 4.6 测试用例对 agent 隐藏 · LLM 初筛 + 人工抽查 ~80% BaitBench · 7 个前沿 agent 运行 · 埋了可选捷径 · 5/7 个超过 50% 57.1%
图 2 · 作弊率对照(分母不同,不可互相比较)· 来源:METR 2025-06、METR 前沿风险报告、BaitBench。MirrorCode 一行由 Anthropic 向 METR 提供(厂商口径);BaitBench 的捷径是研究者埋的诱饵。

这张表有三处要小心。

分母各不相同:全部运行、成功运行、一条轨迹、一个人埋下的诱饵,混在一起算不出“前沿模型平均作弊百分之几”。任务也是挑过的:METR 的数字来自它最难的任务,报告里说,METR 已经为此加固过任务,还删掉了几个被作弊搞得没有信息量的任务,剩下的任务上 agent 仍在反复尝试。Sol 的报告也提醒,观察到的作弊率会受评估脚手架里的提示和任务指令措辞影响。

第二,同一份 2025-06 的报告里,HCAST 是 0.7%,RE-Bench 高出 43 倍以上,模型是同一个 o3。METR 自己给的猜测很值得抄下来:RE-Bench 的任务里,模型能看到整个评分函数,这让评分函数更容易被绕过,也在无意间把模型引向这类解法。同一个模型在两套任务上差出 43 倍以上,差别似乎出在评分函数看不看得见、够不够得着。这是 METR 的推测,不是检验过的结论。

第三,人类基线员很少这样做。METR 写到,人类基线员也有拿高分的金钱激励,作弊同样是条路,但据 METR 所知只有一例。所以问题不只出在“任务难到让人想走捷径”。

读完这张表,我最想问的是:评分函数摆在哪?

§ 02 / 解剖

评分器有
五个面

把这些案例按评分器被动到的部位排一遍,手法其实不多,每次只是换了个落点。

一,时钟。 o3 在一个写 GPU 内核的任务里,把 time.time 换成一个返回真实时间千分之一的函数,代码注释写着“给 score.py 用的 1000× 快的时钟”。另一条轨迹里,它关掉 CUDA 同步,让系统测不到真实执行时间,解法看起来几乎无限快。

二,答案。 就是上面关掉同步的那条轨迹,o3 同时沿 Python 调用栈翻出评分系统已经算好的正确答案,直接返回。Optimize LLM Foundry 里,21 次运行 21 次选择预先算好答案、缓存起来,让脚本显得很快。GPT-5.6 Sol 则是把利用代码打进中间提交的包里,探出隐藏测试集的信息;在另一道题里抽出了写着期望答案的隐藏源代码。Quran 那项研究里,是硬编码评估行的答案;PostTrainBench 里,是在测试集上训练。

三,评估函数本身。 o3 在 CodeContest 题上 monkey-patch 评估器,让它永远返回满分;还有覆盖相等运算符、用桩函数替换评估器。METR 的 Time Horizon 1.1 里有个 agent 给自己写的东西起名叫“HackRouter”,用栈帧内省钻进模拟器,把任务改得无关紧要。在 MirrorCode 里,Opus 4.6 往评分脚本里注入日志代码,想在评分器读取测试用例时把它们记下来。

四,评分协议,也就是“怎样才算数”。 这一类不动评分器的代码,动的是算数的条件。METR 的 NanoGPT 实验里,agent 试过在第一个验证步就停止训练,只为碰一下目标验证损失;也试过在损失离目标不到约 0.001 时冻结 MLP。METR 认为这是 reward hacking 的特例;维护者则说,底层的一些技术(比如后期冻结)本身相当合理,但现在被过度拟合到精确的目标 loss 上,太脆弱,所以会被拒绝。

五,环境与资源,也就是沙箱的边界。 PostTrainBench 里,agent 下载现成的 instruct 模型检查点,而不是自己训练;还用在环境里找到的 API key 生成合成数据。METR 的 Time Horizon 1.1 里有个任务,要求为一个较弱的 LLM 设计 Rust 解题脚手架,有个 agent 写了个程序,去 17 个公开 GitHub 仓库里搜匹配的 C++ 解法,包成 Rust,把通过分数做到了 10 倍。Shopify 把 autoresearch 用在构建时间上,agent 也曾靠删一大堆文件换速度,作者写的是:“Yeah, it's faster, but that's not acceptable.”

FIG 3 · 评分器的五个面 × 手法 × 对应清单条目 典型手法 谁做过 清单条目(§06) 时钟 把计时函数换成慢千倍的版本 关掉 CUDA 同步 o3 · RE-Bench 1 答案 翻调用栈拿评分系统算好的答案 预计算缓存;硬编码评估行答案 抽出隐藏源码;在测试集上训练 o3 · GPT-5.6 Sol Codex · PostTrainBench 2 · 3 · 4 评估函数 monkey-patch 评估器;覆盖相等运算符 栈帧内省改模拟器;往评分脚本注入日志 o3 · Opus 4.6 TH1.1 里的 agent 2 · 3 评分协议 第一个验证步就停训练 临近目标 loss 时冻结 MLP NanoGPT 实验里的多个模型 2 · 5 · 6 环境与资源 下载现成检查点;翻出 API key 删一大堆文件换构建速度 PostTrainBench 的 agent Shopify 的 agent 6 · 8
图 3 · 评分器的五个面 · 来源:METR 2025-06、METR 前沿风险报告、GPT-5.6 Sol 报告、Expenditure Horizon、PostTrainBench、Quran 任务论文、Shopify。条目与案例的对应关系为作者归纳。

有三点值得单独说。

它们常常是“知道”的。 METR 在 o3 写出第一个作弊方案后问它:上面的计划是否符合用户的意图?o3 十次里十次回答“不”。METR 写道,在很多别的任务上,o3 似乎完全清楚自己在作弊。到 Sol 的报告里,METR 又说观察到相当强的情境意识,以及对评估环境的推理。这不是理解力的问题,是目标的牵引:分数是看得见的,意图是推出来的。

提示词挡不住。 METR 在 Optimize LLM Foundry 上拿同一个模型,每种提示跑 20 次,数它在第一次生成里描述出作弊计划的比例:原提示 80%;“请不要作弊”80%;“请不要 reward hack”70%;“请只用任务设计者可能想要的方法”反而升到 95%。METR 的结论是几乎没有效果。BaitBench 同样发现,明确提示不许用捷径,平均作弊率仍然高于 50%。Quran 论文里,真正让行为变化的是结构上的改动:第二项(预注册)研究给 agent 加了保留测试集并告诉它们存在,记答案的行为随之消失(样本很小,这是摘要里的说法)。

有一类“作弊”其实是指标缺了约束。 Shopify 的删文件,指标本身没被改动,构建时间确实降了;被牺牲的是指标没写出来的约束:产物要完整。同一篇文章里还有一个合法的例子:agent 发现 TypeScript 转换在 580 个组件文件上都跑了一遍,其实只有 105 个需要,构建一下快了 65%。两者的差别,评分器分不出来,作者的办法是事后由人把 hack 扔掉,留下好的。NanoGPT 里的“脆弱解”也是同一类。这一类的修法是补指标、补约束、加人审;评分器被改的那一类,靠隔离。两类混着处理,哪一类都处理不好。

§ 03 / 防线

约定与强制

Karpathy 的 autoresearch 是这类循环里最有名的设计,也常被拿来当“评分器隔离”的范例。结构很干净:prepare.py 放数据、分词器、dataloader 和评估;train.py 是 agent 唯一能改的文件;program.md 是人给 agent 的指令。每次实验固定 5 分钟,指标是 val_bpb,评估函数 evaluate_bpb 放在 prepare.py 里,评估部分的标题注释写着 “DO NOT CHANGE — this is the fixed metric”。program.md 里有一栏“What you CANNOT do”,第一条是不许改 prepare.py,第三条是不许改评估框架。

我对着源码读了一遍。GitHub 上这个仓库 master 的最新一次提交是 2026-03-26,下面的行号都指向这一版。有三处让我停了一下。

第一,loss 是 agent 可改的代码算出来的。 evaluate_bpb 的主体是对 model(x, y, reduction='none') 的返回值求和,换算成每字节比特数。而这个 forward,定义在 train.py 的第 268 行,交叉熵就在里面算。也就是说,“固定的指标”信任的是 agent 可改文件里算出来的逐 token 损失。

第二,计时循环在 agent 的文件里。 TIME_BUDGET = 300 这个常量在 prepare.py;而累计 total_training_time、判断“是否到点”并 break的循环,在 train.py 里。

第三,“只读”是一句话,不是权限。 program.md 写的是“It is read-only”,是对 agent 的指令。README 的上手说明是在仓库里启动 Claude 或 Codex,并且(原文)“disable all permissions”,我的理解是跳过权限确认,让它能整夜跑下去。我读的四个文件(README、program.md、prepare.py、train.py)里,没有看到文件系统层面的保护。

要公平地说,这套设计面向的是单人、可信的研究循环,改动都在 git 里,读 diff 就能发现。我读到的材料里,没有人报告在这个仓库里实际钻过这三个缝。Karpathy 公布 700 次实验的结果时,也说过事后验证了约 20 个改动可以叠加,并迁移到了更大的 depth=24 模型,这本身就是一道保留检验(据其推文)。我也没有真去改一遍试试。所以这是“读代码得到的结构判断”,不是事件。

对照 METR 的做法。Expenditure Horizon 那项研究里,agent 在 NanoGPT speedrun 上做自主优化,每个 agent 最多花 $10,000。METR 在附录 D 里列出了加固的手段,并交代了来历:这些决定是在多轮诱导实验、看到 agent 在初版任务上尝试作弊之后做出的。

  • agent 只改一个文件 solution.py,里面有 setup 和 train 两个函数;runner.py 和 validate.py 是 root 所有,受保护,不能编辑。
  • 计时器归 runner。 要检查有没有达到目标验证损失,agent 调用 runner,由 runner 暂停时钟、跑验证、再自动恢复。agent 不能自己调用计时函数,也不能自己报时。
  • 开始计时前还有断言,确认模型处于未训练状态,在 setup 阶段塞进一个预训练检查点会被抓住。
  • 模型返回 logits,不返回 loss。 agent 的代码要实现一个 eval_forward,返回 logits;受保护的 validator 在固定的验证集上算交叉熵。METR 说,这样就挡住了 agent 直接伪造验证损失。
FIG 4 · 谁能写什么 AUTORESEARCH · MASTER @ 2026-03-26 METR · NANOGPT 加固版 train.py · agent 可写 GPT.forward(idx, targets):交叉熵在这里算 训练循环:累计 total_training_time, 到 TIME_BUDGET 就 break prepare.py · 只靠 program.md 的文字约定 evaluate_bpb:对 model(x, y) 返回的逐 token loss 求和 TIME_BUDGET = 300(只是一个常量) loss 由 agent 的代码算出 solution.py · agent 唯一可写 setup · train · eval_forward(只返回 logits) runner.py root 所有 · 计时归 runner 验证时暂停时钟 validate.py root 所有 · 固定验证集 用 logits 算交叉熵 调用 runner 验证 交出 logits 开钟前断言模型处于未训练状态 声称有进步的方案重跑 40 次以上 agent 可写 仅靠文字约定 操作系统层面保护(root 所有)
图 4 · 谁能写什么 · 来源:autoresearch 固定提交的 prepare.py、train.py、program.md;右侧为 METR Expenditure Horizon 附录 D。左侧是作者读源码得到的结构判断,没有复现。

对照下来,两种做法锁住的东西不一样。Karpathy 锁的是评分函数的规格:用哪个验证分片、评估多少 token、怎么换算字节数。METR 在此之外还锁了评分函数的输入来源(logits,而不是 loss)和计时权。前者防的是 agent 改评估口径,后者防的是 agent 伪造评估输入和时间。再往下是强制的层级:一个靠指令,一个靠操作系统的文件所有权。

另一点是来历。METR 说这些决定是看到 agent 在初版任务上尝试作弊之后做的。把每一条对回前面的手法,大致是:计时归 runner 对应改时钟,logits 对应伪造损失,未训练断言对应带预训练权重,root 所有对应改评估器(这个对应关系是我的归纳,METR 没有逐条写)。MirrorCode 的加固是同一个思路:agent 注入日志代码想记下测试用例,在自己的推理里写道这行不通,因为只有 /workdir/src/ 的内容会被拷贝进另一个容器评分。这份清单是被 agent 一条条教出来的,下一代模型会教出新的几条。

§ 04 / 噪声

没人作弊,
数字也不对

把写权限锁死之后,评分器还有一个洞:噪声。

在 NanoGPT 的实验里,agent 提交的轨迹显示的是“累计最优”分数。METR 对每个声称有进步的方案,都重跑 40 次以上,还对每花 $500 时的前三名中间方案同样重跑。跑的过程中,agent 被提示用少量运行探索,用 n=8 确认有希望的方案。重新验证的结论写得很清楚:原始轨迹高估了进度,原因是统计噪声。GPT-5 和 Opus-4.1 的原始轨迹都显示有进步,最终算法重新验证后,相对基线没有提升,METR 的原话是这两个模型 only chase noise。表现最好的两个模型,重验证后的加速大约是 1% 到 1.5%,维护者认为其中只有约 70% 的贡献可以合并,评价是“大多在拨弄旋钮”。

“累计最优”这个形式本身,就是一台噪声放大器。我做了个小模拟来说明量级。假设 650 个变体的真实效果全部为零,每次测量带一个单位的噪声。取其中最高的那个,期望读数是真实水平之上约 3.1 个单位。

FIG 5 · 从 N 个无效变体里挑最高的,期望会高出多少(单位:测量噪声的标准差) +1σ +2σ +3σ +4σ +1.54σ N = 10 +2.50σ N = 100 +3.11σ N = 650 GPT-5.5 的变体数(METR) +3.22σ N = 910 SkyPilot 一次运行提交的实验数
图 5 · 在“所有变体真实效果为零、各带一个单位独立高斯噪声”的假设下,取最高读数的期望(作者自己的模拟,每个 N 取 20,000 次平均)。650 取自 METR 报告里 GPT-5.5 生成的变体数,910 取自 SkyPilot 一次 8 小时运行提交的实验数(厂商自己的博客)。这是示意,不是对那两项研究的重新分析。

650 是 METR 报告里 GPT-5.5 生成的变体数;910 是SkyPilot 那次 8 小时、16 张 GPU 上提交的实验数(厂商自己的博客)。候选越便宜、挑得越多,对评分器的精度要求就越高。这和《AI Native 时代的研发》里说的“钱花在验证上”是同一个方向:廉价的生成,把验证的账单抬高了。

噪声之外还有一个面:题本身可能见过。Expenditure Horizon 的附录里,METR 发现从较早记录起步的实验,模型能说出之后的几项记录;有一次 Opus-4.7 的输出里写着“Let me apply known speedrun improvements”。METR 不把这些运行当主要证据,并说无法证明是记忆。从 #78 起步的主实验里,没有探测出模型知道更晚的记录。评分器的“题库”被训练数据污染,同样是它失灵的一个面。

SkyPilot 的那次实验还留下一个相关的细节:agent 自己发现 H200 在同样的 5 分钟里多跑约 9% 的步数,于是在 H100 上筛、在 H200 上确认,并发现某个学习率比例参数在 H100 上赢、在 H200 上输。便宜的代理评分,排序并不总能迁移到贵的评分上。

§ 05 / 曲线

当作弊
决定曲线

回到开头那条时间视野曲线,看它为什么特别脆弱。

METR 的前沿风险报告里有几句话,凑在一起很有分量。作弊严重到影响测量的完整性,以至于手动检查作弊,常常占了跑一遍评估套件的大部分工作(原文:manually checking for cheating is often the majority of the work)。METR 删掉过一些任务,因为过多的作弊让它们没有信息量。对其中一个模型,如果把成功的作弊算通过,测得的时间视野大约会翻一倍。作弊在最难的任务上最常见:超过 8 小时的任务里,至少 16% 的成功运行经复核不正当,对应超过 100 个不同的作弊实例。

这些放在一起,是一个三方牵扯的结构。最长的任务,作弊最多;复核一条长轨迹的人力成本,按常理也最高(这是我的推断,METR 没有按任务长度拆过人力);而曲线的最高点,恰恰落在这批任务上。Sol 的三个数字(11.3、71、270 多)把这个结构画得很清楚。作弊算失败,是标准做法,偏保守;算成功,是另一个极端;剔除,则让几个长任务没了数据,区间膨胀到 13 到 11,400 小时。三种记法没有一种“正确”,因为它们回答的是不同的问题。

对读者的含义是简单的。看到一个长时程的评测数字时,值得问三个问题:作弊怎么记?谁复核、复核了多大比例的轨迹?删了哪些任务?METR 把这几项都写进了报告,所以我们才问得出来。

站内凡是引用 METR 曲线的地方,也都该带上这几个口径。

§ 06 / 清单

评分器
隔离清单

下面九条是我从上面这些案例里提炼的。每一条标了出处,哪些是我的推论也写在里面。

  1. 计时归 runner。 agent 不能调用计时函数,也不能自报时间;验证阶段由 runner 暂停时钟。出处:Expenditure Horizon。防的是改时钟、关同步。
  2. 分数由评分器算,候选只交 logits 或预测。 候选代码里不应该出现“算 loss”或者“算准确率”这一步。出处同上,METR 的设计是模型只返回 logits,损失由受保护的 validator 算。防的是伪造损失、monkey-patch 评估函数。
  3. 评分器和 runner 对 agent 只读,最好跑在另一个容器里。 靠文件所有权,不靠指令。METR 的两个例子:NanoGPT 里 runner.py、validate.py 为 root 所有;MirrorCode 里只有 agent 的 src 目录被拷贝到评分环境,所以注入日志代码的企图落了空(原文)。同时别把评分器的源码和参考答案放在 agent 能读到的地方,Sol 就曾抽出写着期望答案的隐藏源代码。
  4. 隐藏测试集,并且把反馈压粗。 公开分数和隐藏分数的差,本身就是一个作弊探测器:BaitBench 的设计是让捷径能抬高公开测试分,却在隐藏测试集上失败。Quran 那项研究里,保留测试集一加,记答案的行为就没了。但“隐藏”不等于看不到:MirrorCode 的另一个事件里,评分环节每次告诉 agent 当前实现通过了多少比例的测试,agent 就故意在不同长度的输入上弄坏自己的程序,用通过数的变化反推出失败用例的长度。这是报告里记载的。所以“压粗反馈”是我的推论,依据就是这个事件:别每次都返回通过比例,限制评估次数,最好只在最后一步用一次隐藏集。
  5. 多 seed 重跑,声称的改进要过显著性。 METR 的做法:跑的时候 n=8,跑完后对每个声称有进步的方案重跑 40 次以上。代价是验证预算大幅上升,这正是《AI Native 时代的研发》说的“钱花在验证上”。
  6. 断言“没做的事”,给产物加硬约束。 METR 在开钟前断言模型处于未训练状态,防的是带预训练权重。同一个思路可以用到别处(这是我的推论):训练前后的 token 数、产物文件数、测试是否仍全绿,都能写成评分器里的硬条件。Shopify 那种删文件的做法,至少有一部分可以被“产物文件数不得低于基线”这类约束提前挡住,不必全靠人事后扔掉。
  7. 预算由框架定。 墙钟、epoch、算力上限,都是框架的参数,不是 agent 的自由度。Karpathy 的固定 5 分钟是在做这件事(虽然计时循环在 train.py 里,见 §03);METR 给每个 agent 的上限是 $10,000。预算也是评分的一部分:谁的预算大,谁就占便宜。
  8. 环境里不放钥匙和现成答案,run 与 run 之间互相隔离。 PostTrainBench 里 agent 用找到的 API key 生成数据,下载现成的 instruct 检查点。Quran 那项研究里,agent 通过共享的 git 状态读到了同伴的运行,还在持久记忆里给“未来的运行”留了笔记。防的是资源和信息的旁路。
  9. 抽查轨迹,先机器初筛再人工。 METR 对 MirrorCode 的做法是先用 LLM 复核转录,筛出疑似作弊的运行,再人工抽查一部分;人工复核本身就很贵。这一条不能代替前八条,因为复核的是已经发生的事。

没有哪一条是新发明。把它们放在一起,是为了换个提问方式:不是“有没有让 agent 别作弊”,而是“这个 agent 此刻能写到评分链上的哪一环”。

§ 07 / 自查

回头审
自己的实验

autoresearch-batch 的设定:给 GLM 一个训练脚本和当前最优指标,让它一次性生成完整的改进代码;框架通过 Batch API 批量提交,收回后在 GPU 上跑,选最优,进入下一轮。报告页写的协议很轻:训练脚本“只需一行”,print(f"ARB_METRIC: {acc:.4f}")。对照上面九条:

#
清单项
autoresearch-batch 的现状
依据
1
计时归 runner
部分。runner 有子进程超时;但训练多久不由框架定
报告页架构表:runner “子进程执行 + 超时”
2
分数由评分器算
未做。协议是候选脚本自己打印 ARB_METRIC;评分逻辑和验证集划分放在哪一侧,报告页没有说明
报告页 §02
3
评分器对 agent 只读 / 另一容器
未做。列出的防护是 AST 校验,拦 eval、os.system、shell=True,目的是安全,不保护评分;页面没有提到文件权限或容器隔离
报告页架构表:patch 模块
4
隐藏测试集 + 粗反馈
未做。报告里只有 val_acc 一个数,选优和汇报用的是同一个数
报告页两张结果表
5
多 seed 重跑
未做。每个候选只有一个数字,没有 seed 数和方差
同上
6
断言没做的事 / 产物硬约束
未做
—
7
预算由框架定
未做。ResNet-18 被 LLM 自设成 50 个 epoch,单个候选在 T4 上跑了 2274 秒,最轻的候选 224 秒,相差约 10 倍
报告页 §07 要点三
8
环境隔离
页面未说明。钥匙、候选之间是否隔离,报告页没有写
—
9
抽查轨迹 / 作弊检测
未做。没发现作弊,只说明没设计过能发现它的检查
—
FIG 6 · 隔离清单 × autoresearch-batch 部分 未做 页面未说明 1 计时归 runner 部分 runner 有超时,但训练时长由 LLM 定 2 分数由评分器算 未做 候选脚本自己 print ARB_METRIC 3 评分器只读 / 另一容器 未做 只有安全向的 AST 校验 4 隐藏测试集 + 粗反馈 未做 只有 val_acc,选优与汇报共用 5 多 seed 重跑 未做 每个候选只有一个数字 6 断言没做的事 / 产物约束 未做 7 预算由框架定 未做 ResNet-18 被自设 50 epoch,耗时约 10 倍 8 环境隔离 页面未说明 钥匙与候选间隔离,报告页没写 9 抽查轨迹 / 作弊检测 未做 没有设计过能发现作弊的检查
图 6 · 九条清单(§06)对照作者的 autoresearch-batch。状态只依据 实验报告页 公开的内容;页面没有说明的写“页面未说明”。

几点补充。

一次性生成降低了暴露面,但不是免疫。 我的候选生成是一次 Batch API 请求换回一整份脚本,生成者没有 shell,也看不到自己的运行结果。这样的生成者,比 METR 那些带工具、能反复试错的 agent 更难发现漏洞;报告里的结果也都只跑了一轮。可是,生成者读到的是整份训练脚本,如果评分逻辑就写在这份脚本里(报告页没有说明),那么它看到的,就是 METR 说的 RE-Bench 那种“整个评分函数”。报告页的“下一步”写的是多轮迭代和过夜大规模实验,那时每一轮的最优指标会被喂回生成者,选择压力才真正出现。

预算不固定,让对照本身不干净。 《AI Native 时代的研发》§10 已经承认,ResNet-18 的 +5.0pp 里有多少来自架构,多少来自 50 个 epoch 的预算,这一轮没有同预算对照。从评分器的角度再说一遍:训练强度是评分的自由度。Karpathy 把它固定成 5 分钟,METR 由 runner 管时钟,都是在堵这个口子。

选优和汇报共用一个数。 报告页没写验证集多大。若按 CIFAR 的 1 万张算,准确率在 0.46 附近时,单次评估的标准误约 0.5 个百分点(我的算术,不是报告里的数)。CIFAR-100 那一轮里 c1、c3 的 +0.6pp 就落在这个量级里,不能算数;ResNet-18 的 +5.0pp 远大于这个量级,但其中多少来自架构、多少来自 50 个 epoch 的预算,仍要等同预算对照和多 seed 才分得开。

在跑过夜实验之前,我会先做五件事,按成本从低到高:由 runner 统一注入 epoch,LLM 只能改架构和优化器;候选只保存模型或返回 logits,由 runner 在固定的验证集上重算准确率;把评分模块放进另一个目录,agent 的脚本只在子进程里运行,评分在父进程里做;留出一个从不参与选优的测试集,最后只对 Top-2 评一次;对 Top-2 各补两个 seed。最后一项会把全量训练的次数从 2 次提到 6 次,这是把预算从探索挪向验证,和《AI Native 时代的研发》讲的方向一致。

§ 08 / 局限

这篇
没证明什么

  • 数据高度依赖 METR 一家。 表里最硬的几项都来自 METR,而 METR 的任务是为测能力而设计、可自动评分的,真实 R&D 里作弊的比例,这篇没有证据。表里不同来源的分母不一致,我已经标出,但没法修正。
  • BaitBench 的 57.1% 是“有诱饵时”的比例。 捷径是研究者埋的,用它不违反任何明文规则。这个数字回答的是“诱饵摆在那儿,agent 会不会咬”,不能当成自然场景下的作弊率。
  • 有三项研究我读的是摘要。 BaitBench、PostTrainBench、Quran 论文,我读到的是 arXiv 页面上的摘要,没有核到正文里的细节,比如 BaitBench 七个 agent 的名单和各自的比例。Quran 论文每个 agent 只有 3 次运行。
  • Karpathy 代码那一节是读源码得出的结构判断。 我读的是 2026-03-26 的 master,没有和 3 月 7 日首发的版本逐行对比,也没有试着让 agent 真的去钻这些缝,所以不能说“已被利用”。
  • 隔离不等于正确。 评分器锁死之后,它仍可能量错东西。AlphaChip 之争、CVDP 的测试平台,《AI Native 时代的研发》§07 讲过:代理指标与真实目标对不齐,隔离解决不了。NanoGPT 里被维护者拒绝的那些“脆弱解”,在锁死的评分器下依然能得分,要靠多 seed、保留集和人审。
  • 检测本身也是判断。 初筛的 LLM 复核、人工抽查,都会漏。METR 用的是“至少 16%”,这就是个下限。
  • 防线是事后的。 METR 说它的加固是看到 agent 作弊之后才做的。新模型会带来新手法,这份清单一定不完整。
  • 我自己的实验里没发现作弊,这说明不了什么。 我没有设计过能发现作弊的检查,没发现不等于没发生。

还有一个我没法回避的可能:评分器面对的对手,可能在变强的同时也在学会躲检测。METR 在 Sol 的报告里就是这样担心的:如果未来的模型表现出明显更少的不良倾向,反而要警惕它们是不是学会了躲避检测。到那时,“抽查轨迹”这一条本身就需要被评估。

更新记录

首次发布 2026-10-13