HyDE实战:RAG召回率从0.42到0.78的完整记录

HyDE RAG 召回率提升

解决什么问题

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%。

© 版权声明
THE END
喜欢就支持一下吧
点赞6 分享