多轮Agent越蒸馏越差?解析OPD与状态漂移的真相
最近在做多轮 Agent 蒸馏时,我被一个现象折磨得够呛:小模型在单轮任务上蒸馏后指标还挺能打,一旦进入多轮对话或者工具调用场景,loss 明明在降,效果却肉眼可见地变差。圈子里有个说法叫“多轮 Agent 越蒸馏越差”,我把这个现象和 OPD(在线策略蒸馏)放到一起排查了两周,最后发现问题不在蒸馏本身,而在我们对“多轮”的假设。这篇文章把我踩过的坑、查过的机制、试过有效的方法一次性讲清楚,希望对正在做 Agent 蒸馏的同学有帮助。
1. OPD 到底在蒸什么?先搞清楚在线策略蒸馏和普通蒸馏的差别
1.1 单轮蒸馏与多轮蒸馏的本质差异
传统的大模型蒸馏,本质上是在“拟合输出”。老师模型对一批输入给出答案,学生模型去学这些输入输出对,无论是用 logits 对齐还是序列级别的 KL 散度,核心假设都是:输入是固定的、独立的、服从同一分布的。这种假设在单轮 QA 场景下基本成立,你给它一个问题,它吐一个答案,输入不会因为你学生的表现而改变。
多轮 Agent 蒸馏完全不是这么回事。Agent 的输入是一串交互历史,而每一步的输入都取决于上一步 Agent 自己做了什么。也就是说,学生模型不是在一个固定数据集上做分类,而是在做一个策略:它选择动作,动作改变环境状态,新状态又变成下一步的输入。这种场景下,蒸馏的本质从“拟合条件分布”变成了“让策略在状态空间里走得稳”。如果学生策略和老师策略有偏差,学生走出的状态轨迹就会偏离老师见过的状态分布——这时候再拿老师的离线数据去蒸馏,就会进入一个“越蒸越离谱”的循环。
我们团队当时犯的错误就是拿老师模型跑了一大堆高质量多轮轨迹,然后直接拿这些轨迹做 SFT 式蒸馏。单轮指标确实涨了,但多轮成功率暴跌。现在回头看,这就是典型的分布错配:你让学生的行为分布去模仿老师,但学生实际走出的状态分布和老师的数据分布根本不是同一个东西。
1.2 OPD 的两种常见定义,别混在一起
OPD 这个缩写在不同上下文里指的东西不太一样,我建议先把它拆清楚再讨论问题。第一种是 Online Policy Distillation,也就是在线策略蒸馏,核心流程是:学生模型在环境里真实采样,产生一段轨迹,然后把这段轨迹交给老师模型去打分或纠偏,再用这个反馈更新学生。这种做法的关键是“学生在自己的策略下做决策”,老师提供的是对真实轨迹的监督信号,不是凭空给一段静态数据。
第二种是 Optimistic Policy Distillation,乐观策略蒸馏。它在蒸馏目标里加了一个乐观偏置项,目的是防止学生模型学成一个过度保守的策略。标准蒸馏会把学生往老师的输出分布上压,但老师分布本身存在不确定区域,如果学生在这片区域里只会照抄平均概率,学出来的策略就会平庸。乐观蒸馏的做法是:当学生采样的动作有潜力获得更高回报时,即使老师给出的概率不高,也允许学生保留一部分探索倾向。
这两种 OPD 在多轮 Agent 场景下都会遇到一个共同问题:多步决策的误差传播。在线蒸馏虽然能缓解离线分布的静态偏差,但它要求学生和老师同时在线交互,成本极高;乐观蒸馏则引入了额外的超参数,调不好反而会让学生学到更野的行为。不管选哪种,如果你不理解误差是怎么累积的,都会觉得“越蒸馏越差”。
1.3 为什么多轮场景放大了蒸馏缺陷
单轮蒸馏的缺陷顶多是输出质量不高,比如语言不通顺、事实有幻觉,这些问题在评估时能直接看到。但多轮蒸馏的缺陷会通过环境交互被放大,变成“雪崩效应”。每一轮的小概率错误都可能在后续轮次中被反复引用,导致 Agent 越走越偏,最终挂在一个完全没见过的状态上。
用一个生活化类比:单轮蒸馏就像让你看名厨做菜的视频,然后你照着视频抄一道菜,抄得不好顶多就是菜难吃;多轮蒸馏就像让你看名厨处理一条活鱼的全过程,但你是边做边学——你第一刀切歪了,鱼会动,后续的位置全变了,这时候你脑子里只有名厨在鱼不动时怎么下刀的记忆,根本不知道怎么调整。Agent 蒸馏就是这个“鱼会动”的问题,学生每一步的错误都会改变后续所有状态的语义。
所以核心结论是:多轮 Agent 不能简单地当成“很多个单轮任务”来蒸馏,必须从策略分布的角度看待学生和老师的状态空间差异。这是后面所有诊断和缓解方法的基础。
2. 为什么多轮 Agent 会越蒸馏越差?四个我实测踩中的机制
2.1 暴露偏差:学生永远在回答“没见过的问题”
暴露偏差在 NLP 里是个老概念,机器翻译时代就有:训练时用 teacher forcing,解码时却用自己的预测作为输入,导致模型面对的训练分布和解码分布不一致。多轮 Agent 把这个偏差放大了十倍,因为 Agent 的每一步动作都会构造出新的“上下文”,而这个上下文在老师数据里很可能从未出现过。
我做过一个实验:用老师的策略跑 500 条多轮轨迹,统计中间状态去重后的数量,再用学生策略跑一遍,统计学生轨迹里有多少状态不在老师的 500 条轨迹里。结果让我挺惊讶的,学生轨迹里有接近 40% 的中间状态是老师从未见过的。也就是说,学生大部分时间都在“没有参考答案的区域”里硬撑,而蒸馏 loss 还在一遍遍告诉它“应该输出老师在高概率区域时的答案”,两边根本对不上。
这个机制解释了一个很反直觉的现象:训练 loss 越低,实际效果越差。因为 loss 低只说明学生学会了在“老师数据覆盖的区域”里模仿老师,但多轮交互中学生会迅速滑出这个区域,在区域外只能靠随机猜测,于是成功率暴跌。解决这个问题的起点不是调蒸馏轮数,而是先想办法让学生暴露在更接近自己真实轨迹分布的数据上。
2.2 复合误差:小错误被多轮放大成灾难
如果学生在每一步决策上的误差是 ε,单轮任务最终的误差也就是 ε;但在一个长度为 T 的多轮任务里,误差会以近似 T·ε 甚至 ε^T 级别的速度累积。理论上一堆论文都提到过复合误差的可怕,但只有真实跑过一遍才能体会到什么叫“一步错步步错”。
我遇到过一个很典型的案例:一个订机票的 Agent,需要完成“查航班、选票、填乘客信息、确认支付”四步操作。学生模型在单步动作准确率上和老师只差 5 个百分点,但整个四步流程走完的成功率差了接近 30 个百分点。拆开看每一步的错误率几乎都集中在同一个环节——“选票”之后返回的航班状态描述,学生会漏掉一个字段,导致后续日期解析全错。单看这一步是小错误,但后续所有动作都在这个错误的状态上进行,最终全盘崩溃。
这个机制意味着:如果只看 agent 的“平均步骤准确率”,你根本发现不了问题。必须按完整任务的成功率来评估,或者至少按“一条轨迹中是否存在任何一个错误步骤”来统计。多轮场景下的误差是乘法关系,不是加法关系,这也是为什么“越蒸馏越差”经常在长轨迹任务中被报告。
2.3 老师监督信号失效:正确答案建立在错误前提上
蒸馏的核心依赖是“老师的输出可以作为学生的学习目标”,但这个依赖在多轮场景下有个隐藏前提:老师的输出必须是在“学生当前所处的状态”下产生的。如果学生已经偏离了老师的状态分布,老师给出的那个动作建议可能根本就不是正确动作,这时候学生模仿得越用力,错得越远。
举个例子,学生 Agent 在第二步时错误地认为“用户选择了经济舱”,但实际用户选的是公务舱。第三步的正确动作应该是“确认舱位类型”或者“按公务舱报价”。但老师模型在离线数据里没见过“学生记忆中的那个错误状态”,它只见过“状态特征为经济舱”时的数据,于是给出“继续按经济舱报价”的指导。学生照做,错误被加深。这就是监督信号失效——不是老师不专业,而是老师被问了一个它没见过的“伪状态”。
这种失效没法通过增加老师数据量来解决,因为状态空间是组合爆炸的。我做过统计,在一个工具调用型 Agent 任务里,两步操作后可能出现的状态数量是单步状态的几十倍。老师不可能穷举所有错误前缀,所以要紧的不是让老师更博学,而是设计机制让学生减少进入“伪状态”的概率。
2.4 信用分配冲突:每一步都想模仿,但容量不够导致梯度打架
多轮 Agent 蒸馏还有一个隐蔽问题:即使状态分布没偏、老师监督也没失效,学生模型还会因为容量不足在“梯度层面”打架。每一步的蒸馏 loss 都在逼学生同时满足两种要求:第一步的输出要和老师一致,而第一步的输出又会影响第二步的输入,进而影响第二步的学习目标。如果学生模型容量不够,它就没有足够的表达能力同时记住这些条件关系。
这个现象我是在检查训练 loss 曲线时发现的:单轮的 loss 平滑下降,多轮的 loss 在下降过程中会出现周期性震荡,而且震荡幅度随轮数增加而变大。一开始我以为只是学习率问题,后来做了梯度诊断,发现不同 step 的梯度方向经常互相冲突——某个步骤的梯度想让某个特征往左偏,另一个步骤的梯度想让同一个特征往右偏。这种冲突在小模型上尤其严重,因为小模型没有充裕的参数去单独建模所有分支。
遇到这种问题,靠加大训练数据是没用的,因为不是信息不够,而是容量不够。要么换更大的学生模型,要么改变蒸馏的形式,把“逐步模仿”改成“高层指导+低层自由发挥”,比如只在任务级的动作选择上做蒸馏,让底层的文本生成自由一点。
3. 如何诊断“越蒸馏越差”?一套可以复现的排查方案
3.1 设计对照实验:离线蒸馏 vs 在线蒸馏,单轮 vs 多轮
如果你怀疑自己的多轮 Agent 蒸馏在退化,第一步不是改模型,而是跑一组对照实验,把问题定性。我这里的标准配置是四个实验:离线蒸馏单轮、离线蒸馏多轮、在线蒸馏单轮、在线蒸馏多轮。控制变量包括学生模型大小、数据量、训练步数、评估任务,确保只有“蒸馏方式和任务轮次”不同。
评估时别只报告一个综合指标,要拆开看:单步动作准确率、完整任务成功率、平均轨迹长度、错误分布位置。我实际见过的情况是:离线蒸馏多轮在单步准确率上不输在线蒸馏,但完整任务成功率掉了一大截。这个差异本身就是诊断信号——问题出在状态分布漂移上,而不是输出质量上。
还有一个容易被忽略的对照组:把老师自己的轨迹拿去评估一遍,看它在这个任务上的“天花板”是多少。如果老师多轮成功率本来就只有 60%,学生蒸到 50% 其实不算差,只是被老师的上限限制了。先确认天花板,再谈退化。
3.2 三个关键信号:loss、状态覆盖率、首步错误率
诊断过程中我建议盯住三个信号,任何一个异常都值得警惕。
第一个是训练 loss。如果 loss 下降但评估指标下降,基本可以断定是分布漂移问题,因为这说明模型在训练分布上拟合良好,但实际交互分布已经偏离。如果 loss 不降甚至上升,那更麻烦,可能是学生进入了一个老师完全无法提供有效监督的“无人区”,需要立刻检查轨迹内容。
第二个是状态覆盖率。把你的验证集轨迹丢给老师模型,统计老师见过的状态集合;再让学生自己跑一批轨迹,统计学生命中的状态有多少在老师集合之外。这个比例超过 20% 就要警惕,超过 40% 基本可以宣布当前蒸馏方案失效。状态覆盖率是我们项目里最敏感的指标,比 loss 早两三天就能看出问题。
第三个是首步错误率。多轮任务里,第一步错的代价最大,因为它带偏整个后续轨迹。我会单独统计“第一条动作就偏离老师动作”的比例,如果这个值在训练中不降,说明学生根本没有学会多轮任务中最重要的开局能力。一步错、步步错,开局错误率比任何中期错误率都更值得关注。
3.3 一个实用的状态分布对齐度指标
为了量化“学生状态分布偏离老师状态分布”的程度,我自己设计了一个简单粗暴的指标:让老师模型给学生轨迹里的每个中间状态计算一个感知分数,再让老师给自己轨迹里的对应状态计算同样的分数,两者的差值除以老师自己的分数标准差,得到一个无量纲的偏离度。
具体做法是:取学生轨迹的每一步观察(observation),拼成一个 prompt,用老师模型算这个 prompt 的 log perplexity。如果学生轨迹里的 prompt 是老师没见过的组合,perplexity 会明显偏高。把所有 step 的偏离度求平均,就是这个状态分布对齐度。数值越低越好,超过 1.0 说明学生基本在老师的知识盲区里游荡。
这个指标的好处是便宜且无需训练,只要有一个老师模型和一批学生轨迹就能算。我在项目里把它接进训练日志,每 500 步算一次,一旦发现偏离度超过阈值就暂停训练、检查采样策略。调了一段时间之后,我发现很多“越蒸馏越差”的情况其实在训练中段就能提前发现,不用等到评估阶段才崩溃。
3.4 记录轨迹时必备的字段与对比方法
诊断离不开好日志。我在做多轮蒸馏排查时,轨迹记录至少包含这么几个字段:step 序号、当前状态摘要、原生的 observation、学生动作、老师参考动作、环境返回结果、状态 hash。状态 hash 用观察文本的规范化哈希生成,方便快速查询某个状态是否出现过。
对比时有一套固定动作:先按状态 hash 分组,统计老师数据和学生数据各自覆盖的 hash 集合,算出交集和差集。差集里的状态,就是学生“发明”出来的状态,重点检查这些状态里学生的表现。再按 step 序号看错误率分布,如果错误集中在第二步到第三步之间,说明问题可能是某个中间转换函数没学好;如果错误集中在最后一步,往往是结局偏好没蒸馏到位。
这套方法我们每周跑一次,能形成一张“学生状态漂移热力图”,哪个环节开始漂、漂多远,一目了然。没有这些日志,所谓“越蒸馏越差”就只能靠感觉,根本没法定位。
4. 如何缓解多轮蒸馏退化?五个验证过的方法
4.1 在线蒸馏:让学生在自己策略下走轨迹,老师负责打分
最直接的解法就是把离线蒸馏改成真正的在线策略蒸馏。每一步让学生自己采样动作,把实际轨迹喂给老师模型做 correction,或者用环境里的规则校验器给 reward。这样做的好处是学生永远在自己的分布下学习,暴露偏差被控制在最小。代价是训练成本翻倍甚至更多,因为你不再复用离线数据,每一条轨迹都要实时生成。
我在小规模任务上试过在线蒸馏,效果确实显著。500 条在线轨迹比 5000 条离线轨迹在多轮成功率上的提升还大。原因是学生的状态分布不再漂移,老师给的每一个监督信号都是对当前真实状态的反馈。要是你连在线成本都承受不起,那至少要在离线数据里混入学生自己跑出的轨迹,让训练分布更接近推理分布——这种方法叫“混合蒸馏”,实操上性价比最高。
在线蒸馏还有一个额外好处:它天然带有探索性,能暴露出学生模型在哪些中间状态上最弱。我见过一个团队就是靠在线蒸馏的轨迹分布发现了学生的“长文本记忆”问题,而离线蒸馏完全测不出这个问题。
4.2 乐观蒸馏:给不确定区域留一点探索空间
如果学生模型容量有限,严格的分布匹配反而会让它变得畏手畏脚——每个动作都输出一个“安全但平庸”的概率分布,导致多轮决策缺乏主见。乐观蒸馏就是针对这个问题设计的:在蒸馏 loss 中引入一个鼓励“有潜力动作”的偏置项,具体实现是在老师概率分布的基础上,把高价值动作的概率抬高一点,再让学生去拟合。
这里的“有价值”怎么定义?我的经验是两个来源:一是环境 reward,如果你有规则评估器,直接用它标记高分动作;二是老师模型自身的置信度,如果老师对某个动作的隐状态置信度远高于其他动作,就把它视为高价值动作。乐观项给这些动作额外加了梯度推力,让学生不会被老师的平均分布拉平。
调乐观系数要小心,系数太大学生容易学疯,输出一些环境中不合理的行为;系数太小又起不到挖掘高价值动作的作用。我实际操作中一般从 0.05 开始,每 500 步观察一次状态分布对齐度,保持不漂移的前提下尽量调大。老实说这个超参数比学习率还难调,但调好了能显著缓解“容量不足导致梯度打架”的问题。
4.3 Teacher Forcing 与 Scheduled Sampling:别让学生一直抄答案
训练多轮序列模型时,习惯性用 teacher forcing——每一步都给真实的老师前缀,让学生预测下一步。这个习惯在单轮场景没问题,但在多轮场景会让模型产生“依赖正确答案”的错觉,推理时没有正确答案可依赖,就崩了。
Scheduled Sampling 的思路是:训练时按概率决定当前 step 是使用老师前缀还是学生自己的前缀。前几轮多用老师前缀让模型快速学会基本语义,后几轮逐步降低老师前缀比例,逼学生在自己的错误前缀上学习修正。我在 Agent 蒸馏里把它扩展成“按步骤采样”——前两步多 teacher forcing,后面逐步切换成学生的实际动作,因为 Agent 任务的核心难点往往是长链路中的自我纠错。
这个方法的坑在于切换过程不能太激进。有次我把 teacher forcing 概率从 1.0 直接降到 0.5,训练 loss 立刻爆炸,学生完全跟不上。后来改成 cos 退火,从 1.0 渐进降到 0.2,效果稳定很多。我的建议是:scheduled sampling 的退火周期要覆盖至少一半的训练步数,越是长轨迹任务周期越长。
4.4 验证器重采样:把错误轨迹拉回来重新学
多轮 Agent 通常有比较明确的规则约束,比如工具调用的参数格式、动作类型的合法范围。我们可以利用这些规则写一个验证器,在学生跑完轨迹后判断每一步是否合法、是否成功。如果某条轨迹里出现了错误步骤,就把这条轨迹标记出来,针对错误位置做局部重采样。
我在实际项目里做过一个“错误轨迹增强”的流程:学生跑完 1000 条轨迹后,用验证器筛出失败的轨迹,把它们按错误点切短,只保留“错误发生前”的片段,然后把老师模型叫过来对错误点的状态做一次修正演示,用修正后的片段重新训练学生。这个做法等于在给学生开小灶,让它有机会在真实犯错的时刻看到老师怎么处理,针对性非常强。
这个方法比单纯增加数据量更有用,因为普通数据里正确轨迹占大多数,错误状态很少被覆盖。而用验证器定向重采样后,学生看到的高难度状态密度大大提升,训练效率翻倍。唯一要注意的是别把错误轨迹过度增强,否则学生会认为“犯错的下一步就是要被纠正”,反而学会了制造错误,我碰到过一次这种诡异情况。
4.5 容量匹配:小模型别硬蒸大模型的全部行为
多轮 Agent 领域有个尴尬现实:老师模型比学生模型大很多,学生根本没有足够容量承载老师全部的策略细节。硬蒸的结果就是老师每个行为的“影子”都学了一点,但没有一个学得像样。这时候应该做选择性蒸馏,只蒸馏最关键的决策边界,而不是所有token。
具体做法可以分为两步。第一步是给老师的轨迹做重要性打分,比如只保留那些“对环境状态产生关键转折”的动作,丢弃单纯的文本衔接部分;第二步是降低蒸馏温度,让训练更倾向于老师的确定性决策而不是概率分布里的噪声,防止小模型把精力花在拟合无关紧要的细微差异上。
我试过一个 7B 学生模型蒸馏 70B 老师的场景,一开始全面蒸馏多轮成功率只有 28%,改成只蒸馏关键决策节点后,成功率升到 51%。这不是因为数据变多了,而是因为学生终于不用花容量去学那些它根本学不会的边角行为。容量匹配的核心思想是:让学生把有限的参数花在刀刃上,而不是平均用力。
5. 多轮 Agent 蒸馏避坑速查表
5.1 常见现象、可能原因与排查方向
我把实际踩过的坑按“现象—原因—排查方向”整理成一张表,方便大家对照。
| 现象 | 可能原因 | 排查方向 |
|---|---|---|
| 训练 loss 下降,多轮成功率下降 | 状态分布漂移,学生进入老师未见区域 | 算状态覆盖率和分布对齐度 |
| loss 不降也不升,评估里错误集中在后段 | 老师监督信号在错误前缀下失效 | 过滤触发错误状态的样本,局部重采样 |
| 单步准确率高,完整任务成功率低 | 复合误差在长轨迹中累积 | 按完整轨迹成功率评估,统计首步错误率 |
| 训练 loss 震荡,梯度冲突 | 容量不足或信用分配冲突 | 做梯度诊断,尝试容量压缩或关键节点蒸馏 |
| 在线蒸馏效果反而更差 | 乐观系数过大,探索过度 | 回退乐观系数,检查状态是否进入非法区域 |
| 学生学得越来越保守 | 蒸馏温度过低,概率分布被过度平均 | 调整温度,加乐观偏置项 |
这张表的值不在于列得多全,而在于它能帮你快速缩小排查范围。我自己遇到“loss 下降但效果变差”时,90% 的情况最后都落在状态分布漂移上,可以直接跳到第三节的方法去计算状态覆盖率。
5.2 三个容易忽略的坑
第一个坑是评估只看最终答案,不看过程步骤。多轮 Agent 即使最终结果对,中间也可能绕了一大圈路,甚至用了不正当手段。如果你只盯着最终成功率,可能没发现学生的策略已经退化到每一步都在撞运气的地步。建议把过程指标单独统计,至少要看平均步数和中间状态合法性。
第二个坑是用 teacher forcing 评估,导致虚假高分。在推理多轮 agent 时,如果你每一步都把老师前缀喂进去,学生只需要做一步短预测,难度低很多。这种评估结果会让人误以为蒸馏很成功,一旦改成自己生成前缀,分数立刻崩盘。评估时一定要用自回归形式,让学生自己生成整条轨迹。
第三个坑是蒸馏温度调太高。很多人为了让学生学到老师概率分布里的丰富信息,把温度设到 2.0 甚至 3.0,这在单轮任务问题不大,但在多轮场景会把学生推成“什么动作都有可能输出”的状态,破坏策略的确定性。我的经验是温度在 1.0 到 1.5 之间比较稳,想要探索效果可以交给乐观项,而不是靠温度硬撑。
5.3 我最后想说的话
多轮 Agent 蒸馏“越蒸馏越差”,本质上不是蒸馏这个方法错了,而是多数团队还在用单轮时代的思路做多轮任务。你真正要蒸的不是“照着老师的每一步走”,而是“在不确定的状态下依然能做出可恢复的决策”。我这两周最大的体会是:先诊断分布漂移,再谈优化模型;先盯紧状态覆盖率,再去看 loss 曲线。如果只能给大家留一条建议,那就是别信离线数据里的漂亮曲线,跑到真实交互里去测一测,你会很快认清现实。