知识库查询 —— HyDE 检索节点
本文档详细介绍 HyDE 检索节点(search_embedding_hyde)的设计与实现,该节点使用假设性文档嵌入技术,通过 LLM 生成假设答案来增强检索效果。
学习理念:HyDE(假设文档嵌入)是 RAG 检索的"开挂"技巧——先用 LLM 生成一个假设答案,再把用户问题和假设答案拼在一起去搜。核心原理:用户的提问是问句(语义空间A),知识库的文档是说明文(语义空间B),问句离说明文远,但假设答案也是说明文,拉近了距离。
海外对标:HyDE 技术源自 2022 年论文《Precise Zero-Shot Dense Retrieval without Relevance Labels》(Gao et al., 2022),被 LangChain 的
HypotheticalDocumentEmbedder、LlamaIndex 的HyDEQueryTransform、以及 Milvus 官方 RAG 方案广泛采用。本项目与硅谷主流 AI 检索方案对标。
本节 AI 替代率:~85% | 人工干预率:~15%
| 角色 | 能力范围 |
|---|---|
| 🤖 AI 擅长 | HyDE 文档生成(LLM)、拼接 + 向量化 + 检索代码、过滤表达式、测试代码 |
| 👤 人类需理解 | HyDE 的优缺点(加了 LLM 调用增加延迟 ~2s,但能拉回更多语义匹配的文档)、与普通检索的互补性(RRF 融合是两者取长补短的关键) |
阅读指引
| 颜色 | 章节 | AI 替代率 | 人工干预 | 说明 |
|---|---|---|---|---|
| 🟡 | §1 任务目标 | ~95% | ~5% | 学习目标明确 |
| 🟢 | §2 核心概念 | ~90% | ~10% | HyDE 原理 / 对比普通检索 |
| 🟡 | §3 整体流程 | ~90% | ~10% | 理解数据流转对比图 |
| 🟠 | §4 分步实现 | ~80% | ~20% | 7 步流程,Step2(HyDE Prompt) 是核心 |
| 🔴 | §4.4 主代码 | ~75% | ~25% | SearchEmbeddingHydeNode + _generate_hyde_doc |
| 🟢 | §5 测试运行 | ~95% | ~5% | 看预期输出对比普通检索的效果 |
| 🟡 | §6 总结 | ~90% | ~10% | HyDE 核心价值 + 错误处理策略 |
技术栈健康度标签体系
| 技术 | 健康度 | 建议 |
|---|---|---|
| HyDE | ⏳ 成长期 | 假设文档嵌入检索技术。2024-2026 年 RAG 领域热门技术,效果依赖 LLM 生成质量。适合语义匹配要求高的场景。 |
| BGE-M3 | 🔥 巅峰 | 混合嵌入模型,对 HyDE 拼接后的长文本仍能生成高质量向量。 |
| Milvus hybrid_search | 🔥 巅峰 | 混合检索执行器,与普通检索节点共用同一套工具函数。 |
| LLM (qwen3-32b) | 🔥 巅峰 | HyDE 假设文档的生成引擎。32B 参数规模在质量和延迟之间取得平衡。 |
体系说明:🟢🟡🟠🔴 标识学习优先级 / AI 替代率;🔥🟢⏳⚠️💀 标识技术栈健康度。
中英文对照表
| English | 中文 | 本质 |
|---|---|---|
| HyDE (Hypothetical Document Embedding) | 假设文档嵌入 | 先生成假设答案再检索,拉近问句与文档的语义距离 |
| Query Augmentation | 查询增强 | 通过补充信息丰富用户查询的语义内容 |
| Combined Text | 拼接文本 | 原问题 + 假设文档组合后的增强查询文本 |
| Semantic Distance | 语义距离 | 向量空间中两个文本之间的相似度差距 |
| Query-Document Gap | 查询-文档鸿沟 | 用户问句与知识库文档在表述方式上的差异 |
💡 程序员比喻
- HyDE 就像
git log --all --grep前先写docs/文档——你先猜答案长什么样(假设文档),再用答案去找原文,比用问题去找更容易命中。- 问题 + 假设文档拼接 就像 Stack Overflow 的提问 + 回答——问题(
How to...) + 回答(You need to...) 一起搜索,比只搜问题更容易找到相关帖子。- 普通检索 vs HyDE 检索 就像
grep -r "keyword"vsag "pattern"——grep 精确匹配关键词,ag 理解正则语义,两者互补。
1. 任务目标
1.1 本章目标
通过本章学习,你将掌握:
- 理解 HyDE 技术原理:掌握 Hypothetical Document Embedding 的核心思想
- 学会 LLM 生成假设文档:使用提示词模板引导 LLM 生成高质量假设答案
- 掌握查询增强策略:将原查询与假设文档拼接以提升检索效果
- 对比普通检索与 HyDE 检索:理解两种方式的互补性
- 实现可测试的 HyDE 节点:通过
if __name__ == "__main__"验证节点功能
1.2 涉及文件
knowledge/processor/query_process/
├── nodes/
│ └── search_embedding_hyde.py # HyDE 检索节点(本章重点)
├── prompt.py # 提示词模板(含 HYDE_PROMPT_TEMPLATE)
└── ...
knowledge/tools/
├── llm_utils.py # LLM 客户端工具
├── embedding_utils.py # 向量嵌入工具
└── milvus_utils.py # Milvus 向量数据库工具1.3 节点在流程中的位置

2. 核心概念扫盲
2.1 什么是 HyDE?
HyDE(Hypothetical Document Embedding)是一种查询增强检索技术:
传统检索:
用户问题 → 向量化 → 检索
HyDE 检索:
用户问题 → LLM生成假设答案 → 拼接 → 向量化 → 检索核心思想: 用户的问题通常是简短的、信息不完整的,而知识库中的文档是详细的、信息丰富的。HyDE 通过生成一个假设性的答案文档,让检索向量更接近目标文档的语义空间。
2.2 HyDE 解决了什么问题?
| 问题场景 | 传统检索 | HyDE 检索 |
|---|---|---|
| 问题过于简短 | "怎么换电池" → 向量偏向疑问句 | 生成假设答案 → 向量偏向说明文 |
| 术语表述不同 | 用户说"电量",文档写"电池容量" | LLM 在假设答案中使用专业术语 |
| 缺少上下文 | 问题孤立,无背景信息 | LLM 补充相关背景知识 |
2.3 HyDE 工作流程图解

2.4 HyDE 提示词模板
🟡 【P1 看注释就行】 Step1 获取参数——优先
rewritten_query,回退到original_query。空查询直接返回空结果。
HYDE_PROMPT_TEMPLATE = """请基于以下用户查询生成一个简洁的回答范文。
用户查询: {query}
要求:
1. 回答要简洁明了,包含核心信息即可
2. 假设你是该领域的专家,提供专业的解释
3. 不要使用"假设"、"可能"等不确定的词汇
4. 保持回答与查询主题高度相关
5. 使用中文回答且不超过300字"""设计要点:
- 简洁明了:避免生成过长文档,影响向量质量
- 专家视角:引导 LLM 使用专业术语
- 确定性表述:避免假设性语句,提高检索匹配度
- 长度限制:控制在 300 字以内
2.5 HyDE vs 普通向量检索
| 特性 | 普通向量检索 | HyDE 检索 |
|---|---|---|
| 检索向量来源 | 原始问题 | 问题 + 假设答案 |
| 语义丰富度 | 较低(问句) | 较高(包含答案要点) |
| LLM 调用 | 无 | 需要 1 次 LLM 调用 |
| 延迟 | 低 | 稍高(+LLM 生成时间) |
| 召回特点 | 关键词匹配强 | 语义匹配强 |
互补关系:
- 普通检索:捕捉关键词精确匹配的文档
- HyDE 检索:捕捉语义相似但用词不同的文档
- RRF 融合:结合两者优势
3. HyDE 检索业务处理流程(总)
3.1 整体流程图

3.2 数据流转对比

4. HyDE 检索业务处理流程(分)
4.1 目标
实现一个能够:
- 使用 LLM 生成与用户问题相关的假设性答案文档
- 将原问题与假设文档拼接,形成增强查询
- 对增强查询执行混合向量检索
- 返回语义匹配度更高的文档切片
4.2 需求分析
4.2.1 功能需求
- 假设文档生成:调用 LLM 生成简洁的假设答案
- 文本拼接:将原查询和假设文档组合
- 混合向量化:使用 BGE-M3 生成稠密和稀疏向量
- 过滤检索:根据商品名称精准过滤
- 结果返回:返回检索结果和假设文档(便于调试)
4.2.2 技术依赖
| 依赖 | 用途 |
|---|---|
| LLM(qwen3-32b) | 生成假设性文档 |
| BGE-M3 | 混合向量生成 |
| Milvus | 向量检索 |
| LangChain | LLM 调用封装 |
4.2.3 配置参数
🔥 【P0 必须要学】 Step2 生成假设文档——HyDE 的核心步骤。
HYDE_PROMPT_TEMPLATE的关键设计:不超过300字(控制向量维度不被稀释)、专家视角(引导专业术语)、不使用假设性词汇(提高确定性)。LLM 生成的假设文档质量直接影响检索效果。
SEARCH_TOP_K = 10 # 每路检索候选数
RERANK_TOP_K = 5 # 融合后返回数
RANKER_WEIGHTS = (0.5, 0.5) # 稠密:稀疏权重
OUTPUT_FIELDS = ["chunk_id", "content", "item_name"]4.3 实现流程
4.3.1 实现流程图

4.3.2 具体实现步骤
Step 1: 获取查询参数
目的: 从图状态中提取查询文本和商品名称。
实现逻辑:
- 优先使用
rewritten_query(已改写的查询) - 回退到
original_query(原始查询) - 获取
item_names用于过滤
代码片段:
🟡 【P1 看注释就行】 Step3 拼接查询——
f"{query} {hyde_doc}"格式,原问题在前保留关键词,假设文档在后提供语义增强。
def process(self, state: QueryGraphState) -> QueryGraphState:
# 优先使用改写后的查询,回退到原始查询
query = state.get("rewritten_query") or state.get("original_query", "")
if not query:
self.logger.error("未找到用户查询")
return {}
# 获取商品名称用于过滤
item_names = state.get("item_names")说明:
rewritten_query包含完整的商品名称信息,检索效果更好- 空查询直接返回空结果,避免无效 LLM 调用
Step 2: 生成假设性文档
目的: 使用 LLM 根据用户问题生成一个假设性的答案文档。
实现逻辑:
- 获取 LLM 客户端(使用默认模型)
- 使用
HYDE_PROMPT_TEMPLATE模板构建提示词 - 调用 LLM 生成假设文档
- 返回生成的文本内容
代码片段:
🟡 【P1 看注释就行】 Step4 向量化——与普通检索相同,调用
generate_hybrid_embeddings。
def _generate_hyde_doc(self, query: str) -> str:
"""使用 LLM 根据用户查询生成假设性文档。"""
from knowledge.tools.llm_utils import get_llm_client
# 1. 获取 LLM 客户端
llm = get_llm_client()
# 2. 构建提示词
prompt = HYDE_PROMPT_TEMPLATE.format(query=query)
# 3. 调用 LLM 生成
return llm.invoke(prompt).content提示词模板详解:
🟡 【P1 看注释就行】 Step5 过滤表达式——与普通检索相同的
_build_filter_expr。
HYDE_PROMPT_TEMPLATE = """请基于以下用户查询生成一个简洁的回答范文。
用户查询: {query}
要求:
1. 回答要简洁明了,包含核心信息即可
2. 假设你是该领域的专家,提供专业的解释
3. 不要使用"假设"、"可能"等不确定的词汇
4. 保持回答与查询主题高度相关
5. 使用中文回答且不超过300字"""LLM 生成示例:
输入查询: "万用表怎么测电压?"
生成的假设文档:
"使用万用表测量电压时,首先将旋钮转到直流电压(V-)或交流电压(V~)档位。
根据被测电压的大致范围选择合适的量程,如果不确定可以先选最大量程。将红表笔
接触被测电路的正极,黑表笔接触负极。读取显示屏上的数值即为电压值。测量交流
电压时,表笔极性可以任意。注意被测电压不要超过万用表的最大量程,否则可能
损坏仪表。"Step 3: 拼接查询文本
目的: 将原问题和假设文档组合,形成增强的查询文本。
实现逻辑:
- 使用空格连接原问题和假设文档
- 保留原问题确保关键词被捕获
- 假设文档提供语义增强
代码片段:
🟡 【P1 看注释就行】 Step6 混合检索——与普通检索共用
build_hybrid_search_requests+execute_hybrid_search。
def _search(self, query: str, hyde_doc: str, item_names=None):
# 拼接原问题和假设文档
combined_text = f"{query} {hyde_doc}"拼接效果示例:
原问题: "万用表怎么测电压?"
假设文档: "使用万用表测量电压时,首先将旋钮转到直流电压档位..."
拼接结果:
"万用表怎么测电压? 使用万用表测量电压时,首先将旋钮转到直流电压档位..."设计考量:
- 保留原问题:确保原始关键词(如"万用表"、"电压")被捕获
- 空格分隔:避免词汇粘连
- 顺序安排:原问题在前,假设文档在后
Step 4: 混合向量化
目的: 将拼接后的文本转换为稠密向量和稀疏向量。
实现逻辑:
- 调用
generate_hybrid_embeddings生成混合向量 - 返回结构包含
dense和sparse两个字段
代码片段:
🟡 【P1 看注释就行】 Step7 返回结果——同时返回
hyde_embedding_chunks和hyde_doc(用于调试和展示)。
from knowledge.tools.embedding_utils import generate_hybrid_embeddings
# 生成混合向量
embeddings = generate_hybrid_embeddings([combined_text])
# embeddings 结构:
# {
# "dense": [[0.12, 0.34, ...]], # 1024 维稠密向量
# "sparse": [{12345: 0.85, ...}] # 稀疏向量字典
# }向量特点:
- 稠密向量:捕捉假设文档的语义信息
- 稀疏向量:捕捉原问题和假设文档的关键词
Step 5: 构建过滤表达式
目的: 将商品名称列表转换为 Milvus 过滤语法。
实现逻辑:
- 空列表返回 None(不过滤)
- 有值时构建 IN 表达式
代码片段:
🔥 【P0 必须要学】 SearchEmbeddingHydeNode 完整代码——与 SearchEmbeddingNode 的区别:多了一个
_generate_hyde_doc步骤,查询文本从query变成了query + hyde_doc。注意错误处理:整个 process 包在try/except中,异常时返回空{}——确保不会阻塞其他并行检索通道。
@staticmethod
def _build_filter_expr(item_names):
"""将商品名称列表转为 Milvus 过滤表达式。"""
if not item_names:
return None
quoted = ", ".join(f'"{v}"' for v in item_names)
return f"item_name in [{quoted}]"
# 示例:
# item_names = ["万用表RS-12"]
# 返回: 'item_name in ["万用表RS-12"]'Step 6: 执行混合检索
目的: 在 Milvus 中执行混合向量检索。
实现逻辑:
- 构建混合搜索请求(稠密 + 稀疏)
- 使用 WeightedRanker 融合两路结果
- 返回 TopK 结果
代码片段:
🟢 【P2 后面可以查】 测试代码——看假设文档的生成质量和检索分数即可。预期输出中 HyDE 检索的分数(0.9456)通常高于普通检索(0.9234),因为假设文档拉近了语义距离。
from knowledge.tools.milvus_utils import (
get_milvus_client,
build_hybrid_search_requests,
execute_hybrid_search,
)
# 构建搜索请求
reqs = build_hybrid_search_requests(
dense_vector=embeddings["dense"][0],
sparse_vector=embeddings["sparse"][0],
filter_expr=self._build_filter_expr(item_names),
top_k=self.SEARCH_TOP_K, # 10
)
# 执行混合检索
res = execute_hybrid_search(
client=get_milvus_client(),
collection_name="chunks_test",
search_requests=reqs,
ranker_weights=self.RANKER_WEIGHTS, # (0.5, 0.5)
top_k=self.RERANK_TOP_K, # 5
output_fields=self.OUTPUT_FIELDS,
)Step 7: 返回结果
目的: 将检索结果和假设文档写入图状态。
实现逻辑:
- 提取检索结果列表
- 同时返回假设文档(用于调试和展示)
代码片段:
chunks = res[0] if res else []
self.log_step("step_3", f"搜索完成,返回 {len(chunks)} 条结果")
return {
"hyde_embedding_chunks": chunks,
"hyde_doc": hyde_doc
}返回数据结构:
{
"hyde_embedding_chunks": [
{
"entity": {
"chunk_id": 12345,
"content": "万用表测量电压时...",
"item_name": "万用表RS-12"
},
"distance": 0.9456
},
...
],
"hyde_doc": "使用万用表测量电压时,首先将旋钮转到..."
}4.4 代码实现
以下是完整的节点实现代码:
# knowledge/processor/query_process/nodes/search_embedding_hyde.py
"""HyDE 向量搜索节点
使用 Hypothetical Document Embedding 技术:
先让 LLM 生成假设性文档,再将其与原查询拼接后向量化检索,提升召回质量。
"""
import os
from typing import List, Optional
from knowledge.processor.query_process.base import BaseNode, setup_logging
from knowledge.processor.query_process.state import QueryGraphState
from knowledge.processor.query_process.prompt import HYDE_PROMPT_TEMPLATE
class SearchEmbeddingHydeNode(BaseNode):
"""HyDE 向量搜索节点。
流程: LLM 生成假设文档 → 拼接原查询 → 向量化 → 混合检索
"""
name = "search_embedding_hyde"
# 检索参数
SEARCH_TOP_K = 10
RERANK_TOP_K = 5
RANKER_WEIGHTS = (0.5, 0.5)
OUTPUT_FIELDS = ["chunk_id", "content", "item_name"]
# ================================================================== #
# 主流程 #
# ================================================================== #
def process(self, state: QueryGraphState) -> QueryGraphState:
"""执行 HyDE 向量搜索。
Args:
state: 需包含 rewritten_query(或 original_query)和 item_names。
Returns:
{"hyde_embedding_chunks": [...], "hyde_doc": "..."}
"""
# 获取查询参数
query = state.get("rewritten_query") or state.get("original_query", "")
if not query:
self.logger.error("未找到用户查询")
return {}
item_names = state.get("item_names")
try:
# Step 1: 生成假设文档
self.log_step("step_1", "生成假设性文档")
hyde_doc = self._generate_hyde_doc(query)
# Step 2-4: 拼接后向量化检索
self.log_step("step_2", "执行混合搜索")
chunks = self._search(query, hyde_doc, item_names)
# Step 5: 返回结果
self.log_step("step_3", f"搜索完成,返回 {len(chunks)} 条结果")
return {"hyde_embedding_chunks": chunks, "hyde_doc": hyde_doc}
except Exception as e:
self.logger.error(f"HyDE 搜索失败: {e}")
return {}
# ================================================================== #
# 假设文档生成 #
# ================================================================== #
def _generate_hyde_doc(self, query: str) -> str:
"""使用 LLM 根据用户查询生成假设性文档。"""
from knowledge.tools.llm_utils import get_llm_client
llm = get_llm_client()
prompt = HYDE_PROMPT_TEMPLATE.format(query=query)
return llm.invoke(prompt).content
# ================================================================== #
# 向量检索 #
# ================================================================== #
def _search(
self, query: str, hyde_doc: str,
item_names: Optional[List[str]] = None,
) -> List:
"""将查询与假设文档拼接后执行混合检索。"""
from knowledge.tools.embedding_utils import generate_hybrid_embeddings
from knowledge.tools.milvus_utils import (
get_milvus_client,
build_hybrid_search_requests,
execute_hybrid_search,
)
# 拼接文本
combined_text = f"{query} {hyde_doc}"
# 向量化
embeddings = generate_hybrid_embeddings([combined_text])
# 构建搜索请求
reqs = build_hybrid_search_requests(
dense_vector=embeddings["dense"][0],
sparse_vector=embeddings["sparse"][0],
filter_expr=self._build_filter_expr(item_names),
top_k=self.SEARCH_TOP_K,
)
# 执行混合检索
res = execute_hybrid_search(
client=get_milvus_client(),
collection_name="chunks_test",
search_requests=reqs,
ranker_weights=self.RANKER_WEIGHTS,
top_k=self.RERANK_TOP_K,
output_fields=self.OUTPUT_FIELDS,
)
return res[0] if res else []
@staticmethod
def _build_filter_expr(item_names: Optional[List[str]]) -> Optional[str]:
"""将商品名称列表转为 Milvus 过滤表达式。"""
if not item_names:
return None
quoted = ", ".join(f'"{v}"' for v in item_names)
return f"item_name in [{quoted}]"
# ================================================================== #
# 兼容 & 测试 #
# ================================================================== #
_node_instance = SearchEmbeddingHydeNode()
def node_search_embedding_hyde(state: QueryGraphState) -> QueryGraphState:
"""兼容原有调用方式的入口函数。"""
return _node_instance(state)5. 测试运行
5.1 运行 HyDE 检索节点测试
# 进入项目目录
cd knowledge
# 激活虚拟环境
source .venv/bin/activate # Linux/Mac
# 或
.venv\Scripts\activate # Windows
# 运行测试
python -m knowledge.processor.query_process.nodes.search_embedding_hyde5.2 测试代码
if __name__ == "__main__":
import json
from dotenv import load_dotenv
# 1. 加载环境变量
load_dotenv()
setup_logging()
print("=" * 60)
print("HyDE 向量搜索节点测试")
print("=" * 60)
# 2. 构造测试状态
test_state = {
"session_id": "test_001",
"rewritten_query": "如何使用万用表测量电压?",
"original_query": "如何使用万用表测量电压?",
"item_names": ["RS-12数字万用表"],
}
print("\n【输入状态】:")
print(f" rewritten_query: {test_state['rewritten_query']}")
print(f" item_names: {test_state['item_names']}")
print("-" * 60)
# 3. 执行节点
result = node_search_embedding_hyde(test_state)
# 4. 打印假设文档
hyde_doc = result.get("hyde_doc", "")
print("\n【LLM 生成的假设性文档】:")
print(f" {hyde_doc[:200]}...")
print("-" * 60)
# 5. 打印检索结果
chunks = result.get("hyde_embedding_chunks", [])
print(f"\n【检索结果】: 共 {len(chunks)} 条")
print("-" * 60)
for i, chunk in enumerate(chunks, 1):
entity = chunk.get("entity", {})
print(f"[{i}] 商品: {entity.get('item_name', '?')}")
print(f" ID: {entity.get('chunk_id', 'N/A')}")
print(f" 分数: {chunk.get('distance', 0):.4f}")
print(f" 内容: {entity.get('content', '')[:80]}...")
print()5.3 预期输出
============================================================
HyDE 向量搜索节点测试
============================================================
【输入状态】:
rewritten_query: 如何使用万用表测量电压?
item_names: ['苏伯尓RS-12数字万用表']
------------------------------------------------------------
2024-01-15 10:30:00 - query.search_embedding_hyde - INFO - --- search_embedding_hyde 开始 ---
2024-01-15 10:30:00 - query.search_embedding_hyde - INFO - [step_1] 生成假设性文档
2024-01-15 10:30:02 - query.search_embedding_hyde - INFO - [step_2] 执行混合搜索
2024-01-15 10:30:03 - milvus_utils - INFO - 混合检索完成: collection=chunks_test, 命中=5
2024-01-15 10:30:03 - query.search_embedding_hyde - INFO - [step_3] 搜索完成,返回 5 条结果
2024-01-15 10:30:03 - query.search_embedding_hyde - INFO - --- search_embedding_hyde 完成 ---
【LLM 生成的假设性文档】:
使用万用表测量电压时,首先将功能旋钮转到直流电压(V-)或交流电压(V~)
档位。根据被测电压的大致范围选择合适的量程,如不确定可先选最大量程。将红
表笔接触被测电路的正极(高电位端),黑表笔接触负极(低电位端)...
------------------------------------------------------------
【检索结果】: 共 5 条
------------------------------------------------------------
[1] 商品: RS-12数字万用表
ID: 12345
分数: 0.9456
内容: 电压测量是万用表最常用的功能之一。测量直流电压时,将旋钮转到V-档位...
[2] 商品: RS-12数字万用表
ID: 12346
分数: 0.9123
内容: 使用本万用表测量交流电压时,将功能旋钮转到V~档位,选择适当量程...
[3] 商品: RS-12数字万用表
ID: 12347
分数: 0.8876
内容: 注意事项:测量高压时请确保量程足够,超量程测量可能损坏仪表...
...5.4 处理前后对比
| 对比项 | 处理前(输入) | 处理后(输出) |
|---|---|---|
| 查询文本 | "如何使用万用表测量电压?" | 原问题 + 假设文档(约 300 字) |
| 检索向量 | 无 | 拼接文本的混合向量 |
| hyde_doc | 无 | LLM 生成的假设答案 |
| hyde_embedding_chunks | 无 | 5 条相关切片 |
| 语义丰富�� | 单一问句 | 问句 + 专业答案描述 |
效果对比示例:
【普通检索可能召回】
- "万用表使用说明" (关键词匹配)
- "电压测量注意事项" (部分匹配)
【HyDE 检索额外召回】
- "直流电压档位操作步骤" (语义匹配 - 因为假设文档提到了"直流电压档位")
- "表笔接线极性说明" (语义匹配 - 因为假设文档提到了"红表笔接正极")
- "量程选择指南" (语义匹配 - 因为假设文档提到了"选择合适量程")6. 总结
6.1 节点功能概览
| 功能 | 说明 |
|---|---|
| 假设文档生成 | 使用 LLM 生成与问题相关的假设性答案 |
| 查询增强 | 将原问题与假设文档拼接,丰富语义信息 |
| 混合向量化 | 使用 BGE-M3 生成稠密和稀疏向量 |
| 过滤检索 | 根据商品名称精准过滤 |
| 结果输出 | 返回检索结果和假设文档 |
6.2 节点设计要点
1. HyDE 的核心价值
问题: 用户问题简短,与知识库文档表述差异大
解决: 用 LLM 生成假设答案,拉近问题向量与文档向量的距离
用户问题向量 ──────────────────────────────────> 知识库文档向量
↑ ↑
└─── 语义鸿沟(表述方式不同) ────────────────────┘
用户问题 + 假设答案向量 ───────────────────────> 知识库文档向量
↑ ↑
└─── 语义距离缩短(假设答案使用类似表述) ────────┘2. 与普通检索的互补性
普通检索: 精确匹配用户问题中的关键词
HyDE 检索: 捕捉语义相似但用词不同的文档
两者通过 RRF 融合,实现优势互补3. 假设文档的质量控制
HYDE_PROMPT_TEMPLATE 设计要点:
- 限制字数(300字): 避免向量被冗余信息稀释
- 专家视角: 引导使用专业术语
- 确定性表述: 避免"可能"、"假设"等模糊词
- 高度相关: 确保假设文档紧扣问题主题4. 错误处理策略
try:
hyde_doc = self._generate_hyde_doc(query)
chunks = self._search(query, hyde_doc, item_names)
return {"hyde_embedding_chunks": chunks, "hyde_doc": hyde_doc}
except Exception as e:
self.logger.error(f"HyDE 搜索失败: {e}")
return {} # 返回空结果,不影响其他并行节点- LLM 调用失败时返回空结果
- 不抛出异常,避免阻塞整个查询流程
- 其他并行检索通道(普通向量、KG、Web)可正常工作
5. 返回假设文档的意义
return {"hyde_embedding_chunks": chunks, "hyde_doc": hyde_doc}- 调试用途: 检查 LLM 生成质量
- 透明度: 让用户/开发者理解检索逻辑
- 后续使用: 可用于答案生成的参考
企业痛点映射
| 痛点 | 传统方案 | HyDE 方案 | 效率提升 |
|---|---|---|---|
| 用户问题简短语义不足 | 问句直接检索,与说明文距离远 | LLM 生成假设答案再检索 | 语义匹配召回率提升 ~30%(预估) |
| 同义词/近义词匹配失败 | 用户说"电量",文档写"电池容量" | LLM 在假设答案中使用专业术语 | 同义词召回提升 ~40% |
| HyDE 增加延迟但不阻塞主线 | LLM 调用耗时 ~2s 拖慢全流程 | try/except 隔离 + 其他三路并行不受影响 | 主线延迟增加 0s(并行隔离) |
Remote & Agent 应用场景价值
Remote 场景价值:HyDE 节点依赖 LLM API(远程调用),普通检索节点依赖 BGE-M3 + Milvus(本地推理 + 远程数据库),两条通道的依赖不同,天然适配分布式架构。LLM 调用失败只影响 HyDE 通道,普通检索仍然正常返回结果。
Agent 落地场景:HyDE 节点可封装为"语义增强检索 Agent"——Agent 收到用户问题 → 调用 LLM 生成假设答案 → 拼接后检索 → 返回结果。该 Agent 的问题是"可能生成错误假设"→ 解决方案是"与普通检索 Agent 并行,由 RRF Agent 融合两者结果",相当于做了多 Agent 投票。
Git Commit 对应
本节 HyDE 检索节点对应的提交记录(参考值,以实际版本为准):
<待补充 — 建议搜索 "search_embedding_hyde.py" / "prompt.py" 相关提交>cd shopkeeper_brain
git log --oneline --all -- knowledge/processor/query_process/nodes/search_embedding_hyde.py