跳转至

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

参考文献


上级 · E. 后训练与对齐