RLVR 深入:代码生成方向¶
更新日期:2026-04-26
代码是 RLVR 第二好的赛道(仅次于数学)—— 测试用例就是天然 verifier。但相比数学 RLVR,代码场景的工程复杂度高一个数量级:沙箱、多语言、长程依赖、agent 规划、private repo state 等都让 RL 训练的反馈环远比数学慢且贵。
一、为什么代码 RL 难¶
flowchart LR
Q["问题<br/>(spec / issue / test)"] --> M[LLM rollout]
M --> C["代码<br/>(可能多文件)"]
C --> S["沙箱执行<br/>(秒级 + 安全约束)"]
S --> R{"通过测试?"}
R -- pass_rate --> rew["reward signal<br/>(0~1 连续)"]
rew --> Update[GRPO/PPO update]
Update -.-> M
classDef step fill:#fff,stroke:#cc785c;
class Q,M,C,S,R,rew,Update step
跟数学 RL 的核心差异:
| 维度 | 数学 RLVR | 代码 RLVR |
|---|---|---|
| Verifier 延迟 | 毫秒(字符串匹配) | 秒级(编译 + 执行) |
| 安全风险 | 无 | 必须沙箱(不可信代码) |
| 奖励粒度 | binary | 连续(pass_rate) |
| Rollout 长度 | <500 tokens | 1K-100K+ tokens(多文件) |
| 状态空间 | 数学 expression | repo + filesystem + env |
| Domain 多样 | 数学题统一 | algorithm / SWE / web / mobile / embedded ... |
| Query 生成 | 题目无限合成 | 真实问题难合成 |
→ 整套 pipeline 的工程量比数学 RL 大 5-10×。
二、沙箱执行环境(架构)¶
沙箱不只是"跑代码"。要扛任意攻击代码 + 高并发 + 多语言 + 资源 quota + 反复 reset。
flowchart LR
rl["RL trainer<br/>(GPU)"] --> queue["job queue<br/>(Redis/Kafka)"]
queue --> orch["orchestrator<br/>K8s / Nomad"]
orch --> sb1["sandbox 1"]
orch --> sb2["sandbox 2"]
orch --> sb3["..."]
sb1 -.-> result["result<br/>(stdout/stderr/<br/>traceback/timing)"]
sb2 -.-> result
sb3 -.-> result
result --> rl
classDef gpu fill:#fff,stroke:#cc785c;
classDef sb fill:#f5f3eb,stroke:#bdb9ab;
class rl,queue,orch,result gpu
class sb1,sb2,sb3 sb
2.1 隔离层级¶
从弱到强:
| 层级 | 实现 | 启动开销 | 安全 | 用途 |
|---|---|---|---|---|
| 进程级 | Linux namespaces (unshare) + seccomp |
<10 ms | 中(kernel attack 面) | 内部受信代码 |
| 容器 | Docker / Podman | 100-300 ms | 中(容器逃逸 CVE) | RL 默认起点 |
| MicroVM | Firecracker(AWS Lambda 用)/ Kata Containers | 100-200 ms | 强(独立 KVM kernel) | 跑用户提交代码(HumanEval-X 风格) |
| gVisor | Google 的 user-space kernel | 50-100 ms | 强(syscall 过滤) | Google Cloud Run 用 |
| 完整 VM | QEMU / Cloud Hypervisor | 数秒 | 极强 | 极端不可信 |
主流选择:Firecracker(jail micro-VM)是 frontier lab 的事实标准 —— 启动 < 200ms,per-VM <5MB 内存 overhead,能并发千级。OpenAI / Anthropic / Cursor 等的代码 sandbox 都基于 Firecracker 衍生。
2.2 资源 quota & 反爆破¶
sandbox_config = {
"cpu_limit": "1.0", # 1 vCPU
"memory_mb": 512, # 512 MB
"timeout_sec": 30, # wall clock 上限
"disk_mb": 100, # tmpfs,不持久化
"network": "none", # 切网
"stdin_size_mb": 1, # 输入上限
"stdout_size_mb": 10, # 输出上限(防 OOM 攻击)
"fork_limit": 100, # 防 fork bomb
"syscall_filter": "strict", # seccomp BPF 过滤
}
常见攻击 + 防御:
| 攻击 | 防御 |
|---|---|
Fork bomb (while True: fork()) |
rlimit RLIMIT_NPROC + cgroup pids |
:(){ :\|:& };: shell bomb |
禁 shell / cgroup pids |
| 大文件 / 死循环 print | stdout cap + tmpfs quota |
| Network exfil(外发数据) | network: none |
| Side channel(时序 / cache) | 不接受外部 timing 反馈 |
| Hostfile 写穿(Docker volume bind 错) | read-only rootfs + tmpfs only |
| 加密货币挖矿 | CPU quota + 短 timeout |
Python ctypes 调系统库 |
seccomp 拒非白名单 syscall |
2.3 多语言支持¶
每语言一个 base image,预装 toolchain:
# code-runner-python
FROM python:3.12-slim
RUN pip install numpy pandas scipy scikit-learn # 题目常用
COPY entry.sh /
ENTRYPOINT ["/entry.sh"]
# code-runner-cpp
FROM gcc:13
RUN apt install -y cmake libboost-all-dev
COPY entry.sh /
# code-runner-rust
FROM rust:1.85
COPY entry.sh /
主流 RL pipeline 覆盖:Python / C++ / Java / Rust / Go / JavaScript / TypeScript(≥7 种),再多就成本分布太散。SWE-bench 系列 benchmark 因为是真实 GitHub repo,复杂度更高(需要 build system、依赖安装)。
2.4 开源沙箱栈¶
| 工具 | 维护 | 适用 |
|---|---|---|
| microsoft/playwright-mcp | 活跃 | Browser sandbox(web agent) |
| e2b-dev/E2B | 活跃 | LLM-friendly cloud sandbox SDK |
| SWE-Lancer 评测沙箱 | OpenAI 2025 | Real-world freelance task |
| DocketCode-Sandbox | 开源 | RL training 用 |
| code-r1 sandbox | 开源 R1 复刻 | 数学 + 代码 |
| 自建 Firecracker stack | 内部 | 最常见 |
三、数据来源 / Query 生成¶
代码 RLVR 的 query("问题")从哪来?这是除了沙箱之外最大的工程问题。数学题可以用模板无限合成,代码题难。
3.1 来源分类¶
flowchart LR
Q[Query 来源]
Q --> P1["1. 编程题库<br/>(LeetCode / Codeforces)"]
Q --> P2["2. 真实 GitHub<br/>(SWE-bench / SWE-Smith)"]
Q --> P3["3. 合成 spec→test<br/>(LLM 自生成)"]
Q --> P4["4. Mutation<br/>(已有题改造)"]
Q --> P5["5. Human-in-loop<br/>(标注员写)"]
classDef step fill:#fff,stroke:#cc785c;
class Q,P1,P2,P3,P4,P5 step
3.1.1 编程题库(公开)¶
| 来源 | 体量 | 特点 | 限制 |
|---|---|---|---|
| LeetCode | ~3K 题 | 算法 / 数据结构 | 测试集私有,需自生成 |
| Codeforces | ~9K 题 | 竞赛级 | 公开测试,但需爬 |
| APPS (Hendrycks 2021) | 10K 题 | introductory→competition 分级 | 测试齐全 |
| CodeContests (AlphaCode) | ~13K 题 | 竞赛 | DM 提供完整测试 |
| HumanEval / HumanEval+ | 164 / 700+ | 函数级 | 标准 benchmark |
| MBPP / MBPP+ | 974 / 400 | 简单算法 | 标准 benchmark |
| TACO (2023) | 26K | 多种难度 | RL 训练常用 |
问题:公开题库容易污染 benchmark(被 base model 训过),且多样性有限(all 算法)。
3.1.2 真实 GitHub Issues(SWE 风格)¶
代码 RL 的"成人版":从真实开源 repo 抓 issue + PR + tests,让模型修真实 bug。
| Benchmark | 体量 | 特点 |
|---|---|---|
| SWE-bench (Jimenez 2024) | 2294 issue(来自 12 个 Python repo) | 配 fail-to-pass 测试,真实开源 |
| SWE-bench Verified | 500(人工筛过) | OpenAI 2024 筛选,更可靠 |
| SWE-bench Multilingual | ~200 | 多语言扩展 |
| SWE-bench Live (2024) | 持续更新 | 防训练数据污染 |
| SWE-Smith (2025) | 52K+ training instance | 关键:合成式产生 SWE 训练数据 |
SWE-Smith 思路(重点):
flowchart LR
R[GitHub repo] --> M["mutator<br/>(模拟 bug)"]
M -- "syntactic / semantic mutation" --> B["broken commit"]
B --> T["existing tests<br/>(检测 mutation)"]
T -- fail --> Inst["instance:<br/>broken repo + failing tests"]
Inst --> RL["RL training pair"]
classDef step fill:#fff,stroke:#cc785c;
class R,M,B,T,Inst,RL step
- 从 healthy commit + tests 反向构造 broken(mutation testing 思路)
- 比手抓 GitHub issue 数据规模 × 数十倍
- 模型学到的能力可以 transfer 到真实 issue(SWE-bench Verified 上验证)
3.1.3 合成 spec → test(LLM 自合成)¶
# 让强 LLM 自己出题
def synthesize_problem(seed_topic, difficulty):
spec = llm.generate(f"""
Generate a {difficulty} programming problem about {seed_topic}:
- problem statement
- function signature
- 5+ test cases (input → expected output)
- 1 reference solution (for fuzzing more tests)
""")
spec = parse(spec)
# 用 reference solution 跑生成更多 fuzzing 测试
fuzz_tests = fuzz_with_reference(spec.solution, n=50)
spec.tests.extend(fuzz_tests)
return spec
质量关键:
- 多样性 seed:不能只让 LLM 自己脑补。从 GitHub README 主题分布、技术博客标签、StackOverflow 标签随机 seed
- 难度梯度:明确指定 easy/medium/hard,让 LLM 偏向梯度
- 去 distribution shift:合成题目要与真实分布对齐(不能全是抽象算法题)
代表:WizardCoder(Luo 2023, arXiv:2306.08568)的 Evol-Instruct 在代码上的应用。
3.1.4 Mutation(变种已有题)¶
def mutate_problem(original):
return llm.generate(f"""
Original: {original}
Generate a new problem that:
- Tests similar concepts
- Has different surface (different scenario / variable names / edge cases)
- Includes 5+ new test cases
""")
便宜,但变化空间有限(容易学出"识别 mutation 模板"而非真本事)。
3.1.5 Human-in-loop(标注)¶
最贵但最高质量。用 senior dev 写问题 + 测试。Anthropic / Scale AI 的 frontier 标注 pipeline 主要靠这个补合成不足处。
3.2 综合配比(实操)¶
| 来源 | 占比 | 角色 |
|---|---|---|
| 编程题库(APPS / TACO) | 20-30% | base 算法能力 |
| 真实 SWE(SWE-bench / Smith) | 30-50% | engineering 能力 |
| 合成 / Mutation | 20-30% | 多样性补充 |
| Human-curated | 5-10% | 高质量校准 |
四、Domain 覆盖¶
代码不只是"算法题"。frontier 代码模型要 cover 的 domain:
| Domain | 难点 | benchmark 代表 |
|---|---|---|
| 算法竞赛 | DP / 图论 / 数论,封闭 | LeetCode / CodeContests |
| 基础编码 | 函数级实现 | HumanEval / MBPP |
| SWE engineering | 多文件 / debug / refactor | SWE-bench |
| Web 全栈 | 前后端 / DOM / 浏览器 | WebArena / VisualWebArena |
| Data science | pandas / 可视化 | DS-1000 / DA-Code |
| 科学计算 | numpy / scipy / 仿真 | SciCode |
| 嵌入式 / C/C++ | 内存 / 系统调用 | EvalPlus C/C++ subset |
| DSL | SQL / Regex / shell | BIRD-SQL / regex-eval |
| 形式验证 | Coq / Lean / Dafny | miniF2F-coq |
| Agent / 工具调用 | 跨 turn 状态 | SWE-agent / Devin-bench |
业界共识:单 benchmark 优化已经过时。Frontier 评估 = 一组 benchmark 矩阵 + agent eval。
五、Benchmarks 矩阵¶
按"评估能力维度"分类(避免单 benchmark 过拟合):
5.1 函数级 / 短代码¶
| Benchmark | 体量 | 注意 |
|---|---|---|
| HumanEval | 164 | 基线,但已被严重训练污染 |
| HumanEval+ / MBPP+ (EvalPlus) | 700+ / 400+ | 加了大量 hidden tests,反 overfit |
| LiveCodeBench (2024) | 持续更新 | 按时间窗口切,新题反污染 |
| CRUXEval (2024) | 800 | input prediction + output prediction |
| CodeContests | 13K+ | 竞赛级,AlphaCode 用 |
5.2 SWE / 多文件¶
| Benchmark | 题量 | 特点 |
|---|---|---|
| SWE-bench | 2294 | 12 Python repo,issue → patch |
| SWE-bench Verified | 500 | 人工筛过的子集 |
| SWE-bench Lite | 300 | 简化版,门槛低 |
| SWE-bench Multimodal | ~200 | 包含截图 / UI |
| Multi-SWE-bench (2025) | 多语言 | Python 之外也评测 |
| HumanEvalPack | 1024 | bug-fix / explain / synthesize 三任务 |
5.3 Agent / 长程¶
| Benchmark | 任务 |
|---|---|
| SWE-agent (Yang 2024) | 完整 agent 修 issue |
| Devin-bench | (内部) Cognition AI 用 |
| WebArena (2023) | 浏览器内 task |
| TAU-bench (Anthropic 2024) | 多轮 tool-use |
| OSWorld (2024) | 桌面 OS 控制 |
5.4 数据科学 / 数学交叉¶
| Benchmark | 任务 |
|---|---|
| DS-1000 (2022) | pandas / numpy / sklearn 函数级 |
| DA-Code (2024) | 完整数据分析任务 |
| SciCode (2024) | 科学计算(物理 / 生物 / 化学) |
5.5 评估自由度的"陷阱"¶
- Pass@1 vs Pass@k:训练时 sample 多次,evaluation 一次拿到的还是不一样。GPT-4 时代盛行 Pass@k=1,2024+ 转向 Pass@1 一次性
- 测试可见性:训练时如果模型见过 test cases(数据污染),benchmark 失效。LiveCodeBench 类按时间切是关键
- hidden tests:EvalPlus 加 3-100× hidden tests 揭露很多模型实际并不行
六、Curriculum:从易到难¶
代码 RLVR 训练单一 mixture 容易陷入"好题学不会,难题没信号"。frontier 实践分阶段:
flowchart LR
P1["Phase 1<br/>函数级 + 算法<br/>(HumanEval/APPS easy)"] --> P2["Phase 2<br/>多文件 / debug<br/>(SWE-Smith subset)"]
P2 --> P3["Phase 3<br/>真实 SWE<br/>(SWE-bench Verified)"]
P3 --> P4["Phase 4<br/>Agent / long-horizon<br/>(SWE-agent / Devin-bench)"]
classDef step fill:#fff,stroke:#cc785c;
class P1,P2,P3,P4 step
实操要点:
- Phase 1:题目本身解法 1-30 行,模型 pass_rate 30-70%。不能太简单(梯度信号弱)也不能太难(reward 全 0)
- Phase 2:开始加 SWE-style,但限定单文件 / 短 patch。关键:让模型从"写新代码"过渡到"读懂别人代码 + 改"
- Phase 3:完整 SWE-bench 类,需要 navigate codebase + 测试 + commit message
- Phase 4:跨多 turn,agent loop(read file → think → edit → test → repeat),引入 tool-use
难度估计:
def difficulty_score(problem):
# 综合多个信号
return weighted_sum(
ref_solution_loc=len(reference_code), # 解长度
n_files_touched=patch_files_count, # 涉及文件
avg_pass_rate_n_models=ablation_pass_rate, # N 个 base 模型平均通过率(低 = 难)
cyclomatic_complexity=ccm, # 圈复杂度
reasoning_depth=cot_length, # 思考链长度
)
每个 batch 按当前模型能力自适应采样:让 batch 平均 pass_rate ≈ 30-50%(信号最强区间)。
七、奖励设计深入¶
def code_reward(rollout, sandbox_result):
# 多维加权
pass_rate = sandbox_result.passed / sandbox_result.total
# 1. 主奖励:通过率
r_pass = pass_rate
# 2. 长度惩罚(防过度啰嗦)
r_len = -0.001 * len(rollout.code)
# 3. 测试覆盖奖励(如果模型写了测试)
r_cov = 0.1 * code_coverage(rollout.code, rollout.tests) if rollout.tests else 0
# 4. 编译惩罚(写不出能跑的代码扣分)
r_comp = -0.5 if sandbox_result.compile_error else 0
# 5. SWE 场景:patch 简洁性
r_minimal = -0.05 * patch_lines_changed if rollout.is_swe else 0
return r_pass + r_len + r_cov + r_comp + r_minimal
frontier 实操更复杂:partial reward + format reward + thinking reward 等多 component。详见 E2 RLHF/DPO 的 reward shaping 通用部分。
八、前沿方向(2024-2026)¶
| 方向 | 代表 | 备注 |
|---|---|---|
| Execution feedback RL | CodeRL | 错误信息进 prompt 的 RL |
| Self-Repair / Self-Debug | Self-Debugging | 多轮:写 → 跑 → 修 |
| Multi-file / Repo-level | SWE-bench / SWE-Smith | 真实工程能力 |
| Test-Generation RL | TestGen-LLM (Meta 2024) | 同时学写代码 + 写测试 |
| Formal Verification RL | Lean / Coq tactics | RL 找 proof |
| Agent RL | SWE-agent / Devin / OpenHands | long-horizon tool use |
| Code World Model | "What does this code do?" → state prediction | 训内部 simulator |
九、追问延伸¶
| 问题 | 方向 |
|---|---|
| Sandbox 怎么对抗"代码偷数据"? | 切网(network: none)+ 资源 quota;frontier 会 audit log |
| Pass@1 vs Pass@10 怎么取舍? | RL 训练用 pass_rate(连续);evaluation 同时报 Pass@1 (deploy 真实) + Pass@10 (capacity 上限) |
| 为什么不全用 SWE-bench 训? | 题量 2K+ 不够;多样性不足;优先 SWE-Smith 类合成 |
| 训了代码 RL 模型在数学上会退化吗? | 取决于 mixture。代码 + 数学 mixed RL 一般不退化,单代码 RL 会跟数学正交 |
| Agent vs 函数级训练,哪个先? | 函数级先。Agent 容易 reward hack,需要 base 能力扎实 |
| 怎么防训练数据污染? | 1) 持续刷新 benchmark(LiveCodeBench)2) 训练时严格 dedup against benchmark 3) 用 hidden tests 检测 verbatim |
参考文献¶
- Hendrycks et al. HumanEval / Codex. 2021. arXiv:2107.03374
- Hendrycks et al. APPS. 2021. arXiv:2105.09938
- Li et al. AlphaCode (CodeContests). Science 2022. arXiv:2203.07814
- Le et al. CodeRL. NeurIPS 2022. arXiv:2207.01780
- Chen et al. Self-Debugging. 2023. arXiv:2304.05128
- Luo et al. WizardCoder (Evol-Instruct). 2023. arXiv:2306.08568
- Jimenez et al. SWE-bench. ICLR 2024. arXiv:2310.06770
- Yang et al. SWE-agent. 2024. arXiv:2405.15793
- Jain et al. LiveCodeBench. 2024. arXiv:2403.07974
- SWE-Smith. 2025. arXiv:2504.21798
- Liu et al. EvalPlus (HumanEval+). 2023. GitHub
- Multi-SWE-bench. 2025. arXiv:2502.02767
↑ 上级 · E. 后训练与对齐