让代码模型学会"脑内编译":不执行代码也能验证对错?

你有没有碰到过这种情况——让模型写完一段代码,你想验证它对不对,就得搭环境、跑测试、搞沙箱。竞赛题还好说,真实场景下那些依赖复杂、环境特殊的代码,光配执行环境就能让人崩溃。

更头疼的是,模型自己写的代码,它自己根本不知道跑出来是什么结果。你问它"这段代码在输入 n=5 的时候输出什么",它大概率猜错——哪怕是它自己刚写的。

这篇来自希伯来大学的论文给了一个让我觉得很有意思的思路:与其真跑代码,不如教模型在脑子里模拟执行。用自然语言执行轨迹做监督训练,再用强化学习让模型学会准确预测程序输出。训练完之后,模型可以"脑内编译"自己写的代码,用模拟执行的结果来自我验证、自我修复。

效果呢?在竞赛编程 benchmark 上,Self-RLEF 方法将 pass@1 从 49.0% 拉到了 63.2%,逼近真正执行代码的 oracle 上界(65.3%)。一个 7B 的模型经过训练后,输出预测准确率从不到 35% 飙到 75.5%,直接超过了不少几十B的大模型。

坦率讲,这篇论文的核心 idea 并不复杂,但工程上做得很扎实,几个关键设计选择都经过了充分验证。值不值得花时间细读?我觉得如果你在做代码生成、Agent 自主执行相关的工作,这篇很值得看。


📖 论文信息

  • 标题:Self-Execution Simulation Improves Coding Models
  • 作者:Gallil Maimon, Ori Yoran, Felix Kreuk, Michael Hassid, Gal Cohen, Pierre Chambon, Yossi Adi
  • 机构:arXiv 页面未明确标注机构
  • 日期:2026年3月11日
  • 链接https://arxiv.org/abs/2604.03253

🎯 问题动机:代码模型的"执行盲区"

现有代码模型有一个根本性的短板:它们把代码当成"文本"来处理,对代码的执行语义几乎没有建模。

这带来两个实际问题:

问题一:不会验证自己的输出。 竞赛编程通常给你公开测试用例,让你在提交前自己验证。人类选手会跑一遍看看结果对不对,但模型做不到这一点——它不能真正执行代码(至少在大多数推理场景下做不到)。就算你给它一个执行环境,配置起来也麻烦:依赖安装、权限管理、沙箱隔离,这些在生产环境中都是大问题。

问题二:出错了不会修。 没有执行反馈,模型连"哪里错了"都不知道,更谈不上自我修复。

之前 Meta 的 CWM(Code World Model)也尝试过让模型预测执行结果,但他们发现一个尴尬的结论:模型对自己生成的代码预测得特别差。这篇论文直接挑战了这个结论——通过更好的训练方法,模型完全可以准确预测自己写的代码的执行结果。

图1:Self-Execution Simulation 整体流程

图1:整体流程。模型先生成代码方案,然后模拟执行来预测输出,最后根据预测结果选择最优方案(排名模式)或修复代码(反馈模式)


🏗️ 方法核心:两阶段训练 + 两种推理策略

整个方法分两步走:先教模型"读懂"代码执行过程,再用 RL 磨练它的输出预测精度。

图2:训练和推理框架

图2:左侧是监督微调的数据构建流程——先用 tracer 采集执行轨迹,再用 LLM 转写成自然语言。右侧是多任务 RLVR 训练,包含输出预测和代码生成两个目标,其中执行反馈可以来自真实执行(虚线)或模型自身的模拟执行(实线)

阶段一:自然语言执行轨迹(NLEX)监督微调

这一步的核心思路:让模型逐步模拟程序执行过程,但不是用 JSON 那种结构化格式,而是用自然语言来描述。

具体怎么做?

  1. 从公开代码仓库收集约 3000 万个 Python 函数,外加 3.5 万个竞赛编程题的解法
  2. 对每个函数-输入对,用 Python tracer 记录执行轨迹(中间变量状态)
  3. 用 Qwen3-32B 把结构化的执行轨迹改写成自然语言描述

最终得到大约 8000 万条通用函数的执行描述 + 11.5 万条竞赛编程的执行描述。

为什么用自然语言而不是结构化格式?作者给出的解释是,自然语言能做语义抽象——比如一个循环执行了 100 次,自然语言可以总结为"循环累加到100",而结构化格式得把 100 步全列出来。这对控制 context 长度很关键。

我觉得还有一个更实际的原因:自然语言描述跟模型的 reasoning 训练数据在形式上更接近,更容易跟 CoT 推理能力互相增强。

阶段二:强化学习磨练输出预测

NLEX 训练完之后,模型大致有了"看代码想执行"的能力,但精度还不够。这时候用 RLVR(Reinforcement Learning with Verifiable Rewards)来做精细打磨。

奖励信号很直白: - 预测输出 == 真实输出 → reward = +1 - 预测错了 → reward = -1 - 浮点数允许 1e-5 的误差

训练数据是约 14.3 万条竞赛编程的 代码-输入-输出 三元组。这里有一个关键的设计选择——作者同时训练输出预测代码生成两个任务(Joint training),让模型在写代码和预测执行结果之间形成相互强化。

推理阶段:两种使用方式

训练好的模型在推理时有两种玩法:

Self-Verification(best@k simulate):生成 k 个候选方案,用模拟执行逐个"跑"公开测试用例,选预测通过最多测试的那个。相当于不用执行环境就能做方案筛选。

Self-RLEF(多轮自修复): 1. 生成初始方案 2. 模拟执行公开测试用例 3. 如果预测全过,提交;否则把"哪个测试没过、预测输出是什么"作为反馈,让模型修改代码 4. 重复,最多 10 轮

有个很值得注意的设计:每轮修复都用独立的单轮 prompt,而不是把之前所有轮的对话拼起来。这样避免了长 context 带来的性能下降,也让每轮的推理质量更稳定。


🧪 实验结果:数据说话

输出预测能力

先看模型"脑内编译"的准确率提升,这是整个方法的基础。

CruxEval-O 基准测试(通用代码输出预测)

图3:CruxEval-O 输出预测准确率对比

图3:横轴是模型参数量(对数坐标),纵轴是 CruxEval-O 输出预测准确率。蓝色圆点是训练前,红色星标是 NLEX 训练后。Qwen2.5-3B 从 46% 跳到 68%(+21.9%),Qwen2.5-7B 从 34% 跳到 75.5%(+43.0%),直接超过了 Gemma-3-27B 等大很多的模型。

这张图相当能打。一个 7B 模型训练后直接达到 75.5%,碾压了 Gemma-3-12B(52.5%)、Qwen3-8B(62.5%),甚至跟 Qwen3-14B(69.5%)打得有来有回。3B 模型也到了 68%,超过了 Llama-4-Scout 和 Gemma-3-27B。

竞赛编程输出预测(Table 1)

基础模型 LCB-IO pass@1 pass@5 pass@10 DMC-Test pass@1 pass@5 pass@10
Qwen-7B(完整训练) 79.7 89.4 91.1 77.3 89.3 91.6
Qwen-7B(无 NLEX) 75.7 87.8 89.7 71.8 87.0 89.9
Qwen-3B(完整训练) 66.4 80.6 83.9 59.4 78.2 82.8
Qwen-3B(无 NLEX) 57.1 74.2 78.3 45.9 66.2 72.4

NLEX 在每个配置上都带来了稳定的 3-6 个点提升。特别是 3B 模型上提升更明显(DMC-Test 从 45.9% 到 59.4%,涨了 13.5 个点),说明小模型更依赖这种显式的执行轨迹训练。

RL 对输出预测的加成

这个数据我觉得很有意思(Table 8):

模型 LCB-IO pass@1 DMC-Test pass@1
CWM(官方原版) 60.4 68.9
CWM(无 RL) 30.3 38.4
CWM(加 RL) 89.6 89.2

看到没有?不做 RL 的话,输出预测准确率只有 30%——这基本上是不能用的水平。加了 RL 之后直接飙到 89.6%。RL 在这个任务上的效果是碾压级的。

自执行准确率(对自己写的代码的预测)

前面 CWM 的原始论文声称模型对自己生成的代码预测得很差。来看看这篇论文的结果(Table 2):

模型 RL 目标 DMC pass@1 LCB-IO pass@1
CWM(官方原版) official 57.7 68.6
CWM Joint training 80.2 86.5
CWM 仅输出预测 85.0 88.6
Qwen-7B Joint training 68.4 76.5
Qwen-7B 仅输出预测 74.6 80.1

CWM 官方版本对自己代码的输出预测准确率是 57.7%(DMC),加了 Joint training 之后到 80.2%,差距很大。这说明 CWM 原论文那个"模型不擅长预测自己代码"的结论,更多是训练方法的问题,而不是根本性的限制。

竞赛编程最终效果(Table 3)

重头戏来了——Self-RLEF 在竞赛编程上的最终效果:

方法 DMC pass@1 pass@5 pass@10 LCB-IO pass@1 pass@5 pass@10
CWM(官方原版) 49.0 63.7 67.9 57.4 67.3 70.1
CWM + RL 60.8 72.8 76.0 61.0 67.6 69.2
Self-RLEF(作者方法) 63.2 76.8 80.2 62.3 70.0 71.9
Execution RLEF(oracle) 65.3 77.6 80.6 63.8 70.9 72.8

几个关键观察:

  1. Self-RLEF 相比 CWM 原版,DMC pass@1 从 49.0% 到 63.2%,相对提升接近 29%——这个提升幅度相当实在。

  2. Self-RLEF 跟 oracle(真正执行代码的 RLEF)差距非常小:63.2% vs 65.3%(DMC),62.3% vs 63.8%(LCB-IO)。在 pass@10 上差距更是只有 0.4 个点。

  3. 在 pass@5 和 pass@10 上,Self-RLEF 跟 oracle 几乎持平,说明模拟执行在"能不能淘汰掉错误方案"这件事上做得很好。

Self-Verification 效果(best@k)

图4:Self-Verification 效果对比(多模型多数据集)

图4:四个子图分别展示 Qwen-7B 和 CWM 在 DMCValTest 和 LCB-IO 上的 best@k 表现。蓝线是模拟执行筛选(ours),灰线是真实执行筛选(oracle),灰色阴影区域是"Simulation Gap"。可以看到 CWM 上模拟执行和真实执行几乎重叠。

CWM 上的 Simulation Gap 非常小,说明模拟执行在这个模型上已经相当可靠了。Qwen-7B 上 gap 稍大一些,但 best@k simulate 仍然显著优于 short1@k(不做任何筛选的基线)。

Self-RLEF 的修复能力分析(Table 5)

这个分析很细致:

DMC 公开测试集上的修复情况: - 初始失败的案例中,17.0% 被成功修复 - 初始通过的案例中,只有 1.2% 被"改坏了"

这个比例很健康——修复的远多于改坏的,说明模拟执行提供的反馈信号是有效的。

但看私有测试集(才是最终评判标准)就没那么乐观了:成功修复只有 10.4%,改坏了 5.0%。这说明模型在公开测试上模拟正确并不完全等价于在私有测试上正确——这是模拟执行的一个固有局限。


🔧 消融与分析:几个值得关注的发现

脚手架本身不够,必须训练

Table 4 的消融实验让我觉得很有说服力。作者把 Self-RLEF 的推理框架直接套到未经训练的模型上,结果:

  • Qwen3-32B + Self-RLEF:pass@1 暴跌 10.6-20.1 个点
  • CWM(未做 RL)+ Self-RLEF:pass@1 下降 4.8-7.4 个点

很直白的结论:推理框架只是一个壳,核心能力来自训练。没有输出预测能力的模型,给它再好的推理策略也白搭,反而因为错误的模拟执行反馈把原本对的方案改错了。

跨模型验证能力

一个很实用的发现:训练好的模型可以用来验证其他模型生成的代码。

用 CWM(仅输出预测训练)来筛选 Qwen3-32B 生成的方案,pass@1 能到 86.1%(DMC),相比不筛选的 baseline 提升明显。而且跟用真实执行来筛选的差距很小。

这个能力意味着你可以训练一个小的"验证器"模型,专门用来校验大模型的代码输出,成本比每次都真跑代码低得多。

不损害通用能力

这一点很重要(Table 7)——NLEX 训练并没有让模型在其他任务上变差:

能力 Qwen2.5-7B 原版 + NLEX
CruxEval-Input 0.469 0.505 (+)
MBPP 0.634 0.632 (=)
HumanEval+ 0.652 0.659 (+)
LiveCodeBench v5 0.414 0.413 (=)
GSM8k 0.842 0.826 (-)
Math 500 0.518 0.528 (+)

除了 GSM8k 有轻微下降(-1.6%),其他任务基本持平甚至有提升。CruxEval-Input(输入预测)涨了 3.6 个点,说明执行模拟训练对代码理解能力有正向迁移。

计算效率(Table 9)

把 Self-RLEF 限制到最多 3 轮(而不是默认的 10 轮),性能只从 63.2% 降到 61.5%(DMC pass@1),实际平均使用 2.38 轮。这说明大部分情况下 2-3 轮就够了,计算开销可控。


🔬 更多实验图表

图5:CWM 在 DMCValTest 和 LCB-IO 上的 Self-Verification 效果

图5:CWM(Joint training)的 best@k 效果。蓝色线(模拟执行)和灰色线(真实执行 oracle)几乎完全重叠,Simulation Gap 极小。说明 Joint training 后的 CWM 模拟执行能力已经非常接近真实执行。

图6:CWM 仅输出预测训练的 Self-Verification 效果

图6:CWM(仅输出预测训练)的 best@k 效果。有趣的是,仅做输出预测训练的模型在自我验证上表现也很好,且 Simulation Gap 同样很小。

图7:Qwen-7B 的 Self-Verification 效果

图7:Qwen-7B(Joint training)在 DMCValTest 和 LCB-IO 上的表现。相比 CWM,Qwen-7B 的 Simulation Gap 更大,但 best@k simulate 仍然显著优于 short1@k 基线。

图8:CWM 作为跨模型验证器筛选 Qwen3-32B 方案

图8:用 CWM 来验证 Qwen3-32B 生成的代码方案。蓝色线是模拟执行筛选,灰色线是真实执行筛选。即便跨模型使用,模拟执行的筛选能力也非常接近真实执行。

图9:Qwen-7B 的跨模型验证效果

图9:Qwen-7B 作为验证器筛选 Qwen3-32B 方案的效果。虽然 Simulation Gap 比 CWM 大,但仍然提供了有意义的筛选增益。


🤔 我的判断:这篇论文怎么样?

亮点

  1. 直接否定了 CWM 的核心结论。 CWM 说模型不擅长预测自己代码的执行结果,这篇论文证明那只是训练方法的问题。Joint training + RLVR 之后,自执行预测准确率从 57.7% 到 80.2%,这不是小修小补。

  2. 工程上做得很扎实。 NLEX 数据构建、RL 奖励设计、Self-RLEF 的单轮 prompt 设计,每个环节都有消融验证,不是拍脑袋拼凑的。特别是 Table 4 那个"脚手架消融"实验,直接堵住了"是不是 prompt 工程就够了"这个疑问。

  3. Self-RLEF 跟 oracle 的差距非常小。 63.2% vs 65.3%,说明模拟执行已经可以作为真实执行的实用替代品。这对于不方便搭执行环境的场景(边缘设备、安全沙箱受限)非常有价值。

我觉得有问题的地方

  1. 评估范围偏窄。 全部实验都集中在竞赛编程上——这类问题有标准输入输出、有公开测试用例。但真实的软件工程场景(多文件、有副作用、需要 mock)能不能用?论文自己也承认了这个局限,但没给任何初步验证。

  2. NLEX 数据构建成本不低。 3000 万个 Python 函数跑 tracer + 用 Qwen3-32B 转写,这个计算开销不小。论文没有讨论 NLEX 数据量和最终效果的关系——也许用 500 万条就够了?也许 1000 万条就饱和了?缺少这个消融让人不太踏实。

  3. 对复杂计算的处理。 论文坦承模型在大数乘法、对数运算等复杂计算上表现不好。这其实不意外——LLM 做精确数值计算本来就是弱项。但这意味着在涉及数学运算的代码上,模拟执行可能会给出错误反馈,反而误导修复方向。

  4. 私有测试集上的"改坏"比例。 Table 5 显示公开测试集上 pass->fail 只有 1.2%,但私有测试集上是 5.0%。这说明模型的模拟执行存在 overfitting 公开测试的倾向——公开测试上模拟正确不等于真正理解了程序逻辑。

跟同期工作的对比

这篇论文跟 Meta 的 CWM(Code World Model)关系最密切——实际上直接基于 CWM 做的改进。相比 CWM,核心贡献是:(1) NLEX 自然语言执行轨迹,(2) Joint training with RLVR,(3) Self-RLEF 推理框架。

另一个相关工作是 CodeIO,也是做代码输入/输出预测的,但 CodeIO 更多是评估性质的工作,没有做到这篇论文的自验证和自修复闭环。

说实话,这篇论文不是什么颠覆性的创新——自然语言执行轨迹、RLVR、best@k 筛选这些组件都不新。但把它们组合起来形成一个完整的"不需要执行环境就能自验证"的闭环,并用充分的实验证明这个闭环是 work 的,这个工程价值是实在的。

工程启发

如果你在做代码生成相关的产品,有几个可以直接借鉴的点:

  • 训练一个小的输出预测模型做验证器,比每次都启动执行环境成本低得多,而且 Table 6 证明了跨模型验证是可行的
  • 多轮修复用独立单轮 prompt,不要把所有历史拼成长 context,效果反而更好
  • NLEX 风格的训练数据可以作为代码能力的通用增强手段,它对代码理解(CruxEval-Input)和基础编程(HumanEval+)都有正向迁移
  • 3 轮就够了——Self-RLEF 限制到 3 轮只损失 1.7 个点,实际部署时不需要 10 轮那么多

📝 写在最后

这篇论文做的事情,用一句话概括就是:教代码模型学会"脑内编译"。

方向上我觉得很对——长远来看,代码模型不应该只是"写代码的打字员",而应该真正理解代码的执行语义。当模型能准确预测"这段代码跑完输出什么"的时候,很多下游任务(自动测试、自动调试、代码审查)都会变得容易很多。

但这条路还远没有走完。竞赛编程只是一个起点,真正的挑战在于处理有副作用、有外部依赖、有状态的真实代码。等什么时候模型能在脑子里模拟执行一个有数据库读写的 Web 服务,那才是真正的突破。

不过从这篇论文展示的结果来看,至少在"模型能不能模拟执行自己写的代码"这个基础问题上,答案已经从 CWM 时代的"不太行"变成了"挺行的"。这个进步很实在。


觉得有启发的话,欢迎点赞、在看、转发。跟进最新AI前沿,关注我