RAG学习篇:Query Transformation 查询改写,让烂问题也能找到好答案

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

实现方式

  1. 基于词典:用 WordNet、同义词词典人工维护
  2. 基于模型:用 LLM 生成相关词(轻量版)

Prompt 示例

请为以下查询生成 3-5 个相关的搜索词,用逗号分隔:

查询:{query}

相关搜索词:

优点

  • 实现简单,几乎零成本
  • 不需要额外模型(如果用词典)或小模型(如果用 LLM 扩展)
  • 稳定性高

缺点

  • 扩展质量依赖词典或模型能力
  • 可能引入不相关的词(比如「Python」扩展到「蟒蛇」)
我的经验:Query Expansion 是性价比最高的第一步优化。先把这个做好,再考虑更复杂的方法。

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 证书

实现方式

  1. 用 LLM 分析问题包含哪些子问题
  2. 对每个子问题分别检索
  3. 合并所有检索结果作为上下文

Prompt 示例

请将以下复杂问题分解成 2-4 个更简单的子问题。每个子问题应该独立可回答。

原始问题:{query}

子问题:
1.
2.
3.

优点

  • 每个子问题更精准,检索质量更高
  • 适合复杂的多跳推理问题

缺点

  • 需要多次检索,成本翻倍
  • 合并策略需要设计(简单拼接还是智能合并)
什么时候用:当用户问题明显包含多个子任务时(「怎么实现 A 同时还要 B 和 C」),分解后效果更好。

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%
关键洞察:80% 的收益来自前三个阶段。HyDE 这类高级方法是为了最后那 10% 的提升,但成本和复杂度会显著增加。先跑基线,再决定是否需要。

四、生产落地的实战经验

4.1 延迟优化

Query Transformation 最大的问题是增加了延迟

  • Query Expansion:几毫秒(如果用词典)到几百毫秒(如果用 LLM)
  • HyDE:几百毫秒到几秒(取决于 LLM)
  • RAG-Fusion:查询数 × 单次检索时间

优化策略

  1. 分层缓存

    • 高频查询的改写结果缓存
    • 低频查询走原始查询
  2. 并行执行

    • RAG-Fusion 的多个查询并行检索
    • 子查询分解的多个查询并行处理
  3. 降级策略

    • LLM 超时或失败时,回退到原始查询
    • 设置改写质量的阈值,质量不够就回退
  4. 小模型替代

    • HyDE 的假设文档生成不需要大模型,用 GPT-3.5 级别的小模型替代 GPT-4,延迟可以降低 3-5 倍,效果损失在可接受范围内

4.2 改写质量评估

怎么知道改写有没有变好?

离线评估

  • 准备一批测试查询和期望的相关文档
  • 对比改写前后的 Recall@K、Precision@K、MRR

在线评估

  • A/B 测试:一部分用户走改写,一部分走原始
  • 观察下游指标:回答的点击率、满意度、任务完成率
避坑提醒:Query Transformation 可能导致「改写后召回的文档更相关,但 LLM 生成的答案反而更差」的情况。一定要评估端到端的效果,不要只看检索指标。

4.3 常见坑

表现 解决
改写过度 原始查询很清晰,改写后反而引入无关词 设置改写置信度阈值
改写跑偏 改写后的查询偏离了用户真实意图 用原始查询兜底
多语言问题 用户用中文问,改写成英文后检索不到中文文档 按语言分别处理或混合检索
领域适配 通用改写策略在专业领域效果差 用领域特定词典或微调

五、Prompt 工程技巧

5.1 通用原则

  1. 明确输出格式:告诉模型你要什么格式的输出
  2. 加示例(few-shot):给 1-2 个例子,模型学得更快
  3. 约束条件:限制输出的长度、风格、范围

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 检索优化的核心手段,但不是银弹

核心观点: 1. 先跑基线,知道当前问题在哪 2. 从轻量方法(Query Expansion)开始,逐步迭代 3. 高级方法(HyDE、RAG-Fusion)是为了最后 10% 的提升,成本和复杂度会显著增加 4. 一定要评估端到端效果,不要只看检索指标 5. 改写可能跑偏,一定要有降级策略

最后送大家一句话:用户的烂问题不是你的错,但如果检索不到答案,那就是你的问题了。Query Transformation 就是来解决这个问题的。


参考资源

论文原文

官方实现参考