查询改写与多轮独立化
查询改写(Query Rewriting)在检索前增加一步:根据用户的问题,生成一条更适合检索的查询,再用它查知识库。需要改写的原因通常有两类:问题依赖前面的对话,单独看不完整;或者问题已经完整,但用词与文档相差较大。
多轮独立化处理第一类问题。它把“那去年呢”这样的追问补成脱离历史也能理解的问题。完成独立化之后,还可以调整用词,使查询更接近知识库中的表达。
先把追问补成完整问题
看这段差旅报销对话:
用户:2026 年境内出差,回来后多久要交报销?
助手:现有制度要求差旅结束后 30 个自然日内提交。
用户:那去年呢?检索器如果只收到“那去年呢”,既不知道事项,也不知道要找哪类期限。把全部历史拼进查询同样有问题:上一轮的“30 个自然日”会混在查询里,而这一轮恰恰要重新查找旧版本的期限。
独立化需要从历史中取回“境内差旅”“提交报销”“差旅结束后”这些仍然适用的信息,再把年份改成 2025:
当前追问:那去年呢?
继承的内容:境内差旅、报销提交期限、从差旅结束起计算
变化的内容:2026 年 → 2025 年
独立问题:2025 年境内差旅结束后,应在多久内提交报销?这里将“去年”按前一轮讨论的 2026 年解析;对话如果没有明确的参照时间,就需要先问清年份。上一轮回答中的“30 个自然日”不应写进独立问题,否则改写器相当于替新一轮检索提前填了答案。
可以用这样的提示词生成独立问题:
根据对话历史,将用户当前问题补全为可以独立检索的问题。
补全省略的对象、指代和仍然适用的条件,保留用户本轮修改的条件。
不要把历史回答中的结论填入问题。
如果无法确定指代或时间,返回需要澄清的内容。
对话历史:{history}
当前问题:{message}独立化也需要识别话题切换。用户接着问“我的报销单付款了吗”,目标已经变成个人单据状态,不能继续带入“2025 年境内制度”。对话历史提供上下文,但每一轮都要重新判断哪些信息仍然适用。
再把口语表达改成检索表达
得到完整问题后,还可能遇到措辞差异。用户说“出差回来把报销交上去”,制度写的是“行程结束后提交费用报销申请”。可以将前者改写为后者的表达:
完整问题:2025 年境内出差回来后,多久必须把报销交上去?
改写查询:2025 年境内差旅结束后的费用报销提交期限这一步保留信息需求,只调整表达。将“提交期限”改成“审批后付款时间”则改变了问题,即使检索到了有关报销的文档,也不能回答用户的请求。
不同检索器需要的查询形式也不完全一样:
| 检索方式 | 查询示例 | 处理重点 |
|---|---|---|
| 向量检索 | 境内差旅结束后的费用报销提交期限 | 用完整、明确的语义表达需求 |
| 关键词检索 | 境内 差旅 费用报销 提交 申报 时限 | 用文档可能出现的词项覆盖不同表述 |
| 元数据过滤 | policy_year = 2025 | 让数据库严格限定版本年份 |
向量查询通常不需要堆叠许多同义词;给关键词查询补充“申报”等词项,则属于查询扩展(Query Expansion)。从自然语言生成元数据条件的方法见 Self-Query。
在 RAG 链路中的位置
改写只影响检索输入。检索结果仍然交给回答模型,回答模型同时接收用户问题和必要的对话上下文:
当前问题 + 必要的对话历史
↓ 多轮独立化
完整问题
↓ 调整检索表达
查询知识库
↓
检索片段 + 完整问题 + 用户原问题
↓
生成回答两次转换可以由同一次模型调用完成。下面用伪代码表示数据如何流转:
def answer_turn(message, history):
prepared = prepare_query(message, history)
if prepared.needs_clarification:
return prepared.clarification_question
chunks = retrieve(prepared.retrieval_query)
return generate_answer(
user_message=message,
standalone_question=prepared.standalone_question,
evidence=chunks,
)prepare_query 输出的完整问题和检索查询应分别保留,便于发现错误发生在哪一步。例如完整问题仍写着“提交”,检索查询却变成了“付款”,问题出在表达改写;如果完整问题已经把年份补错,则要回查对话解析。
对于单轮、信息充分的请求,也可以让原查询与改写查询分别检索,再合并结果。多轮省略问题则通常需要先完成独立化,“那去年呢”本身不能充当有效的检索输入。
哪些内容不该被改掉
编号、函数名、版本号、否定词和“仅限”等限制,往往直接决定目标文档。用户查询 TRAVEL-2025-01,把它改成“旧版差旅政策”会丢失精确入口。用户要求“无需主管审批的报销事项”,删掉“无需”会把需求反转。
这些明确内容可以用规则辅助检查,但规则不能证明任意两句话语义等价。改写后若年份、对象或范围仍有歧义,应澄清后再查。对于已经包含准确文档标题、错误码或编号的问题,可以直接检索,省去一次生成调用。
改写效果最终要看目标条款是否更容易被找到。回到开头的追问,如果旧版规定为 60 个自然日,检索就应找到 2025 年的提交条款,而非继续返回 2026 年的 30 天规定。句子是否通顺,无法代替这项判断。
参考阅读
- Query Rewriting for Retrieval-Augmented Large Language Models:提出 Rewrite-Retrieve-Read,并研究利用阅读模型反馈训练改写器。本文介绍的是使用提示词进行改写的流程;训练改写器是进一步的选择。
- Multi-Query 与 RAG-Fusion:当一条改写查询仍然覆盖不了文档的不同表达时,可以尝试多条查询。