我做过很长时间的普通SFT(监督微调)训练,基本套路很熟:收集问答数据、清洗、格式化、跑几个epoch、看loss、上线下线。可真把训练目标从“让模型会答题”换成“让模型能当智能体(Agent)去调用工具、看中间结果、决定下一步”之后,我发现自己过去的经验一下失效了大半。
这篇文章不是什么标准教程,而是把我持续记录“Agentic SFT”的思考、踩坑和一些隐约成立的经验留下来。标题里“浅薄认知”几个字不是客气话,是这个方向更新太快、我大多数结论都只能算是阶段性的。写出来是给同样在搭智能体训练流程的人做参照。
1. Agentic SFT和传统SFT看似同源,本质却是两个物种
1.1 我最开始的失误:把轨迹数据当成多轮对话喂进去
第一次做Agent训练,我犯了个很典型的错。当时拿了一批工具调用轨迹,把“用户提问、模型调工具、工具返回结果、模型再调工具、最后给答复”整条链路当成一种变长的多轮对话,直接套用了之前做对话SFT的模板和训练流。结果验证集上loss很不错,但真实跑Agent任务的时候,模型非常热衷调用第一个工具,之后就开始胡来:有的直接无视工具返回值开始编总结,有的反复调用同一个工具不推进任务,只有极少数样本能完整走完一条正确链路。
这个问题追踪到最后,我意识到根源在于“数据形态看起来一样,但学习的对象变了”。对话SFT学的核心是语言风格和内容质量,模型要预测的是“一段合理的人类回复”。而Agentic SFT里大量预测位置是“一个动作”——调用哪个工具、带什么参数、要不要停止调用开始总结。动作的合理与否,不仅取决于上下文和用户意图,还取决于外部工具实际返回了什么。模型必须学会读取返回值里的关键信息来修正下一步。
如果用一句话概括我当时的教训:普通SFT教的是“怎么把话说好”,Agentic SFT教的是“怎么把事情做对”。话好与事对,有时候一致,有时候完全不一致。
1.2 同样的交叉熵损失,为什么训练目标已经变了
表面看,损失函数还是那一个交叉熵,但拆开看内部标签的语义,一切都不一样了。
传统SFT的标签是“人类认为更合适的文本”。智能体轨迹里的标签,在动作位置上是“当时环境下最合理的动作选择”。这个“环境”很重要,它意味着:
- 如果工具返回了异常内容,下一步的最佳动作可能是“换一个工具”或者“向用户澄清”,而不是沿着正常流程继续走;
- 如果工具调用的参数填错了,最佳动作可能是“修正参数重试一次”;
- 如果已经能总结答案,最佳动作可能是“停止调用”,哪怕上下文里还能继续调。
这一层如果没想清楚,很容易直接用“成功轨迹”堆训练数据,并且把数据里每一步都当成唯一的黄金答案。模型学到的东西会趋向“复现成功路径的惯性”,却学不会“根据环境变化切换策略”。
1.3 我给自己重新下的定义
在持续记录里,我暂时把Agentic SFT的定位写成这样:在固定动作空间和观察反馈的条件下,从轨迹数据中学习“状态到动作”的行为策略。它保留了SFT的高效和稳定,但它本质是在模仿策略,而不是模仿文风。
这个认知帮我避掉了后面很多弯路。比如后文会提到:既然是在学策略,数据里就必须覆盖足够多的“状态”,特别是那些失败和异常状态,而不是覆盖足够多的“语气”。
2. 轨迹数据构造:我踩过且目前还在踩的坑
2.1 数据来源配比:人工、环境采样、大模型蒸馏各有各的用
Agent训练轨迹数据最常见的三个来源,我分别试过,各有明显的脾气。
| 数据来源 | 主要优点 | 主要缺点 | 我的建议占比 |
|---|---|---|---|
| 人工标注完整轨迹 | 质量最高、策略意图清晰 | 贵、慢、规模上不去 | 起步阶段必须有一批,三百到一千条就足够发现问题 |
| 环境自动采样后筛选 | 量大、覆盖真实环境状态 | 噪声高、大量轨迹是失败的 | 中期的扩充主力 |
| 用更强模型蒸馏轨迹 | 速度快、能覆盖边缘场景 | 可能带入模型自身偏见 | 适合冷启动和规则之外的兜底 |
我反复翻过车的地方是,一开始光顾着大模型蒸馏轨迹,因为量大又便宜。后面两个问题冒出来:
一是蒸馏来的轨迹里“应急处理”特别少。因为强模型本身能力强,很多在普通模型看来会导致失败的中间状态,它天然避开了。投给模型的数据全是“顺风顺水”型,模型根本没看过错误返回值长什么样,真正上线时一旦工具开始报错,动作链立刻断掉。
二是蒸馏轨迹里存在“假动作”,比如输入一个完全重复的工具参数,或者在根本无需调工具时调用工具刷存在感。这些噪声在蒸馏阶段不明显,但作为标签时会被模型当成一种风格去学习,形成很难改掉的坏习惯。
2.2 失败轨迹:一开始我全扔了,后来发现亏大了
之前做文本SFT接收过一条约定成俗的说法:尽量别拿坏样本训练,不然模型会学坏。这条在Agent轨迹上只对一半。
我后来把一组失败轨迹拆开做对比试验。同一批工具调用任务,基础模型主要用成功轨迹训练,在模拟环境的完成率仅35%。另一组我在成功轨迹里掺了15%左右的失败轨迹,但做了处理:在失败发生的位置之后,修正或拼接出“如何从失败中恢复”的动作。
效果提升非常大,对异常返回值的后续处理能力肉眼可见地变好了。现在的做法是:保留轨迹里导致失败的那个步骤和返回结果,把后面恢复过程的动作留下,组成半失败半恢复的轨迹。
用个类比:你不该让消防员只看“顺利救火”的视频,也应该让他看几百次“进屋发现门是锁的、破拆工具没带、对讲机信号断了”该怎么办。动作空间能不能用好,很大程度上取决于模型见过多少种异常状态。
2.3 格式与动作耦合:工具Schema不是装饰品
又踩了一个不起眼但很折腾的坑:数据里的工具Schema和系统Prompt必须当“动作约束”来看,而不是当说明文档。Agent动作空间某种意义上就是能调用工具的集合和规则,训练时如果工具Schema描述不够准确,模型会把参数名、参数类型、值域要求一起学错。
比如有个工具参数叫“target_date”,格式是YYYY-MM-DD。结果我有一部分蒸馏数据里填的是“明天”这类自然语言。模型立刻学会在参数位置输出“明天”,然后在运行时被工具解析直接拒绝;更闹心的是,这种错误很难靠推理修正,因为训练分布里已经存在了。
后来我把所有工具字段全量做了强制校验,标注工具自动规范化非法值。凡是参数值不符合Schema的数据要么改写、要么丢弃,绝不能让格式错误进入标签。
3. 训练过程中,我认为Agentic SFT必须单独处理的细节
3.1 掩码处理:工具观察结果到底该不该算损失
Loss掩码是Agentic SFT里最容易被忽略、影响却很大的设计点。
普通对话SFT里,标签全算Loss没有太大问题,因为所有内容都是模型应该生成的文本。但Agent轨迹不一样,整体结构通常是这样:
用户提问 助手调用工具(这条要学) 函数返回结果(这条不能学) 助手再调用工具(这条要学) 函数返回结果(这条不能学) 助手生成最终答案(这条要学)工具返回结果凭什么不能学?因为它不是模型生成的文本,而是外部环境写入的,模型在训练位点上根本不可能预测出来。如果不mask掉,模型会去背一些工具返回的随机报错信息、时间戳、编号之类,既浪费训练容量,也会干扰关键动作Token的梯度占比。
我见过一次很典型的案例:LossMask没做细,工具返回内容很长,导致整条样本里绝大部分Token都被外部内容占据。训练下来模型确实记住了某个工具返回的固定字段格式,但对“何时调用工具”这件事学得稀烂,因为动作Token数量少、梯度被稀释了。
3.2 截断策略:滚动截断很容易切断“观察-动作”对应
Agent轨迹天然是长文本,上下文窗口有限的条件下必须做截断。一开始用了一个通用做法:从开头开始,超长就丢弃前面的历史,只保留最后一段。结果训练完发现模型在中间的决策点经常“忘记”用户最初的指令是什么,尤其是工具调用比较多的时候。
后来把截断逻辑改成了以回合为单位的滚动截断,并且强行保证:只要进入样本,最后一条一定是一个完整的“观察-动作”对,前面可以丢,但这个不能断。
这里有个细节值得单独记录:截断时保留的窗口里应该有最近一次工具返回和随后的动作,因为这是模型最容易学习的“基于当前状态行动”的最小语义单元。如果窗口把工具返回截掉、只留动作,模型看到的动作就是“无源之水”,学出来的策略会变成瞎猜。
3.3 “假收敛”:Loss低到一定程度后,Agent能力反而下降
我还撞上过一种非常迷惑的现象,到现在想起来还觉得值得继续深挖。
跑一个中期训练任务时,Loss掉到很低,继续训练后Loss继续缓慢下降,但Agent完成率反而开始跌。一开始以为是过拟合,提前停掉以后,发现完成率也没回来多少。
分析下来,原因是这样的:当Loss很低时,模型对训练轨迹里的高频路径已经形成极强的先验。在评估集和训练集分布一致的场景下表现很好,一旦评估环境出现训练集里少见的返回状态,模型更倾向于按训练高频路径惯性行动,而不是根据当前观察去调整。
这和传统SFT的“过拟合”表现不同,更像是策略在动作空间上变得更尖、更窄。后来我给自己定下一个规矩:只看Loss还不够,每个训练阶段的Agent轨迹里,必须抽一批人工观察——看模型是不是会在不同环境下做出不同动作。如果动作模式越来越少、越来越僵硬,就该考虑做数据扩增或者提前停了。
4. 只靠 Agentic SFT,模型会卡在哪几道门槛
既然标题是“浅薄认知”,我也想把SFT这条路的边界如实写下来。以下四道门槛是我目前观察下来,纯SFT自身难以跨过的。
4.1 面对从未见过的状态,模型倾向于“照着最像的路径走”
SFT的本质是模仿,模仿决定了对已覆盖状态会有不错表现,但对覆盖之外的状态,模型会给一个“看起来最顺”的动作,而不是一个真正有规划的下一步。比如工具突然返回一个超出预期的权限错误,做过类似处理的模型可能会尝试换参数重试,但如果错误原因完全没进训练分布,它就站在原地打转。
4.2 连续失败时,很少能主动改变整套策略
智能体失败一次不可怕,可怕的是连续失败后不换策略。SFT模型容易在轨迹层面学习到一种“执着”:当前动作失败,它会微调参数再试,试两三次之后,依然不调用另一个完全不同的工具,也不转向用户求助。
这很像在没有监督信号的情况下,模型缺少对策略的整体评估能力。它知道每个局部步骤的“平均正确做法”,却缺少“这个方案已经僵死了,需要换方案”的高层判断。
4.3 “快速应答”和“多调几步”之间的平衡不容易学清楚
智能体动作空间里有一个重要的隐形参数:什么时候该停止调用工具输出答案。很多轨迹里正确答案已经出现,模型依然多调好几个无关工具;也有些情况是信息不足,模型却提前开始总结。这两个极端在纯SFT数据里很难靠标签数量去压制,本质是因为SFT阶段没有交互式价值反馈,模型分不清“多探索”和“少动作”哪个更符合当前场景。
4.4 对动作空间的利用存在局部性
还有一个现象是:训练数据里某个工具出现得频繁,模型就很会用;某个工具出现得少,即使其它地方触发了调用条件,模型也想不起来用。模型学到的“工具偏好”很容易被数据频次带偏,而真实的智能体任务往往要求按需均匀调度工具。
我目前唯一有效的缓解方式是长期维护一套工具调用频次统计,定期给低频工具补充合成轨迹,把分布拉平整一些。
5. 我对“浅薄认知”的自洽理解,以及持续记录的方法
5.1 SFT在Agent训练链路里更适合当“地基校准器”
被这几次实验教育之后,我对Agentic SFT的定位变成:它主要解决的是“让模型会用动作空间、能读懂观察结果、能按基本协议执行多步任务”。更高级的试错、规划、策略调整,更适合交给基于环境反馈的强化阶段去解决。
换句话说,先靠SFT把动作格式和基本策略学到足够好,再进入强化阶段效率更高,如果SFT阶段动作协议都没成型,强化阶段会花大量时间在“补格式”而不是“优化决策”。
SFT阶段应该贪的是对动作准确率的要求;至于在多变环境中的长期规划,我不再寄希望于单靠堆数据解决。
5.2 我维护训练笔记的方式:笔记最终会汇成数据集需求
“持续记录”对我而言不是写日记。我的实际做法是:失败案例、反常行为、模型能力突变的观察,全部记进同一个待办池,然后每周把笔记归类一次,转化成明确的数据需求或训练改动项。
举个例子,某次我发现模型非常喜欢在单步就能完成的任务里调用三个工具。记录下来之后,我去统计训练数据,发现确实有大量多步调用样本,主动单步完成的样本偏少。于是后续数据收集就开始大幅补充“一步到位”的样本,把这个偏差纠正过来。
小技巧是,记录时不要只写现象,要多写一句话:这说明训练数据主要缺了哪一块。这样笔记才不会停留在描述层。说白了,笔记就是数据集的“缺口清单”。
5.3 持续记录本身的三条小经验
坚持记这类训练笔记一阵子之后,我留了三条经验给自己:
第一,记录要快,失败现场尤其不能隔夜再补。当场记下模型输出里最畸形的动作,比事后看训练日志有效得多。
第二,定期用记录反向审计训练集。如果某个现象反复出现,通常意味着训练分布里有系统性的偏差,只是之前没察觉。
第三,阶段性地推翻自己也不敢偷懒。标题里的“浅薄认知”很重要,我定期回看旧记录,会发现自己当时的结论很粗糙,甚至是错的,这一般都是因为对Agent训练的理解又往前推了一小步。
写在记录中间的一个补充
如果这时有人问我Agentic SFT到底怎么看,我会把这个思路留着:他更像一面地基,把模型的“动作语法”校准好;但真正的策略智能需要环境交互反馈来完成。数据面注定要长期维护,只是不要一次性把所有希望都压在SFT这一步上。
“持续记录”最在意的不是某个时点认知有多完善,而是你有没有在每次实验后攒下新的观测。下一次再看这份记录,我大概率能对着某个段落笑出声——这和模型训练一样,只能先出一个版本,再靠迭代把它推好。