读技术文章时,我经常能在看到某一句话之前,先知道它属于哪个项目、哪个章节,以及哪个版本。检索系统却未必拥有这些背景。它可能只拿到一小段文字,然后判断这段内容能不能回答用户的问题。
阅读 Anthropic 的上下文检索文章时,我最关心的就是这个差异。长文档被切成小块后,怎样把理解它所需的背景保留下来?为此多调用一个模型,究竟能改善多少问题,又会增加什么风险?下面我用一个虚构的 Agent 任务恢复场景,把自己的理解串起来。文中的例子和数字都是说明性的假设,不是复现实验的结果。
先找到资料,再组织回答
假设我正在维护一个服务端 Agent,想知道进程意外退出后,怎样接着上次的任务继续运行。答案可能已经写在项目文档里,但负责回答的模型当前并没有读到它。
RAG 的中文名称是检索增强生成。它先从知识库寻找相关材料,再把取回的原文和用户问题一起交给生成模型,由模型组织回答。检索负责提供证据,生成负责理解和使用证据。
这两个环节都可能出问题。相关文档没有被找到,模型就缺少可靠依据;文档找到了,模型仍然需要分清版本、主体和适用条件,避免把别的项目方案套过来。
为了在大量资料中查找,系统通常先把长文档拆成文本块,为每个块建立索引。索引可以理解为帮助快速定位内容的一套记录。用户提问时,系统根据问题找到一批候选块,再从中选择需要的材料。
切开文章,也可能切断背景
假设一份任务恢复文档正在介绍检查点机制。检查点就是系统在某个时刻保存下来的执行状态,有了它,任务恢复时就有机会接着先前的进度继续。
其中一个块写着:“重新启动后会加载最近一次保存的状态,继续执行尚未完成的步骤。”放在全文里,我知道主语是服务端任务执行器。但单独拿出这句话,信息就不够明确了。谁重新启动?保存的是页面状态,还是后台任务的状态?
用户问的可能是“Agent 崩溃后如何从检查点恢复”。这个块能提供答案,却没有直接出现 Agent 和检查点这两个词。识别它需要的背景,可能被留在文档标题、上一个章节或更早的段落里。
切块因此需要权衡。块太小,必要背景容易被分开;块太大,又可能混入许多无关内容。保留标题、选择自然段边界、设置适当的重叠,都可以影响检索。上下文检索关注的,就是块独立出现时缺少背景的这类问题。
含义和关键词,提供两种查找线索
一种查找方式是语义检索。它使用嵌入模型,把文字转换成固定长度的数字向量,这种向量表示称为嵌入。问题与文档需要采用相互兼容的编码方式,随后系统比较这些向量的相似程度。
“中断后接着运行”和“从检查点恢复”用词不同,却有语义关联。训练好的检索模型应当尽量学会这种关系。一种常见训练思路,是让问题和相关材料的表示更接近,让不相关材料相对更远。相似度分数反映这种接近程度,但不能证明内容本身正确。
另一种方式是关键词检索。对于错误码、函数名、版本号等明确标识,直接匹配词语很有价值。BM25 是常见的关键词评分方法,会考虑一个词出现多少次、在整个语料里是否常见,以及文档长度。它进行的是统计计算,不会像语言模型一样生成新文字。
原图还出现了 TF-IDF 这个名称,它指词频与逆文档频率的统计思路。BM25 与这套思路有关,但具体评分方式不同。理解流程时,可以先把它们放在关键词检索这条路线上,与寻找含义关联的语义路线对照。
两条路线可以共同使用。语义路线帮助跨越表达差异,关键词路线帮助保留具体标识的匹配能力。这样的组合本来就可以存在于普通 RAG 中,并不是补充上下文之后才有的。
给原始块加上一段必要背景
Anthropic 提出的处理发生在建立索引之前。语言模型同时看到完整文档和当前块,根据全文为这个块生成一小段背景说明,再把说明放到原始块前面。这个整体随后用于向量索引和关键词索引。
对于前面的例子,如果全文提供了相应依据,背景说明可以写成:“本段介绍服务端 Agent 任务执行器的检查点恢复机制,用于进程异常退出后的任务恢复。”后面继续保留原始块那句关于加载状态、继续未完成步骤的话。
原始内容没有被替换,也不需要被重新写成另一种表达。新增说明交代的是当前块的主体、主题和必要条件。它可能利用全文其他位置的信息,但不能把没有依据的内容补进来。
接下来,系统将这段说明与原始块拼接,交给两条检索路线处理。Embedding 模型能够获得更明确的语义输入,关键词索引则能够记录 Agent、检查点等新增词语。
这里有两类模型:负责补背景的语言模型生成文字,负责嵌入的模型把文字编码成向量。它们的职责不同。背景说明和原始块也不需要互相打分,它们共同组成一个等待检索的单元。
入库与提问,是两个不同阶段
入库时,系统完成切块、补背景和建立索引,同时保存每个块的原文位置。处理好的资料可以被多次查询使用,文档更新后再更新受影响的部分。理解这个时间关系,就不会把所有模型调用都算到每次提问上。
用户提问后,系统利用语义和关键词两条路线寻找候选,再融合两份结果。融合主要负责综合排序、处理重复块,让后续环节面对一份候选列表。
还可以加入 Reranker,也就是重排模型。它结合问题重新判断候选块的相关性,将更适合的内容排到前面。例如,先找出二十个候选,再经过重排保留五个,是一种可以测试的配置。
因此,初步查找多少候选,与最终把多少块交给生成模型,是两个可以分别设置的数量。最后,生成模型接收用户问题和筛选后的材料,形成回答。给块补背景与增加重排属于不同改进,也可以分别评估。
额外成本要看发生在哪里
补背景确实增加了成本。语言模型需要生成说明,嵌入模型需要处理更长的输入,系统还要保存和索引新增文本。如果每个块都重新输入全文,长文档与大量切块会放大处理量。
这些开销主要发生在入库或更新时。实现支持缓存时,可以减少重复处理;并行可以缩短等待,但新增工作仍然存在。对于已经建好索引的资料,用户每次提问通常不必重新生成背景。
如果用户上传文档后马上提问,入库时间就会直接影响等待体验。资料频繁更新时,还要关注处理积压和索引是否及时更新。查询阶段也可能变贵,因为重排模型或生成模型接收到的背景和原文加在一起更长了。
输入变长,不代表固定维度的嵌入模型会输出更长的向量。增加的主要是编码工作、文本和相关索引开销。能否接受这些代价,要看资料被重复使用多少次,以及回答质量究竟改善了多少。
补背景,也可能补错背景
保留原文不意味着生成过程一定可靠。模型可能把全文中真实存在的信息,关联到错误的块。例如,同一份设计文档还介绍了前端面板恢复,模型却给服务端恢复块补上“本段介绍浏览器面板重新打开后恢复界面的机制”。
这些词可能都在全文里出现过,但主体已经错了。错误说明会改变整体向量表示,也可能把错误词语写入关键词索引,造成相关块漏掉,或者无关块被找出来。两条路线共享同一段背景,同时命中也不能算两次独立核实。
重排模型主要判断与问题是否相关,不能自动充当全文事实核验器。如果错误背景被直接传给生成模型,答案也可能跟着出错。即使说明完全真实,过于宽泛、重复的背景也未必对定位当前块有帮助。
我会优先使用能准确提取的标题、章节和版本信息;确实需要生成时,只补当前块缺少的内容。原文、生成说明、依据位置和文档版本应分别保存,方便检查和撤回。让模型承诺不编造,只是要求,仍然需要抽检与实际评测。
取更多块,不等于检查更多次
Top 5 表示取排序前五个块,Top 20 表示取前二十个块。文章主要观察前二十个结果中的检索失败情况,关心需要的证据有没有被漏掉,而不是直接统计最终回答的错误率。作者尝试了五、十、二十个块,并报告二十在其测试中最好,同时提醒更多信息可能干扰模型。
在同一份固定排序里,前二十条包含前五条,已经找到的证据不会因为多取了十五条而消失。但如果前五条已经足够,新增的旧版本或其他项目内容可能增加干扰。取二十块也不会自动核查二十次生成背景,它与多次生成答案、看是否至少一次通过测试的评测不同。
新增背景同样不能靠扩大数量来抵消。用模型处理文本的计量单位 token 举例,它不与汉字一一对应。假设每块原文八百个 token,背景一百个,取五块仍然保留四千个原文 token,只是额外加入五百个背景 token;取二十块时,原文和背景都会变成四倍。
所以,补背景并没有挤掉原始块的一半文字。如果限制的是整个输入的总长度,背景确实会占用本可用于其他原文的空间,这时需要在相同输入预算下比较。更合适的块数取决于问题、块长度和模型,不能仅因为调用了额外模型,就推导出必须取二十块。
我会怎样决定是否采用
我会把原始块和加背景的块分别拿来测试,再分别设置取五块和取二十块,得到四种组合。这样可以区分究竟是补背景带来了收益,还是单纯因为多取了材料。这里是一种验证设计,并不代表我已经完成了实验。
比较时,应固定文档版本、切块方式、问题集、嵌入模型、融合策略和重排设置,并为每个问题标出应该找到的原文证据。除了有没有找回证据,还要检查答案是否正确、背景是否有依据,并记录入库成本、输入量与响应时间。
同样的块数适合观察补背景后的变化,相同输入预算则适合比较成本效率。问题集中还应包含不同版本、不同主体、否定条件,以及需要结合多个块才能回答的情况,避免只用容易的问题证明方法有效。
我对上下文检索的判断是:它针对了一个真实的信息损失点,给文本块补回必要背景,具有明确的用途。但增加模型调用本身不能证明方案更好。如果标题和章节信息已经足够,就可以保持简单;如果短背景确实改善了证据和答案,同时错误与成本可控,再采用它会更有依据。
参考资料
Anthropic:Introducing Contextual Retrieval,发布于 2024 年 9 月 19 日

