每日一技第三天——RAG的查询重写机制
导入
先看这样一个场景
前两天,我用豆包查高考作文题。我说:
“豆包,简单回答一下今年新高考二卷的语文作文要求写什么。”
它给了我材料主旨和核心立意。我接着又问了一句:
“英语呢?”
就两个字,但豆包准确地回答了新高考二卷的英语作文要求。
它知道我在问什么——它记住了“新高考二卷”,记住了“作文”。
这个体验很自然,自然到我们几乎不会去思考它为什么能做到。
但如果我们把视角切到后台,看看这短短两个字的背后发生了什么,你会发现事情远没有那么简单。
当我说“英语呢”,豆包的系统实际上做了两件事:
第一,它回头看了一眼我们的对话历史,确认了“新高考二卷”这个实体和“作文”这个主题,然后默默地把我的问题补全成了——“新高考二卷的英语作文要求是什么?”
第二,它把这个补全后的问题,拿去检索或生成答案,而不是直接用“英语呢”这三个字去理解。
这两件事,共同构成了一个看似简单的能力:让AI能听懂“人话”,能理解那些“你懂的”“那个”“还是刚才那个问题”这类人类对话中再正常不过的省略和指代。
但在我自己搭建RAG系统的过程中发现,这恰恰是最容易翻车的地方。当用户第三轮问“那第二个方案呢”,我的机器人一脸茫然——它完全不知道“第二个方案”指的是什么。
我们的RAG系统是需要先去向量数据库中检索相关的chunk,然而检索系统拿到的query是用户的原始问题,那向量化后和我们想要的chunk的向量距离可能差距有点大,检索回的结果大概不是我们想要的。
这个问题的根本就是,在经历之前的会话记忆加载后大模型有了记忆,但是呢,检索系统没有,但是我们可以通过Query改写来补全原始的问题。
如何让RAG系统具备有效的Query改写能力,让对话像豆包那样连贯自然,正是这篇文章要探讨的核心问题。
Query改写要解决的问题
问题一:指代消解(Reference Resolution)
用户说的“它”、“那个”、“这个”指的是什么?
| 用户原话 | 需要补全成 |
| “那第二个方案呢?” | “那第二个方案的价格是多少?” |
| “它支持这个功能吗?” | “(刚才提到的那个产品)支持(刚才提到的那个功能)吗?” |
| “那红色的呢?” | “那红色的款式有货吗?” |
用户的表达天然依赖上下文,但检索系统和LLM没有“眼睛”回头看,如果你不把指代物显式写进查询,它根本无法知道“它”是什么。
问题二:省略补全(Ellipsis Resolution)
用户省掉的主语、谓语、宾语是什么?
| 用户原话 | 完整语境 | 需要补全成 |
| “英语呢?” | 上下文在聊“新高考二卷的语文作文” | “新高考二卷的英语作文要求是什么?” |
| “那北京呢?” | 上文在问“上海的天气怎么样” | “北京的天气怎么样?” |
| “那后来呢?” | 上文在讲一个故事的经过 | “(那个故事的)后续发生了什么?” |
人类对话中省略是常态,但检索系统需要完整的句子才能做语义匹配。拿“英语呢”去向量库里搜,匹配到的大概率是不相关的内容。
问题三:意图澄清与补全(Intent Completion)
用户真正想问的是什么? 有时用户的问题太短、太模糊,甚至包含错别字,需要结合历史推断完整意图。
| 用户原话 | 上下文 | 推断后的完整意图 |
| “那便宜的那个呢?” | 之前讨论过两款产品 | “那款价格更低的产品有哪些参数?” |
| “有风险吗?” | 之前提到某个投资方案 | “(刚才提到的投资方案)存在哪些风险?” |
| “确定吗?” | 前文模型给出一个结论 | “(前文结论)的置信度/依据是什么?” |
问题四:多轮对话中的信息衰减与聚焦(Information Focus)
有时候用户问的不是“缺了什么”,而是“问的是什么”。
在多轮对话中,用户的关注点可能发生了转移,但他不会明说。Query改写不仅要“补全”,有时还要重新聚焦。
例如:
第1轮:“帮我看看A产品的价格”
第2轮:“那B产品呢”(改写为“B产品的价格是多少”——主语变了,但谓宾沿用)
第3轮:“能便宜点吗”(改写为“A产品和B产品分别能便宜多少”——此时主语变成了“A和B”,谓语变成了“便宜多少”,需要综合前两轮推断)
Query改写的本质是:把用户丢给你的“碎片”,基于对话历史拼回一张完整的“拼图”。
问题五:多意图混合问题
以上四个问题(指代、省略、意图、聚焦)还只是“单个问题”的补全。但现实中的对话更复杂——用户的一句话里,往往同时包含多个意图。
比如:“新高考二卷的语文和英语作文要求分别是什么?”
这里有两个平行的意图。如果我只把它改写成“新高考二卷的语文和英语作文要求”,拿去检索,大概率只能翻到其中一篇相关的资料,另一篇被模糊掉了。
更复杂的是:“那便宜的方案能用吗?”
这句话至少依赖两个前置意图:一是“哪个是便宜的方案”,二是“这个方案是否可用”。如果第二步的改写没有先解决“哪个是便宜的方案”,第三步的回答一定跑偏。
所以,有效的Query改写,不仅要“补全”,还要“拆解”,甚至要“排顺序”。这就是多意图混合问题——Query改写中最具挑战性的一环。
Query改写的核心策略
策略一:基于规则的改写(Rule-Based Rewriting)
核心思想:预定义一系列规则,匹配特定模式后进行字符串替换或补全。
常见规则类型:
规则类型 | 触发条件 | 改写动作 | 示例 |
指代词替换 | 检测到“它/那个/这个” | 替换为上一轮提到的实体 | “它支持吗”→“A产品支持吗” |
省略补全 | 检测到“X呢”模式 | 复用上一轮的谓语/宾语 | “英语呢”→“新高考二卷的英语作文要求是什么” |
短句补全 | 长度≤3个字 | 拼接上一轮的主题 | “价格呢”→“A产品的价格是多少” |
关键词映射 | 匹配预定义关键词 | 替换为标准查询模板 | “天气”→“今天的天气情况如何” |
优点 | 缺点 |
实现简单,零延迟,零成本 | 覆盖率低,只能处理预定义的模板 |
可解释性强,容易调试 | 无法处理复杂、模糊的省略 |
适用场景:对话模式固定、高频套路明确的场景。
策略二:基于模板的改写(Template-Based Rewriting)
核心思想:维护一套查询模板库,根据意图分类匹配对应模板,把槽位填满。
示例:
用户输入 | 意图分类 | 模板 | 补全后的Query |
“英语呢” | 主题切换 |
| “新高考二卷的英语作文要求” |
“那第二个呢” | 序列引用 |
| “第二个方案的信息” |
“便宜的那个” | 属性筛选 |
| “A产品中价格最低的” |
优点 | 缺点 |
比纯规则更灵活,适合结构化场景 | 模板维护成本高,意图类别爆炸 |
可批量生成改写样本 | 无法处理模板外的自由表达 |
策略三:基于LLM的生成式改写(LLM-Based Rewriting)
核心思想:直接把对话历史+当前用户输入丢给LLM,让模型生成改写后的完整查询。
这是目前最主流、效果最好的方式。
Prompt设计(关键):
你是一个查询改写助手。请根据对话历史,将用户当前的问题改写为完整、独立、可检索的查询语句。 规则: 1. 把“它”、“这个”、“那个”替换为具体实体 2. 补全省略的主语、谓语、宾语 3. 如果用户切换了话题,以新话题为主 4. 只输出改写后的查询,不要输出其他内容 对话历史: 用户:新高考二卷的语文作文要求是什么? 助手:材料主旨是关于守住根本的古语,写作要求结合材料自拟标题... 用户:英语呢? 改写后的查询: 新高考二卷的英语作文要求是什么多意图拆解增强版Prompt:
如果用户的问题包含多个意图,请拆解为多个独立查询,用[SEP]分隔。 示例: 用户:A和B哪个便宜?哪个口碑好? 输出:A的价格是多少[SEP]B的价格是多少[SEP]A的口碑如何[SEP]B的口碑如何优点 | 缺点 |
理解准确率高,能处理复杂语境 | 每次改写多一次LLM调用,有延迟和成本 |
天然支持多意图拆解和指代消解 | Prompt设计敏感,需要反复调优 |
零规则维护,适应性强 | 输出不稳定,可能产生幻觉 |
策略四:基于检索增强的改写(Retrieval-Augmented Rewriting)
核心思想:改写时不只是依赖对话历史,还会检索历史中的相关片段(而非完整对话),用这些片段辅助生成更精准的改写。
这和普通的RAG的区别:普通RAG是用原始用户问题去检索;这里是用检索结果反过来辅助查询改写。
用户当前问题:“那个方案的风险呢?” Step 1: 用原始问题模糊检索历史 → 命中“方案A”、“方案B”、“风险评估”等片段 Step 2: 把检索到的片段作为上下文,注入改写Prompt Step 3: LLM生成改写 → “方案A和方案B分别存在哪些风险?”适用场景:超长对话(几十轮),历史摘要不足以补全信息时。
五种策略对比总览
策略 | 实现复杂度 | 准确率 | 延迟 | 成本 | 适用场景 |
规则匹配 | ⭐ 极低 | ★★☆ 中低 | 极低 | 零 | 高频固定模式 |
模板填充 | ⭐⭐ 低 | ★★★ 中 | 极低 | 零 | 结构化意图场景 |
LLM生成 | ⭐⭐⭐⭐ 高 | ★★★★★ 高 | 中 | 每次改写1次 | 大多数生产场景(推荐) |
检索增强改写 | ⭐⭐⭐⭐⭐ 最高 | ★★★★★ 高 | 高 | 检索+LLM | 超长对话、复杂上下文 |
混合策略 | ⭐⭐⭐ 中 | ★★★★ 高 | 低 | 部分触发 | 先规则后LLM的兜底方案 |
学习途径:马丁老师
