上下文化检索

10 分钟读完

R11
深入解析 · 检索与 RAG

上下文化检索:那个分块回答不了它自己提出的问题。

「营收较上一季度增长 3%」这句话里没有公司、没有季度、也没有年份,而这就是你的切分器递给嵌入模型的那个分块——这正是为什么在一套本来还算称职的 RAG 栈里,召回失败最大的单一来源不是检索器,而是你把文档切开的那一刻。在嵌入之前给每个分块前置一小段生成的、用来交代它出处的引子,能把 top-20 检索失败率降低 35%;让同一段引子也进 BM25 索引,降低 49%;再叠上重排,降低 67%——从 5.7% 降到 1.9%。代价大约是每百万文档令牌一美元。而那个没人计价的坑在于:你的索引从此成了第二个模型、以及一段你迟早想改的提示词的函数。

STEP 1

切分究竟毁掉了什么。

切分通常被当作一个尺寸权衡来讨论——太小丢上下文,太大稀释嵌入——而这个说法掩盖了真正的失败。问题不在于分块短。问题在于,承载这个分块含义的那些文字,已经不再与它相邻了。

  • 指代对象不见了。代词、「该公司」、「本政策」、「上表」、「如第 4 节所述」。人在原位读这个分块,能从三页之前的一个标题里把这些解析出来。嵌入模型拿到的是孤零零的分块,于是编码了一句没说清楚的话。
  • 起区分作用的细节留在了容器里。十份季报产出十个说法几乎相同的分块。它们的嵌入落在几乎同一个位置,而一个点名了具体公司和季度的查询对它们中的任何一个都使不上力,因为它要找的那些词,不在它所搜索的文本里。
  • 词法索引同样受害。这不是稠密向量的怪癖。BM25 在同一个分块上因同一个原因失效:那个起区分作用的词元——公司名、年份、产品编号——压根不在。这既是这个修复能同时惠及两套索引的原因,也是只在其中一套上衡量它会低估它的原因。
  • 语料越大越糟。200 份文档时,近似重复很少见,一个还行的检索器蒙混得过去。到了 20 万份,近似重复就是常态,检索失败率不再是个烦恼,而成了给你答案质量封顶的那个东西。本条建立在分块与向量检索的机制之上。

判断这是不是你的问题,有个一锤定音的诊断:取二十个你的系统为真实查询检索出来的分块,把文档标题和上下文去掉逐个读,问自己能不能说出它来自哪份文档。如果你说不出,嵌入模型也说不出。

STEP 2

这项技术,以及它喂养的两套索引。

上下文化检索由 Anthropic 于 2024 年 9 月发表,此后被吸收进了多数正经流水线,它的机制很简单。在嵌入一个分块之前,把整份文档连同这个分块发给一个便宜的模型,要它写一两句话交代这个分块的位置。把这段话前置到分块上。索引这个结果。

<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 个令牌:「本分块出自 ACME Corp 的 2023 年第二季度 SEC 申报文件;上一季度营收为 3.14 亿美元。」前置、嵌入、入库。有三个细节决定你拿到的是公开的那组数字,还是它的一个零头:

  • 把上下文化后的文本同时喂给两套索引。只做上下文化嵌入,失败率降低 35%。让同一批上下文化分块也过一遍 BM25,就到 49%。第二套索引几乎是白送的,而剩下增益的一半就在那里——生成的引子里装的恰恰是专名和日期,而那正是词法检索擅长的,这个论证在混合检索与重排里已经说过。
  • 生成时用原始分块。引子是检索辅助,不是内容。在上下文化文本上检索;把干净的分块(或分块加上它真正的邻居)交给模型。把生成出来的上下文当作原文递给生成器,就是引用指向了没有任何人写过的句子的由来。
  • 给文档开提示词缓存。文档在它所有分块之间是恒定的,所以整份文档的前缀被缓存下来,变动的只有分块。这才是把成本压到每百万文档令牌约 1.02 美元的原因——不做缓存,你就是每个分块重读一遍文档,经济账完全改变。见提示词缓存。
STEP 3

诚实地读这些数字。

35 / 49 / 67 是真的,同时也比看上去要窄。四条注意事项,每一条都曾把某个团队带上岔路:

  • 它们是 top-20 的检索失败率,不是答案准确率。「失败」指的是正确分块不在前 20 之内。如果你的生成器只看得到 5 个分块,那么一个把某分块从第 40 名挪到第 15 名的修复,会改善被测指标、却对用户毫无改变。要么把候选集放宽再重排下来,要么就在你真正传下去的那个 k 上衡量。
  • 5.7% → 1.9% 是整套叠起来的结果。67% 这个数字是上下文化嵌入加上上下文化 BM25再加上一个重排器。最后那一步里很大一部分是重排干的,而重排是更便宜、该先做的改动——在现有索引上加一个交叉编码器,完全不需要重新嵌入。
  • 增益随语料而定,而且会反转。这项技术在分块脱离上下文就有歧义的地方帮助最大:申报文件、合同、手册、工单会话、满是重复写法的代码库。在每个分块本来就自报家门的地方帮助最小——每段都带着产品名的产品页、FAQ 条目、新闻报道。在一份自报家门的语料上,你是在为一些重述分块已有内容的引子付钱,而多出来的令牌还会略微稀释嵌入。
  • 小语料不需要它。如果整份语料塞得进一个长上下文窗口,正确答案是别检索了。相关对照在有效上下文与标称上下文,而那个临界点比多数团队以为的要高。

在一组固定的、带已知正确分块 id 的查询上,把检索质量与答案质量分开衡量。一百个查询就够了,而搭起这套集合是本页每一个决定的前置条件——没有它,你就是在凭感觉和供应商的基准在各项技术之间挑选。完整方法见评估 RAG。

STEP 4

真正的代价是那份重建索引的承诺。

那个美元数字是容易的部分,也是人人都在引用的部分。昂贵的后果是架构性的:你把一个模型和一段提示词,加进了你索引的定义里。

  • 你的索引现在有四个依赖,不是两个。切分器、嵌入模型、上下文化模型、上下文化提示词。改动其中任何一个,已入库的向量就不再与一次全新摄取所产出的可比。把这个四元组写进索引元数据,好让未来的工程师看得出它是什么生成的。
  • 「摄取时的一次性成本」是按索引代次算的。百万文档的语料,上下文化一次并不贵。问题是你会做多少次——对一条年轻的流水线,诚实的答案是好几次,因为提示词会改进、那个便宜模型会被弃用、切分器会变。按三次重建做预算,不是一次。
  • 增量摄取必须钉住版本。提示词改动之后进来的新文档,拿到的是另一段提示词写的引子,于是你有了一个无声异质化的索引。按索引代次钉住上下文化器的版本,并把新文档路由到它所匹配的那一代。迁移的操作手法与重建索引与嵌入迁移里的是同一套。
  • 延迟花在摄取,不花在查询。这是这项技术最好的性质,值得明说:上下文化是离线发生的,所以查询时延迟毫无变化。对照那些查询时的替代方案——一个改写步骤、或者一个更大的重排器——它们是从用户的延迟预算里换质量的。
  • 删除会正常传播,但派生文本不会自报身世。引子里可能引用了文档别处的数字。如果那份文档被撤回,挂在某个不相关分块上的引子仍可能带着它的数字。这个陷阱的一般形态见索引新鲜度与失效。
STEP 5

该先试的那些更便宜的东西,按顺序。

上下文化检索不是第一招。它是第三或第四招,而早早就去摸它的团队,往往本来有个更便宜的问题。按成本递增排列:

  • 确定性前缀。把文档标题与标题路径前置上去——ACME Corp / 10-Q Q2 2023 / Item 2. MD&A——一次模型调用都不用。免费、即时、完全可复现,而且在结构良好的语料上,它能把增益中相当一部分捞回来,因为缺掉的那个区分特征通常就在某个标题里。先做这件事;如果你的解析器没给你标题路径,那就先去修那个(为 RAG 解析文档)。
  • 一个重排器。多数技术栈里质量杠杆最大的单项,不需要改索引,一个下午就能加上、也能撤掉。如果你还没有,那就在衡量别的任何东西之前先衡量它。
  • 混合检索。在稠密检索旁边加上 BM25,能抓住那些嵌入系统性漏掉的精确匹配情形——标识符、错误码、零件号。同样在索引侧,同样便宜。
  • 延迟分块(late chunking)。用一个长上下文嵌入模型嵌入整份文档,再把令牌级嵌入池化成每个分块的向量。每个分块的向量都见过整份文档,既没有生成步骤,也没有要维护的提示词。它比上下文化检索便宜,放弃的是显式性——上下文在向量里,而不在你或 BM25 读得到的文本里,所以它对词法索引毫无帮助。
  • 上下文化检索。当语料脱离上下文确实有歧义、更便宜的杠杆都已就位并衡量过、而且你手上有一套能告诉你它是否奏效的检索评测集时,再去摸它。

这些是叠加关系,不是竞争关系。公开的那 67% 是确定性结构、上下文化引子、词法与稠密两套索引、再加一个重排器,全都一起上——而之所以要分先后,是因为每上一样都会告诉你下一样还需不需要。

STEP 6

周一该做什么。

这个决定可以归结为三个问题,而且要对着你自己的数字问,不是对着某个基准。

  • 检索真的是你的瓶颈吗?把你的失败拆开:检索到了错的分块、检索到了对的分块但被忽略、检索到了对的分块但读错了。本页上的一切只对第一种有用,而它并不总是最大的那一桶。
  • 你的分块自报家门吗?跑一遍 STEP 1 里那个二十分块诊断。如果多数分块都点明了自己的主题,把预算花在重排上。
  • 你承受得起把索引重建三次吗?如果一次全量重建是一场全员大动员,那就先修这个——能否廉价重建,决定了其他每一项检索改进能不能被试;它比任何单项技术都更值钱。

针对又长、又有结构、且满是近似重复的文档语料,默认配方是:每个分块都带确定性的标题路径前缀,用一个小模型在提示词缓存下生成上下文化引子,两者同时喂进稠密索引和 BM25,用交叉编码器把前 100 重排到你传给模型的那 10 个,而生成与引用一律使用原始的干净分块、绝不使用引子。索引按「切分器、嵌入器、上下文化模型、上下文化提示词」这个四元组做版本标记,并保有一套每次重建都会跑的一百查询检索评测。如果你只能做其中两件,就做标题前缀和重排器,等评测说检索仍是天花板时再回头做其余的。

延伸阅读:进阶 RAG 架构说明这一环在完整流水线里的位置,智能体式检索处理的是智能体自己发起查询、以至于单次召回数字不再能描述整个系统的情形,带权限的检索讲的则是无论你怎么建索引都必须成立的那条约束。