Chat Template 是把结构化对话消息转换成模型 token 序列的规则。它定义 system、user、assistant、tool 等角色如何排列,特殊 token 如何插入,assistant 应该从哪里开始生成,以及哪些 token 在训练时参与 loss。对 chat model 来说,chat template 不是展示层格式,而是模型行为边界的一部分。

很多后训练问题表面上像“模型不听话”“工具调用格式不稳定”“多轮对话串角色”,实际根因可能是 template 错配:训练时模型学习了一套角色标记,推理时 serving 系统却使用另一套序列化方式。

目标与问题

Base language model 接收的是一串 tokens,并不知道 JSON 里的 role=userrole=assistant 是什么。Chat template 的作用是把高层 conversation schema 映射到模型可学习的文本/特殊 token 序列,使模型能够区分:

  • system 指令和普通用户请求;
  • 用户输入和 assistant 输出;
  • tool call 请求和 tool observation;
  • 当前轮应该生成的位置;
  • 对话何时结束。

因此,chat template 同时承担三个任务:角色编码、生成边界定义、训练与推理协议。

角色结构

常见角色包括:

Role含义训练/推理中的作用
system全局行为约束、身份、政策、输出风格优先级通常高于 user
user用户请求、上下文、输入数据作为模型生成 assistant 回复的条件
assistant模型应生成的回答SFT loss 通常只作用于 assistant tokens
tool外部工具返回结果作为上下文供 assistant 整合
developer开发者级约束或应用策略一些系统中位于 system 与 user 之间

不同模型家族对这些角色的支持不同。某些模型只显式支持 system/user/assistant;某些模型为 tool call 设计了专门 special tokens 或 JSON schema。

序列化示例

结构化消息:

[
  {"role": "system", "content": "You are a precise assistant."},
  {"role": "user", "content": "Return a JSON object with name and age."},
  {"role": "assistant", "content": "{\"name\":\"Ada\",\"age\":36}"}
]

可能被序列化为:

<bos><|system|>
You are a precise assistant.
<|user|>
Return a JSON object with name and age.
<|assistant|>
{"name":"Ada","age":36}<eos>

也可能使用 [INST]...[/INST]<start_of_turn><|im_start|> 等完全不同格式。重要的不是某一种格式更“自然”,而是模型训练时见过哪一种格式。

特殊 Token

Chat template 常用 special tokens 表示:

  • beginning-of-sequence / end-of-sequence;
  • role boundary;
  • assistant generation prompt;
  • tool call start/end;
  • tool result start/end;
  • stop tokens;
  • message separator。

特殊 token 的语义来自训练。一个 token 只有在 tokenizer 中存在且训练数据持续使用,才会成为稳定控制信号。随意更换特殊 token 或把它们当普通字符串处理,会造成模型无法识别边界。

Loss Masking

SFT 时通常只在 assistant response 上计算 loss:

<system> ...        mask = 0
<user> ...          mask = 0
<assistant> answer  mask = 1

原因是 system/user/tool tokens 是条件,不是模型应学习生成的目标。如果对 user tokens 也计算 loss,模型会被训练去复述或模拟用户;如果 assistant 起始 token mask 错误,模型可能学不到何时开始回答。

对于 tool-use 数据,mask 更复杂:

  • assistant 的自然语言和 tool call 参数通常需要训练;
  • tool observation 通常不训练,因为它来自外部系统;
  • 最终 assistant 总结需要训练;
  • 如果包含隐藏 reasoning trace,需要决定 trace 是否作为可见输出学习。

训练与推理一致性

Chat template 最重要的工程原则是:训练、评估、推理、数据生成必须使用同一套语义等价的模板。不一致常见于:

  • 训练数据使用 <|assistant|>,推理系统使用 Assistant:
  • 训练时有 system prompt,评估时没有;
  • SFT 时添加 <eos>,推理 stop sequence 没有包含它;
  • tool call 训练为 JSON,部署时使用函数调用专用 wrapper;
  • tokenizer 版本变更导致 special token id 不一致;
  • 多轮对话拼接时遗漏上一轮 assistant 结束标记。

这些问题会显著影响模型行为,却不一定反映在通用 benchmark 上。

与后训练方法的关系

  • SFT:chat template 决定输入输出序列和 loss mask。
  • Instruction Tuning:instruction 数据需要被 template 包装成模型认识的对话形式。
  • RLHF:policy rollout、reward model 输入和 reference model 输入必须使用一致模板。
  • DPO:chosen/rejected responses 必须在相同 prompt template 下计算 log-prob。
  • Rejection Sampling:采样候选时的 template 会影响候选质量和长度。
  • Tool-use training:模板就是函数调用协议的一部分。

常见失败模式

Role Leakage

模型输出类似 <user>System: 或继续生成下一轮用户消息,通常说明角色边界学习不稳或 stop token 配置错误。

Prompt Injection 边界弱

如果 system 与 user 在模板中没有清晰区分,或者训练数据没有强化优先级,模型更容易把用户文本中的“忽略前面指令”当作高优先级命令。

Tool Call 格式漂移

工具调用需要严格 schema。若训练中工具参数格式多样但推理系统只接受一种格式,模型会产生不可解析调用。

EOS / Stop Token 错误

遗漏 EOS 会导致模型继续生成多轮对话;stop sequence 过宽会截断合法输出;stop sequence 过窄会让模型泄露模板 token。

评估模板错配

同一个模型在不同 benchmark harness 中分数差异很大,有时不是能力差异,而是 chat template、few-shot 包装或 generation prompt 不同。

实践检查清单

  • tokenizer 中 special tokens 是否与模板一致;
  • 训练样本是否全部通过同一个 template 函数生成;
  • assistant 起始位置和 loss mask 是否正确;
  • 多轮数据是否包含清晰轮次边界;
  • 推理 serving 是否调用同一个 apply_chat_template 逻辑;
  • reward model / DPO / RL rollout 是否复用同一 prompt 格式;
  • stop tokens 是否覆盖 assistant 结束但不会误伤正文;
  • tool call 与 tool result 是否有可解析边界。

相关概念