RAG学习篇:Query Transformation 查询改写,让烂问题也能找到好答案
这是之前学习 RAG 时整理的学习篇。用户问的问题往往很烂——要么太泛(「Python 教程」),要么太绕(「那个什么什么…」),Query Transformation 就是解决这个问题的:把烂问题改写成好问题,检索效果直接翻倍。
一、为什么需要 Query Transformation
1.1 用户的提问有多不靠谱
做 RAG 的时候你会发现,用户的问题质量参差不齐:
- 太宽泛:「讲讲 Python」——讲什么?基础语法?进阶特性?Web 开发?数据分析?
- 太模糊:「那个东西怎么用」——哪个东西?怎么用?
- 有歧义:「Java 的继承」——是说
extends继承父类?还是implements实现接口?还是组合优于继承(composition over inheritance)的重构方向? - 有拼写错误:「Pythno 教程」——搜索引擎能纠错的还好,向量检索直接傻眼
- 太专业/太口语:「怎么让模型过拟合」——过拟合通常是负面词,但用户想问的是「怎么在特定领域微调」
核心痛点:用户的问题 ≠ 文档里的表达方式。
用户问「Python 教程」,文档里可能写的是「Python 3.9 入门指南」。字面完全不同,但语义相关。这就是 Query Transformation 要解决的问题。
1.2 Query Transformation 的本质
用一句话概括:
改写可以是:
- 扩展(加同义词、相关概念)
- 分解(把复杂问题拆成多个子问题)
- 重构(换个问法,更接近文档的表达方式)
- 生成(直接生成一个「理想答案」的草稿去搜——这就是 HyDE)
二、常见的 Query Transformation 方法
2.1 方法对比总览
| 方法 | 原理 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|---|
| HyDE | 生成假设文档再编码 | 零样本、效果好 | 需要 LLM、有延迟 | 问答式、语义模糊 |
| Query Expansion | 扩展同义词/相关词 | 简单、成本低 | 扩展质量不稳定 | 通用场景 |
| Step-back Prompting | 退一步问更抽象的问题 | 能召回更相关的背景知识 | 可能召回过多无关内容 | 需要背景知识的复杂问题 |
| 子查询分解 | 把复杂问题拆成多个子问题 | 适合复杂多跳问题 | 流程复杂、成本高 | 复杂推理问题 |
| RAG-Fusion | 生成多个查询取并集 | 召回更全面 | 计算成本高 | 对召回率要求高的场景 |
2.2 HyDE(Hypothetical Document Embeddings)
HyDE 是 Query Transformation 里最有意思的方法,本系列下一篇会专门深入讲,这里先把核心思路说清楚。
核心思想:与其纠结怎么优化问题,不如直接生成一个「理想答案」,然后用这个答案去搜。
用户问题 → LLM 生成假设文档 → 编码 → 检索真实文档
什么时候用:
- 用户问题很短(1-3 个词),语义太泛
- 用户不知道怎么表达,需要「补全」语义
- 你试过其他方法,效果都不够好
2.3 Query Expansion(查询扩展)
最传统、最轻量的方法。
原理:给原始查询加同义词、相关词,扩大检索范围。
举个例子:
原始查询:Python 教程 扩展后:Python 教程 入门 基础 学习 guide tutorial
实现方式:
- 基于词典:用 WordNet、同义词词典人工维护
- 基于模型:用 LLM 生成相关词(轻量版)
Prompt 示例:
请为以下查询生成 3-5 个相关的搜索词,用逗号分隔:
查询:{query}
相关搜索词:
优点:
- 实现简单,几乎零成本
- 不需要额外模型(如果用词典)或小模型(如果用 LLM 扩展)
- 稳定性高
缺点:
- 扩展质量依赖词典或模型能力
- 可能引入不相关的词(比如「Python」扩展到「蟒蛇」)
2.4 Step-back Prompting(退一步提问)
这个方法来自 Google DeepMind 的论文《Take a Step Back: Evoking Reasoning via Abstraction in Large Language Models》(Zheng et al., 2023),思路很有意思。
核心思想:当问题太具体时,退一步问更抽象、更通用的问题,先召回背景知识,再用背景知识辅助回答具体问题。
举个例子:
| 原始问题 | Step-back 问题 | 召回内容 |
|---|---|---|
| 「Transformer 的注意力机制在医疗文本上怎么优化」 | 「Transformer 注意力机制是什么」 | 基础原理 |
| 「React useEffect 依赖数组问题」 | 「React useEffect 是什么」 | useEffect 文档 |
Prompt 示例:
你是一个问题改写助手。用户的问题是具体的,你需要生成一个更通用、更抽象的问题,用于检索背景知识。
用户问题:{query}
请生成一个更通用的背景问题:
什么时候用:
- 问题很具体,需要背景知识才能理解
- 用户问的是某个领域的专业问题,但知识库里有更基础的解释
注意事项:
- Step-back 可能会召回太多无关内容
- 建议配合 Reranker 使用,先召回更多,再精确排序
2.5 子查询分解(Sub-query Decomposition)
适合那种「一步问完但其实包含多个问题」的复杂查询。
举个例子:
原始问题:「怎么在 Kubernetes 上部署一个带数据库的 Web 应用,还要配置 SSL」 子查询 1:Kubernetes 部署 Web 应用 子查询 2:Kubernetes 部署数据库 子查询 3:Kubernetes 配置 SSL 证书
实现方式:
- 用 LLM 分析问题包含哪些子问题
- 对每个子问题分别检索
- 合并所有检索结果作为上下文
Prompt 示例:
请将以下复杂问题分解成 2-4 个更简单的子问题。每个子问题应该独立可回答。
原始问题:{query}
子问题:
1.
2.
3.
优点:
- 每个子问题更精准,检索质量更高
- 适合复杂的多跳推理问题
缺点:
- 需要多次检索,成本翻倍
- 合并策略需要设计(简单拼接还是智能合并)
2.6 RAG-Fusion
RAG-Fusion 由 Zackary Rackauckas 在 2024 年提出(arXiv:2402.03367),核心融合算法 RRF(Reciprocal Rank Fusion)则来自 Cormack 等人 2009 年的 SIGIR 论文。思路是:一个查询不够,我生成多个查询。
流程:
原始查询 → LLM 生成 5 个相关查询 → 分别检索 → 用 RRF 融合结果 → Top-K 返回
Prompt 示例:
请根据以下查询,生成 5 个不同角度但相关的查询。每个查询应该能检索到相关的补充信息。
原始查询:{query}
相关查询:
1.
2.
3.
4.
5.
RRF 融合公式:
RRF 分数 = Σ 1 / (k + rank_i)
简单说就是:一个文档在多个查询的结果里排名越高,最终分数越高。
优点:
- 召回更全面,不容易漏掉相关内容
- 多个角度覆盖,减少 query 表述差异的影响
缺点:
- 需要多次检索,成本高
- 需要调 RRF 的参数 k
三、方法选择决策树
这么多方法,怎么选?
决策流程:
用户问题质量如何? ├── 问题清晰、具体 → 直接用原始查询 │ ├── 问题太泛/太短 → Query Expansion 或 HyDE │ ├── 延迟敏感 → Query Expansion │ └── 追求效果 → HyDE │ ├── 问题太复杂/多条件 → 子查询分解 │ ├── 需要背景知识 → Step-back Prompting │ └── 召回率不够 → RAG-Fusion(多查询生成)
我的建议:渐进式优化
不要一上来就搞最复杂的方法。建议这个顺序:
| 阶段 | 方法 | 预期收益 | 成本 |
|---|---|---|---|
| 第一阶段 | 原始查询 + 好的 Embedding 模型 | 60% | 低 |
| 第二阶段 | + Query Expansion | 70% | 很低 |
| 第三阶段 | + Reranker(Cross-Encoder) | 80% | 中 |
| 第四阶段 | + HyDE / Step-back / RAG-Fusion | 85-90% | 高 |
四、生产落地的实战经验
4.1 延迟优化
Query Transformation 最大的问题是增加了延迟:
- Query Expansion:几毫秒(如果用词典)到几百毫秒(如果用 LLM)
- HyDE:几百毫秒到几秒(取决于 LLM)
- RAG-Fusion:查询数 × 单次检索时间
优化策略:
分层缓存
- 高频查询的改写结果缓存
- 低频查询走原始查询
并行执行
- RAG-Fusion 的多个查询并行检索
- 子查询分解的多个查询并行处理
降级策略
- LLM 超时或失败时,回退到原始查询
- 设置改写质量的阈值,质量不够就回退
小模型替代
- HyDE 的假设文档生成不需要大模型,用 GPT-3.5 级别的小模型替代 GPT-4,延迟可以降低 3-5 倍,效果损失在可接受范围内
4.2 改写质量评估
怎么知道改写有没有变好?
离线评估:
- 准备一批测试查询和期望的相关文档
- 对比改写前后的 Recall@K、Precision@K、MRR
在线评估:
- A/B 测试:一部分用户走改写,一部分走原始
- 观察下游指标:回答的点击率、满意度、任务完成率
4.3 常见坑
| 坑 | 表现 | 解决 |
|---|---|---|
| 改写过度 | 原始查询很清晰,改写后反而引入无关词 | 设置改写置信度阈值 |
| 改写跑偏 | 改写后的查询偏离了用户真实意图 | 用原始查询兜底 |
| 多语言问题 | 用户用中文问,改写成英文后检索不到中文文档 | 按语言分别处理或混合检索 |
| 领域适配 | 通用改写策略在专业领域效果差 | 用领域特定词典或微调 |
五、Prompt 工程技巧
5.1 通用原则
- 明确输出格式:告诉模型你要什么格式的输出
- 加示例(few-shot):给 1-2 个例子,模型学得更快
- 约束条件:限制输出的长度、风格、范围
5.2 示例:带 few-shot 的 Query Expansion Prompt
你是一个查询扩展助手。请为用户的查询生成 3-5 个相关的搜索词,帮助检索更多相关内容。
示例 1:
查询:Python 教程
扩展:Python 入门, Python 基础, Python 学习指南, Python 编程, Python 3.9
示例 2:
查询:Kubernetes 部署
扩展:K8s 部署, Kubernetes 安装, Kubernetes 集群搭建, Docker 容器编排
用户查询:{query}
请生成扩展词(用逗号分隔):
5.3 示例:子查询分解 Prompt
请将以下复杂问题分解成 2-4 个更简单的子问题。每个子问题应该独立可回答。
示例:
原始问题:「怎么在 AWS 上部署一个高可用的 Web 应用,还要配置负载均衡和自动扩缩容」
子问题:
1. AWS 上如何部署 Web 应用
2. AWS 负载均衡器(ELB)如何配置
3. AWS 自动扩缩容(Auto Scaling)如何设置
4. AWS 高可用架构设计
原始问题:{query}
子问题:
1.
2.
3.
4.
六、总结
Query Transformation 是 RAG 检索优化的核心手段,但不是银弹。
最后送大家一句话:用户的烂问题不是你的错,但如果检索不到答案,那就是你的问题了。Query Transformation 就是来解决这个问题的。
参考资源
论文原文:
- HyDE:Gao, L., Ma, X., Lin, J., & Callan, J. (2022). Precise Zero-Shot Dense Retrieval without Relevance Labels. arXiv:2212.10496.
- Step-back Prompting:Zheng, H., Mishra, S., Chen, X., et al. (2023). Take a Step Back: Evoking Reasoning via Abstraction in Large Language Models. arXiv:2310.06117. Google DeepMind.
- RAG-Fusion:Rackauckas, Z. (2024). RAG-Fusion: a New Take on Retrieval-Augmented Generation. arXiv:2402.03367.
- RRF(基础算法):Cormack, G. V., Clarke, C. L. A., & Büttcher, S. (2009). Reciprocal Rank Fusion outperforms Condorcet and Individual Rank Learning Methods. SIGIR 2009.
官方实现参考:
- LangChain:Query Transformations 官方博客(含 MultiQueryRetriever 使用说明)
- Haystack:Advanced RAG: Query Expansion(实现细节清晰,工程向)
- RAG-Fusion GitHub 实现:Raudaschl/rag-fusion