怎样用 SFT 学会工具接口:格式、参数与任务完成要分开检查

成长演进

具备清晰骨架、初步数学推导或工程验证,持续补充论据与实测。

ℹ数字花园

🌿 什么是数字花园与思想演进?

遵循 Maggie Appleton 与 Andy Matuschak 的常青笔记理念:这里不是按时间沉底的流水账,而是持续修剪打磨的知识生态。文章按成熟度经历:

  • 🌱 萌芽 (Seedling): 发散灵感、读书摘录、开放猜想
  • 🌿 演进 (Budding): 结构渐明、初步代码推导、积极修正
  • 🌳 常青 (Evergreen): 逻辑闭环、持久经得起检验的基石
  • 🔬 实验待证 (Speculative): 结合沙盒与仿真探针的理论探索
浏览全站数字花园与笔记全景 →

上一篇已经有一条完整示范:用户需要 3 件 A17,先查杭州;杭州只有 2 件,再查苏州;苏州有 5 件,最后报告苏州满足需求,没有预订。

现在想让模型学会这件事。最容易想到的办法,是准备很多类似记录,做监督微调。但“记录放进去了”和“正确行为被监督了”之间,还有几处容易漏掉的细节。

比如,工具调用可能存放在 tool_calls 字段里,而预处理只读取 content。训练仍然能跑,损失也能下降,可模型根本没有见到需要学的调用内容。又比如,模型学会了合法 JSON,却总把仓库写成苏州;接口能执行,用户要求的顺序却错了。

本篇沿用只读库存任务,把数据、损失和评测逐层拆开。数字与样本都是教学设定;配套脚本只核对监督范围和算例,没有执行 SFT 训练。

先把三个“正确”分开#

格式正确,表示生成结果能被接口解析。例如它包含工具名、参数对象和必要字段。

参数正确,表示调用符合当前请求与已知历史。warehouse="SZ" 是合法值,但在本例的第一步就查询苏州,违反了“先杭州”的约定。sku="A18" 同样可能是合法商品,却不是用户要的 A17。

任务完成,还要把一连串动作和最终回答放在一起看。两个查询都正确,最后却说“已为你预订”,仍然失败。上一篇的执行器没有任何预订操作。

这三个层次不能只用“调用准确率”一个名字带过。本文在评测时先定义检查项目,再报告分母,不把解析通过的样本筛出来以后,就忘记其余失败。

一条轨迹实际提供了三次决策示范#

不要先从训练框架参数开始。先问:在每个时刻,模型能够看到什么,应该生成什么?

决策当时可见的内容本次监督目标
第一次调用用户请求、工具说明查询 A17 的杭州库存
第二次调用上述内容,加第一次调用和“杭州 2 件”查询 A17 的苏州库存
最终回答上述内容,加第二次调用和“苏州 5 件”报告苏州满足需求且未预订

这里的监督目标是模型应输出的动作或回答,工具结果则提供条件。训练第二次调用时可以看见杭州数量,不能看见还没查询到的苏州数量。

也不要把最终答案写进一个“辅助说明”字段,再无意中拼到每一步输入里。字段名字叫 metadata,并不保证它不会被模型看到;只有实际渲染后的输入能回答这个问题。

两种组织方式,都要明确损失算在哪里#

第一种是保留完整对话,让模型在因果注意力下预测各个 assistant 片段。序列中虽然存在后续内容,正常的因果掩码会阻止当前位置看到未来。工具返回片段保留在输入中,但在本篇选定的训练目标下不计预测损失。

第二种是把每次决策单独拆成“历史前缀→下一条 assistant 消息”。三个训练样本各自只监督最后那一次决策;前缀里以前的 assistant 消息也只是条件。

这两种方式并不天然等价。拆分会重复读取历史;如果每个样本等权,短调用与长回答的相对权重也可能改变。更常见的错误是:拆成前缀样本后,仍然监督前缀里所有 assistant 消息。第一次调用就会在三份样本中被算三次,第二次调用算两次,最终回答只算一次。

TRL 0.23.1 文档分别说明了 completion-only 与 assistant-only 的损失设置,并要求相应模板支持 assistant token 掩码。工具训练样本还需要保存调用、结果和可用工具列表。选项名称不能代替实际检查。SFT Trainer 固定版本文档

本文不提供一个未经运行的“通用训练配置”。同一个开关遇到不同数据形态、模板和版本,监督范围可能不同。真正训练前,应把一条样本的 token、标签和被计分区间一起导出。

用一组小数字看清重复监督#

假设三段需要学习的内容,分别有 4、6、10 个目标 token,平均负对数似然分别为 0.5、1.0、0.2。这里只是人为设定的长度与损失,不是对实际中文或 JSON 的分词结果。

若完整轨迹中的每个目标 token 只计一次,总共 20 个目标 token,总损失和为 10,平均损失为 0.5。

若拆成三个前缀样本,却错误地再次监督历史 assistant 片段,计数会变成:第一段 4×3,第二段 6×2,第三段 10×1,共 34 个目标 token。三段加权后的损失和分别是 6、12、2,因此:

S=6+12+2=20,L=2034≈0.5882.\begin{aligned} S&=6+12+2=20,\\ L&=\frac{20}{34}\approx0.5882. \end{aligned}

这不是模型突然退步,而是你换了样本权重。早期调用被重复强调了。

即便没有重复监督,把三次决策的平均损失直接等权平均,也得到约 0.5667,而不是 0.5。前者让每次决策等权,后者让每个目标 token 等权。两者可以有各自的用途,但要写出选择,不能把它们当成同一个数比较。

更一般地,token 平均目标可以写成:

ℓt=−log⁡pθ(yt∣y<t),L=∑tmtℓt∑tmt.\begin{aligned} \ell_t&=-\log p_\theta(y_t\mid y_{<t}),\\ L&=\frac{\sum_t m_t\ell_t}{\sum_t m_t}. \end{aligned}

这里的前缀包含已经渲染的请求、工具说明和历史;m_t 为 1 表示这个目标位置计分,为 0 表示不计分。没有任何目标位置的样本不能直接拿零分母计算。结束标记是否纳入监督,也要按模板明确,不能把停止行为漏掉后再期待模型自然停止。

真正应该查看的是渲染后的输入#

数据表里字段齐全,还不足以证明模型能读到它们。聊天模板决定这些字段怎样变成实际 token 序列。

Transformers 的模板文档说明了工具定义、调用与结果需要由模板处理。某些模型使用特定边界标记,工具调用也不一定以普通回答的形式渲染。应检查与目标模型配套的模板,而不是只看原始 JSON。聊天模板编写文档

对第一条样本,至少手工核对下面四处:工具名和参数是否还在;杭州返回值是否位于第二次调用之前;工具结果有没有被误标为 assistant;调用片段与结束位置是否确实获得了训练标签。

再做一个简单变化:把杭州库存从 2 改成 3。正确示范应在第一次查询后结束,不能继续保留查询苏州的旧目标。只改输入、不改标签,会制造相互矛盾的数据。

长轨迹还会遇到截断。若截掉第二次调用,却保留它的结果,历史就失去了配对;若截掉工具定义,剩下的请求可能无法解释。先检查样本在真实长度限制下还剩什么,再决定按完整决策边界切分、缩短环境输出,还是舍弃样本。把最大长度调小并不是纯粹的性能优化。

数据要覆盖分支,不能只把成功句子换个说法#

库存任务至少有三类正常分支:杭州足够,杭州不足但苏州足够,两个仓库都不足。还应有不需要调用的说明性问题,以及信息缺失时需要澄清的请求。

样本变化想教会的行为容易学到的捷径
杭州刚好 3 件达标后停止只记“杭州通常不足”
两地都不足如实报告不足每次都推荐苏州
商品编号改变从用户请求提取参数永远填 A17
只问字段含义直接解释已有说明每次都查库存
缺少需求数量询问数量默认猜 3 件

这些是覆盖要求,不是建议把所有类型机械地平均分配。部署请求的分布、错误代价与稀有分支的学习难度,会影响配比。记录每类数量和抽样方式,比声称“数据很多样”更有用。

把商品名随机替换几百次,也不自动得到几百个独立测试问题。同一轨迹骨架的近重复样本应该按家族划分,避免一部分进训练、一部分进测试后,夸大对新场景的适应能力。

错误示例不能不加区别地当成正确目标#

假设旧模型先查了苏州,后来被指出顺序错了,才重新查杭州。整段轨迹里既有错误动作,也有恢复动作。

如果对所有 assistant 消息照常计算 SFT 损失,早先的错误调用也会被鼓励。想学习恢复,可以把错误历史保留为输入,只监督经过核对的修正动作;想学习首次就做对,则另外构造正确初始决策。两种目的应该分开标注。

更不能为了让错误轨迹“看起来成功”,手工把工具返回值改成能支持原动作的数字。修正示范要在一致的环境里重新核对,而不是修改证据来迎合答案。本卷后面会专门讨论失败轨迹的利用。

离线预测正确,不等于实际执行成功#

离线检查时,可以给模型一段正确历史,让它预测下一步。这能定位模型会不会在“杭州 2 件”之后查苏州,但它绕过了一个问题:模型自己生成的第一步是否就已经错了?

闭环评测则让模型从用户问题开始,自己生成调用,交给环境执行,再读取真实结果继续。错误会影响后续历史,不再由标准答案替它铺路。

设想一个人为构造的 100 次首步调用检查:90 次满足接口格式,其中 72 次参数与当前请求正确。在另一个覆盖这些任务的完整交互评测里,最终有 54 个任务成功。这里可以报告格式通过 90/100、首步语义正确 72/100、条件语义正确 72/90,以及任务成功 54/100;不能只说“准确率 80%”。

这组数字只是说明分母,不是实测。真实系统里还可能有多次调用、允许重试和无需工具的任务,因此调用级与任务级指标不能随意相乘。一次错误能否恢复,也由任务协议决定。

怎样设计一轮有解释力的小实验#

先固定基础模型、数据划分、模板、环境和解码设置。保留未微调模型作为基线,再比较“只监督最终回答”与“监督调用及最终回答”的版本。工具结果在两种训练条件里都保留为上下文,避免同时改变可见信息。

但前者无法直接学到调用片段;闭环差异也可能来自基础模型原本会不会调用。因此同时报告有标准历史的逐步测试与从头执行的任务测试。训练预算还应注明按总读取 token、目标 token 还是更新步数对齐,这些口径不总能同时一致。

这是一项待运行的实验设计。本文实际完成的是小算例复算:20 与 34 个目标位置的计数、两种损失平均、历史重复监督的次数,以及示范前缀不包含未来事件的检查。它帮助排除数据管线错误,不能支持“某种 SFT 配方提高了多少成功率”的结论。

开始训练前,应当能拿出一条渲染输入、一份标签范围、一份分支覆盖表,以及一条从请求到结束的评测日志。如果这四样还解释不清,先扩大数据或提高训练步数,往往只是把同一个错误重复更多次。

文章概念网络

从章节进入正文概念。实线表示所属层级,虚线连接跨章节重复出现的概念,不代表因果关系。

50 / 50 个节点 · 正文标记术语与关键词概念

怎样用 SFT 学会工具接口:格式、参数与任务完成要分开检查同一概念 / Shared concept同一概念 / Shared concept同一概念 / Shared concept同一概念 / Shared concept同一概念 / Shared concept同一概念 / Shared concept同一概念 / Shared concept同一概念 / Shared concept同一概念 / Shared concept同一概念 / Shared concept同一概念 / Shared concept同一概念 / Shared concept同一概念 / Shared concept怎样用 SFT 学会工具接口:格式、参数与任务完成要分开检…怎样用 SFT 学会工具接口:格式、参数与任务完成要分开检查toolcallstoolcallscontentcontent先把三个“正确”分开先把三个“正确”分开格式正确格式正确参数正确参数正确任务完成任务完成一条轨迹实际提供了三次决策示范一条轨迹实际提供了三次决策示范监督目标监督目标两种组织方式,都要明确损失算在哪里两种组织方式,都要明确损失算在哪里SFT Trainer 固定版本文档SFT Trainer 固定版本文档用一组小数字看清重复监督用一组小数字看清重复监督样本权重样本权重真正应该查看的是渲染后的输入真正应该查看的是渲染后的输入聊天模板聊天模板聊天模板编写文档聊天模板编写文档数据要覆盖分支,不能只把成功句子换个说法数据要覆盖分支,不能只把成功句子换个说法错误示例不能不加区别地当成正确目标错误示例不能不加区别地当成正确目标离线预测正确,不等于实际执行成功离线预测正确,不等于实际执行成功闭环评测闭环评测怎样设计一轮有解释力的小实验怎样设计一轮有解释力的小实验工具调用工具调用SFTSFT微调微调动作动作动作动作tokentoken因果因果注意力注意力SFTSFTtokentoken分词分词选择选择tokentoken工具调用工具调用环境环境优化优化近重复近重复机械机械动作动作SFTSFT利用利用环境环境语义语义环境环境tokentokenSFTSFT微调微调环境环境实验实验
怎样用 SFT 学会工具接口:格式、参数与任务完成要分开检查

怎样用 SFT 学会工具接口:格式、参数与任务完成要分开检查

回到原文 ↗
BIBTEX

引用本文

@article{2026-10-23-llm-training-tool-02-supervision,
  author  = {枫语},
  title   = {怎样用 SFT 学会工具接口:格式、参数与任务完成要分开检查},
  journal = {枫语},
  year    = {2026},
  month   = {oct},
  url     = {https://meiluosi.github.io/posts/2026-10-23-llm-training-tool-02-supervision/}
}
🌐

本篇知识星丛与滑动侧栈

怎样用 SFT 学会工具接口:格式、参数与任务完成要分开检查 当前主轴

搜索

正在载入索引…