晚交互检索

12 分钟读完

R12
深入解析 · 检索与 RAG

晚交互检索。

对晚交互的那条标准反对意见——「每个 token 一个向量,存储是一百倍」——大约四年前就已过时,而现实中几乎每一个架构决定仍然按它来做。ColBERTv2 的残差压缩把一个 MS MARCO 索引在每维两比特下从 154 GiB 压到 25 GiB,而这与同样 884 万条段落上一个普通 float32 单向量索引是同一个数量级;而 token 池化还能再去掉一半的向量,实测几乎没有损失。剩下要做的那个决定与磁盘无关。对文本来说,它是「你的失败是不是召回失败」——因为这是更好的第一阶段唯一能修的东西;而对页面图像来说,它是「你愿不愿意把解析流水线删掉」——这才是真正值回那份占用的理由。

STEP 1

一个性质承担了全部重量:打分被推迟了,但编码没有。

单向量检索器把整段文字池化成空间里的一个点,于是这段文字的每一个侧面——主题、实体、数字、限定条件——都得以一个平均值的形式存活下来。交叉编码器靠「把查询与文档一起读」来避开这次平均,这既是它排得更好的原因,也是它什么都没法预计算的原因:每一个查询-文档对都是一次前向传播。

晚交互恰好坐在两者之间,而把机制说准是值得的,因为成本模型是由它推出来的。每个文档 token 在建索引时各自拿到一个向量,投影到 128 维。每个查询 token 在查询时各自拿到一个向量。得分是 MaxSim:对每个查询 token,取它与任意文档 token 之间的最高相似度,再把这些最大值加起来。打分时查询编码器与文档编码器之间不共享任何参数,所以文档仍然可预计算;但比较发生在 token 粒度上,所以没有任何东西必须被平均掉。

  • 这是用学到的「词项」做词项匹配。结构性行为像 BM25——一个查询词项要么在文档里找到匹配,要么没有——只不过这些词项是带上下文的嵌入,于是「已弃用」能匹配上「不再受支持」。这正是它在稠密检索最弱的那类查询上表现好的原因。
  • 索引是一堆 token 向量,不是一堆文档向量。从存储、到 ANN 策略、到查询流水线的形状,工程上的一切都由这一个改变推出。
  • 它是第一阶段检索器,不是重排器。它可以当重排器用,而且常被这么用,但那种用法扔掉了让它与众不同的那个性质:找到第一阶段漏掉的那份文档的能力。它该插进的那条分阶段流水线,见混合检索与重排序。
STEP 2

自己把存储的算术算一遍,因为那套民间智慧是十年前的。

未压缩的版本确实很糟:128 维 float32 是每 token 512 字节,而一个段落语料的 token 很多。ColBERTv2 的残差压缩把每个向量换成「最近质心的 ID 加一个量化残差」,按设置落在大约每向量 20 到 32 字节——6 到 10 倍的缩减,使 MS MARCO 段落索引在每维两比特下从 154 GiB 降到 25 GiB,每维一比特则降到 16 GiB。

现在把对照摆在它旁边,而不是把那个倍率照单全收。MS MARCO 约有 884 万条段落。同一语料上一个 1024 维 float32 的单向量索引是 884 万 × 1024 × 4 字节 ≈ 36 GB;768 维则 ≈ 27 GB。那个被压缩的晚交互索引比同一语料未压缩的单向量索引还小。所以对成本诚实的说法是:如果你从来没量化过稠密向量,晚交互的代价大致就是你已经在付的;如果你量化过,大约是 4 倍。

  • token 池化是第一个旋钮,而且免费。在建索引前对一篇文档的 token 向量做层次聚类,能把向量数量砍掉 2–3 倍,不需要改架构、查询时也不多做事:约 50% 的缩减在检索上实测几乎无降级,而 66–75% 的缩减在多数数据集上降级不到 5%。它与上面那个两比特量化是叠加关系,不是互斥关系。
  • 「池化感知」的微调还能更进一步。2026 年有一条工作线训练模型去预期池化,报告在相当高的压缩率下准确率追平甚至超过未池化基线——所以这条取舍曲线仍在朝对你有利的方向移动。
  • 延迟是另外被解决掉的。PLAID 在打分之前用质心交互剪掉候选文档,报告相对朴素 ColBERTv2 在 GPU 上延迟最多降低 7 倍、在 CPU 上 45 倍——绝对数上,在 MS MARCO、k=1000 时 GPU 为 38.4 毫秒、单 CPU 核为 352.3 毫秒。对一个马上要花两秒生成的智能体循环来说,GPU 上的几十毫秒不是瓶颈。
  • 余下的代价是内存带宽,不是字节数。打分要触碰散落在索引各处的许多小向量,所以预测你 p99 的那个数字是「索引的热部分是否常驻页缓存」。这是一个容量规划问题,答案与「它在磁盘上多大」不同。

请带一个数字进设计评审:压缩后每 token 20–32 字节,再被池化砍掉一半。对一个一千万 token 的内部语料来说,这是一百兆字节量级,不是任何人的预算问题。那条存储反对意见之所以还活着,主要是因为它是对着 ColBERTv1 形成的、而且从来没被重新推导过——而选向量数据库里满是「在 2021 年是对的」的默认值。

STEP 3

质量增益到底是什么,以及产出它的那些查询形状。

ColBERTv2 可复现的那项主张是「域外稳健性」而不是域内峰值分数:MS MARCO 上 39.7 MRR@10、BEIR 平均 50.0 nDCG@10,在 28 项域外 BEIR 测试里有 22 项取得最高质量、相对次优检索器最多高出 8%——而且用的是压缩后的表示。域外才是你永远所处的那个状态,因为你的语料不是 MS MARCO,而你也不会去为它微调一个嵌入模型。

2026 年,这幅经验图景拿到了一层理论地板。微软研究院印度院的一篇论文给出了第一个显式的查询与文档集族,在其上单向量嵌入需要指数级维度才能把所有相关文档排在不相关文档之前,而多项式规模的多向量嵌入就够了;论文还提出了一个把这些困难案例实例化的基准(ANDOR),在它上面最先进的单向量模型零样本表现很差,而多向量模型撑得住。另有一条并行工作线从表达力角度论证了同一道分离。

请去读那个构造,而不只是读标题,因为它会告诉你你的哪些查询受影响。困难案例是合取与析取的复合——文档必须同时满足这个和那个,而每个条件单独看都很常见。单个向量必须把文档放在一个同时靠近每个侧面的点上;token 级匹配则可以用不同的 token 分别满足每个条件。

  • 智能体的查询多数就是这个形状。「那个设置了重试预算、并且在事故之后被改过的配置。」「与那家荷兰子公司的合同里关于终止的那一条。」多约束、实体密集、合取——正是那道分离结果所谈的结构,这也是为什么它对智能体式检索比对一个搜索框更要紧。
  • 短而主题化的查询不是这个形状。如果你的流量是「我怎么重置密码」,单个向量池化得挺好,而晚交互买到的很少。
  • 稀有 token 能活下来。标识符、错误码、零件号与版本号各自保有自己的向量,而不是被平均进一个主题里——这正是混合检索要靠 BM25 去糊住的那道缝。
STEP 4

拿它和你已有的那条流水线比,别和朴素稠密检索比。

相关的替代方案几乎从来不是「一个稠密索引」。它是「稠密加 BM25 做融合,再用交叉编码器重排前 50 或前 100」。对着那套栈,晚交互并不显然更好——而真正能分辨的那个问题是诊断性的,不是架构性的。

  • 如果那份正确文档从来没进过候选集,重排器在构造上就帮不了你:它只能把给它的东西重新排序。这是一次召回失败,而「更好的第一阶段」——混合、或晚交互、或两者——存在的意义就是修这个。
  • 如果那份正确文档在前 100 里,但排在第 40 位,交叉编码器是更便宜也更强的答案。它把两份文本一起读,在榜单顶端通常会赢过 MaxSim。
  • 如果你的延迟预算装不下对 100 个候选跑交叉编码器,晚交互是证据最好的那个折中:排序发生在检索步之内,所以你只付一次。
  • 如果内容确实是多侧面的——技术文档、合同、事故报告——上面那道分离结果说这道落差是结构性的,不会靠一个更好的池化嵌入收窄。

所以在迁移之前先跑那个诊断:取五十条失败查询,核对正确文档有没有出现在你当前第一阶段的前 100 里的任何位置,然后把它们分成两堆。这个比例会告诉你该把钱花在流水线的哪一半,而它只要一个下午。评估 RAG 里有测量脚架;在你自己查询上的 recall@k 才是定这件事的指标,而厂商基准里没有任何东西能替代它。

还有两项更便宜的干预也该先摆上桌,因为两者都在不改索引格式的前提下攻击召回:查询分解,把一条合取查询变成几条简单查询(见查询理解与变换),以及上下文化检索,它修的是那个伪装成排序问题的「孤儿分块」问题。

STEP 5

真正让那份占用说得过去的场景是页面图像。

晚交互最强的用法与文本排序毫无关系。ColPali 式的模型把一份文档的页面图像嵌入成一张图块向量网格——在 448×448 像素下,一个 ViT 编码器每页产出约 1,030 个图块 token,每个投影到 128 维,半精度下每页大约 256 KB——并用同样的 MaxSim 把查询的 token 与这些图块打分。ColQwen 式的后继者在 ViDoRe 基准上报告相对最初的 ColPali 约 +5.3 nDCG@5,而 ViDoRe v2 的存在本身就是因为第一版已经饱和。

对着一个 4 KB 的单一池化页面嵌入,这是 60 倍的占用,而这一次这个倍率是真的。为它买单的不是排序质量,而是整条流水线被删掉:

  • 解析错误不可挽回,排序错误可以。如果你的版面分析把两列表格并在了一起、或者 OCR 丢了一条脚注,那么下游任何检索器都找不回从未被抽取出来的东西。这种不对称正是文档解析与摄取质量的全部论点,而视觉晚交互把「发生它」的那一段去掉了。
  • 难搞的语料都是视觉的。扫描表单、财务报表、工程图纸、幻灯片,以及任何「信息在排布里」的东西。把这些转成线性文本正是那个有损步骤,而那也是你一直在维护的步骤。
  • 成本按页扩张,而页比 token 少得多。十万页乘 256 KB 是 25 GB。那是一个真实的索引,也是一个寻常的索引。
  • 图块剪枝那片文献是有原因的。自适应图块级剪枝与池化是 2025–2026 年活跃的研究,恰恰因为每页占用是采纳的阻碍;请预期这个数字会继续下降,不要围着今天的取值做架构。

什么时候不该用:一个生来就是文本的语料。把干净的 Markdown 或 HTML 转成页面图像再去视觉检索,是在为一个你本来没有的解析问题付那份占用。

STEP 6

在生产里把它做出来:支持情况、两阶段模式,以及那道验收测试。

原生多向量索引不再稀奇。Qdrant 自 v1.10 起就有、Weaviate 自 v1.29 起;Vespa 支持多向量文档已有好几年;LanceDB 与 Milvus 也都能对多向量列建索引并用 MaxSim 打分。普通 pgvector 没有 MaxSim 算子;VectorChord 扩展给 Postgres 加了一个——这很要紧,因为「我们本来就在 Postgres 上」是这件事在没有任何测量的情况下被否掉的最常见理由。

  • 标准模式是「预取再重排」。用一个便宜的单向量索引取候选,再用存着的多向量给它们重新打分。这是个不错的默认,而它也把你当初采纳晚交互就是要去掉的那道召回上限又装回来了——如果预取漏了那份文档,MaxSim 永远见不到它。请刻意决定你买的是哪个性质。
  • MUVERA 是那个模式讲原理的版本。它把一组多向量编码成一个固定维度的单向量,于是检索变成普通的 MIPS,然后再用完整的多向量做重排。Weaviate 在他们自己的测试里报告启用后摄取快 3 倍、查询快 1.8 倍。请注意它对容量的含义:你现在同时存着两种表示。
  • 该为之做准备的失败是重排那一段,不是检索。给许多小向量打分是随机访问型的工作,于是一旦多向量字段长到超出页缓存,p99 就会急剧恶化,而你的 ANN 指标看起来还挺健康。请按「常驻」而不是按「磁盘」定规模,并在你预期一年后的索引规模上做压测。
  • 重建索引是一次迁移,不是一次配置改动。切到晚交互或从它切走,会重写整个索引并改变它的规模档位;请用重建索引与嵌入迁移里的纪律来对待它,包括一个影子索引,以及切换前后可比的 recall@k 测量。

在迁移任何东西之前,先跑那个诊断:五十条真实的失败查询,只问一个问题——正确文档当时有没有出现在现有前 100 的任何位置。如果多数失败是排序失败,就买一个交叉编码器,然后停手。如果多数是多约束查询上的召回失败,就在一个集合上做晚交互试点,并从第一天起就开着 token 池化——「少一半向量、实测几乎无代价」不是一项该往后拖的优化。而如果语料是页面图像,那就跳过文本那场争论:在那里真正要比的是你的解析流水线,不是你的检索器。

相关:分块与向量检索——这一切出发的那个基线;嵌入——为什么池化丢掉的正是它丢掉的那些东西;以及本地优先检索——在优化「用哪一个索引」之前,先问你到底需不需要索引。