Skip to content

03.3 - HyDE 与查询转换

定位: RAG Pipeline 的"查询预处理"环节——在检索之前对查询进行优化,提高检索命中率 面试高频度: ⭐⭐⭐ 考查方式: HyDE 原理、查询转换策略、适用场景

一、这是什么?为什么需要它?

是什么

查询转换(Query Transformation)是一组在检索之前优化用户查询的技术,目的是弥补"用户原始查询"和"知识库中文档表述"之间的语义差距。

用户查询:"年假怎么休"  (口语化、简短)
  |
  v
查询转换后:"公司年假制度、带薪年假规定"
  |
  v
检索命中 ↑  ↑  ↑

为什么需要查询转换?

核心问题:查询和文档的表述方式天然不同

用户查询(短、口语化、模糊):
  "公司怎么休年假"  "最近有啥政策"  "那个XX产品怎么用"

知识库文档(长、正式、结构化):
  "根据公司员工手册第三章第2条,员工年假..."
  "2025年第一季度人力资源政策更新汇总"
  "XX产品使用说明书 - API调用方式"

两者之间的 gap 导致:向量相似度低 -> 检索不到 -> RAG 失败

查询转换 = 架设 query 和 doc 之间的桥梁

二、原理拆解

2.1 HyDE(假设文档嵌入)

核心思想:先让 LLM 根据查询生成一个"假设的文档",再用这个文档去检索。

普通检索:
  查询 -> [向量化] -> 检索向量数据库
  问题:查询太短,向量包含的信息太少

HyDE 检索:
  查询 -> [LLM] -> 假设文档 -> [向量化] -> 检索向量数据库
  优点:假设文档包含更多语义信息 -> 向量更丰富 -> 检索更准

例子:
  查询:"年假政策"
  LLM 生成假设文档:
    "年假政策是公司为员工提供的带薪休假制度...通常为5-15天..."
  用这个"伪文档"去检索 -> 更容易匹配到真正的年假文档
HyDE 的原理流程:

+----------+    +----------+    +----------+    +----------+
| 用户查询   |--->|  LLM     |--->| 假设文档  |--->| Embedding |
| "年假..."  |    | 生成假设  |    |          |    | (同模型)  |
+----------+    +----------+    +----------+    +-----+----+
                                                      |
                                                      v
                                            +---------+------+
                                            | 向量数据库检索  |
                                            +---------+------+
                                                      |
                                                      v
                                            +---------+------+
                                            | 返回真实文档块   |
                                            +----------------+

HyDE 的适用条件

HyDE 最有效的场景:
  1. 查询非常简短(2-5个字)
  2. 查询口语化("咋弄啊" -> 生成正式版)
  3. 查询和文档表述差异大

HyDE 效果一般的场景:
  1. 查询已经很长、很详细
  2. 查询本身就是一段完整描述
  3. 需要精确匹配专有名词(产品编号等)

2.2 Multi-Query(多查询生成)

核心思想:从一个查询生成多个不同角度的查询,分别检索后合并结果。

用户查询:"公司的福利待遇怎么样?"

Multi-Query 生成多个子查询:
  查询1: "公司薪资和奖金制度"
  查询2: "公司年假和病假政策"
  查询3: "公司社保和公积金"
  查询4: "公司培训和发展机会"

分别检索 -> 合并去重 -> 得到更全面的结果
python
# Multi-Query 实现
from langchain.retrievers.multi_query import MultiQueryRetriever

multi_query_retriever = MultiQueryRetriever.from_llm(
    retriever=base_retriever,
    llm=llm
)

results = multi_query_retriever.invoke("公司的福利待遇怎么样?")
# 内部自动生成多个查询变体,分别检索,合并结果

2.3 Step-back Prompting(后退提问)

核心思想:从具体问题"后退"到更抽象的问题,先获取背景知识再回答具体问题。

用户问题:"公司的 MacBook Pro 内存最低配置要求是 16GB 还是 32GB?"

Step-back 抽象问题:"公司对开发设备的硬件配置要求是什么?"

检索抽象问题 -> 获取设备配置总体政策 -> 再回答具体内存问题

适用场景:具体问题需要先理解宏观背景才能回答

2.4 查询路由(Query Routing)

核心思想:根据查询类型路由到不同的处理路径。

用户查询
  |
  +-- 事实性问题 -> 向量检索 -> 生成回答
  |
  +-- 计算/推理问题 -> 代码解释器 + 检索
  |
  +-- 开放创作 -> 不使用检索,直接生成
  |
  +-- 问候/闲聊 -> 直接回答,不走 RAG
  |
  +-- 多步复杂问题 -> Agent 模式

三、图解全景

查询转换技术全景

+------------------------------------------------------------------+
|                     查询转换技术全景                                |
|                                                                   |
|  原始查询:"年假怎么休"(简短、口语化)                              |
|                                                                   |
|  转换方式:                                                        |
|                                                                   |
|  HyDE:           [LLM] -> "年假制度是公司为员工..." -> 检索        |
|  适用:短查询、低频词汇                                             |
|                                                                   |
|  Multi-Query:    [拆分] -> ["年假天数","申请流程","规定"] -> 合并   |
|  适用:多角度、需要全面覆盖                                         |
|                                                                   |
|  Step-back:      [抽象] -> "公司假期制度" -> 背景 -> 具体          |
|  适用:具体问题需要宏观背景                                           |
|                                                                   |
|  查询重写:        [扩写] -> "公司年假的休假天数规定和申请流程"        |
|  适用:补充上下文、消除歧义                                          |
|                                                                   |
|  Query Routing:  [分类] -> 事实/推理/创意 -> 路由到不同模块         |
|  适用:复杂系统、多类型查询                                          |
|                                                                   |
+------------------------------------------------------------------+

四、实战验证

4.1 HyDE 实现

python
from langchain_openai import ChatOpenAI, OpenAIEmbeddings
from langchain.retrievers import HyDERetriever

# 创建 HyDE 检索器
hyde_retriever = HyDERetriever(
    llm=ChatOpenAI(model="gpt-4o-mini"),
    embeddings=OpenAIEmbeddings(),
    vectorstore=vectorstore,
    k=3,
    # HyDE prompt 模板
    hyde_prompt="""请根据以下查询,写一段假设的文档段落。
假设这是知识库中应该存在的文档内容。
查询:{query}
假设文档内容:"""
)

# 对比普通检索
normal_results = vectorstore.similarity_search("年假政策", k=3)
hyde_results = hyde_retriever.invoke("年假政策")

print(f"普通检索 Top-1: {normal_results[0].page_content[:50]}")
print(f"HyDE 检索 Top-1: {hyde_results[0].page_content[:50]}")

4.2 查询转换效果对比

方法召回提升延迟增加适用查询不适用查询
无转换基线0--
HyDE+10-20%+200-500ms短查询/口语化精确编号查询
Multi-Query+15-25%+300-800ms多角度问题单一事实问题
Step-back+10-15%+200-400ms具体->抽象已经是抽象问题
查询重写+5-15%+100-300ms歧义/不完整完整问题
路由+0-30%+50-100ms混合类型单一类型

五、面试视角

问题答案要点
HyDE 的核心创新点是什么?用 LLM 生成"假设文档"代替原始查询做检索。因为查询和文档的表述差异大(查询短/口语化/文档长/正式),假设文档能缩小语义差距,提高检索准确率
Multi-Query 和 HyDE 的适用场景差异?Multi-Query 适合需要全面覆盖、多角度的问题(如"公司的福利");HyDE 适合查询短、表述不准确的情况(如"年假咋休")。可以组合使用
查询转换的成本如何?主要成本是额外的 LLM 调用。HyDE 和 Multi-Query 各增加 1 次 LLM 调用,但能显著提升检索质量。对于高质量要求的生产系统来说,成本可接受
什么时候不应使用查询转换?1) 查询已经很长且详细时;2) 需要精确匹配时(产品编号/代码);3) 延迟敏感场景;4) 简单 FAQ(直接检索就够)

📚 相关链接

  • **检索与重排序** — 转换后的查询如何检索和重排序
  • **生成模型选择与Prompt设计** — Prompt 模板配合查询转换
  • **AgenticRAG** — 查询转换的进阶:Agent 自主规划
  • <- 返回 **生成融合索引**

Knowledge4J — Java 知识库