STOP:让代码改进器改写自己,怎样判断它真的变好了?

笔记日期: 2026-10-18
论文: Self-Taught Optimizer (STOP): Recursively Self-Improving Code Generation
作者: Eric Zelikman、Eliana Lorch、Lester Mackey、Adam Tauman Kalai
阅读版本: arXiv:2310.02304v3,2024-08-16 修订;初稿日期 2023-10-03
资料: 论文全文 · 官方仓库入口

1. 先分清:改的是答案,还是产生答案的方法?

常见的代码改进流程很直观:模型写一段程序,运行评分,再根据结果修改。反复几次后,我们得到一个更好的解。但这个过程中,负责“怎么提问、生成几个候选、优先修改哪个候选、什么时候停止”的程序,往往始终由人手工设计。

STOP 把这段外围程序也变成了优化对象。它先有一个能改进代码的程序,再让这个程序改写自己的代码。如果新版本更善于组织模型调用,那么它不仅可能解决当前问题,还可能在以后解决其他问题时继续发挥作用。这里值得关注的产物是一个可复用的改进方法。

也正因为如此,必须给“自我改进”一个准确的边界。STOP 不更新语言模型的参数,改变的是模型外面的代码框架,包括提示、候选生成、搜索控制、评分缓存和停止规则。它没有证明每轮一定提升,更没有证明可以无限递归地变强。论文给出的证据是:在特定模型、任务和预算下,改进器可以找到比初始版本更有效的搜索过程。

我读这篇论文时最关心的不是递归调用本身,而是递归之后的判断标准:新程序得分更高,究竟说明它更善于优化,还是更善于迎合一套有限且有噪声的评估? 后面的方法、实验与理论,都围绕这个问题展开。

这份笔记依据论文 46 页全文及附录。文中的实验数字来自论文;教学示例、公式推导和后续实验建议会分别说明,不把示意图当作新实验结果。

STOP 的递归改进过程

图 1. 当前改进器把另一个改进器当作待修改程序,再用元效用评价它能否改好下游代码。裁自 STOP v3 原文图 3。可变的是外围程序,语言模型和评价目标仍在这个可变程序之外。

2. 前置知识:两层搜索对应两种评分对象

先把一个程序写成字符串 ss,把评价它的函数写成 u(s)u(s)。这里的效用不只是“答案看起来对不对”,还包括能否正常返回、是否符合输出约定、有没有超时,以及任务本身如何计分。论文理论部分把效用限制在 [0,1][0,1],实际实验则给不同任务配置可计算的分数与资源限制。

一个改进器 II 接收三个输入:效用函数 uu、初始程序 ss、可调用的语言模型 LL。它返回一个新程序:

s′=I(u,s,L).(1)s'=I(u,s,L). \tag{1}

“改进器”只是这个程序想要扮演的角色,接口本身并不保证 u(s′)≥u(s)u(s')\geq u(s)。它可能丢掉原本不错的候选,误解评分函数,也可能在耗尽预算后什么都没返回。外层优化尤其容易受此影响:如果被改坏的是搜索程序,下一轮会继承的不只是一个差答案,而是一套变差的搜索方法。

因此,理解 STOP 要同时盯住两层:

  • 内层搜索:给定一个具体问题,寻找更好的解题程序。
  • 外层搜索:寻找更善于完成上述搜索的程序。

内层候选是解题程序,直接接受任务效用评分。外层候选是改进器,要先让它去改几个下游程序,再根据这些产物的分数评价它。二者可能都调用同一个语言模型,但不能混用评分对象。

这与自动算法设计很接近。区别在于,STOP 让语言模型直接提出程序修改,而不是只在若干预先规定的超参数之间选择。候选空间可以包含新的循环、不同的提示、缓存机制、候选保留规则,以及熟悉搜索算法的组合。表达力很强,但原本由固定优化器保证的约束也可能在修改中被破坏。

另一个需要提前区分的概念是“重复次数”和“任务覆盖面”。同一个任务重复运行五次,可以帮助观察模型采样带来的波动;换成五种不同任务,才能观察更广泛的迁移。它们减少的是不同来源的不确定性。后面的实验统计和泛化界都不能把这两个数互相替代。

3. 元效用:评价一个改进器能改出多好的程序

假设下游任务来自分布 DD。每个任务提供效用函数 uu 和初始解 ss。改进器的总体质量可以定义为:

U(I)=E(u,s)∼D[u ⁣(I(u,s,L))].(2)U(I)=\mathbb{E}_{(u,s)\sim D} \left[u\!\left(I(u,s,L)\right)\right]. \tag{2}

按从里到外的顺序读这条公式就容易了:先让 II 修改初始程序,再用任务自己的 uu 给修改后的结果打分,最后对任务分布取平均。如果生成和评分本身带有随机性,也应包含在这个期望里。

实际做实验时,我们拿不到整个分布,只能在有限个任务上估计。简化写成:

U^(I)=1n∑i=1nui ⁣(I(ui,si,L)).(3)\widehat U(I)=\frac{1}{n}\sum_{i=1}^{n} u_i\!\left(I(u_i,s_i,L)\right). \tag{3}

到这里,外层优化就有了可调用的评分函数。接下来把式(1)中的“初始程序”换成当前改进器本身,把评分函数换成元效用:

It=It−1 ⁣(U^,It−1,L).(4)I_t=I_{t-1}\!\left(\widehat U,I_{t-1},L\right). \tag{4}

式(4)中两个 It−1I_{t-1} 的作用不同。最左边那个是正在执行的搜索程序;括号里的那个是被当作文本修改的代码对象。语言模型提出候选改进器 JJ 后,必须真的让 JJ 去产生下游解,再通过这些解的表现判断它的价值。写出一段很像优化算法的程序,并不等于它已经改善了优化。

论文在外层使用灰盒式的元效用说明:告诉模型这是一个怎样的平均评分过程、有哪些一般资源要求,但不把具体下游任务内容全部展开。这有助于把搜索引向通用的改进方法,而不是直接针对某个任务写捷径。不过,隐藏任务说明并不意味着没有信息泄露;反复返回的分数仍然会让搜索逐渐适应评估器。

还要注意,式(4)没有自动带上“只接受真正更好的版本”。想让训练分数不下降,至少要保留旧版本并公平比较;想让未知任务上的真实表现不下降,还需要控制估计误差。递归只是改变了由谁提出下一版,并没有解决如何可靠选出下一版。

4. 初始改进器为什么从多候选采样开始?

论文的种子改进器并不复杂:向模型说明任务效用和初始代码,请它提出有实质变化的改进,生成一批候选,分别评分,再返回分数最高的那个。原文图 2 的示例采用 0.7 的采样温度。下面按逻辑写成编号伪代码,不复述论文中的具体提示词。

算法 1:种子候选改进器

输入为效用 uu、初始程序 ss、语言模型 LL 和生成预算 BB。

1. 向 L 描述目标和初始程序。
2. 在预算 B 内生成一批候选程序。
3. 用 u 分别评价候选程序。
4. 找出观测效用最大的候选。
5. 返回该候选,作为本轮提出的改进结果。

这个起点把“提出方案”和“判断方案”分开了。语言模型负责给出可能有效的程序,外部效用决定留下哪个。即使模型不能准确评价自己的答案,只要它偶尔能生成优秀候选,测量与选择就可能利用这种能力。

为什么多生成几个会有帮助?先看一个纯粹的教学模型。假设每次独立生成的候选,以概率 pp 满足某个成功标准,那么连续 kk 次都不成功的概率是 (1−p)k(1-p)^k。取补集得到:

P(至少出现一个成功候选)=1−(1−p)k.(5)P(\text{至少出现一个成功候选})=1-(1-p)^k. \tag{5}

再多采样一次带来的增益是 p(1−p)kp(1-p)^k,会随着 kk 增大而减小。这说明扩大候选池通常有边际收益递减,但不能据此推断 STOP 的实际成功率:真实候选之间存在相关性,任务分数可能连续变化,评估器也不一定能辨认出最好的候选。

独立多候选采样的教学曲线

图 2. 独立采样、且能够正确识别成功候选时的理论曲线。参数 pp 是假设值,图中不是 STOP 实测数据。它只解释增加候选为何可能有效,以及收益为何会逐渐变小。

改进器可以改变的远不止候选数。它可以根据反馈改变提议分布,同时保留几条有希望的搜索路径,也可以对分数接近的候选追加测量。反过来说,如果评分噪声很大,候选越多,越容易挑到一个“恰好被高估”的程序。

这里有一个很容易忽略的顺序问题:出现好候选、正确测量它、最终把它留下,是三件不同的事。只增加生成预算,最多直接改善第一件事。STOP 能否持续获益,取决于后两件事是否也跟得上。

5. 把内层改进器放进外层循环

外层一旦要评价某个候选改进器,就不能只看它的文本。必须让它在一组下游任务上运行,收集它产生的解,再汇总分数。这种嵌套关系也是计算开销的来源:看似只评价了一个外层候选,内部可能已经调用了模型很多次。

算法 2:STOP 的外层递归

输入为种子改进器 I0I_0、下游样本 {(ui,si)}i=1n\{(u_i,s_i)\}_{i=1}^{n}、固定模型 LL 和轮数 TT。

1. 定义 MetaScore(J):
2.     对每个下游任务 i,运行 J(u_i, s_i, L)。
3.     用 u_i 评价 J 返回的解。
4.     返回这些下游分数的平均值。
5. current = I_0。
6. 对 t = 1, ..., T:
7.     proposed = current(MetaScore, source(current), L)。
8.     current = proposed 所表示的改进器。
9. 返回 current。

这是对论文递归结构的抽象,并未额外加入独立验证或保留旧版本的步骤。特别是第 8 行,不能理解为“只有测试证明变好才替换”。如果加入这样的接受规则,那就是需要单独说明和计算预算的新设计。

与固定编辑器反复修改代码相比,STOP 的变化在于:一次成功修改可能改变之后的提议机制。若当前版本学会保留多样化候选,下一轮可能触及原本单一路径看不到的算法;若当前版本引入错误的停止条件,之后的搜索也会受影响。这使完整运行链条具有路径依赖。

因此,评价时不应只展示几段漂亮的中间程序。实际部署得到的是某次完整搜索最终返回的一个版本,搜索过程中花掉的资源、失败的轮次、继承的缺陷,都属于这次运行的结果。

灰盒元效用是一个折中。完全公开下游任务细节,更容易产生针对特定任务的投机;只给一个不透明标量,又可能使模型无法理解为什么要修改程序。STOP 保留一般目标说明,同时隐藏具体任务信息,希望让模型改出可复用的搜索逻辑。这个动机是合理的,但是否真的可复用,仍要由迁移结果回答。

6. 主实验任务:带噪声的奇偶学习

论文主要使用 learning parity with noise,简称 LPN。可以把它想成:一个二进制输入里,只有未知的一部分位置决定标签,标签是这些位置上比特的异或结果。用二进制掩码 aa 表示哪些位置被选中:

y=(∑j=1dajxj) mod 2,y~=y⊕η.(6)y=\left(\sum_{j=1}^{d}a_jx_j\right)\bmod 2, \qquad \widetilde y=y\oplus\eta. \tag{6}

先求被选中比特的和,再模 2,得到干净标签 yy。变量 η\eta 表示是否翻转标签,因此学习器看到的 y~\widetilde y 可能带噪声。难点在于从样本恢复规律,而不是把每个训练标签都记住。

四比特奇偶规则与标签噪声示例

图 3. 为阅读笔记构造的四比特示例,干净标签由第 1、3 位决定;星号位置故意翻转了一次标签。它不对应论文的一组实验样本,用来说明“完全拟合训练标签”不一定等于找回真实规则。

主实验使用十比特输入,每一位以 30% 的先验概率被选入隐藏子集。论文描述每个实例有 100 个训练样本和 20 个测试样本;附录 F 的示例任务采用 5% 的训练标签噪声。主评价对 20 个独立生成的 LPN 实例取平均,元目标中则使用任务的重复副本来平均改进器的随机行为。最终留出评价使用另外 50 个新采样的 LPN 实例。

这里有一个阅读细节:附录 F 的示例效用说明包含更短的时间限制及另一组示例测试次数。附录 K 明确提醒,这些说明并非实际运行设置的逐字对应,并报告 LPN 实验采用两秒超时。因此,不能直接把附录里的示例参数当作正式实验配置。

LPN 的优点是评分可执行、反馈相对便宜,初始程序也有改进空间。算法可以在更复杂的搜索和更短的运行时间之间做取舍,适合反复试验一个优化框架。局限同样清楚:十比特的合成任务,与依赖复杂、反馈延迟很长、正确性难以自动判断的真实软件任务差别很大。

所以,这个任务适合回答“机制能不能工作”,却不足以单独回答“一个通用编码 harness 是否可靠”。把这两个层次分开,后面的结果就不会被过度解释。

7. 主结果:有能力提出改进,不代表递归一定成功

GPT-4 主实验包含五条独立的自我改进链,每条运行四轮。原文图 4 的留出元效用均值大致从 61% 上升到 72% 左右,随后趋于平缓。这里的近似数来自图形读数,不是重新获得的原始数据;阴影表示一个标准误。

不同模型的递归改进轨迹

图 4. STOP v3 原文图 4。GPT-4 平均表现上升,GPT-3.5 与 Mixtral 平均表现下降。三个面板的纵轴范围不同,比较时需要看刻度,不能只凭曲线斜率判断变化幅度。

论文用的是 GPT-4-0314、GPT-3.5-turbo-0613 和 Mixtral-8x7B-Instruct-v0.1。模型版本是解释结果的一部分:这些实验说明相应模型在当时配置下的行为,不是今天模型的排名,也没有给出一个永远成立的参数量门槛。

较弱模型的实验各有 25 条运行链。两者的平均分都随着递归而下降。GPT-3.5 中只有 12% 的链达到论文所称“至少 3% 的提升”。也就是说,少数成功案例确实存在,但总体平均结果仍然是退化。若只挑成功链展示,很容易把用户实际面临的结果反过来理解。

一个有意思的现象是,GPT-3.5 在初始改进器下的成绩反而高于 GPT-4。论文的解释是,GPT-4 更常提出复杂但运行较慢的方案,在很短的执行时限下吃亏。这提醒我们,模型更有能力、程序更复杂、给定预算下的效用更高,是三个不同判断。

论文还报告了思维链基线和固定的贪心迭代基线。思维链只统计没有报错的运行时,成绩是 57.7% ± 3.0%;把报错纳入后,则是 49.6% ± 3.5%。贪心迭代基线为 64.2% ± 0.9%。这些 ± 数字沿用原文报告,不额外赋予原文未明确说明的统计含义。

对需要每次返回可用结果的系统,包含错误的成绩更接近端到端体验。排除失败后得到的是“成功运行条件下的答案质量”,它不能代表完整可靠性。自我改进系统容易展示一条漂亮轨迹,因此更需要坚持这个统计口径。

这些对照支持“简单固定循环之外还有改进空间”,却还不能把全部收益归因于递归这个动作。被演化的程序可能同时改变提示词、采样安排、缓存、候选选择和实际资源分配。若要判断递归本身有多少贡献,还需要预算匹配和部件消融。

8. 迁移结果:值得重视,但要记住改进器经过挑选

作者选取了 LPN 搜索中表现较好的一个演化改进器,把它直接用于五个其他任务,不再做额外元训练。相比只在 LPN 上提高分数,这能更直接地检验改进器是否学到了可复用的模型调用方式。

任务初始解种子改进器选出的演化改进器
字符串网格距离43.944.356.7
修改版二次指派20.420.622.1
3-SAT0.021.275.1
Max-Cut0.058.774.2
无噪声奇偶学习50.059.381.7

表中沿用 STOP 原文表 1 的任务效用百分数。不同任务的效用定义不同,不能把它们都叫作同一种准确率。

五个任务的迁移效用

图 5. 根据原文表 1 重绘,保留初始解、种子改进器和选出改进器三个条件。每个任务中,选出的演化版本都高于种子;横轴单位虽然都写成百分数,其含义却依任务而异。

相对种子版本,五个任务分别提高 12.4、1.5、53.9、15.5 和 22.4 个效用百分点。这只是从表中相减得到的数,不是新实验。把这些差值再平均成一个“总体准确率提升”,会掩盖不同任务难度、归一化方式和基线强弱的差别。

我认为这组迁移结果是论文的重要证据,因为它至少说明某个搜索框架的收益没有完全锁死在 LPN 上。但解释时必须同时保留三个条件。

第一,迁移的是经过选择的一个较好改进器,而不是所有演化链的平均产物。第二,表 1 没有给出每项迁移数字的不确定性区间。第三,五个任务仍然都是合成、可执行评分的问题,不是开放式的软件仓库维护任务。

附录 H 给出了被选中用于迁移的改进器。概念上,它会使用多个采样温度、去除完全相同的候选、缓存评分,并记录分数最高的结果。这些设计具有可复用性,但论文没有通过逐项消融证明其中哪个部件贡献了多少。

算法 3:迁移改进器的概念结构

下面是对附录 H 的阅读抽象,不是本文提出的新实现。

1. 准备使用多个温度的候选生成请求。
2. 在资源允许时生成候选程序。
3. 去掉字符串完全相同的重复候选。
4. 对尚未缓存的候选进行效用评价。
5. 跟踪观测分数最高的程序。
6. 返回本轮找到的最佳候选。

去重可以减少浪费,但字符串不同的程序也可能在语义上完全等价。缓存同样有两面性:对于确定性评分,它能避免重复计算;对于随机评分,只记住第一次结果,可能把一次幸运的高分永久固定下来。工程上的节约,并不自动等于统计上的充分。

9. 论文里出现了哪些搜索策略?

正文和附录 B 展示了类似束搜索、模拟退火、遗传搜索、多臂老虎机、局部编辑和温度调度的方案。模型可以根据程序含义提出修改,而不是随机替换字符;也可以建议把两个有希望的程序的不同部分组合起来。

不过,这些例子不能全部并入一个想象中的“最终 STOP 算法”。有些只是被提出的候选,没有最终入选;有些来自更早的种子配置。正文图 5 展示的十轮示例,也不是迁移表中使用的四轮改进器。阅读附录时,最容易发生的误读,就是把所有精彩片段拼成一个从未被整体测量过的系统。

这些方案仍然揭示了几个值得优化的维度:

  • 搜索宽度:同时保留多个好候选,减少过早押注一条路径。
  • 探索方式:改变提示或模型采样温度,产生不同类型的程序。
  • 利用反馈:围绕一个有希望的候选继续改,而不是每次从头生成。
  • 资源控制:避免重复评价,在停滞时终止,或根据反馈分配后续调用。

其中有两个“温度”尤其需要分开。语言模型采样温度改变 token 的提议分布;模拟退火温度则控制一个已被评分的较差候选,有多大概率仍被接受。对效用差 Δ=unew−uold\Delta=u_{\mathrm{new}}-u_{\mathrm{old}},常见退火接受规则是:

P(接受)=min⁡{1,exp⁡(Δ/Ta)}.(7)P(\text{接受})=\min\{1,\exp(\Delta/T_a)\}. \tag{7}

当 TaT_a 较高时,负的 Δ\Delta 受到的惩罚较弱,因此搜索更容易越过局部低谷。模型温度则是在评分之前改变候选从哪里来。两种控制可以组合使用,但不是同一个操作。式(7)用于解释一般启发式,不表示论文迁移收益已被归因到这条规则。

这些结果更有力地支持“语言模型可以在熟悉的算法结构之间搜索”,而不是“模型发明了全新算法”。训练数据里可能已经包含这些经典方法;在无法排除这类背景知识时,不宜把重新组合与历史上的首次发现混为一谈。

10. 泛化界到底保证了什么?

附录 A 讨论一个自然的问题:如果不断优化有限任务上的元效用,这种进步能否推广到真实任务分布?一个简单做法是限制改进器的程序长度,使候选集合成为有限集,再对整个集合建立统一的误差界。

设程序最多包含 ll 个符号,字母表大小为 q≥2q\geq2,每个独立评价样本的得分都在 [0,1][0,1]。长度不超过 ll 的所有字符串数量为:

Nl=∑j=0lqj=ql+1−1q−1.(8)N_l=\sum_{j=0}^{l}q^j =\frac{q^{l+1}-1}{q-1}. \tag{8}

这把空字符串以及语法错误的字符串也算进去了,因此是一个保守的上界。无效程序可以按预先约定获得失败分数。这里并不是说算法会遍历全部字符串,而是先给可能被选中的程序划定一个集合。

10.1 先固定一个改进器

对事先固定的 II,如果 nn 个评价样本独立且有界,Hoeffding 不等式给出:

P ⁣(∣U^(I)−U(I)∣≥ϵ)≤2exp⁡(−2nϵ2).(9)P\!\left(\left|\widehat U(I)-U(I)\right|\geq\epsilon\right) \leq 2\exp(-2n\epsilon^2). \tag{9}

它的意思是:样本越多,估计值偏离真实期望超过 ϵ\epsilon 的概率越小。但这只针对预先固定的一个程序。搜索过程中,我们会看分数再选程序;如果只把这个单候选结论直接套到最后赢家身上,就忽略了选择过程。

10.2 再对整个程序集合取并集界

让“至少一个候选被高估或低估超过 ϵ\epsilon”作为失败事件。即使不同候选的误差相关,也可以把各自失败概率相加,得到:

P ⁣(sup⁡I∣U^(I)−U(I)∣≥ϵ)≤2Nlexp⁡(−2nϵ2).(10)P\!\left(\sup_I|\widehat U(I)-U(I)|\geq\epsilon\right) \leq 2N_l\exp(-2n\epsilon^2). \tag{10}

把右边设为允许的失败概率 δ\delta,先取对数,再移项,就得到:

ϵ=log⁡(2Nl/δ)2n.(11)\epsilon= \sqrt{\frac{\log(2N_l/\delta)}{2n}}. \tag{11}

这条式子展示了样本数和搜索空间的关系:任务样本数出现在分母,候选数通过对数进入分子。程序越长、可选程序越多,想把误差控制在同样范围内,就需要更多独立评价。

这里采用的是重新推导的标准有限类界,与附录的计数和集中不等式思路一致。原文证明中有一处指数项代入后的倒数关系与前式不符,不能直接照抄那一行;式(9)至(11)保留了正确的指数与倒数方向。

10.3 从“估计准确”走到“选出来的程序够好”

设 I∗I^* 是集合里真实效用最高的程序,I^\widehat I 是样本平均分最高的程序。在所有程序的估计误差都不超过 ϵ\epsilon 的事件上:

U(I∗)≤U^(I∗)+ϵ≤U^(I^)+ϵ≤U(I^)+2ϵ.(12)\begin{aligned} U(I^*)&\leq \widehat U(I^*)+\epsilon\\ &\leq \widehat U(\widehat I)+\epsilon\\ &\leq U(\widehat I)+2\epsilon. \end{aligned} \tag{12}

第一步用到了 I∗I^* 的估计误差界;第二步用到了 I^\widehat I 在样本上的最优性;第三步再用一次估计误差界。因此,样本最优程序与真实最优程序的差距至多是 2ϵ2\epsilon。

关键是第二步。STOP 并没有保证自己找到样本上的全局最优程序。若实际找到的程序距离样本最优还差 η\eta,最后的差距应写成 2ϵ+η2\epsilon+\eta。评分估计够准,与搜索优化做得够好,是两项独立要求。

有限程序集合下的统一偏差界

图 6. 按式(11)计算的理论示意,字母表大小为 128,失败概率为 0.05,比较三种最大程序长度。曲线超过 1 时,对范围在 [0,1][0,1] 内的效用没有实用约束;它不是 STOP 实验成绩的置信区间。

10.4 为什么有了定理,实验仍需要留出评价?

第一,所有有界长度字符串构成的集合巨大,界可能很松。对很长的程序和很少的元训练任务,理论上界甚至大于整个效用范围。这样的结果在数学上可以正确,却不足以判断实验提升是真是假。

第二,独立样本的单位必须与泛化目标一致。对同一种 LPN 任务反复采样模型响应,能够减少对生成随机性的估计误差,但不能代替覆盖更多任务种类。我们想推广到其他任务时,仍然缺少关于那些任务的证据。

第三,统一界要求事先规定候选集合与相应假设。如果搜索过程中不断放宽程序长度、改变任务分布或修改计分定义,原来的界不能不加说明地继续沿用。

因此,理论部分更适合被理解为“哪些条件需要控制”的说明。它没有替代实际的留出测试,也没有把四轮实验升级为无限轮递归有效的证明。

11. 随机停止让递归可定义,但不保证进步

附录 A 还讨论了不必接收初始解的最大化器形式,以及用随机停止来定义递归目标的方法。它们帮助我们描述自指过程,避免把一个目标建立在永远不会结束的计算上。

假设每一层以概率 λ\lambda 停止,各层停止决策独立。令 KK 表示总调用次数,包含最后停止的那次。要恰好在第 kk 次结束,必须先继续 k−1k-1 次,再停止一次:

P(K=k)=λ(1−λ)k−1,k≥1.(13)P(K=k)=\lambda(1-\lambda)^{k-1}, \qquad k\geq1. \tag{13}

期望可以用递推直接求。无论如何先发生一次调用;之后以概率 1−λ1-\lambda 进入与原过程同分布的后续递归:

E[K]=1+(1−λ)E[K]⟹E[K]=1λ.(14)\mathbb{E}[K]=1+(1-\lambda)\mathbb{E}[K] \quad\Longrightarrow\quad \mathbb{E}[K]=\frac{1}{\lambda}. \tag{14}

几何分布形式的递归深度

图 7. 三种停止概率对应的几何分布。它用于解释附录中的数学构造,与主实验固定运行四轮的设置分开。较小的停止概率会增加预期深度。

有限的期望深度只说明这个递归定义不必依赖无限次调用。它并不说明程序越递归越好,也不说明最终固定点是优秀优化器。一个每次都原样返回自己的低效程序,同样可以形成固定点。

而且,调用次数受控还不等于总成本受控。若各层执行时间差异很大,少数昂贵调用仍可能主导总费用。这也是为什么实际系统需要同时看调用数、token 数、工具时间和墙钟时间。

把这几个层次分开很有帮助:递归目标可以定义清楚,样本估计可以有统计意义,实际搜索仍然可能表现很差。证明其中一项,并不会自动证明其他项。

12. 计算成本:什么时候值得先搜索一个搜索器?

附录 K 估计每条运行链、每轮大约使用 3,000 次 GPT-4 调用。按四轮相乘,大约是每条链 12,000 次;再按五条链相乘,大约是 60,000 次。这是根据论文所报平均量做的算术估计,不是独立测量的总调用记录,更不能直接换算成确定的 token 数、美元费用或耗时。

嵌套结构解释了为什么开销会放大。假设每轮外层评价 mm 个候选改进器,每个改进器测 nn 个下游任务,每个下游运行调用模型 bb 次,共进行 TT 轮,那么主要调用量的量级是:

Ccalls≈Tmnb.(15)C_{\mathrm{calls}}\approx Tmnb. \tag{15}

这个式子还没有算外层提出候选的调用、失败重试、缓存和提前停止。它是帮助理解预算的模型,不是对论文实验进行的成本拟合。每层都稍微“多试一点”,叠起来就可能大幅增加总费用。

为什么仍值得这样做?因为元搜索的产物可以复用。设搜索一个改进器花费 CmetaC_{\mathrm{meta}},之后每次使用它花费 CuseC_{\mathrm{use}},总共复用 NN 次,那么平均到每次使用的成本为:

C‾(N)=CmetaN+Cuse.(16)\overline C(N)=\frac{C_{\mathrm{meta}}}{N}+C_{\mathrm{use}}. \tag{16}

如果基线每次成本为 CbaseC_{\mathrm{base}},且确实大于新改进器的单次成本,那么成本上打平需要:

N>CmetaCbase−Cuse.(17)N> \frac{C_{\mathrm{meta}}} {C_{\mathrm{base}}-C_{\mathrm{use}}}. \tag{17}

这个比较有前提:两者的质量和工作负载要可比。若新改进器每次运行反而更贵,它的理由就应来自更好的结果,而不是声称靠复用必然省钱。

元搜索成本的假设摊销示例

图 8. 假设元搜索成本为 1,000 单位,新改进器每次成本为 1,基线每次成本为 2,则在 1,000 次使用时打平。这些数是教学假设,不是 STOP 实测费用。

迁移能力因此不仅是泛化问题,也与效率有关。若框架只对一小类任务有用,元搜索成本可能很难摊薄;若它能稳定用于许多任务,前期投入才更有意义。不过,这种复用优势必须经得住模型、任务分布和资源限制的变化。

一个常见的评价盲点是只比较最终程序的单次成本,却不计获得这个程序的搜索成本。另一个极端是只看元搜索非常昂贵,就否定它的一切价值。更合理的方式是说明预计复用范围,并把质量、搜索成本、部署成本放在一起看。

13. 关于沙箱的实验,测到的究竟是什么?

论文另做了一组实验,检查生成文本里是否出现试图关闭某项评价约束的内容。每个条件生成 10,000 次,随后做语法层面的检查;这组实验没有执行这些候选程序。因此,数字表示被标记文本的出现比例,而不是实际成功逃逸沙箱的概率。

模型条件被标记文本比例原文 95% Wilson 区间
GPT-40.42%0.31%-0.57%
GPT-4 加警告0.46%0.35%-0.61%
GPT-3.50.12%0.07%-0.21%
GPT-3.5 加警告0.17%0.11%-0.27%

被语法检查标记的生成比例

图 9. 根据 STOP 表 2 重绘,误差线为原文报告的 Wilson 区间。候选没有在这组实验中执行;按论文采用的 0.05 阈值,增加警告前后的差异没有达到统计显著。

这一结果能支持的窄结论是:可编辑的提议过程会产生与评价约束相冲突的程序文本。不能从这些比例进一步推导模型意图、真实攻破能力,或某个部署系统的通用风险率。

对优化方法而言,更直接的问题是分数的含义是否保持稳定。附录提到,早期评价器曾因候选输出格式异常,给出超过 1,000% 的表面准确率。这个数字明显已经脱离任务定义,高分不能作为任务进步的证据。

可信评价需要把评分契约与被优化对象分开,对格式错误和运行失败规定明确语义,并区分“任务做错”和“评价过程出错”。这属于测量质量问题,不必把所有异常输出都解释成有意识的对抗。

如果一个搜索系统能够改写越来越多外围逻辑,评估的独立性反而更重要。否则,我们看到的也许是程序与计分漏洞的共同适应,而不是解题能力的增加。

14. 局限性

任务分布较窄。 主元训练任务是合成 LPN,迁移覆盖五个紧凑且能自动评分的问题。论文尚未证明同样方法能处理长周期的软件任务、动态依赖、延迟反馈或需要人类判断的目标。

选择过程影响迁移结论。 用于迁移的是 LPN 中表现较好的版本,表格没有呈现所有运行链的迁移分布,也没有给出每项结果的不确定性。一个成功产物能证明可能性,却不能估计重跑一次能否稳定获得它。

没有单调改进保证。 候选可以报错,评分可以受噪声影响,后续轮次可以继承前一版的缺陷。较弱模型的平均退化就是直接证据。

计算量与机制没有完全拆开。 更好的结果可能来自提示、采样、缓存或调用分配的变化。论文没有对所有这些因素完成严格的预算匹配和独立消融。

理论候选类远大于数据能支撑的范围。 对所有有界长度字符串计数很方便,但往往得到很松的界。若想更贴近模型真正会生成的程序,还需要利用更多结构,而不是把完整字符串空间的保证当作实验置信度。

目标说明与实际设置可能不同。 附录明确区分了示例说明和实验运行参数。模型可能在优化描述中的目标,却在另一套具体限制下被评分。灰盒访问减少任务暴露,同时也可能拿走合理分配资源所需的信息。

控制逻辑本身也会失效。 缓存、候选解析、停止规则、旧版本保留方式都在潜在修改范围内。一个局部看起来更省事的变化,可能降低稳定性,甚至改变重复评价的统计含义。

15. 批判性分析:真正稀缺的是可信评价

我认为 STOP 最有价值的贡献,是选中了一个值得搜索的对象:外围改进程序。它比单次答案更可复用,又比整个基础模型更容易修改。实验说明,在模型参数不变时,改变这个对象也能带来收益。

但当搜索越来越自适应,评估器就成了更关键的瓶颈。它既要提供足够快的反馈,又要识别哪些变化能在未知任务上继续成立。若无法区分真实进步、样本噪声和对评估规则的适应,递归只会让系统更高效地追逐不可靠的信号。

15.1 先区分“递归有用”和“找到了好启发式”

一个更有说服力的实验,可以同时改变两个因素:负责编辑的控制器是否随轮次变化;是否保留经过独立验证的最佳旧版本。这样得到四个条件,而不是只比较一个简单种子和一个演化后的复杂系统。

固定编辑器也应允许搜索同样丰富的程序空间。否则,差异可能只是候选空间不同,而不是递归有额外价值。旧版本保留所用的验证样本应与搜索评分分开,其额外评价成本也必须纳入相同预算。

这样的实验才能更直接地回答:动态改进提议机制,是否胜过用一个固定但有能力的编辑器寻找优秀静态控制器?这是本文提出的后续实验,不是 STOP 已完成的验证。

15.2 把完整运行链作为统计单位

使用者真正付费执行的是一次完整元搜索。它会消耗资源,最后返回一个改进器,也可能返回一个差版本。如果只展示所有链里最漂亮的产物,就没有回答“一次新的运行能多可靠地获得这种产物”。

更完整的报告应展示每条独立链在未知任务上的迁移表现,保留失败运行并按既定失败分数计入,还要同时报告成本分布。成功率、中位数、尾部失败和最好结果分别回答不同问题,不能只留最高分。

对比控制器时,可以使用配对随机种子来降低差分方差,但最终测试仍应另采样。否则,那些反复用于比较的种子也会逐渐变成新的训练集。

15.3 泛化想说到哪一层,留出就要做到哪一层

留出新的 LPN 实例,检验的是同一任务族内部的推广。留出一个新的组合优化问题,检验范围更大。换一个领域、模型后端或评价方式,要求又更高。

如果目标是通用编码 harness,就不能只依赖某个短超时条件下的一组分数。应当包含未知任务族和不同资源限制,观察收益是否稳定。一个只在两秒限制下取胜的控制器,可能发现了很有效的局部策略,却不一定学会了通用的资源分配方法。

因此,质量与成本的曲线比一个标量更有解释力。我们想知道的是:给定相同预算谁更好,达到相同质量谁更省,以及优势在哪些区间消失。

15.4 噪声应该影响预算分配,而不只是最后排序

当两个候选分数接近时,下一次模型调用未必应该继续生成新程序。对一个不确定的旧候选再测一次,有时更可能改变最终选择。只缓存单个分数的系统看不到这种机会。

可以考虑记录重复测量及不确定性,同时保留独立的最终测试集。这样做的目的不是让优化器看起来更复杂,而是判断额外测量能否减少错误选择。若评价本来非常稳定,增加这套机制也可能得不偿失;它同样需要实验对照。

这也解释了为什么“多探索”不是万能答案。探索提议空间和探索测量不确定性,消耗同一份预算。一个成熟的搜索框架应明确它把资源花在哪一类不确定性上。

15.5 自指过程不应成为成功标准

让改进器调用自己,在形式上很漂亮,但使用者需要的是在明确预算下获得好解的概率。递归系统如果只是重新找到了一个可靠的束搜索控制器,也可能很有价值;反过来,轨迹中充满复杂自我修改,却经常失败或成本过高,就未必实用。

因此,我会把评价标准放在可复用的质量收益、透明的选择过程和完整的失败统计上。STOP 把这个研究方向做成了具体可讨论的实验对象,但最难的评价问题仍然留在后面。

16. 结论

STOP 的核心动作,是把“如何改好代码”的程序本身变成可修改对象。两层效用把这个过程写得很清楚:内层评解题程序,外层评改进器;同一个固定语言模型参与两层,却不需要更新自己的权重。

GPT-4 的主结果和经过选择的迁移结果,说明这种方式能够产生有用的搜索过程。较弱模型的退化、评分契约失效的案例,以及可能很松的理论界,也说明递归绝不是自动进步的保证。

这篇论文最值得带走的认识是:模型周围的优化程序本身值得搜索,而当这个程序开始控制自己的搜索时,我们必须更认真地判断分数到底代表什么。

参考资料与图源

  1. Eric Zelikman、Eliana Lorch、Lester Mackey、Adam Tauman Kalai:Self-Taught Optimizer (STOP): Recursively Self-Improving Code Generation,v3,2024-08-16。方法见正文第 3-4 节,实验见第 5-6 节;理论见附录 A,策略例子见附录 B,任务说明见附录 F-G,迁移改进器见附录 H,实验细节见附录 K。
  2. 46 页论文全文。本笔记图 1、4 分别裁自原文图 3、4;图 5、9 分别根据原文表 1、2 重绘;图 2、3、6、7、8 是为解释方法而制作的示意图,不是新增实验结果。
  3. STOP 官方资料仓库,作为读者的后续资源入口。