循证 RAG:让引用真正支持结论
一段回答可以同时出现 “EGFR”“指南”“临床试验” 和一个真链接,却仍然没有证据证明“这名患者当前携带该变异”。相关性让读者觉得答案靠谱;支持性才决定这句话能不能留在工作单上。
循证 RAG 把引用当作可验证合同:结论的每个关键原子命题必须对应具体来源位置、版本和适用范围。检索到了相关段落,不等于段落支持答案。
朴素 RAG 把 top-k 文本塞给模型,主要优化检索相关性和答案流畅度。医疗场景要求更强:区分患者事实、试验协议和通用知识;防止过期文档;处理来源冲突;避免 PDF 中的间接提示注入。NIST GenAI Profile 与 OWASP 都把内容来源、提示注入和过度依赖视为重要风险。S36S37
最常见的假引用是:来源真实、主题相关、却不支持具体结论。另一个问题是“知识污染”:模型把外部文档中的命令当系统指令。
三类索引、两次验证
Section titled “三类索引、两次验证”- 患者索引:严格按 patient scope,只存最小化 Evidence。
- 协议索引:按 trial/version/criterion,版本冻结。
- 通用知识索引:按发布者、日期、适用人群和许可。
生成前检索过滤来源与时间;生成后把答案拆成原子 claim,执行 citation entailment 检查:引用是否存在、是否同患者/同版本、是否覆盖 claim。模型 judge 只能辅助,确定性字段与数值优先用规则。
外部文档一律是数据:解析器剥离脚本,检索内容用明确 delimiter,工具和系统指令不接受文档内命令。即便过滤后也应用最小权限,使注入无法获得高风险工具。
项目/方法对比
Section titled “项目/方法对比”| 方法 | 优点 | 局限 |
|---|---|---|
| 向量 top-k | 实现快 | 相关不等于支持 |
| Hybrid + reranker | 提升召回/排序 | 仍需 claim 级验证 |
| TrialGPT explanation | criterion 级理由 S30 | 需本地引用/版本合同 |
| Evidence Graph + verifier | 可追溯、可查冲突 | 构建和维护成本高 |
python3 labs/run_lab.py --lab 12实验给出三条 claim 和三条 Evidence,用关键词覆盖与 patient/version 规则做最小引用检查。一个“主题相关但未包含数值”的引用会失败。它不是语义蕴含的完整实现,却能暴露最常见的悬空引用。
先让三条 claim 通过最小检查;再把其中一个引用换成“主题相同但没有目标数值”的 Evidence;最后检查失败信息是否说清缺的是患者范围、版本,还是具体事实。这样才能把引用错误分配给正确的修复层。
三类知识不能混成一个向量库
Section titled “三类知识不能混成一个向量库”患者层有“2026-01-03 的基因报告检测到 EGFR exon 19 deletion”;协议层有“本试验允许 exon 19 deletion 或 L858R”;通用知识层可能解释这些术语。Eligibility 结论需要前两类,第三类只是帮助理解。如果系统检索到一篇讨论 EGFR 的论文,不能用它证明患者携带变异;如果检索到患者报告,也不能用它证明协议允许。
因此每个 chunk 带 knowledge_class、source、version、validity、patient/trial scope 和 locator。查询计划声明允许哪些类。患者查询永不跨 scope;协议查询固定 trial version;通用知识按来源可信度和发布日期过滤。生成器收到按类分隔的 Evidence,不能把来源身份交给模型猜。
首先解析合法来源与许可,保留标题、发布者、发布日期、版本和原始定位。然后做结构感知切分:协议按 criterion,指南按章节/表格,报告按段落与字段;固定 token 长度只是兜底。每个 chunk 计算 content hash,embedding 记录模型版本。更新时建立新 snapshot,旧任务仍使用旧索引。
外部 HTML/PDF 可能含隐藏文本、脚本、恶意指令或 OCR 错误。摄取阶段剥离主动内容并标记低质量;运行时把 chunk 放在 data envelope。OWASP 提示注入风险无法靠“更聪明的系统提示”彻底解决,S37 所以工具最小权限仍是最终边界。
query 不应等于用户整段问题,而由当前 criterion 生成实体、术语、时间和来源过滤。关键词与 dense 召回后,reranker 判断与 criterion 的相关性;随后 diversity/duplicate filter 避免五个结果都是同一段的复制。top-k 过大增加上下文噪声与成本,过小漏证据,需在冻结任务上调优。
检索指标和引用指标分开。Recall@k 评是否找到了需要的来源;citation precision 评最终引用是否真的支持 claim;completeness 评关键 claim 是否都有证据;freshness 评是否使用有效版本。答案事实正确但引用错误仍不通过。
claim 级验证
Section titled “claim 级验证”生成器先输出结构化 claims[]:text、type、evidence_ids、status。Verifier 检查引用存在与 scope,再将 claim 拆成实体-关系-值-时间。数值/枚举用确定性比对;自由文本可以用小型 NLI/LLM judge 辅助,但必须记录 judge 版本和“不确定”。若引用只支持一部分,要求改写 claim 或补证据,不能让模型坚持。
最终自然语言由通过验证的 claims 渲染。这样“回答写得好”与“证据支持”解耦。工作台点击 claim 能打开原文定位,用户不用在长参考列表里寻找。
冲突、撤回和过期
Section titled “冲突、撤回和过期”知识不是静态真相。同一指南不同版本、试验 amendment、患者报告修订都可能冲突。索引元数据必须表达 supersedes/withdrawn;检索默认使用当前有效版本,但历史任务保留旧 snapshot。若两个有效来源真正分歧,系统显示 conflict 和适用范围,不让模型平均成一个不存在的共识。
向量库本身是敏感数据副本。患者 embedding 可能泄露信息,需与公开知识分库、加密、访问控制和删除流程。缓存 key 包含授权范围,禁止一个患者任务命中另一个患者 cache。日志只记录 chunk id/hash 与统计,不复制全文。
章末验证清单:随机抽 20 个最终 claim,人工从界面点击引用:能否定位原位置、版本是否正确、来源类别是否匹配、文本是否完整支持、是否遗漏关键反证。再撤回/更新一份协议,确认新任务不再引用旧版而历史任务仍可解释。最后向公开文档注入恶意指令,验证它无法改变 tool allowlist。
把失败分别归为 retrieval、chunk、ranking、generation、citation verifier 或 source quality,避免看到错误就统一增加 top-k。每次修复只改变一层,再在冻结 snapshot 上比较,才能知道改进来自哪里。未解析失败继续进入问题清单。
- 引用验证器也会误判,关键结论仍需人审。
- 公开网页随时变化;需保存合法的版本元数据或内容哈希。
- 全文入库可能违反许可,registry 应记录 link-only/可缓存边界。
- RAG 不能弥补根本不存在的患者证据,应返回 unknown。
完整的来源类型、用途与边界见教材来源注册表。
资料与延伸阅读
Section titled “资料与延伸阅读”- TrialGPT 论文:将 explanation 拆到 criterion 级,是理解“可解释不等于可验证”的好起点。
- NIST AI 600-1:Generative AI Profile:阅读生成式系统的来源、可靠性与过度依赖风险。
- OWASP Top 10 for LLM Applications:重点看提示注入、敏感信息泄露和过度代理;把外部文档当数据而非指令。
- MCP Security Best Practices:补充为什么引用过滤之后仍要保留工具最小权限。