Query-Rewrite查询改写
2388 字约 8 分钟
domain/aiai/rag
2026-07-24
Query Rewrite 是 RAG 检索前处理的核心环节,负责在查询进入检索器之前,将用户原始输入转化为更利于检索的形态。它的本质不是"优化",而是对齐预期——对齐用户真正想解决的问题、对齐系统可提供的信息能力、对齐最终答案的形态。
一句话解释
在查询进入检索器之前,通过 LLM 对用户原始 Query 进行规范化、扩展、分解或抽象,使其更好地匹配知识库中的文档表述,从而提升检索命中率和答案质量。
核心问题
为什么要改写 Query?
- 表述风格不匹配:用户问"RAG 咋优化 query",知识库里是"如何优化 RAG 中的查询语句"——向量检索能跨过这个鸿沟,但不是每次都跨得过去
- 复合问题"平均化":用户问"对比 MySQL 和 PostgreSQL 的锁机制和并发性能",embedding 被多个子话题稀释,哪个都检索不好
- 多轮对话指代:用户说"它怎么配置",需要补全指代对象
- 过于具体或过于抽象:需要调整抽象层级来命中知识库
核心改写策略
1. HyDE(假设性文档嵌入)
核心思想:不直接用用户 Query 做向量检索,而是先让 LLM 根据 Query 生成一篇"假设性答案文档",再用假设文档的 embedding 向量去检索真实文档库。
为什么有效:在向量空间中,"答案"和"答案"之间的相似度,远高于"问题"和"答案"之间的相似度。HyDE 将 Query 的表述风格"翻译"成与知识库文档一致的风格,在 embedding 空间中更好地对齐。
工作流程:
- LLM 基于 Query 生成假设性文档
- 将假设文档输入编码器,映射为稠密向量
- 用该向量从文档库中检索最相似的真实文档
局限:当 LLM 对该领域不熟悉时,生成的假设文档质量差,反而增加噪声;对开放式问题可能引入 LLM 固有偏见。
2. 查询分解(Decomposition)
核心思想:将包含多个子问题的复合 Query 拆分成多个独立的单意图子 Query,分别检索后整合结果。
典型场景:用户问"对比 MySQL 和 PostgreSQL 在高并发写入下的性能差异,以及各自的锁机制有什么不同"。
分解后:
- MySQL 在高并发写入场景下的性能表现
- PostgreSQL 在高并发写入场景下的性能表现
- MySQL 的锁机制详解
- PostgreSQL 的锁机制详解
实现方式:基于规则(正则匹配"和""与""对比"等关键词)或基于 LLM(Plan-and-Solve Prompting,先输出分解计划再拆分)。
3. 查询扩展(Expansion)
核心思想:在原始 Query 基础上补充相关语义信息,解决 Query 过于简洁导致的检索遗漏。
| 方法 | 原理 | 适用场景 |
|---|---|---|
| HyDE | 生成假设性文档,用文档 embedding 检索 | Query 简洁、语义模糊 |
| Query2Term | LLM 生成离散关键词补充到 Query | 延迟敏感、需要快速扩展 |
| Query2Doc | LLM 生成完整段落与原始 Query 拼接 | 稠密检索、需要全面语义覆盖 |
| Query2Term-PRF | 先 BM25 检索 Top-K 伪相关文档,结合文档生成关键词 | 领域性 RAG、避免无关信息 |
关键数据:Query2Doc 在 BM25 上召回率提升约 15%,在向量检索上提升 3% 以内[1]。稀疏检索从 Query Rewrite 中受益更大,因为 LLM 扩展能有效弥补词汇不匹配问题。
4. Step-Back Prompting(退一步查询)
核心思想:当用户 Query 过于具体导致检索结果过少时,先提炼核心意图,去除具体细节,形成更通用的高层次表达,用抽象 Query 检索背景知识,再结合原始细节回答问题。
典型示例:
- 原始:如何用 PyTorch 实现 RAG 的 query 扩展
- 抽象后:RAG 中 query 扩展的常用实现方法及工具
- 最终检索:原始 Query 结合抽象 Query 的结果
两步流程:抽象(Abstraction)→ 推理(Reasoning)。适用场景:技术咨询、教程类,用户带上具体框架/版本/环境时,直接检索容易命中不到通用原理。
5. Multi-Query 生成(多角度改写)
核心思想:生成 3-5 个语义相同但措辞不同的 Query 变体,分别检索,合并去重,提升召回率。
典型示例:
- 原始:"LangChain 如何实现 RAG 功能"
- 变体 1:"LangChain 框架实现 RAG 系统的步骤"
- 变体 2:"基于 LangChain 开发 RAG 功能的方法"
- 变体 3:"LangChain 中 RAG 功能的实现原理与实践"
工程要点:变体必须保证语义一致,不能偏离原始需求;措辞多样化,覆盖不同表述风格。但成本较高,每生成一个变体就需一次检索。通常不作为默认策略,在检索结果不足时触发。
改写策略选择决策框架
需要改写的场景
| 场景 | 触发条件 | 推荐策略 |
|---|---|---|
| 多轮对话 | 包含指代词("它""这""那个") | 指代消解 + 上下文补全 |
| 口语化 Query | 过于随意、不规范 | Query 规范化改写 |
| 复合问题 | 包含多个子问题 | Query 分解 |
| 过于简洁 | 1-2 个词,语义模糊 | Query 扩展 / HyDE |
| 过于具体 | 细节过多,检索范围窄 | Step-Back Prompting |
| 检索结果不足 | 召回数量少、相关性低 | Multi-Query 生成 / HyDE |
不需要改写的场景
| 场景 | 原因 |
|---|---|
| 用户给出明确关键词或 ID | 改写会丢失精确匹配 |
| 强结构化查询(SQL、报表类) | 改写可能破坏查询语义 |
| 明确指定"只查某一文档" | 无需扩展语义范围 |
| 单术语精确查询(如仅输入"Redis") | 改写成句子反而丢失精确匹配 |
| Query 本身已经规范化、完整 | 不必增加延迟和成本 |
精确词保护机制(关键工程实践)
核心问题:Query Rewrite 会把用户的关键术语改掉。例如用户问"Redis",rewrite 可能输出"缓存数据库的使用方法",导致召回的文档全是泛泛的缓存概念,没有一篇提到 Redis。
保护策略:
- 精确词提取:正则匹配 2-32 字符的技术术语/标识符(如 "JVM"、"GC"、"Redis"、"@Transactional")
- 跳过逻辑:当问题本身是单个精确词时,跳过 rewrite
- 多候选队列:
[改写Query, 原始Query],按顺序尝试,第一个产生有效命中的胜出 - 精确词命中校验:所有精确词必须至少在一篇召回文档中出现,否则回退到下一候选
- 动态检索参数:短问题(≤4 字符)使用 topK=20 宽召回,长问题(>12 字符)使用 topK=8 精准召回
设计哲学:改写是增强,不是替换。原始 Query 始终在候选队列里兜底。宁可漏召回,不可错召回。
成本效益分析
| 成本项 | 估算 | 说明 |
|---|---|---|
| LLM 调用延迟 | 200-1000ms/次 | 取决于模型规模 |
| Token 消耗 | 输入 ~100,输出 ~200-500 | 单次改写 |
| Multi-Query 额外成本 | 改写次数 × 检索次数 | N 个变体 = N 次额外检索 |
优化建议:
- 改写任务用轻量模型(GPT-3.5-turbo 或 qwen2.5:7b),降低延迟和成本
- 按需改写:通过规则判断是否触发,不是所有 Query 都需要
- 本地修复优先于 LLM 修复:精确词校验、Query 长度判断等能用本地逻辑解决的,不引入额外 LLM 调用
- 候选队列 + 降级兜底:改写 Query 优先,原始 Query 兜底,最差不会比不改写更差
2024-2026 最佳实践
- 默认开启基础改写:指代消解 + 口语规范化(成本最低,收益最大)
- 按需触发高级策略:HyDE、Multi-Query 在检索结果不足时触发
- 精确词保护:提取技术术语,防止改写丢失关键匹配
- 候选队列兜底:改写 Query 优先,原始 Query 兜底
- 动态检索参数:短 Query 宽召回,长 Query 精准召回
- Query 分类路由:不同 Query 类型走不同策略管道
- 保持轻量:改写任务用轻量模型,降低延迟和成本
常见误区
- 所有 Query 都改写:增加延迟和成本,很多 Query 不需要改写
- 改写替换原始 Query:精确词被改写后丢失,导致关键匹配失败
- 只改不验:改了 Query 但不验证检索结果是否有改善
- 复杂改写策略用大模型:改写任务用轻量模型即可,大模型成本高且延迟大
- 忽视多轮对话的指代消解:这是最基础也是收益最大的改写
进阶方向
- Hybrid Search:改写后的 Query 同时执行 BM25 和向量检索
- Keyword Search:改写后提升 BM25 的词汇匹配
- Rerank重排序:改写 + 检索 + Rerank 组成完整检索流水线
- Query Routing:根据 Query 类型路由到不同检索策略
关联笔记
来源:Query2Doc 论文(Wang et al., 2023),基于 TREC DL 数据集评测 ↩︎