知识蒸馏实战指南:如何将大模型能力迁移到Agent小模型 先说个我最近常被问到的问题模型越做越大推理成本越来越高但业务的响应时延又卡在那里没法降。很多人第一反应是上量化、上剪枝、上各种推理加速框架但这些手段大多治标不治本。真正能让一个7B、3B甚至1B的模型在某些关键能力上逼近几十B大模型的是知识蒸馏。这词听起来学术味很重实际干起来也很费功夫但它解决的恰恰是工程上最朴素的需求——用更小的模型、更低的成本把大模型在特定任务上的“手感”继承下来。尤其是当你要部署一个Agent智能体时小模型的价值会被进一步放大Agent需要多轮工具调用、需要快速响应、需要高并发支撑这些场景下大模型的体积和速度往往是致命的而蒸馏恰好能帮你把大模型的一部分能力抽出来塞进一个小模型的身躯里。这篇文章我不打算讲学院派的理论推演只讲我在实际项目里怎么拆解蒸馏、怎么构造数据、怎么调训练细节、以及最后踩过哪些坑。如果你正准备把一个大模型能力迁移到一个小模型Agent上这篇文章应该能帮你少走不少弯路。1. 先搞清楚知识蒸馏到底在“蒸”什么1.1 从logit到soft label蒸馏的基本逻辑传统监督学习里训练一个小模型用的是硬标签hard label比如一张图片是猫还是狗答案就是“猫”或“狗”这种离散的、非0即1的标签。但人类认识事物从来不是这么武断的——一张图既像猫又有点像狐狸的时候你希望模型知道“它更像猫但也有一点点像狐狸”这种细腻的模糊信息。知识蒸馏的核心逻辑就是把这层“模糊信息”显式地传递给学生模型。我们通常把大模型叫作教师模型Teacher小模型叫作学生模型Student。教师模型在训练时不仅输出预测结果还输出一个概率分布这个分布里包含了它对每个候选类别的置信度。比如输入一句“帮我订明早8点去机场的出租车”教师模型可能给出“调用打车API”的概率是0.82、“查询日历”的概率是0.11、“直接回答”的概率是0.07。这个概率分布远比硬标签“调用打车API”这一个结果要丰富得多它隐含了教师模型对任务边界的理解。实现时需要考虑的一个重要参数是温度系数Temperature。蒸馏时我们用温度T对logits做软化处理[ q_i \frac{\exp(z_i / T)}{\sum_j \exp(z_j / T)} ]T越大概率分布越平滑类别之间的差异被压缩学生模型能学到教师模型在“接近但不确定”的样本上所表现出的犹豫感。T越接近0分布就越趋近于硬标签。常规做法是训练时对教师logits和学生logits用同一个较大的T比如4~8计算KL散度推理时再把T调回1。这个细节看起来轻描淡写但实际很多第一次做蒸馏的人都会卡在这里要么温度调太小导致蒸馏和普通训练没区别要么温度调太大导致小模型学到一堆噪声。1.2 蒸馏的三种模式离线、在线与自蒸馏蒸馏发展到现在已经从最初的“拿一个训好的教师去教学生”不断演化了不同场景下手段差异很大。离线蒸馏Offline KD最经典的模式。先把教师模型完整训练好冻结住然后让它对大量样本生成soft label再拿这些soft label去训练学生模型。好处是逻辑清晰、训练稳定坏处是教师模型的推理开销在数据标注阶段是一次性的但如果你有海量数据这个阶段会非常漫长。在线蒸馏Online KD教师模型和学生模型同时训练。教师模型可能是另一个初出茅庐的模型或者和学生共享底层编码器。典型做法如Co-Distillation两个模型互为师生、互相学习。这种模式适合你的场景里没有现成的强教师或者数据规模太大、离线生成成本过高的情况。自蒸馏Self-Distillation模型自己教自己。通常的做法是取模型深层的输出作为教师信号浅层的输出作为学生输出让浅层去拟合深层。这类方法在视觉模型里用得比较多文本模型里偶尔也会用主要解决的是深层特征利用率不足的问题。对于Agent场景我自己的经验是如果你已经有一个跑得动、效果也不错的超大模型比如几十B以上优先选离线蒸馏如果你的教师模型本身还在频繁迭代那就选在线蒸馏省得每次教师更新都重新生成一遍数据。1.3 为什么Agent场景更需要蒸馏很多人有个误解觉得蒸馏只是“用小模型模仿大模型”——听起来不是刚需毕竟大模型API也不是用不起。但放到Agent场景里你的判断会完全改变。Agent天然是延迟敏感型业务。用户说一句话Agent要做意图识别、槽位抽取、决策规划、工具调用、结果生成多个环节串行或并行地跑。如果每个环节背后都是一个大模型端到端延迟几百毫秒是家常便饭更关键的是成本——一次完整任务要调很多次大模型接口按token计费的账单很快就会让你肉疼。更重要的一个因素是高并发下的吞吐瓶颈。大模型的推理吞吐受限于显存带宽和KV Cache一台8卡机器可能只能同时服务几十个并发Agent会话。但如果你把核心环节压缩成一个小模型Agent同样的硬件可以轻松支撑几千路并发。在这种情况下蒸馏不是“锦上添花”而是“规模化落地的前提条件”。2. Agent小模型的特殊性蒸馏的“知识面”被撑大了2.1 Agent和普通小模型的差异传统意义上的蒸馏目标是让一个小模型学会大模型的“输入到输出”映射。但Agent比这复杂得多。一个Agent小模型要承担的角色至少包括理解用户意图判断当前该不该调用工具。在多个工具之间做选择并填充正确的参数。根据工具返回的结果做下一步决策。多轮对话中维护状态不丢失上下文。换句话说普通小模型学的是“问答”Agent小模型学的是“决策操作”。后者的输出空间是开放式的——不是说一个答案对错就可以而是要一连串动作都对中间任何一步出错都会导致任务失败。这也意味着蒸馏Agent能力时你不能只拿大模型的最终答案来当soft label还要让它把中间推理过程和工具调用轨迹一起“教”给学生模型。2.2 Agent知识蒸馏的三层知识任务知识、工具知识、规划知识我把Agent场景下的蒸馏知识拆成三层分别对应不同的蒸馏目标和数据形式第一层任务知识。这是最常见的一层也就是训练数据里的“用户指令-标准答案”对。在Agent场景里任务知识对应的是意图分类结果、从用户语言中抽取的槽位、以及对工具参数值的准确填充。比如用户说“帮我查一下北京到上海的航班”模型需要正确识别出出发地北京、目的地上海、查询对象航班。这部分知识通常用分类任务或抽取式任务来蒸馏loss直接用教师模型在该任务上的logits蒸馏到学生模型即可。第二层工具知识。工具知识指的是模型对“有哪些工具可用每个工具应该填什么参数什么情况下该用什么工具”的理解。在函数调用Function Calling的语境下这本质上是一个受限的生成任务模型需要输出一个格式化严格的工具调用文本比如search_flight(departure北京, arrival上海)。蒸馏时可以做序列级蒸馏Sequence-Level KD让学生模型在给定对话历史和工具定义的情况下生成与教师模型一致的工具调用序列。第三层规划知识。这是最值钱但也最难蒸的一层。一个Agent在面对复杂指令时可能需要先后调用几个工具才能完成任务。比如“帮我对比北京的天气和上海的天气再帮我把更暖和的那个城市的机票订一张”正确的做法是先调用两次天气查询再调用一次机票预订。这种多步规划能力通常来自大模型在广泛语料中习得的推理链。要蒸馏这一层常规做法是用大模型生成完整的思考链Chain of Thought或ReAct轨迹Thought-Action-Observation再让学生模型去模仿这条轨迹。有一点要特别提醒轨迹蒸馏很容易“学形式、不学逻辑”。学生模型如果只是机械记住了输出格式但在工具结果变化后不能做出适应性决策那就是失败的蒸馏。解决这个问题没有银弹主要靠数据多样性——你必须让教师模型生成足够多“中间步骤不同但结果等价或相近”的轨迹让学生模型真正学会规划而不是背答案。2.3 蒸馏数据从哪来合成、筛选与清洗蒸馏Agent能力的第一大难题不是模型训练而是数据。网上能拿到的开源Agent轨迹数据非常有限而且大多是英文场景、工具集也很固定。如果你要在自己的业务里蒸馏最靠谱的数据来源是“让大模型自己造数据”。具体做法大致是把你线上真实的用户请求脱敏后汇总起来作为种子数据。配上你定义的函数schema工具的定义说明。让教师模型针对这些请求生成完整的多轮Agent轨迹包括推理过程、工具调用、工具返回值分析、最终回复。把这些轨迹统一整理成微调格式。合成数据看起来省事但质量参差不齐必须做一轮筛选。我自己常用的过滤规则有三条一是请求成功率只有任务完成标记为成功的轨迹才保留二是语法检查工具调用的JSON必须能被解析且参数符合schema定义三是相似度去重如果一段轨迹和已有数据在语义上高度重复直接丢弃保持数据覆盖面。清洗这一环尤其关键。一次Agent轨迹数据里往往包含多轮工具调用如果其中某一步的工具参数填错了整条数据就是毒药不仅不能教会学生模型正确的行为还会把错误模式固化下来。我的做法是先用规则自动过滤掉明显解析失败的数据再抽样人工审核最后把通过率低于某个阈值的批次整体丢掉。3. 实操一个可落地的蒸馏流程3.1 准备工作与模型选型在动工之前先把两个模型定下来教师模型和学生模型。教师模型的选择标准很直接在你自己的业务评测集上效果足够好。有些团队图省事直接拿通用大模型的API当教师但你很快会发现通用模型在你业务里那种特定工具格式下经常输出不规范的JSON反而污染数据。相比之下一个针对你业务微调过的模型往往更稳。基于我的经验最佳实践是先微调出一个业务专用的教师模型再拿它去蒸馏。学生模型的选型我建议遵循一个原则在你能接受的延迟和显存范围内选最大的。蒸馏不是魔法它不能无中生有——一个1B模型再怎么蒸也很难继承70B模型复杂的多步推理能力但一个7B模型通过蒸馏逼近13B~30B模型在特定任务上的表现是完全可能的。业内有很多团队报告过类似的结论对于Agent任务7B级别的学生模型配合高质量蒸馏数据大致能继承教师模型70%到85%的能力且推理速度提升数倍。3.2 数据构造细节格式、字段、配比数据格式在整个蒸馏过程中是最容易被低估的一环。如果你的学生模型用的是AutoTokenizer和HuggingFace Trainer这套工具链数据格式一般保持和训练教师模型时一致即可。但Agent场景有一条额外要求工具定义是动态的、结构化信息很多你需要把工具列表也放入上下文中。以常用的对话格式为例我通常这样组织一条Agent训练样本{ messages: [ {role: system, content: 你是智能助手你有以下工具可用tool_schema}, {role: user, content: 帮我订一张后天去上海的机票最好下午两点前到达。}, {role: assistant, content: 我来帮你查一下航班信息。, tool_calls: [{name: search_flight, arguments: {from: 北京, to: 上海}}]}, {role: tool, name: search_flight, content: 以下航班符合要求CA1501 到达14:30...}, {role: assistant, content: 找到合适航班CA150114:30到达。} ], loss_mask: [0, 0, 1, 0, 1] }loss_mask这个字段是我强烈建议你加上的。在计算loss时只对student生成的部分做反向传播system prompt、user输入、工具返回的这些内容一律mask掉。如果忘了做mask模型会花大量容量去记忆“预测用户说了什么”而不是学会“怎么调用工具”。数据配比上我踩过的坑也不少。最合理的做法是混合多类数据纯对话数据帮助模型保持基本对话能力占比30%~40%。单轮工具调用数据帮助掌握工具格式占比30%~40%。多轮工具调用轨迹帮助学习规划能力占比20%~30%。少量困难样本、负样本不该调用工具的场景模型要学会不调工具直接回复占比5%~10%。负样本极其重要。很多蒸馏出来的Agent模型会有乱调工具的病核心原因就是训练数据里全是“需要调工具”的样本模型没见过“不需要工具”的场景。这一点务必要在数据里刻意留出位置。3.3 训练配置与损失函数组合数据准备妥当之后进入训练环节。Agent蒸馏的loss一般不是单一的交叉熵loss而是多个loss的组合。我常用的做法是logits蒸馏loss加序列级CE loss加工具调用字段的重点加权。import torch import torch.nn.functional as F def kd_loss(teacher_logits, student_logits, temperature4.0): teacher_probs F.softmax(teacher_logits / temperature, dim-1) student_log_probs F.log_softmax(student_logits / temperature, dim-1) # 对教师概率做stop_gradient是关键 loss F.kl_div(student_log_probs, teacher_probs.detach(), reductionbatchmean) return loss * (temperature ** 2) def ce_loss(student_logits, labels, loss_mask): # 只对有效token位置计算交叉熵 vocab_size student_logits.size(-1) logits_flat student_logits.view(-1, vocab_size) labels_flat labels.view(-1) mask_flat loss_mask.view(-1).float() losses F.cross_entropy(logits_flat, labels_flat, reductionnone) return (losses * mask_flat).sum() / mask_flat.sum().clamp(min1.0)训练时几个关键参数我的默认值是这样的temperature4.0到8.0之间小模型容量低时用偏高的温度让软化后的分布更平滑但温度也不能太高否则小模型会连教师模型的“错误自信”也一起学了。KD loss权重0.5到1.0。如果教师模型和学生模型的能力差距太大比如70B蒸馏到1B建议把KD权重调低到0.3否则学生会拟合不动loss震荡严重。学习率1e-5到2e-5比普通SFT要低一些。因为soft target本身已经包含了很多信息过大的学习率容易让学生模型在一个batch内被某几个异常样本带跑偏。Epoch数2到3个就够了多轮重复训练Agent轨迹数据极易过拟合表现为模型开始”背题“而不是学会工具调用的泛化逻辑。另外一个经常被人忽略的细节冻结部分参数。如果学生模型的backbone是基座模型在训练早期冻结前几层只更新后面几层和输出头可以大幅减少灾难性遗忘的风险训练也更稳定。等loss降到一定程度后再解冻全部参数做第二段训练。3.4 评估体系不能只用准确率Agent蒸馏的评估比普通模型评估复杂得多因为单个指标很难描述这个模型到底好不好用。我强烈建议搭建一套分层评估体系第一层工具调用格式正确率。让模型针对一批测试指令只输出一次工具调用用JSON Schema校验工具名和参数格式是否正确。这一项不过关就没有继续评估的必要了。第二层单步任务成功率。针对需要单次工具调用的指令端到端执行一遍流程看模型能否正确选择工具、填入参数、并根据工具结果生成合理回复。第三层多轮轨迹任务成功率。拿复杂的、需要调用多个工具的任务去测标准是任务最终完成且关键路径工具调用顺序、参数与大模型教师给出的参考轨迹一致。我自己习惯用tool_call_acc工具调用准确率和plan_accuracy多步规划准确率作为两个核心的北极星指标。还有一个额外建议评价时一定要对比学生模型蒸馏前后、以及教师模型本身的表现三组数字放一起才能看出蒸馏到底带来多少增益。否则你很容易被“蒸馏后效果不错”这种幻觉误导——很可能只是任务太简单基座模型本来就能做。4. 踩坑记录我见过最多的五类翻车现场4.1 温度系数调不好学生模型“学歪了”温度对蒸馏效果影响非常大但这个参数很多人都是随手填的。我在一个Agent蒸馏项目里最开始用T2.0结果学生模型在工具调用参数上频繁出现边界模糊的问题教师模型对参数置信度是0.9和0.1的区别但T2.0的softmax拉平后变成0.7和0.3学生模型的参数选择明显不如教师果断。后来把T调到6.0软化后的分布拉开了一些参数准确率提升明显。如果你发现学生模型的输出变得“模棱两可”——不确定该填什么参数、不确定该不该调工具——优先怀疑温度没有调好。4.2 数据全是“简单题”能力迁移了个寂寞我第一次做Agent蒸馏时合成数据来自大模型对种子请求的回应但种子请求大多只有一种标准解法。这导致学生模型面对新指令时一旦遇到稍长的上下文或多步工具需求就直接崩掉。核心原因是数据覆盖不够教师模型在简单任务上的表现本来就不需要蒸馏就能逼近真正拉开差距的是复杂任务。解决办法是刻意在合成数据时加扰动给同一用户指令生成多个合法变体变换措辞、变换槽位值、变换工具调用顺序。教师模型能力允许的情况下让它在一条指令上生成多条成功的轨迹再随机抽取进入训练集。4.3 灾难性遗忘蒸馏后连基础指令都听不懂蒸馏Agent数据占比过高、而且全是工具调用格式数据时学生模型很可能会出现“工具调用能力上来了但基础对话能力崩了”的现象。你问它“你好”它回你一段JSON调用。两个解决思路一是按我之前提到的配比混入足够多的通用对话数据比例不要低于30%二是在训练过程中间做增量评测每几百个step在通用指令集上测一次。一旦发现通用能力掉的幅度超出你设定的阈值马上回滚到之前合适权重存档而不是硬着头皮把整个训练流程跑完。4.4 评估指标的安慰剂效应有的团队做蒸馏蒸馏到一半发现准确率很高上线之后却被用户反馈说“变笨了”。我后来复盘发现评测集和训练数据高度重合说白了模型已经把测试集答案背下来了。更隐蔽的问题是评测集里的工具调用场景过于单一模型只要记住了几个特定格式的模板就能拿到极高的通过率。建议评估集和训练集在数据来源上严格隔离而且评测数据要保持新鲜——线上真实用户请求中定期抽取一批新样本加入测试集才能持续反映模型的真实能力。4.5 小模型容量不够硬蒸馏反而掉点蒸馏不是在所有情况下都能带来正向收益。如果你的学生模型太小比如1B以下而教师模型的能力高到“完全超出学生容量的表达上限”强行蒸馏时学生模型会处于一个很尴尬的境地它既没法完全拟合教师输出又被soft label干扰了原本在硬标签上的学习。这种情况的典型表现是蒸馏后的模型在训练集上loss降不下去同时基线能力还在掉。我在某个对比实验中见过一个0.5B的模型做普通SFT时工具调用能力是70分拿一个大模型蒸馏它反而降到了55分。后来换成能力更接近的教师模型或者减少KD loss权重、加大CE loss权重情况才好转。5. 一点私人经验做了这么多蒸馏项目我最深的体会是知识蒸馏的瓶颈不在于算法而在于数据质量与任务边界的设计。同样的学生模型喂给它高质量的教师轨迹数据和不加筛选的合成数据最终效果可能差出一倍。与其花时间去调那些花哨的蒸馏loss不如把数据链路夯实——种子指令的覆盖面、教师轨迹的筛选规则、负样本的配比这些才是真正决定Agent蒸馏成败的地方。另外一个小技巧收尾分享项目初期不要急着蒸馏完整能力先挑一个最核心的单工具调用场景把一条“教师产出轨迹-筛选清洗-SFT训练-评测”的流水线跑通。这段流程跑顺了后面再扩展到多工具、多轮场景就只是数据的横向扩展问题而不是重新摸索。先小步快跑再逐步放大这是我在Agent蒸馏项目里一以贯之的节奏。