RAG 调试与评估:别只看最终答案,要看检索证据链
围绕similarity_search、MMR、metadata filter、RetrievalQA 和 source_documents 的示例,讲清 RAG 系统应该怎样定位召回错误、重复内容、引用缺失和答案不稳。
相关工具
RAG 出错时,先别急着改 Prompt
RAG 的坏答案经常看起来像模型问题:答非所问、漏掉细节、引用不准,或者把两份资料揉在一起。很多人第一反应是改 Prompt,让模型“更严谨”“只根据上下文回答”。Prompt 当然要写好,但如果检索阶段已经把错误材料送进来,生成阶段很难完全补救。
相关概念 里的做法更适合作为调试习惯。它不是只打印最终回答,而是反复查看 `similarity_search` 返回的文档、每条文档的 `metadata`,以及 `RetrievalQA` 里的 `source_documents`。也就是说,先看模型拿到了什么证据,再判断答案为什么这样写。
这一步很朴素,却能省掉大量无效调整。答案错了,先问三个问题:有没有检索到正确来源?正确来源排在第几?上下文里有没有混入重复或无关 chunk?这三个问题没有看清楚之前,Prompt 改得再漂亮,也是在黑箱里摸索。

RAG 调试要把用户问题、检索结果、source_documents、Prompt 和最终答案串起来看,先确认证据,再看生成。
source_documents 是最基本的调试窗口
资料 在 `RetrievalQA.from_chain_type` 示例里设置了 `return_source_documents=True`,然后打印 `result["source_documents"][0]`。这个参数很重要。没有它,你只能看到模型答了什么;打开它以后,你能看到答案背后的文档片段。
`source_documents` 至少要看四类信息。第一是内容本身,看看 chunk 是否真的包含答案。第二是 `source`,确认是不是来自正确文件。第三是 `page`,确认引用页是否合理。第四是排序,看看正确材料是在前几条,还是被无关材料压到后面。
很多 RAG 问题不是“完全搜不到”,而是“搜到了但位置不好”。如果正确 chunk 排在第五条,而你的 `k=3`,生成阶段根本见不到它;如果 `k=5` 后答案变好,说明问题更可能在召回数量,而不是模型能力。
similarity_search 先看准不准,再看够不够
文中多次用 `similarity_search(question, k=3)` 和 `similarity_search(question, k=5)` 做检索。这个动作适合做第一轮排查,因为它绕开了复杂链路,直接看向量库按语义相似度召回了什么。
调试时不要只改 k。先固定一个问题,分别看 k=3、k=5 的结果。正确来源是否出现?出现后排第几?多出来的两条是补充信息,还是噪声?如果 k 变大只是召回更多重复片段,答案未必更好;如果 k 变大刚好召回缺失页码,答案才可能变稳。
这也是为什么 RAG 调试需要保存原始检索结果。只保存最终回答,很难知道一次改动到底改善了什么。保存每次命中的 source、page、score 和 chunk,后面才能比较参数变化。
MMR 用来处理重复,不是用来替代过滤
这里介绍了最大边际相关性 MMR,并用蘑菇知识库展示 similarity search 和 MMR 的差异。普通相似度搜索容易把相似片段排在一起,MMR 会同时考虑相关性和文档之间的差异,让结果更分散一些。
这对 RAG 很实用。很多文档被切分后,相邻 chunk 本来就有 overlap,向量又很接近。普通 Top-K 可能召回三条几乎一样的内容,模型读到的 token 变多,信息却没有增加。MMR 能减少这种重复,让上下文里有更多不同角度的证据。
但 MMR 解决的是重复,不解决范围错误。文中 Matplotlib 的例子里,用户问第二讲 Figure,普通检索混入了第一讲。这个时候只靠 MMR 不够,因为第一讲也可能相关。真正要做的是 metadata filter,把 source 限定到第二回讲义。

similarity_search 看最相似结果,MMR 减少重复,metadata filter 先限定来源。三者解决的问题不同。
filter 和 verbose 能看清系统是否理解了问题
文中先展示手动 `filter={"source": ...}`,再展示 `SelfQueryRetriever` 自动从问题里抽取 query 和 filter。示例里设置 `verbose=True` 后,可以看到问题被拆成 `query='Figure'`,同时生成 source 等于第二回讲义的条件。
这类日志对调试很有价值。用户问“第二讲里 Figure 做了什么”,系统到底有没有理解“第二讲”是来源限定?有没有把 Figure 当成真正要检索的关键词?如果 verbose 输出里的 filter 错了,后面向量检索再准也会跑偏。
自动过滤正式使用前,建议准备一批带条件的问题:某一讲、某一页、某个版本、某个部门、某类权限。每个问题都记录期望 filter。这样你能判断 SelfQueryRetriever 是真的在抽条件,还是只是在少数示例里刚好成功。
评估不能只给答案打分
RAG 评估最容易偷懒的地方,是只看最终答案对不对。这样会漏掉很多问题。比如答案碰巧正确,但引用页错了;答案基本正确,但 source_documents 里混入无权限文档;答案能回答当前问题,但换一个 k 就不稳定。
更稳的评估记录应该包含问题、期望来源、期望页码、实际 source_documents、最终答案和人工判断。文中的 source_documents 打印习惯,可以直接扩展成评估表。每次改 chunk_size、k、search_type、filter 或 chain_type,都用同一批问题跑一遍。
评估结果也要分层看。命中正确来源,是检索层指标;引用页码正确,是可追溯指标;答案可用,是生成层指标;噪声比例,是上下文质量指标。这样问题出现时,能知道该改检索、切分、过滤,还是改 Prompt。

把问题、期望来源、Top-K、source_documents、答案状态和人工判断放在一起,方便比较每次参数改动。
回归测试要固定问题集
RAG 系统每次改动都可能影响旧问题。你把 k 从 3 调到 5,某些复杂问题变好了,简单问题可能被噪声干扰;你把 search_type 换成 MMR,重复减少了,但某些需要连续片段的问题可能少了一块关键上下文。
所以要有固定问题集。问题集不用一开始就很大,先准备二三十个真实问题,覆盖精确查找、跨页总结、指定来源、带过滤条件、无答案问题。每个问题都写清期望来源和判断标准。
之后每次改参数都跑同一批问题。不要只看平均分,也要看哪些问题退步了。RAG 优化不是让某一次演示更漂亮,而是让真实问题长期稳定。
把调试信息做进后台,而不是藏在 notebook 里
文中的示例大多在 notebook 中打印结果,这对学习很方便。但产品化以后,调试信息不能只留在本地环境。客服、运营、内容维护人员也需要知道答案来自哪份资料,为什么引用这一页,哪些问题经常命中错误来源。
一个轻量后台就够用:左边是问题和答案,右边展开 source_documents;每条文档显示 source、page、score、chunk 摘要;再加上人工标记,区分正确、部分正确、错误、无法判断。需要排查时,不必翻日志,也不必重新跑脚本。
RAG 做到这一步,维护成本会明显下降。因为团队看到的不是一个神秘回答,而是一条能追踪的证据链。哪里脏,就改哪里。
常见问题
RAG 调试时最先看什么?
先看 source_documents。确认正确文档是否被召回、排在第几、是否有无关或重复 chunk,再决定调 k、MMR、filter 还是 Prompt。
MMR 能解决所有检索问题吗?
不能。MMR 主要减少重复、增加结果多样性;如果问题需要限定来源、版本或权限,还需要 metadata filter。
为什么要做 RAG 回归测试?
因为改 chunk_size、k、search_type、filter 或 chain_type 都可能让旧问题退步。固定问题集能看出一次改动到底改善了哪些问题,又伤到了哪些问题。