
解决什么问题
RAG 问答系统里,模型够强但检索层召回率太低——向量检索拿不到正确答案的上下文,模型 hallucinate。客服场景下"工单"vs"订单"语义相似度仅 0.05,直接检索返回错误段落。HyDE(假设文档检索)把这个问题的 Recall@5 从 0.42 拉到了 0.78,提升 36%。
HyDE 原理:不查"问题",先查"答案"
传统 RAG:用户问题 → 向量化 → 找相似段落 → 送 LLM。
HyDE 多了一步:先让 LLM 根据问题生成一个假设答案,再把假设答案向量化去检索。假设答案包含完整语义结构,和真实段落更接近,召回率大幅提升。
# 传统 RAG
query_embedding = embed(user_question)
results = vector_db.search(query_embedding, top_k=5)
# HyDE:多一次 LLM 生成
hypothetical_answer = llm.generate(question) # LLM 先写答案
hypothetical_embedding = embed(hypothetical_answer)
results = vector_db.search(hypothetical_embedding, top_k=5)
实测数据:召回率从 0.42 到 0.78
测试环境:
- 数据集:客服对话历史,500 条 QA 对
- Embedding:text-embedding-3-small
- LLM:GPT-4o-mini(生成假设答案)
- 向量库:Qdrant
| 方法 | Recall@5 | 提升 |
|---|---|---|
| 传统向量检索 | 0.42 | 基准 |
| HyDE | 0.78 | +36% |
HyDE 对语义模糊的短查询提升最明显。用户问"我那个订单处理得怎么样了",直接检索命中"订单查询"相关段落,但拿不到进度。HyDE 先生成"您查询的订单号 XXX 正在处理中,预计...",再去检索,就能命中真实进度段落。
三个工程陷阱
1. 假设答案不能太长
生成 500 字假设答案会引入过多无关细节,召回率反而下降。实测最优区间是 80-150 字。
# 错误示范
hypothetical_answer = llm.generate(question, max_tokens=500)
# 正确:控制字数
hypothetical_answer = llm.generate(question, max_tokens=120)
2. 生成的答案要加"不确定"标记
LLM 可能生成一个自信但完全错误的假设答案,错误会传递给检索层。加 system prompt 强制输出"据您描述..."而非"确认..."。
SYSTEM_PROMPT = \'\'\'你是一个信息检索助手。
- 根据用户问题生成一个简短答案(80-150字)
- 答案开头用"据您描述"而非"确认"
- 只输出答案内容,不要输出解释
- 如果不确定,直接说"未找到相关信息"
\'\'\'
3. 额外 LLM 调用有延迟成本
每条查询多一次 LLM 调用,延迟增加约 300-500ms。高并发场景下加缓存:相同问题第二次查询直接用缓存假设答案,不再调 LLM。
import hashlib
from functools import lru_cache
@lru_cache(maxsize=1000)
def get_hypothetical_answer(question: str) -> str:
return llm.generate(question, max_tokens=120)
现在你可以做什么
第一步:RAG 流程加 HyDE——在检索代码前加一行 llm.generate(question),限制 120 tokens,生成假设答案后送 Embedding;
第二步:加缓存——用 question 原文(或 MD5)做 key,相同问题第二次直接走缓存,不再调 LLM;
第三步:跑 Recall@5 评估,对比加 HyDE 前后的召回率变化,通常能提升 20-40%。
更多交流点击入群






