首页 / 资料库 / Hugging Face · 小模型课

资料库35 分钟读完Apache-2.0聊天模板微调Hugging Face小模型课

聊天模板

译自《Chat Templates》 · 查看英文原文

原文出处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):systemuserassistant(函数调用场景还有 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:标明说话的是谁(systemuserassistanttool)。
  • content:消息的实际内容。

消息类型:

  1. 系统消息:为整场对话设定行为和背景
  2. 用户消息:人类用户提出的问题、请求或陈述
  3. 助手消息:AI 模型给出的回复
  4. 工具消息:函数调用的返回结果(用于高级场景)

系统消息:设定上下文

系统消息对控制 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=Truecontinue_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 XML tags:

{'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']}}}

For each function call, return a json object with function name and arguments within

这篇在讲什么,跟咱们的课怎么对?

资料库是大厂公开教材的中文译本,偏原理和工程做法。想看面向中小企业的白话版本,去入门课场景课