TRL documentation
聊天模板
聊天模板
聊天模板是一段 Jinja2 代码片段,用于将消息格式化为模型训练时所使用的字符串。例如:
>>> from transformers import AutoTokenizer
>>> tokenizer = AutoTokenizer.from_pretrained("Qwen/Qwen2-0.5B-Instruct")
>>> tokenizer.chat_template
"{% for message in messages %}{% if loop.first and messages[0]['role'] != 'system' %}{{ '<|im_start|>system\nYou are a helpful assistant.<|im_end|>\n' }}{% endif %}{{'<|im_start|>' + message['role'] + '\n' + message['content'] + '<|im_end|>' + '\n'}}{% endfor %}{% if add_generation_prompt %}{{ '<|im_start|>assistant\n' }}{% endif %}"
>>> tokenizer.apply_chat_template([{"role": "user", "content": "Hi!"}], tokenize=False)
'<|im_start|>system\nYou are a helpful assistant.<|im_end|>\n<|im_start|>user\nHi!<|im_end|>\n'在大多数情况下,你无需担心聊天模板:模型会随分词器一起附带其模板,并且 TRL 会为你应用它。整个过程是透明的。但某些 TRL 配方依赖于大多数附带模板中未包含的功能:
- SFT with
assistant_only_loss=True需要在助手输出周围添加{% generation %}/{% endgeneration %}标记,以便损失掩码仅针对助手的 token。 - 带工具调用的 GRPO 要求模板是前缀保持的:追加一条工具消息时,不能改变之前消息的渲染方式。
TRL 在 trl/chat_templates/ 下提供了针对常见模型系列(Qwen、Llama、DeepSeek-V3、GPT-OSS 等)的修补模板,并会为受支持的模型自动替换。对于其他模型,您需要自行修补其模板。本页其余部分列出了内置的模板。
支持的模型系列
TRL 会存储原始模板的参考副本,以便在初始化时识别受支持的模型,并在需要时替换为训练模板。以下模型系列会被识别:Cohere、Cohere2、DeepSeek-V3(包括 DeepSeek-R1、DeepSeek-R1-0528 和 DeepSeek-V3-0324)、DeepSeek-R1-Distill、DiffusionGemma、Gemma、Gemma3、Gemma4(包括 Gemma-4-12B、Gemma-4-26B-A4B 和 Gemma-4-31B)、GLM-4-MoE(4.5、4.6、4.7)、GLM-5、GLM-5.1、GLM-5.2、GLM-5.3、GLM-5.3-Flash、GPT-OSS、Idefics3、LFM2、LFM2.5(包括 LFM2.5-1.2B-Instruct、LFM2.5-2.6B 和 LFM2.5-24B-A2B)、LFM2.5-VL(包括 LFM2.5-VL-450M)、Llama 3 / 3.1 / 3.2、Llava-Next、Mistral Large 4、Mistral Medium 3.5、Muse Glimmer、Muse Spark Open、Nemotron 3(Nano、Super、Ultra)、Nemotron 3.5 Lightning、Phi-3、Phi-3.5、Qwen2-VL、Qwen2.5(包括基础检查点)、Qwen2.5-VL、Qwen3(包括 Instruct-2507 和 Thinking-2507 变体)、Qwen3-Coder(包括 Qwen3-Coder-Next)、Qwen3-VL、Qwen3.5、Qwen3.6、Qwen3.8、SmolVLM(包括 SmolVLM2)。
训练模板
用于修复训练特定问题的补丁模板。在启用工具(GRPO)或 assistant_only_loss=True(SFT)时,会在初始化阶段替换使用。
cohere_training.jinja
修补后的 Cohere 模板。与 cohere.jinja 的差异:
使用 {% generation %} / {% endgeneration %} 包裹助手消息的输出,以便 return_assistant_tokens_mask=True 能为 SFT 中仅针对助手的损失生成正确的掩码。
cohere2_training.jinja
修补后的 Cohere2 模板。与 cohere2.jinja 的差异:
将位于角色分发 {% endif %} 之后的尾部 <|END_OF_TURN_TOKEN|> 移动到每个角色分支中,并使用 {% generation %} / {% endgeneration %} 包裹助手分支(<|START_RESPONSE|>...<|END_RESPONSE|><|END_OF_TURN_TOKEN|>),以便 return_assistant_tokens_mask=True 能为 SFT 中仅针对助手的损失生成正确的掩码。
deepseek_r1_distill_training.jinja
已修补的 DeepSeek-R1-Distill 模板。与 deepseek_r1_distill.jinja 的差异:
- 移除了对助手内容中
'</think>'的分割。原始实现会丢弃包括</think>在内的所有内容,这会悄无声息地移除训练目标中的推理部分。 - 使用
{% generation %}/{% endgeneration %}标记包裹助手消息的输出,以便在 SFT 中仅对助手部分计算损失。开启助手回合的<|Assistant|>和<|tool▁outputs▁end|>保留在标记之外,因为提示词已经以它们结尾。
deepseekv3_training.jinja
修补后的 DeepSeek-V3 模板。与 deepseekv3.jinja 的差异:
- 对
tool['function']['arguments']使用| tojson,以便将arguments作为dict传递(transformers 文档中规定的格式)。原始模板使用原始字符串拼接,在处理 dict 输入时会崩溃。 - 移除了对助手内容中
'</think>'的分割,以确保推理块始终被渲染。原始实现会丢弃包括</think>在内的所有内容,这会悄无声息地从训练目标中移除推理部分。 - 使用
{% generation %}/{% endgeneration %}标记包裹助手消息的输出,以便在 SFT 中仅对助手部分计算损失。开启助手回合的<|Assistant|>和<|tool▁outputs▁end|>保留在标记之外,因为提示词已经以它们结尾。
deepseekv3_v2_training.jinja
已修补的 DeepSeek-R1-0528 模板。与 deepseekv3_v2.jinja 的差异:
- 使用
| tojson处理tool['function']['arguments'],以便arguments可以作为dict传递,如deepseekv3_training.jinja所示。 - 移除了对助手内容中
'</think>'的分割,以确保推理块始终被渲染。原始实现会丢弃包括</think>在内的所有内容,这会悄无声息地从训练目标中移除推理部分。 - 使用
{% generation %}/{% endgeneration %}标记包裹助手消息输出,用于 SFT 中仅针对助手的损失计算。在工具输出之后开启助手回合的<|tool▁outputs▁end|>保留在标记之外,因为提示词已经以该标记结尾。
deepseekv3_v3_training.jinja
修补后的 DeepSeek-V3 模板(由 deepseek-ai/DeepSeek-V3 使用)。与 deepseek_r1_distill_training.jinja 相同的差异,应用于 deepseekv3_v3.jinja:在助手消息输出周围添加 {% generation %} / {% endgeneration %} 标记,用于 SFT 中仅针对助手的损失计算,而开启助手回合的 <|Assistant|> 和 <|tool▁outputs▁end|> 则保留在这些标记之外。
deepseekv3_v4_training.jinja
修补后的 DeepSeek-V3-0324 模板(由 deepseek-ai/DeepSeek-V3-0324 使用)。与 deepseekv3_v2_training.jinja 相同的差异,应用于 deepseekv3_v4.jinja:在工具调用参数上使用 | tojson,并在助手输出周围添加 {% generation %} / {% endgeneration %} 标记。
diffusion_gemma_training.jinja
修补后的 DiffusionGemma 模板。与 diffusion_gemma.jinja 的差异:
将模型在回合中输出的部分(思考通道、工具调用、内容以及结尾的 <turn|>)用 {% generation %} / {% endgeneration %} 包裹起来,以便 return_assistant_tokens_mask=True 能为 SFT 中仅针对助手的损失生成正确的掩码。嵌入在回合中的工具响应来自环境,应保留在生成块之外。
gemma_training.jinja
修补后的 Gemma 模板(Gemma 和 Gemma2 共用,两者内置的聊天模板完全相同)。与 gemma.jinja 的差异:
将统一助手输出进行拆分,使 <start_of_turn>model\n 头部(作为提示线索,并非由模型生成)位于生成块之外,并使用 {% generation %} / {% endgeneration %} 标记包裹助手内容,以便进行仅针对助手的 SFT 损失计算。
gemma3_training.jinja
已修补的 Gemma 3 模板。与 gemma_training.jinja 中的差异相同(将统一的输出行拆分为按角色划分的分支,使 <start_of_turn>model\n 提示标记位于生成块之外,并使用 {% generation %} / {% endgeneration %} 包裹助手内容),该差异应用于支持系统消息和多模态内容块的 Gemma 3 基础模板。
glm4moe_training.jinja
修补后的 GLM-4-MoE 模板。与 glm4moe.jinja 的差异:
Close each assistant turn inside the generation block with the role marker that opens the next turn (<|observation|> after tool calls, otherwise <|user|> or <|system|>). GLM’s original template has no end-of-turn token: a turn is ended by the next message’s role marker, and GLM declares <|user|> and <|observation|> as end-of-sequence tokens, so the training template appends the marker to the assistant turn to keep the stop token in the loss mask, and the following message no longer renders its own marker.
使用 {% generation %} / {% endgeneration %} 标记包裹助手消息的输出(包括思考块和工具调用),以便进行仅针对助手的 SFT 损失计算。当 enable_thinking=False 时,思考块会保留在标记之外,因为生成提示词已经以空的 \n<think></think> 结尾。
qwen3_training.jinja
修补后的 Qwen3 模板。与 qwen3.jinja 的差异:
loop.last 会基于条件性地省略思考块,这会在追加工具消息时改变助手的渲染方式,从而破坏前缀保留:
- {%- if loop.index0 > ns.last_query_index %}
- {%- if loop.last or (not loop.last and reasoning_content) %}
- {{- '<|im_start|>' + message.role + '\n<think>\n' + reasoning_content.strip('\n') + '\n</think>\n\n' + content.lstrip('\n') }}
- {%- else %}
- {{- '<|im_start|>' + message.role + '\n' + content }}
- {%- endif %}
- {%- else %}
- {{- '<|im_start|>' + message.role + '\n' + content }}
- {%- endif %}
+ {{- '<|im_start|>' + message.role + '\n<think>\n' + reasoning_content.strip('\n') + '\n</think>\n\n' + content.lstrip('\n') }}使用 {% generation %} / {% endgeneration %} 包裹助手消息的输出,以便 return_assistant_tokens_mask=True 能为 SFT 中仅针对助手的损失生成正确的掩码。生成提示词预先填充的思考块部分(在 enable_thinking=False 时为 <think>\n\n</think>\n\n)保留在标记之外,因为提示词本身已经以该部分结尾。其他 Qwen3 训练模板对其自身生成提示词预先填充的内容也采用相同做法,例如 <think>\n。
qwen3_v2_training.jinja
修补后的原始 Qwen3 模板(2025 年 5 月之前随 Qwen3 检查点发布,目前仍随 Qwen3 基础检查点发布)。与 qwen3_training.jinja 相同的差异,应用于 qwen3_v2.jinja。
qwen3_vl_training.jinja
修补后的 Qwen3-VL 模板。与 qwen3_vl.jinja 的差异:
将助手消息的输出(包括 content 和 tool_calls)用 {% generation %} / {% endgeneration %} 包裹起来,以便在 return_assistant_tokens_mask=True 时,为仅针对助手的 SFT 损失生成正确的掩码。
gptoss_training.jinja
修补后的 GPT-OSS 模板。与 gptoss.jinja 的差异:
使用 {% generation %} / {% endgeneration %} 包裹助手消息的输出,以便 return_assistant_tokens_mask=True 能为 SFT 中仅针对助手的损失生成正确的掩码。开启该轮次的 <|start|>assistant 保留在标记之外,因为生成提示词已经以它结尾。
idefics3_training.jinja
修补后的 Idefics3 模板。与 idefics3.jinja 的差异:
将助手消息拆分为独立的分支,以便{% generation %} / {% endgeneration %}标记能够包裹助手内容。这使得return_assistant_tokens_mask=True能够为 SFT 中仅针对助手的损失生成正确的掩码。
lfm2_training.jinja
修补后的 LFM2 模板。与 lfm2.jinja 的差异:
将统一的消息输出行拆分为按角色划分的分支,使 <|im_start|>assistant\n 提示标记位于生成块之外(它不由模型生成),而助手的内容以及 <|im_end|>\n(模型必须学会生成并在此处停止)则位于生成块之内。使用 {% generation %} / {% endgeneration %} 包裹助手内容,以便 return_assistant_tokens_mask=True 能为 SFT 中仅针对助手的损失计算生成正确的掩码。
lfm2_v2_training.jinja
修补后的 LFM2 模板,后续修订版。与 lfm2_v2.jinja 的差异:
在渲染 tool_calls 的分支和纯内容分支中,均使用 {% generation %} / {% endgeneration %} 包裹助手输出,以便 return_assistant_tokens_mask=True 能为 SFT 中仅针对助手的损失生成正确的掩码。<|im_start|>assistant\n 提示标记保留在生成块之外,而内容、工具调用以及 <|im_end|>\n 则位于生成块内部。
llama3_training.jinja
修补后的 Llama 3 模板。与 llama3.jinja 的差异:
使用 {% generation %} / {% endgeneration %} 包裹助手消息的输出,以便 return_assistant_tokens_mask=True 能为 SFT 中仅针对助手的损失生成正确的掩码。
llava_next_training.jinja
修补后的 Llava-Next 模板。与 llava_next.jinja 的差异:
- 使用
{% generation %}/{% endgeneration %}标记包裹助手消息输出,用于 SFT 中仅针对助手的损失计算。 - 保留
</s>之后的空格在生成块之外。分词器会将其与下一轮的第一个 token(▁[)合并,否则该 token 会被视为助手输出而被掩码,从而将</s>停止 token 隐藏在掩码区间的末尾。
mistral_large_4_training.jinja
已修补 Mistral Large 4 模板。与 mistral_large_4.jinja 的差异:与 mistral_medium_3_5_training.jinja 的更改相同,使用 {% generation %} / {% endgeneration %} 包裹助手轮次(内容、工具调用和轮次结束标记),以便 return_assistant_tokens_mask=True 为 SFT 中仅针对助手的损失生成正确的掩码。
mistral_medium_3_5_training.jinja
已修补的 Mistral Medium 3.5 模板。与 mistral_medium_3_5.jinja 的差异:原始模板本身已具备前缀保留特性,因此唯一的改动是将助手轮次(内容、工具调用以及轮次结束标记)包裹在 {% generation %} / {% endgeneration %} 中,以便 return_assistant_tokens_mask=True 能为 SFT 中仅针对助手的损失计算生成正确的掩码。
muse_glimmer_training.jinja
已修补的 Muse Glimmer 模板。与 muse_glimmer.jinja 的差异:将整个助手分支——即该轮次的每个 ATEM 通道——包裹在 {% generation %} / {% endgeneration %} 中,以便 return_assistant_tokens_mask=True 能为 SFT 助手专用损失生成正确的掩码。开启该轮次的 <|start|>assistant 保留在标记之外,因为生成提示词已经以它结尾。
muse_spark_training.jinja
已修补的 Muse Spark Open 模板。与 muse_spark.jinja 的差异:原模板中的 {% generation %} 标记还涵盖了开启该轮对话的 <|start|>assistant,而生成提示词本身已经以该标记结尾。训练模板将其置于标记之前输出,从而确保 return_assistant_tokens_mask=True 不会对其进行训练。渲染后的文本保持不变。
nemotron_3_nano_training.jinja
已修补的 Nemotron Nano 模板。与 nemotron_3_nano.jinja 的差异:原始模板本身已具备前缀保留特性,因此修改内容是将助手消息的输出包裹在 {% generation %} / {% endgeneration %} 中,以便 return_assistant_tokens_mask=True 能为 SFT 中仅针对助手的损失生成正确的掩码。生成提示词预先填充的思考块部分(<think>\n,或在 enable_thinking=False 时为 <think></think>)保留在标记之外,因为提示词本身已以该部分结尾。
nemotron_3_super_training.jinja
已修补的 Nemotron Super 模板。与 nemotron_3_super.jinja 的差异:与 nemotron_3_nano_training.jinja 相同——原始模板本身已保留前缀,因此修改在于使用 {% generation %} / {% endgeneration %} 包裹助手消息的输出,以便 return_assistant_tokens_mask=True 能为 SFT 中仅针对助手的损失生成正确的掩码。生成提示词预先填充的思考块部分(<think>\n,或在 enable_thinking=False 时为 <think></think>)保留在标记之外,因为提示词本身已以该部分结尾。
nemotron_3_ultra_training.jinja
已修补的 Nemotron Ultra 模板。与 nemotron_3_ultra.jinja 的差异:与 nemotron_3_nano_training.jinja 相同——原始模板本身已具备前缀保留特性,因此修改在于使用 {% generation %} / {% endgeneration %} 包裹助手消息的输出,以便 return_assistant_tokens_mask=True 能为 SFT 中仅针对助手的损失生成正确的掩码。生成提示词预先填充的思考块部分(<think>\n,或在 enable_thinking=False 时为 <think></think>)保留在标记之外,因为提示词本身已以该部分结尾。
nemotron_3_5_lightning_training.jinja
已修补的 Nemotron 3.5 Lightning 模板。与 nemotron_3_5_lightning.jinja 的差异:与 nemotron_3_nano_training.jinja 相同——原始模板本身已具备前缀保留特性,因此修改在于使用 {% generation %} / {% endgeneration %} 包裹助手消息的输出,以便 return_assistant_tokens_mask=True 能为 SFT 中仅针对助手的损失生成正确的掩码。生成提示词预先填充的思考块部分(<think>\n,或在 enable_thinking=False 时为 <think></think>)保留在标记之外,因为提示词本身已以该部分结尾。
phi3_training.jinja
修补后的 Phi-3 模板。与 phi3.jinja 的差异:
使用 {% generation %} / {% endgeneration %} 包裹助手消息的输出,以便 return_assistant_tokens_mask=True 能为 SFT 中仅针对助手的损失生成正确的掩码。
phi3_5_training.jinja
修补后的 Phi-3.5 模板。与 phi3_5.jinja 的差异:
使用 {% generation %} / {% endgeneration %} 包裹助手消息的输出,以便 return_assistant_tokens_mask=True 能为 SFT 中仅针对助手的损失生成正确的掩码。
qwen2_5_training.jinja
修补后的 Qwen2.5 模板。与 qwen2_5.jinja 的差异:
使用 {% generation %} / {% endgeneration %} 包裹助手消息的输出,以便 return_assistant_tokens_mask=True 能为 SFT 中仅针对助手的损失生成正确的掩码。
qwen2_5_base_training.jinja
修补后的 Qwen2.5 基础模板(用于基础检查点,例如 Qwen2.5-0.5B,其模板与 instruct 模板仅在默认系统提示词上有所不同)。与 qwen2_5_training.jinja 相同的差异,应用于 qwen2_5_base.jinja。
qwen2_5_vl_training.jinja
修补后的 Qwen2.5-VL 模板(也用于 Qwen2-VL,其模板与前者完全一致)。与 qwen2_5_vl.jinja 的差异:
将助手消息拆分为独立的分支,以便{% generation %} / {% endgeneration %}标记能够包裹助手内容。 这使得return_assistant_tokens_mask=True能够为 SFT 中仅针对助手的损失生成正确的掩码。
qwen3_instruct_2507_training.jinja
已修补的 Qwen3-Instruct-2507 模板(用于 Qwen3-4B-Instruct-2507 等模型,这些模型附带一个更简单的 Qwen3 变体,不包含 reasoning_content / <think> 解析、multi_step_tool 跟踪或 enable_thinking 标志)。与 qwen3_instruct_2507.jinja 的差异:
使用 {% generation %} / {% endgeneration %} 包裹助手消息的输出,以便 return_assistant_tokens_mask=True 能为 SFT 中仅针对助手的损失生成正确的掩码。
qwen3_thinking_2507_training.jinja
修补后的 Qwen3-Thinking-2507 模板(用于 Qwen3-4B-Thinking-2507 等模型,这些模型会在生成提示中预填充 <think>\n)。与 qwen3_thinking_2507.jinja 的差异:与 qwen3_training.jinja 的更改集相同,即移除了 loop.index0 > ns.last_query_index 条件,以便始终输出思考块(前缀保留),并使用 {% generation %} / {% endgeneration %} 标记包裹助手输出,以实现仅针对助手的 SFT 损失。
qwen3_5_think_training.jinja / qwen3_5_nothink_training.jinja
已修补的 Qwen3.5 模板,两种变体共享相同的逻辑(它们仅在 enable_thinking 标志的默认值上有所不同):
qwen3_5_think_training.jinja默认启用思考模式,适用于 Qwen3.5-4B 及更大规模的模型。qwen3_5_nothink_training.jinja默认禁用思考模式,用于 Qwen3.5-2B 及更小的模型。
与 qwen3_5_think.jinja / qwen3_5_nothink.jinja 的差异:与 qwen3_training.jinja 相同(移除 loop.index0 > ns.last_query_index 条件,使思考块始终输出,并将助手输出包裹在 {% generation %} / {% endgeneration %} 中)。
qwen3_6_training.jinja
已修补的 Qwen3.6 模板。与 qwen3_6.jinja 的差异:与 qwen3_training.jinja 相同(移除 loop.index0 > ns.last_query_index 条件,使思考块始终输出,并使用 {% generation %} / {% endgeneration %} 包裹助手输出)。
qwen3_8_training.jinja
已修补的 Qwen3.8 模板。与 qwen3_8.jinja 的差异:移除了 preserve_thinking / loop.index0 > ns.last_query_index 条件,使思考块始终被输出——即使调用方传入 preserve_thinking=False,前缀保留特性依然成立——并使用 {% generation %} / {% endgeneration %} 标记包裹助手输出,以实现仅针对助手的 SFT 损失计算,其中由生成提示预填充的思考块部分位于这些标记之外。
smolvlm_training.jinja
修补后的 SmolVLM 模板(也用于 SmolVLM2,其附带一个字节完全相同的模板)。与 smolvlm.jinja 的差异:
将助手消息拆分为独立的分支,以便{% generation %} / {% endgeneration %}标记能够包裹助手内容。这使得return_assistant_tokens_mask=True能够为 SFT 中仅针对助手的损失生成正确的掩码。
相关实用工具
有关操作这些模板的辅助函数(clone_chat_template()、is_chat_template_prefix_preserving、get_training_chat_template()),请参阅 Chat Template Utilities。