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 自主规划
- <- 返回 **生成融合索引**