什么是 RAG 查询改写(Query Rewriting)?它怎么提升检索效果的?
一则或许对你有用的小广告
欢迎加入小哈的星球,你将获得:专属的实战项目(4个项目都能学) / 1v1 提问 / 简历修改 / Java 学习路线 / 社群讨论 / 学习打卡 / 每月赠书
《Spring AI 项目实战(问答机器人、RAG 智能客服、联网搜索)》已完结,基于
Spring AI + Spring Boot 3.x + JDK 21...,查看介绍《从零手撸:仿小红书(微服务架构)》 已完结,基于
Spring Cloud Alibaba + Spring Boot 3.x + JDK 17...,查看介绍;演示链接:http://116.62.199.48:7070/《从零手撸:前后端分离博客项目(全栈开发)》 2 期已完结,演示链接:http://116.62.199.48/
新开坑项目:《从零手撸:秒杀系统高并发优化实战》 正在更新中...,查看介绍
截止目前,星球内专栏累计输出 150w+ 字,讲解图 5110+ 张,还在持续爆肝中.. 后续还会上新更多项目,已有 4700+ 小伙伴加入学习,欢迎点击围观
面试考察点
-
概念边界:Query Rewriting 到底改的是啥?跟 Prompt 改写、HyDE 这些名词什么关系,能不能拎清楚。
-
范式储备:除了 "同义改写",你还知道哪些改写套路?Step-Back、Decomposition、HyDE、Multi-Query,各自的适用场景能说清吗?
-
原理理解:为什么改一改 Query 就能提升检索?这背后是向量检索的什么特性决定的?
核心答案
Query Rewriting 指的是:在用户原始 Query 进入向量检索之前,先用 LLM(或规则)对它做一次变形,让它更容易召回相关文档。
它提升检索效果的根本原因只有一个:向量检索是拿 Query 的 Embedding 去和 Document 的 Embedding 算相似度,而用户的口语化 Query 和文档的书面化表达之间存在 "语义鸿沟",召回率会很难看。改写就是把这个鸿沟补上。
主流的改写范式有五种,按 "改的方向" 分两类:
| 改写方向 | 范式 | 一句话描述 |
|---|---|---|
| 精炼(1 个 Query → 1 个更好的 Query) | 同义改写 / 上下文压缩 / Step-Back | 把 Query 变得更准、更全、更抽象 |
| 扩展(1 个 Query → 多个 Query) | Multi-Query / Decomposition / HyDE | 用多个变体并行检索,提高召回覆盖 |
下面逐个拆。
深度解析
一、为什么要改写:Query-Doc 语义鸿沟
上图是改写为什么有效的核心原理。向量检索说到底是算两段文本的 Embedding 距离,距离越近就越算 "相关"。麻烦在于:
- 用户问得很随意,比如 "那个东西咋用"
- 知识库里写得很正式,比如 "XX 模块的使用方法与配置说明"
这两句话意思一样,但字面差太远,Embedding 算出来的相似度可能很低,根本召不回。
改写的作用就是把用户那句话,换成更接近知识库表述的方式,或者拆成多个角度去检索,把召回率拉上来。
二、五种主流改写范式
1. 同义改写(Synonym Rewriting)
最基础的改写。让 LLM 把 "怎么部署" 改成 "应用的部署流程、安装步骤、上线方法"。
适合场景:知识库表述风格统一、用户提问口语化。
2. 上下文压缩(Contextual Compression)
多轮对话里特别重要。用户说 "那它性能怎么样","它" 指的是上一轮聊的东西。这种 Query 直接拿去检索就是灾难,因为离了上下文完全没意义。
改写要做的是:结合对话历史,把 "那它性能怎么样" 改写成 "Spring AI 的 TokenTextSplitter 性能怎么样"。
LangChain4j 的 CompressingQueryTransformer 就是干这个的。
3. Step-Back Prompting(退一步提示)
Google 2023 年提的思路。遇到太具体的问题,反而往回退一步,问一个更抽象、更通用的问题,先把背景知识检索到。
举个例子:
- 原 Query:"2010 年世界杯决赛哪个球员进了制胜球?"
- Step-Back 后的 Query:"2010 年世界杯决赛的赛况和进球情况"
原理是:太具体的 Query 可能根本召不到对应文档(知识库里未必有这么细的描述),但退一步后的通用 Query 更可能命中。
4. Multi-Query(多查询扩展)
让 LLM 把一个 Query 改写成 N 个不同角度的变体,N 个变体并行检索,结果用 RRF(Reciprocal Rank Fusion)算法融合去重。
这是生产环境最常用的扩展式改写。LangChain4j 内置的 ExpandingQueryTransformer 就是这个套路,社区里有个测试结论挺有意思:9 种改写技术里,Multi-Query 和 HyDE 效果最稳。
适合场景:知识库覆盖面广、单一 Query 容易漏召回。
5. HyDE(Hypothetical Document Embeddings)
HyDE 的思路比较特别。不是改 Query,而是让 LLM 先根据 Query 编一个 "假想答案文档",再拿这个假想答案的 Embedding 去检索。
原理是:答案和文档的语义分布更接近。用户问 "怎么部署" 的 Embedding,和文档 "部署流程……" 的 Embedding,距离比较远;但 LLM 编出来的假想答案 "部署的步骤一般是……" 的 Embedding,和真实文档的 Embedding 距离就近多了。
HyDE 用在技术文档、风格化写作这类 "文档语言和问题语言差异大" 的场景效果不错。代价也有:多一次 LLM 调用,首 Token 延迟会涨;如果 LLM 编的答案方向偏了,反而会把检索带歪。
三、Java 落地:LangChain4j 的 QueryTransformer
LangChain4j 把改写抽象成了 QueryTransformer 接口,一个 Query 进去,一个或多个 Query 出来。内置两个实现:
import dev.langchain4j.rag.query.transformer.QueryTransformer;
import dev.langchain4j.rag.query.transformer.ExpandingQueryTransformer;
import dev.langchain4j.rag.query.transformer.CompressingQueryTransformer;
import dev.langchain4j.model.chat.ChatLanguageModel;
// 1. 扩展式改写:一个 Query 变多个,并行检索
QueryTransformer expandingTransformer = ExpandingQueryTransformer.builder()
.chatLanguageModel(chatModel)
.n(3) // 生成 3 个变体
.build();
// 2. 压缩式改写:结合对话历史,把代词指代补全
QueryTransformer compressingTransformer = CompressingQueryTransformer.builder()
.chatLanguageModel(chatModel)
.build();
// 3. 组装到 RetrievalAugmentor 上
RetrievalAugmentor augmentor = DefaultRetrievalAugmentor.builder()
.queryTransformer(expandingTransformer) // 挂上改写器
.contentRetriever(contentRetriever)
.contentAggregator(DefaultContentAggregator.builder()
.reciprocalRankFuser() // 多 Query 结果用 RRF 融合
.build())
.build();
Step-Back、Decomposition、HyDE 这三种 LangChain4j 没内置,需要自己实现 QueryTransformer 接口。核心就是写一个 apply(Query) 方法,内部调 LLM 做改写。
HyDE 自定义实现的骨架大概长这样:
public class HyDEQueryTransformer implements QueryTransformer {
private final ChatLanguageModel chatModel;
private final EmbeddingModel embeddingModel;
@Override
public Collection<Query> transform(Query query) {
// 1. 让 LLM 根据原始 Query 编一个假想答案
String prompt = "请针对以下问题,写一段可能的答案文档(200字内):\n" + query.text();
String hypotheticalDoc = chatModel.generate(prompt);
// 2. 把假想答案当作新 Query 去检索
return List.of(Query.from(hypotheticalDoc, query.metadata()));
}
}
四、几个常被问到的坑
改写不是万能药。知识库里压根没有相关文档的话,再怎么改写也召不回。改写只能解决 "有但召不到",解决不了 "根本没有"。
多查询扩展记得去重。N 个变体检索回来的结果很容易大量重叠,得用 RRF 之类的算法融合去重,不然 LLM 的调用就白费了。
延迟成本得算清。HyDE、Multi-Query、Decomposition 都会额外触发 LLM 调用,首 Token 延迟可能涨 1-2 秒。对延迟敏感的场景(客服、实时问答),要么慎用,要么把改写结果缓存起来。
改写用的 LLM 没必要太强。改写任务本身不算难,用小模型(比如 Qwen-Turbo、GPT-4o-mini)就够了,没必要上旗舰模型,能省不少成本。
五、行业前沿
2025 年 EMNLP Findings 有一篇 Q-PRM 论文,讲的是 "自适应查询改写"。它根据 Query 的复杂度,动态决定要不要改写、用哪种方式改写。简单 Query 直接检索,复杂 Query 才上 Multi-Query 或 HyDE。这思路对生产环境很有启发:别一刀切地给所有 Query 都套改写管线,按需启用才经济。
面试高频追问
-
Multi-Query 和 HyDE 哪个好?
看场景。知识库风格化写作、技术文档,HyDE 更猛;通用问答、事实检索,Multi-Query 更稳。有条件就两个都试,拿自己的测试集跑数据说话。
-
改写会不会越改越偏?
会。LLM 改写时如果理解偏了,会把检索带歪。规避办法:用小模型快速生成 + 多个变体取并集(一个偏了其他还能兜底);或者上 Q-PRM 那种自适应方案,简单 Query 不改写。
-
改写和 Rerank 是不是冲突?
不冲突,是互补的。改写管的是 "召回更多潜在相关的内容",Rerank 管的是 "在召回的内容里把最相关的排前面"。生产环境经常两个一起上。
常见面试变体
- "RAG 里 Query 改写有几种方式?"
- "HyDE 的原理是什么?为什么有效?"
- "多轮对话的 Query 怎么处理?"
- "Query 改写和 Rerank 先做哪个?"
记忆口诀
改写两种方向:精炼(同义、压缩、Step-Back)、扩展(Multi-Query、Decomposition、HyDE)。
核心原理一句话:补平 Query 和 Doc 之间的语义鸿沟。
生产三注意:去重、延迟、按需启用。
总结
Query Rewriting 的本质是给向量检索做一道 "翻译",补平用户口语和文档书面语之间的语义鸿沟。五种范式按 "精炼 vs 扩展" 分类记,Java 落地用 LangChain4j 的 QueryTransformer 接口,生产环境重点考虑去重、延迟和按需启用。面试时把原理、范式、Java 实现、踩坑经验讲清楚,这道题基本就拿下了。
