Contextual Retrieval:给每个文本块补回它丢掉的上下文

要让 AI 模型在特定场景里有用,它常常需要背景知识:客服机器人得懂它服务的那家公司,法律分析机器人得知道海量过往判例。

开发者通常用 RAG(Retrieval-Augmented Generation,检索增强生成) 来给模型补知识:从知识库里检索相关信息、拼到用户的 prompt 上,显著增强回答。问题是——传统 RAG 在编码信息时会把上下文剥掉,结果系统经常检索不到那条本该相关的信息。

这篇介绍一种大幅改进 RAG 检索环节的方法,叫 Contextual Retrieval(上下文检索),由两个子技术组成:Contextual EmbeddingsContextual BM25。它能把检索失败数降低 49%,再叠加 reranking(重排)能降到 67%


先说一句:有时直接用长 prompt 就够了

最简单的方案往往最好。如果你的知识库小于 20 万 token(约 500 页),直接把整个知识库塞进 prompt 就行,根本不用 RAG。

我们前阵子发布的 prompt caching(提示缓存) 让这条路又快又省:常用 prompt 可以在多次 API 调用间缓存,延迟降一半以上、成本最多省 90%

不过知识库一旦变大,你就需要更可扩展的方案——这才是 Contextual Retrieval 登场的地方。


RAG 速成:扩展到更大的知识库

对装不进 context window 的大知识库,RAG 的常规预处理是:

  1. 把知识库(文档「语料」)切成小块(chunk),通常不超过几百 token;
  2. 用 embedding 模型把每块转成向量嵌入(编码语义);
  3. 把这些嵌入存进向量数据库,支持按语义相似度检索。

运行时,用户的查询拿去向量库里找语义最相近的块,再把最相关的块拼进 prompt。

embedding 擅长抓语义关系,但会漏掉关键的精确匹配。这时一个更老的技术能帮忙:BM25——一个用词法匹配找精确词/短语的排序函数,对含唯一标识符或技术术语的查询特别有效。

例子:用户查「Error code TS-999」。embedding 模型可能找到一堆讲「错误码」的泛泛内容,却漏掉「TS-999」这个精确串;BM25 会盯着这个具体字符串去找对的文档。

把两者结合(用 rank fusion 合并去重),传统 RAG 能在「精确词匹配」和「广义语义理解」之间取得平衡。但它有个大软肋:经常把上下文给毁了


传统 RAG 的「上下文困境」

文档被切成小块利于检索,但单个块缺乏足够上下文时就出问题。

设想你的知识库里嵌了一堆财报(比如美国 SEC 文件),有人问:「ACME 公司 2023 年 Q2 的营收增长是多少?

一个相关块可能写着:

「公司营收较上一季度增长了 3%。」

但这块自己根本说不清指的是哪家公司、哪个时段——既难被检索到,也难被有效利用。


Contextual Retrieval 怎么解

办法是:在嵌入前、建 BM25 索引前,给每个块前面拼上一段针对该块的解释性上下文。回到 SEC 的例子:

内容
原始块公司营收较上一季度增长了 3%。
加上下文后这块出自 ACME 公司 2023 年 Q2 业绩的 SEC 文件;上一季度营收为 3.14 亿美元。公司营收较上一季度增长了 3%。

注:过去也有人提过别的上下文增强法(给块加通用文档摘要、假设性文档嵌入、基于摘要的索引等),我们都试过,收益很有限。本文这套和它们不同。


怎么实现

手动给成千上万、甚至上百万个块写注解显然不现实——所以我们交给 Claude。我们写了一段 prompt,让模型用「整篇文档的语境」为每个块生成简洁的、针对该块的上下文。用的是 Claude 3 Haiku,大意是:把整篇文档和待定位的那个块都给它,让它只输出一段简短的、用于改善检索的定位说明,别的不要。

生成的上下文通常 50~100 token,拼到块前面,再去嵌入、建 BM25 索引。

⭐ 用 Prompt Caching 把成本压下来

Contextual Retrieval 之所以能在 Claude 上低成本做到,靠的正是前面说的 prompt caching:你不用为每个块都重新传一遍参考文档,只需把文档载入缓存一次,后续引用缓存内容即可。按 800 token/块、8k token/文档、50 token 指令、100 token/块上下文算,生成上下文化块的一次性成本约为每百万文档 token 1.02 美元


效果

我们在多个领域(代码库、小说、ArXiv 论文、科学论文)、多种 embedding 模型和检索策略上做了实验,用 1 − recall@20 作指标(衡量「相关文档没能进前 20 块」的比例):

配置top-20 检索失败率相对降幅
基线5.7%
Contextual Embeddings3.7%−35%
Contextual Embeddings + Contextual BM252.9%−49%
再叠加 Reranking1.9%−67%

实现时要注意几点


再加一步:Reranking(重排)

大知识库的初次检索常常返回几百个相关度参差的块。Reranking 是常用的过滤技术:只把最相关的块交给模型,既提升回答质量,又因模型处理的信息变少而降本降延迟。步骤是:

  1. 先初检索拿到一批候选块(我们取前 150);
  2. 把这批块连同用户查询喂给 reranking 模型;
  3. reranker 给每块按相关性打分,选出 top-K(我们取前 20);
  4. 把 top-K 拼进 prompt 生成最终结果。

我们用 Cohere 的 reranker 测试,跨多个领域,Reranked Contextual Embedding + Contextual BM25 把 top-20 失败率降低了 67%(5.7% → 1.9%)

⚠️ 注意 reranking 会在运行时多加一步、带来少量延迟。「重排更多块换更好效果」vs「重排更少换更低延迟成本」之间有内在权衡,建议在你的场景上调出平衡点。


总结

我们跑了大量测试,综合结论:

我们鼓励所有和知识库打交道的开发者,用我们的 cookbook 去试这些方法,解锁新的性能水平。

本译文采用 CC BY-NC-SA 4.0 协议发布,仅供学习交流。

原作品版权归 Anthropic(Anthropic Engineering)所有,原文请见 这里