检索与 RAG

Article / 检索与 RAG

让 RAG 的回答可信

介绍 CitedRAG 如何用引用校验、fail-closed 拒答与评测,把回答可信落实为可执行的设计约束。

CitedRAG RAG引用校验幻觉治理评测

检索增强生成(RAG)的默认承诺是:先把资料找出来,再让模型基于资料回答,这样就能减少凭空编造。但检索到了证据,和回答忠于证据,是两件不同的事——模型完全可能把两段证据拼接成一个它们都没有支持的结论,再配上一个看起来很专业的引用。对技术文档问答来说,这种「看起来合理」的答案比一句「不知道」危险得多,因为读者几乎不会去逐条核对。

CitedRAG 是一个本地优先的论文与文档问答系统,它的设计前提只有一句话:不可核验的回答比拒答更有害。围绕这个前提,引用在校验流程中参与控制,而不只是渲染层的展示。

流畅与可信的差别

大语言模型的「幻觉」在日常语境里常被理解成「说错话」,但在 RAG 里更常见的失败形态是三种:没有证据的断言、超出证据的断言,以及引用与结论错位——角标指向了真实存在的证据,但那段证据并不支持这句话。前两种是生成问题,第三种是校验问题,而它们有一个共同点:语言上完全流畅,只有回到原文才能发现。

把 RAG 拆开看,其实有三个环节:检索能不能找到相关证据,生成有没有忠实使用证据,以及读者能不能核验这句话的来源。多数系统的注意力在第一个环节上,调的是召回率与排序;但用户是否信任回答,主要取决于后两个环节。CitedRAG 的取舍是把重点放在「可核验」上:引用是生成之后必须通过的检查,过不了就不返回。

这里的核心机制叫 fail-closed,失败时关闭:只要无法确认回答与证据的对应关系,系统就选择不回答,而不是「尽力而为」地放行。这是一种偏保守的取舍:无法确认时就选择不返回。

提示词也可以要求「必须引用来源」,但它是软约束:随着对话变长、证据变多,模型对指令的遵循会漂移,而系统没有任何信号知道自己错了。把同一件事变成生成后的硬检查,才能在返回用户之前发现问题。

引用校验的规则化

第一道校验处理角标本身。回答里的 [N] 会被解析并与本次检索到的证据数量比对,三类情况直接拒绝:出现畸形角标(如 [abc][1,2])、编号越界(引用了不存在的证据)、以及全文没有任何角标却又不属于标准拒答文案。通过之后还有一步规范化重编号:有效角标按首次出现顺序重排为连续编号,重复引用合并到同一条来源,对外暴露的引用列表里不允许有悬空项与重复项。

第二道校验处理语义:逐句蕴含校验。系统把回答拆成句子,识别每句引用的证据编号,再交给模型做一次严格的检查——证据必须直接支持完整结论,不得引入未声明的事实;提示词要求只输出 JSON 格式的裁决列表。校验结果有三种去向:全部通过则原样返回;部分不通过则删掉那些没有支撑的句子、保留其余部分;校验根本无法完成(服务报错、返回格式不符、句子数与裁决数对不上)则整题拒答。

一个具体细节能说明校验本身的成本。校验使用的证据比生成时更完整:生成侧每条证据只带 800 字符,校验侧给到 2000 字符。原因很实际——800 字符会在列举型证据的中途截断,「论文提出 A、B、C 三种方法」这类完整结论如果只看到一半证据,就会被误判成无依据。换句话说,校验如果比生成更省上下文,它就会误判正确的回答;校验能否发现问题,取决于它是否拿到了足够的信息。这也解释了为什么校验层通常比生成层更「贵」:它必须看到完整的证据,才能做出可靠的判断。这个取舍也说明,上下文预算在「省钱」和「可信」之间需要权衡,在这个系统里后者优先。

第三道校验发生在流式输出上。回答以约 400 字符一批、只在完整句边界切分后送检,只有通过校验的句子才带增量重编号流给前端,因此流式看到的内容与最终落库的内容完全一致;如果某个批次校验失败,流式立即停止,最终整题按拒答落库。句边界切分本身也踩过一个典型 bug:99.8 这样的数字会被正则拆出一个孤立的「8」,看上去像一句新声明;后来用前后数字边界的判断修掉了。在 fail-closed 的体系里,这类文本处理误判会直接造成可用性损失。

带引用与证据的回答

拒答的设计

当引用校验失败、检索证据不足或服务不可用时,系统统一返回固定文案「提供的文档证据不足以回答该问题」,并附带结构化的 warning,说明缺口与下一步建议。

INSUFFICIENT_EVIDENCE_ANSWER = "提供的文档证据不足以回答该问题"

warning = {
    "code": "citation_fallback",
    "message": "引用证据未能支持回答中的全部结论,已拒绝返回该回答",
    "node": node,
    "recoverable": True,
    "question": question,
    "retrieved_count": retrieved_count,
    "gap": "引用的证据与所述结论之间未通过蕴含校验",
    "next_step": "补充更精确的检索证据后重试",
}

这段结构让拒答带有可展示的原因:前端可以显示为什么拒答、缺的是什么、用户可以做什么。warning 里还带着节点名、是否可恢复、检索到的证据条数与缺口描述,前端可以据此渲染不同的引导;其中「可恢复」为真时,用户补充材料后重试的成本最低。拒答识别本身也做了细分——模型在拒答文案后补充解释是允许的,但只有出现在开头的拒答才算拒答,结尾顺带一句「证据可能不足」的正常回答不会被判为拒答。

两处取舍能说明这套设计的取向。其一,重排阶段如果所有候选都低于置信阈值,系统会保留第一候选而不是清空证据——把「给不给证据」和「能不能回答」分开:检索层负责尽量提供候选,拒答的决定权留给引用校验链。其二,当直答路径拒答时,编排层会暂存这个结果并升级到多步检索再试一次,仍不足才把拒答交给用户。拒答只在其他合理手段都无效时才出现。

可信度的评测口径

可信度需要明确的评测口径。CitedRAG 的检索评测集 gold50 由 50 道题组成,其中 45 道可答、5 道是文档外问题(期望拒答)。每道题以 JSON 记录期望来源、期望页码、期望关键词(或关键词的任一变体)以及一条答案备忘;判分时把期望来源经别名表解析成文档 ID,避免文件名漂移,然后计算三项指标:来源命中、内容命中与页码命中,三者同时成立才算一道可答题通过。50 题里有 45 题带页码约束、44 题按关键词判分、12 题是中文提问——这些比例决定了这组评测擅长衡量什么,也暴露了它没有覆盖什么,比如图注与公式类证据就没有单独标注。

{
  "question": "What inference throughput advantage does Mamba have over Transformers of similar size?",
  "expected_source": "Mamba",
  "expected_pages": [1, 2, 15],
  "expected_keywords": ["5 × higher throughput"]
}

默认组合(dense 与 BM25 混合检索、重排、父块扩展)在 top_k=8 下的结果是可答题 41/45、来源命中 100%、页码命中 93%。这里需要说清口径:41/45 的分母是可答题,拒答题不计入;dense 模式另有一组 44/50,含 5 道拒答题全部判为合理拒答,两者分母不同,直接比较没有意义。把分母与判分规则写进报告,本身就是评测可信度的一部分——数字口径含糊,指标就失去意义。

更重要的是这些数字参与了设计。chunk 大小从 1200 降到 800 是为了学术文档的检索精度;top_k 从 5 提到 8 让可答题通过数从 40 升到 41;MMR 的 λ 从 0.75 调到 0.88 让结果从 38 升到 40;而 query rewrite 被默认关闭,因为实测它是「高方差、无净增益,每题还多花 1.4–2.1 秒」。这些决定都来自评测结果。仓库也坦承边界:评测报告是本地输出,仓库内不随附报告文件,上述数字来自 README 的记录,复跑脚本随仓库提供——让报告入库或由 CI 定期产出,是路线图上明确的改进项。

编排层另有一套 15 任务评测,区分两个口径:grounded 看「回答非拒答、角标结构合法、期望来源命中」,拒答题则以语义拒答为 grounded;success 在 grounded 之上再加关键词覆盖。默认路径 15/15 grounded、14/15 success。之所以用 grounded 做路径质量信号,是因为关键词覆盖对语言与转述太敏感——评测口径的选择取决于系统希望优化什么。

幻觉治理的常见手段与拒答边界

治理幻觉的常见手段大致有五种:引用与归因、guardrails(在输入输出两侧加可编程的校验层)、自我一致性(多次采样取一致结论)、人在回路(高风险结论人工复核),以及 groundedness 评测(用自动指标衡量回答是否被证据支持)。这些手段的强度依次递增,成本也依次递增。它们并不互斥:护栏负责拦下明显违规,归因让答案可查证,自我一致性用于高风险推理,人在回路兜住最后的业务责任。

其中最容易被低估的是引用的两种处理方式。展示型引用只是把角标渲染给用户看,不参与任何控制流,误引时系统自己一无所知;校验型引用则进入判定,错了会导致重试或拒答。区别在于引用是否被系统消费,与 UI 无关。CitedRAG 属于后者,这也是它能「主动不回答」的原因。guardrails 的价值则在于把这类策略从代码里抽出来变成可配置、可审计的层,适合有合规要求的团队。

是否宁愿拒答,判断标准是错误发生的代价,与行业无关。医疗、法律、金融合规、企业知识库、技术文档这类场景里,一句看起来合理的错误结论可能直接导致错误决策,而且没人会去核对,拒答是可接受的失败方式;创意写作、头脑风暴、探索性研究里,拒答反而是最大的伤害。可信度与可用性是一条连续谱,产品需要显式选择位置,然后把拒答做得可解释——告诉用户缺的是哪个证据、下一步补什么,比一句「无法回答」有用得多。

最后要承认,可信由整个系统决定:检索决定了有没有可用的证据,生成决定了是否忠实,校验决定了能不能放行,评测决定了前三个环节有没有退步。任何一环缺失,引用都无法发挥作用。