什么是聊天模板?
本质上,大型语言模型只知道如何完成纯文本,不知道它们的输入和输出之间的区别。为了支持与人的聊天,LLM 设计成使用模板将对话转换为特定格式的纯文本。
对于给定的模型,使用适当的聊天模板非常重要,因为每个模型都设计为最适合特定格式。内置模型随附的聊天模板应该足以满足大多数用途。
有两个原因你可能想要修改聊天模板
- 你正在旁加载一个模型,并且没有可用的聊天模板,
- 你想对 LLM 的输入有比系统消息提供的更大控制权。
什么是系统消息?
系统消息是一种控制 LLM 响应的消息,它会影响整个对话。系统消息可以很短,例如“像海盗一样说话。”,也可以很长并包含大量上下文供 LLM 记住。
并非所有模型都设计为使用系统消息,因此它们与某些模型的配合优于其他模型。
如何自定义聊天模板或系统消息?
要自定义聊天模板或系统消息,请转到“设置”>“模型”。确保在顶部选择正确的模型。如果你克隆一个模型,你可以使用与基础模型不同的聊天模板或系统消息,从而为每个对话使用不同的设置。
这些设置会立即生效。更改后,你可以在聊天视图中点击“重做上次响应”,响应将考虑新设置。
我需要编写聊天模板吗?
通常你不需要编写自己的聊天模板。例外情况是不在官方模型列表中的模型,并且没有内置聊天模板。这些模型在“模型设置”页面上的聊天模板字段上方会显示“清除”选项,而不是“重置”选项。请参阅有关查找或创建聊天模板的部分。
GPT4All v3.5 有什么变化?
GPT4All v3.5 全面改革了聊天模板系统。有三个关键区别
- 聊天模板现在格式化整个对话,而不是单一的消息对,
- 聊天模板现在使用 Jinja 语法而不是
%1
和%2
占位符, - 并且系统消息不应再包含控制令牌或尾随空格。
如果你在升级到 GPT4All v3.5 或更新版本之前添加或修改过任何聊天模板或系统消息,它们将不再起作用。请参阅下方了解如何解决升级后可能看到的常见错误。
错误/警告:系统消息不是纯文本。
这很容易修复。转到模型的设置,查看系统提示。有三件事需要注意
- 控制令牌,例如
<|im_start|>
、<|start_header_id|>
或<|system|>
- 前缀,例如
### System
或SYSTEM:
- 尾随空格,例如空格字符或空白行。
如果你看到这些情况中的任何一个,请移除它们。例如,这个遗留系统提示
应该变成这样
如果你看不到任何需要更改的内容,可以通过对消息进行微小修改然后改回来以解除错误。
如果你看到警告,你的系统消息似乎不是纯文本。如果你认为此警告不正确,可以安全地忽略它。如有疑问,请在 Discord 上提问。
错误:遗留的系统提示需要在设置中更新。
这与上面提到的相同,但显示在聊天页面上。
错误/警告:聊天模板不是 Jinja 格式。
这是在 GPT4All 3.5+ 中尝试使用旧式模板(可能来自先前版本)的结果。
转到“模型设置”页面并选择受影响的模型。如果你看到“重置”按钮,并且你没有故意修改提示模板,则可以点击“重置”。否则,你可以这样做
- 通过安全地将其复制到文本文件并保存来备份你的聊天模板。在下一步中,它将从 GPT4All 中删除。
- 点击“重置”或“清除”。
- 如果你点击了“清除”,聊天模板现在已消失。按照步骤为你的模型查找或创建一个基本聊天模板。
- 自定义聊天模板以满足你的需求。如需帮助,请阅读有关创建聊天模板的部分。
错误:遗留的提示模板需要在设置中更新。
这与上面提到的相同,但显示在聊天页面上。
聊天模板存在语法错误。
如果在编辑聊天模板时出现语法错误,详细信息将显示在输入框上方的错误消息中。这可能是因为聊天模板实际上不是 Jinja 格式(参见上面)。
否则,你可能输入正确,或者模型自带的模板与 GPT4All 不兼容。请参阅下面的部分了解如何创建聊天模板,并确保一切正确。如有疑问,请在 Discord 上提问。
错误:未配置聊天模板。
对于非官方模型列表中且不包含聊天模板的模型,可能会出现此情况。旧版本的 GPT4All 在这种情况下选择了一个糟糕的默认值。如果你按照步骤为你的模型查找或创建聊天模板,你将获得更好的结果。
错误:聊天模板不能为空白。
如果“模型设置”页面上聊天模板上方的按钮显示“清除”,请参见上面。如果你看到“重置”,点击该按钮以恢复合理的默认设置。另请参见有关语法错误的部分。
如何找到聊天模板?
如有疑问,你随时可以在 Discord 社区寻求帮助。以下是自行查找模板的说明。
模型聊天模板的权威来源是原始(非 GGUF)模型所在的 HuggingFace 仓库。首先,你应该找到这个页面。如果你只有一个模型文件,可以尝试在 Google 上搜索模型的名称。如果你知道下载 GGUF 模型的页面,其 README 通常会链接到原始的非 GGUF 模型。
找到原始模型后,有两种方法可以提取其聊天模板。选择你最熟悉的一种。
使用 CLI(所有模型)
- 使用你偏好的包管理器安装
jq
,例如 Chocolatey (Windows)、Homebrew (macOS) 或 apt (Ubuntu)。 - 从模型的“文件和版本”选项卡下载
tokenizer_config.json
。 - 在下载了模型文件的目录中打开命令提示符。
- 运行
jq -r ".chat_template" tokenizer_config.json
。这会以人类可读的形式显示聊天模板。你可以复制此内容并将其粘贴到设置页面。 - (可选)你可以将输出保存到文本文件,如下所示:
jq -r ".chat_template" tokenizer_config.json >chat_template.txt
如果输出为“null”,则模型未提供聊天模板。请参阅下面的说明了解如何创建聊天模板。
Python(开源模型)
- 使用你偏好的 Python 包管理器安装
transformers
,例如pip install transformers
。确保版本至少为 v4.43.0。 - 复制 HuggingFace 模型的 ID,使用名称旁边的剪贴板图标。例如,如果 URL 是
https://hugging-face.cn/NousResearch/Hermes-2-Pro-Llama-3-8B
,则 ID 是NousResearch/Hermes-2-Pro-Llama-3-8B
。 - 打开一个 Python 解释器(
python
)并运行以下命令。将示例中的模型 ID 更改为你复制的 ID。你可以复制输出并将其粘贴到设置页面。 - (可选)你可以将输出保存到文本文件,如下所示
如果你收到 ValueError 异常,则此模型未提供聊天模板。请参阅下面的说明了解如何创建聊天模板。
Python(受限模型)
一些模型,例如 Llama 和 Mistral,不允许公开访问其聊天模板。你必须使用上面的 CLI 方法,或者按照以下说明使用 Python
- 对于这些步骤,你必须安装 git 和 git-lfs。
- 你必须拥有 HuggingFace 账户并已登录。
- 你必须已经有权访问受限模型。否则,请申请访问。
- 你必须为 HuggingFace 的 git 访问配置 SSH 密钥。
- 使用 SSH 克隆 URL
git clone
模型的 HuggingFace 仓库。无需下载整个模型,因为模型非常大。在 Linux 上执行此操作的好方法是 - 遵循上述开源模型的说明,但将模型 ID 替换为包含
tokenizer\_config.json
的目录路径。
进阶:聊天模板如何工作?
聊天模板应用于你在聊天窗口中看到的整个对话。模板遍历消息列表,每条消息包含 role
和 content
字段。role
是 user
、assistant
或 system
之一。
GPT4All 还支持特殊变量 bos_token
、eos_token
和 add_generation_prompt
。请参阅 HuggingFace 文档了解它们的作用。
进阶:如何创建聊天模板?
创建聊天模板的最佳方法是以现有模板为参考开始。然后,修改它以使用给定模型文档中记载的格式。其 README 页面可能会明确给出模板示例。或者,它可能提到一个知名标准模板的名称,例如 ChatML、Alpaca、Vicuna。GPT4All 尚未包含这些模板的预设,因此必须在其他模型中查找或从社区获取。
更多信息,请参阅非常有用的 HuggingFace 指南。其中一些信息不适用,例如有关工具调用和 RAG 的信息 - GPT4All 以不同方式实现这些功能。
一些模型使用的提示模板无法直观地映射到多轮聊天,因为它更多用于单个指令。FastChat 对这些模板的实现是正确扩展它们到多条消息的有用参考。
进阶:什么是 GPT4All v1 模板?
GPT4All 支持自己的模板语法,该语法非标准,但提供了对 LocalDocs 源和文件附件插入对话方式的完全控制。这些模板以 {# gpt4all v1 #}
开头,看起来类似于下面的示例。
对于标准模板,GPT4All 将用户消息、源和附件组合到 content
字段中。对于 GPT4All v1 模板,不会这样做,因此必须在模板中直接使用它们,这些功能才能正常工作。
{# gpt4all v1 #}
{%- for message in messages %}
{{- '<|start_header_id|>' + message['role'] + '<|end_header_id|>\n\n' }}
{%- if message['role'] == 'user' %}
{%- for source in message['sources'] %}
{%- if loop.first %}
{{- '### Context:\n' }}
{%- endif %}
{{- 'Collection: ' + source['collection'] + '\n' +
'Path: ' + source['path'] + '\n' +
'Excerpt: ' + source['text'] + '\n\n' }}
{%- endfor %}
{%- endif %}
{%- for attachment in message['prompt_attachments'] %}
{{- attachment['processed_content'] + '\n\n' }}
{%- endfor %}
{{- message['content'] | trim }}
{{- '<|eot_id|>' }}
{%- endfor %}
{%- if add_generation_prompt %}
{{- '<|start_header_id|>assistant<|end_header_id|>\n\n' }}
{%- endif %}