embedding(向量嵌入)把文本映射到高维空间中的一个点,语义相近的文本距离相近。基于它的dense retrieval(稠密检索)解决了关键词检索最大的痛点:用户不必说出文档里的那个词。问「怎么把钱要回来」能召回标题为《退款流程》的文档——这是词面匹配永远做不到的。
但同一个机制也带来了系统性的盲区。理解它们的共同根源很重要:embedding 是一次有损压缩——把一段文本压成几百上千个浮点数,必然丢掉信息,而它优先保留的是「大意」,优先丢弃的是「细节」。于是凡是「细节决定成败」的查询,稠密检索都会失手:
| 盲区 | 例子 | 为什么 |
|---|---|---|
| 精确标识符 | 订单号 A7X-99210、错误码 ERR_5521、SKU | 这类字符串没有语义,向量空间里几乎随机;且相似编号的向量彼此极近 |
| 罕见专有名词 | 内部项目代号、新产品名、人名 | embedding 模型训练时没见过,落在向量空间的无意义区域 |
| 否定与限定 | 「不支持退款的情况」 | 「支持退款」与「不支持退款」的向量高度相似——语义相反,字面相近 |
| 领域外泛化 | 通用模型用在法律 / 医疗 / 内部术语上 | 训练分布与使用分布不一致,相似度判断整体失准 |
| 数值与时间 | 「2023 年 Q3 的数据」 | 数值大小关系在向量空间里没有可靠表示 |
这就是为什么 BM25 这个 1990 年代的算法今天仍然是每个严肃检索系统的组成部分。它做的事情朴素得多——统计词频、按稀有度加权、按文档长度归一——但正因为它直接匹配字面,上表里的五类盲区它一个都没有。订单号搜订单号,一击必中。
两者的失败模式是互补的,这才是「混合」的理由。不是「BM25 是弱一点的备胎」,而是它们错的地方不一样:稠密检索在同义改写上强、在精确匹配上弱;BM25 反之。把两个错误不相关的系统组合起来,整体召回率会显著高于任何一个单独使用——这是集成方法的基本原理,与模型强弱无关。
问题在于两套分数不可直接比较:BM25 的分值范围随语料和查询变化,向量余弦相似度在 [-1,1],把它们加权求和需要归一化,而归一化参数又需要按数据集调——很脆弱。
实践中更常用的是 RRF(Reciprocal Rank Fusion,倒数排名融合):只看排名,不看分数。
RRF 的价值在于它几乎不需要调参就能工作,非常适合作为第一版。当你有了评估集(3.5),再考虑是否值得换成需要调参的加权融合。
检索系统面对一个规模上的矛盾:索引里可能有几百万个 chunk,而最终只能送 5 个进上下文。用一个足够精确的模型去逐一评估几百万个候选,成本上不可能。
解法是分两个阶段,用两种结构不同的模型:
两阶段的分工由此清晰:第一阶段的目标是「别漏」(recall),第二阶段的目标是「排对」(precision)。它们的失败也要分开衡量——第一阶段没召回的东西,重排再强也救不回来。
一个常被忽略的调参点:召回数量(通常记作 top-k)该取多少?取小了,正确答案根本进不了重排阶段;取大了,重排的延迟和成本线性上升。这个值应该由「召回率随 k 的变化曲线」来定——找到召回率开始平坦的那个 k,而不是拍一个 20。
在 BEIR 之前,检索模型大多在同分布的数据集上评测——训练和测试来自同一个领域,稠密检索的表现自然亮眼。Thakur 等人换了个问法:把模型放到它没见过的领域里,还行不行?
论文的三条结论几乎逐条对应了本章的工程主张:
① BM25 是稳健的基线。这个没有任何神经网络的算法,在跨领域场景下依然难以被轻易超越——因为它不需要泛化,它只是在数词。这直接支持了「不要丢掉词法检索」的实践。
② 基于重排的方案取得了整体最好的零样本表现,但计算成本高昂。这正是两阶段架构存在的理由——把昂贵的精确打分限制在少量候选上,让它的成本变得可以承受。
③ 稠密检索模型计算上更高效,但常常表现不及其他方法,泛化能力有相当大的改进空间。
对工程的直接含义:公开榜单上的 embedding 模型排名,对你的领域可能没有参考价值——那些分数大多在模型见过的分布上测得。如果你的场景涉及内部术语、专有名词或小众领域,务必假设稠密检索的表现会明显低于榜单,并在自己的数据上重测。这也解释了为什么「换一个榜单排名更高的 embedding 模型」经常带不来预期的改善。
Embedding 模型:开源侧有 BGE、E5、GTE 等系列(含多语言版本),闭源侧各厂商均有对应 API。选型关注三点:是否支持你的语言、向量维度(直接决定存储与检索成本,且高维不总是更好)、最大输入长度(须 ≥ 你的 chunk 大小,否则会被静默截断——这是一个很隐蔽的 bug)。
Rerank 模型:开源侧 BGE-reranker 等,闭源侧多家提供 rerank API。它们通常比 embedding 模型小,但因为要对每个候选跑一次前向,延迟与候选数成正比。务必把 rerank 的延迟计入 TTFT 预算(2.1)。
混合检索的实现:Elasticsearch / OpenSearch 原生同时支持 BM25 与向量检索并内置 RRF;PostgreSQL 可用全文索引 + pgvector 组合;多数专用向量库也已支持稀疏-稠密混合。如果你已经有 Elasticsearch,混合检索几乎是免费的——这常常是比引入新组件更优的路径。
别忘了元数据过滤:时间范围、权限、文档类型的过滤应该在检索层完成,而不是召回后再筛(后者会导致「过滤完不够 5 条」)。这一点对企业场景尤其关键,也是选型时最该确认的能力之一。
换 embedding 模型 = 全量重建索引:不同模型的向量空间不通用。这意味着换模型的成本远不止调用费,还包括全量重新编码的时间与费用。选型时就要把这个迁移成本考虑进去。
原因:A7X-99210 是一个无语义的标识符。embedding 模型没有为它学到任何有意义的表示,它在向量空间中的位置基本是任意的;更糟的是,所有格式相似的订单号会被编码到彼此很近的位置——因为模型看到的是「一串字母数字」这个模式,而不是具体数值。于是检索返回的是「长得像订单查询」的其他记录。
修法,按优先级:
① 混合检索。加入 BM25——精确的标识符正是它的强项,一击必中。这是最直接的修复。
② 更好的做法:这根本不该走检索。「查订单 A7X-99210 的状态」是一次结构化查询,应该由模型识别出订单号后调用订单查询接口(4.1)。检索返回的是文档,而用户要的是当前状态——即使检索成功,索引里的数据也可能已经过期。
③ 如果确实要走检索:把标识符作为元数据字段做精确过滤,而不是指望它出现在向量里。
记住这条判断线:凡是能被精确查询到的东西,都不要交给近似检索。向量检索是为「我说不清但大概是这个意思」准备的,不是为「我知道确切的 ID」准备的。
精度高的原因:它把 query 和 document 拼在一起送进模型,二者在每一层注意力中充分交互,模型可以捕捉「这个词是否回应了那个词」这类细粒度关系。双塔结构则是分别编码后只算一次距离——两个向量在生成时互相看不见,任何交互信息都在压缩中丢失了。
不能全库用的原因:交叉编码器的分数依赖于具体的 query,因此无法预计算。给定一个新 query,要对 N 个文档打分就得跑 N 次模型前向。索引有一百万个 chunk,就要跑一百万次——延迟和成本都不可能接受。
双塔的关键优势正在于此:doc 向量与 query 无关,可以离线批量算好并建索引;线上只需编码 query 一次,再做一次近似最近邻搜索。这个「可预计算」的性质,才是它能支撑大规模检索的根本原因——不是它更快,而是它把大部分工作挪到了线下。
所以两阶段架构不是折中,而是必然:用可预计算的模型把候选缩到几百条,再用不可预计算但精确的模型排序。
因为它和稠密检索的失败模式不重叠,而不是因为它更好或更差。
BM25 的不可替代之处:① 精确匹配——标识符、错误码、专有名词、罕见词,向量检索的系统性盲区;② 零训练、零领域适配——它不需要泛化,因此不存在「领域外表现下降」的问题;③ 完全可解释——匹配了哪些词、各自权重多少,一目了然,这对排查问题极其重要;④ 成本极低——倒排索引成熟到不需要讨论。
实证支持:BEIR 基准(NeurIPS 2021)在 18 个跨领域数据集上评测 10 种检索系统,结论之一就是 BM25 是一个稳健的基线,而稠密检索模型虽然计算高效,却常常表现不及其他方法、泛化能力有较大改进空间。
正确的心智模型:这不是「新技术淘汰旧技术」,而是两种召回机制的集成。就像推荐系统会同时用协同过滤和内容特征——组合多个错误不相关的弱信号,效果强于单独优化其中任何一个。
加分:主动提到混合检索的融合方式(RRF 免调参、加权融合需归一化),以及「换更强的 embedding 模型」经常不如「加一路 BM25」来得有效。