以下是“当前问题补全与改写”小节的内容,定位为 RAG 系统检索前的查询优化环节,力求实用、真实、简洁。
当前问题补全与改写
在实际的智能问答系统中,用户很少一次就把问题描述得完整、清晰。尤其是在多轮对话场景中,后续提问常常是省略的、跳脱的,或带有大量指代词。如果直接将用户的原始提问送入检索模块,召回的相关文档片段往往质量很低,直接影响最终回答的准确性。
问题补全与改写就是在检索之前,对用户当前输入进行自动化加工,使其变成一个“完整、独立、易于检索”的查询语句。这一环节直接决定了 RAG 系统能否准确找到正确的上下文。
1. 为什么需要这一步
考虑一个典型的多轮对话:
- 用户第一轮:“公司年假有多少天?”
- 系统回答:“每年 15 天。”
- 用户第二轮:“那老员工呢?”
如果第二轮直接以“那老员工呢”去检索,向量数据库几乎不可能命中正确的内容——这个问句缺少了“年假”“公司”等核心词汇,语义过于模糊。
类似的问题还包括:
- 指代不明:“它”、“他”、“这个”、“那个”指什么?
- 上下文省略:前文提到了某个产品型号,后续只问“颜色有哪些”,缺少型号限定。
- 口语化表达:“上次说的那个报销流程,再讲一下”,其中的“上次说的那个”需要还原成具体政策名。
问题补全与改写的目的,就是将这类不完整的、依赖上下文的问题,重构成一个自包含的、可以直接用于检索的完整查询。
2. 常见的实现方式
实际工程中,最灵活、效果最好的方式是借助大语言模型本身来完成改写。基本流程如下:
- 保存历史对话摘要或关键实体
系统维护一个精简的上下文记录,可以是最近几轮对话的原文,也可以是用模型提取出的核心实体(如产品名称、政策标题、当前用户问题所处的业务领域)。
- 构造改写提示词
将上下文信息与当前问题拼成一个指令,要求模型输出一个独立的、适合检索的查询。例如:
> 以下是用户与助手的对话历史:
> 用户:公司年假有多少天?
> 助手:每年 15 天。
> 当前用户问题:那老员工呢?
> 请将当前问题改写成一个不依赖对话历史的独立问题,能够清晰表达用户的真实意图。
LLM 可能会输出:“老员工的年假天数是多少?”
- 用改写后的查询进行检索
将改写后的问题送入嵌入模型生成向量,再去向量数据库中检索。
这种方式也称为查询重写。它可以有效消解指代、补全省略,甚至纠正用户输入中的错别字或不规范表达。
3. 实践中的优化要点
- 保留原始意图,不过度扩展
改写时只需补全缺失的上下文信息,使问题能独立看懂,不要随意增加用户没问的内容。例如用户问“颜色有哪些”,如果前文明确是讨论“X100 型号”,改写为“X100 型号有哪些颜色”即可,不必扩展成“X100 型号的外观设计、配色方案及可选颜色清单”。
- 对单轮也可以做优化
即便没有历史对话,用户首次提问也可能很口语化或模糊。例如输入“最近那个政策改了啥”,改写模块可以结合知识库中最近更新的文档标题,推断用户可能指什么,或至少补全为“最近的哪个政策”(然后返回澄清问题),但实用中更常见的做法是让 LLM 根据通用知识将模糊表达标准化。
- 提取关键词与生成多路查询
除改写出一句自然语言查询外,还可以同时输出几个关键词组合,或多路改写版本(一种扩展查询策略),分别检索后合并结果,提高召回覆盖面。例如将“年假政策”改写为“年假 政策 修订”等。
- 缓存与成本控制
每次调用 LLM 做改写会增加延迟和费用。对于简单的指代消解,也可以先用规则(如将“它”替换为上一轮抽取的实体)处理一部分,复杂情况再交给模型。实际部署时通常用一个小参数模型(或专门的查询改写模型)来完成,平衡效果与效率。
4. 一个真实的改进案例
某公司内部 HR 助手上线初期,发现很多多轮对话中检索结果不相关。日志分析后发现,用户常问“那需要什么材料?”“多久能办好?”之类问题,检索模块只能匹配到“材料”“办好”这些弱信号词,返回一堆无用片段。
引入查询改写层后,系统将第二轮问题结合首轮提到的“异地报销申请”补全为“异地报销申请需要什么材料?”“异地报销申请多久能办好?”。修改后,检索命中率提升约 40%,用户因“答非所问”而转人工的比例明显下降。
5. 与其他优化环节的关系
问题补全与改写通常位于 RAG 流水线中查询处理的第一步。它之后可能还会衔接查询扩展(增加同义词、子问题拆分)或意图分类(决定走哪个子知识库)。但究其根本,补全与改写解决的是“用户到底在问什么”的问题,是保障检索质量的先决条件。
在实际小型或原型系统中,如果暂时没有多轮对话场景,也可以先跳过这一步,直接检索。但当系统需要支持连续对话、或者用户输入质量不高时,问题补全与改写会变得不可或缺。