原文出处:Chat Templates 原作者:Hugging Face · 许可证:Apache-2.0 License 中文译本由诸葛AI学院整理,仅供学习参考,版权归原作者与 Hugging Face 所有。
聊天模板
聊天模板(chat template)是指令微调(instruction tuning)的地基。它给语言模型、用户和外部工具之间的交互提供了一个统一的格式。你可以把它理解成一套"语法":教模型怎么读懂对话、区分不同的说话人、再给出得体的回应。
基座模型与指令模型
先要弄清基座模型(base model)和指令模型(instruct model)的区别。这点对做好微调(fine-tuning)非常要紧。
基座模型(SmolLM3-3B-Base):在原始文本上训练,任务是预测下一个令牌(token)。你给它"The weather today is",它可能接上"sunny and warm",也可能接任何说得通的延续。
指令模型(SmolLM3-3B):经过微调,会听从指令、参与对话。你问"What's the weather like?",它明白这是一个问题,要作为一条新消息来回答。
转变过程
从基座模型走到指令模型,中间要过两道东西:
- 聊天模板:为语言模型、用户和外部工具之间的交互提供结构化格式。
- 监督微调(supervised fine-tuning):训练模型生成恰当回答的技术。
SmolLM3 用的是 ChatML(Chat Markup Language,聊天标记语言)格式。这个格式因为清晰、灵活,已经成为行业里的通行标准。
【提示】下一章会讲偏好对齐(preference alignment)。这是一项让模型学会生成人类更偏爱的回答的微调技术。
pipeline 的用法:自动处理对话
用开源大模型,最省事的办法是使用 🤗 Transformers 里的 pipeline 抽象。它把聊天模板的处理打理得妥妥帖帖,你不用手工管理模板就能用对话模型。省心到什么程度呢——你甚至不需要知道聊天模板长什么样。
```python from transformers import pipeline
初始化 pipeline
pipe = pipeline("text-generation", "HuggingFaceTB/SmolLM3-3B", device_map="auto")
定义你的对话
messages = [ {"role": "system", "content": "You are a friendly chatbot who always responds in the style of a pirate"}, {"role": "user", "content": "How many helicopters can a human eat in one sitting?"}, ]
生成回复 - pipeline 自动处理聊天模板
response = pipe(messages, max_new_tokens=128, temperature=0.7) print(response[0]['generated_text'][-1]) # 打印助手的回复 ```
输出:
python
{
'role': 'assistant',
'content': "Matey, I'm afraid I must inform ye that humans cannot eat helicopters. Helicopters are not food, they are flying machines. Food is meant to be eaten, like a hearty plate o' grog, a savory bowl o' stew, or a delicious loaf o' bread. But helicopters, they be for transportin' and movin' around, not for eatin'. So, I'd say none, me hearties. None at all."
}
这段输出的意思是:模型用海盗腔回答,人不能吃直升机,直升机是飞行器不是食物,所以一架也吃不了。
在这个例子里,pipeline 自动做了这些事:
- 根据 Hugging Face Hub 仓库里模型的 tokenizer 配置,套用正确的聊天模板。
- 根据模型的 tokenizer 配置自动完成分词和生成。
- 返回带角色信息的结构化输出。
- 管理生成参数和停止条件。
更精细的 pipeline 用法
给 pipeline 传入一个 generation_config 字典,就能对生成过程做精细控制。
```python
配置生成参数
generation_config = { "max_new_tokens": 200, "temperature": 0.8, "do_sample": True, "top_p": 0.9, "repetition_penalty": 1.1 }
多轮对话
conversation = [ {"role": "system", "content": "You are a helpful math tutor."}, {"role": "user", "content": "Can you help me with calculus?"}, ]
生成第一轮回复
response = pipe(conversation, **generation_config) conversation = response[0]['generated_text']
继续对话
conversation.append({"role": "user", "content": "What is a derivative?"}) response = pipe(conversation, **generation_config)
print("Final conversation:") for message in response[0]['generated_text']: print(f"{message['role']}: {message['content']}") ```
理解 SmolLM3 的聊天模板
已经会了对话模型的基本推理,现在来看聊天模板的格式。SmolLM3 用的模板很常见,能处理多种对话类型。我们看看它是怎么工作的。
想动手体验聊天模板,可以试试官方的聊天模板演练场(chat template playground):https://huggingfacejs-chat-template-playground.hf.space
ChatML 格式结构
SmolLM3 使用带特殊令牌(special token)的 ChatML 格式,把对话的各个部分划分得清清楚楚。比如系统消息,用 <|im_start|>system 开头,以 <|im_end|> 收尾。
xml
<|im_start|>system
You are a helpful assistant focused on technical topics.<|im_end|>
<|im_start|>user
Hi there!<|im_end|>
<|im_start|>assistant
Nice to meet you!<|im_end|>
<|im_start|>user
Can I ask a question?<|im_end|>
<|im_start|>assistant
关键组成:
- <|im_start|> 和 <|im_end|>:标记每条消息开头和结尾的特殊令牌
- 角色(role):system、user、assistant(函数调用场景还有 tool)
- 内容(content):角色声明和 <|im_end|> 之间的实际消息文本
双模式推理支持
SmolLM3 属于一类新模型:可以推理,也可以不推理。它靠专门的格式加一个参数来实现这个功能。参数设为 think 时,模型会把推理过程展示出来。这个开关是通过 thinking 令牌传给模型的。
标准模式(no_think):
xml
<|im_start|>user
What is 15 × 24?<|im_end|>
<|im_start|>assistant
15 × 24 = 360<|im_end|>
思考模式(think):
```xml
<|im_start|>user
What is 15 × 24?<|im_end|>
<|im_start|>assistant
<|thinking|>
I need to multiply 15 by 24. Let me break this down:
15 × 24 = 15 × (20 + 4) = (15 × 20) + (15 × 4) = 300 + 60 = 360
</|thinking|>
15 × 24 = 360<|im_end|> ```
这种双模式能力让 SmolLM3 能按需展示推理过程,把复杂任务和简单任务混在同一批活儿里干也很合适。
在代码里使用 SmolLM3 聊天模板
transformers 库通过分词器(tokenizer)自动处理聊天模板的格式化。你只需要把消息按结构组织好,特殊令牌的拼接交给库来做。下面是操作 SmolLM3 聊天模板的写法:
```python from transformers import AutoTokenizer
加载 SmolLM3 的 tokenizer
tokenizer = AutoTokenizer.from_pretrained("HuggingFaceTB/SmolLM3-3B")
把对话组织成消息字典的列表
messages = [ {"role": "system", "content": "You are a helpful assistant focused on technical topics."}, {"role": "user", "content": "Can you explain what a chat template is?"}, {"role": "assistant", "content": "A chat template structures conversations between users and AI models by providing a consistent format that helps the model understand different roles and maintain context."} ]
应用聊天模板
formatted_chat = tokenizer.apply_chat_template( messages, tokenize=False, # 返回字符串而不是令牌 add_generation_prompt=True # 为助手的下一条回复加上提示 )
print(formatted_chat) ```
输出:
xml
<|im_start|>system
You are a helpful assistant focused on technical topics.<|im_end|>
<|im_start|>user
Can you explain what a chat template is?<|im_end|>
<|im_start|>assistant
A chat template structures conversations between users and AI models by providing a consistent format that helps the model understand different roles and maintain context.<|im_end|>
<|im_start|>assistant
理解消息结构
对话里的每条消息都是一个简单的字典:
role:标明说话的是谁(system、user、assistant或tool)。content:消息的实际内容。
消息类型:
- 系统消息:为整场对话设定行为和背景
- 用户消息:人类用户提出的问题、请求或陈述
- 助手消息:AI 模型给出的回复
- 工具消息:函数调用的返回结果(用于高级场景)
系统消息:设定上下文
系统消息对控制 SmolLM3 的行为非常要紧。它像一条持续生效的指令,影响后面所有的交互。创建系统消息,用 system 角色和 content 键:
```python
专业客服助手
system_message = { "role": "system", "content": "You are a professional customer service agent. Always be polite, clear, and helpful." }
技术专家
system_message = { "role": "system", "content": "You are a senior software engineer. Provide detailed technical explanations with code examples when appropriate." }
创意写作助手
system_message = { "role": "system", "content": "You are a creative writing assistant. Help users craft engaging stories and provide constructive feedback." } ```
【提示】系统消息对模型行为的影响很大。它是对话里的第一条消息,给整场对话定下基调。要写得具体,划清边界,交代背景,最好再带上例子。
多轮对话
SmolLM3 能在多轮对话之间保持上下文,每条消息都建立在前面内容的之上。比如下面这段代码,构造了一个跟编程辅导老师的对话:
python
conversation = [
{"role": "system", "content": "You are a helpful programming tutor."},
{"role": "user", "content": "I'm learning Python. Can you explain functions?"},
{"role": "assistant", "content": "Functions in Python are reusable blocks of code that perform specific tasks. They're defined using the 'def' keyword."},
{"role": "user", "content": "Can you show me an example?"},
{"role": "assistant", "content": "Sure! Here's a simple function:\n\npython\ndef greet(name):\n return f'Hello, {name}!'\n\nresult = greet('Alice')\nprint(result) # Output: Hello, Alice!\n"},
{"role": "user", "content": "How do I make it return multiple values?"},
]
生成提示:控制模型行为
聊天模板里最重要的概念之一是生成提示(generation prompt)。它告诉模型什么时候该开始生成回复,什么时候该续写已有的文本。
理解 add_generation_prompt
add_generation_prompt 参数控制模板要不要加上表示"机器人开始回复"的令牌:
```python from transformers import AutoTokenizer
tokenizer = AutoTokenizer.from_pretrained("HuggingFaceTB/SmolLM3-3B")
messages = [ {"role": "user", "content": "Hi there!"}, {"role": "assistant", "content": "Nice to meet you!"}, {"role": "user", "content": "Can I ask a question?"} ]
不加生成提示,用于已完成的对话
formatted_without = tokenizer.apply_chat_template( messages, tokenize=False, add_generation_prompt=False )
print("Without generation prompt:") print(formatted_without) print("\n" + "="*50 + "\n")
加上生成提示,用于推理
formatted_with = tokenizer.apply_chat_template( messages, tokenize=False, add_generation_prompt=True )
print("With generation prompt:") print(formatted_with) ```
输出: ```xml Without generation prompt: <|im_start|>user Hi there!<|im_end|> <|im_start|>assistant Nice to meet you!<|im_end|> <|im_start|>user Can I ask a question?<|im_end|>
==================================================
With generation prompt: <|im_start|>user Hi there!<|im_end|> <|im_start|>assistant Nice to meet you!<|im_end|> <|im_start|>user Can I ask a question?<|im_end|> <|im_start|>assistant ```
生成提示的作用,是保证模型接下来生成文本时写的是机器人的回复,而不是去续写用户的消息这类意外行为。
什么时候用生成提示
- 推理时:想让模型生成回复,用
add_generation_prompt=True。 - 训练时:准备包含完整对话的训练数据,用
add_generation_prompt=False。 - 评估时:测试模型的回复质量,用
add_generation_prompt=True。
续写最后一条消息:更高级的回答控制
continue_final_message 参数可以让模型续写对话里的最后一条消息,而不是另起一条。要做"预填充"(prefilling)回答、或者保证输出格式时,这个功能特别好用。
基本示例
```python
预填充一个 JSON 回答
chat = [ {"role": "user", "content": "Can you format the answer in JSON?"}, {"role": "assistant", "content": '{"name": "'}, ]
续写最后一条消息
formatted_chat = tokenizer.apply_chat_template( chat, tokenize=False, continue_final_message=True )
print("Continuing final message:") print(formatted_chat) print("\n" + "="*50 + "\n")
对比:另起一条新消息
formatted_new = tokenizer.apply_chat_template( chat, tokenize=False, add_generation_prompt=True )
print("Starting new message:") print(formatted_new) ```
输出: ```xml Continuing final message: <|im_start|>user Can you format the answer in JSON?<|im_end|> <|im_start|>assistant {"name": "
==================================================
Starting new message: <|im_start|>user Can you format the answer in JSON?<|im_end|> <|im_start|>assistant {"name": "<|im_end|> <|im_start|>assistant ```
实用场景
1. 生成结构化输出:
```python
强制模型补全指定的格式
messages = [ {"role": "system", "content": "You are a helpful assistant that always responds in JSON format."}, {"role": "user", "content": "What's the capital of France?"}, {"role": "assistant", "content": '{\n "question": "What\'s the capital of France?",\n "answer": "'} ]
模型只需接着写答案本身,JSON 结构保持不变
```
2. 代码补全:
```python
引导模型把代码写完
messages = [ {"role": "user", "content": "Write a Python function to calculate factorial"}, {"role": "assistant", "content": "def factorial(n):\n if n == 0:\n return 1\n else:\n return n * "} ]
模型会把递归调用补完
```
3. 分步推理:
```python
引导模型按结构思考
messages = [ {"role": "user", "content": "Solve: 2x + 5 = 13"}, {"role": "assistant", "content": "Let me solve this step by step:\n\nStep 1: "} ]
模型会接着写第一步
```
注意事项
add_generation_prompt=True和continue_final_message=True不能同时使用- 用
continue_final_message=True时,最后一条消息的角色必须是 "assistant" - 这个功能会去掉最后一条消息的序列结束令牌
使用推理模式
SmolLM3 的双模式推理靠专门的格式来控制:
标准模式与思考模式
```python
标准模式,直接给答案
standard_messages = [ {"role": "user", "content": "What is 15 × 24?"}, {"role": "assistant", "content": "15 × 24 = 360"} ]
思考模式,展示推理过程
thinking_messages = [ {"role": "user", "content": "What is 15 × 24?"}, {"role": "assistant", "content": "<|thinking|>\nI need to multiply 15 by 24. Let me break this down:\n15 × 24 = 15 × (20 + 4) = (15 × 20) + (15 × 4) = 300 + 60 = 360\n</|thinking|>\n\n15 × 24 = 360"} ]
应用模板
standard_formatted = tokenizer.apply_chat_template(standard_messages, tokenize=False) thinking_formatted = tokenizer.apply_chat_template(thinking_messages, tokenize=False)
print("Standard mode:") print(standard_formatted) print("\nThinking mode:") print(thinking_formatted) ```
用思考模式训练
准备带思考过程的数据集时,你可以控制每条样本要不要包含推理内容:
```python def create_thinking_example(question, answer, reasoning=None): """Create a training example with optional thinking""" if reasoning: assistant_content = f"<|thinking|>\n{reasoning}\n</|thinking|>\n\n{answer}" else: assistant_content = answer
return [
{"role": "user", "content": question},
{"role": "assistant", "content": assistant_content}
]
使用示例
math_example = create_thinking_example( question="What is the derivative of x²?", answer="The derivative of x² is 2x", reasoning="Using the power rule: d/dx(x^n) = n·x^(n-1)\nFor x²: n=2, so d/dx(x²) = 2·x^(2-1) = 2x" ) ```
工具使用与函数调用
现代的聊天模板支持工具使用(tool use)和函数调用(function calling)。SmolLM3 里工具是这么用的:
定义工具
```python
定义可用的工具
tools = [ { "type": "function", "function": { "name": "get_weather", "description": "Get the current weather for a location", "parameters": { "type": "object", "properties": { "location": { "type": "string", "description": "The city and state, e.g. San Francisco, CA" }, "unit": { "type": "string", "enum": ["celsius", "fahrenheit"], "description": "The temperature unit" } }, "required": ["location"] } } }, { "type": "function", "function": { "name": "calculate", "description": "Perform mathematical calculations", "parameters": { "type": "object", "properties": { "expression": { "type": "string", "description": "Mathematical expression to evaluate" } }, "required": ["expression"] } } } ] ```
带工具的聊天模板
```python
带工具使用的对话
messages = [ {"role": "system", "content": "You are a helpful assistant with access to tools."}, {"role": "user", "content": "What's the weather like in Paris?"}, { "role": "assistant", "content": "I'll check the weather in Paris for you.", "tool_calls": [ { "id": "call_1", "type": "function", "function": { "name": "get_weather", "arguments": '{"location": "Paris, France", "unit": "celsius"}' } } ] }, { "role": "tool", "tool_call_id": "call_1", "content": '{"temperature": 22, "condition": "sunny", "humidity": 60}' }, { "role": "assistant", "content": "The weather in Paris is currently sunny with a temperature of 22°C and 60% humidity. It's a beautiful day!" } ]
应用带工具的聊天模板
formatted_with_tools = tokenizer.apply_chat_template( messages, tools=tools, tokenize=False, add_generation_prompt=False )
print("Chat template with tools:") print(formatted_with_tools) ```
带工具的聊天模板输出如下:
```xml Chat template with tools: <|im_start|>system
Metadata
Knowledge Cutoff Date: June 2025 Today Date: 01 September 2025 Reasoning Mode: /think
Custom Instructions
You are a helpful assistant with access to tools.
Tools
You may call one or more functions to assist with the user query.
You are provided with function signatures within
For each function call, return a json object with function name and arguments within