什么是假设性文档嵌入(HyDE)?它的工作原理是什么?

#RAG#Agent#面试八股

什么是假设性文档嵌入(HyDE)?它的工作原理是什么?

结论:HyDE(Hypothetical Document Embeddings,假设性文档嵌入)先让大模型针对问题写一段"假设答案", 再拿这段假答案的向量去检索真实文档。它赌的是:答案和文档在同一个语义分布里,问题和文档之间有鸿沟—— 用一段"像文档的话"当桥,比用一句"像问题的话"更容易撞上正确的原文。

1. 为什么需要这座桥

用户问题:"报销为什么被打回来了"        ──▶ [问题向量空间]
                                             ▲
                                    距离远,向量不在一个区域
                                             ▼
文档原文:"报销单被退回的常见原因:发票抬头错误、超过 30 天提交期限、缺少审批人签字……"
                                        ──▶ [文档向量空间]

HyDE 生成的假答案:"报销被退回通常因为发票抬头与税号不符、超期提交、或缺少审批签字。"
                                        ──▶ 落在文档空间里,紧邻原文 ✔

问题短、口语、缺术语;文档长、书面、术语密集。两者的向量在训练中从未被要求对齐到同一位置,这就是"语义鸿沟"。

2. 五步流程

步骤动作说明
1提示模型写假设答案只要求"像文档一样陈述",不要求事实正确
2向量化假设答案与索引时使用同一个 embedding 模型
3用它检索 top-k也可与原 query 的向量加权平均
4融合多份假设生成 2~3 份不同假设,RRF 合并
5交给重排与生成生成阶段仍只用真实文档

注意第 1 步的"不要求正确":假设答案里的数字、条款可能全是编的,但它只要语义方向对,就足以把检索带到正确区域。 真正的答案仍来自检索回来的原文。

3. 代码实现

HYDE_PROMPT = """针对下面的问题,写一段 80 字以内的说明性文字,风格像内部文档,直接陈述结论。
不要提问,不要开场白,不确定的地方用概括表述。

问题:{q}"""

def hyde_vectors(q: str, n: int = 2) -> list[list[float]]:
    docs = []
    for _ in range(n):
        resp = llm.chat.completions.create(
            model="gpt-4o-mini", temperature=0.7,   # 需要多样性,温度不能太低
            messages=[{"role": "user", "content": HYDE_PROMPT.format(q=q)}],
        )
        docs.append(resp.choices[0].message.content.strip())
    return embed(docs)

def search_with_hyde(q: str, k: int = 20, alpha: float = 0.5) -> list[str]:
    """alpha 控制原 query 与假设答案的权重:0=纯 HyDE,1=纯原查询。"""
    q_vec = embed([q])[0]
    h_vecs = hyde_vectors(q, n=2)
    rankings = []
    for hv in h_vecs:
        mixed = [alpha * a + (1 - alpha) * b for a, b in zip(q_vec, hv)]
        hits = store.query(query_embeddings=[mixed], n_results=k)
        rankings.append(hits["ids"][0])
    return rrf(rankings)[:10]

alpha 是个便宜的调节旋钮:领域词陌生时调低(更信假设答案),问题本身已经很具体时调高(更信原查询)。

4. 什么时候有效,什么时候帮倒忙

场景效果原因
问题很短、口语化明显提升假设答案补足了领域术语
FAQ、制度问答提升答案形态与文档形态高度一致
问题已含精确术语提升有限原查询本来就够近,白付一次生成
查错误码、型号、人名可能变差假答案会编出不存在的编号,把检索带偏
强时效信息有风险假设答案可能写入过时常识
多语言跨语种检索有用假设答案可指定输出语言,对齐目标语料

所以 HyDE 常和混合检索一起用:字面精确那一路兜住"编号类"查询,HyDE 那一路负责语义扩展。

5. 成本与替代方案

方案额外延迟额外成本效果
原始查询00基线
查询改写(同义扩展)~0.2 s几百 token中
HyDE0.51 s一次生成(100 token 级)中高
微调 embedding 模型0(离线)标注 + 训练高,但周期长

延迟敏感的场景可以把 HyDE 结果按 query 哈希缓存,同一问题重复问时直接复用。

6. 常见追问

Q:假设答案是错的,检索为什么还能对? 因为检索只比较"语义邻域",不校验事实。写错日期的假答案,仍然落在"报销制度"这个区域里。

Q:和查询改写有什么区别? 改写保持"问题"的形态(还是问句),HyDE 生成的是"答案/文档"的形态。前者缩短 query 与 query 的距离,后者缩短 query 与 document 的距离。

Q:需要专门训练吗? 原始论文提出了无监督与带对比学习的版本,但工程实践里 zero-shot 提示就够了,收益主要来自分布对齐而非模型能力。

Q:生成几份假设合适? 1~3 份。再多,边际收益迅速下降,而检索次数线性增加;多份之间用 RRF 合并,别直接平均分数。

小结

HyDE 是一个"用生成换召回"的技巧:花一次便宜的生成,把问题翻译成文档的语言。它不能替代好的分块与重排,但能在语义鸿沟明显时补上关键的一脚。

笔记加载中…