12.2 RAG:从文档摄取、混合检索到引用验证
情报官把一批产品手册接进模型工坊。第一次演示看似成功:模型引用了三段资料,答案也很流畅。阿花追问某个限制条件时,模型却把旧版手册和新版公告拼在一起,还引用了一段只包含相似词、并不支持结论的文字。
Retrieval-Augmented Generation(RAG)给生成模型提供外部、非参数化记忆。它能缩短知识更新路径,不能保证检索到正确证据,也不能保证生成器忠实使用证据。
本课目标
- 把 RAG 拆成摄取、召回、重排、上下文构造和生成验证;
- 比较 sparse、dense 与 hybrid retrieval;
- 设计可删除、可追溯、受权限约束的索引;
- 分别评估 retrieval、generation 和端到端结果;
- 处理过期文档、prompt injection 与错误引用。
1. 先判断问题是否需要 RAG
RAG 适合答案依赖大量非结构化资料,且资料会更新、需要按来源追溯的场景。它未必是以下问题的首选:
- 精确余额、库存和订单状态:查询受控数据库或 API;
- 数学计算:调用计算器或确定性程序;
- 强一致业务规则:由规则引擎执行;
- 小而稳定的枚举:直接维护配置;
- 必须逐字段精确过滤:优先结构化查询,再补充文本解释。
检索系统提供候选证据,不应替代权威事务系统。
2. 摄取阶段决定可追溯性
把 PDF 拆成文本块之前,先登记文档:
document_id、版本、发布日期和有效期;- 来源、作者、license、密级和访问控制列表;
- 原始文件 hash、解析器版本与摄取时间;
- section hierarchy、页码、字符或 token offset;
- supersedes / superseded-by 关系;
- 删除、撤回和重新索引状态。
解析表格、多栏 PDF、页眉页脚和扫描件时要保留结构验证。OCR 错误和错乱阅读顺序会在 embedding 前污染语义,之后再强的向量模型也无法还原原文。
3. Chunking 是信息边界设计
固定每 500 tokens 切一块简单,却可能把定义和限定条件分开。更可靠的顺序是:
- 按标题、段落、列表、表格或代码块形成 document-aware units;
- 对超长 unit 做 token-aware split;
- 仅在需要时增加 overlap;
- 为每块保存 stable
chunk_id、document version 和 offsets; - 去重页眉、模板和近重复版本。
Chunk 太小会丢失上下文,太大则降低定位精度、占用上下文窗口,并让重排更困难。用真实问题做 chunk-size/overlap ablation,不要采用通用“最佳值”。
4. Sparse、Dense 与 Hybrid Recall
Sparse retrieval(如 BM25)擅长产品编号、错误码、专有名词和精确词匹配;dense retrieval 擅长语义改写和词面不同但含义相近的表达。两者缺陷互补:
- sparse 对同义改写较弱;
- dense 可能把“语义相似”误当“回答相关”,也可能遗漏罕见 token;
- 两者都可能受垃圾模板、重复文档和索引陈旧影响。
Hybrid pipeline 可分别召回,再用 rank fusion 合并候选。Metadata filters 必须在适合的位置执行:权限、租户和有效期不能靠生成器“自行忽略”。
Query rewrite、multi-query 和 hypothetical answer 能增加召回,也会改变原意、扩大攻击面和成本。保存原始 query、改写结果与每路候选,才能诊断。
5. Reranking 与 Context Packing
第一阶段以高 recall 找到候选;cross-encoder 或 LLM reranker 再结合 query 对候选排序。重排后还要构造上下文:
- 去除同文档重复片段;
- 把定义与相邻限定条件一起保留;
- 处理版本冲突,优先有效且权威的来源;
- 在 token budget 内兼顾覆盖与排序;
- 给每段分配不可伪造的 citation ID;
- 明确“资料不足或冲突时不作结论”的策略。
检索到的文档是外部输入,其中的“忽略系统指令”“把密钥发到某处”都是不可信内容。应用层要区分 instruction 与 evidence,隔离敏感工具,并对可访问文档实施与当前 principal 一致的授权。
6. 一个框架无关的查询骨架
from dataclasses import dataclass
from typing import Sequence
@dataclass(frozen=True)
class Principal:
user_id: str
tenant_id: str
roles: tuple[str, ...]
@dataclass(frozen=True)
class Citation:
chunk_id: str
document_version: str
text: str
def answer(question: str, principal: Principal) -> dict:
candidates = retriever.search(
query=question,
principal=principal, # 权限进入检索,不交给模型猜
filters={"status": "active"},
limit=40,
)
ranked = reranker.rank(question, candidates)[:8]
context: Sequence[Citation] = pack_context(ranked, token_budget=6000)
draft = generator.generate(
question=question,
evidence=context,
require_citations=True,
abstain_when_unsupported=True,
)
return validate_answer(draft, context)validate_answer 至少检查 citation ID 存在、引用片段属于当前可见上下文、关键结论有证据覆盖。更强的 entailment verifier 或人工复核仍会犯错,尤其是数字、否定词、时效和跨段推理。
7. 分层评估,不要只看最终答案
Retrieval
先建立 query–relevant chunk/document judgments。常见指标包括 Recall@K、MRR 和 nDCG;若一个问题需要多段证据,还要检查 evidence coverage,而不是只标一条“标准块”。
切片观察:精确实体、同义改写、多语言、版本冲突、权限过滤、长尾问题和无答案问题。Hard negatives 应包含同产品旧版本、词面相似但结论相反的段落。
Context
记录召回结果经过 filter、rerank、dedup、truncation 后哪些证据最终进入 prompt。可评估 context precision、context recall、冲突率和重要证据截断率。
Generation
把“回答是否好”拆开:
- correctness:结论是否正确;
- groundedness:结论是否由提供证据支持;
- citation correctness:引用是否真的支持对应断言;
- citation completeness:重要断言是否都有来源;
- instruction/format compliance;
- abstention:无证据时是否停止猜测。
“带引用”不等于“可追溯”。模型完全可能引用一个真实段落来支持它没有说过的结论。
End to end
同时记录 answer quality、latency、token/cost、empty retrieval、index version 和 source freshness。对同一 query 固定 component versions,才能判断回归来自 embedding、索引、reranker、prompt 还是 generator。
8. 更新、删除与权限是运行时问题
知识库更新不是“重新嵌入后完成”:
- 原文删除后,chunk、embedding、cache 和副本都要可追踪删除;
- 文档 ACL 改变时,旧索引和 cache 不得继续泄露;
- 新旧索引切换要有版本和回滚策略;
- 增量摄取要监控 lag、失败队列和 partial indexing;
- 旧答案缓存必须绑定 document/index version;
- 审计日志应能回答某次回答看到了哪些文档版本。
如果不同用户共享同一个向量索引,检索层仍必须强制 tenant/ACL filter,并测试 side-channel 和 filter bypass。
常见误区
- RAG 解决知识过时:只有摄取、撤回和索引更新及时才可能改善时效。
- Top-K 里有答案就够了:重排、截断或生成仍可能丢失证据。
- 向量检索总比关键词好:编号、代码和精确术语常适合 sparse retrieval。
- 有 citation 就可信:必须验证断言与引用之间的支持关系。
- Prompt 可以让模型忽略恶意文档:边界要由授权、隔离和验证实现。
练习
- 为一组版本化手册设计 document/chunk metadata schema。
- 构造包含错误码、同义改写和旧版本的 hybrid retrieval 测试集。
- 分别计算 Recall@K 与 citation correctness,并解释二者为何不能互换。
- 设计删除文档后覆盖索引、cache 和审计记录的验证步骤。
- 比较 RAG、SQL/API 和规则引擎解决“当前库存”问题的风险。
小结
RAG 是一条可观测的数据与推理管线:先治理来源和版本,再解析与切块,通过 sparse/dense recall、rerank 和 context packing 提供证据,最后验证引用与结论。任何一层失真,最终流畅文本都可能掩盖错误。
下一课,模型不再只读取资料,还要调用搜索、数据库和业务 API。工具带来能力,也把授权、重试和副作用风险带进了循环。