一句话讲清楚 一个 bug ,模型可能写出两份都能过测试的修复:一份只改了几行,另一份顺手把整个文件重构了一遍。小米 MiMo 团队发布的 GAGAR 让评分模型把同一道题的几个解法放在一起排名,再按名次重新分配训练奖励。总分不变,没通过测试的那几条该怎么罚还怎么罚。
论文标题:Groupwise Agentic Grading and Advantage Redistribution for Code Agent RL
论文链接:https://arxiv.org/abs/2609.32577
跑一遍仓库自带的测试,通过就是 1 ,不通过就是 0 。这种只有两种取值的奖励,是目前训练代码 Agent 最省事的做法:不用人写评语,也不用额外训一个奖励模型。
它的麻烦出在「通过」这一档内部。一份只改了几行,一份顺手重构了整个文件,测试给出同样的结果,训练时也拿到同样的奖励。这样一版一版训下去,模型学到的是「能过就行」,而接手这份代码的人要多花一轮评审时间。
论文把这个问题写成一句很直接的话:在成功的轨迹里,哪些更值得被强化?
测试通过这个信号太粗
按组相对策略优化( GRPO ,一种按同组相对表现更新模型的强化学习方法),算法本身不复杂。同一条任务让模型生成多条完整尝试,每条拿到 0 或 1 的结果奖励 ,再算一个优势值:
优势值( advantage )衡量的是这条轨迹比同组平均水平好多少,更新模型时它直接决定每条轨迹分到多大权重。问题就藏在这个减法里:所有通过的轨迹优势值都是同一个数 $1-\bar R-\bar R$。质量差异在公式里被抹平了。
后面的训练还沿用了两个设定。一是照 Dr. GRPO 的做法,只减去组内平均分,不再除以分数波动幅度。二是只保留既有通过又有失败的组,全对或全不过的直接跳过,因为没有可比较的对象。
这也说明作者为什么不打算给单条轨迹打质量分。一条改动单独摆出来,很难判断某个小工具函数是不是多余;但旁边就放着另一份只改了 5 行的解法时,多余与否立刻清楚。质量判断只能靠组内比较。
GAGAR :先组内排名,再分档打折
GAGAR 是 Groupwise Agentic Grading for Advantage Redistribution 的缩写。第一个部件是把同一条任务的全部尝试放进一个共享工作区,里面摊着任务说明、仓库代码、模型与工具来往的完整日志、提交的代码改动、测试输出。
评分模型手里有工具,可以直接动手。它先浏览每一轮交互的摘要,挑出需要细看的部分,再去读日志片段,把改动和仓库里的原始代码、测试日志逐处对上。看完还有疑问,它可以针对某个疑点专门跑一次检查,比如复现某个测试。负面结论有硬性要求:任何一条「这里有问题」的判断,都必须给出对应的代码位置、日志事件或者执行结果。失败的解法不参与排名,但它们留在工作区里,让评分模型看到哪些思路走不通。
GAGAR 的整体流程。左侧是同一道题的一组尝试,评分模型在工作区里读代码、跑检查,给通过的解法排名;右侧按名次重新分配优势值,通过解的总优势不变,失败解也不动。
评分模型按五个维度各打 1 到 5 分,加权得到初始排名:
按权重从高到低,这五项分别是:改动有没有解决根本问题( 30%)、改动位置对不对且该覆盖的行为都覆盖到了( 25%)、是不是只做了必须做的事( 20%)、有没有连累别处( 15%)、符不符合仓库原有的代码习惯( 10%)。
评分的五个维度,以及每一维上高质量与低质量改动的典型信号。左列是维度名,中间是加分项,右列是扣分项。
打分之后分三个质量等级。一条解法如果在没被要求的地方顺手大改,或者写了专门为了骗过测试的取巧代码,直接进最低等级 T3 。另外两类也直接进 T3 :改动方向根本不对的(「有没有解决根本问题」这一项拿到最低分),以及又改得多又惹了麻烦的(改动精简度和副作用两项都只有 2 分或更低)。剩下的解法里,五项都不低于 4 分、没有严重过程问题、也没有改坏别处的进 T1 ,其余进 T2 。
等级再换算成折扣比例 ,作用在优势值上。 Flash 这套配置里, T1 排第一的系数是 1 ,也就是不打折,同等级其余解法 0.9 ; T2 从 0.85 到 0.4 按名次均匀递减; T3 统一给 0.2 。名次并列的解法拿同一个比例。
换算成人话:同一等级里排在前面、又没踩红线,权重几乎拿满;排在末尾或者踩了红线的只保留两成,省下来的优势分给同组评价更高的伙伴。留 0.2 而不是直接清零,是因为这些解法毕竟通过了测试,不该按失败处理。
还有一道单独的完整性检查。如果某条通过的轨迹抄了答案或者直接复制了外部现成的代码,它的奖励直接清零,按失败处理,不参加质量排名。这一步改变的是奖励本身,组内平均分也会跟着重算,和后面的质量再分配是两件事。
评分模型本身是小米用监督微调( SFT ,拿标注数据再把模型训一轮)练出来的 MiMo-V2.6-Pro ,用的是进入强化学习之前的那一个模型存档。作者在这里交代了一段工程取舍:最开始直接拿 Claude Opus 5 当评分器,一组要跑约 2000 秒,而且必须等这一组全部生成完才能开始评,训练流程会被拖住。换成自己微调的评分器之后,平均时间降到约 600 秒;再把评分做成「哪组先生成完就先评哪组」,组与组之间就能并行,不用等所有题目都跑完。
此前的做法大多在「怎么给单条轨迹打一个更细的分」上做文章。 ReCode 给通过解补一层过程奖励, PAPO 拿评分标准在通过解内部做归一化,两者都还是逐条独立打分; EDAS 干脆把力气花在给失败轨迹重新分配权重上。这条路的天花板在于,一份改动单独摆出来,很难说清某个小工具函数是不是多余。
另一条路是 GiGPO 和 GraphGPO 走的中间状态对应:把两条轨迹跑到中途的状态一一对上,再据此分配权重。代码 Agent 的轨迹动辄上百轮,仓库和执行状态早就分叉,这件事基本做不成。 GAGAR 绕开了它,只要求同一道题的一组解法能被摆在一起看。
只打折会失衡,所以要「还回去」
到这里有一个很自然的做法:把低质量的通过解优势值乘上折扣比例,其余不动。论文用一个对照实验说明这样做的后果。
设通过解集合为 ,只打折之后优势值变成 。原本一组轨迹的优势值加起来是 0 ,现在通过解这边少掉的那一块是 ,整组优势值之和随之变成 。
也就是说,负优势的总量开始超过正优势的总量。通过解被削了,失败解一点没变,整组的正负优势失衡了。
做法是先打折,再把扣下来的部分按质量权重还给通过解,也就是给所有通过解乘上同一个系数 :
其中 是原来分配给通过解的总优势。因为通过解的初始优势值都相等,把它代进分母,公共因子约掉之后 就剩下「 1 除以通过解的平均折扣比例」,于是式子简化成 ,其中 是通过解的平均折扣比例。翻译过来就是一句:折扣比例高于平均的解法多分到一份,低于平均的让出一部分。
三件事因此同时成立。通过解的总优势不变,失败解的惩罚不变,通过解之间的比例 也不变。最后一条最关键,它保证了排名带来的偏好顺序没有在缩放中被改掉。
两种极端情况会自动退回原样,不用额外处理:一组里只有一条通过解,或者所有通过解的折扣比例都一样时,再分配算出来的结果就是原始优势值。
实现层面还有一个保护。训练代码给缩放系数 设了 1.5 的上限,理由是名次差距特别大时,缩放可能把某条轨迹的优势值抬得过高。上限一旦被触发,实现会额外减掉组均值,把正负拉回平衡;代价是「通过解的总优势不变」和「失败解的惩罚不变」这两条不再成立,通过解之间的先后顺序仍然保留。论文把这段例外写进了附录,没有藏。
还有一个容易读反的细节。论文把目标优势值反推回奖励,证明在「先减去组内平均奖励」这种估计方法下,这套改写和优势值版本给出完全相同的梯度。作者顺带提醒:别把奖励直接乘上折扣比例,那会连带改变组内平均奖励,把失败解的优势也一起动掉,和上面这套改写不是一回事。
主结果:基线先崩, GAGAR 还在涨
主实验是纯代码任务的强化学习。主角是 MiMo-V2.6-Flash 进入强化学习之前的模型存档:混合专家( MoE ,每次只调用模型里的一部分参数)结构,总参数 310B ,每次真正调用的只有 15B 。
训练配置上,每次更新用 128 道题,每道题生成 16 条尝试。两块考卷: DeepSWE v1.1 是一批原创的长程软件工程任务,做完一道要几十上百轮操作; SWE-bench Pro 测的是仓库级别的真实问题修复。
两道基准上,基线和 GAGAR 用完全相同的设置训练,在共同经历的训练步上逐点对比;每道题评测 3 次取平均通过率,同时记录平均交互轮数和平均 token 用量。基线在第 28 步停掉之后, GAGAR 继续往下训,所以它的曲线更长。
先看左上角那条通过率曲线。基线在第 20 步达到 58.5% 之后开始往下掉,第 28 步只剩 50.2%,作者据此把基线停在第 28 步。同一个第 28 步, GAGAR 是 62.2%,比基线高 12.1 个百分点。继续训到第 44 步, GAGAR 摸到峰值 63.4%。
两种比较方式要分开看。 12.1 个百分点比的是「同一个训练步」上的差距,而基线那一侧此刻已经在往下走;如果拿两边的峰值比, 58.5% 对 63.4%,差距是 4.9 个百分点。前者说明训练崩没崩,后者才是两者最好状态的差距。
SWE-bench Pro 上的形态不同。基线从第 16 步到第 28 步一直卡在 59% 附近, GAGAR 到第 52 步做到 62.5%。这道基准的轨迹本来就短,还有继续往上走的余地。
中间和右边两列是效率。第 28 步时, GAGAR 把 DeepSWE 的平均交互轮数从 132.3 压到 111.6 ,降了 15.6%;平均 token 长度从 191.9k 降到 172.9k ,降了 9.9%。 SWE-bench Pro 上,交互轮数从 58.4 降到 54.6 , token 长度从 79.9k 降到 68.6k ,分别降 6.5% 和 14.1%。
训练崩掉是不是随机波动?作者给出的线索是长度。不做质量评分时, DeepSWE 的轨迹长度快速上涨,越来越多轨迹撞到长度上限被硬切断,通过率下滑和截断同时出现。 GAGAR 这一侧的轮数到第 52 步基本平稳, token 长度涨得也慢。长度失控和性能退化在时间上对得上,这是全文反复用到的一条线索。
消融与盲评:把优势还回去,质量也跟着变
上一个结论里还有一个变量没固定:如果不还这部分优势,只保留打折,会发生什么。两个对照都跑 Flash 的纯代码强化学习,唯一差别在扣掉的正优势还不还。
先解释一个词:策略熵衡量模型输出的随机程度。熵越高,说明模型越拿不定主意,回答越发散。只打折那一侧,熵从第 1 步的 0.359 涨到第 30 步的 0.905 ,训练时的轨迹长度从 47.1k 涨到 114.1k token ;另一侧,熵从 0.358 涨到 0.513 ,长度从 46.5k 涨到 69.9k 。两边都在涨,差别在幅度上:涨得快的那一侧,通过率同时往下掉,说明扩散没有换来更好的结果。作者没有把熵本身当成好坏标准,判断依据是它有没有伴随着评测提升。
还有一组更直接的数字。同一段训练里,只打折那一侧的策略梯度损失平均是 0.0304 ,完整方法只有 0.0021 。作者把原因归到符号上:新旧策略对同一个动作的概率比接近 1 时,策略梯度损失约等于加权平均优势值的相反数,负优势过剩会把这个数抬上去。这和前面的正负失衡是同一件事的两种说法。
评测结果同样分叉。只打折那一侧, DeepSWE 通过率从第 18 步的 56.5% 掉到第 20 步的 48.8%,第 28 步勉强回到 56.2%;交互轮数和 token 长度在第 22 步冲到 158.3 轮和 246.7k token 。同一个第 28 步:完全不做评分的基线 50.2%,只打折 56.2%,还优势 62.2%。从 50.2 到 62.2 一共 12 个百分点,前一半来自给低质量通过解打折,后一半来自把扣掉的优势还回去,各占 6.0 个百分点。质量评分让训练不至于崩掉,把优势还回去才是把分数拉上去的那一步。
另一组补充实验把测试通过率之外的东西也量了出来。作者抽了 30 个 DeepSWE 任务,在两者共享的评测存档上各取最多 3 条轨迹,把方法名抹掉、顺序打乱,交给 Claude Opus 5 按那五个维度一起打分排名。这个评审模型和训练时在线跑的评分器不是同一个,用来降低自评自夸的嫌疑。
左图是加权质量分随训练步数变化,右图是最终共享存档上两种方法的通过解拿到组内第一名的比例,以及一对一比较的平均胜率。
五项加权后的质量分( 5 分制), GAGAR 是 4.03 ,基线是 3.70 。通过解之间一对一比较, GAGAR 的平均胜率 69.8%,基线 30.2%;把两个方法的最多 3 条轨迹混在一起排名, GAGAR 拿到组内第一的比例是 65.0%,基线 35.0%。按维度拆开,改善集中在改动位置、精简程度和副作用这三项;评审模型判定的最高质量等级 T1 ,在通过解里的占比从 25.4% 升到 34.3%。两组胜率互为补数,实际是一组信息; 30 个任务、单个评审模型的规模也没有给出误差范围,用它支撑「质量确实变好」力度有限。
工业规模:混着别的任务一起训
最后一组实验把 GAGAR 放进小米自己的混合任务强化学习流程,代码只是其中一类任务,论文只说这次训练把代码和其他领域混在一起,没有列出具体是哪几类。每次更新 1568 道题,每道题生成 16 条轨迹,评分和再分配只对其中符合条件的代码任务生效。
混合任务强化学习之后的两个 MiMo 存档与外部模型的对比。表里给的是通过率,两道基准都是越高越好。 K3 指 Kimi K3 ,和 GPT-5.6 Sol 、 Claude Opus 5 一样是外部对照模型。
MiMo-V2.6-Pro 总参数 1.02T 、每次实际调用 42B ,在 DeepSWE v1.1 上拿到 71.9%,比总参数 2.8T 的 K3 高 2.9 个百分点;在 SWE-bench Pro 上 62.7%,比 GPT-5.6 Sol 的 60.5% 高 2.2 个百分点。两道基准上它都还落后 Claude Opus 5 ( 74.0% 和 79.9%)。表里最极端的一行也是 Claude Opus 5 :它在 SWE-bench Pro 上比第二名 K3 高 16.6 个百分点,在 DeepSWE 上比第二名 GPT-5.6 Sol 只高 1 个百分点,这个落差的来源论文没有解释。
另外要留意比较方式。这张表里 Flash 在 DeepSWE v1.1 上是 67.9%,高于主结果里同一道基准上 Flash 的峰值 63.4%;两者训练设置不同,一个是混合任务,一个是纯代码任务,数字不能直接搬。这张表也没有「加不加 GAGAR 」的对照,只能说明这套方法放在工业规模混合训练里跑得通,训出来的模型有竞争力;增益数字要看前面纯代码任务的对照实验。
适用范围,以及论文没回答的问题
方法只对「既有通过又有失败」的组生效。一组全过或者全不过,评分环节直接跳过,算法退回原样。长程任务又难又长,一道题上 16 条尝试全军覆没、或者 16 条全过,都不罕见,按这个逻辑推,相当一部分题目根本轮不到评分环节;论文没有给这个比例,所以这套方法能覆盖多少训练数据,目前还是未知数。
评分本身是笔开销。一组约 600 秒,而且同一组内部要等所有尝试生成完才能开评,所以「哪组先完先评哪组」的异步调度是必需的配套。换成更强的外部模型做评分器,一组要 2000 秒。这套流程离开小米自己的训练系统之后好不好复制,论文没有讨论。
评分标准是人定的,五个维度的权重也是。原文没有做权重敏感性分析,换成另一组权重结果会不会变,读者无从判断;不同仓库的代码习惯差异会不会让「代码习惯一致性」这一项失准,也没展开。
还有一处是论文没有正面处理的自评风险。在线评分器用的是 MiMo-V2.6-Pro 微调出来的模型,被训练的 Flash 和它同属一个家族。训练久了,策略的优化方向有可能从「改得更干净」偏到「更讨评分器喜欢」。盲评环节用外部模型做评审能验证结果,但验证不了这个回路本身。这大概是后续工作最该补的一项。
对做代码 Agent 训练的团队来说,「组内对比加重新分配」最容易复用:不改奖励定义,不加人工标注,只在优势值上做一层缩放,就能把「测试通过」这种粗信号里丢掉的质量差异捡回来一部分。要额外投入的是评分能力和调度,也就是那 600 秒一组能不能被训练流程消化掉。


VIP复盘网