Skip to content

知识库查询 —— 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" vs ag "pattern"——grep 精确匹配关键词,ag 理解正则语义,两者互补。

1. 任务目标

1.1 本章目标

通过本章学习,你将掌握:

  1. 理解 HyDE 技术原理:掌握 Hypothetical Document Embedding 的核心思想
  2. 学会 LLM 生成假设文档:使用提示词模板引导 LLM 生成高质量假设答案
  3. 掌握查询增强策略:将原查询与假设文档拼接以提升检索效果
  4. 对比普通检索与 HyDE 检索:理解两种方式的互补性
  5. 实现可测试的 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。空查询直接返回空结果。

python
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 目标

实现一个能够:

  1. 使用 LLM 生成与用户问题相关的假设性答案文档
  2. 将原问题与假设文档拼接,形成增强查询
  3. 对增强查询执行混合向量检索
  4. 返回语义匹配度更高的文档切片

4.2 需求分析

4.2.1 功能需求

  1. 假设文档生成:调用 LLM 生成简洁的假设答案
  2. 文本拼接:将原查询和假设文档组合
  3. 混合向量化:使用 BGE-M3 生成稠密和稀疏向量
  4. 过滤检索:根据商品名称精准过滤
  5. 结果返回:返回检索结果和假设文档(便于调试)

4.2.2 技术依赖

依赖用途
LLM(qwen3-32b)生成假设性文档
BGE-M3混合向量生成
Milvus向量检索
LangChainLLM 调用封装

4.2.3 配置参数

🔥 【P0 必须要学】 Step2 生成假设文档——HyDE 的核心步骤HYDE_PROMPT_TEMPLATE 的关键设计:不超过300字(控制向量维度不被稀释)、专家视角(引导专业术语)、不使用假设性词汇(提高确定性)。LLM 生成的假设文档质量直接影响检索效果。

python
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: 获取查询参数

目的: 从图状态中提取查询文本和商品名称。

实现逻辑:

  1. 优先使用 rewritten_query(已改写的查询)
  2. 回退到 original_query(原始查询)
  3. 获取 item_names 用于过滤

代码片段:

🟡 【P1 看注释就行】 Step3 拼接查询——f"{query} {hyde_doc}" 格式,原问题在前保留关键词,假设文档在后提供语义增强。

python
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 根据用户问题生成一个假设性的答案文档。

实现逻辑:

  1. 获取 LLM 客户端(使用默认模型)
  2. 使用 HYDE_PROMPT_TEMPLATE 模板构建提示词
  3. 调用 LLM 生成假设文档
  4. 返回生成的文本内容

代码片段:

🟡 【P1 看注释就行】 Step4 向量化——与普通检索相同,调用 generate_hybrid_embeddings

python
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

python
HYDE_PROMPT_TEMPLATE = """请基于以下用户查询生成一个简洁的回答范文。
用户查询: {query}
要求:
1. 回答要简洁明了,包含核心信息即可
2. 假设你是该领域的专家,提供专业的解释
3. 不要使用"假设"、"可能"等不确定的词汇
4. 保持回答与查询主题高度相关
5. 使用中文回答且不超过300字"""

LLM 生成示例:

输入查询: "万用表怎么测电压?"

生成的假设文档:
"使用万用表测量电压时,首先将旋钮转到直流电压(V-)或交流电压(V~)档位。
根据被测电压的大致范围选择合适的量程,如果不确定可以先选最大量程。将红表笔
接触被测电路的正极,黑表笔接触负极。读取显示屏上的数值即为电压值。测量交流
电压时,表笔极性可以任意。注意被测电压不要超过万用表的最大量程,否则可能
损坏仪表。"

Step 3: 拼接查询文本

目的: 将原问题和假设文档组合,形成增强的查询文本。

实现逻辑:

  1. 使用空格连接原问题和假设文档
  2. 保留原问题确保关键词被捕获
  3. 假设文档提供语义增强

代码片段:

🟡 【P1 看注释就行】 Step6 混合检索——与普通检索共用 build_hybrid_search_requests + execute_hybrid_search

python
def _search(self, query: str, hyde_doc: str, item_names=None):
    # 拼接原问题和假设文档
    combined_text = f"{query} {hyde_doc}"

拼接效果示例:

原问题: "万用表怎么测电压?"
假设文档: "使用万用表测量电压时,首先将旋钮转到直流电压档位..."

拼接结果:
"万用表怎么测电压? 使用万用表测量电压时,首先将旋钮转到直流电压档位..."

设计考量:

  • 保留原问题:确保原始关键词(如"万用表"、"电压")被捕获
  • 空格分隔:避免词汇粘连
  • 顺序安排:原问题在前,假设文档在后

Step 4: 混合向量化

目的: 将拼接后的文本转换为稠密向量和稀疏向量。

实现逻辑:

  1. 调用 generate_hybrid_embeddings 生成混合向量
  2. 返回结构包含 densesparse 两个字段

代码片段:

🟡 【P1 看注释就行】 Step7 返回结果——同时返回 hyde_embedding_chunkshyde_doc(用于调试和展示)。

python
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 过滤语法。

实现逻辑:

  1. 空列表返回 None(不过滤)
  2. 有值时构建 IN 表达式

代码片段:

🔥 【P0 必须要学】 SearchEmbeddingHydeNode 完整代码——与 SearchEmbeddingNode 的区别:多了一个 _generate_hyde_doc 步骤,查询文本从 query 变成了 query + hyde_doc注意错误处理:整个 process 包在 try/except 中,异常时返回空 {}——确保不会阻塞其他并行检索通道。

python
@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 中执行混合向量检索。

实现逻辑:

  1. 构建混合搜索请求(稠密 + 稀疏)
  2. 使用 WeightedRanker 融合两路结果
  3. 返回 TopK 结果

代码片段:

🟢 【P2 后面可以查】 测试代码——看假设文档的生成质量和检索分数即可。预期输出中 HyDE 检索的分数(0.9456)通常高于普通检索(0.9234),因为假设文档拉近了语义距离。

python
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: 返回结果

目的: 将检索结果和假设文档写入图状态。

实现逻辑:

  1. 提取检索结果列表
  2. 同时返回假设文档(用于调试和展示)

代码片段:

python
chunks = res[0] if res else []

self.log_step("step_3", f"搜索完成,返回 {len(chunks)} 条结果")

return {
    "hyde_embedding_chunks": chunks,
    "hyde_doc": hyde_doc
}

返回数据结构:

python
{
    "hyde_embedding_chunks": [
        {
            "entity": {
                "chunk_id": 12345,
                "content": "万用表测量电压时...",
                "item_name": "万用表RS-12"
            },
            "distance": 0.9456
        },
        ...
    ],
    "hyde_doc": "使用万用表测量电压时,首先将旋钮转到..."
}

4.4 代码实现

以下是完整的节点实现代码:

python
# 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 检索节点测试

bash
# 进入项目目录
cd knowledge

# 激活虚拟环境
source .venv/bin/activate  # Linux/Mac
# 或
.venv\Scripts\activate     # Windows

# 运行测试
python -m knowledge.processor.query_process.nodes.search_embedding_hyde

5.2 测试代码

python
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_docLLM 生成的假设答案
hyde_embedding_chunks5 条相关切片
语义丰富��单一问句问句 + 专业答案描述

效果对比示例:

【普通检索可能召回】
- "万用表使用说明" (关键词匹配)
- "电压测量注意事项" (部分匹配)

【HyDE 检索额外召回】
- "直流电压档位操作步骤" (语义匹配 - 因为假设文档提到了"直流电压档位")
- "表笔接线极性说明" (语义匹配 - 因为假设文档提到了"红表笔接正极")
- "量程选择指南" (语义匹配 - 因为假设文档提到了"选择合适量程")

6. 总结

6.1 节点功能概览

功能说明
假设文档生成使用 LLM 生成与问题相关的假设性答案
查询增强将原问题与假设文档拼接,丰富语义信息
混合向量化使用 BGE-M3 生成稠密和稀疏向量
过滤检索根据商品名称精准过滤
结果输出返回检索结果和假设文档

6.2 节点设计要点

1. HyDE 的核心价值

问题: 用户问题简短,与知识库文档表述差异大
解决: 用 LLM 生成假设答案,拉近问题向量与文档向量的距离

用户问题向量 ──────────────────────────────────> 知识库文档向量
     ↑                                                ↑
     └─── 语义鸿沟(表述方式不同) ────────────────────┘

用户问题 + 假设答案向量 ───────────────────────> 知识库文档向量
     ↑                                                ↑
     └─── 语义距离缩短(假设答案使用类似表述) ────────┘

2. 与普通检索的互补性

普通检索: 精确匹配用户问题中的关键词
HyDE 检索: 捕捉语义相似但用词不同的文档

两者通过 RRF 融合,实现优势互补

3. 假设文档的质量控制

python
HYDE_PROMPT_TEMPLATE 设计要点:
- 限制字数(300字): 避免向量被冗余信息稀释
- 专家视角: 引导使用专业术语
- 确定性表述: 避免"可能""假设"等模糊词
- 高度相关: 确保假设文档紧扣问题主题

4. 错误处理策略

python
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. 返回假设文档的意义

python
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" 相关提交>
bash
cd shopkeeper_brain
git log --oneline --all -- knowledge/processor/query_process/nodes/search_embedding_hyde.py

OPC 超级个体实战指南