Skip to content

引入 Contextual Retrieval

发布日期: 2024年9月19日 来源: Anthropic 工程博客


为了让 AI 模型在特定场景中有用,它通常需要访问背景知识。例如,客户支持聊天机器人需要了解它所服务的特定业务,法律分析机器人需要了解大量过去的案例。

开发者通常使用检索增强生成(RAG)来增强 AI 模型的知识。RAG 是一种从知识库中检索相关信息并将其附加到用户提示词中的方法,显著增强了模型的响应能力。问题是传统的 RAG 解决方案在编码信息时移除了上下文,这通常导致系统无法从知识库中检索到相关信息。

文章概述了一种显著改进 RAG 中检索步骤的方法,称为 "Contextual Retrieval",它使用两种子技术:Contextual Embeddings 和 Contextual BM25。这种方法可以将检索失败次数减少 49%,结合重排序后减少 67%。这些代表了检索准确性的显著提升,直接转化为下游任务的更好性能。

提供了一个 Cookbook,用于使用 Claude 部署你自己的 Contextual Retrieval 解决方案。

关于直接使用更长提示词的说明

有时最简单的解决方案就是最好的。如果你的知识库小于 200,000 个 Token(约 500 页材料),你可以直接在给模型的提示词中包含整个知识库,无需 RAG 或类似方法。

Anthropic 为 Claude 发布了 Prompt Caching,使这种方法显著更快且更具成本效益。开发者可以在 API 调用之间缓存常用的提示词,将延迟降低 2 倍以上,成本降低高达 90%。

然而,随着知识库的增长,你需要更具可扩展性的解决方案。这就是 Contextual Retrieval 的用武之地。

RAG 入门:扩展到更大的知识库

对于无法放入 Context Window 的更大知识库,RAG 是典型的解决方案。RAG 通过以下步骤预处理知识库:

  1. 将知识库(文档的"语料库")分解为更小的文本块,通常不超过几百个 Token;
  2. 使用嵌入模型将这些块转换为编码含义的向量嵌入;
  3. 将这些嵌入存储在向量数据库中,允许按语义相似性搜索。

在运行时,当用户向模型输入查询时,向量数据库用于根据与查询的语义相似性找到最相关的块。然后,最相关的块被添加到发送给生成模型的提示词中。

虽然嵌入模型擅长捕获语义关系,但它们可能会错过关键的精确匹配。BM25(Best Matching 25)是一个使用词法匹配来查找精确单词或短语匹配的排序函数。它对于包含唯一标识符或技术术语的查询特别有效。

BM25 通过建立在 TF-IDF(词频-逆文档频率)概念之上来工作。TF-IDF 衡量一个单词对文档集合中文档的重要性。BM25 通过考虑文档长度并对词频应用饱和函数来改进这一点,这有助于防止常见词汇主导结果。

以下是 BM25 在语义嵌入失败时如何成功的例子:假设用户在技术支持数据库中查询"错误代码 TS-999"。嵌入模型可能会找到关于错误代码的一般内容,但可能错过精确的 "TS-999" 匹配。BM25 会查找这个特定的文本来识别相关文档。

RAG 解决方案可以通过以下步骤结合嵌入和 BM25 技术来更准确地检索最适用的块:

  1. 将知识库分解为更小的文本块,通常不超过几百个 Token;
  2. 为这些块创建 TF-IDF 编码和语义嵌入;
  3. 使用 BM25 根据精确匹配找到顶级块;
  4. 使用嵌入根据语义相似性找到顶级块;
  5. 使用排名融合技术合并和去重 (3) 和 (4) 的结果;
  6. 将前 K 个块添加到提示词中生成响应。

通过利用 BM25 和嵌入模型,传统 RAG 系统可以提供更全面和准确的结果,平衡精确的术语匹配与更广泛的语义理解。

这种方法允许你经济高效地扩展到庞大的知识库,远超单个提示词所能容纳的范围。但这些传统 RAG 系统有一个重大限制:它们通常会破坏上下文。

传统 RAG 中的上下文困境

在传统 RAG 中,文档通常被拆分为更小的块以实现高效检索。虽然这种方法对许多应用效果很好,但当单个块缺乏足够的上下文时可能会导致问题。

例如,假设你有一个财务信息集合(比如美国 SEC 文件)嵌入在你的知识库中,你收到以下问题:"ACME 公司 2023 年第二季度的收入增长是多少?"

一个相关的块可能包含文本:"该公司收入比上一季度增长了 3%。" 然而,这个块本身没有指明它指的是哪家公司或相关时间段,使得难以检索正确的信息或有效使用信息。

引入 Contextual Retrieval

Contextual Retrieval 通过在嵌入之前("Contextual Embeddings")和创建 BM25 索引("Contextual BM25")之前,为每个块添加特定于块的解释性上下文来解决这个问题。

回到 SEC 文件集合的例子,以下是块可能如何被转换的:

original_chunk = "The company's revenue grew by 3% over the previous quarter."

contextualized_chunk = "This chunk is from an SEC filing on ACME corp's performance in Q2 2023; the previous quarter's revenue was $314 million. The company's revenue grew by 3% over the previous quarter."

值得注意的是,过去也提出了其他使用上下文来改进检索的方法。其他提案包括向块添加通用文档摘要(作者实验后发现收益非常有限)、假设文档嵌入和基于摘要的索引(他们评估后发现性能较低)。这些方法与文章中提出的方法不同。

实现 Contextual Retrieval

为了实现 Contextual Retrieval,团队使用了 Claude。他们编写了一个提示词,指示模型提供简洁的、特定于块的上下文,使用整体文档的上下文来解释该块。他们使用以下 Claude 3 Haiku 提示词为每个块生成上下文:

<document>
{{WHOLE_DOCUMENT}}
</document>
Here is the chunk we want to situate within the whole document
<chunk>
{{CHUNK_CONTENT}}
</chunk>
Please give a short succinct context to situate this chunk within the overall document for the purposes of improving search retrieval of the chunk. Answer only with the succinct context and nothing else.

生成的上下文文本通常为 50-100 个 Token,在嵌入之前和创建 BM25 索引之前添加到块前面。

使用 Prompt Caching 降低 Contextual Retrieval 的成本

得益于 Prompt Caching,Contextual Retrieval 在 Claude 上可以低成本实现。通过 Prompt Caching,你不需要为每个块传入参考文档。你只需将文档加载到缓存中一次,然后引用之前缓存的内容。假设 800 Token 的块、8k Token 的文档、50 Token 的上下文指令和每个块 100 Token 的上下文,生成上下文化块的一次性成本为每百万文档 Token 1.02 美元。

方法论

团队在各种知识领域(代码库、小说、ArXiv 论文、科学论文)、嵌入模型、检索策略和评估指标上进行了实验。

原文中的图表显示了使用表现最佳的嵌入配置(Gemini Text 004)在所有知识领域上的平均性能,检索前 20 个块。使用的评估指标是 1 减去 recall@20,衡量在前 20 个块中未能检索到的相关文档的百分比。上下文化在评估的每个嵌入-来源组合中都提高了性能。

性能改进

实验表明:

  • Contextual Embeddings 将前 20 块检索失败率降低了 35%(5.7% -> 3.7%)。
  • 结合 Contextual Embeddings 和 Contextual BM25 将前 20 块检索失败率降低了 49%(5.7% -> 2.9%)。

实现注意事项

在实现 Contextual Retrieval 时,有几个注意事项:

  1. 块边界: 考虑如何将文档拆分为块。块大小、块边界和块重叠的选择会影响检索性能。
  2. 嵌入模型: 虽然 Contextual Retrieval 在所有测试的嵌入模型上都提高了性能,但某些模型可能比其他模型受益更多。Gemini 和 Voyage 嵌入被发现特别有效。
  3. 自定义上下文化提示词: 虽然提供的通用提示词效果很好,但你可能可以通过针对特定领域或用例定制的提示词获得更好的结果(例如,包含可能只在知识库其他文档中定义的关键术语的词汇表)。
  4. 块数量: 在上下文中添加更多块会增加包含相关信息的机会。然而,过多的信息可能会分散模型的注意力,所以存在限制。他们尝试了 5、10 和 20 个块,发现使用 20 个是这些选项中表现最好的,但值得在你的用例上进行实验。

始终运行评估: 通过将上下文化的块传递给响应生成并区分上下文和块本身,可以改进响应生成。

通过重排序进一步提升性能

在最后一步,Contextual Retrieval 可以与另一种技术结合以获得更大的性能提升。在传统 RAG 中,AI 系统搜索其知识库以找到潜在相关的信息块。对于大型知识库,这种初始检索通常返回大量块——有时数百个——具有不同的相关性和重要性。

重排序是一种常用的过滤技术,确保只有最相关的块被传递给模型。重排序提供更好的响应并降低成本和延迟,因为模型处理的信息更少。关键步骤是:

  1. 执行初始检索以获取前 N 个潜在相关的块(团队使用了前 150 个);
  2. 将前 N 个块与用户查询一起通过重排序模型;
  3. 使用重排序模型,根据每个块与提示词的相关性和重要性给出分数,然后选择前 K 个块(他们使用了前 20 个);
  4. 将前 K 个块作为上下文传递给模型以生成最终结果。

性能改进

市场上有几个重排序模型。测试使用了 Cohere 重排序器。Voyage 也提供了重排序器,但他们没有时间测试。实验表明,在各个领域,添加重排序步骤进一步优化了检索。

具体来说,他们发现重排序的 Contextual Embedding 和 Contextual BM25 将前 20 块检索失败率降低了 67%(5.7% -> 1.9%)。

成本和延迟注意事项

重排序的一个重要考虑是对延迟和成本的影响,特别是当重排序大量块时。因为重排序在运行时添加了一个额外步骤,即使重排序器并行对所有块评分,它也不可避免地会增加少量延迟。在重排序更多块以获得更好性能 vs. 重排序更少块以获得更低延迟和成本之间存在固有的权衡。建议在你的特定用例上尝试不同的设置以找到正确的平衡。

结论

团队进行了大量测试,比较了上述所有技术的不同组合(嵌入模型、BM25 的使用、上下文检索的使用、重排序器的使用以及检索的前 K 个结果总数),跨越多种不同的数据集类型。以下是他们发现的总结:

  1. 嵌入+BM25 比单独使用嵌入更好;
  2. Voyage 和 Gemini 在测试中拥有最好的嵌入;
  3. 将前 20 个块传递给模型比只传递前 10 个或前 5 个更有效;
  4. 向块添加上下文显著提高了检索准确性;
  5. 重排序比不重排序更好;
  6. 所有这些好处可以叠加:为了最大化性能提升,你可以将上下文化嵌入(来自 Voyage 或 Gemini)与上下文 BM25 结合,加上重排序步骤,并将 20 个块添加到提示词中。

鼓励使用知识库的开发者使用 Cookbook 来实验这些方法,以解锁新的性能水平。

附录 I

关于跨数据集、嵌入提供商、在嵌入之外使用 BM25、使用上下文检索以及使用重排序进行 Retrievals @ 20 的结果细分,请参阅原文。

附录 II 单独提供 Retrievals @ 10 和 @ 5 的细分以及每个数据集的示例问答。

致谢

研究和写作由 Daniel Ford 完成。感谢 Orowa Sikder、Gautam Mittal 和 Kenneth Lien 的关键反馈,Samuel Flamini 实现 Cookbook,Lauren Polansky 的项目协调,以及 Alex Albert、Susan Payne、Stuart Ritchie 和 Brad Abrams 对博文的塑造。

AI 落地咨询
艾维禾砺数字科技

企业 AI 落地全链路服务

Agent 开发工作流搭建Claude Code 集成
微信咨询
d187l8801b6124
访问官网 ivheli.com