跳转至

第 18 章 检索增强生成:从召回到可信回答

开场问题:向量库已经召回了包含答案的文档,模型为什么仍然答错?

18.1 RAG 是一条链,不是一个向量数据库

完整链路:

查询理解 → query 改写/分解 → 召回 → 过滤 → 重排 → 上下文组装 → 生成 → 引用校验

最终错误要沿链路归因。文档没被召回是 retrieval failure;召回了但没进入上下文是 ranking/budget failure;上下文有证据却答错才是 generation failure。只看最终正确率无法决定该修哪一段。

18.2 文档切分决定检索单元

块太小,语义和上下文丢失;块太大,embedding 被多个主题稀释、召回后又浪费上下文。固定 512 token 是起点,不是答案。

更好的做法是尊重文档结构:标题、段落、表格、函数、类、页面。保存 parent-child 关系:小块用于精确检索,命中后带回更大的父块供模型理解。

每块必须带元数据:来源、时间、权限、文档版本、章节路径。没有元数据,后续无法过滤过期内容、做权限控制或生成可靠引用。

18.3 Dense、Sparse 与 Hybrid

Dense embedding 擅长语义相似和改写;BM25 等 sparse 检索擅长精确术语、编号、错误码和罕见实体。企业知识里两者都不可缺。

Hybrid retrieval 分别取候选,再用 RRF 或归一化分数组合。不能直接把两个系统的原始分数相加,因为尺度不同。

18.4 Query 改写与分解

多轮对话里的“它上一版有什么问题”不能直接拿去检索,要结合历史改写成独立查询。复杂问题还需分成多个子查询,分别召回后合并。

但改写会改变用户意图。应保留原查询并做多路召回,而不是完全替换;专有名词、数字、否定条件要明确要求保留。

18.5 Reranker 在做什么

embedding 检索通常从百万文档快速取 top-100,精度有限;cross-encoder reranker 同时阅读 query 和每个候选,重新排出 top-5/10。它慢但候选少,能显著提升精度。

召回关心 recall@k:正确证据有没有进候选;重排关心 nDCG/MRR:正确证据是否靠前。两阶段指标不能混成一个。

18.6 上下文组装

不能把 top-k 原样拼起来。要做:

  • 去掉重复和近重复块。
  • 保留来源和层级标题。
  • 同一文档的相邻块适度合并。
  • 多来源证据控制多样性,避免前 k 全来自一篇文档。
  • 按 token 预算选择,而不是固定块数。
  • 明确告诉模型只能基于证据作答,不足时说不知道。

还要处理证据冲突和时效性。通常优先权威、更新的来源,并把冲突暴露给模型,而不是静默选一个。

18.7 引用必须可验证

要求模型输出 [1] 不代表引用可靠。生成后要检查:

  1. 引用 ID 是否存在。
  2. 引用段落是否支持对应主张。
  3. 关键主张是否都有引用。
  4. 是否把来源里没有的信息“搭便车”挂在引用后。

可用 NLI/judge 做规模化初筛,高风险场景仍需规则或人工。

18.8 RAG 的端到端评测

至少分三层:

  • Retrieval:recall@k、MRR/nDCG、权限过滤正确率。
  • Context:证据覆盖率、冗余率、token 成本。
  • Answer:正确性、faithfulness、引用精度/召回、拒答准确率。

还要构造“知识库里没有答案”的问题。若只测有答案样本,系统会学会无论如何都编一个。

18.9 RAG 与微调怎么配合

RAG 提供事实,SFT 教模型怎样使用事实:引用、处理冲突、证据不足时拒答。可以用 RAG 轨迹做 SFT,但训练时应随机加入检索噪声和空结果,否则模型只会在完美检索条件下工作。

不要用微调掩盖低召回,也不要指望 RAG 自动修复模型不具备的复杂推理能力。

核心问答

Q:文档召回了,为什么仍答错? 可能排名靠后没进入预算、被重复块挤掉、上下文组装丢了标题,或模型没有正确利用证据。要分层记录。

Q:Dense 能完全替代 BM25 吗? 不能。错误码、编号、罕见名字和精确术语通常 sparse 更强,混合检索更稳。

Q:RAG 最容易被忽略的安全问题? 权限过滤必须发生在召回层,不能把无权查看的文档先送给模型再要求它别说。

Q:引用存在就说明 grounded 吗? 不说明。必须检查引用内容是否真的支持该主张。