跳到主要内容

掩盖检索器 Bug 的 RAG 评估反模式

· 阅读需 12 分钟
Tian Pan
Software Engineer

RAG 系统中存在一种常见的失败模式,数月内都不会被察觉:你的检索器(retriever)返回了错误的文档,但你的生成器(generator)足够擅长即兴发挥,以至于端到端的质量分数依然保持绿色。你不断调整提示词(prompt)。你升级模型。但都无济于事。这个 Bug 存在于上游三层,而你的指标对其视而不见。

这就是检索器评估反模式(retriever eval antipattern)——将整个 RAG 流水线作为一个整体进行评估,这让生成器吸收并隐藏了检索失败。其结果是,你无法区分是“生成器失败”还是“检索器失败”,从而使得系统性的改进几乎变得不可能。

修复方法并不复杂,但它要求你从端到端评估中抽身,并构建一个独立的测试框架,按照检索器自身的逻辑进行测试。这篇文章将解释如何操作。

为什么端到端评估会隐藏检索器 Bug

当你只测量最终回答的质量时——无论是通过 LLM-as-judge、人工评分还是精确匹配——你正在将一个包含四个阶段(索引、检索、重排序、生成)的流水线坍缩为一个单一信号。而这个信号是由你生成器的能力所主导的。

现代 LLM 非常擅长生成看似合理的回答,即使检索到的上下文是错误的、不完整的或完全缺失的。一个 GPT-4 级别的模型在面对无关上下文时,往往会退回到其参数化知识库中,并生成在人类审核员看来是正确的内容,尤其是对于常见的事实性查询。2025 年的研究将此正式定义为“上下文忽略”(context neglect)——即模型根据先验知识而非检索内容进行生成,即使正确的上下文确实存在。

这意味着:如果你的生成器足够强大,且查询内容接近训练数据,那么一个召回率(recall)仅为 40% 的检索器也能驱动一个端到端回答质量达到 80% 的系统。你会得出流水线运行良好的结论。但事实并非如此。边缘情况、冷门查询和特定领域的內容都会悄无声息地失败。

还有一个复合问题。检索器会系统性地偏向具有表面特征的文档:较短的文档、答案出现在开头的文档、与查询有词汇重合的文档,以及重复查询实体的文档。这些特征都与实际相关性没有可靠的相关性。你的端到端评估无法测量这种偏差,因为生成器将其抹平了。

真正重要的指标

一个仅针对检索器的评估框架需要少量精心挑选的指标。正确的选择取决于你的使用场景。

Recall@K 回答了这样一个问题:“我们到底有没有找到相关的文档?”它测量所有相关文档中出现在前 K 个结果中的比例。如果你对一个查询有 10 个相关文档,而你的检索器将其中的 6 个排在前 10 位,那么你的 Recall@10 就是 0.6。这个指标不关注排名顺序——它只计算是否存在。这是一个特性,而非缺陷:如果你下游的生成器使用了所有的 K 个结果,那么检索顺序的重要性就低于覆盖率。

当你的使用场景对完整性敏感时,请使用 Recall@K。例如法律研究、医学文献回顾、合规检查——在这些场景中,遗漏相关文档就是严重的失败。Recall@K 低于 0.5–0.6 是检索器成为瓶颈的强烈信号。

Precision@K 回答了:“我们给生成器发送了多少噪音?”它测量前 K 个结果中实际相关的比例。如果你检索了 10 个文档,而其中只有 4 个是相关的,那么 Precision@10 就是 0.4。高噪音会从两个方面损害生成器:它挤占了相关上下文(特别是下文讨论的“迷失在中间”效应),并且引入了矛盾信息,增加了幻觉风险。Precision@K 低于 0.4 表明你的检索器引入了太多噪音。

MRR (Mean Reciprocal Rank) 是适用于存在单一正确答案系统的指标——如问答助手、聊天机器人、搜索界面。它测量第一个相关结果的排名高低,并在所有查询中取平均值。MRR 低于 0.3 意味着正确答案平均排在第 3 或第 4 位之后——位置太靠后,以至于大多数用户或重排序器(reranker)都找不到它。MRR 简单且易于解释,是第一个值得接入的良好指标。

NDCG@K (Normalized Discounted Cumulative Gain) 是当文档具有分级相关性时——即某些文档高度相关,另一些部分相关,还有一些不相关时——最具信息量的指标。它同时考虑了相关性和排名位置,并对排名较低的结果进行对数衰减。如果你正在构建基于技术文档、科学文献或任何文档质量差异显著的语料库,NDCG 应该是你的首选指标。

对于大多数生产环境中的 RAG 系统,从 Recall@10 和 Precision@10 开始。如果你正在构建问答系统,请加入 MRR。当你收集了分级相关性标签后,再使用 NDCG。

构建评估框架 (Eval Harness)

该框架包含三个组件:测试集、基准事实映射 (ground-truth mapping) 和运行器 (runner)。

测试集是代表你的系统实际接收请求的查询集合。其关键属性是代表性——即查询应与生产流量具有相同的分布。这比听起来要难。通过要求 LLM 根据你的文档生成问题而创建的合成查询虽然容易收集,但往往严重偏向于简单的简单事实查找。研究表明,约 95% 的简单 LLM 生成查询属于单事实类别,这会产生不切实际的高性能预期,从而掩盖了在复杂查询上的失败。

构建测试集的更好方法包括:

  • 如果你有生产日志,从中抽取真实的候选查询。即使是来自生产流量的 100 个带有标签的示例,其价值也高于 10,000 个合成查询。
  • 对于你已知存在的常见问题类型,进行手动编写。50 个精心设计的查询成本极低且具有高度代表性。
  • 为了扩充少量的种子集,可以使用 LLM 在不同的抽象层级上生成变体:直接的事实问题、需要结合多个文档信息的多跳 (multi-hop) 问题,以及种子查询的改写版本。

基准事实映射标注了哪些文档与每个查询相关。这是最昂贵的步骤。对于每个查询,你需要知道应该检索哪些文档 ID,理想情况下还包括相关性分级。建议增量式地构建你的基准事实:从具有二元相关性标签(相关/不相关)的 50–100 个查询开始。一旦你发现二元标签不足以满足需求,再逐渐扩展,增加分级标签(高度相关 / 部分相关 / 不相关)。

对于从零开始的内部语料库,可以使用 LLM 进行初始的相关性标注,然后由人工审核不确定的情况。当查询-文档对界限清晰时,LLM 作为评审 (LLM-as-judge) 进行相关性标注的准确性足以满足大多数应用,并且标记边缘案例供人工审核的成本很低。

运行器非常直观:对于测试集中的每个查询,运行检索器并收集前 K 个文档 ID。将其与基准事实进行对比并计算你的指标。将此作为 CI 流水线的一部分运行,以便在检索器性能退化进入生产环境之前将其捕获。

诊断瓶颈

一旦有了指标,问题就变成了:检索器真的是你的瓶颈吗?以下是三种诊断模式:

模式 1:低召回率,高生成质量。 你的生成器正在利用参数化知识进行补偿。检索器的缺陷在端到端测试中是不可见的,但在处理分布外 (out-of-distribution) 查询、近期事件或模型未训练过的特定领域内容时就会暴露出来。此时应修复检索器。

模式 2:高召回率,低生成质量。 你检索到了正确的文档,但你的生成器没有使用它们。检查是否存在“迷失在中间 (lost in the middle)”问题(见下文),并验证检索到的文档是否确实正确地传递给了生成器。

模式 3:高精确率,中等召回率。 你的检索器比较保守——当它返回内容时,通常是相关的,但它漏掉了大量的相关内容。这种模式通常表明存在查询-文档不匹配的问题:你的查询表述方式与文档不同。混合搜索(结合稠密和稀疏检索)通常可以解决这个问题。

“迷失在中间”诊断: 斯坦福大学和华盛顿大学的研究表明,当相关信息位于长上下文的中间,而不是开头或结尾时,LLM 的性能会急剧下降——在某些设置下超过 30%。如果你怀疑存在这种模式,请进行对照实验:针对相同的查询和基准事实,分别检索 3 个文档和 15 个文档进行对比。如果性能随上下文增多而下降,那么你面临的不是检索数量问题,而是位置偏差 (position bias) 问题。解决方法是对检索到的文档进行重排序,将得分最高的结果放在上下文的开头和结尾,而不是按得分降序排列。

私有语料库的合成数据

一个常见的反对意见是:“我们没有针对内部知识库的带标签查询。”这是正常情况,而非例外。大多数企业级 RAG 系统都是基于专有内容构建的,没有预先存在的查询-文档对。

为了大规模生成合成测试数据,一个可靠的模式是:

  1. 在整个语料库中抽取多样化的文档块 (chunks) 样本,按文档类型和长度进行分层,以避免过度代表任何子集。
  2. 对于每个块,提示 LLM 在不同的复杂度级别上生成 3–5 个查询:关于该块中某个事实的直接问题、需要推理的更抽象的问题,以及只有将该块与另一个块结合才能回答的问题。
  3. 使用 LLM 对整个语料库(不仅是源文档块)中的查询-文档对进行相关性标注,以识别硬负样本 (hard negatives)——即看起来相关但实际不相关的文档。

硬负样本是这个过程中最重要的部分。没有硬负样本的测试集会显示出虚高的精确率,因为检索器只需要避免明显的错误匹配。添加与查询共享词汇和主题但并未实际回答问题的文档,你的指标才能开始反映现实世界的难度。

对于无法将文档内容发送给外部 LLM 的敏感数据,存在差异隐私 (differentially private) 查询生成框架——其工作原理是对文档进行聚类,从聚类中提取统计模式,并根据这些模式生成合成查询,而不会暴露单个文档的内容。

选择你的 K 值

Recall@K 和 Precision@K 中的 K 应与你的流水线实际处理检索文档的方式相匹配。

如果你向用户展示前 5 个结果,请在 K=5 处进行评估。如果你将前 10 个结果传给重排器(reranker),并由其选出 3 个作为最终上下文,那么请评估 Recall@10(你是否检索到了相关文档?)和重排后的 Precision@3(重排器是否将它们置顶了?)。选择 K=100 来刷高你的召回率数值是一个常见的错误,这会导致产生的指标与生产环境的行为无关。

Snowflake 的工程团队在研究金融领域 RAG 时发现,检索和分块(chunking)策略对答案质量的影响比生成模型(generator model)的选择更大——即使是支持 10 万+ token 的长上下文模型也是如此。这一发现具有普遍性:在特定领域的部署中,检索器往往比生成器更容易成为主要瓶颈。

形成闭环

构建一个检索器评估框架需要几天时间。在对流水线进行任何重大更改之前,针对生产环境的检索器运行它只需几分钟。这种投入产出的不对称性使得这项投资显而易见,但它需要你接受一个事实:端到端指标无法捕捉到检索器的性能倒退(regressions)。

从最小化起步:50 个带标签的查询、二元相关性标签、Recall@10 和 MRR。在 CI 中运行该评估框架。根据你观察到的实际故障模式来扩大覆盖范围。一旦你拥有了可信的检索指标,关于分块策略、嵌入模型(embedding model)选择、重排(reranking)和混合搜索(hybrid search)的决策,就会从猜测变成具有可衡量结果的实验。

目标并非实现完美的检索,而是区分“这是检索器的问题”还是“这是生成器的问题”。一旦你能可靠地回答这个问题,解决这两项问题的方法就会变得清晰。

References:Let's stay in touch and Follow me for more thoughts and updates