在检索增强生成(RAG)管道中,文档检索是至关重要的一步。这一环节的性能直接决定了整个 RAG 系统的表现。如果未能获取最相关的文档,大型语言模型(LLM)将难以准确回答用户的提问。本文将深入探讨获取最相关文档的传统方法、旨在改进检索效果的各项技术,以及通过优化 RAG 管道中的文档检索所能带来的显著益处。
延续之前关于“利用元数据丰富LLM上下文以显著增强能力”的文章,本文的核心目标是:
本文旨在强调如何获取和筛选最适合 AI 搜索的文档。

上图展示了一个传统的 RAG 管道。整个流程始于用户查询,该查询首先通过嵌入模型进行编码。随后,将此查询嵌入与整个文档语料库预先计算的嵌入进行比较。通常,文档会被分割成若干带有重叠的块,尽管某些系统也直接处理完整的文档。在计算出嵌入相似度后,系统会保留前 K 个最相关的文档,其中 K 是一个可由用户自行选择的数字,通常介于 10 到 20 之间。根据语义相似性获取最相关文档的这一步骤,正是本文讨论的重点。获取到最相关的文档后,这些文档会连同用户查询一起被输入到 LLM 中,最终 LLM 会返回一个响应。图片由作者提供。
目录
为何最佳文档检索至关重要?
理解文档获取步骤对任何 RAG 管道的重要性至关重要。为了更好地理解这一点,必须对 RAG 管道的总体流程有一个清晰的认识:
- 用户输入查询。
- 查询被嵌入,并计算查询嵌入与每个文档(或文档块)之间的嵌入相似度。
- 根据嵌入相似度获取最相关的文档。
- 最相关的文档(或文档块)被输入到 LLM 中,并被提示在给定这些文档块的情况下回答用户问题。

上图突出了嵌入相似度的概念。左侧是用户查询“总结租赁协议”,该查询被嵌入到文本下方的向量中。此外,顶部中间显示的是可用的文档语料库,本例中有四个文档,所有文档都已预先计算了嵌入。然后,计算查询嵌入与每个文档之间的相似度,并得出一个相似度分数。在本例中,K=2,因此将两个最相关的文档输入到 LLM 中进行问答。图片由作者提供。
RAG 管道的多个方面都非常重要,例如:
- 使用的嵌入模型。
- 使用的 LLM 模型。
- 获取的文档(或文档块)数量。
然而,可以论证的是,文档的选择可能是最重要的方面。因为如果没有正确的文档,无论 LLM 有多优秀,或者获取了多少文档块,答案都极有可能是错误的。
模型可能在嵌入模型稍差或 LLM 稍旧的情况下依然工作。但是,如果未能获取正确的文档,RAG 管道就会失败。
传统方法
首先,将了解目前常用的一些传统方法,主要是利用嵌入相似度或关键词搜索。
嵌入相似度
利用嵌入相似度来获取最相关文档是当今主流的方法。这是一种稳健的方法,在大多数用例中表现良好。采用嵌入相似度进行文档检索的 RAG 流程与上文描述的完全一致。
关键词搜索
关键词搜索也常用于获取相关文档。TF-IDF 或 BM25 等传统方法至今仍被成功使用。然而,关键词搜索也有其弱点。例如,它仅基于精确匹配来获取文档,当无法进行精确匹配时就会出现问题。
因此,有必要探讨一些可以改进文档检索步骤的其他技术。
获取更多相关文档的技术
本节将讨论一些更高级的文档获取技术。本节将分为两部分:第一部分将介绍如何优化文档检索以提高召回率,即从现有文档语料库中尽可能多地获取相关文档;另一部分则探讨如何优化精确率,即确保所获取的文档对于用户查询确实是正确且相关的。
召回率:获取更多相关文档
将探讨以下技术:
- 上下文检索
- 获取更多文档块
- 重新排序(Reranking)
上下文检索

上图展示了上下文检索的管道。该管道包含与传统 RAG 管道相似的元素,包括用户提示、向量数据库(DB),以及用前 K 个最相关文档块提示 LLM。然而,上下文检索进一步引入了一些新元素。首先是 BM25 索引,所有文档(或文档块)都已为 BM25 搜索建立了索引。每当执行搜索时,可以快速索引查询并根据 BM25 获取最相关文档。然后,保留 BM25 和语义相似度(向量数据库)中前 K 个最相关的文档,并结合这些嵌入。最后,像往常一样,将最相关的文档连同用户查询一起输入到 LLM 中,并接收一个响应。图片由作者提供。
上下文检索是 Anthropic 在 2024 年 9 月提出的一种技术。其文章涵盖两个主题:为文档块添加上下文,以及结合关键词搜索(BM25)和语义搜索来获取相关文档。
为了给文档添加上下文,该方法对每个文档块进行处理,并在给定文档块和整个文档的情况下,提示 LLM 重写该文档块,使其既包含原文档块的信息,也包含整个文档中的相关上下文。
例如,如果一个文档被分成两个文档块,其中一个文档块包含地址、日期、位置和时间等重要元数据,另一个文档块包含租赁协议的信息。LLM 可能会重写第二个文档块,使其既包含租赁协议,又包含第一个文档块中最相关的部分,例如本例中的地址、位置和日期。
Anthropic 的文章还讨论了结合语义搜索和关键词搜索的方法,即同时使用这两种技术获取文档,并采用一种优先级策略来组合从每种技术中检索到的文档。
获取更多文档块
一种更简单的获取更多相关文档的方法是,简单地获取更多的文档块。获取的文档块越多,获取相关文档块的可能性就越高。然而,这有两个主要缺点:
- 很可能会获取更多不相关的文档块(影响精确率)。
- 会增加输入到 LLM 的令牌数量,这可能会对 LLM 的输出质量产生负面影响。
为召回率重新排序
重新排序(Reranking)也是一种强大的技术,可用于在为用户查询获取相关文档时提高精确率和召回率。当基于语义相似度获取文档时,系统会为所有文档块分配一个相似度分数,并通常只保留前 K 个最相似的文档块(K 通常介于 10 到 20 之间,但对于不同应用会有所不同)。这意味着重新排序器应尝试将相关文档放置在前 K 个最相关文档之中,同时将不相关文档排除在该列表之外。Qwen Reranker 是一个表现良好的模型;此外,市面上也有许多其他优秀的重新排序器。
精确率:过滤掉不相关文档
- 重新排序(Reranking)
- LLM 验证
为精确率重新排序
如上一节关于召回率的讨论,重新排序器也可用于提高精确率。重新排序器通过将相关文档添加到最相关文档的前 K 列表中来提高召回率。另一方面,重新排序器通过确保不相关文档不进入前 K 个最相关文档列表,从而提高精确率。
LLM验证
利用 LLM 来判断文档块(或文档)的相关性,也是一种过滤掉不相关文档块的强大技术。可以简单地创建一个如下所示的函数:
def is_relevant_chunk(chunk_text: str, user_query: str) -> bool:
"""
验证文档块文本是否与用户查询相关
"""
prompt = f"""
给定用户查询和文档块文本,判断该文档块文本是否与回答用户查询相关。
返回一个 JSON 响应,格式为 {
"relevant": bool
}
<user_query>{user_query}</user_query>
<chunk_text>{chunk_text}</chunk_text>
"""
return llm_client.generate(prompt)
然后,将每个文档块(或文档)通过此函数进行处理,并仅保留被 LLM 判断为相关的文档块或文档。
这项技术有两个主要缺点:
- LLM 成本高昂。
- LLM 响应时间较长。
大量的 LLM API 调用必然会产生显著的成本。此外,发送如此多的查询会耗费时间,从而增加 RAG 管道的延迟。在实践中,需要权衡响应速度与用户对即时反馈的需求。
改进文档检索的益处
改进 RAG 管道中的文档检索步骤具有诸多益处。例如:
- 提升 LLM 问答性能。
- 减少幻觉。
- 更频繁地正确回答用户查询。
- 从根本上说,它简化了 LLM 的工作。
总而言之,问答模型成功回答用户查询的能力将得到提升。建议将此指标作为评估 RAG 系统的标准,关于 LLM 系统评估的更多信息,可以参考“通过自动化 LLM 评估评估 500 万份文档”一文。
减少幻觉也是一个极其重要的因素。幻觉是 LLM 面临的最严重问题之一。它们具有极大的危害性,因为它们会降低用户对问答系统的信任度,从而使他们不太可能继续使用应用程序。然而,确保 LLM 既能接收到相关文档(精确率高),又能最大程度减少不相关文档的数量(召回率高),对于最大限度地减少 RAG 系统产生的幻觉至关重要。
减少不相关文档(提高精确率)也避免了“上下文膨胀”(上下文噪音过多)甚至“上下文投毒”(文档中提供了不正确信息)的问题。
总结
本文探讨了如何改进 RAG 管道中的文档检索步骤。首先讨论了文档检索步骤是 RAG 管道中最重要的部分,并强调应投入时间对其进行优化。此外,文章还介绍了传统 RAG 管道如何通过语义搜索和关键词搜索获取相关文档。接着,讨论了可以利用的技术来提高检索文档的精确率和召回率,包括上下文检索和 LLM 文档块验证等方法。
