跳到正文
Hacker News· mxudev·· 7 天前AI 评分52

BreadBowl 发布 BreadBowl-Embed:用 KV cache 式槽位同时做检索与重排

Using KV cache as embeddings

AI 导读

BreadBowl AI 发布 BreadBowl-Embed,一种 late-interaction 嵌入架构,把每段文本编码为 16 个 routing 与 value 槽位,检索和重排共用同一份存储表示,重排时不再重读候选文本。

正文 · AI 翻译

Introducing BreadBowl-Embed  ·  Open weights  ·  Apache-2.0

BreadBowl-Embed 是一种全新的late-interaction嵌入架构。它不再为每个文档或每个 token 存储一个向量,而是把每段文本存为 16 个routing–value槽位。routing 向量在索引中寻找候选;随后同样的向量决定每个查询读取哪些已存储的 value。检索与重排序共享同一套表示:文档只编码一次,重排序时无需重新读取其文本。

Ming (Jerry) Xu Founder, BreadBowl AI October 2026 15 min read

Model weights HUGGING FACE Code GITHUB Paper PDF

一段文本,只编码一次真实的模型权重

16 个存储槽位routing ▴ / value ▾

Routing 向量 · 去哪里找Value 向量 · 读什么 悬停或点击某个槽位,查看它汇聚了什么。

Slot Encoder 只读取这段文本一次,并将其汇聚为 16 个槽位。流动的线条展示了已发布检查点针对该文本最强的实际汇聚权重(四个头的均值)。每个槽位存储为一个 routing 向量和一个 value 向量;这就是索引所保留的全部内容。

01 — 问题所在

高精度检索会把你的文档读两遍。

一遍用来找到它们,另一遍用来判断哪些才真正重要。

现代搜索与 RAG 流水线分两个阶段运行。首先,bi-encoder 把每个文档压缩成单个向量,这样最近邻索引就能在毫秒内搜索数百万个文档。然后,由于单个向量丢弃了决定相关性的细节(日期、异常、谁对谁做了什么),cross-encoder 会结合查询重新读取排名靠前的候选并再次打分。

精度正来自第二阶段,而它的代价以一种非常具体的方式体现:每当有查询到来,重排序器都要对每个(查询,候选)对运行一次完整的 transformer 前向传播。其中大部分工作无法提前缓存,因为它取决于查询。

Agent 让情况更糟。一个编码或研究 agent 不会每个任务只发一个查询;它会发几十个,而每一个都要再次支付重排序的成本。在我自己的编码 agent 会话中,我估计大约 80% 的工作是找到正确的上下文:搜索、阅读并判断什么重要。寻找上下文正在成为 AI 工作的内循环。

于是我们问了一个简单的问题:仅凭查询到来之前就已计算并存储的内容,检索表示本身能完成重排序器多少工作?

一个查询,两套检索栈粗略估算

Embed + cross-encoder 重排序常见的两阶段栈

Query~20 tokens

Embed query1 transformer pass

Vector indexnearest neighbors

100 candidatestext fetched again

Cross-encoder1 pass per candidate

Ranked top 10

0transformer passes 0candidate tokens re-read 0rerank FLOPs

BreadBowl-Embed从已存储的槽位进行检索与重排序

Query~20 tokens

Slot Encoder1 transformer pass

Routing index16 keys per query

100 个候选已存储槽位已加载

读取存储值16×16 注意力,无文本

排名前 10

0次 transformer 前向传播 0个候选 token 被重新读取 0次重排 FLOPs

对(查询 + 候选)文本进行一次 transformer 前向传播对存储向量做注意力

每个橙色方块是对一个(查询,候选)对进行一次 cross-encoder 前向传播,假设段落为 384 个 token。BreadBowl-Embed 的重排步骤是对索引时写入的向量做 16×16 注意力。两种方案仍然都只对查询编码一次并搜索索引,而 BreadBowl-Embed 的 16 向量搜索比单向量搜索做更多算术。计算数字是算术估算,不是延迟测量;假设见注释 1。

02 — 表示谱系

一个向量太少。每个 token 一个又太多。

对于文档应如何存储以供搜索?这个问题,历来有两种经典答案。

每个文档一个向量。诸如 Qwen3-Embedding、E5 或 EmbeddingGemma 这类双编码器把整段文本池化为一个点。存储便宜、搜索快。但每个查询都与同一个摘要比较:关于日期的问题和关于建筑师的问题命中空间中的同一个点。得分只是一个点积,因此没有可进一步细化的余地。这就是单向量常常要搭配重排器的原因。

每个 token 一个向量。像 ColBERT 这样的后期交互模型为每个 token 保留一个上下文向量,并让每个查询 token 找到其最佳匹配(MaxSim)。这保留了细节且排序效果好。代价也随之而来:索引随每个存储的 token 增长,评分成本随查询和文档长度双双增长,而且评分器对存储的 token 向量所能做的只是衡量它与查询 token 的相似度。每个查询 token 取它唯一的最佳匹配;没有单独的内容可供读取。

BreadBowl-Embed 有意居于两者之间。每段无论多长都获得固定的 16 个槽位预算。每个槽位携带两个承担不同任务的向量:

  • 一个 路由向量(256 维),说明去哪里找。它被索引和搜索。
  • 一个 值向量(256 维),在查询决定去哪里找之后保存要读什么。

为一段存储的内容来自各架构的算术

384 个 token

单向量双编码器,例如 1,024 维

每个 token 一个向量ColBERT 风格,每 token 128 维

BreadBowl-Embed16 个槽位 ×(256 路由 + 256 值)

存储的数字是压缩前计数的。以字节计,压缩后的 ColBERTv2 索引(大约每 token 向量 20–36 字节)目前小于我们未压缩的槽位(float32 下每段 32 KiB,bf16 下 16 KiB);结构差异在于 ColBERT 的向量数量和评分成本随长度增长。评分成本是每个候选的乘加次数,ColBERT 查询为 32 个 token,BreadBowl-Embed 为 16 个查询槽位。BreadBowl-Embed 最多读取 384 个 token 并截断其余部分,因此该组件假设长文档先被切分为段落。详情见注释 2。

你究竟需要从表示中得到什么?

这是我们论文中的表 1,已做成交互式。逐条开启需求,看看哪些设计能存活。

需求:

架构 检索器 重排序器 额外主干前向 独立值读出 存储的文档向量 评分成本
双编码器✓✓*无—1O(h)
交叉编码器—✓每对——B(nq + nd)
Poly-encoder—✓无—1O(mq h)
ColBERT✓✓无—ndO(nq nd h)
ConstBERT✓✓无—mdO(nq md h)
MVA(池化)—✓无✓2mdO(mq md h)
BreadBowl-Embed✓✓无✓2mdO(mq md h)

展示了全部 7 种设计。选择一项需求。

nq、nd:查询和文档的 token 数量。mq、md:每个输入的池化向量数(BreadBowl-Embed 为 16)。h:向量宽度。B(ℓ):对 ℓ 个 token 进行一次主干前向。*双编码器通过复用其检索时所用的同一套点积来“重排序”。成本仅描述候选评分。摘自论文表 1。

开启全部四项需求后,恰好只剩一行;其他所有设计都至少缺少一项。双编码器和 ColBERT 可以检索,但它们存储的向量只能被匹配,永远无法被读取。ConstBERT 解决了 ColBERT 的存储增长问题,但保留了 MaxSim。多向量注意力(MVA)引入了我们所基于的键–值读出,但其评估流程只对另一个系统检索出的候选进行重排序。交叉编码器仍是精度的黄金标准,但它们无法检索,且必须重新读取文本。

BreadBowl-Embed 的核心思想用一句话就能说清:让找到文档的向量,与决定如何读取该文档的向量,是同一组向量。我们称之为多态路由。

03 — 工作原理 · 一次编码

十六个学习到的问题,向每一段文本提问。

BreadBowl-Embed 的槽编码器从一个现成的语言模型开始:Qwen3.5-0.8B-Base 读取文本,并为每个 token 生成一个上下文向量。ColBERT 风格的模型会存储这些 token 向量。我们不这样做。

取而代之,它用引用交叉注意力(RCA)对它们进行池化:一组 16 个学习到的引用向量,被每个输入共享,每个引用向量对所有 token 状态进行注意力计算,并提取出一个槽。你可以把这些引用理解为模型向每一段文本提出的 16 个学习到的问题。问题是固定的;答案取决于文本。

随后两个小头部读取每个槽:一个线性路由头,以及一个值头(线性,外加一个小型残差 MLP 以增加容量)。两者都在索引时运行一次,其输出并排存储:每段文本 16 × 256 个路由数值和 16 × 256 个值数值。无论文本是 20 个 token 还是 384 个 token,都是 8,192 个数值。

槽编码器查询和文档使用相同权重

文本查询(≤ 128 个 token)或段落≤ 384 个 token

主干Qwen3.5-0.8B-Base,24 层n × 1024

RCA 池化16 个学习到的引用对所有 token 进行注意力计算(4 个头)16 × 1024

路由头线性16 × 256

值头线性 + 残差 MLP16 × 256

一次存储为路由行建立索引;将值保留在其旁边8,192 个数值

查询只是一个带有指令前缀(Retrieve relevant documents.)的短输入,由相同的权重编码。没有参考跳跃连接、前馈块或迭代槽位细化;提取器只是一个池化步骤。

这些槽位实际持有什么?

下面是我们在论文附录中检查的查询–文档对。选择任意槽位,查看它池化了哪些 token。查询槽位 13 将近一半的权重放在 completed 上。文档槽位 13 池化了日期:1889 的最后一位数字,然后是 Fair,再是 1887 的 7。槽位 14 抓取了 Rome。训练中没有任何东西要求这样的槽位;这只是这个例子的样子。

槽位池化,查询和文档真实模型输出

查询槽位槽位 13

文档槽位槽位 13

高亮强度是槽位在每个 token 上的池化权重(4 个头的均值);权重至少占 8% 的 token 显示百分比。选择一个查询槽位还会勾勒出路由向量与其最接近(余弦)的文档槽位。这些权重池化的是上下文状态,因此它们不是词重要性分数:年份的最后一位数字可以承载整个年份。一个例子不能确立稳定的槽位角色。使用已发布的权重计算(注意 3)。

04 — 用路由检索

用十六个键搜索。

在查询时,查询经过相同的编码器:对约二十个 token 进行一次骨干前向传播。然后它的 16 个路由向量中的每一个都会搜索一个索引,该索引保存了所有已存储文档的路由向量。命中结果按文档分组为一个候选短名单,然后用完整路由分数对短名单重新评分:

槽位相似度 C=R^qR^d⊤(16 × 16 余弦)

路由分数 sr=1m∑i=1mτrlog(1m∑j=1meCij/τr),τr=0.02

换句话说:对于每个查询槽位,在文档的 16 个槽位上取软最大值,然后在查询槽位上取平均。当 τr → 0 时,这变成平均 MaxSim,即 ColBERT 中熟悉的后期交互分数,只是作用于 16 个槽位而不是数百个 token。在论文的流程中,每个查询槽位拉取其前 128 个槽位命中,这些命中被分组为一个 1,024 篇文档的短名单,按路由分数最好的 100 篇进入重排序。

05 — 通过读取值重排序

相同的向量获得第二份工作。

这是单向量模型无法采取的步骤。对于每个候选,我们已经有了它的 16 × 16 路由相似度。用更高的温度软化它们,它们就变成了对候选的存储值向量的注意力权重:

来自路由的注意力 A=softmaxj(C/τv),τv=0.1

读取存储值 U=AVd(每个查询槽位一次读出)

值分数 sv=1m∑i=1mcos(viq,ui)

最终分数 s=0.75sr+0.25sv

每个查询槽位都有自己的读出:文档值的加权混合,由该查询槽位选择。读出与查询槽位自己的值向量进行比较,一致性被加到路由分数中。比较时没有新的投影,没有骨干,也没有文本,只是对已经存储在存储中的向量进行 16 × 16 注意力。成本与候选的长度无关。

文档从不改变。查询从中读取的内容会改变。

相同的存储文档,四个不同的问题真实模型输出

这个问题读取的内容所有查询槽位

路由分数–

值分数–

最终 0.75·r + 0.25·v–

行:查询槽位 · 点击一个可将其单独隔离列:存储的文档槽位

热力图:所选问题的值-注意力权重 A(颜色越深表示权重越大)。高亮文本:该问题读取的槽位所池化的文档 token(注意力权重乘以池化权重)。存储的路由向量和值向量在每个视图中都相同;只有查询发生变化。使用已发布的权重计算(见注 3)。

问关于 Colosseum 的问题,全部 16 个查询槽位都把最重的权重放在文档槽位 9 上,也就是池化了 Colosseum … is in Rome 的那个槽位。问哪个博物馆在巴黎,全部 16 个都移到槽位 5,也就是池化了卢浮宫那句话的槽位。两个埃菲尔铁塔问题大多读取相同的铁塔槽位(11 和 12),因为它们问的是同一件事;差异体现在各个查询槽位上。在 completed 问题中,池化了 completed 的两个查询槽位最倚重槽位 13,即日期槽位。每次都是同样的 8,192 个存储数字。

单向量模型做不到这一点:无论你问什么,它都只与同一个点比较。ColBERT 会为不同问题匹配不同的 token,但匹配是它唯一能衡量的东西。BreadBowl-Embed 增加了第二个通道:路由决定去哪里看,值携带在那里找到的内容。

06 — 读取值有帮助吗?

相同的候选。更好的排序。

对这个想法最干净的测试是:取一个训练好的模型和一份候选列表,然后用两种方式对列表排序:仅路由(QK),或路由加值读出(QKV)。召回率在构造上完全相同,因此排序的任何变化都来自读取值。

48.47 → 51.54

BEIR 宏平均 nDCG@10,跨 15 个任务,每个查询相同的 100 个候选

15 个中的 10 个

读取值后 BEIR 任务得到改善

+11.9

NQ 上的 nDCG@10 点数,TREC-COVID 上同样如此

≈ ⅓

在用于检查点选择的开发集上,与 0.6B 交叉编码器教师之间差距的缩小比例

BEIR nDCG@10:仅路由 vs 路由 + 值论文,表 2

仅路由(QK)路由 + 值,更高路由 + 值,更低

Show as a table
在完整 BEIR 语料库上的 nDCG@10 × 100。QK 和 QKV 对每个查询排序相同的 100 个路由选出的候选,因此两者的 recall@100 完全相同(宏平均 67.91)。Δ 在四舍五入前计算。协议见注 4。

增益并不均匀。最大的增益出现在问答和事实核查上:NQ(+11.9)、TREC-COVID(+11.9,仅 50 个查询)、FEVER(+8.7)和 Climate-FEVER(+6.9)。最大的损失出现在相关性意味着相似或对立的文本而非包含答案的地方:ArguAna 的反驳论点检索(−5.2)和 Quora 的重复问题(−2.2)。这与机制相符,但模式并不干净:SciFact 同样是主张验证,却下滑(−1.05),而 Touché-2020 与 ArguAna 一样是论点检索,却有所增益(+5.8)。还有一个混杂因素。NQ、FEVER、HotpotQA 和 MS MARCO 在我们的蒸馏数据中有训练划分,因此 BEIR 对这个模型并非统一零样本;TREC-COVID 的 +11.9 是在那里没有训练划分的任务上最大的增益。

最高结果发生变化的地方

宏观平均值会掩盖变动,所以这里给出直白的版本。在我们的开发集上(5,000 条查询,检索 341 万篇文档),我们统计了读取数值后把标注段落推入第一位的查询,以及把标注段落挤出第一位的查询。这一统计来自与论文表 3 和表 5 相同的运行,但并未出现在论文中。

读取数值后 Top-1 结果被修正 vs 被破坏全新检索运行,开发集

数值把正确段落挤出 #1数值把正确段落推入 #1

Show as a table
统计范围是 4,572 条开发集查询,其标注段落出现在 100 个路由候选中。每个来源都从其各自准备好的语料库中检索。开发集也用于检查点选择,因此请将其视为诊断而非留出测试(见注释 5)。

总体而言,数值把标注段落推到首位 479 次,把标注段落挤出首位 238 次:净增益 241,同时伴随大量变动。FEVER 严重偏向正向(178 次修正,7 次破坏)。MS MARCO 则相反(112 次修正,126 次破坏),PubMedQA 略为负向(2 对 4),SQuAD v2 持平。按 nDCG@10 衡量,数值改善了九个来源中的六个(论文表 5)。

这距离真正的重排序器有多近?

我们通过蒸馏 Qwen3-Reranker-0.6B(一个交叉编码器)来训练数值通路。在用于检查点选择的固定开发集候选列表上,教师模型得分为 69.80 nDCG@10,仅路由为 60.36,路由加数值为 63.40。因此在这些列表上,该读出机制缩小了与其教师模型之间约三分之一的差距,且在重排序时无需运行 transformer 前向传播。

与交叉编码器教师模型的差距论文,表 7

60.36仅路由

63.40+ 数值

69.80交叉编码器教师模型

在用于选择检查点(第 4,000 步)的已保存开发集候选上,针对一个 0.6B 教师模型的来源宏观 nDCG@10 × 100。该读出机制弥补了 9.44 分差距中的 3.04 分,约 32%。

它并不能取代交叉编码器:仍有三分之二的差距,你仍然可以在其之上运行一个交叉编码器。它所做的,是把重排序器价值中相当一部分转移到一个每个候选仅需约 0.27 MFLOPs 的步骤中。

在真实查询上试试

这些是来自早期验证集的真实问题和事实核查声明,每个都带有一个 10–12 篇段落的候选短名单,取自保存的候选列表,并用已发布的权重重新评分。在仅路由和路由加数值的排序之间切换,观察标注段落落在哪里。最后一个例子是数值使结果变差的情况,因为这种情况也会发生。

重排序游乐场真实查询 · 已发布权重

    查询和段落来自早期验证集的 Natural Questions、FEVER 和 HotpotQA 子集(不是那个 5,000 条查询的开发集)。每个短名单都是用一个较早检查点构建的已保存候选列表的顶部,此处用已发布权重重新评分(见注释 3);我们尚未验证这些查询与训练池是否不相交。绿色标签标记数据集的标注段落。条形图显示已发布检查点的路由得分 sr 和数值得分 sv;最终得分为 0.75·sr + 0.25·sv。这些示例是为了展示该机制而挑选的,因此请相信上面的总体数字,而非任何单个示例。

    单看路由本身,它把什么推到了 Natural Questions 榜单的前列:标题残段和列表片段,比如 Thursday Night Football 或 1. “Take It Easy”。它们与主题匹配,但其中没有任何内容能回答问题,而且它们的价值分数为负,因此下沉。Hamilton 这个例子展示了另一种模式:路由把其他几位 Alexander Hamilton 排在前列,而价值分数把这位开国元勋的传记——被标注的证据——推到了最上面。这个失误也很有启发性。对于“The Rocket”,价值分数抓住了一段答案形态的段落(一座名为 The Rocket 的过山车,于 1979 年被摧毁),但说的是错误的 Rocket。

    路由找到主题。价值寻找答案。

    07 — 训练

    教它排序。重放如何查找。

    在检索和重排序之间共享同一个表示会引发一场拉锯战。训练价值通路去模仿交叉编码器会更新共享的 Slot Encoder,而这可能会悄悄移动检索所依赖的路由向量。在更差的候选上得到更锐利的重排序器并不是进步。

    阶段 1 · 弱对比训练

    学会查找,并铺设槽位

    骨干网络、槽位提取器和两个头在 LightOn 精选预训练集合(一个 3000 万对的准备目标)中的弱查询–文档对上端到端训练。一个损失教路由在批内文档中挑出正例;第二个损失教组合分数将正例与路由挖掘出的难负例区分开;一个多样性惩罚阻止 16 个路由槽位彼此坍缩。

    8 × H200 · 约 8 小时

    阶段 2 · 带检索重放的蒸馏

    学会阅读,同时不忘记如何查找

    冻结骨干网络和路由头。将教师的列表式排序(每个查询 32 个候选上的 KL 散度,来自九个来源的 500,000 个查询)蒸馏到槽位提取器和价值模块中。每八次更新,重放一次在标注查询–正例对上的对比路由损失,这样候选发现保持有监督,同时相关性细化得到改进。

    8 × RTX 5090 · 约 10 小时

    蒸馏更新(价值)+ 检索重放(路由),每第 8 次更新

    论文中的两阶段配方(§3.4 和附录 A.3)。所报告的运行使用了约 64 H200 GPU 小时和 80 RTX 5090 GPU 小时,不包括开发、数据准备、教师评分,或阶段 2 所延续的早期蒸馏运行(在 20,000 个查询上进行 1,600 次更新)。

    重放重要吗?我们在有和没有重放的情况下运行了相同的蒸馏:相同的初始化、相同的查询、各 4,000 次更新,每组一个随机种子。重放确实增加了训练计算量(额外 820 万次查询–正例呈现)。然后每个检查点重新编码开发语料并检索自己的候选,因此比较同时捕捉了发现和排序。

    训练Recall@100nDCG@10 路由nDCG@10 路由 + 价值
    仅 KL 蒸馏86.6358.6462.57
    KL + 检索重放87.19 +0.5660.40 +1.7663.42 +0.85

    在 5,000 查询开发集上的来源宏平均(论文,表 3)。最终 nDCG@10 中的重放增益(四舍五入前为 0.84)具有 95% 自助法区间 [0.45, 1.24] 分。

    重放既改善了我们发现的内容,也改善了我们的排序方式。一个诚实的瑕疵:在路由选出最终 100 之前,粗选名单的召回率略有下降(88.50 → 88.38),因此重放并不能保留候选覆盖的每一个方面。

    08 — 现状

    不是排行榜榜首。一种新形态的模型。

    我们希望明确说明这次发布是什么、不是什么。在已发布的 BEIR 平均值上,BreadBowl-Embed 落后于强大的单向量模型,包括一些规模小得多的模型:

    已发布的 BEIR 均值(nDCG@10 × 100)协议不同 · 论文,表 8

    Show as a table
    已发布的均值和参数数量来自 Akram 等人(2026),表 4。这些报告未控制输入格式、截断、检查点修订或检索设置,因此这是文献背景而非匹配比较。两行 BreadBowl-Embed 是我们自己的评估(论文,表 2),其参数数量是名义主干规模。

    由于已发布的数字无法直接比较,我们还在相同的开发数据上自行重新运行了其中两个模型,使用相同的查询、语料库、标签和 token 限制:

    模型nDCG@10Recall@100
    Qwen3-Embedding-0.6B62.2587.42
    Jina v5 text-small(检索)66.1490.73
    BreadBowl-Embed,仅路由60.4087.19
    BreadBowl-Embed,路由 + 值63.4287.19

    九个开发来源的源宏平均(论文,表 9)。每个系统检索自己的候选。训练数据、模型规模和训练计算量均不匹配。这些查询与 BreadBowl-Embed 的训练数据来自相同来源,且该集合用于模型选择。

    仅路由落后于 Qwen3-Embedding-0.6B。读取值使 BreadBowl-Embed 在平均值上领先,召回率相近,但领先是集中的:它在九个来源中赢了四个,而 FEVER 和 NQ 支撑了平均值。Jina v5 text-small 领先于两者。

    那么为什么现在发布?因为有趣的结果不是排行榜位置,而是机制。同一个模型,相同的候选,在 BEIR 上 +3.07 nDCG@10,来自已经存在于索引中的信息。(该比较在一个训练好的模型中于推理时关闭值分数;它不是单独训练的仅路由模型。)单向量索引在查询时没有存储内容可读取,因此没有等效的步骤可采取。随着路由改进,值增益会增长还是缩小是一个开放问题:在我们的重放消融中,更好的路由伴随着更小的值增益(3.93 → 3.02 分)。而且我们还没有扩展该方案的任何部分。

    我们尚未展示的内容

    • 与检索器 + 交叉编码器堆栈相比的端到端延迟和吞吐量。这里的效率论据是架构算术,而非基准测试。
    • 交叉编码器级别的精度。在我们的开发列表上,0.6B 教师模型仍领先 6.4 nDCG@10 分。
    • 在匹配训练下与 ColBERT 系列模型的正面比较。
    • 跨训练种子的方差,或训练数据与 BEIR 之间重叠的完整审计。一些 BEIR 领域出现在我们的适配数据中,早期的 SciFact 和 NFCorpus 运行影响了开发。
    • 存储嵌入跨模型版本的兼容性。

    09 — 试一试

    编码一次。检索并重排。

    权重在 Hugging Face 上以 Apache-2.0 许可发布,为 breadbowl-embed-v1.2-preview,6,推理工具包在 GitHub 上(尚未在 PyPI 上)。它默认在 CPU 上运行,在 Apple Silicon 上使用 device="mps"。需要 Python 3.12 或更新版本。

    python -m pip install "git+https://github.com/BreadBowlAI/breadbowl-embed.git"
    from breadbowl_embed import BreadBowl
    
    model = BreadBowl.from_pretrained("dotproductx/breadbowl-embed-v1.2-preview")
    passages = [
        "Octopuses have three hearts.",
        "The Pacific is Earth's largest ocean.",
        "Plants use sunlight during photosynthesis.",
    ]
    for hit in model.rerank("How many hearts does an octopus have?", passages):
        print(hit.score, passages[hit.document_index])

    对于重复查询,将文档编码一次并重用存储的槽位。索引会为每个命中返回路由、值和组合分数:

    documents = model.encode_documents(passages, batch_size=2)   # routing + value slots, stored once
    index = model.index(documents, ids=["octopus", "ocean", "plants"])
    queries = model.encode_queries(["How many hearts does an octopus have?"])
    
    for hit in index.search(queries, top_k=2, candidate_k=3)[0]:
        print(hit.id, hit.routing_score, hit.value_score, hit.score)
    
    print(documents.routing.shape)  # [3, 16, 256]
    print(documents.value.shape)    # [3, 16, 256]

    捆绑的索引是对每个文档的路由槽进行精确线性扫描,用于实验。它没有实现论文中的逐槽候选列表流水线,也没有 ANN 后端。原始向量在 float32 下每个段落占用 32 KiB。在注意力读取之前保持文档值未归一化;对它们进行归一化会改变评分器。

    10 — 我们为什么要构建这个

    可以被读取的表示,而不仅仅是匹配。

    越来越多的 AI 产品本质上都是检索产品。编码智能体搜索代码仓库,企业副驾驶搜索公司知识,研究智能体搜索网络,推荐系统搜索人、产品和意图。在它们之下都有一层表示层,而如今这层大多指向最近邻,把真正的判断留给第二个模型。

    BreadBowl 的赌注是,这一层应该承载更多结构:足以进行排序、适应领域,并被每个问题以不同方式读取。BreadBowl-Embed 是第一步:一种既能查找又能读取的存储表示。

    想看看它能在你的数据中发现什么吗?

    BreadBowl 托管服务的 Alpha 访问已开放,面向模型评估、设计合作以及构建检索密集型产品的团队。如果你的瓶颈是重排序器,我们尤其希望听到你的声音。

    contact@breadbowl.ai

    引用

    @misc{xu2026breadbowlembed, title = {BreadBowl-Embed: Encode Once, Retrieve and Rerank}, author = {Xu, Ming}, year = {2026}, note = {Preprint}, url = {https://huggingface.co/dotproductx/breadbowl-embed-v1.2-preview} }

    注释

    1. 交叉编码器估算:前向 FLOPs ≈ 2 × 非嵌入参数 × token 数。对于 Qwen3-Reranker-0.6B(我们的蒸馏教师,约 0.44B 非嵌入参数),在约 420 token(查询 + 384 token 段落)的配对上,计入注意力后每个候选约 0.4 TFLOPs,100 个候选约 40 TFLOPs;重排序器的提示模板会增加 token,因此这是保守估计。更短的段落会按比例缩小。BreadBowl-Embed 的值读取:每个候选 2 × (16·16·256 + 16·16·256 + 16·256) ≈ 0.27 MFLOPs,100 个候选约 27 MFLOPs。不过它的第一阶段比单向量搜索更重:精确平面搜索将 16 个查询向量与每个文档的 16 个向量进行比较(每个文档 65,536 次乘加,是 1,024 维单向量的 64 倍),并且用完整路由分数对 1,024 个文档的候选列表重新评分会增加约 0.13 GFLOPs。这些都不包括移动存储向量(每个候选 16–32 KiB)。我们尚未测量端到端延迟。
    2. 计数遵循论文表 1,且先于压缩和索引复制。ColBERT 以 128 维 token 向量和 32 token 查询展示;BreadBowl-Embed 以 16 个槽、每个槽 256 路由 + 256 值维度展示。评分成本:ColBERT 32 × n × 128 次乘加;BreadBowl-Embed 每段落 16 × 16 × 256(路由)+ 16 × 16 × 256(值读取)+ 16 × 256(比较)。在 384 token 下,2 位 ColBERTv2 索引每个段落需要约 13.5 KiB,而 BreadBowl-Embed 的槽在 bf16 下为 16 KiB。
    3. 演示数字来自在 CPU 上以 float32 通过 NumPy 重新实现推理路径运行已发布的权重。其分词与参考 token ID 匹配,其路由向量与参考 GPU 输出(bf16)在每槽余弦相似度 0.9998 或更高上一致,因此数值可能与论文中的数字在小数点后第三位有所不同。
    4. 官方 BEIR 完整语料库与相关性判断,测试划分除 MS MARCO(dev)外。nDCG 使用 pytrec_eval 计算。CQADupStack 的 12 个论坛在 15 任务宏平均前先平均为一个任务。候选来自论文的算法 1(每个查询槽 128 个槽命中,1,024 篇文档的候选列表,按路由分数取前 100),使用精确扁平搜索。所有任务使用一个固定检查点和一套检索设置;结果为点估计。
    5. 来自九个来源的 5,000 条开发查询(MS MARCO、NQ、TriviaQA、SQuAD v2、HotpotQA 和 FEVER 的 LightOn 子集,加上 StackExchange 重复项、SciRepEval 搜索和 PubMedQA),每条查询在其各自准备的语料库上搜索(总计 341 万篇文档)。它们在候选挖掘之前被留出,但来自与训练数据相同的来源,并用于模型选择。top-1 修复/破坏计数是我们从相同运行中自行统计的。
    6. 检查点发布为 breadbowl-embed-v1.2-preview。“v1.2”是内部研究迭代;这是 BreadBowl 的首个公开模型发布。

    来源:Hacker News · breadbowl.ai