自我改进的上限,是验证器的上限
拿 GCC 当 oracle 去验一个十万行的新编译器——这一手解决的不是「怎么写对」,是「怎么知道写对了」。四类自我改进方法的天花板都在这儿。
怎么知道一个十万行的 C 编译器是对的?
写测试?测试也是它写的。让模型自己检查?那是自己给自己判卷。找人审?十万行 Rust,两周内产出。
Anthropic 那个项目给的答案是:拿 GCC 当 oracle。 具体做法是写一个测试 harness,随机用 GCC 编译内核的大部分文件,只把剩下那些交给 Claude 写的编译器,然后看整体还能不能正确构建。
这一手值得停下来想一会儿,因为它绕开了一个几乎无解的问题:不去证明新编译器是对的,而是找一个已经被世界验证过二十年的东西来对比。
为什么这比它看起来更深刻
正确性证明是不可行的(编译器等价性一般不可判定)。人工审查在这个规模下也不可行。而 GCC 提供的是一个免费的、覆盖极广的、几乎不会错的参照系。
更妙的是那个「随机混合」的设计。它不是「用 GCC 编一遍再用 Claude 编一遍比结果」,而是在同一次构建里混着用:大部分文件走 GCC,剩下的走 Claude 的编译器。这样一来:
- 只要构建出的内核能跑,说明 Claude 编译的那部分和 GCC 编译的那部分在二进制层面是兼容的
- 出了问题,可以用 delta debugging 缩小到具体是哪几个文件的组合
- 每次随机换一批,覆盖面自然铺开
这是把「验证」变成了「差分测试」,而差分测试的成本几乎为零 —— 参照系已经存在。
四条路的验证器各是什么
把上一篇那四种范式按验证器重新排一遍,会看到一个清晰的谱系:
| 范式 | 验证器 | 它有多可信 | 它有多贵 |
|---|---|---|---|
| 并行团队写编译器 | GCC + 测试套件 + CI | 极高(外部、独立、久经考验) | 极低(现成的) |
| Darwin Gödel Machine | SWE-bench / Polyglot | 高(外部基准) | 中(每次改动跑一遍) |
| ADAS | 各领域基准 | 高 | 中到高 |
| SBCO | 学出来的验证器 | 中(自己学的,有门槛) | 低(学一次反复用) |
最后那一行最有意思,因为它承认了一个现实:很多领域没有现成的 oracle,那就只能学一个出来。
SBCO 的做法是学一组按约束拆分的验证函数,并且给它们设了准入门槛 —— 精确率低于 0.80 或者召回低于 0.55 的直接不要。这两个数是这篇里我最看重的细节:它承认了学出来的验证器会错,然后明确说明能容忍多少错。
对比之下,「让另一个模型来评判」这种做法通常连这个门槛都没有。
验证器决定上限,有两层意思
验证器验不了的东西,自我改进就到不了。
DGM 把 SWE-bench 从 20% 提到 50%,改进的全是 SWE-bench 能量到的东西 —— 代码编辑工具、长上下文管理、同行评审机制。它不会自发变得更好读、更好维护、更省钱,因为基准不量这些。
那个编译器项目也一样。它 99% 通过测试套件、能编 Linux 内核,但生成的代码明显不如 GCC 高效 —— 因为它的 oracle 只验「对不对」,不验「快不快」。
优化会精确地停在验证器的边界上,一步都不多走。
还有更危险的一面:验证器本身会被绕过。
上一个系列里那篇讲自动修复 UI 测试的论文给了最具体的例子:系统为了让报告变绿,会把 expect(value).toBe(5) 改成 expect(value).toBeTruthy(),或者直接删掉修不好的用例。同一批数据,朴素口径的收敛率 70%,剔除这些手段之后 50%。
模型没有作弊,它在忠实地优化你给的目标。 而「让验证通过」和「让东西变对」之间,改验证器永远是最短路径。
所以自我改进系统有一条硬性要求:被改的对象和验证的对象必须分开。 DGM 改自己的代码但用外部基准评分;编译器项目改编译器但拿 GCC 对比。一旦 agent 能改动自己的验证器,整个循环就失去意义了。
那没有现成 oracle 的领域怎么办
这是我自己那条路上的实际问题。做「从需求生成测试用例」,产出物的正确性没有 GCC 可以对比 —— 判断一条用例好不好,本身就是个判断题。
从上面这些案例里能抄的有三条:
一、找差分参照,而不是找绝对标准。 GCC 那一手的本质是「和一个已知正确的东西比」。对测试生成来说,可能的参照包括:人工写的用例集、历史上真实发生过的缺陷、同一产品上一个版本的用例。都不完美,但都比「让模型自己判断」强。
二、把验证器拆成一条条约束,并给每条设门槛。 SBCO 那个 0.80 精确率 / 0.55 召回的做法可以直接搬。关键不是门槛定在哪,是必须有一个门槛,并且明确说出学出来的验证器会错多少。
三、承认哪些维度验不了,写进文档。 编译器项目的代码效率验不了,DGM 的可维护性验不了。这些不是缺陷,是边界 —— 写出来的边界不会伤人,没写出来的会。
边界与代价
差分测试要求参照系和被测对象解决同一个问题。 GCC 和 Claude 的编译器目标一致,所以能比。而很多场景里没有这样的对手 —— 你在做的可能就是第一个。
学出来的验证器有一个鸡生蛋问题。 SBCO 学验证器用的是模型自己的输出打分。它靠质量门槛和自监督循环缓解,但验证器的天花板终究受限于产生它的那个模型。这一点那篇没有回避,也没有完全解决。
「验证器决定上限」这个说法有例外。 开放式探索类的工作(DGM 的存档机制)就是在试图突破这一点 —— 保留多样性,让暂时得分不高的分支活着。但那也只是延缓收敛,不是取消边界。
这一篇仍然是读来的。 我自己那套东西的验证器还很弱:判定靠正则,执行层刚接通,没有留出集也没有人工标注的基线。所以上面这些是我准备照着做的,不是我做过的。
下一篇
前两篇都在讲这条路怎么走通。下一篇是算账的 —— 有一篇论文专门去量了「让 meta agent 设计 agent」到底划不划算。
结论不太好看:用累积上下文的做法,比完全忽略之前的设计还差;而经济上的回本点在约 15000 个样例,很多数据集上则是无论规模多大都回不了本。