论文解读·

LEGO-RL: Harness-Native Reinforcement Learning for Coding Agents

从真实 sepal_ui-814 case 出发,拆解 LEGO-RL 如何让 Claude Code、OpenHands 与 OpenCode 保留原生 control flow,同时把 token 对齐、MoE routing replay、隐藏 verifier、任务筛选和异步训练接成可信的…

页数
20
形式
交互图解
更新
2026.09.29
文章目录

Yiming Du et al. · arXiv:2608.17393v1 · 2026-08-18

20 页交互图解 · 从 GSPO 到真实 sepal_ui verifier大屏阅读 ↗

使用按钮或 ← → 翻页,F 进入或退出全屏独立打开后可随时点“返回文章”

LEGO-RL 研究的是一个很具体的系统问题:Claude Code、OpenHands 或 OpenCode 会读仓库、改文件、跑命令、压缩历史,policy-gradient trainer 怎样从这类长轨迹中拿到可信的 token 和 reward?论文把 model-API proxy、Harbor sandbox、隐藏 verifier、MoE routing replay、任务筛选和 Live UI 接成一条链,让“一次修复得到 1 分”可以对应到“这批 token 应该被上调”。

作者兴趣度 9.5 / 10和 Claude Code、Codex 这类完整 harness 的 RL 训练直接相关
64.0 → 70.4OpenHands SDK · SWE-bench Verified
62.4 → 68.2Claude Code
57.2 → 66.6OpenCode
> 0.99rollout-training probability correlation

Q1. 为什么完整 coding harness 很难直接拿来做 RL?

训练框架通常希望自己控制 token 序列,完整 harness 却会主动管理上下文和工具交互。 一次 Claude Code trajectory 可能包含多轮模型调用、shell 输出、文件 diff、tool schema、history compaction 和 SDK 重序列化。Trainer 如果只在结束后读取 transcript,再把文字 tokenize 一遍,拿到的 token IDs、response mask 或上下文边界可能已经和 rollout 时不同。

这个差异会直接破坏 policy gradient 的概率比。分母如果对应另一串 token,或者 MoE expert routing 已经变化,优化目标就失去原来的统计含义。论文的要求很直接:在相同权重上,trainer 对 policy-generated tokens 重算的 log-prob 应当接近 serving boundary 记录的 rollout log-prob。

第二个问题来自 reward。Coding Agent 最终只得到一个 sparse verifier reward。论文在系统开发期间看到过六类污染:

失败方式部署防护前出现比例LEGO-RL 的处理
读取 git history4.6% 至 20.5%Agent 阶段把 history rebase 成单一 commit,grading 前恢复
下载 reference fix1.9%分阶段 egress firewall
修改 tests2.4% 至 19.4%tests 到 grading 才注入,并回滚 test-path edits
grader 自己套用 reference patch2.5%审计并剔除受影响任务,线上检测 degenerate reward
grader 依赖网络未单独量化把 grade-time dependency 打包进环境
仓库构建不完整未单独量化hermetic fail-fast build,报告 setup failure 而不是 reward 0

LEGO-RL 的 motivation 可以压缩成一句话:训练系统必须同时守住 trajectory fidelity 和 reward integrity。 前者回答“梯度是不是对着 rollout 时的 policy 行为算的”,后者回答“1 分是不是 Agent 真的修好了任务”。

Q2. 它和现有 agentic RL 框架的边界在哪里?

与 LEGO-RL 最接近的路线选择在 model API 边界接入现成 harness。 论文把相关工作分成几类。

路线rollout 由谁控制强项与 LEGO-RL 的区别
verl、slime、MOLT、SkyRL、AReaLRL framework 内部 loop 或其 abstraction生成和 optimizer 靠得近,容易做异步与大规模训练完整 coding harness 往往要适配成框架规定的 interaction loop
ALE:ROLL + ROCK + iFlowtrainer、sandbox、CLI agent 协同设计整栈一致性通过共同控制 full stack 获得一致性,目标并非兼容任意现成 harness
Agent Lightning现有 Agent 加 SDK callback能接多种 agent system论文 Table 1 没有报告 history alignment、R3 或 reward-hack defense
Polar、rLLM、OpenForgeRLmodel API 边界观察原生 harness保留既有 Agent control flow与 LEGO-RL 最接近;各自对 routing replay、sandbox、operations 和 observability 的覆盖不同
LEGO-RLClaude Code、OpenHands、OpenCode 原生 looptoken capture、R3、Harbor verifier、Live UI 和运维闭环贡献在完整系统组合;论文没有声称每项组件都是第一次提出

论文也和 SWE-RL、DeepSWE、LongContext 一类工作区分开来。SWE-RL 没有 executable interaction;其他工作常在框架自有 loop 中训练。LEGO-RL 关注的是部署时会真实使用的 harness 是否也能原样参与训练。

这里要保留一个证据边界:Table 1 的功能勾选由 LEGO-RL 作者整理,本文没有逐个复现十个框架。它适合说明设计空间,不能当成独立 benchmark 排名。

Q3. LEGO-RL 的训练闭环、数据和 judge 到底怎样工作?

3.1 一条 trajectory 的数据流

LEGO-RL 训练基础设施
原论文 Figure 1。Native harness 在独立 sandbox 中运行;in-process proxy 保存真实生成;verifier reward 和 trajectory 进入 buffer,再由 trainer 更新 policy。论文按 CC0 发布。

每个 task 是 x = (q_x, R_x, V_x):问题描述、初始化仓库和 task-specific verifier。Harness 根据当前交互状态构造 context,policy 生成 assistant tokens,harness 执行工具并改变仓库。LEGO-RL 只训练 assistant response tokens,不把 tool output 或 user message 当成 policy action。

Proxy 需要处理一个麻烦细节:harness 可能在下一轮请求里重新序列化旧消息。官方实现 revision a3e28f171be165b5e8cda45030f336ec103690a7 直接把 proxy 捕获的 token、mask、log-prob 和 routing 放进 AgentLoopOutput:

prompt_ids = traj_acc[:prompt_token_len]
response_ids = traj_acc[prompt_token_len:]
response_mask = tail_mask
response_logprobs = tail_lp
response_routing = (
    tail_routing if len(tail_routing) == len(tail_mask) else []
)

output = AgentLoopOutput(
prompt_ids=prompt_ids,
response_ids=response_ids[:self.response_length],
response_mask=response_mask[:self.response_length],
response_logprobs=response_logprobs[:self.response_length],
routed_experts=self._build_routed_experts(
prompt_ids, response_routing[:self.response_length]
),
reward_score=reward_score,
)

这段代码来自官方仓库,不是文章重构的伪代码。实现还会把 timeout、environment setup failure、max turns 和 context overflow 分开标记;trajectory filter 再决定是否进入 loss。

3.2 训练任务从 36,884 条缩到 2,699 条

任务来自 OpenSWE candidate pools。作者先做规则过滤:

  1. 36,884OpenSWE-derived candidates
  2. 22,806通过基本有效性、repo diversity、coarse complexity
  3. 21,681build 与 verifier 可运行
  4. 4 rolloutsQwen3.6-27B + OpenHands SDK
  5. 1-3 / 4保留既不全错也不全对的任务
  6. 2,699最终训练 index

训练集与 SWE-bench Verified 在 repository 和 instance 两层都不重叠。难度筛选只使用一个 model-harness 组合,这是潜在 bias。作者随后在 Claude Code 和 OpenCode 上也获得提升,说明这批任务可以迁移;现有实验没有证明三种 harness 共享同一个最优 curriculum。

官方 utils/create_task_index.py 也说明了 index 的性质。Parquet 行里的 prompt 只是路径占位符,真实 issue 在 Harbor task 的 instruction.md 中:

return {
    "prompt": [{"role": "user", "content": str(path)}],
    "reward_model": {"style": "rule", "ground_truth": None},
    "extra_info": {
        "harbor_task_path": str(path.resolve()),
        "instance_id": path.name,
        "data_source": "harbor",
    },
}

3.3 真实 case:12rambau__sepal_ui-814

这个 instance 来自 openforis/pysepal 的 issue #813 和 PR #814。原始需求由项目维护者提出:第一次调用 alert.update_progress(0, total=10) 后,循环内应该只写 alert.update_progress(i),不必每次重复传 total。

从公开 issue 到 verifier verdict

  1. 上游来源:issue #813 给出可运行示例;PR #814 的公开 patch 修改 alert.py,并把 test_update_progress 改为先以 0/100 初始化,再调用 update_progress(50)。
  2. OpenSWE record:作者保存 base commit 6d825ae167f96ad2e7b76b96ca07de562f74dcf0、问题描述、gold patch、test patch、FAIL_TO_PASS 和 PASS_TO_PASS。
  3. Agent 可见状态:Harbor 初始化仓库,只把 issue text 交给 Agent。Agent 可以读写源代码、运行公开命令,但看不到 gold patch、test patch、测试清单或 grader;egress firewall 阻止它下载公开 PR。
  4. Agent 输出:Agent 修改工作树。论文没有公开这条 instance 的实际训练 transcript,因此本文不编造它具体读过哪些文件或用了哪条 shell 命令。
  5. Verifier setup:Agent 结束后,host 才把 tests/test.patch、parser.py 和含 raw record 的 config.json 注入 sandbox。test.sh 重置测试、应用 test patch、运行 pytest 并解析结果。
  6. PASS:tests/test_sepalwidgets/test_Alert.py::test_update_progress 必须从 fail 变 pass,所有 PASS_TO_PASS 也必须继续通过。否则 reward 为 0;全部满足才是 1。
sepal_ui-814 raw instance
原论文 Figure 15。Raw record 包含 gold/test patch 和测试列表,但图中长字段已截断。
sepal_ui-814 Harbor task
原论文 Figure 16。Host-side task package 的 tests 目录不会在 Agent 阶段出现。
LEGO-RL task index row
原论文 Figure 17。Trainer index 只保存 Harbor task path,不把任务全文复制进训练表。

论文和官方 GitHub 仓库 revision a3e28f1 没有公开这条 instance 的完整 Harbor directory;Figures 15-16 也明确标注 long fields truncated。本文可以核验上游 issue、公开 reference patch 和总体 grading protocol,无法逐行审计作者实际使用的 test.sh 与完整 PASS_TO_PASS 清单。

Q4. 实验怎样设置,结果到底说明了什么?

4.1 主实验控制

三次 production run 都从 Qwen3.5-35B-A3B 开始,使用 GSPO、64 prompts/batch、8 rollouts/prompt、3 epochs。Prompt budget 是 30k tokens,response budget 是 170k;rollout temperature 1.0,validation temperature 0.7。每个 harness 单独训练一次,验证集固定为 SWE-bench Verified 500 tasks。

Harness初始 Qwen3.5LEGO-RL绝对增益同协议 KAT-Coder
OpenHands SDK64.0%70.4%+6.467.0%
Claude Code62.4%68.2%+5.866.8%
OpenCode57.2%66.6%+9.464.8%
三种 harness 的训练与验证曲线
原论文 Figure 3。三种 harness 的 training reward 和 validation 都提高,response length 也随训练增长。Step-0 分数不同,因此不能把三行直接解释为 harness 排名。

每个主要配置只有一次 run。表格证明三次训练都沿同一方向改善,也超过作者在同协议下重测的 Qwen3.6-35B-A3B 和 KAT-Coder-V2.5-Dev;它不能给出优化方差或复现成功概率。

4.2 Alignment:训练的真是 rollout 时那批 token 吗?

HarnessMedian Pearson rKL ×10^-3p99 |Δ mean log p| ×10^-3
OpenHands SDK0.99930.752.1
Claude Code0.99801.352.7
OpenCode0.99930.602.0

每个 training step 的 Pearson 都不低于 0.989。Routing replay 的 negative control 更有说服力:关闭 replay 时 Pearson 为 0.9946;正确对齐后是 0.9993;把每个 token 的 route 故意错一位后跌到 0.7503。机制“在运行”不够,token 与 route 必须逐位置匹配。

4.3 Task admission 与 curriculum

Trajectory termination profiles
原论文 Figure 4。被排除的 trajectory:Claude Code 7.1%,OpenHands SDK 2.4%,OpenCode 6.4%。
In-batch reward distributions
原论文 Figure 5。0/8 与 8/8 groups 都没有 group-relative learning signal。
Task selection ablation
原论文 Figure 6。四个 951-task pools 中,full band 与 upper half 的 post-warmup validation average 分别为 0.671、0.670;lower half 为 0.640;unscreened pool 没有净改善。

Unscreened pool 在四次 screening rollout 中有 72.7% 的任务从未解出,13.4% 总能解出。能产生组内 reward variation 的任务只占很小一部分。

4.4 Live UI 发现了什么真实 failure?

论文附录给出一个 collapsed run。模型是 Qwen3-30B-A3B,harness 是 OpenHands SDK,task pool 有 449 条。29 个 logged steps 内,training reward 从 0.351 降到 0.050,validation 从 0.230 降到 0.014,mean turns 从 18.9 降到 0.96。Agent 后期几乎不再发 tool call,而是把 shell command 写进 fenced prose。

作者据此构造 early-stop 条件 mean turns < 3,它会在 step 22 触发,比人工终止早 8 步。这个阈值只在一个 case study 上得到验证,能否跨任务工作仍未知。

Live UI failure diagnosis
原论文 Figure 7。左侧 environment-failure run 与右侧 collapsed run 是两个不同诊断案例。
Live UI behavioral analysis
原论文 Figure 8。Claude Code production run 的单题 8-rollout tool trajectory 与 task-level solve-rate changes。

4.5 训练后的行为和系统代价

在 OpenHands SDK production run 的首尾各 420 条 trajectory 上,修改后重新读文件从 73.6% 增到 98.1%,运行 tests 从 85.0% 增到 93.6%,首次修改前检查的文件数从 3.45 增到 6.92,malformed tool calls 从 1.07% 降到 0.15%。出错后仍解决任务的比例只从 63.9% 增到 66.8%,变化较小。

Reasoning share over training
原论文 Figure 11。Reasoning share 上升表示 trajectory shape 改变,不能单独证明它导致成功。
Behavior changes before and after training
原论文 Figure 12。Tool allocation 与具体行为变化。

3,699 条 OpenHands SDK trials 中,Agent execution 平均占 840.5 / 920.4 秒,也就是 91.3%。相同 7.5 小时内,synchronous training 完成 3 steps,asynchronous 完成 7 steps;实测 step time 改善 2.5×。两组 optimizer throughput 不匹配,校正后估计约 1.9×。

Synchronous and asynchronous schedule
原论文 Figure 9。异步执行允许新 rollout 不等待上一批最慢 trajectory。
Nydus and OCI image delivery
原论文 Figure 10。100 个 images 上,network 21.6GB→1.59GB,disk writes 65.6GB→5.29GB。

Deck 还完整收录了论文 Figures 13-14 的 task grid 与 diagnostic panels,以及 Figures 15-17 的三种 data representation。原论文 17 幅 Figure 均未遗漏;10 张 Table 已在正文、Deck 或 source manifest 中逐项登记。

Q5. 对 Claude Code、Codex harness 研究有什么直接启发?

最值得复用的是三份明确的契约。

第一份是 trajectory contract。Harness 可以自由压缩和重写上下文,训练系统必须在 model-serving boundary 保存真实 token IDs、response mask、log-probs 和必要的 routing metadata。只保存最终 transcript 不够。

第二份是 reward contract。Agent 看得到 issue 和 repo,看不到 gold patch、hidden tests、测试列表或 grader;外网也不能成为下载 reference fix 的旁路。Verifier 在 Agent 结束后运行,基础设施错误单独分类。这样 reward 才能代表“修复是否通过验收”。

第三份是 curriculum contract。Task validity、trajectory validity 和 reward informativeness 要分开检查。一个能构建、能判分的任务,仍可能因为当前 policy 0/8 或 8/8 而没有训练价值。

如果沿着这篇论文继续做,我会优先补五个实验:

  1. 训练一个同时经历 Claude Code、OpenHands、OpenCode 的 shared policy,再与三个单-harness policies 做等 token、等 wall time 对照,测 harness-general behavior。
  2. 让 sampler 按最近的组内 reward variance 动态重加权,替代三轮训练都使用固定 2,699-task pool 的做法。
  3. 对 reward-integrity defense 做逐项消融,分别量化 hidden tests、egress firewall、history rebasing 和 test-path rollback 的价值。
  4. 在同一配置上做至少 3 个 training seeds,并报告 final、best、area under validation curve 与每题转移矩阵。
  5. 把 alignment 变成 fail-closed invariant。Token coverage、route coverage 或 log-prob discrepancy 一旦越界,trainer 立即停止更新。

Q6. 这篇论文能证明什么,还缺什么?

它证明了三套完整 coding harness 可以在不修改内部 control flow 的前提下进行有效 policy-gradient training。 三次 production run 都提高 SWE-bench Verified。Token-level alignment、routing negative control、reward-integrity audit、task selection ablation 和系统 profiling 同时检查了训练链本身。

能证明Qwen3.5-35B-A3B 在 OpenHands SDK、Claude Code、OpenCode 中分别提高 6.4、5.8、9.4 个绝对百分点。
训练方差未知每个主要配置只有一次 run。三条正向曲线是有价值的证据,但不是重复实验。
任务筛选有单-harness bias2,699 tasks 由 Qwen3.6-27B + OpenHands SDK 的四次 rollout 选出。
Case checker 披露不完整论文只展示截断 record 与目录;官方 GitHub revision a3e28f1 未包含 sepal_ui-814 的完整 Harbor task。
行为分析不是因果实验更多重读、测试和 reasoning 与分数同时变化,论文没有干预单个行为后测因果贡献。
异步 speedup 有校正项2.5× 是实测 wall-time 对比;optimizer throughput 修正后约 1.9×,两组并非完全 matched。

我的结论是:LEGO-RL 的主要贡献是一条可审计的训练数据链。 Native harness 生成什么 token、这些 token 走了哪些 experts、Agent 在什么仓库状态上行动、grader 为什么给 1 分,都能回到明确的系统边界。对 Claude Code、Codex 方向的研究,先把这套训练协议做扎实,再讨论 optimizer 的差异。

留言

留言正在载入…

搜文章