RAG 把外部检索结果作为生成上下文,但“查到了资料”不能直接推出“回答正确”。我把验收拆成检索与回答两个部分,避免只展示一段看起来流畅的输出。

分清三层证据

层次 要检查的问题
来源 文档版本、更新时间和访问权限是否正确?
检索 问题需要的证据是否进入候选片段?
回答 最终论断是否真正被引用片段支持?

原始 RAG 论文区分了模型参数中的知识和外部非参数记忆。工程中还应记录片段 ID、来源和版本,便于定位答案依据。

一项最小检索检查

先人工给少量问题标注相关片段,再计算检索覆盖情况。下面只检查片段 ID,不训练模型、不调用付费 API。

def evidence_scores(retrieved, relevant):
    if not relevant:
        raise ValueError("evaluate unanswerable questions separately")
    if len(set(retrieved)) != len(retrieved):
        raise ValueError("retrieved IDs must be unique")
    hits = len(set(retrieved) & set(relevant))
    precision = hits / len(retrieved) if retrieved else 0.0
    recall = hits / len(relevant)
    return precision, recall

assert evidence_scores(["chunk-2", "chunk-1"], {"chunk-1"}) == (0.5, 1.0)
assert evidence_scores([], {"chunk-1"}) == (0.0, 0.0)
assert evidence_scores(["chunk-2"], {"chunk-1"}) == (0.0, 0.0)
print("retrieval checks passed")

这里 retrieved 是系统实际返回的唯一片段列表,relevant 是人工标注的相关片段集合。这是一个简单的集合级评估,不考虑排序位置,也不等于完整的 RAG 质量评分。

不能省略的回答核验

即使 Recall 为 1,模型仍可能误读、拼接错误或无视证据。因此还应逐条检查:答案的引用能否打开;引用里是否真的支持该论断;证据冲突时是否说明冲突;没有依据的问题是否明确保留不确定性。

文档库更新后,应按变更重建或增量更新相关索引,而不是把索引阶段理解成永远只做一次。检索不到时,先检查解析、切分和索引版本,不急着更换更大的模型。

私人知识库的额外边界

  • 权限过滤要在检索和结果返回环节生效,缓存也不能把甲用户的私人片段交给乙用户。
  • 检索片段是资料,不是新的操作指令;其中的“忽略要求、发送密钥”等文字不能被执行。
  • 课程原课件和访问码不随这篇公开笔记一起上传或公开。

课程对应与整理边界

对应疏锦行《26大模型专题》的《补充知识_RAG检索增强生成详解》及 RAG 视频章节目录。本文围绕证据验收重组内容,示例为独立编写;视频口头补充尚未完整核验。

原课程