
聊 Coding Agent 强化学习RL的内容这两年在技术社区讨论度很高。大家从最初关心怎么让大模型补全几行代码慢慢转向一个更实际的问题怎么让 Agent 像一名工程师一样接到 Issue 后能自己改代码、跑测试、修失败用例最后提交一个可用补丁。我整理资料时发现网上大量文章一上来就是 PPO / GRPO 公式容易把新手劝退。其实对绝大多数想落地的人来说第一个门槛往往不是算法而是数据——任务从哪来轨迹怎么采集奖励怎么设计这三个问题不解决再好的训练框架也跑不出稳定效果。这篇文章就围绕 Coding Agent RL 训练中的三个关键环节展开数据来源、轨迹采集、奖励函数。我会从概念讲起给出可直接套用的数据格式、代码示例和设计建议并聊聊工程上常见的坑。适合正在做 Agent 产品、准备给模型接入 RL 的开发者也适合想系统了解 Coding Agent 训练流程的同学。1. Coding Agent 为什么要和 RL 绑定1.1 什么是 Coding Agent先说结论Coding Agent 是一个以大语言模型LLM为“大脑”能够自主完成编码任务的智能体系统。它和普通的代码补全工具不同。普通补全模型只根据上下文续写代码而 Coding Agent 要做完整的任务闭环理解自然语言任务描述比如一个 GitHub Issue。读取仓库结构定位相关文件。通过工具调用执行命令、运行测试、搜索代码。修改多个文件生成补丁。根据测试结果修复问题直到任务完成。所以 Coding Agent 本质上是一个“会使用工具的决策系统”。它每一步都要判断接下来是继续读代码还是直接改文件还是跑一下测试这种多步决策能力单靠预训练和 SFT 很难学出来。1.2 为什么 SFT 不够用有同学可能会问直接用人工标注的“下一步动作”做监督微调SFT不是也可以吗可以用但有两个问题。第一标注成本极高。Coding Agent 的一次任务可能有几十步交互每一步都让人类标注“该做什么”成本远高于写一段普通文本。即使使用 GPT-4 等模型辅助标注也仍然存在误差。第二SFT 只学习“模仿”不学习“结果”。一个动作在过程里看起来合理不代表最终能让测试通过。SFT 缺少对结果的反馈模型很难自己判断哪些步骤真正有用哪些只是看起来像。强化学习正好补上了这个环节。RL 的思路是让 Agent 在环境里执行任务根据最终结果比如测试是否通过给出奖励信号再用策略优化算法更新模型。因为环境里跑的是真实代码、真实测试模型能逐步学会“哪个动作组合能拿到更高分数”。1.3 强化学习解决的核心问题从工程视角看Coding Agent RL 解决的是三个问题长程决策一次任务可能有几十步工具调用模型需要学会在长序列里做合理规划。结果反馈让奖励信号来自代码执行结果而不是人类猜测。策略改进通过反复采样和优化让模型在同类任务上越做越好。理解了这些下面就可以进入正题了。Coding Agent RL 的训练闭环里有三个环节是绕不开的数据来源、轨迹采集、奖励函数。2. Coding Agent RL 的整体闭环在拆开讲三个环节之前先建立整体认知。Coding Agent RL 的训练流程可以简化成下面几步。任务采集从公开基准、真实仓库、合成数据中得到一批“任务描述”。轨迹生成让 Agent 在沙箱环境里执行任务记录每一步的思考、工具调用、工具结果和代码修改。结果评估对 Agent 生成的补丁运行测试用例、静态检查等得到量化分数。策略优化把轨迹和奖励打包成训练样本用 GRPO / PPO 等算法更新模型参数。回归验证在保留的评测集上跑一轮评估确认模型能力没有变差。用一句话概括数据来源决定模型“看过哪些任务”轨迹采集决定模型“学到哪些过程”奖励函数决定模型“往哪个方向优化”。这个闭环里三个环节高度耦合。数据质量差后面轨迹和奖励都受影响轨迹记录不完整奖励无法回溯到具体步骤奖励设计不合理模型可能学到“刷分技巧”而不是真实编码能力。还要说明一点目前大多数实践采用“离线 RL 在线采样”的混合模式。先用一批固定轨迹做策略预热再不断从环境中采样新轨迹加入训练池。这也是开源社区和云平台 Agent 训练工具里比较常见的做法。3. 数据来源RL 训练的前置燃料3.1 公开代码数据集与评测基准对于刚起步的团队公开数据集是成本最低的起点。下面几个是比较常见的SWE-bench / SWE-bench Verified从真实 GitHub 仓库中提取 Issue 和对应修复补丁任务复杂度高是目前 Coding Agent 评测的事实标准之一。HumanEval / MBPP以“函数级编程题”为主单任务规模小适合验证训练流程是否跑通但不适合直接代表真实工程能力。LiveCodeBench持续更新的编程题目集特点是不定期加入新题能在一定程度上减少模型“背题”造成的数据污染问题。使用公开数据集时要注意一个关键风险预训练阶段可能已经见过这些题目。如果训练集和评测集都来自同一批公开数据模型很容易产生“伪提升”。常见做法是把公开数据按题号、仓库、时间做切分保证训练集和评测集不重叠。3.2 真实仓库与 Issue 数据公开集毕竟是别人整理的和你业务里的代码风格、依赖环境往往差异很大。更贴近生产的做法是采集真实仓库数据。采集来源包括内部 GitLab / GitHub 仓库中已经关闭的 Issue。一个 Issue 对应的修复 Pull RequestPR。CI 日志中暴露的失败用例。代码评审记录里的人工修改意见。把这些数据整理成“任务三元组”通常包含三部分任务描述Issue 标题和正文。初始代码状态PR 合并前的仓库快照。期望结果PR 合并后的代码或修复后通过的测试用例。整理过程中最难的是把 Issue 和对应 PR 准确配对。有的仓库维护不规范一个 PR 可能解决多个 Issue或者一个 Issue 被多个 PR 分步解决。这个环节需要投入较多人工核对时间。3.3 合成数据与自我生成当真实数据不够时可以借助更强的模型来生成合成数据。常见思路有两种。第一种是“题目合成”。让一个强模型根据业务代码库中的函数和模块生成新的编程任务和测试用例。比如读取某个 utils 模块生成“请实现一个参数校验函数”的任务并配套单元测试。第二种是“轨迹增强”。把已有任务交给不同模型、不同温度参数多次执行产生多条不同轨迹。成功的轨迹可以直接作为正样本失败的轨迹也可以保留作为 RL 训练中的“负例”。合成数据的风险在于噪声和奖励欺骗。模型生成的任务可能本身存在逻辑错误或者测试用例写得过于宽松导致 Agent 用“偷懒实现”就能骗过测试。所以合成数据一定要经过人工抽查和自动化校验。3.4 业务侧与垂类数据如果你的业务场景不只是通用后端开发比如想做舆情文本分析 Agent、工业缺陷检测 Agent那么数据来源会更分散。以舆情和工业视觉场景为例常见来源有三类公开行业数据集比如焊缝缺陷公开数据集、文本分类公开语料可以转换成“读取数据、输出分析报告”的 Agent 任务。业务日志与历史工单把过去人工处理的工单、标注结果结构化生成任务描述和期望动作。人工补充标注对冷启动阶段的高价值场景人工构造少量任务保证基础质量。这些垂类数据通常量不大核心思路不是拼数量而是把“业务知识”转成 Agent 能理解的任务格式。先跑通 1000 条高质量任务往往比直接堆 10 万条低质量数据更有效。4. 轨迹采集RL 训练的燃料4.1 什么是轨迹轨迹Trajectory指的是一个 Agent 从拿到任务到给出最终答案的完整交互记录。在 Coding Agent 场景里轨迹通常包含任务描述。Agent 的每一次思考。Agent 发起的工具调用。工具返回的结果。文件编辑操作。最终答案或补丁内容。Agent 执行过程中产生的 token 数、耗时等元信息。为什么轨迹这么重要因为 RL 训练需要知道“在某个状态下模型选择了什么动作”。没有完整的轨迹你只能看到输入和输出无法判断模型是在哪一步开始走偏的。4.2 轨迹数据格式示例目前社区里没有完全统一的标准但一种比较通用的格式是 JSONL每行一个任务。下面是一个简化的轨迹示例。{ task_id: task_0001, task: 修复 src/parser.py 中 parse_config 函数在空字符串输入时抛异常的问题, repo: internal/config-parser, trajectory: [ { step: 0, type: thought, content: 先读取 parser.py 查看当前实现。 }, { step: 1, type: tool_call, tool_name: read_file, tool_input: {path: src/parser.py} }, { step: 2, type: tool_result, content: def parse_config(text):\\n return json.loads(text) }, { step: 3, type: thought, content: json.loads 对空字符串会抛错需要增加空值处理。 }, { step: 4, type: edit, file: src/parser.py, content: def parse_config(text):\\n if not text or not text.strip():\\n return {}\\n return json.loads(text) }, { step: 5, type: tool_call, tool_name: run_tests, tool_input: {target: tests/test_parser.py} }, { step: 6, type: tool_result, content: 1 passed, 1 failed, exit_code: 1 }, { step: 7, type: final_answer, content: 已完成空字符串处理修复了一个边界问题。 } ], reward: 0.5, metadata: { tool_call_count: 4, token_usage: 3280, duration_ms: 15600, test_summary: {\passed\: 1, \failed\: 1} } }这个格式的关键点在于每一步都必须包含“类型标记”。这样后续处理时可以按thought、tool_call、tool_result、edit等类型分别统计也可以方便地判断 Agent 是否在关键节点上做出正确决策。4.3 轨迹采集的常见方式实际工程中轨迹采集有三种主流方式。第一种是模型采样。用当前策略模型或一个较强的基座模型在沙箱环境里执行任务完整记录交互过程。这是 RL 训练中最核心的数据来源因为策略优化的目标就是让模型自己产生更好的轨迹。第二种是日志回流。如果 Agent 已经在生产环境运行可以从中抽取真实任务轨迹。比如用户给 Agent 下发了一个修改代码的请求Agent 执行过程中产生的日志就是天然轨迹。这种方式最贴近真实场景但需要处理敏感信息和隐私数据。第三种是人工标注。当自动生成的轨迹质量不高时可以让工程师手动修正轨迹。常见做法是让工程师检查 Agent 的某一步决策如果错误就重新标注期望动作。人工数据量少但质量高适合作为种子数据。4.4 轨迹过滤与清洗采集到原始轨迹后不建议直接送进训练。先做一轮清洗常见规则如下。删除奖励与结果严重不符的轨迹。比如测试全部失败却标记为高分。删除包含敏感信息的轨迹比如密钥、Token、内部 URL。删除工具调用链明显断裂的轨迹。对过长轨迹做截断或分段处理避免训练时超出上下文长度。保留一定比例的失败轨迹帮助模型学习“如何从失败中恢复”。一个好的做法是同时保存成功和失败轨迹在训练样本里按比例混合。如果只喂成功轨迹模型面对失败时会不知道如何调整。5. 奖励函数策略优化的方向盘5.1 为什么不能只看最终测试结果最简单直观的奖励设计是代码改完后测试全部通过给 1 分否则给 0 分。这种“结果奖励”Outcome Reward ModelORM思路没有错但真实工程里会遇到两个问题。第一奖励太稀疏。一个任务要经过十几步才可能让测试通过中间大部分步骤拿不到任何有效梯度模型很难知道“哪一步做对了”。第二测试覆盖不全。业务代码的测试用例很难覆盖所有边界情况Agent 可能写出“碰巧让测试通过但逻辑错误”的代码。这就是典型的奖励欺骗Reward Hacking。所以现代 Coding Agent RL 训练里越来越倾向于引入“过程奖励”Process Reward ModelPRM和“多信号奖励组合”。5.2 结果奖励与过程奖励结果奖励只看最终输出实现简单适合快速验证训练链路。过程奖励则对轨迹中的每个关键步骤打分。举个例子假设一个任务需要 5 个步骤完成读取相关文件。定位问题函数。修改代码。运行测试。修复失败用例。过程奖励会在每个步骤结束后判断当前动作是否正确是否接近最终目标并把每一步的分数累加起来作为整条轨迹的奖励。过程奖励的实现主要有两种基于规则的判断比如“是否读取了目标文件”“是否运行了测试”这些显式行为。训练一个奖励模型让模型对每一步的动作质量打分。过程奖励的缺点是标注成本高。一个 20 步的轨迹需要给每个步骤都做质量判断工作量远大于只判断最终结果。5.3 基于执行的规则奖励对 Coding Agent 来说最可靠、最不容易被“忽悠”的奖励信号来自真实代码执行结果。常见的规则奖励信号如下编译是否通过。单元测试通过比例。静态检查lint是否通过。关键函数输出是否与预期匹配。是否修改了目标文件。其中单元测试通过比例是核心指标。不要只看“全过或全挂”而是按比例计算。比如总共 10 个测试通过 7 个那测试得分就是 0.7。这样可以给模型更细粒度的反馈让它在部分通过时也有梯度信号。下面是一个简化的规则奖励计算示例。def compute_execution_reward(test_summary: dict, used_tokens: int, max_tokens: int 8000) - float: passed test_summary.get(passed, 0) failed test_summary.get(failed, 0) error test_summary.get(error, 0) total passed failed error if total 0: test_score 0.0 else: test_score passed / total # 静态检查加分项 lint_score 1.0 if test_summary.get(lint_passed, False) else 0.0 # token 效率惩罚超过阈值后开始扣分 token_penalty 0.0 if used_tokens max_tokens: token_penalty 0.1 * (used_tokens - max_tokens) / max_tokens reward 0.6 * test_score 0.2 * lint_score - min(token_penalty, 0.2) return round(reward, 4)这段代码把奖励拆成三部分测试通过率权重 0.6静态检查权重 0.2token 超限惩罚最大 0.2。实际项目中权重需要根据任务特点调整但总体思路是“以执行结果为主成本约束为辅”。5.4 LLM-as-a-Judge 奖励当任务没有确定性测试时比如主观性较强的“重构一段代码提高可读性”可以用一个更强的模型来当裁判对 Agent 的输出打分。LLM-as-a-Judge 的优点是灵活能覆盖测试用例覆盖不到的场景。缺点是开销大每个样本都要调用一次大模型。可能存在位置偏见裁判模型容易对长答案更友好。裁判模型本身可能出错需要抽样人工验证。实际工程中建议把规则奖励放在优先级更高的位置Judge 分数只作为辅助信号。纯靠 Judge 打分训练出来的 Agent容易学会“迎合裁判偏好”而不是“真正把代码写好”。5.5 奖励函数的常见设计误区第一个误区是奖励公式过于复杂。同时堆叠十几个加权项参数互相影响训练时很难定位问题。正确做法是从 2 到 3 个核心信号开始稳定后再逐步加项。第二个误区是奖励信号和任务目标不一致。比如你希望 Agent 修好测试但给“工具调用次数少”设了较高权重模型就可能只调一两次工具不深入排查就返回结果。第三个误区是忽视惩罚项。RL 训练中惩罚比奖励更敏感。如果惩罚太强模型会变得过度保守减少探索如果惩罚太弱模型可能疯狂调用工具直到超时。6. 一个简化的 RL 训练流程示例下面用一个简化流程把数据、轨迹、奖励三个环节串起来。这个示例不依赖特定大模型 API核心是演示思路可以按实际项目替换成你自己的模型调用方式。6.1 准备任务数据首先准备一批任务放在tasks.jsonl里。每行一个任务格式如下。{task_id: t001, task: 修复 add 函数在输入 None 时崩溃的问题, repo: demo, test_target: tests/test_math.py} {task_id: t002, task: 实现 subtract 函数并保证负数场景正确, repo: demo, test_target: tests/test_math.py}用一个脚本读取任务并预留 Agent 执行函数。# data_prep.py import json def load_tasks(pathtasks.jsonl): tasks [] with open(path, r, encodingutf-8) as f: for line in f: line line.strip() if line: tasks.append(json.loads(line)) return tasks if __name__ __main__: tasks load_tasks() print(fload {len(tasks)} tasks)6.2 轨迹采集框架下面是一个示意性的轨迹采集脚本。run_agent_once是你要对接真实 Agent 的位置这里用占位函数代替。# rollout.py import json import time def run_agent_once(task: dict): 这里应替换成真实的 Coding Agent 调用逻辑。 返回轨迹列表和结果摘要。 trajectory [ {step: 0, type: thought, content: 读取任务并开始排查}, {step: 1, type: tool_call, tool_name: read_file, tool_input: {path: src/math.py}}, {step: 2, type: tool_result, content: def add(a, b): return a b}, {step: 3, type: edit, file: src/math.py, content: def add(a, b):\\n if a is None or b is None:\\n return 0\\n return a b} ] test_summary {passed: 3, failed: 0, error: 0} return trajectory, test_summary def rollout_one_task(task: dict): start time.time() trajectory, test_summary run_agent_once(task) used_tokens 5000 reward compute_execution_reward(test_summary, used_tokens) record { task_id: task[task_id], task: task[task], trajectory: trajectory, reward: reward, metadata: { tool_call_count: len([t for t in trajectory if t[type] tool_call]), token_usage: used_tokens, duration_ms: int((time.time() - start) * 1000), test_summary: test_summary, }, } return record def collect_rollouts(tasks, output_pathrollouts.jsonl): with open(output_path, w, encodingutf-8) as f: for task in tasks: record rollout_one_task(task) f.write(json.dumps(record, ensure_asciiFalse) \n)这里需要注意run_agent_once目前返回的是写死的轨迹。真实项目中这个函数应该调用你的 Agent 框架并捕获每一步的中间状态。建议在 Agent 框架里增加“事件回调”机制每当 Agent 执行一次工具调用就自动记录到轨迹列表。6.3 奖励函数奖励函数部分就是前面写的compute_execution_reward。把它单独放在reward.py里方便后续复用。# reward.py def compute_execution_reward(test_summary: dict, used_tokens: int, max_tokens: int 8000) - float: passed test_summary.get(passed, 0) failed test_summary.get(failed, 0) error test_summary.get(error, 0) total passed failed error if total 0: test_score 0.0 else: test_score passed / total lint_score 1.0 if test_summary.get(lint_passed, False) else 0.0 token_penalty 0.0 if used_tokens max_tokens: token_penalty 0.1 * (used_tokens - max_tokens) / max_tokens reward 0.6 * test_score 0.2 * lint_score - min(token_penalty, 0.2) return round(reward, 4)6.4 GRPO 训练循环示意GRPOGroup Relative Policy Optimization是目前 Coding Agent RL 训练中比较常用的一类算法。它的核心思想是对同一个任务采样多个输出在组内比较奖励得到每个输出的优势值Advantage然后用优势值更新策略。下面给一个高度简化的训练循环示意用于理解整体逻辑。生产环境建议使用开源框架或参考 GRPO 论文实现完整版本。# simplified_grpo.py import torch import torch.nn.functional as F def grpo_simplified_loss( output_logprobs: torch.Tensor, ref_logprobs: torch.Tensor, rewards: torch.Tensor, beta: float 0.04, ) - torch.Tensor: output_logprobs: shape [G] 每个采样序列的总对数概率 ref_logprobs: shape [G] 参考模型通常是 SFT 模型的总对数概率 rewards: shape [G] 每条轨迹的奖励 mean rewards.mean() std rewards.std() 1e-6 advantages (rewards - mean) / std # 简单 KL 惩罚限制策略不要偏离参考模型太远 kl ref_logprobs - output_logprobs # 策略梯度损失 KL 惩罚 loss (-advantages * output_logprobs beta * kl).mean() return loss if __name__ __main__: G 4 # 每个任务采样 4 条轨迹 rewards torch.tensor([1.0, 0.5, 0.0, 0.8]) output_logprobs torch.tensor([-5.2, -6.1, -7.8, -5.8]) ref_logprobs torch.tensor([-5.0, -5.9, -6.0, -5.5]) loss grpo_simplified_loss(output_logprobs, ref_logprobs, rewards) print(loss:, loss.item())由于 GRPO 是在“同一任务”的一组样本内做归一化实际使用中你需要维护一个 batch多个 prompt每个 prompt 对应 G 条采样结果。上面的代码为了方便展示把G当作一个任务组的大小。完整实现还需要考虑 mask、padding、token 级损失累加等细节。6.5 运行与验证先运行数据准备脚本再采集轨迹最后计算损失。python data_prep.py python rollout.py python simplified_grpo.py输出大致如下。load 100 tasks collect 100 rollouts, positive rate: 0.62 loss: 0.0342如果你的环境能正常打印这三行日志说明基础链路已经跑通。接下来就可以替换真实 Agent、真实奖励信号、真实训练循环逐步完善自己的 RL 训练系统。7. 常见问题与排查思路Coding Agent RL 训练中问题往往来自数据、轨迹、奖励三个环节的配合。下面整理了几个高频问题。问题现象常见原因解决思路训练后模型越来越“懒”很少调用工具工具调用次数被设成惩罚项且权重过高降低惩罚权重或改为超阈值后惩罚测试通过率提升但代码质量下降奖励只关注测试结果忽略代码可读性增加静态检查、代码风格、Judge 分数等辅助信号训练集评测提升公开基准不升反降训练集与评测集同源存在数据污染按仓库、时间、题目 ID 隔离严格去重轨迹过长训练显存不足单条轨迹包含太多步骤对轨迹分段或截断控制最大步骤数奖励波动大训练不稳定组内归一化样本太少或奖励信号噪声大增大每组采样数对奖励做平滑或裁剪Agent 在测试用例上作弊测试文件本身被 Agent 修改运行测试时使用只读挂载禁止修改测试文件模型总是输出超长内容Judge 奖励对长答案有偏好评估 Judge 是否存在长度偏见加入长度惩罚排查顺序建议先看数据再看奖励。很多“训练不收敛”的问题最后都指向同一个原因轨迹和奖励对不上。比如轨迹里明明没有运行测试奖励却是满分模型会学到完全错误的行为。8. 最佳实践与工程建议8.1 数据质量优先于数据数量Coding Agent RL 对数据质量的要求比普通文本训练更高。宁可一条任务经过人工确认也不要几十条自动生成的脏数据。起步阶段建议把任务数量控制在 1000 到 5000 条先验证训练流程是否合理。每条任务至少要有三个属性清晰的任务描述、可验证的测试目标、明确的成功标准。缺一个都不建议进入训练。8.2 奖励设计从简单开始奖励函数应该从“最不容易错”的信号开始。第一版可以只做两件事测试通过率、禁止修改测试文件。跑通训练后再逐步加入静态检查、代码覆盖率、token 效率等信号。不要一开始就设计一个 10 项权重的奖励公式。参数越多Reward Hacking 的空间越大也越难排查。8.3 严格隔离训练集与评测集数据污染是 Coding Agent RL 中最隐蔽的问题。公开数据集本身可能被模型在预训练阶段见过如果训练集和评测集切分不严你看到的“提升”只是记忆不是能力。建议在数据准备阶段就建立去重机制。按仓库、文件路径、函数名、测试用例内容综合做相似度过滤并且把评测集单独存放在一个模型无法接触的环境中。8.4 轨迹记录要完整轨迹采集阶段宁可多存字段也不要事后后悔。除了思考内容和工具调用还要记录工具返回的完整内容。Agent 对环境错误信息的反应。每次编辑前后的文件 diff。执行时间与 token 消耗。这些字段在分析训练失败、构造过程奖励时非常有用。8.5 训练与回归验证闭环RL 训练很容易出现过拟合或能力遗忘。每次训练后至少要跑三类评估训练集内同类任务确认能力有提升。公开基准集确认没有出现大的能力回退。业务测试集确认真实场景可用。如果出现“训练集提升、评测集下降”优先检查数据污染如果出现“全部指标下降”优先检查奖励是否和任务目标一致。8.6 控制成本与小步快跑Coding Agent RL 的算力成本不低。一条轨迹可能涉及十几次模型推理和代码执行批量跑下来开销可观。建议从 100 个任务、小组采样开始先验证策略优化代码没有 bug再逐步扩大数据规模。如果算力有限可以先用 API 模型采样离线轨迹再在开源模型上做策略优化。这种模式虽然在线性差一些但能快速验证数据格式和奖励设计。结尾这篇文章从 Coding Agent RL 训练中最容易被忽略的三个环节说起数据来源、轨迹采集、奖励函数。数据来源决定了训练的上限轨迹采集决定了模型能否学到有效过程奖励函数决定了优化的方向。三者合在一起才是 Coding Agent RL 的完整骨架。如果你正准备开始做 Agent RL 训练建议不要急着追求大规模算力先拿 100 个任务跑通全链路从规则奖励做起逐步积累自己的轨迹数据集。等数据管道稳定后再去深入 GRPO、PPO 的算法细节你会发现这些算法其实只是帮你把“好数据”变成“好模型”的工具。下一步可以继续学习 GRPO 的完整实现、偏好优化DPO 等在 Coding Agent 中的应用以及如何在真实仓库沙箱中做更安全的在线采样。每一步都会比单纯读论文更有价值。