为什么需要聊天模板
你有没有想过这样一个问题:当你在 ChatGPT 或者其他聊天界面里打字对话的时候,你输入的文字到底是怎么传给模型的?
你可能觉得——不就是把我打的字直接喂给模型吗?
没那么简单。
大语言模型本质上是一个「文本续写器」——给它一段文本,它预测下一个最可能出现的 token(关于 token 的概念,可以看 Token 到底是什么 这篇)。它并不天然理解「这段话是用户说的」「那段话是AI回复的」这种对话结构。
所以,你的输入在传给模型之前,需要被包装成一种特定的格式,告诉模型:这里是系统指令、这里是用户说的话、这里是你之前的回复——现在该你继续说了。
这种格式,就叫做聊天模板(Chat Template)。
而 ChatML,就是其中一种非常流行的聊天模板格式,也是 Hermes 系列模型默认使用的格式。
ChatML 的由来
ChatML 最早是 OpenAI 提出的。没错,就是做 GPT 系列的那个 OpenAI。
2023 年,OpenAI 在其 API 文档中定义了一种叫 ChatML(Chat Markup Language)的格式,用来结构化多轮对话。虽然 OpenAI 后来并没有大力推广这个格式的标准化,但它的设计简洁直观,很快被开源社区采纳。
Nous Research 的 Hermes 系列从 Hermes 2 开始就采用 ChatML 作为默认聊天模板,并且一直延续至今。在开源社区里,ChatML 已经成为了最广泛使用的聊天模板格式之一。
ChatML 长什么样
直接看一个例子,一切就明白了:
1 | <|im_start|>system |
这就是一个最基本的 ChatML 格式的对话。拆开来看:
特殊标记
<|im_start|>:一条消息的开始标记(im = internal message)<|im_end|>:一条消息的结束标记
这两个标记是特殊的 token,不是普通文本。模型在训练时专门学习了这些标记的含义。
三种角色
紧跟在 <|im_start|> 后面的是角色标识:
- system:系统角色。用来设定模型的行为、身份、规则
- user:用户角色。就是你说的话
- assistant:助手角色。就是模型的回复
完整结构
一条消息的完整结构是:
1 | <|im_start|>角色名 |
注意:角色名后面有一个换行符,然后才是消息内容。消息内容结束后紧跟 <|im_end|>。
多轮对话的格式
实际使用中,对话往往不止一轮。来看一个多轮对话的例子:
1 | <|im_start|>system |
整个对话历史会被拼接成一个长字符串,按时间顺序排列。最后一条消息以 <|im_start|>assistant 结尾(没有 <|im_end|>),这就是在告诉模型:「轮到你说话了,请继续生成文本。」
模型看到这个格式,就知道:
- 我的角色设定是「友好的AI助手」
- 用户先问了天气,我回复了无法获取
- 用户又问了我能做什么,现在该我回答了
System Prompt 的作用
三种角色中,system 可能是最容易被忽视但最重要的。
System prompt 就是你给模型的「人设说明书」。你可以用它来:
定义身份
1 | <|im_start|>system |
设定行为规则
1 | <|im_start|>system |
控制输出格式
1 | <|im_start|>system |
Hermes 特别擅长遵从 system prompt。 这是 Hermes 系列的一个核心卖点——很多模型虽然也支持 system prompt,但在实际使用中经常「忘记」或者不严格遵守。Hermes 在这方面的表现明显更好,这和它的「个体对齐」训练理念有关。具体可以看 Hermes 的个体对齐哲学 那篇。
为什么 Hermes 选择 ChatML
现在开源社区里的聊天模板格式不止 ChatML 一种,常见的还有:
Llama 格式
1 | <s>[INST] <<SYS>> |
Alpaca 格式
1 | Below is an instruction that describes a task... |
Vicuna 格式
1 | USER: 你好 |
相比之下,ChatML 有几个明显的优势:
1. 结构清晰
<|im_start|> 和 <|im_end|> 是明确的分隔符,不容易和正文内容混淆。你在消息内容里不太可能正好写出 <|im_start|> 这样的字符串(而 Alpaca 格式里 ### Instruction: 在正文中出现的概率就大多了)。
2. 角色扩展性好
ChatML 的角色不是固定死的。除了 system、user、assistant 这三个标准角色,你还可以定义其他角色:
1 | <|im_start|>tool |
这对做函数调用和工具使用的场景非常有用。Hermes 2 Pro 开始引入的 Function Calling 就依赖于这个扩展能力。
3. 多轮对话处理自然
ChatML 的多轮对话就是把消息一条条拼接起来,逻辑直观,不需要复杂的嵌套结构。
4. 社区生态成熟
因为 Hermes 和很多其他模型的采用,ChatML 在开源社区里的工具支持非常完善。Ollama、llama.cpp、vLLM、text-generation-inference 等主流推理框架都原生支持 ChatML。
实际使用中你需要注意什么
大多数情况下你不需要手写 ChatML
如果你通过 Ollama、Open WebUI 之类的工具使用 Hermes,这些工具会自动帮你处理 ChatML 格式。你只需要正常打字聊天就行,不用操心底层的模板格式。
什么时候需要关心 ChatML
以下几种情况你需要了解 ChatML:
1. 直接调用模型 API 时
如果你用代码直接调用模型的推理接口(比如通过 vLLM 的 completions API),你可能需要自己组装 ChatML 格式的输入。
2. 写 system prompt 时
虽然前端界面会帮你处理格式,但了解 system prompt 在 ChatML 中的位置和作用,有助于你写出更好的 system prompt。
3. Debug 问题时
如果模型的回复出了问题(比如不遵从指令、输出格式混乱),查看实际发送给模型的 ChatML 格式文本往往能帮你发现问题。可能是格式拼装错了,也可能是 system prompt 写得有歧义。
4. 做二次开发或微调时
如果你要基于 Hermes 做进一步的微调训练,你需要确保训练数据的格式和 ChatML 一致。格式不匹配会导致训练效果大幅下降。
常见的格式错误
错误1:忘记 <|im_end|>
1 | <|im_start|>system |
这会导致模型把 system 消息和 user 消息混在一起理解。
错误2:角色名拼写错误
1 | <|im_start|>systme ← 拼错了 |
模型可能不认识这个角色,导致 system prompt 不生效。
错误3:在非 ChatML 模型上使用 ChatML
不是所有模型都用 ChatML 格式。如果你把 ChatML 格式的文本喂给一个用 Llama 格式训练的模型,模型会把 <|im_start|> 这些标记当成普通文本来处理,输出会很混乱。
一定要确认你用的模型支持什么格式。 Hermes 全系列用 ChatML,但其他模型未必。
ChatML 与函数调用
Hermes 2 Pro 以来的一个重要能力是函数调用(Function Calling),而这个能力的实现就依赖于 ChatML 的扩展性。
一个典型的函数调用流程是这样的:
Step 1:在 system prompt 中定义可用函数
1 | <|im_start|>system |
Step 2:用户提问
1 | <|im_start|>user |
Step 3:模型输出函数调用
1 | <|im_start|>assistant |
Step 4:工具返回结果
1 | <|im_start|>tool |
Step 5:模型基于结果回复
1 | <|im_start|>assistant |
整个流程中,ChatML 的角色系统(system、user、assistant、tool)让每个环节都有清晰的标识,模型能准确理解对话的流程和状态。
在 cocoloop 社区里,有开发者做过测试对比,Hermes 在 ChatML 格式下的函数调用准确率明显高于使用其他格式的模型。这不奇怪——Hermes 专门针对 ChatML 做了大量的训练数据优化。
Jinja2 模板与 Hugging Face
如果你用过 Hugging Face 的 Transformers 库,你可能会接触到 Jinja2 聊天模板。这是 Hugging Face 提供的一种机制,让每个模型可以在自己的配置文件中定义聊天模板。
Hermes 模型的 Jinja2 模板大致长这样:
1 | {% for message in messages %} |
这样,当你通过 Transformers 库的 apply_chat_template() 方法处理对话时,它会自动帮你生成正确的 ChatML 格式。
1 | from transformers import AutoTokenizer |
写在最后
ChatML 的设计哲学可以总结为两个词:简洁和通用。
它没有花里胡哨的嵌套结构,也没有复杂的转义规则。三个标记(<|im_start|>、<|im_end|>、角色名)就搞定了所有事情。
对于刚入门的人来说,你需要记住的核心要点就三个:
- ChatML 是一种给模型看的对话格式,有 system/user/assistant 三个基本角色
- Hermes 系列模型全线使用 ChatML
- 大多数工具(Ollama、Open WebUI 等)会自动处理格式,你不需要手写
更深入的理解可以等你实际上手后再慢慢建立。现在先把概念记住就够了。