17. Anthropic Contextual Retrieval 是什么?它解决了什么问题?
整理 Contextual Retrieval 的原理、收益与适用边界。
简单回答
Contextual Retrieval 是 Anthropic 在 2024 年提出的一种 RAG 召回优化方案。核心思路是在切片(chunk)入库前,用 LLM 给每个 chunk 生成一段 50-100 token 的"上下文前缀"——说明这个 chunk 在原文档里是什么背景、属于哪个章节、讲的是什么——再把"前缀 + chunk 原文"一起做 Embedding 和 BM25 索引。这样解决的是"切片在脱离原文档语境后语义稀释"的问题。配合 BM25 + 向量的混合检索和 rerank,Anthropic 报告召回错误率最高能降低 67%。
详细解释
Chunk 脱离原文档后的语义稀释问题
传统 RAG 切片有一个长期被忽视的问题:chunk 一旦从原文档切下来,就失去了大量隐含的上下文。
举个真实的例子:一份公司财报里有一句"该季度营收同比增长 12%"。在原文档里,前后文清楚地说明这是"ACME 公司 2024 Q3 的财报,主营业务是云服务"。但切成 chunk 后只剩孤零零一句话,向量化后的 embedding 既对不上"ACME 财报"也对不上"2024 Q3 云业务表现"——这两个对它来说都是新概念。
用户检索"ACME 2024 Q3 云业务收入"时,这个 chunk 在向量空间里很可能排在很靠后的位置,因为 embedding 里没有任何关于"ACME"、"2024 Q3"、"云"的语义。这是本专题第 03 篇(chunking)和第 04 篇(embedding 选型)都会触发的痛点,但常规优化(更小的 chunk、更好的 embedding)都解决不了根本问题——信息根本就不在 chunk 里。
Contextual Retrieval 的做法
Anthropic 的方案是在入库前给每个 chunk 加一段"上下文前缀"。流程是:
对每个 chunk,把它和整篇原文档一起丢给一个 LLM,用一个固定 Prompt 让模型生成 50-100 token 的上下文说明。Prompt 大致是:
模型会输出类似"这是 ACME 公司 2024 Q3 财报中关于云服务业务的部分,紧接在整体业绩概述之后"这样的一段话。
把这段说明拼在 chunk 原文前面,作为新的"contextualized chunk",再去做 Embedding 索引和 BM25 索引。检索时直接拿这个增强版的 chunk 来匹配。
为什么这个简单的改动有效
它本质上是把"原文档的语境信息"显式地写进了每个 chunk 里,让 chunk 在语义层面变成"自描述"的——读这个 chunk 不需要回去翻原文档也能知道它在讲什么。
向量层面,contextualized chunk 的 embedding 包含了完整的语义锚点(公司名、时间、主题),用户 query 的 embedding 能更准确地匹配上。BM25 层面,关键词覆盖度也变高了——原本只在标题里出现的"ACME"、"Q3"现在每个相关 chunk 都有,BM25 命中率明显提升。
Anthropic 的实验结果是:单用 Contextual Embeddings 让召回错误率降低 35%,加上 Contextual BM25 混合检索降低 49%,再加 rerank 降低 67%。这个数字在他们公开的几个评测集(codebase、scientific papers、legal documents)上都能复现。
成本和工程取舍
最直接的代价是入库时的 LLM 调用成本。每个 chunk 都要调一次 LLM,文档越多成本越高。100 万个 chunk 大概是几百到几千美元。这是一次性成本(除非文档更新),分摊到长期使用里通常划得来。
Anthropic 用了一个工程技巧大幅降这个成本——Prompt Caching。每次调 LLM 时整篇原文档作为前缀(input)是固定的,只有 chunk 内容在变。开 Prompt Caching 后整篇文档只需要算一次 KV Cache,后续 chunk 复用,成本降到原来的 1/10 左右。这也是为什么这个方案在 Anthropic 平台上特别划算——它的 Prompt Caching 支持很到位。换到其他 LLM 服务上可能要重新算账。
另一个考虑是文档长度。原文档要塞进 LLM 上下文里,超长文档(几十万 token)就要先切成小段再做。但切完段后"上下文"就不完整了,效果会打折。
和其他 RAG 优化的关系
Contextual Retrieval 不是替代别的优化,而是和它们正交。它和本专题第 07 篇文章讲的混合检索配合:contextualized chunk 同时提升向量检索和 BM25 的召回。它和第 06 篇讲的 rerank 配合:rerank 是排序优化,Contextual Retrieval 是"召回池本身的内容质量"优化,两者解决的是不同环节的问题。它和第 08 篇讲的 query rewrite 也是互补的——query rewrite 是优化查询端,Contextual Retrieval 是优化文档端,两端都做收益最大。
适合用 Contextual Retrieval 的场景
效果最明显的场景有几类。一是文档篇幅较长、章节结构清晰、单 chunk 容易脱离上下文的——比如财报、法律文件、技术文档、长篇研究论文。二是知识库由大量小片段组成、且片段间关联强的——比如代码库(一个函数的含义离不开它所在的文件和模块)。三是 query 经常包含文档级元信息(公司名、时间、产品线)的——这些信息通常只在文档头部出现,但用户搜索时会带上。
不太适合的场景:FAQ 这种本身每个 chunk 就是独立完整问答的、或者文档本身很短没有"上下文"可言的、或者更新极其频繁导致重新生成上下文成本过高的。
面试时可以这样答
Contextual Retrieval 是 Anthropic 提出的 RAG 召回优化方案。它解决的是 chunk 切下来后脱离原文档语境的问题——比如财报里"该季度营收同比增长 12%"这句话,单独切出来后向量里没有公司名、没有时间、没有业务线,用户查"ACME 2024 Q3 云业务"根本召回不到。
做法是入库前给每个 chunk 用 LLM 生成一段 50-100 token 的上下文说明,比如"这是 ACME 2024 Q3 财报中云业务部分",把这段说明拼在 chunk 前面再做 Embedding 和 BM25 索引。chunk 在语义上变得自描述,向量匹配和 BM25 命中率都明显提升。Anthropic 报告单用 Contextual Embeddings 召回错误率降 35%,配混合检索降 49%,再加 rerank 降 67%。
工程上的代价是入库时每个 chunk 都要调一次 LLM。Anthropic 用 Prompt Caching 大幅降低成本——整篇原文档作为前缀是固定的可以缓存,只有 chunk 在变,成本降到 1/10 左右。
它和混合检索、rerank、query rewrite 都是正交的,可以叠加。最适合长文档、结构化、用户 query 常带元信息的场景,比如财报、法律文件、代码库。FAQ 这种 chunk 本身就独立的场景收益不大。
常见追问
- 上下文生成用大模型还是小模型?质量差异大吗?
- 文档更新后 contextualized chunk 怎么重新生成?只更新变化的 chunk 还是整篇重做?
- Contextual Retrieval 和给 chunk 加 metadata 字段(标题、章节、时间)有什么本质区别?