跳转至

Toward Training Superintelligent Software Agents through Self-Play SWE-RL

会议: ICML 2026
arXiv: 2512.18552
代码: 无
领域: LLM Agent / 软件工程 Agent / RL
关键词: 自博弈, SWE-RL, Bug 注入, 一致性校验, 课程演化

一句话总结

本文提出 Self-play SWE-RL (SSR),让同一个 LLM 在沙箱化代码仓里既扮演"造 bug 的 proposer"又扮演"修 bug 的 solver",仅以 Docker 镜像为输入、用一致性校验和 solve-rate 作奖励做联合 RL,在 SWE-bench Verified 与 SWE-Bench Pro 上分别自我提升 +10.4 / +7.8 分,并稳定优于使用人类标注 issue + 测试套件的"human-data"基线。

研究背景与动机

领域现状:当前主流的软件工程 agent(SWE-agent、CWM、DeepSWE、Kimi-K2 等)都通过 RL with verifiable rewards 来训练,奖励信号来自人类标注的 issue 描述 + pass-to-pass / fail-to-pass 测试集,典型基准是 SWE-bench Verified。

现有痛点:一方面,人类标注的 issue + 测试既贵又不可靠(SWE-bench 必须出 Verified 子集靠人工核验),规模化困难;另一方面,即使加了 RL,agent 实质上还是在"复盘人类开发轨迹",难以发现新的问题类与解法,天花板被人类知识锁死。

核心矛盾:要训练"超人类"软件 agent,就必须摆脱对人类标注数据/环境的依赖;但完全"zero-data" 的自博弈(如 Absolute Zero、R-Zero、LSP)只能在 Python 解释器规则内打转,学不到真实仓库中那些无法从语义推出的工程知识。换句话说,自博弈要"接地"(grounded in real codebases)才能突破固有知识边界。

本文目标:构建一个只需要 Docker 镜像(源码 + 依赖)即可自我进化的软件 agent,做到三件事:(1) 不依赖人写的 issue/测试命令/测试解析器;(2) 提议方和求解方共享同一组 LLM 参数联合训练;(3) 课程难度随当前策略持续演化。

切入角度:把代码仓里"运行测试 → 注入 bug → 弱化测试 → 修 bug"这一全套人类开发流程形式化成一个由 agent 自己产生的 bug 制品(artifact)——5 个文件就能完整定义一个 bug 与其修复规范。这样 bug 的"对错"完全由测试执行客观可验,奖励信号无需任何自然语言。

核心 idea:把代码仓自身当作"游戏规则",让同一 LLM 在 bug-injection 与 bug-solving 两个角色间自博弈,用 solve-rate 把"造的 bug 难度刚好"作为 proposer 的奖励,把"修对所有测试"作为 solver 的奖励,配合"求解失败 → 升级为高阶 bug"的机制,让训练分布随策略自然演化。

方法详解

整体框架

输入是一组沙箱化 Docker 镜像(仅含源码与依赖,不假设已有测试、测试运行命令、测试解析器或语言/框架先验)。同一个 LLM 策略通过不同 prompt 实例化为两个角色,共享参数、联合 RL 更新:

  1. Bug-injection agent 在沙箱里用 Bash + editor 工具探索仓库 → 自学如何跑测试 → 产出 5 文件 bug artifact → 经一致性校验。
  2. Bug-solving agent 拿到 bug artifact 中"反向的 test-weakening patch"作为唯一规范(没有自然语言 issue),在被注入 bug 的代码上产出修复 patch。
  3. Solver 修复失败的轨迹会被回收成"二阶 bug",扩充训练分布。
  4. Proposer 的奖励 = 一致性校验 + solver 的 solve-rate(鼓励"难但可解"),solver 的奖励 = 测试是否全过的二值信号。

工具脚手架直接复用 Code World Model (CWM) 的实现,base model 用 CWM-sft(32B,CWM 的 RL 前 checkpoint),保证比较公平。

关键设计

  1. Bug Artifact 五件套 + 一致性校验:

    • 功能:用 5 个文件把"一个 bug 是什么、怎么判定修没修好"完全形式化——test_script.sh(跑测试)、test_files.txt(评测前重置的 oracle 测试文件白名单)、test_parser.py(任意语言写的测试输出→JSON 解析器)、bug_inject.diff(注入 bug 的 patch)、test_weaken.diff(弱化/删除现有测试以隐藏 bug)。
    • 核心思路:每个 artifact 必须通过一整套自动校验才算合法——测试文件存在且覆盖弱化补丁触及范围、parser 能可靠输出 pass/fail JSON、原始码上 test_script.sh 至少跑出 min_passing_tests 个通过、bug_inject.diff 至少触及 min_changed_files 个文件、注入后至少 min_failing_tests 个原本通过的测试转为失败、弱化补丁能让某些失败测试恢复通过;最关键的是反向 mutation testing:对 bug patch 中每个文件,单独回滚到 fixed 版本,若至少一个失败测试恢复通过则该文件"贡献于 bug",否则整套 artifact 被拒。test_files.txt 的存在保证评测时 oracle 测试永远会被重置回原版,防止 solver 通过改测试来 hack。
    • 设计动机:把"什么是合法 bug"从"人工判断"变成"可执行验证",奖励 \(r_{\text{inject}}\) 才能在没有任何人类标注的情况下保持有意义;反向 mutation 进一步阻止 proposer 通过塞无关改动来骗过校验。
  2. 难度自适应奖励 + 高阶 bug 课程演化:

    • 功能:让 proposer 产出"难度刚好"的 bug,并把 solver 修不掉的失败状态升级为新 bug,使训练分布随当前策略持续滚动更新。
    • 核心思路:设 solver 在该 bug 上的 solve-rate 为 \(s\in[0,1]\),奖励定义为 \(r_{\text{inject}} = -1.0\)(一致性失败)/ \(-\alpha\)(合法但 \(s=0\)\(s=1\))/ \(1-(1+\alpha)s\)\(0<s<1\),理想难度),其中 \(\alpha=0.8\);这相当于在"既非过易也非过难"区间最大化奖励,且对极端 solve-rate 只给小负值以保留梯度信息。Solver 端用极简二值奖励 \(r_{\text{solve}}=+1\)(全测通过)/ \(-1\)(其他)。高阶 bug 的构造:从原仓出发先应用 bug_inject.diff + test_weaken.diff 得到 buggy 仓库,再应用 solver 之前失败的 pred_patch.diff 形成新的 buggy 状态,去掉 .git 重新初始化以防泄漏,作为新一轮 solver 的输入;只到二阶,避免与已有 bug 重叠率过高。
    • 设计动机:静态合成数据集(如 SWE-smith、BugPilot)无法跟着 agent 能力涨而变难;solve-rate 形式的奖励把"难度调度"内生到策略本身,而高阶 bug 正好模拟真实开发中"改 A 又顺手写出 B"的层叠错误,让 agent 学到 multi-step 编辑模式。
  3. Bug-injection 策略:Removal + 历史回退(无自然语言 issue):

    • 功能:保证生成的 bug 既多样又"非平凡",避免退化为一行字面修改(如 var=0 → var=1)。
    • 核心思路:用两个简单 prompt 随机采样——(a) Removal-only:要求 agent 删除整个文件或代码块,并做必要的兼容性修复以保证仓库仍可构建,强迫 solver 重建缺失功能;(b) 历史回退:让 agent 读 git log,挑选有意义的历史变更进行反向应用,使 bug 模式贴近真实演化历史。给 solver 的 prompt 中不合成任何自然语言 issue,只把 "test_weaken.diff 的反向"作为形式化规范——这等价于告诉 solver"请实现让这些被弱化的测试通过的行为",回避了"评估自然语言 issue 质量"这个无解问题;下游在 SWE-bench Verified(用自然语言 issue)上的提升就只能来自"学会写让测试通过的代码",而非 in-domain 泄漏。
    • 设计动机:作者实验发现 direct-injection 会迅速塌缩到 one-line 改动、奖励信号微弱;removal 强迫"从无到有"重建代码、学到仓库结构理解;history-aware 则注入真实开发中的复杂改动模式。两者随机混合在 ablation 中获得最优表现。

损失函数 / 训练策略

  • 两个角色共享同一组 LLM 参数,奖励 \(r_{\text{inject}}\)\(r_{\text{solve}}\) 分别作用于对应轨迹,联合做 on-policy RL 更新。
  • 评估时 temperature=1.0, top-p=0.95,每题只跑一次(无 best-of-N、无 reranker),以排除 test-time scaling 干扰。
  • Baseline 与 SSR 训练用完全相同的环境镜像和超参,唯一差异在 baseline 额外能看到人类 issue 描述与 pass-to-pass / fail-to-pass 测试,从而隔离"自博弈"本身的贡献。

实验关键数据

主实验

基准 指标 Base (CWM-sft) Baseline RL (human-data) SSR (本文) 自我提升
SWE-bench Verified resolve rate 起点 训练全程低于 SSR +10.4 pt 自提升 +10.4
SWE-Bench Pro (public) resolve rate 起点 训练全程低于 SSR +7.8 pt 自提升 +7.8

关键观察:SSR 全程(不是仅最终点)在两个基准上都稳定超过使用人类 issue + 测试的 baseline RL,说明自生成任务比人类工程化数据提供了更丰富、更有效的学习信号。

消融实验

配置 resolve rate 趋势 说明
Full SSR (自博弈) 稳定上升、最优 proposer + solver 联合训练
Injection-only 下降 / 无收益 只训 proposer,没修复信号
Repair-only 提升但弱于 SSR 只在过去 SSR 跑出的有效 bug 池上做 RL,缺少课程演化
Direct-injection prompt 最差 bug 退化为一行字面修改
Removal-only prompt 中等 强迫重建功能
Removal + history 最优 历史回退引入真实复杂模式
二值 reward (忽略 solver feedback) 仅略低于 full reward solve-rate 信号噪声大、对 proposer 增益有限

平均 resolve rate 计算覆盖 1231 条任务(500 SWE-bench Verified + 731 SWE-Bench Pro)。

关键发现

  • 自博弈比"修-only"重要:repair-only 也用 SSR 产出的 bug 池训练,但缺少"边修边升级课程"的在线性,效果显著弱于 full SSR——说明真正的增益不是 bug 池本身的质量,而是分布跟随当前策略持续演化。
  • 造 bug 也是学习:proposer 必须探索如何跑测试、写 parser、设计弱化补丁,这些活动本身就是高质量训练信号,是 injection-only 失败、自博弈成功的关键差异点。
  • Solver-feedback 奖励边际收益小:作者承认 \(r_{\text{inject}}\) 中加入 solve-rate 项相比"只看一致性"提升微弱,原因是单个 \(s\) 数值噪声大、且过易/过难其实并不有害——但即使忽略 solver 反馈,proposer 仍因策略在线更新而产出演化课程,这才是真正的护城河。
  • 不写自然语言 issue 反而泛化:训练时 solver 看到的只是"反向 test-weaken patch",但在 SWE-bench Verified 上面对真实自然语言 issue 仍稳定提升,证明 SSR 学的是"写让测试通过的代码"这件根本能力。

亮点与洞察

  • "形式化 bug"取代"形式化 issue"是最聪明的设计选择:自然语言 issue 既难自动评估又稀缺,而"bug 是什么"完全可以用 5 个可执行文件 + 一致性校验完整定义,把奖励彻底机器化。这一思路可以迁移到任何"输出难评估但执行结果易评估"的领域(如数学证明、SQL 优化、协议实现)。
  • 反向 mutation testing 反 hacking 极妙:proposer 完全可能塞一堆无关 diff 来骗过校验,作者用"逐文件回滚→看是否失败转通过"判定每个文件是否真正贡献于 bug,这等于给 proposer 加了一道"不许塞水"的过滤,且实现成本只是多跑几次测试。
  • 高阶 bug 把失败变资产:solver 失败的预测 patch 直接被回收成新 bug,既避免数据浪费、又自然模拟真实开发中"叠加错误"的多步编辑场景,这套机制可推广到所有 self-play 框架——"对手的弱点"应该成为下一轮训练的核心。
  • Grounded self-play 是关键差异点:作者用"只会 Python 解释器的人 vs. 能读真实 GitHub 仓库的人"这个思想实验,清晰刻画了 ungrounded zero-self-play(Absolute Zero / R-Zero)的天花板——真正的超人类能力必须"接地"到外部世界的真实复杂性。

局限与展望

  • 作者承认的局限:评估只覆盖 1 次 attempt、无 test-time scaling,单基准结果与顶级闭源系统仍有差距;只测试到二阶 bug,更深层级因与已有 bug 重叠率高被回避,长期是否能继续演化未验证。
  • 方法层面:所有 bug 都通过"破坏现有代码 → 修复"产生,本质上仍在 "已有代码的语义流形" 内打转,agent 不会自发提出"这个仓库还缺一个 feature X"这类创造性任务;要真正"超人类",下一步必须把任务定义从 bug-fixing 扩展到 feature-adding / refactoring 等正向开发。
  • 奖励层面:solve-rate feedback 经验证收益极弱,说明当前 proposer 几乎只靠一致性校验在学,"造一个对当前 solver 来说难度刚好的 bug"这件事并未真正被显式优化;未来可探索基于"信息增益"或"学习进度"的更密集 proposer 奖励。
  • 可改进:可以引入跨仓泛化测试(在仓库 A 上自博弈训练→评测仓库 B),目前只在单一镜像池内训练评估;另外二阶以上 bug 的去重可以靠语义 hash 而非简单避免。

相关工作与启发

  • vs SWE-RL (Wei et al., 2025): SWE-RL 是首个开源用 RL + 规则奖励训练 SWE LLM 的工作,但其训练信号来自 GitHub PR 等人类演化数据;SSR 把这套思想推到极致——完全不需要人类标注,仅靠沙箱镜像,由 agent 自己产生整个训练分布。
  • vs SWE-smith / BugPilot (Yang 2026a / Sonwane 2025): 这些工作同样用 LLM 合成 bug 与环境,但 (a) 依赖测试套件、parser、teacher model 蒸馏等更强的人类先验;(b) 是静态生成 pipeline,不随 agent 能力演化;SSR 既最小化先验又把 bug 生成在线耦合到 RL 训练。
  • vs Absolute Zero / R-Zero / LSP (Zhao 2025 / Huang 2025 / Kuba 2025): 这些 ungrounded 自博弈只能在固定规则空间(Python 解释器、推理任务)内打转,无法获取规则外的世界知识;SSR 通过"grounded in real repositories"突破这层天花板。
  • vs SPICE (Liu 2025): SPICE 也强调 corpus-grounded self-play 以获取外部世界反馈来提升通用推理,SSR 可视为该思想在软件工程域的具体落地,且把"corpus"具象化为可执行 Docker 镜像,奖励完全可由执行决定。
  • vs CWM (FAIR CodeGen 2025): CWM 是 32B 开源 SOTA 编码 agent,SSR 直接以 CWM-sft 为底座,证明在不再增加人类数据的前提下,仅靠自博弈就能继续把 CWM 系列往前推。

评分

  • 新颖性: ⭐⭐⭐⭐⭐ 首次把 grounded self-play 在软件工程 agent 上跑通,"bug artifact 五件套 + 反向 mutation testing + 高阶 bug" 整套形式化是真正的方法学贡献。
  • 实验充分度: ⭐⭐⭐⭐ 双基准 + 三组关键 ablation(自博弈组件 / 注入策略 / 奖励项)涵盖到位,但缺跨仓泛化与 scale-up 曲线,整体属于"first step"定位下的合格证明。
  • 写作质量: ⭐⭐⭐⭐⭐ 动机推导极清晰("只会 Python 解释器的人"思想实验出彩),方法各环节图示充分、奖励/校验定义严谨,读完一遍即可复现概念框架。
  • 价值: ⭐⭐⭐⭐⭐ 给"如何让软件 agent 突破人类标注数据天花板"提供了完整、可执行、可扩展的范式,且 base model 选用开源 CWM-sft,为社区后续 scale-up 自博弈奠定了基础。