- 大模型
- 人工智能
- 微调
- 本地部署
- AI Agent
- RAG
【免费下载链接】ChatGLM3
ChatGLM3 series: Open Bilingual Chat LLMs | 开源双语对话语言模型
本篇文章完整解读 ChatGLM3 系列开源模型所采用的全新对话格式(Chat Format)。该格式是 ChatGLM3 在项目仓库中统一多轮对话、工具调用(Tool & Agent)与代码解释器(Code Interpreter)三类任务的输入骨架,其规范定义见 PROMPT.md。读完本文,你将掌握<|system|>、<|user|>、<|assistant|>、<|observation|>四种角色的拼接规则与防注入设计原理,理解仓库中 conversation.py、demo_tool.py、demo_ci.py 等示例是如何逐 token 解析这套格式的,并能在自己的应用中正确构造与解析 ChatGLM3 的对话输入。
为什么 ChatGLM3 需要一套全新的对话格式
传统对话模型通常用"用户:/ 助手:"之类的纯文本拼接来组织上下文,且代码执行、工具调用等任务往往各自有独立的 prompt 模板,彼此之间难以互通。ChatGLM3 设计全新对话格式,主要有两个目标(见 PROMPT.md 开篇说明):
- 避免用户输入的注入攻击:对话头中的角色标记(如
<|user|>)使用special token表示,无法从文本形式被 tokenizer 编码,因此用户即使把<|user|>、<|assistant|>之类字符串写进自己的输入,也不会被模型误解为"角色切换指令"。 - 统一 Code Interpreter、Tool & Agent 等任务的输入:无论是普通闲聊、让模型调用天气工具,还是让它写 Python 代码并执行,都采用同一套角色框架,降低上层应用接入成本。
整体结构:对话 = 对话头 + 内容
ChatGLM3 的对话由若干"对话(conversation)"组成,每个对话包含对话头与内容两部分。一个典型的多轮对话结构如下:
<|system|> You are ChatGLM3, a large language model trained by Zhipu.AI. Follow the user's instructions carefully. Respond using markdown. <|user|> Hello <|assistant|> Hello, I'm ChatGLM3. What can I assist you today?需要说明的是,实际中每轮对话内容并不一定以换行符结尾,示例中的换行只是为了美观。在仓库的流式输出实现中,client.py 将每轮对话经由tokenizer.build_chat_input(query, history=history, role=role)编码为模型输入,即历史列表中的每条记录最终都会按这套结构拼进输入序列。
对话头规范:<|role|>{metadata}
对话头占完整的一行,格式为:
<|role|>{metadata}其中<|role|>部分使用special token表示,无法从文本形式被 tokenizer 编码,以此防止注入攻击;metadata部分采用纯文本表示,是可选内容。四种角色及其约束如下(引自 PROMPT.md):
<|system|>:系统信息。设计上可穿插于对话中,但目前规定仅可以出现在开头;<|user|>:用户。不会连续出现多个来自<|user|>的信息;<|assistant|>:AI 助手。在出现之前必须有一个来自<|user|>的信息;<|observation|>:外部的返回结果(如工具调用返回值、代码执行结果)。必须在<|assistant|>的信息之后。
从源码结构看,这四种角色在仓库中有明确映射:在 conversation.py 中定义了Role枚举,Role.SYSTEM / USER / ASSISTANT / TOOL / INTERPRETER / OBSERVATION的字符串表示分别为<|system|>、<|user|>、<|assistant|>(其中 TOOL 与 INTERPRETER 也归为<|assistant|>)、<|observation|>——这与TOOL、INTERPRETER都通过<|assistant|>的 metadata 来区分的格式设计一一对应。
metadata 的作用:区分"普通回复"与"动作"
metadata 是对话头中紧随角色名之后的纯文本,是判断当前<|assistant|>是在"说话"还是在"执行动作"的关键:
- 工具调用时,
<|assistant|>的 metadata 为工具名,如<|assistant|>get_current_weather; - 代码执行时,
<|assistant|>的 metadata 只有interpreter,即<|assistant|>interpreter。
openai_api_demo/utils.py 中的process_response正是按此规则解析:它把模型输出按<|assistant|>分割,若 metadata 为空则为普通文本回复;若 metadata 非空且启用了工具,则识别为一次函数调用,返回形如{"name": "<工具名>", "arguments": "<JSON 参数>"}的结构。
样例场景一:多轮对话(基础格式)
多轮对话是最简单的场景,有且仅有<|user|>、<|assistant|>、<|system|>三种 role,不允许出现<|observation|>:
<|system|> You are ChatGLM3, a large language model trained by Zhipu.AI. Follow the user's instructions carefully. Respond using markdown. <|user|> Hello <|assistant|> Hello, I'm ChatGLM3. What can I assist you today?在仓库的 basic_demo/cli_demo.py 中,构建历史 prompt 时把用户输入与模型回复按顺序追加到对话历史,再由model.stream_chat(...)维护多轮上下文;而 basic_demo/web_demo_gradio.py 与 web_demo_streamlit.py 则展示了 Web 界面下的同格式多轮交互。
样例场景二:工具调用(Tool & Agent)
工具调用在基础多轮对话之上引入了工具描述(tools 列表)、动作发起与观察结果回填三个新要素。完整示例见 PROMPT.md:
<|system|> Answer the following questions as best as you can. You have access to the following tools: [ { "name": "get_current_weather", "description": "Get the current weather in a given location", "parameters": { "type": "object", "properties": { "location": { "type": "string", "description": "The city and state, e.g. San Francisco, CA", }, "unit": {"type": "string"}, }, "required": ["location"], }, } ] <|user|> 今天北京的天气怎么样? <|assistant|> 好的,让我们来查看今天的天气 <|assistant|>get_current_weather ```python tool_call(location="beijing", unit="celsius") ``` <|observation|> {"temperature": 22} <|assistant|> 根据查询结果,今天北京的气温为 22 摄氏度。解析这段完整流程:
- 系统消息携带工具清单:
<|system|>后紧跟固定引导语Answer the following questions as best as you can. You have access to the following tools:以及 JSON Schema 风格的工具定义数组。工具定义包含name、description、parameters(含type、properties、required等字段)。 - 模型发起工具调用:第二个
<|assistant|>的 metadata 是工具名get_current_weather,其内容是一个 Python 代码块,通过tool_call(location="beijing", unit="celsius")表达需要传给工具的参数。 - 外部返回结果:
<|observation|>承载真实工具执行的返回(如{"temperature": 22}),模型据此生成最终回复。
工具调用在仓库中的真实实现
- 工具定义与注册:tools_using_demo/tool_register.py 提供
@register_tool装饰器,从 Python 函数的签名、docstring 与typing.Annotated类型标注自动生成上述 JSON 工具描述,保证定义格式与示例一致以获得最优性能。 - 工具调用 Demo:tools_using_demo/cli_demo_tool.py 展示了以
role="observation"把工具返回值喂回模型、并依据response是str还是dict判断"继续对话"还是"继续调用工具"的完整循环。注意官方文档明确:目前 ChatGLM3-6B 的工具调用只支持通过chat方法,不支持stream_chat方法(见 tools_using_demo/README.md)。 - 流式解析工具调用:composite_demo/demo_tool.py 逐 token 消费生成流:遇到
<|assistant|>特殊 token 即认为开始工具调用;遇到<|observation|>则把输出文本按行拆分,首行是工具名,剩余内容用正则extract_code提取代码块、以eval(code, {'tool_call': tool_call}, {})解析出参数,随后调用dispatch_tool(tool, args)执行并把结果回填为Role.OBSERVATION。 - 多轮历史构造:composite_demo/conversation.py 的
preprocess_text按<|system|>\n+ 系统提示(有工具时替换为TOOL_PROMPT)+ 工具 JSON + 历史对话 +<|assistant|>\n的顺序拼装最终 prompt,并在 client.py 中把Conversation列表转成带role/metadata/content的 chat history 提交给stream_chat。
样例场景三:代码执行(Code Interpreter)
代码执行场景允许出现全部四种 role:<|user|>、<|assistant|>、<|system|>、<|observation|>,其中<|assistant|>的metadata 只有 interpreter。完整示例见 PROMPT.md,这里摘录关键流程:
<|system|> 你是一位智能AI助手,你叫ChatGLM3,你连接着一台电脑,但请注意不能联网。在使用Python解决任务时,你可以运行代码并得到结果,如果运行结果有错误,你需要尽可能对代码进行改进。你可以处理用户上传到电脑上的文件,文件默认存储路径是/mnt/data/。 <|user|> #File: /mnt/data/metadata.jsonl #Size: 35380 #File uploaded 文件中是否存在缺失值或异常值? <|assistant|> 首先,我将读取您提供的文件,并查看其内容以确定是否存在缺失值或异常值。 我们从加载文件开始: <|assistant|>interpreter ```python import json # Load the uploaded file data = [] with open("/mnt/data/metadata.jsonl", 'r') as file: for line in file: data.append(json.loads(line)) # Display the first few records to get a sense of the data data[:5] ``` <|observation|> ```result [{'file_name': 'aceinthehole.png', ...}] ``` <|assistant|> 该文件看起来包含有关某些条目的元数据……该示例展示了代码执行的三段式循环:模型先用普通<|assistant|>文字说明思路 → 再用<|assistant|>interpreter附带 Python 代码块 → 代码在沙箱/内核中执行后,以<|observation|>中包裹```result ... ```的形式返回输出(文本或[Image])。此后模型可继续写代码(如检查缺失值、统计 type 分布、用 matplotlib 画爱心),直到完成回答。
代码执行在仓库中的真实实现
- 系统提示一致:composite_demo/demo_ci.py 中定义的
SYSTEM_PROMPT与 PROMPT.md 代码执行示例的系统消息完全一致("你是一位智能AI助手,你叫ChatGLM…文件默认存储路径是/mnt/data/")。 - 执行内核:composite_demo/demo_ci.py 的
CodeKernel基于jupyter_client启动 IPython 内核(可通过环境变量IPYKERNEL指定内核名,默认chatglm3-demo),execute(code)同步提交代码并轮询消息直到内核空闲,返回标准输出或image/png结果。 - 输出清洗:composite_demo/demo_ci.py 在执行前会剥除输出文本中残留的
<|observation|>、<|assistant|>interpreter、<|assistant|>等特殊 token;conversation.py 的postprocess_text同样会把<|assistant|>、<|observation|>、<|system|>、<|user|>从展示文本中剔除,避免角色 token 泄漏到用户界面。
从源码看对话格式的底层机制
1. 特殊 token 参与停止条件
在 composite_demo/client.py 与 openai_api_demo/utils.py 中,eos_token_id均被设置为[tokenizer.eos_token_id, tokenizer.get_command("<|user|>"), tokenizer.get_command("<|observation|>")]。这意味着模型生成到<|user|>或<|observation|>时就会停止——与格式规定中"<|assistant|>之后要么轮到用户、要么轮到外部 observation"的结构约束完全呼应,从生成侧保证了对话不会越界。
2. 角色 token 参与停止序列
composite_demo/demo_tool.py 与 demo_ci.py 都把stop_sequences设置为<|user|>与<|observation|>的字符串形式,流式生成时一旦命中即截断并进入相应的角色分支处理。
3. 防注入的具体落地
对话头中的角色标记是 special token,用户文本无论怎么写都无法被编码成这些 token,这是从 tokenizer 层面杜绝注入;而postprocess_text/process_response等工具函数则在展示层进一步清洗这些标记,防止其被当作普通文本展示或二次注入(如 openai_api_demo/utils.py 的process_chatglm_messages会把 OpenAI 风格消息转换为 ChatGLM3 角色,其中function角色的返回被转换为observation)。
实际使用中的注意事项
- 换行符:为提升可读性,示例中角色 special token 前额外添加了换行符;实际使用及 tokenizer 实现中均无需额外添加这一换行(见 PROMPT.md 说明)。
- 角色顺序约束:
<|system|>目前只能出现在开头;<|user|>不能连续出现;<|assistant|>之前必须有<|user|>;<|observation|>必须紧跟<|assistant|>(或其后续动作)之后。违反这些约束会破坏模型对上下文的解析。 - 工具调用与流式:ChatGLM3-6B 的工具调用目前仅支持
chat方法(见 tools_using_demo/README.md);role="observation"参数在回填工具结果时不可省略。 - 多次工具调用:复杂问题模型可能连续发起多次工具调用,可通过判断返回的
response是str还是dict来区分"最终回复"与"新一轮工具调用请求"。 - 超长输入与截断:composite_demo/client.py 对输入序列长度与
max_new_tokens之和做了上限检查,超限时给出调整生成参数的提示;工具/代码执行结果也可通过truncate_length截断(demo_tool.py)。
小结
ChatGLM3 的对话格式以四种角色(<|system|>、<|user|>、<|assistant|>、<|observation|>)+ 可选 metadata 为核心,既用 special token 从编码层面杜绝注入,又把多轮对话、工具调用(metadata=工具名)与代码执行(metadata=interpreter)统一在同一个输入框架内。仓库中的 composite_demo(含 conversation.py、demo_tool.py、demo_ci.py、client.py)、tools_using_demo、openai_api_demo 与 langchain_demo 提供了从拼装、解析到执行的完整参考实现,是深入理解并二次开发这套对话格式的最佳起点。
- 大模型
- 人工智能
- 微调
- 本地部署
- AI Agent
- RAG
【免费下载链接】ChatGLM3
ChatGLM3 series: Open Bilingual Chat LLMs | 开源双语对话语言模型
相关推荐
Claude Sonnet 4 系统提示词深度解析:基于 leaked-system-prompts 泄露文档的行为规范拆解与演化对比
Claude Sonnet 4 系统提示词深度解析:基于 leaked system prompts 泄露文档的行为规范拆解与演化对比 本文以开源仓库 leak
人工智能大模型提示工程ChatGPT 语音助手系统提示词全解析:GPT-4o-mini Voice Mode 的人格设定与对话规范剖析
ChatGPT 语音助手系统提示词全解析:GPT 4o mini Voice Mode 的人格设定与对话规范剖析 导读 本文基于本仓库中泄露的 openai c
人工智能大模型提示工程ChatGPT-5 系统提示词全解:来自 leaked-system-prompts 的完整工具链与人格规范分析
ChatGPT 5 系统提示词全解:来自 leaked system prompts 的完整工具链与人格规范分析 本文基于开源仓库 leaked system
人工智能大模型提示工程
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考