介绍 Contextual Retrieval:通过上下文化检索改进 RAG

举报
yd_298479158 发表于 2026/09/27 07:40:29 2026/09/27
【摘要】 为了让 AI 模型在特定场景中真正有用,它通常需要访问相关的背景知识。例如:客服聊天机器人需要了解它所服务的具体企业;法律分析机器人需要了解大量历史案例。开发者通常会使用 检索增强生成(Retrieval-Augmented Generation,RAG) 来扩展 AI 模型的知识。RAG 会先从知识库中检索相关信息,再把这些信息附加到用户 Prompt 中,从而显著提升模型回答的质量。但传...

为了让 AI 模型在特定场景中真正有用,它通常需要访问相关的背景知识。

例如:

  • 客服聊天机器人需要了解它所服务的具体企业;
  • 法律分析机器人需要了解大量历史案例。

开发者通常会使用 检索增强生成(Retrieval-Augmented Generation,RAG) 来扩展 AI 模型的知识。

RAG 会先从知识库中检索相关信息,再把这些信息附加到用户 Prompt 中,从而显著提升模型回答的质量。

但传统 RAG 存在一个问题:

在对知识进行编码时,它往往会丢失上下文。

这会导致系统无法从知识库中检索出真正相关的信息。

本文介绍一种能够显著改善 RAG 检索阶段的方法,我们称之为:

Contextual Retrieval,上下文化检索。

它由两项技术组成:

  • Contextual Embeddings,上下文化 Embedding
  • Contextual BM25,上下文化 BM25

实验显示,这种方法可以将检索失败数量降低:

49%

如果再结合 Reranking,则可以降低:

67%

这是检索准确率上的显著提升,并且会直接转化为下游任务性能的改善。

一个简单提醒:有时候直接使用更长的 Prompt 就够了

有时候,最简单的方案就是最好的方案。

如果你的知识库小于:

200,000 Tokens

大约相当于:

500 页材料

那么你完全可以直接把整个知识库放入发送给模型的 Prompt 中,而不需要使用 RAG 或类似方法。

Prompt Caching 还能进一步让这种方式变得更快、更便宜。

开发者可以在不同 API 调用之间缓存经常重复使用的 Prompt,从而:

  • 将延迟降低到原来的不到一半;
  • 将成本最多降低 90%。

不过,当知识库继续扩大时,就需要一种更可扩展的解决方案。

这正是 Contextual Retrieval 要解决的问题。

RAG 基础:如何扩展到更大的知识库

对于那些已经大到无法放入单个上下文窗口的知识库,RAG 通常是标准解决方案。

RAG 会先对知识库进行预处理。

典型流程如下:

  1. 把知识库,也就是文档语料库,拆分成较小的文本块,通常每块不超过几百个 Token;
  2. 使用 Embedding 模型,把这些文本块转换成能够编码语义的向量;
  3. 把这些 Embedding 存入支持语义相似度搜索的向量数据库。

运行时,当用户向模型提出一个问题后,系统会使用向量数据库,根据用户 Query 与各个文本块之间的语义相似度,找出最相关的 Chunk。

随后,再把这些最相关的 Chunk 添加到发送给生成模型的 Prompt 中。

Embedding 的不足

Embedding 模型非常擅长捕捉语义关系。

但它们可能错过一些至关重要的:

精确匹配。

幸运的是,有一种更早出现的技术很适合弥补这一问题:

BM25,也就是 Best Matching 25。

BM25 是一种排序函数,它通过词法匹配来寻找精确的单词或短语。

它尤其适合处理包含:

  • 唯一标识符;
  • 技术术语;

的查询。

BM25 建立在:

TF-IDF,Term Frequency-Inverse Document Frequency

也就是“词频-逆文档频率”的思想之上。

TF-IDF 用于衡量某个词对于一篇文档相对于整个文档集合的重要程度。

BM25 在此基础上进一步考虑:

  • 文档长度;
  • 词频饱和函数。

这样可以避免一些高频常用词在排序结果中占据过大权重。

BM25 能解决 Embedding 容易漏掉的问题

假设用户在技术支持数据库中搜索:

Error code TS-999

Embedding 模型可能会找到许多关于:

“错误代码”

的一般性内容,

但它不一定能够准确找到:

TS-999

这一具体字符串。

BM25 会直接寻找精确文本匹配,因此更容易找到真正相关的技术文档。

同时使用 Embedding 和 BM25

为了获得更加准确的检索结果,RAG 系统通常可以同时结合:

  • Embedding;
  • BM25。

完整流程可以是:

  1. 把知识库拆分成若干较小文本块;
  2. 为这些 Chunk 创建 TF-IDF 编码和语义 Embedding;
  3. 使用 BM25,根据精确匹配找到排名靠前的 Chunk;
  4. 使用 Embedding,根据语义相似度找到排名靠前的 Chunk;
  5. 使用 Rank Fusion 等方法,把第 3 步和第 4 步的结果合并并去重;
  6. 把最终排名最高的 Top-K Chunk 放入 Prompt,用于生成回答。

通过同时使用 BM25 和 Embedding,传统 RAG 可以兼顾:

  • 精确词汇匹配;
  • 更广泛的语义理解。

这样,就可以以相对低廉的成本扩展到规模极大的知识库。

但传统 RAG 依然存在一个非常重要的问题:

它经常破坏上下文。

传统 RAG 中的上下文难题

为了提高检索效率,传统 RAG 通常会把文档切分成较小的 Chunk。

这种方法在很多任务中都很好用。

但如果某个单独 Chunk 本身缺少足够上下文,就会产生问题。

假设你的知识库中包含大量金融信息,例如美国 SEC 文件。

用户提出问题:

ACME Corp 在 2023 年第二季度的营收增长是多少?

某个相关 Chunk 可能只包含一句:

该公司的营收较上一季度增长了 3%。

这段信息本身当然是正确的。

但这个 Chunk 单独拿出来时,并没有说明:

  • 它说的是哪家公司;
  • 它对应哪个时间段。

因此,系统很难:

  • 在检索阶段正确找到这个 Chunk;
  • 在生成阶段正确理解和使用它。

引入 Contextual Retrieval

Contextual Retrieval 的核心思路是:

在对 Chunk 进行 Embedding 和 BM25 索引之前,先给每个 Chunk 加上一段专门针对它生成的解释性上下文。

这分别形成了:

  • Contextual Embeddings;
  • Contextual BM25。

继续使用前面的 SEC 文件例子。

原始 Chunk 可能是:

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."

也就是说,原本:

该公司的营收较上一季度增长了 3%。

会被补充成类似:

这段内容来自 ACME Corp 2023 年第二季度业绩的 SEC 文件;上一季度营收为 3.14 亿美元。该公司的营收较上一季度增长了 3%。

这样一来:

  • Embedding 更容易理解 Chunk 真正讨论的主题;
  • BM25 也能够匹配:
    • ACME Corp;
    • Q2 2023;
    • 3.14 亿美元;
    • Revenue;

等更具体的信息。

过去也有其他利用上下文改善检索的方法,例如:

  • 给所有 Chunk 添加通用的文档摘要;
  • Hypothetical Document Embedding;
  • 基于摘要进行索引。

我们也测试过其中一些方法。

给 Chunk 添加通用文档摘要,带来的收益非常有限。

基于摘要进行索引的表现也比较差。

这些方法与本文提出的 Contextual Retrieval 有明显区别。

如何实现 Contextual Retrieval

显然,不可能人工为知识库中的:

  • 数千个;
  • 数百万个;

Chunk 逐一添加上下文注释。

因此,可以让语言模型自动生成这些上下文。

我们的做法是:

让模型阅读完整文档,然后为其中每一个 Chunk 生成一段简短、专属于这个 Chunk 的上下文说明。

我们使用如下 Prompt:

<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.

中文含义是:

<document>
{{完整文档}}
</document>

下面是我们希望放回整篇文档上下文中理解的文本块:

<chunk>
{{文本块内容}}
</chunk>

请给出一段简短精炼的上下文说明,
用于解释这个文本块在整篇文档中的位置和含义,
目标是改善这个文本块之后的搜索和检索效果。

只输出这段简短上下文,不要输出其他内容。

最终生成的上下文通常只有:

50~100 Tokens

然后将它放到原始 Chunk 前面。

之后:

  • 对“上下文 + 原始 Chunk”创建 Embedding;
  • 同样使用“上下文 + 原始 Chunk”构建 BM25 索引。

因此,Contextual Retrieval 的预处理流程本质上是:

原始文档
    ↓
切分 Chunk
    ↓
读取整篇文档,为每个 Chunk 生成专属上下文
    ↓
上下文 + Chunk
    ↓
同时创建:
    ├── Contextual Embedding
    └── Contextual BM25 Index

使用 Prompt Caching 降低 Contextual Retrieval 成本

Contextual Retrieval 需要在为每个 Chunk 生成上下文时,参考整个原始文档。

如果每次都重新把整篇文档输入模型,成本会非常高。

Prompt Caching 可以解决这个问题。

不需要为每一个 Chunk 都重新提交完整参考文档。

只需要:

  1. 把完整文档载入 Cache;
  2. 后续为不同 Chunk 生成上下文时重复引用已缓存内容。

假设:

  • Chunk 大小:800 Tokens;
  • 文档大小:8,000 Tokens;
  • Context Instruction:50 Tokens;
  • 每个 Chunk 生成的上下文:100 Tokens;

那么,为每 100 万文档 Token 一次性生成全部上下文化 Chunk 的成本约为:

1.02 美元。

实验结果

我们在多个不同知识领域进行了实验,包括:

  • Codebase;
  • 小说;
  • ArXiv 论文;
  • 科学论文。

同时测试了不同的:

  • Embedding 模型;
  • 检索策略;
  • 评价指标。

我们使用表现最好的 Embedding 配置:

Gemini Text 004

并检索:

Top 20 Chunk

作为主要结果。

评价指标使用:

1 - recall@20

它表示:

真正相关的文档中,有多少没有出现在前 20 个检索结果里。

因此:

这个数字越低越好。

实验显示,在我们测试的每一种:

  • Embedding;
  • 数据来源;

组合中,加入上下文化信息都会改善性能。

Contextual Embeddings

只使用 Contextual Embeddings,就能够把:

Top-20 Chunk 检索失败率

从:

5.7%

降低到:

3.7%

相对下降:

35%

Contextual Embeddings + Contextual BM25

把 Contextual Embeddings 与 Contextual BM25 结合后,Top-20 Chunk 检索失败率进一步从:

5.7%

降低到:

2.9%

相对下降:

49%

实现 Contextual Retrieval 时需要考虑的因素

1. Chunk 边界

文档如何被切分会直接影响检索表现。

需要考虑:

  • Chunk 大小;
  • Chunk 边界;
  • Chunk Overlap。

不同文档类型可能适合不同切分策略。

2. Embedding 模型

Contextual Retrieval 在我们测试过的所有 Embedding 模型上都能带来改善。

不过,不同模型获得的收益并不完全相同。

实验中:

  • Gemini;
  • Voyage;

Embedding 的效果尤其好。

3. 自定义 Contextualizer Prompt

前面给出的通用 Prompt 已经能够取得不错效果。

但如果针对自己的领域进行定制,性能还可能进一步提升。

例如:

可以在 Prompt 中加入:

关键术语词汇表。

因为某些专业术语的定义可能只出现在知识库中的其他文档,而不是当前文档中。

4. 传入多少个 Chunk

往上下文窗口中加入更多 Chunk,会提高真正相关信息被包含进来的概率。

但更多信息也会对模型产生干扰。

因此,并不是越多越好。

我们测试了:

  • Top 5;
  • Top 10;
  • Top 20。

实验中:

Top 20 表现最好。

但具体应用中依然值得根据自己的数据做实验。

5. 始终进行 Eval

检索效果变好,不代表最终回答一定会同样程度改善。

因此应该始终对最终 Response Generation 进行评测。

一种可能有效的做法是:

把上下文化之后的 Chunk 传入生成模型时,明确区分:

  • 哪部分是额外 Context;
  • 哪部分是原始 Chunk。

使用 Reranking 进一步提升性能

Contextual Retrieval 还可以与另一种常见方法结合:

Reranking,重排序。

从而进一步提升性能。

传统 RAG 会先从知识库中搜索潜在相关的 Chunk。

对于大型知识库,初始检索可能返回:

几十个;

甚至几百个;

相关程度不一的 Chunk。

Reranking 的目标就是:

在把这些内容交给生成模型之前,再进行一次更加精确的筛选。

这样可以:

  • 提升回答质量;
  • 降低成本;
  • 降低延迟。

因为最终模型需要处理的信息更少。

Reranking 的基本流程

  1. 执行初始检索,获得一批可能相关的 Chunk;
  2. 我们的实验中取 Top 150;
  3. 将这些 Top-N Chunk 与用户 Query 一起送入 Reranking Model;
  4. Reranking Model 根据每个 Chunk 对当前 Query 的:
    • 相关性;
    • 重要性;
      给出分数;
  5. 再从中选择 Top-K Chunk;
  6. 我们使用 Top 20;
  7. 把最终 Top 20 作为上下文传给生成模型。

流程可以概括为:

用户 Query
    ↓
Embedding + BM25 初步检索
    ↓
Top 150
    ↓
Reranker
    ↓
重新评分
    ↓
Top 20
    ↓
生成模型

Reranking 带来的性能提升

市面上有多种 Reranking Model。

我们的实验使用了 Cohere Reranker。

Voyage 同样提供 Reranker,不过我们当时没有时间测试。

实验结果显示:

在多个领域中加入 Reranking,都能进一步改善检索表现。

具体来说:

Reranked Contextual Embedding + Contextual BM25

能够把 Top-20 Chunk 检索失败率从:

5.7%

降低到:

1.9%

相对下降:

67%

这比单纯使用 Contextual Retrieval 的:

49%

提升更进一步。

Reranking 的代价

Reranking 并不是免费的。

它会增加:

  • 延迟;
  • 成本。

因为运行时多了一步处理。

虽然 Reranker 可以并行给所有 Chunk 评分,但仍然必然会增加一定延迟。

因此,这里存在一个天然权衡:

Rerank 更多 Chunk

优点:

  • 检索准确率更高。

缺点:

  • 成本更高;
  • 延迟更高。

Rerank 更少 Chunk

优点:

  • 延迟更低;
  • 成本更低。

缺点:

  • 可能漏掉真正相关信息。

因此,我们建议根据自己的具体应用测试不同配置,寻找合适平衡点。

结论

我们进行了大量实验,对比了多个变量的不同组合,包括:

  • Embedding 模型;
  • 是否使用 BM25;
  • 是否使用 Contextual Retrieval;
  • 是否使用 Reranker;
  • 最终检索多少个 Top-K 结果;
  • 不同类型的数据集。

最终得到几个主要结论。

1. Embedding + BM25 优于单独使用 Embedding

Embedding 擅长:

  • 语义匹配。

BM25 擅长:

  • 精确词汇匹配。

二者结合效果更好。

2. Voyage 和 Gemini 是我们测试中表现最好的 Embedding

不过,不同实际数据集仍然值得自行评测。

3. Top 20 比 Top 10 或 Top 5 更有效

至少在我们的测试中:

向模型传入 20 个 Chunk 的表现优于只传入:

  • 10 个;
  • 5 个。

4. 为 Chunk 添加上下文,可以大幅提升检索准确率

这正是 Contextual Retrieval 的核心价值。

5. 使用 Reranking 优于不使用

通过第二阶段重新评分,可以进一步过滤噪声。

6. 这些收益可以叠加

为了最大化性能,可以组合:

  • Contextual Embeddings;
  • Contextual BM25;
  • Reranking;
  • Top 20 Chunk。

例如:

Document
    ↓
Chunking
    ↓
生成 Chunk-Specific Context
    ↓
Contextualized Chunk
    ↓
┌───────────────────────┐
│ Contextual Embedding  │
│ Contextual BM25       │
└───────────────────────┘
    ↓
Rank Fusion
    ↓
Top 150
    ↓
Reranker
    ↓
Top 20
    ↓
生成模型

实验中,这套组合把 Top-20 检索失败率从:

5.7%

降低到了:

1.9%

也就是:

降低 67%。

对于任何需要使用大型知识库构建 RAG 系统的开发者来说,Contextual Retrieval 都值得进行实验。

附录 I

我们分别针对不同:

  • 数据集;
  • Embedding Provider;
  • 是否同时使用 BM25;
  • 是否使用 Contextual Retrieval;
  • 是否使用 Reranking;

比较了 Retrieval @ 20 的表现。

实验总体趋势是一致的:

在所有测试过的 Embedding 与数据来源组合中,为 Chunk 加入专属上下文都能够改善检索效果。

同时:

  • Contextual Embeddings 优于传统 Embeddings;
  • Contextual Embeddings + Contextual BM25 进一步提升;
  • 再加入 Reranking 后达到最佳结果。
【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。