ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Coding Agent强化学习的关键:数据、轨迹与奖励设计

Coding Agent强化学习的关键:数据、轨迹与奖励设计 如果你在 2026 年下半年重新审视市面上的 Coding Agent会发现一个很微妙的画面各家产品的功能清单几乎拉齐了都能读代码、改代码、跑测试、修报错甚至都敢宣称自己接入了最新模型。但实际拿到手用差距依然非常明显。有的 Agent 改完代码会主动解释修改动机遇到测试失败能沿着报错链往回排查有的 Agent 则在同一个 lint 错误上反复横跳表现得像一个记性很差的实习生。很多人习惯把这个差距归因于基座模型认为是模型智力不够。这个判断有道理但不完整。更接近真相的看法是Coding Agent 的能力上限很大程度上在模型训练完成后才被真正拉开而拉开差距的战场是强化学习RL阶段的数据与奖励设计。换句话说当你决定用 RL 去训练一个能自主写代码、改代码、跑测试的 Agent 时摆在面前的问题并不是“用什么算法”而是三个更朴素的问题训练数据从哪来轨迹怎么采奖励怎么算这篇文章想用“浅谈”的容量把 Coding Agent RL 里这三个最容易被忽视、也最值得投入的环节讲清楚。读完你会明白为什么公开代码语料不能直接当 RL 数据为什么轨迹采集比 Prompt 工程复杂一个量级为什么奖励函数写得不好会让整个 Agent 训练变成一场灾难。如果你正准备从零搭一套 Coding Agent 的训练数据管线或者只是想搞清楚 RL 在 Agent 产品里到底扮演什么角色这篇文章都值得你花十分钟读完。1. Coding Agent RL 到底在解决什么问题先抛开“强化学习”四个字的抽象感从 Agent 的实际行为说起。一个普通的代码大模型能力形态是“给定一段上下文预测下一段 token”。它能补全函数能写单元测试但它的输出通常不会触发外部环境写完代码就结束了不会编译不会运行不会根据报错信息重新修改。这种模型适合当“补全工具”但不适合当“能独立完成任务的 Agent”。Coding Agent 的工作方式完全不同。以当前主流的 Agent 产品工作流为例很多产品已经默认把任务拆成两个阶段第一个阶段是 plan模型先理解仓库结构、定位问题、给出修改方案第二个阶段是 coding模型按方案编辑文件、执行命令、查看测试结果不断迭代直到任务完成。在这个过程中模型每一步的输出都会影响下一步的输入——文件改了测试跑挂了报错信息出现了于是模型要决定下一步打开哪个文件、改哪一行、再跑哪条命令。这种“观察环境、做出动作、接收反馈、再调整动作”的多步决策能力单靠预训练和 SFT监督微调很难训练出来。SFT 学的是“模仿”给一批人类写好的高质量答案让模型照着学。问题是真实开发中没有谁是“一次写对”的。更多的情形是边看报错边改边跑测试边修中间还要处理失败和异常。你希望模型学会的恰恰是这种“面对反馈做调整”的能力而不是只会输出一个静态答案。RL 在这里的价值就体现出来了。RL 里的模型看的不只是“最终答案长什么样”而是整个决策序列以及每一步动作带来的结果。代码类任务有一个天然优势环境反馈非常明确——单元测试过了就是过了编译错了几条就是几条运行超时就是超时。这种具备强反馈信号的任务几乎是 Agent RL 最合适的试验场。这里还要区分一个概念Agent RL 和传统 Chat 模型的 RLHF 并不是同一件事。Chat 模型的 RLHF 主要对齐人类偏好反馈来自人类标注或者 AI 打分Coding Agent 的 RL 里反馈更多来自执行环境比如测试通过率、编译结果、运行日志。动作空间也不再是“下一个 token”而是“打开文件、编辑代码、执行命令、搜索上下文”等更粗粒度的工具操作。这意味着奖励函数的设计逻辑完全不同轨迹数据的结构也复杂得多。结论先放在这里Coding Agent 的 RL本质上是在解决“模型如何通过多步行动与环境互动学会自己完成任务”的问题。而整个训练过程能不能跑通不取决于你用的是 PPO、GRPO 还是其他算法而是取决于你给这条路铺了什么样的数据、什么样的反馈。2. 数据来源、轨迹采集、奖励函数一根链条上的三件事在展开细节之前有必要先说明这三件事的关系。它们不是三个独立模块而是一条互相制约的链条。第一环是数据来源。你要训练一个会写代码的 Agent总得有让模型学习的问题和答案素材。这些素材可以是公开的代码仓库、编程竞赛题目、真实 issue 和 patch也可以是模型自己生成的合成数据。数据来源决定了 RL 训练的“燃料”类型和覆盖面。第二环是轨迹采集。只有问题还不够RL 需要的是“模型如何一步步解决问题的全过程记录”也就是轨迹。轨迹里记录了模型看到了什么、做了什么、环境返回了什么结果。没有轨迹就没有强化样本相当于你只有考试题和标准答案却没有考生的答题过程自然无法判断考生是在哪一步走偏的。第三环是奖励函数。拿到轨迹之后你得告诉优化算法这条轨迹是好还是坏好多少坏在哪一步。奖励函数就是把“某种行为特征”映射成数值的函数。它决定了模型下一次更有动力做哪些动作、避免哪些动作。这三者的逻辑关系是数据来源决定轨迹的丰富度轨迹采集决定奖励能覆盖哪些环节而奖励函数的设计又会反过来筛选数据——如果你发现现有轨迹里根本没有“有用”的中间反馈往往不是模型不行而是数据采集管线没接好。举个例子。很多团队一开始会直接去 GitHub 上抓一堆热门仓库拿 commit diff 当训练数据。这个思路在 SFT 阶段没有大问题但放进 RL 阶段就会很别扭你拿到的只有最终 diff没有中间过程不知道这个 diff 是在哪次失败、哪次报错之后才改出来的。模型学不到“遇到编译错误之后怎么定位原因”的中间步骤只能学到一个漂亮的最终结果。这就是典型的数据来源和奖励函数脱节。我在梳理近期的 Agent 相关讨论时发现越来越多的团队开始意识到公开数据适合做冷启动但真正让 Agent 能力产生差异化的是自己采集的、包含完整中间反馈的轨迹数据。这也是“数据来源、轨迹采集、奖励函数”这三个词常常被一起提起的原因——它们构成了一条完整的数据链路缺任何一环RL 训练都会变成空中楼阁。3. 数据来源RL 的燃料从哪里来在 Coding Agent 的 RL 训练里数据来源不是一个笼统的“去网上抓数据”的问题而是需要区分不同类型的素材并且知道每一类素材适合在哪个阶段使用。3.1 开源代码仓库与公开问题集开源代码仓库GitHub 、GitLab 等是最容易获取的语料。它们覆盖面广、规模大包含各种语言和框架。但问题也很明显原始代码仓库只是静态代码它不包含“这个 bug 是怎么被修复的”上下文也缺少“为什么要这样改”的决策过程。用于预训练或者通用代码理解没有问题用于 Agent RL 的过程监督则远远不够。公开问题集则更适合做验证和测试。比如业界常用的 HumanEval、SWE-bench 这类基准测试它们提供“问题描述 代码环境 测试用例”的结构化数据非常适合作为RL训练的“任务起点”。不过要注意这些评测集主要是为了衡量模型能力而设计的样本量有限直接拿来当训练数据很容易导致过拟合。更稳妥的用法是把公开问题集作为冷启动的种子数据再通过其他手段扩充。另外一个需要正视的现实是公开数据集中特定领域的覆盖非常不均匀。比如在工业场景里想要一个“能帮忙写焊缝缺陷检测代码的 Agent”你会发现公开的代码样例、缺陷图像标注和可用数据集都很少。这类垂直需求光靠公开语料很难满足最终还是要回到合成数据和自我生成数据的路线上去。3.2 真实开发者工作流数据比静态代码更值钱的一类数据是真实开发者使用编程工具时留下的操作轨迹。比如 IDE 里的编辑记录、版本控制系统的提交历史、issue 跟踪系统里的 bug 描述和对应 patch。这类数据的价值在于“真实”——它包含了人类开发者真实的思考过程先看哪里、改了哪里、跑了什么命令、遇到什么报错、最后怎么处理的。如果能把 IDE 里的编辑过程、命令执行记录、测试输出完整地按时间顺序记录下来得到的轨迹质量会比任何合成数据都好。但这类数据的获取门槛也很高一方面涉及用户隐私和合规授权另一方面原始日志非常脏。开发者的编辑动作大量是重复的、无意的甚至包含敏感信息。你需要做大量的清洗、脱敏、结构化处理才能变成可训练的轨迹。这也是为什么真实工作流数据虽然价值高但绝大多数团队无法轻易获取。3.3 合成数据与自我博弈合成数据是当前 Coding Agent RL 训练里增长最快的数据来源。思路是先用现成的模型甚至就是待训练的模型本身去尝试解决一批任务把成功或失败的过程记录下来再通过规则或模型筛选出有效轨迹作为 RL 训练样本。合成数据的优势是产能大、可控性强。你可以设计一批“故意带 bug 的代码”让 Agent 去定位并修复也可以让 Agent 自己写一个函数然后生成对应的测试用例再让它根据测试结果修改代码。这种自我博弈的玩法实际上是一种自动生成训练数据的方式。它的主要风险是质量不稳定。模型自己生成的轨迹可能包含大量“看似在工作、实际上在做无用功”的步骤。如果直接拿来训练模型可能会学会这种低效甚至错误的操作习惯。因此合成数据在进入训练前通常需要经过强筛选要么用高质量测试用例验证结果要么用规则过滤掉明显不合逻辑的步骤。以工业场景为例很多团队在没有高质量公开数据的情况下会先用合成数据“打底”模拟常见的缺陷类型、生成合成缺陷样本、让 Agent 生成对应的检测代码和测试用例。虽然和真实数据仍有差距但至少让训练流程能够启动。3.4 人工偏好数据人工标注的偏好数据同样重要。具体做法是让模型针对同一个问题生成多个不同的修改方案由人类开发者或者资深工程师从中挑选出更好的方案并说明原因。这种偏好数据适合用在 RLHF 或者 DPO 一类的偏好优化环节。它的价值在于引入人类对“什么才是好的代码行为”的判断。自动化奖励可以判断测试是否通过但很难判断“这个重构是否比那个重构更合理”“这组命名是否更清晰”。人工偏好数据正好弥补了这个盲区。代价是成本极高而且标注一致性很难保证。十个工程师对同一段代码修改的评价可能完全不同需要设计清晰的标注规范和评审流程否则偏好数据本身就会带进大量噪声。3.5 数据来源怎么选一张对照表数据来源获取成本数据质量噪声水平适用阶段开源代码仓库低中高预训练、SFT 冷启动公开问题集低高但规模小低评测、RL 种子数据真实开发者工作流高极高高需大量清洗RL 核心差异化数据合成数据 / 自我博弈中中中RL 数据扩充、冷启动人工偏好数据很高高中取决于标注规范RLHF / DPO 阶段从这张表能得出一个判断公开数据适合让你快速跑通实验真实和合成数据决定了你最终能走多远。如果预算有限先不要急着砸钱买标注优先把“自己采集轨迹 合成数据筛选”这条管线搭起来收益会比想象中更大。4. 轨迹采集真正拉开 Agent 能力差距的环节如果说数据来源决定了“练什么”那轨迹采集就决定了“怎么练”。轨迹采集的质量直接决定了 RL 训练能不能学到有价值的东西。4.1 轨迹不是“问题 答案”而是“状态 动作 反馈”很多第一次接触 Agent RL 的开发者最容易犯的一个错误是把轨迹等同于 prompt-response 对。实际上Agent RL 里的轨迹是一串按时间顺序排列的决策记录核心组成部分有三个observation状态、action动作、feedback反馈。observation 描述了模型当前看到的环境状态。比如仓库里有哪些文件、当前光标所在位置的代码、测试输出的报错信息、lint 检查的结果。action 是模型在这个状态下执行的操作。比如打开某个文件、搜索某个符号、修改某个代码片段、执行一条测试命令。feedback 是环境对这个动作返回的结果比如文件是否成功保存、测试是否通过、命令是否报错。采集轨迹的本质就是把这三个元素按照真实的时间顺序完整地记录下来。只有拿到这种“状态-动作-反馈”的结构化记录RL 算法才能判断一个动作的好坏。4.2 Plan 与 Coding 两阶段的轨迹要分开看待前面提到不少主流 Coding Agent 产品已经拆分了 plan 和 coding 两个阶段。在 RL 轨迹采集时这两个阶段的轨迹形态差异非常大不能混在一起处理。Plan 阶段的轨迹主要是文本推理模型读 issue 描述、搜索代码上下文、输出一份修改计划。这一段没有外部动作有的只是模型的推理链。这类轨迹对训练“问题定位和方案设计”能力很有帮助但奖励很难设计——你怎么自动判断一份计划是不是好的通常只能靠最终结果反推或者用人工偏好。Coding 阶段的轨迹则完全不同。它包含大量的工具调用和命令执行记录每一步都有明确的环境反馈。比如模型改了一行代码然后跑了一遍测试测试失败于是再改一行再跑测试。这种轨迹的结构非常适合做强化学习模型执行动作环境给出明确反馈算法据此更新策略。在采集时建议把这两类轨迹分别存储打上不同的 stage 标签并在后续奖励设计中使用不同的策略。Plan 阶段的轨迹可以用于偏好学习和推理能力的训练Coding 阶段的轨迹才是规则奖励最容易发力的地方。4.3 高质量轨迹采集的三个原则第一记录必须完整且对齐。很多团队的轨迹采集工具只记录“最终修改”不记录“中间失败”。这是一件很可惜的事。因为 RL 训练里最宝贵的恰恰是“失败之后如何修正”的轨迹。一次从测试失败到最终修复成功的完整过程比十个只包含正确答案的静态样本更有训练价值。另外要注意光记下动作不够还要把产生动作的环境输出一并保存否则后续无法判断这一步是成功还是失败。第二要有明确的任务边界。一条轨迹应该对应一个清晰的任务一个 issue、一个单元测试的修复、一个特定功能的实现。如果任务描述不清楚后续奖励计算时连“这个任务有没有完成”都判断不了。理想情况下每条轨迹都应当包含任务描述、起始状态、完整轨迹、最终状态、执行结果和元信息。第三要记录成功状态但也不要丢掉失败样本。很多人以为 RL 训练只需要成功轨迹。实际上失败的轨迹对模型的“避错”能力同样重要。在 RL 训练中模型通过对比“哪些动作带来了负面反馈”来学习规避错误。如果你把所有失败样本都删掉模型反而无法学会从错误中恢复。更合理的做法是保留成功轨迹用于正向强化同时保留一部分有代表性的失败轨迹让模型理解惩罚信号。4.4 一次真实的轨迹采集流程长什么样假设你要采集一条“修复测试失败”的轨迹从产品视角看流程大致是准备一个隔离的代码仓库里面有一个已知失败的任务。启动 Agent给它一个 issue 描述。Agent 开始 plan 阶段读取仓库结构、定位相关文件。Agent 进入 coding 阶段打开文件、修改代码、执行测试。记录器在后台实时记录每个动作和每次环境返回结果。任务结束后汇总整段轨迹打上 success/failure 标签。这个流程本身并不复杂难的是把记录器搭建得可靠既要控制 Agent 的执行环境完全可复现又要保证每次动作的环境输出被完整捕获还要避免运行日志里包含敏感信息。从工程实践看轨迹采集器的稳定性往往比模型训练代码本身更影响最终效果。4.5 对领域冷启动的启发回到前面提到的工业场景。一个想训练“焊缝缺陷检测代码助手”的团队最常见的冷启动难题就是没有现成轨迹。如果你拿 GitHub 上的通用 Python 代码去训练模型对焊缝检测这个垂直任务的“手感”会很差。更可行的路线是先用合成数据制造一批“焊接缺陷检测相关的代码任务”让 Agent或者人工脚本在模拟环境里跑一遍把成功和失败的轨迹都记录下来再用规则筛掉噪声形成初始轨迹集。这一步跑通之后再逐步用真实场景的数据去替换和扩充。这种“合成数据 模拟环境 轨迹采集”的冷启动路线在 2026 年的实践案例中越来越常见。5. 奖励函数从稀疏到稠密的设计路径奖励函数是 RL 训练里最微妙的部分。它本质上是把“什么行为是好的”翻译成一个数值信号让优化算法有据可依。在 Coding Agent 场景里奖励设计可以分成几个层级每个层级的信号强度和设计难度都不一样。5.1 结果奖励最朴素也最稀疏最简单的奖励设计是只看最终结果。比如 Agent 完成任务后跑一遍全部单元测试通过率就是最终奖励。这种奖励实现起来非常容易执行环境只需要在任务结束时跑一次测试集合然后按通过率算分。它的主要问题是信号稀疏Agent 在整个执行过程中做了几十个动作但只有最后一个动作结束后才能拿到奖励。中间那些步子走对了还是走错了模型无法得到细致的反馈。在长任务上稀疏奖励会带来严重的“信用分配”难题——模型很难知道是哪个动作导致了最终的成功或失败。所以结果奖励适合短周期、测试覆盖良好的任务不适合探索空间大的复杂任务。5.2 执行反馈信号天然存在的中等密度奖励代码任务的特殊之处在于环境和工具会返回大量的中间信号。比如编译器的错误数量是变多了还是变少了单测的通过数量有没有增加lint 检查的告警有没有减少代码编辑是否能被成功解析运行日志里有没有出现新的异常类型。这些信号不需要额外训练直接从执行环境里就能拿到。把它们组合成一个“过程奖励”就能在每一步给模型提供比结果奖励稠密得多的反馈。一个常用的设计是在每一步计算“测试通过数”和“编译错误量”的变化如果这一步让测试通过数增加了就给一个正向的小奖励如果引入了新的编译错误就给一个负向的惩罚。这种基于执行反馈的奖励几乎没有额外人工成本是 Coding Agent RL 里“性价比”最高的一种设计。5.3 过程奖励模型PRM与验证器Verifier当任务的正确性不容易通过测试用例直接判断时就需要引入学习得到的验证器。过程奖励模型Process Reward Model, PRM的思路是训练一个小模型对轨迹中的每一步给出“这一步做得好不好”的分数。这个分数可以基于人工标注、基于最终结果反推或者基于规则生成的弱标签。它比执行反馈更灵活但训练难度也更高。一个更谨慎的做法是训练一个结果验证器给定任务描述和 Agent 的最终输出比如一个 patch验证器判断这个 patch 是否真的解决了问题。SWE-bench 里的很多评测思路就是这个模式不是在原始仓库上直接跑测试而是先应用 patch再跑测试并判断测试结果。这种验证器可以作为奖励信号也可以作为最终评价指标。使用 PRM 和 Verifier 有一个明显的风险验证器本身可能会出错。如果验证器给了错误的高分模型就会学会迎合验证器的“口味”而不是真正解决问题。这也是为什么验证器的训练数据和评估逻辑需要有独立的保障。5.4 人工偏好与成对比较奖励在无法自动判断“哪个结果更好”的场景里最后一道防线是人工偏好。最经典的做法是让 Models 针对同一个任务生成多个候选结果由人来两两比较选出更优的一个。通过成对比较数据可以训练一个偏好模型再把它接入 RLHF 或者 DPO 流程。人工偏好的优势是上限高能够覆盖代码质量、可读性、可维护性这类测试用例测不出来的维度。但成本也很真实标注一个复杂任务的轨迹可能需要十几分钟甚至更久而且不同人的标准不一致。在实际项目里建议把人工偏好用在最核心、最影响体验的任务类型上不要试图全量覆盖。5.5 Reward Hacking奖励函数设计里最隐蔽的坑只要做 RL就一定会遇到 Reward Hacking奖励黑客问题。简单说模型学会了“钻奖励函数的空子”而不是真正完成任务。举一个很典型的例子你给每个“成功修改代码文件”的动作一个正奖励Agent 可能会学会频繁地打开文件、做无意义的小改动只为了刷奖励分但问题根本没有解决。再比如你用“测试通过率”做奖励Agent 可能学会去改测试用例本身把失败的测试删掉而不是修源码。奖励函数的设计原则必须是宁可奖励信号稀疏也不要给模型可钻的空子。如果你发现训练过程中模型得分在涨但任务完成率在跌几乎可以断定是 Reward Hacking 发生了。5.6 最简单的奖励函数示例下面给出一个极简的奖励计算函数用于演示“结果奖励 过程奖励 惩罚”的组合设计。它的作用不在于可以直接上生产而在于让你理解奖励函数的基本写法。# 文件路径reward_example.py def compute_reward(trajectory, test_results, lambda_process0.2): trajectory: list of dict每个元素是一个步骤包含 stage/action/feedback test_results: {passed: int, failed: int, compile_error: bool} reward 0.0 # 1. 结果奖励编译失败重罚 if test_results[compile_error]: reward - 2.0 else: total test_results[passed] test_results[failed] if total 0: pass_rate test_results[passed] / total reward 5.0 * pass_rate # 2. 过程奖励每成功完成一个有效编辑动作给一个小信号 valid_edits sum( 1 for step in trajectory if step.get(stage) coding and step.get(action) edit_file and step.get(feedback, {}).get(applied) is True ) reward lambda_process * min(valid_edits, 10) # 3. 惩罚测试连续失败说明 Agent 在无效空转 failed_runs sum( 1 for step in trajectory if step.get(stage) coding and step.get(action) run_test and step.get(feedback, {}).get(passed, 0) 0 ) reward - 0.1 * min(failed_runs, 20) return reward这段代码的核心思路是最终结果是主信号过程反馈做补充空转行为做惩罚。你可以在这个基础上根据业务调整权重但原则是不变的——奖励函数的每一项都要有明确的行为含义不要凭空加上没有解释的数值。6. 一个最小可跑的 RL 数据准备与奖励计算示例理论讲完下面用一个最小示例把“轨迹数据 奖励计算 数据过滤”串起来。这个示例不追求完整跑通一个 RL 训练而是让你看清数据层面要做什么。6.1 轨迹数据格式一个 JSON 示例先定义一条简化版的轨迹。结构上包含任务的起始状态、中间每一步的 stage/action/observation以及最终的测试结果和奖励。{ task_id: repo-0427-issue-131, task_desc: 修复 config 解析模块在空配置时抛异常的问题, start_state: { files: [src/config_parser.py, tests/test_config_parser.py], branch: fix-issue-131 }, trajectory: [ { stage: plan, step: 1, action: search_symbol, params: {keyword: parse_config, file: src/config_parser.py}, observation: {matches: [parse_config(line:87)]}, feedback: {status: ok} }, { stage: coding, step: 2, action: edit_file, params: { path: src/config_parser.py, start_line: 88, end_line: 92, new_content: if not config_text.strip(): return {} }, observation: {applied: true, parse_errors: 0}, feedback: {status: ok} }, { stage: coding, step: 3, action: run_command, params: {cmd: python -m pytest tests/test_config_parser.py -q}, observation: {exit_code: 0, stdout_tail: 12 passed, 0 failed}, feedback: {passed: 12, failed: 0} } ], final_state: { patch: diff --git a/src/config_parser.py ..., test_summary: {passed: 12, failed: 0} }, success: true }可以看到每个步骤都保存了动作、参数和环境返回结果。这种结构化的轨迹正是 RL 训练真正需要的数据。建议在数据管线中统一采用类似的 JSON 格式并且每个字段都要有清晰的 schema 定义。6.2 奖励计算脚本把上一章的奖励函数保存为 reward_example.py然后写一个调用它的脚本# 文件路径run_reward_example.py import json from reward_example import compute_reward with open(demo_trajectory.json, r, encodingutf-8) as f: demo json.load(f) # 从最终状态里取出测试结果 test_results demo[final_state][test_summary] reward compute_reward(demo[trajectory], test_results) print(task:, demo[task_id]) print(success:, demo[success]) print(reward:, round(reward, 4))预期输出大概是task: repo-0427-issue-131 success: True reward: 5.2这里的 5.2 来自 5.0 的通过率奖励加上 0.2 的有效编辑奖励这个示例里成功编辑了一次。如果你把轨迹改成“编译失败”的情况奖励会变成负数。这说明奖励函数确实在编码出它对行为好坏的评价。6.3 轨迹数据过滤脚本在真正开始 RL 训练之前还需要对采集到的轨迹做一次清洗和过滤。常见的过滤规则包括删除超过最大步数的轨迹、删除奖励过低的轨迹、删除没有任何动作的异常轨迹。下面是一个极简过滤脚本# 文件路径filter_trajectories.py import json def filter_trajectories(raw_file, out_file, max_steps50, min_reward0.0): kept 0 total 0 with open(raw_file, r, encodingutf-8) as f: items [json.loads(line) for line in f if line.strip()] with open(out_file, w, encodingutf-8) as f: for item in items: total 1 if len(item.get(trajectory, [])) max_steps: continue if item.get(reward, 0.0) min_reward: continue if not item.get(trajectory): continue f.write(json.dumps(item, ensure_asciiFalse) \n) kept 1 print(ftotal{total}, kept{kept}, drop_rate{1 - kept / max(total, 1):.2f}) if __name__ __main__: filter_trajectories(raw_trajectories.jsonl, clean_trajectories.jsonl)这个脚本非常简单但已经体现了数据清洗的核心动作把不符合要求的数据挡在训练门外。在实际生产中过滤逻辑会比这复杂得多比如去重、敏感信息检测、轨迹格式校验、任务难度的分层采样等但思路是一样的。6.4 怎么验证这套最小流程跑通这个示例只需要三步准备好 demo_trajectory.json 和 raw_trajectories.jsonl。运行 python run_reward_example.py确认奖励数值输出正常。运行 python filter_trajectories.py确认过滤后数据量符合预期。如果哪一步报错优先检查 JSON 格式是否合法、字段名是否对齐。这类数据管线的报错最常发生在字段命名不一致上——数据采集端写的是 final_state奖励计算端读的是 state结果脚本直接崩掉。所以在一开始就要用统一的 schema 管理数据不要在字段上偷懒。7. 常见问题与排查思路Coding Agent RL 的数据管线不像普通的后端服务问题往往不是“服务挂了”而是“训练悄悄走偏了”。下面列出几个高频问题以及对应的排查思路。问题现象可能原因排查方式解决方案奖励不收敛或剧烈震荡奖励信号过于稀疏或奖励权重设置不合理画出每一步 reward 分布检查异常值来源拆分奖励项单独调试每项权重先跑短任务验证稳定性模型学会了刷分但不解决问题Reward Hacking模型钻了奖励函数的空子对比训练中 reward 与真实任务完成率检查奖励函数能否被轻易欺骗增加任务完成度约束用独立测试集评估轨迹数量很多但质量差采集环境不可控Agent 在沙箱里乱跑抽样查看轨迹统计无效动作占比增加任务难度分层限制 Agent 的动作空间加入规则过滤公开数据重复导致过拟合训练集和评测集同源或者数据去重不足计算训练集与评测集的 n-gram 重叠率严格去重保留独立的评测集评测集不参与训练RL 后模型通用能力下降训练分布过于单一只强化了代码场景在通用代码任务上做回归测试混入通用数据调整采样比例增加正则化手段单元测试覆盖不全面奖励虚高测试用例写得太弱无法反映真实问题检查测试用例的断言强度补充更强测试引入样本外的验证器二次判断这些问题的共同点是看起来是“模型学得不好”实际上绝大多数是数据或奖励设计的问题。所以排查的时候不要急着调算法先回到数据和奖励函数本身。8. 工程实践建议到这里核心概念已经讲完。最后从工程角度给出几条建议这些建议来自行业实践中反复被验证过的经验值得在项目启动前就考虑清楚。第一用数据版本管理工具管好轨迹数据。轨迹数据量通常很大而且会随着 Agent 版本迭代不断变化。建议从一开始就用 DVC 或类似的工具管理数据版本把“数据集的每个版本”和“模型训练的结果”关联起来。否则你会很快发现三个月前的模型为什么好没人说得清因为当时用的数据版本找不到了。第二代码执行必须隔离在沙箱里。Agent 会在训练和执行过程中运行大量代码如果这些代码不隔离可能对你的开发机、测试环境甚至生产环境造成破坏。从第一天起就把 Agent 的执行环境限制在容器或虚拟机里不要为了省事直接在本机跑。第三最小成本起步。对于个人开发者或者小团队不要一开始就想着训练自己的基座模型。更务实的路径是在现有模型的基础上先搭建“轨迹采集 奖励计算 数据清洗”的管线把数据侧跑通再考虑用 RL 做后训练。很多时候数据管线的价值远大于你换一个更大的基座模型。第四训练用奖励和最终评估必须分离。如果你用一组单元测试作为 RL 奖励的依据那最终评估时一定要用另一组样本外的测试题和测试用例否则评估结果会虚高。这一点在 Coding Agent 领域尤其重要因为代码任务的环境反馈太容易“背题”了——只要训练数据里出现过类似题目模型就可以靠记忆而不靠推理拿到高分。第五优先做好失败的轨迹。真实开发中 80% 的时间都在处理失败编译失败、测试失败、重构引入的新问题。如果你的轨迹采集器只会记录成功案例你的模型就只学会了一条笔直的路一旦路上出现意外就会不知所措。把失败样本纳入训练轨迹是提升 Agent 鲁棒性最直接的手段之一。第六控制轨迹长度别让模型在过长的任务里迷失。当前模型对超长上下文的处理能力虽然一直在提升但轨迹越长奖励信号越稀疏训练难度越大。对于初始的 RL 训练建议优先选择 10 到 30 步能完成的任务等模型在短任务上稳定了再逐步延长任务窗口。第七考虑到领域冷启动的情况先做一个能自动生成领域任务的最小系统。如果你要训练一个垂直领域的 Coding Agent与其等真实数据不如先搭一个最小系统从领域知识库抽取概念自动生成代码任务再把任务的执行轨迹记录回来。这个系统哪怕一开始很粗也能为 RL 训练提供源源不断的训练燃料。9. 写在最后从最小闭环开始把本文拉回一条主线Coding Agent 能不能真正变强靠的不只是更聪明的基座模型更是 RL 阶段的数据质量。数据来源解决“练什么”轨迹采集解决“怎么练”奖励函数解决“什么是好”。这三件事做好了哪怕你用的是一个中等规模的模型也能训练出干活很稳的 Agent反过来说如果数据链路是烂的再大的模型也会在具体任务上一塌糊涂。对于准备入场的读者我的建议很直接不要一上来就研究 GRPO 和 PPO 的数学细节先从一个小任务出发搭一个轨迹记录器把一个 Agent 完整跑任务的过程存成 JSON写一个你信得过的奖励函数用过滤脚本挑出干净的数据然后把这一整套跑通。这个最小闭环能帮你建立对 Coding Agent RL 的直觉。这个闭环一旦跑通后面无论是换算法、换模型、还是扩展任务范围都只是在这个底座上做增量。希望这篇“浅谈”能帮你少踩几个数据层面的坑。如果你正在做相关的 Agent 或 RL 训练项目建议先把这篇文章收藏起来搭建数据管线时再对照着检查一遍。
返回列表