一条指令样本应该包含什么:消息角色、聊天模板与任务边界

成长演进

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

ℹ数字花园

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

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

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

一份 SFT 数据看起来很简单:左边是问题,右边是答案。真正送进模型时,却还要决定谁在说话、哪段是材料、回答从哪里开始、什么时候结束。

这些边界如果写错,训练也许还能运行,但学到的是另一种对话格式。更隐蔽的是,训练与上线使用不同模板,导致同样的自然语言内容变成不同 token 序列。

本篇沿用实验记录提取任务,把一条样本从“人看得懂”推进到“训练程序能正确解释”。示例中的模型名与实验材料仍是教学虚构。

一条完整样本先分清三类内容#

任务规则、待处理材料和示范答案各有用途。一个起步版本可以写成:

{
"messages": [
{
"role": "system",
"content": "从一项实验的材料中提取 model、learning_rate、steps。只返回 JSON 对象。model 为字符串或 null,learning_rate 为数值或 null,steps 为整数或 null。材料未明确记录的字段填 null,不补充推测。"
},
{
"role": "user",
"content": "实验材料:\n本次使用 Cedar-30M,学习率为 2e-4。记录未说明训练步数。"
},
{
"role": "assistant",
"content": "{\"model\":\"Cedar-30M\",\"learning_rate\":0.0002,\"steps\":null}"
}
]
}

这里 assistant 的 content 是包含 JSON 文本的字符串,所以外层数据文件需要对内部双引号转义。模型最后应生成的是那段 JSON 文本,不是把整个数据文件原样输出。

这一区别在实际处理中很重要:样本文件能被 JSON 解析,只证明外层记录合法;还应单独解析 assistant 内容,检查其字段和类型。

角色是结构信息,不是天然有效的按钮#

system、user、assistant 是消息结构中的角色标签。它们如何进入模型,由聊天模板决定;模型是否按预期使用它们,也依赖已有训练。

不要仅凭字段名就假定任何基础模型都懂 system 指令,也不要给不支持某种角色的模板强塞一条消息。不同模板可能合并角色、要求交替顺序,或对角色集合有具体限制。

对于已经做过聊天训练的模型,通常应从它配套的模板和约定开始。对于纯基础模型,新建对话格式则是训练设计的一部分,需要决定控制标记、结束方式和训练覆盖,不能当作无成本替换。

角色约定也不意味着材料中的文字被可靠隔离了。材料里若出现“忽略前面的要求”,我们希望它仍被当作待提取文本,而不是新的任务指令;这需要任务定义、数据覆盖和专门测试,不能只加一个标签就宣布问题解决。

聊天模板负责把结构变成一串 token#

模型最终看到的是 token 序列,不是 Python 字典。聊天模板负责把消息列表转换为带角色与边界的文本或 token。

可以用一个纯教学形式理解:

<SYS>任务规则<END>
<USER>实验材料<END>
<ASSISTANT>{"model":"Cedar-30M",...}<END>

这些尖括号标记只是示意,不能直接拿去替换某个模型真正使用的控制 token。两个模型即使来自同一基础模型,也可能采用不同聊天格式。Transformers 4.57.1 聊天模板说明

因此,一条可复现样本实际有三层:

层保存什么排查什么
任务记录原始材料、规则、答案与来源任务是否合理,答案是否有依据
消息列表每条消息的角色、内容与顺序轮次与角色是否正确
模型序列模板、token ID、位置与标签控制标记、截断、监督范围是否正确

只看第一层可能漏掉模板错误,只看最后一层又很难追溯答案为什么这样标注。三层需要能相互对应。

训练样本和生成提示的结尾不同#

训练时,我们有完整示范回答。推理时,通常只给出任务和材料,让模型开始生成 assistant 内容。

这两种情况的末尾应该按模板分别处理。已有完整回答的训练记录,不需要再额外开启一个空的新 assistant 回合;准备生成的输入,则可能需要表示“接下来由 assistant 开始”的前缀。

Transformers 的 add_generation_prompt 用于添加这类生成起始信息,但具体是否添加、添加什么,取决于模板。有的模型还支持续写最后一条消息,那又是另一种使用方式。生成提示约定

一个容易操作的检查是:把完整训练样本与实际推理提示并排打印。推理时的前缀,是否对应训练中答案真正开始之前的序列?如果不同,先解释原因,而不是直接开始训练。

在分词层面也要核对,不能仅凭显示出的文字看起来相同就推定 token ID 一致。

别把开始和结束标记添加两遍#

模板可能已经包含开始、结束或轮次边界标记。如果先渲染成字符串,再让分词器按默认行为额外添加特殊 token,就可能重复。

固定版本文档提醒:对 apply_chat_template(tokenize=False) 的结果再编码时,应避免重复添加特殊标记;直接由模板完成分词可以减少这个环节的混淆。聊天模板中的特殊 token

这里只说明接口约定,不提供“把所有特殊 token 都关掉”的通用规则。真正需要的是按模型模板核对实际输出:

  • 开始标记出现几次,位于哪里。
  • 回答的结束标记是否保留。
  • 多轮之间使用哪种边界。
  • 推理时停止条件是否识别相应标记。

结束一条消息与结束整个生成流程也不一定使用同一标记。不要因为它们显示得相似,就把编号与停止逻辑混用。

多轮样本应该反映真实可见的历史#

假设第一轮材料只有模型名和学习率,模型正确输出 steps:null。第二轮用户补充:“训练步数是 800。”这时新的回答可以更新 steps,同时保留此前两项。

训练记录需要包含第二轮作答时确实可见的历史。若只保留“训练步数是 800”这一句,却要求模型输出 Cedar-30M 和学习率,就把没有依据的填充教进去了。

反过来,也不能把第二轮才出现的信息放进第一轮输入,再评价第一轮“准确提取”。那改变了任务可见信息。

多轮历史还带来监督选择:训练全部 assistant 回答,还是只训练最后一轮?如果前面存在已知错误回答,是保留它作为纠错背景,还是也让模型模仿?这不是 JSON 格式能替我们决定的,下一篇会展开。

截断可能把一个正确样本变成矛盾样本#

一条消息文件没有问题,不代表截断后的训练序列仍然合理。

假设材料开头写模型名,末尾写学习率。为满足长度上限,程序截掉末尾材料,却保留了完整示范答案。模型现在被要求输出输入中已经不存在的信息。

另一个情况是截掉回答后半段,只留下半个 JSON。它可能仍然有若干有效标签,但已经不是完整提取任务的示范。

所以应在模板渲染和分词之后,检查最终长度与截断结果。可以设计几种处理政策:保留完整短样本、对长材料做有依据的分段提取、排除不完整记录,或另设长上下文任务。选哪一种取决于任务,不能靠默认截断方向代替设计。

统计时还应记录有多少样本被排除、主要来自哪类材料,否则长度处理可能悄悄改变任务分布。

元数据用来追踪,不应误进提示#

为了审计,我们会保存 source_id、标注版本、划分、审核状态和错误分类。这些字段很有用,但不都应该给模型看。

如果训练数据中的来源编号恰好与答案或类别相关,模型可能利用编号走捷径。若测试时没有该字段,表现就会掉下来。审核结论或答案提示更不能因为和消息放在同一个对象里,就被格式化函数一起拼进输入。

一种清晰组织方式是把输入消息与管理信息分开,并明确列出哪些字段参与渲染。训练前随机抽样打印最终提示,检查没有混入答案标签、划分名称或审核备注。

来源相同的改写、多轮拆分和合成变体应共享分组信息,以便划分训练与测试时放在同一侧。只按最终文本哈希去重,不能识别这些共同来源。

写一个小型样本验收集#

先准备十几条有明确预期的检查样本,比直接处理十万条记录更容易发现问题。可以覆盖:

样本情况应检查的结果
正常一轮对话完整回答和结束边界都保留
缺失步数assistant 中是 null,不是猜测值
材料里有引号、换行和代码外层转义不破坏实际内容
材料含角色样式字符串不被误当成新的消息边界
两轮补充材料后一轮能看到必要历史
超过长度上限按明确政策处理,并留下统计
模板不支持某角色明确报错或按已定义规则转换

每条都保存消息、渲染文本、token ID 与预期边界,再交给后续 loss mask 检查。若只保存最后一份 token 文件,问题会很难回溯。

这份验收集是用来检查数据转换的,不用于宣称模型具有这些能力。样本转换正确之后,还要训练并在独立材料上测试行为。

两个检查理解的问题#

第一,外层 JSON 可以成功读取,是否说明 assistant 的答案也一定是合法 JSON、字段也都正确?请列出还需要的两层检查。

第二,第二轮用户只补充步数,训练输入却删除了第一轮材料。模型被要求输出完整三字段,会学到什么不符合任务要求的行为?

下一篇讨论哪些 token 应该计算损失。模型能够看见一段文字,与我们要求它模仿生成这段文字,是两件不同的事。

文章概念网络

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

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

一条指令样本应该包含什么:消息角色、聊天模板与任务边界同一概念 / Shared concept同一概念 / Shared concept同一概念 / Shared concept同一概念 / Shared concept同一概念 / Shared concept同一概念 / Shared concept同一概念 / Shared concept同一概念 / Shared concept一条指令样本应该包含什么:消息角色、聊天模板与任务边…一条指令样本应该包含什么:消息角色、聊天模板与任务边界实验记录提取任务实验记录提取任务一条完整样本先分清三类内容一条完整样本先分清三类内容contentcontent角色是结构信息,不是天然有效的按钮角色是结构信息,不是天然有效的按钮system、user、assistantsystem、user、assistant聊天模板负责把结构变成一串 token聊天模板负责把结构变成一串 token聊天模板聊天模板Transformers 4.57.1 聊天模板说明Transformers 4.57.1 聊天模板说明训练样本和生成提示的结尾不同训练样本和生成提示的结尾不同addgenerationpromptaddgenerationprompt生成提示约定生成提示约定别把开始和结束标记添加两遍别把开始和结束标记添加两遍聊天模板中的特殊 token聊天模板中的特殊 token多轮样本应该反映真实可见的历史多轮样本应该反映真实可见的历史steps:nullsteps:null多轮历史多轮历史截断可能把一个正确样本变成矛盾样本截断可能把一个正确样本变成矛盾样本模板渲染和分词之后模板渲染和分词之后元数据用来追踪,不应误进提示元数据用来追踪,不应误进提示sourceidsourceid写一个小型样本验收集写一个小型样本验收集nullnull两个检查理解的问题两个检查理解的问题哪些 token 应该计算损失哪些 token 应该计算损失实验实验tokentokenSFTSFTtokentokenPythonPython推理推理tokentoken分词分词tokentoken分词分词推理推理选择选择分词分词去重去重利用利用状态状态tokentokentokentoken
一条指令样本应该包含什么:消息角色、聊天模板与任务边界

一条指令样本应该包含什么:消息角色、聊天模板与任务边界

回到原文 ↗
BIBTEX

引用本文

@article{2026-10-20-llm-training-sft-02-messages,
  author  = {枫语},
  title   = {一条指令样本应该包含什么:消息角色、聊天模板与任务边界},
  journal = {枫语},
  year    = {2026},
  month   = {oct},
  url     = {https://meiluosi.github.io/posts/2026-10-20-llm-training-sft-02-messages/}
}
🌐

本篇知识星丛与滑动侧栈

一条指令样本应该包含什么:消息角色、聊天模板与任务边界 当前主轴

搜索

正在载入索引…