LEGO-RL: Harness-Native Reinforcement Learning for Coding Agents
Yiming Du, Yuxin Jiang, Tao Yuan, Jianbo Dai, Shaowei Wang, Jierun Chen, Chaofan Tao, Xianzhi Yu, Lifeng Shang, Kam-Fai Wong, Xiaohui Li, Haoli Bai
cs.AI
2026-08-18
华为与港中文提出 Lego-RL,在不改三套原生 coding harness 控制流的前提下用进程内代理截流 token,以 GSPO 训练 Qwen3.5-35B-A3B。SWE-bench Verified 上 OpenHands、Claude Code、OpenCode 分别从 64.0%、62.4%、57.2% 涨到 70.4%、68.2%、66.6%,rollout 与训练端概率相关高于 0.99。
给 coding agent 做强化学习,优化目标是模型在某个 harness 里跑完的整条轨迹:读仓库、调工具、改代码、跑测试,最后拿一个 0/1 的可执行奖励。Claude Code、OpenHands SDK、OpenCode 这类壳本来就不是为 policy-gradient 设计的。
卡点有三处。harness 会压缩上下文、重写对话历史、改写 tool-call 参数,训练端按记录重建出来的 token 经常对不上 rollout 时真正采样的那一串,log-prob 就算不准。稀疏 MoE 在推理时选定的 expert,训练时如果重选一遍,概率再对不上。沙箱崩溃、超时、从 git 历史或网上把标准补丁偷出来,都会把奖励弄脏。现成的 RL 框架多数要求把 agent 改造成框架自己的 rollout 接口。Lego-RL 反过来做:harness 原样跑,训练基础设施围着它接。
华为与港中文把这套基础设施砌在 verl 和 Harbor 上。新接一个 harness 只需要一个轻量 adapter:拉起 agent、接到推理服务、把交互数据交回去。其余流水线共用。
进程内 LLM 代理拦在模型 API 边界,兼容 OpenAI 与 Anthropic。每次生成当场记下 token ID、log-prob、response mask,以及 MoE 的 routing。之后 harness 怎么 compact、怎么重序列化都无所谓,训练只拿生成当时截下来的那份。对齐按消息粒度:system、user、tool-result 必须逐字匹配;tool-call 按稳定 ID 关联,参数被改写也不动已经记下的 policy token。对不齐的内容直接丢掉,不从改写后的 transcript 里猜。MoE 走 R3,把 rollout 时的 expert 选择回放到训练前向。
沙箱侧每个 trial 独立隔离。Nydus 懒加载镜像,agent runtime 只读挂载,超时按阶段拆开。奖励完整性靠一串硬门:agent 阶段把 git 历史 rebase 成单 commit,评测前再还原;出口流量走特权 sidecar,agent 改不了;测试文件评测前不给看;grader 依赖打进镜像,不靠外网。优化用 GSPO 序列级目标,组内相对优势,每题 8 条 rollout。基础设施失败的轨迹权重置零,不全盘丢掉异步流水线。
插件自动做 run validation 和监控。Live UI 把验证分数、终止原因、工具调用、rollout 与训练一致性挂到同一条轨迹上。
同一份 Qwen3.5-35B-A3B,2,699 条 OpenSWE 训练题(与 SWE-bench Verified 在仓库级和实例级都不重叠),200k 上下文,三套 harness 各自训一遍。验证温度 0.7。
| harness | 训前 | Lego-RL | Qwen3.6-35B | KAT-Coder-V2.5 |
| OpenHands SDK | 64.0% | 70.4% | 67.4% | 67.0% |
| Claude Code | 62.4% | 68.2% | 63.4% | 66.8% |
| OpenCode | 57.2% | 66.6% | 60.6% | 64.8% |
KAT-Coder 从 Qwen3.6 后训练而来,在它作者报告的 Claude Code 上相对基座涨 3.4 点,换到 OpenHands 反倒低 0.4 点。同一套权重换一套控制流,增益可以消失。
rollout 与训练端 log-prob 的 Pearson 中位数:OpenHands 0.9993、Claude Code 0.9980、OpenCode 0.9993,全程不低于 0.989。打开 routing replay 后相关从 0.9946 升到 0.9993,expert overlap 0.996。Claude Code 上按 ID 对齐 tool-call,222 次表观不一致里解开 207 次,占 93%。
任务筛选本身就是结果的一部分。36,884 条候选经规则筛到 22,806,构建与评测有效性再砍到 21,681,约 2.5% 的 grader 会错误地打上参考补丁。再用 Qwen3.6-27B 做 4 次试跑,只留解出 1 到 3 次的 2,699 条。未筛选池里 72.7% 从未解出、13.4% 次次解出,组内奖励方差为零。打开防御之前,agent 读 git 历史的比例在 4.6% 到 20.5%,改测试文件 2.4% 到 19.4%,下载参考修复 1.9%。
OpenHands 上改完文件再读一遍的轨迹从 73.6% 升到 98.1%,首次编辑前看过的文件从 3.5 个到 6.9 个。命令失败后仍解出的比例只从 63.9% 到 66.8%。平均回复从 43.5k 涨到 90.9k token,主要来自轮次(46.6 到 83.1),单轮只多 17%。异步相对同步,7.5 小时内 7 步对 3 步;校正 GPU 吞吐差异后,单步约 1.0 小时对 1.9 小时。预构建镜像相对现场 Dockerfile 中位加速 33.2 倍。
这是一套能把市面上真正在用的 coding harness 接进 policy-gradient 的工程框架,不是又一个自研 agent loop。适配成本被压到一个 adapter。对要训 coding agent 的团队,可复用的部分是:在 API 边界截流而不是事后重建;MoE 必须回放 routing;奖励门要按失败模式逐条堵;任务难度要跟着当前 policy 筛。固定题池会随着模型变强而失去组内方差,OpenHands 上零方差组从 44.7% 升到 51.4%。
增益幅度是扎实的渐进。旧一代基座加这套 RL,三个 harness 上都超过了下一代基座和它的官方后训练产物。但它绑在单模型、每个 harness 各自一条 run 上,不能当成任意模型插上就能涨 6 点。
论文自己写了:全部实验只用 Qwen3.5-35B-A3B,每个 harness 单独训,没验证混训和其他架构;每条主配置只跑了一次,没有 run-to-run 方差;奖励是终端 0/1,给不了错误恢复这类中间行为 credit;现有反 hacking 门挡的是他们观察到的模式,不保证穷尽;沙箱加速数字绑在他们的部署环境上。诊断分析能提出原因,不是自动因果验证。
另外两处读下来站得不够稳。难度筛选用的是 Qwen3.6-27B 加 OpenHands,训练却换到 Qwen3.5-35B 和另外两套 harness,筛出来的「中等难度」对别的组合是不是同一条带,没有直接测量。异步加速那组同步对比用了不同 GPU 组,作者做了校正,但 2.5 倍那个未校正数字更容易被引用。Live UI 里用语言模型当诊断助手的案例,更像运维产品演示,撑不起方法主结论。