ChatML Format Guide
用于结构化 LLM 对话的聊天标记语言
ConversationSpecification
ChatML(Chat Markup Language,聊天标记语言)是一种结构化文本格式,用于表示与大语言模型的多轮对话。该格式最初由 OpenAI 提出并被业界广泛采用,使用特殊标记来界定消息边界和角色标识符,提供了一种明确的方式将对话编码为单一的 token 序列,供模型训练和推理使用。该格式确保模型能够可靠地区分系统指令、用户输入和助手回复,即使消息内容中包含可能与结构元素混淆的文本也不受影响。
ChatML 格式使用特殊标记分隔符来包裹每条消息。每条消息以起始标记开头,随后是角色标识符和换行符,接着是消息正文,最后以结束标记结尾。标准角色包括 "system"(行为指令)、"user"(人类输入)和 "assistant"(模型回复)。部分实现扩展了额外的角色,如 "tool" 用于函数调用结果,"ipython" 用于代码执行输出。特殊标记(通常显示为 <|im_start|> 和 <|im_end|>)在训练期间被添加到模型的词汇表中,永远不会作为常规文本输出的一部分生成。
ChatML 已成为许多开源模型的默认聊天模板,包括 Qwen、Yi 以及使用 OpenAI 兼容格式微调的模型。Hugging Face Transformers 库使用基于 Jinja2 的聊天模板在结构化消息列表和 ChatML 格式文本之间进行转换,每个模型的分词器都指定了自己的模板。这种标准化使得不同模型提供商和推理框架之间的行为保持一致,因为结构化格式消除了关于消息边界的歧义。
When to Use ChatML
当你的目标模型使用 ChatML 聊天模板时,应使用 ChatML——包括 Qwen 系列模型、Yi 系列模型以及许多基于 OpenAI 对话格式的社区微调模型。如果你的训练框架(Axolotl、LLaMA-Factory、TRL)要求指定聊天模板,且你的基础模型是使用 ChatML 进行预训练或指令微调的,你应该以 ChatML 格式或可转换为 ChatML 的结构化格式来准备训练数据。
当你需要明确的消息边界检测时,应优先选择 ChatML 而非原始文本或临时构造的提示格式。特殊标记分隔符确保模型不会将消息内容与结构标记混淆,这对于安全性(防止提示注入)和可靠性(确保多轮对话中正确的轮次交替)至关重要。当你的部署使用 OpenAI 兼容 API 格式时,ChatML 也是首选格式,因为结构化消息列表可以直接映射到 ChatML 编码。
当你的目标模型使用不同的聊天模板时(LLaMA 使用不同的格式,Mistral 使用自有标记),ChatML 的相关性较低,此类情况请使用模型的原生模板。对于简单格式如 Alpaca 即可满足的单轮任务,或当训练框架从结构化对话数据(ShareGPT 格式)自动处理模板应用时,ChatML 也不是必需的。
Schema / Structure
ChatML Token Structure:
<|im_start|>system
{system_message}<|im_end|>
<|im_start|>user
{user_message}<|im_end|>
<|im_start|>assistant
{assistant_message}<|im_end|>
Special Tokens:
<|im_start|> - Message start delimiter
<|im_end|> - Message end delimiter
Roles:
system - Behavioral instructions (optional, first message)
user - Human/user input
assistant - Model/AI response
tool - Tool/function call result (extended format)
Jinja2 Chat Template (HuggingFace):
{% for message in messages %}
<|im_start|>{{ message.role }}
{{ message.content }}<|im_end|>
{% endfor %}
<|im_start|>assistantExample Data
<|im_start|>system
You are a senior software engineer who provides concise, practical code reviews.<|im_end|>
<|im_start|>user
Please review this Python function:
def get_user(id):
conn = sqlite3.connect('app.db')
cursor = conn.cursor()
cursor.execute(f"SELECT * FROM users WHERE id = {id}")
return cursor.fetchone()<|im_end|>
<|im_start|>assistant
This function has several critical issues:
1. **SQL Injection vulnerability**: Using f-string interpolation in SQL queries is dangerous. Use parameterized queries instead:
```python
cursor.execute("SELECT * FROM users WHERE id = ?", (id,))
```
2. **Resource leak**: The database connection is never closed. Use a context manager:
```python
with sqlite3.connect('app.db') as conn:
cursor = conn.cursor()
cursor.execute("SELECT * FROM users WHERE id = ?", (id,))
return cursor.fetchone()
```
3. **Hardcoded database path**: Move 'app.db' to a configuration variable.
4. **No error handling**: Add try/except for database errors and handle the case where no user is found.<|im_end|>
<|im_start|>user
Good points. Can you show the corrected version?<|im_end|>
<|im_start|>assistant
```python
import sqlite3
from typing import Optional, Tuple
def get_user(user_id: int, db_path: str = "app.db") -> Optional[Tuple]:
try:
with sqlite3.connect(db_path) as conn:
cursor = conn.cursor()
cursor.execute("SELECT * FROM users WHERE id = ?", (user_id,))
return cursor.fetchone()
except sqlite3.Error as e:
logging.error(f"Database error fetching user {user_id}: {e}")
return None
```
Key improvements: parameterized query, context manager, type hints, configurable DB path, error handling, and renamed parameter to avoid shadowing the built-in `id`.<|im_end|>Ertas Support
Ertas Data Suite 支持将 ChatML 作为对话训练数据的导入和导出格式。你可以导入 ChatML 格式的文本数据集,在保留特殊标记结构的同时进行 PII 脱敏处理,并导出保持消息边界完整的已处理对话。平台会验证 ChatML 结构,包括标记配对的正确性、角色标识符的有效性以及消息交替模式的合规性。
Ertas Studio 在微调使用 ChatML 聊天模板的模型时,会自动处理 ChatML 模板的应用,在训练数据准备阶段将结构化对话数据转换为正确分词的 ChatML 序列。
Related Resources
Ship AI that runs on your users' devices.
Early bird pricing starts at $14.50/mo — locked in for life. Plans for builders and agencies.