什么是 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+ 小伙伴加入学习,欢迎点击围观

面试考察点

  1. 概念边界:Query Rewriting 到底改的是啥?跟 Prompt 改写、HyDE 这些名词什么关系,能不能拎清楚。

  2. 范式储备:除了 "同义改写",你还知道哪些改写套路?Step-Back、Decomposition、HyDE、Multi-Query,各自的适用场景能说清吗?

  3. 原理理解:为什么改一改 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 语义鸿沟

查询改写如何补平 Query 与文档的语义鸿沟
查询改写如何补平 Query 与文档的语义鸿沟

上图是改写为什么有效的核心原理。向量检索说到底是算两段文本的 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-TurboGPT-4o-mini)就够了,没必要上旗舰模型,能省不少成本。

五、行业前沿

2025 年 EMNLP Findings 有一篇 Q-PRM 论文,讲的是 "自适应查询改写"。它根据 Query 的复杂度,动态决定要不要改写、用哪种方式改写。简单 Query 直接检索,复杂 Query 才上 Multi-Query 或 HyDE。这思路对生产环境很有启发:别一刀切地给所有 Query 都套改写管线,按需启用才经济。

面试高频追问

  1. Multi-Query 和 HyDE 哪个好?

    看场景。知识库风格化写作、技术文档,HyDE 更猛;通用问答、事实检索,Multi-Query 更稳。有条件就两个都试,拿自己的测试集跑数据说话。

  2. 改写会不会越改越偏?

    会。LLM 改写时如果理解偏了,会把检索带歪。规避办法:用小模型快速生成 + 多个变体取并集(一个偏了其他还能兜底);或者上 Q-PRM 那种自适应方案,简单 Query 不改写。

  3. 改写和 Rerank 是不是冲突?

    不冲突,是互补的。改写管的是 "召回更多潜在相关的内容",Rerank 管的是 "在召回的内容里把最相关的排前面"。生产环境经常两个一起上。

常见面试变体

  • "RAG 里 Query 改写有几种方式?"
  • "HyDE 的原理是什么?为什么有效?"
  • "多轮对话的 Query 怎么处理?"
  • "Query 改写和 Rerank 先做哪个?"

记忆口诀

改写两种方向:精炼(同义、压缩、Step-Back)、扩展(Multi-Query、Decomposition、HyDE)。

核心原理一句话:补平 Query 和 Doc 之间的语义鸿沟。

生产三注意:去重、延迟、按需启用。

总结

Query Rewriting 的本质是给向量检索做一道 "翻译",补平用户口语和文档书面语之间的语义鸿沟。五种范式按 "精炼 vs 扩展" 分类记,Java 落地用 LangChain4j 的 QueryTransformer 接口,生产环境重点考虑去重、延迟和按需启用。面试时把原理、范式、Java 实现、踩坑经验讲清楚,这道题基本就拿下了。