ColossalQA 检索会话系统的提示词设计:从模板结构到超长对话的递归摘要
本文围绕 ColossalAI 应用中 ColossalQA 子项目的 Prompt Design Guide 展开,系统讲解检索问答(Retrieval QA)场景下三类可定制提示词——检索 QA 提示词、摘要提示词、消歧提示词——的完整模板与最终拼装形态,并结合 prompt.py、memory.py 等源码,揭示"检索文档 + 对话历史 + 问题"如何在运行时被组装进上下文、超长对话如何通过递归摘要压缩回上下文窗口预算之内。读完本文,你可以理解 ColossalQA 提示词的每一部分由谁填充、何时触发摘要,以及如何按官方模板结构定制自己的提示词。
三类可定制的提示词
ColossalQA 的检索会话(retriever conversation)系统在链(chain)执行前会向 LLM 拼装提示词,官方设计文档 README.md 指出,用户可以定制三类提示词:
- 检索 QA 提示词(The Retrieval QA Prompt):最终用于生成答案的提示词,输入是三部分——用户问题、检索到的文档、历史对话;
- 摘要提示词(Summarization Prompt):由记忆模块(memory module)使用,对超长对话进行递归摘要,以压缩提示词长度;
- 消歧提示词(Disambiguity Prompt):执行零样本的指代消解(zero-shot reference resolution),将用户问题中模糊的实体指代替换为具体名称。
这三类提示词在源码中统一定义于 prompt.py,全部基于 LangChain 的 PromptTemplate 构建,模板字符串本身即下文各节展示的内容。
检索 QA 提示词:模板与变量
官方设计文档给出的检索 QA 提示词由"安全/有用性声明 + 上下文约束 + 背景信息 + 聊天记录 + 问题"五段构成。文档同时提供了中英文两个版本。
中文版模板
你是一个善于解答用户问题的AI助手。在保证安全的前提下,回答问题要尽可能有帮助。你的答案不应该包含任何有害的、不道德的、种族主义的、性别歧视的、危险的或非法的内容。请确保你的回答是公正和积极的。
如果不能根据给定的上下文推断出答案,请不要分享虚假、不确定的信息。
使用提供的背景信息和聊天记录对用户的输入作出回应或继续对话。您应该只生成一个回复。不需要跟进回答。请使用中文作答。
背景信息:
[retrieved documents]
聊天记录:
[historical conversation, overlength chat history will be summarized]
用户: [question]
Assistant:
该模板明确传达三条约束:回答要尽可能有帮助且安全;无法从上下文推断时不得编造信息;只生成一个回复,无需追问。
英文版模板
[INST] <<SYS>>Always answer as helpfully as possible, while being safe. Your answers should not include any harmful, unethical, racist, sexist, toxic, dangerous, or illegal content. Please ensure that your responses are socially unbiased and positive in nature.
If the answer cannot be inferred based on the given context, please don't share false information.<</SYS>>
Use the context and chat history to respond to the human's input at the end or carry on the conversation. You should generate one response only. No following up is needed.
context:
[retrieved documents]
chat history
[historical conversation, overlength chat history will be summarized]
Human: {question}
Assistant:
需要特别注意的是文档中的说明:[INST] <<SYS>>...<</SYS>> 是 LLaMA2 模型特有的提示格式。英文模板将系统级指令包在 <<SYS>>...<</SYS>> 中,以匹配 LLaMA2 预训练时的对话标记结构,而中文模板则直接使用自然语言指令。这一点决定了模板与模型家族的绑定关系——换用不同指令格式的基座模型时,应相应调整指令包装方式。
从源码看,实际使用的英文模板(_EN_RETRIEVAL_QA_PROMPT,定义于 prompt.py)与设计文档略有差异:它进一步要求"若无法基于上下文推断答案,请说 'I cannot answer the question based on the information given.'",并将模板变量命名为 {context}、{chat_history}、{question}。中文模板(_ZH_RETRIEVAL_QA_PROMPT,prompt.py)则采用 <指令>...<问题> 的结构化标签组织三段变量。两个模板在 PromptTemplate 注册时声明的 input_variables 均为 ["question", "chat_history", "context"],与上文模板中的占位一一对应。
拒答机制:触发关键词与兜底回复
英文/中文模板中都要求模型在无法回答时输出特定句式,这个句式与源码中的"拒答"机制是配套设计的。prompt.py 中定义:
ZH_RETRIEVAL_QA_TRIGGER_KEYWORDS = ["无法回答该问题"]
ZH_RETRIEVAL_QA_REJECTION_ANSWER = "抱歉,根据提供的信息无法回答该问题。"
EN_RETRIEVAL_QA_TRIGGER_KEYWORDS = ["cannot answer the question"]
EN_RETRIEVAL_QA_REJECTION_ANSWER = "Sorry, this question cannot be answered based on the information provided."
在问答链的执行逻辑 base.py 中,链调用结束后会检查 LLM 输出是否包含 rejection_trigger_keywords 中的任一关键词;若命中,则将最终答案替换为 rejection_answer 的兜底文案。也就是说,模板中"不能推断就不要编造"的自然语言约束,还叠加了一层确定性的字符串匹配保障,防止模型以措辞略有差异的方式拒答而污染对话历史。这也是定制提示词时必须保持的契约:你自定义的模板中"无法回答"的措辞,必须与传入的触发关键词一致,否则拒答兜底逻辑会失效。
最终提示词示例:k=3 时的完整形态
设计文档以"检索器返回 k=3 篇文档"为前提,给出了拼装完成后的最终提示词示例。文档分 Normal Length(对话未超长)与 Overlength(对话超长,历史被摘要)两种形态。
英文示例
Normal Length
[INST] <<SYS>>Always answer as helpfully as possible, while being safe. Your answers should not include any harmful, unethical, racist, sexist, toxic, dangerous, or illegal content. Please ensure that your responses are socially unbiased and positive in nature.
If the answer cannot be inferred based on the given context, please don't share false information.<</SYS>>
Use the context and chat history to respond to the human's input at the end or carry on the conversation. You should generate one response only. No following up is needed.
context:
[document 1]
[document 2]
[document 3]
chat history
Human: XXX
Assistant: XXX
...
Human: {question}
Assistant:
Overlength
[INST] <<SYS>>Always answer as helpfully as possible, while being safe. Your answers should not include any harmful, unethical, racist, sexist, toxic, dangerous, or illegal content. Please ensure that your responses are socially unbiased and positive in nature.
If the answer cannot be inferred based on the given context, please don't share false information.<</SYS>>
Use the context and chat history to respond to the human's input at the end or carry on the conversation. You should generate one response only. No following up is needed.
context:
[document 1]
[document 2]
[document 3]
chat history
A summarization of historical conversation:
[one line summary of historical conversation]
Most recent conversation:
Human: XXX
Assistant: XXX
...
Human: {question}
Assistant:
中文示例
Normal Length
你是一个善于解答用户问题的AI助手。在保证安全的前提下,回答问题要尽可能有帮助。你的答案不应该包含任何有害的、不道德的、种族主义的、性别歧视的、危险的或非法的内容。请确保你的回答是公正和积极的。
如果不能根据给定的上下文推断出答案,请不要分享虚假、不确定的信息。
使用提供的背景信息和聊天记录对用户的输入作出回应或继续对话。您应该只生成一个回复。不需要跟进回答。请使用中文作答。
背景信息:
[document 1]
[document 2]
[document 3]
聊天记录:
用户: XXX
Assistant: XXX
...
用户: [question]
Assistant:
Overlength
你是一个善于解答用户问题的AI助手。在保证安全的前提下,回答问题要尽可能有帮助。你的答案不应该包含任何有害的、不道德的、种族主义的、性别歧视的、危险的或非法的内容。请确保你的回答是公正和积极的。
如果不能根据给定的上下文推断出答案,请不要分享虚假、不确定的信息。
使用提供的背景信息和聊天记录对用户的输入作出回应或继续对话。您应该只生成一个回复。不需要跟进回答。请使用中文作答。
背景信息:
[document 1]
[document 2]
[document 3]
聊天记录:
历史对话概要:
[one line summary of historical conversation]
最近的对话:
用户: XXX
Assistant: XXX
...
用户: [question]
Assistant:
四种形态的差异完全体现在 chat history(聊天记录)区域:未超长时直接罗列完整对话;超长时先给一行"历史对话概要"(摘要),再附"最近的对话"。下面结合源码说明这些片段是如何自动拼装出来的。
源码解析:提示词的运行时拼装
Stuff 链:检索文档如何进入 {context}
ColossalQA 使用自定义的 Stuff 文档合并链 CustomStuffDocumentsChain。其 _get_inputs 方法(stuff.py)负责填充 context 变量:
- 每篇文档按
doc_prefix + 编号加前缀(默认前缀为Supporting Document,即 "Supporting Document1"、"Supporting Document2"...),对带is_key_value_mapping元数据的键值型文档,会用metadata["value"]替换正文; - 各文档字符串经
document_separator拼接后整体写入inputs["context"]; - 同时从入参中抽取
stop、temperature、top_k、top_p、max_new_tokens及模板声明的输入变量(question、chat_history、context),交给LLMChain。
而 chat_history 变量则由记忆模块提供——base.py 中,链在执行前会先保存对话缓冲区快照,调用 combine_documents_chain.run(input_documents=docs, question=question),执行完成后再恢复缓冲区状态并在末尾保存本轮问答,从而保证记忆状态不因重试或异常而错乱。问答链本身由 load_chain.py 的 load_qa_chain 构建,当前仓库中仅实现了 stuff 一种 chain_type,且接受任意 BasePromptTemplate 作为 prompt 参数——这正是"用户可定制提示词"的入口。
摘要提示词与超长对话处理流程
递归摘要机制
设计文档对摘要提示词的一句话描述是:"用于由记忆模块对超长对话进行递归摘要,以收缩提示词长度"。其实现位于 summary.py 的 SummarizerMixin.predict_new_summary(summary.py):
def predict_new_summary(self, messages, existing_summary, stop=[]) -> str:
new_lines = get_buffer_string(messages, human_prefix=..., ai_prefix=...)
chain = LLMChain(llm=self.llm, prompt=self.prompt, llm_kwargs=self.llm_kwargs)
return chain.predict(summary=existing_summary, new_lines=new_lines, stop=stop)
"递归"体现在:每次只取一对新的用户/助手消息,连同已有的摘要(existing_summary)一起送入提示词,生成融合新对话的新摘要,再以此作为下一次迭代的 existing_summary。摘要提示词因此必须声明且仅声明 summary 与 new_lines 两个变量——summary.py 中的 validate_prompt_input_variables 校验器会强制检查这一点,变量不符会直接抛出 ValueError。
仓库内置的中文摘要模板(_CUSTOM_SUMMARIZER_TEMPLATE_ZH,prompt.py)要求模型"将当前对话的摘要内容添加到先前已有的摘要上",并给出一个 few-shot 例子(人类询问 AI 对人工智能的看法、AI 补充"人工智能是善的力量"),演示如何把新对话融入旧摘要产出一行新摘要;英文场景则默认沿用 LangChain 的 SUMMARY_PROMPT(见 summary.py)。摘要生成时传入了 stop=["\n\n"] 并取第一行结果(见下文 format_dialogue),以保证摘要保持单行形态。
token 预算与 "历史概要 + 最近对话" 结构
摘要何时触发、触发几次,由 memory.py 中的 ConversationBufferWithSummary 控制。关键流程在 load_memory_variables(memory.py):
- 先用与问答相同的提示词模板和本次检索到的文档计算当前提示词长度
prompt_length(self.chain.prompt_length(docs, **inputs)),得到对话历史可用的 token 预算remain = max_tokens - prompt_length(max_tokens默认 2000); - 只要格式化后的对话长度超过
remain,就从对话缓冲区头部成对弹出消息,移入临时摘要缓冲区summarized_history_temp; - 若剩余消息不足 2 条仍超长,抛出
RuntimeError("Exceed max_tokens, trunk size of retrieved documents is too large"),提示检索文档切块过大。
随后 format_dialogue(memory.py)对临时缓冲区逐对调用 predict_new_summary 累积 existing_summary,并按语言拼接最终 chat_history 字符串:
message = f"A summarization of historical conversation:\n{self.existing_summary}\nMost recent conversation:\n{conversation_buffer}" # en
message = f"历史对话概要:\n{self.existing_summary}\n最近的对话:\n{conversation_buffer}" # zh
这两行代码产出的格式,正是设计文档"Overlength"示例中 A summarization of historical conversation: / Most recent conversation:(以及中文的 历史对话概要: / 最近的对话:)的来源;若尚无摘要,则只输出原始对话,对应 "Normal Length" 示例。至此,设计文档中四种最终提示词示例与代码实现形成一一对应。
注意一个容易忽略的细节:initiate_document_retrieval_chain(memory.py)要求外部传入 prompt_template,用同一个模板构建一条只用于测量长度的问答链。这意味着摘要预算是"模板 + 文档 + 问题"三者共同挤占后的剩余空间——检索文档越多、模板越长,留给对话历史的 token 越少,越容易触发摘要。
消歧提示词:零样本指代消解
设计文档对消歧提示词的描述是:"用于执行零样本指代消解,消除用户问题中的实体指代歧义"。仓库中的中英文模板(prompt.py 与 prompt.py)采用单例示范(single-example / one-shot 形式的 few-shot 说明)引导模型行为:
- 任务:用聊天记录中提到的具体名称/实体替换句子中模糊的指代;若无聊天记录或句子本身无歧义,则原样输出;
- 输出约束:只输出消歧后的句子本身,与提示行同一行,不得包含其他内容;
- 示例:聊天记录中"我有一个朋友,张三"之后,句子"他最喜欢的食物是什么?" 应被改写为"张三最喜欢的食物是什么?"(英文模板对应 Mike 的例子)。
模板变量为 chat_history 与 input。从源码结构看,消歧发生在问答之前:先把用户当前输入改写为自包含的明确句子,再送入检索器与问答链,这样检索阶段拿到的 query 就不含"他/它/那个公司"这类无法单独检索的代词。英文模板还完整保留了 LLaMA2 的 [INST]...[/INST] 与 <<SYS>> 标记结构。
如何定制这三类提示词
三类提示词对应的可导入模板对象均定义在 prompt.py:
| 提示词 | 模板对象 | 输入变量 |
|---|---|---|
| 英文检索 QA | PROMPT_RETRIEVAL_QA_EN |
question, chat_history, context |
| 中文检索 QA | PROMPT_RETRIEVAL_QA_ZH |
question, chat_history, context |
| 英文消歧 | PROMPT_DISAMBIGUATE_EN |
chat_history, input |
| 中文消歧 | PROMPT_DISAMBIGUATE_ZH |
chat_history, input |
| 中文摘要 | SUMMARY_PROMPT_ZH |
summary, new_lines |
定制时的三条约束来自源码的硬性校验与运行逻辑:
- 检索 QA 模板必须包含
context、chat_history、question三个变量(context的变量名需与CustomStuffDocumentsChain.document_variable_name一致,默认为context);其中"无法回答"的措辞要与传入的rejection_trigger_keywords匹配; - 摘要模板必须恰好包含
summary与new_lines两个变量,否则 summary.py 的校验器会拒绝构造; - 消歧模板需要
chat_history与input两个变量,且输出必须保持"只有一行消歧句"的约定,供下游环节安全解析。
替换模板的入口是 load_qa_chain 的 prompt 参数(load_chain.py)以及 ConversationBufferWithSummary.initiate_document_retrieval_chain 的 prompt_template 参数(memory.py)。完整的会话组装与运行流程可参考示例 retrieval_conversation_universal.py 与测试 test_retrieval_qa.py,后者通过 UniversalRetrievalConversation 分别以中文/英文问题验证了整条"检索—拼装—回答"链路。
小结
ColossalQA 的提示词设计可以概括为"一个主模板 + 两个辅助模板":检索 QA 模板决定最终答案的拼装方式(安全声明、上下文约束、context/chat_history/question 三段变量,英文版本适配 LLaMA2 指令格式);摘要模板配合 token 预算机制,把超长对话压缩为"一行概要 + 最近对话",保证提示词不超出 max_tokens;消歧模板在检索前完成零样本指代消解,让 query 自包含。设计文档中的四份最终提示词示例(中/英文 × 常规/超长)并非静态样例,而是 CustomStuffDocumentsChain 与 ConversationBufferWithSummary 在运行时真实产出的结构。理解模板变量契约(变量名与数量的硬校验)和拒答关键词的字符串匹配机制,是安全地定制这三类提示词的前提。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0623
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00