跳到主要内容

找得准 — 两条路、一次融合、一次重排

这一章讲三件事: 为什么按意思找不够、要再加一条按词面找的路; 两路结果怎么合成一份;以及为什么最后还要用一个更贵的模型再排一遍。 它在全书链条里的位置: 这是六站里的第五站(检索)的精修版。 第 05 章交代了「怎么取回来」,这一章交代「怎么取得对」 —— 而这是全书数字最扎实的一段。

1. 顶层全景

一个问题

├──── 路 A:按意思找(把问题变成一串数,比远近)

└──── 路 B:按词面找(数两边共同的词,罕见词加权)


两份名单 ──► 融合成一份 ──► 取前几条


用一个更贵的模型逐条重打分(重排)


最终交给模型的那几条

图说:前面三步都在「快速地缩小范围」,最后一步在「慢工出细活」。
这个「粗一遍、再细一遍」的形状,是检索系统里最通用的一条骨架。

一句话链条: 按意思找有死角 → 补一条按词面找的路 → 两路分数没法直接加,要先缩到同一个尺度 → 合出来的名单前几条仍然不够准 → 那就对这几条花大价钱重算一遍 → 而这一步的效果大到不像话。

2. 两条路各有死角

先看现象

你问什么按意思找按词面找
「辞职要提前多久打报告」(文档里写的是「离职需提前一个月提交书面申请」)找得到 —— 说法不同但意思一样找不到 —— 一个共同的字都没有
「XR-4471B 这个型号的保修期」常常找偏 —— 型号在它眼里就是一串没意义的字符一抓一个准 —— 这个字串在全库只出现一次

两条路的死角正好互补,所以真实系统一般两条都走。

这两条路的正式名字

这两条路在这一行有固定的叫法,你出门一定会撞见

按意思找的那条路,每篇文档变成一串 384 个数,而这串数几乎每一位都是非零的小数, 密密麻麻 —— 所以这一行管这种表示叫稠密

按词面找的那条路,心里另有一张表。这一行管「你的资料里一共出现过哪些词」的那张清单叫词表, 通常有几万个词条。

它的做法概念上就是给词表里每个词分配一个位置,文档里没出现的词全记 0。 一份几万个位置的表里绝大多数是 0 —— 空空荡荡,所以这种表示叫稀疏

于是两条路各自的正式名字就叫稠密检索稀疏检索1这两个名字来自各自表示的形状,不是来自效果好坏。

3. BM25 比「数词频」多做的三件事

按词面找那条路最常用的算法叫 BM25,是几十年前就有的老办法。 书在这里给了全书唯一一次真正的算法解释,值得完整留住2

最朴素的做法及其毛病

最朴素的做法是数:查询里的词在这篇文档里出现了几次,次数多就排前面。三个毛病立刻冒出来。

BM25 逐个修掉它们

毛病一:重复刷词。 一篇文档把「保险」写了 50 遍,按次数算它就赢了。

BM25 的修法:词频饱和 —— 同一个词重复出现,分数会涨,但涨得越来越慢,最后基本不再涨。 出现 1 次到 2 次,加分很多;出现 20 次到 21 次,几乎不加。

毛病二:所有词一样重。 查询「RAG 的保修期」里,「的」和「RAG」被同等对待。

BM25 的修法:罕见词加权 —— 一个词在整个文库里出现得越少,它一旦命中就越值钱。 「的」在每篇文档里都有,所以几乎不加分;「RAG」只在几篇里有,命中一次就是强信号。 这一项的正式名字叫逆文档频率。

毛病三:长文档天然占便宜。 一篇一万字的文档,什么词都可能出现几次。

BM25 的修法:按长度扣分 —— 同样出现 3 次,短文档得分高于长文档。

边界:它只看表面

书自己也说清楚了:BM25 简单、高效、可解释,常被当作基线(一个简单、公认、拿来做对照的起点做法)或补充检索器; 但它只看表面的关键词重合,抓不住深层语义2

这正是需要另一条路的理由 —— 两条路合起来才叫混合检索。

一词一义提醒: 书在第 5 章还把「按意思找 + 按标签过滤」也叫过 hybrid(混合)。 那是书自己的用法。 出门撞见「混合检索」这个词时,业界指的是这一章讲的 「稠密 + 稀疏」,不是那个。

4. 主走查:同一个问题,五种打法

这一节是本章的主走查。上面和下面每个机制,在这条线上都占一步。

书在第 9 章拿同一个问题跑了六个配方:

How does RAG reduce hallucinations?(RAG 怎么减少胡编?)

语料只换过一次,接缝写在下面。 前四步用的是同一批六条短文档; 后两步换成七条(多了「烹饪食谱」和「出国旅行」两条完全无关的,并把第一条换成一句正面回答)。

打法出自前三名的分数
按意思找配方 91(六条)0.3682 / 0.0452 / −0.0133
按词面找配方 92(同六条)1.2374 / 0.0000 / 0.0000
两路融合配方 93(同六条)1.0000 / 0.1570 / 0.0949
把问题写长(查询扩展)配方 94(同六条)0.5879 / 0.4707 / 0.3435
接缝书换了语料配方 95/96六条 → 七条
粗排取前五配方 96(七条)「烹饪食谱」和「出国旅行」都进了前五
重排配方 96(七条)+8.4985−11.3435 / −11.3601 / −11.3980 / −11.4207

第 ① 步:按意思找,第三名是负的

1. score=0.3682 | RAG combines retrieval with generation to ground answers in evidence.
2. score=0.0452 | BM25 is a sparse retrieval method based on term frequency statistics.
3. score=-0.0133 | Dense retrieval uses vector embeddings instead of keyword matching.

图说:满分是 1。第一名只拿到 0.37,而第三名是负的——方向已经相反了,照样进了前三。
这就是第 05 章那个「永远凑够 k 条」的又一次现场。

第 ② 步:按词面找,第二三名并列 0.0000

1. score=1.2374 | RAG combines retrieval with generation to ground answers in evidence.
2. score=0.0000 | Dense retrieval uses vector embeddings instead of keyword matching.
3. score=0.0000 | FAISS is a fast vector similarity library from Facebook/Meta.

图说:第一名靠的是命中了 RAG 这个罕见词。
而第二、三名的分数都是 0.0000——**查询里的词在这两篇里一个都没出现。**

并列 0 分意味着一件很要紧的事:这两条的名次是随机的。 排序时分数相同,谁在前谁在后取决于它们原本在列表里的顺序 —— 和相关性没有一点关系。

而这两条照样占了前三名的位子。

第 ③ 步:两路融合,第一名恰好是 1.0000

书的做法是3:

① 把六条的稠密分整体缩到 0 到 1 之间:最高的那条记 1,最低的记 0,其余按比例落在中间
② 把六条的 BM25 分照同样的办法缩一遍
③ 最终分 = alpha × 缩过的稠密分 + (1 − alpha) × 缩过的 BM25 分 这里 alpha = 0.5

图说:第 ①② 步那个动作叫 **min-max 分数归一**。
(注意:它和第 04 章那个「把向量除以自己的长度」是两件不同的事,只是中文都带「归一」两个字。
本文一律把这一个叫「min-max 分数归一」,不简称。)

输出是:

score=1.0000 | RAG combines retrieval with generation to ground answers in evidence.
score=0.1570 | Dense retrieval uses vector embeddings instead of keyword matching.
score=0.0949 | FAISS is a fast vector similarity library from Facebook/Meta.

第一名恰好 1.0000,这不是巧合,也不代表「完全匹配」。

那一条在稠密这一路是最高分 → 缩完记 1
那一条在词面这一路也是最高分 → 缩完也记 1
0.5 × 1 + 0.5 × 1 = 1.0000

图说:min-max 的定义就是「把最高的那个记成 1」。
**所以只要同一条在两路都排第一,融合分就必然恰好是 1.0000。**
别把它当成置信度看。

再看第二、三名:它们的 BM25 原始分都是 0.0000,缩完还是 0。 所以 0.1570 和 0.0949 完全来自稠密那一路的贡献 —— 词面这一路对它们一个字都没说。

第 ④ 步:把问题写长一点

书的做法是从一张事先写死的候选词表里,挑三个和问题最像的词拼到问题后面4:

原问题: How does RAG reduce hallucinations?
挑出的词: document matching, query understanding, retrieval
新问题: How does RAG reduce hallucinations? document matching query understanding retrieval

按意思找那一路的结果变成了:

扩展前: 0.3682 / 0.0452 / -0.0133
扩展后: 0.5879 / 0.4707 / 0.3435

图说:三个分数全都涨了,最低那个从负数涨到了 0.34。
**而且第二、三名换了人**——扩展前第二名是讲 BM25 的那条,扩展后变成讲稠密检索的那条。

这一步看起来是个大成功。第 7 节会说明为什么它其实是个警告。

第 ⑤⑥ 步(接缝):粗排一遍,再重排一遍

书在这里换了语料:七条,多了「烹饪食谱」和「出国旅行」两条完全无关的。

先用按词面找的那一路粗排,取前五条。书印出来的前五名是5:

RAG reduces hallucinations by grounding answers in retrieved evidence. ← 唯一相关的
Cooking recipes often require precise measurements and timing. ← 烹饪食谱
Traveling to new countries helps you learn about culture and history. ← 出国旅行
Vector databases store embeddings to enable semantic search.
FAISS is a fast vector similarity library from Meta.

图说:烹饪和旅行排在第二、第三。原因和第 ② 步一样——
它们的分数并列为 0,名次是随机的。**粗排就是这么粗。**

然后用一个更贵的模型对这五条逐条重打分:

score= 8.4985 | RAG reduces hallucinations by grounding answers in retrieved evidence.
score=-11.3435 | Traveling to new countries helps you learn about culture and history.
score=-11.3601 | Cooking recipes often require precise measurements and timing.
score=-11.3980 | FAISS is a fast vector similarity library from Meta.
score=-11.4207 | Vector databases store embeddings to enable semantic search.

图说:相关的那条和第二名差了将近 20 分;而四条无关的挤在 −11.34 到 −11.42 之间,
彼此只差 0.08。**界限干净得不像话。** 这是全书最漂亮的一段数字。

注意这些分数不是 0 到 1 那种「有多大把握」的数。 它们可正可负、没有上下限; 跨模型比绝对值没有意义,只能在同一次打分里比大小6书没有说这件事。

5. 两路结果怎么合:min-max 加权,以及业界的另一种做法

为什么不能直接把两个分数加起来

看第 ①② 步那两组数就知道:

按意思找的第一名: 0.3682 (范围大致在 −1 到 1)
按词面找的第一名: 1.2374 (没有上限,语料一大能到几十)

直接相加 → 词面那一路会碾压另一路,而这跟谁更相关毫无关系。

图说:两把尺子刻度不一样,不换算就没法加。

书里做对的那一次

就是第 ③ 步那个办法:两路各自缩到 0 到 1,再按 alpha 加权。

alpha 这个旋钮的含义很直白:alpha=1 就是纯按意思找,alpha=0 就是纯按词面找, 中间是混合的比例。

书里做错的那一次

第 5 章还有一个「加权融合」的配方。它给两路各定了一个权重(就是「这一路的分数按几成算进最终分」的那个乘数), 可两边都写死成 0.5,而且没有做任何缩放

最后那次排序因此什么都没排 —— 它实际做的只是「把两路结果拼起来、按内容去重」。 而书把它当成一种正经的融合方法介绍了。

业界的标准做法:只看名次,不看分数

补充(不在书里): 还有一条更省事、也更稳的路 —— 干脆不看分数,只看名次。

每一路各出一份名单:
第 1 名贡献 1/1 = 1.00 分
第 2 名贡献 1/2 = 0.50 分
第 3 名贡献 1/3 = 0.33 分
……

同一篇文档在两路里各拿一份,相加就是它的最终分。

图说:一篇文档如果在两路里都靠前,倒数相加就高;只在一路里冒头的,分数有限。
这个办法叫 **RRF**(倒数排名融合)。

它凭什么比缩放更稳:因为它对分数的量纲免疫。 分数不可比、某一路的分数挤成一团、某一路突然全是 0 —— 都不影响名次。

我们书架上那套公开了源码的检索系统就是这么判的:分数没缩放过时用 RRF,缩放过了才用加权求和7

这本书从头到尾没有提过它。

6. 两种编码器的分工

这一节解释第 ⑥ 步那个「更贵的模型」到底贵在哪。

先给第 04 章那种模型补个正式名字。那种「问题和文档各自单独变成一串数、再比远近」的做法, 用的模型叫双编码器:「双」说的是它对两边各跑一次、互不相干。

书在这里也给了真解释,是全书第二处值得完整留住的地方8:

双编码器交叉编码器
怎么算把问题和文档各自单独变成一串数,再比远近把问题和候选文档拼在一起喂进模型,直接吐一个相关分
能不能预先算 —— 全库的向量可以离线算好、存起来不能 —— 分数依赖于「这个问题 + 这篇文档」这一对
快慢快。查询时只算一次(问题的向量)慢。有几条候选就要跑几次模型
准不准一般更准 —— 它看得到两者之间的完整交互
用在哪全库只用在前几条候选上
百万条文档:
双编码器 离线:算 100 万次(一次性) 查询时:算 1 次 + 查索引
交叉编码器 离线:无法预先算 查询时:候选几条就算几次

图说:所以顺序只能是「先用便宜的把范围缩到几条,再用贵的把这几条排准」。
倒过来做在算力上不成立——那等于每查一次就把全库跑一遍模型。

这就是第 1 节那张图「粗一遍、再细一遍」的全部理由。

换一批语料,同一个现象

书在第 6 章还有一个重排配方,语料完全不同:四段话,分别讲人工智能是什么、 机器学习(让程序从数据里自己总结规律,而不是由人把规则一条条写死)是什么、 以及这些技术都用在哪儿。

第四段讲的是深度学习:机器学习里的一支,特点是把很多层堆起来一起学。

(它堆的那种结构叫神经网络 —— 一大堆简单零件按层排开、中间装着大量可以调的数; 第 01 章说的那台机器,内部就是这个。)

查询是「机器学习有哪些应用」。它的价值在于重排真的改了名次9:

名次按意思找排出来的重排之后重排分
1Applications of ML and AI are diverse同左9.0629
2Machine Learning (ML) is a subset of AI同左7.0553
3Artificial Intelligence (AI) is the simulation…Deep Learning is a specialized branch6.0113
4Deep Learning is a specialized branchArtificial Intelligence (AI) is the simulation…1.7071

那条泛泛讲 AI 定义的文档,按意思找把它排第 3,重排把它踢到最后,分数只有 1.7 —— 和前三名差了一个数量级。 它输在哪?它讲的是 AI 是什么,而问题问的是 ML 有哪些应用。 按意思找看不出这个区别(两段话都在讲 AI 领域),交叉编码器看得出。

7. 把问题写长一点,分数涨了不等于找得更准

回到第 ④ 步那个「大成功」。

现象:三个分数全涨了

扩展前: 0.3682 / 0.0452 / -0.0133 三者拉得很开:第一名是第二名的 8 倍
扩展后: 0.5879 / 0.4707 / 0.3435 三者挤在一起:第一名只比第二名高 25%

图说:分数涨了,可**名次之间的差距缩小了**。

为什么会这样

因为拼上去的三个词是「document matching / query understanding / retrieval」—— 它们把问题整个拉向了「检索」这个大话题。

原问题问的是: RAG 怎么减少胡编? ← 一个很具体的问题
扩展后问的是: RAG 怎么减少胡编 + 检索 + 查询理解 + 文档匹配 ← 一个泛化的话题

于是库里**每一篇讲检索的文档**都变得更像这个问题了——
包括那些和「减少胡编」毫无关系的。

图说:分数普遍升高的代价是**区分度下降**。
你要的不是「谁的分高」,是「谁明显比别人高」。

判据

看名次之间的间距,不看绝对分数。 扩展之后第一名和第二名从差 8 倍变成差 25%, 这是变差了,不是变好了。

顺带:这个配方的输出有一处对不上

它按词面找那一路的三个分数,和上一个配方一字不差(1.2374 / 0.0000 / 0.0000)。 可它喂进去的是加了 retrieval 这个词的新问题,而 retrieval 在好几篇文档里都出现过。

判断(我们的,不是书里的): 这一处的输出是从上一个配方抄过来的,不是跑出来的。 依据:BM25 的分数由查询里的每个词分别贡献,查询多了一个在多篇文档里出现的词, 那些文档的分数不可能一点不变,尤其是原本为 0.0000 的两条 —— 其中一条正含 retrieval如果错,会错在: 如果 retrieval 这个词恰好在全部六篇里都出现, 那么罕见词加权会把它的贡献压到接近 0,分数看起来「几乎不变」是可能的。 但那也只是「几乎」,不会一位小数都不差。

8. 一个被用错的名字:「层级检索」

第 ⑤⑥ 步那个配方,书给它的标题是层级检索

而书自己在同一节给的定义是对的: 层级检索指的是按粒度分层 —— 先定位到相关的大单元(文档、章节),再往下钻到小单元(段落)10

可代码里只有一层语料、一次粗排、一次重排,和第 6 章那个重排配方是同一件事。

书里那个配方做的「层级检索」的标准含义
分几层一层至少两层:文档 → 段落
第一步找什么直接找段落先找出相关的整篇文档
第二步做什么对同一批段落重打分在选中的文档内部再找段落

出门撞见这个词时按标准含义理解。 而书里那个配方,正确的名字是「粗排 + 精排」。

9. 作者的判断与证据

书里给了证据的:

说法证据
BM25 靠词频饱和 + 罕见词加权 + 长度归一打分书里那段解释是全书唯一一次讲清算法内部
按词面找抓不住深层语义配方 92 的第二、三名并列 0.0000
两路分数各自缩放再加权是可行的配方 93 的三个融合分
交叉编码器比双编码器准配方 96 的 +8.4985 对 −11.34 一线;配方 65 的 9.0629 对 1.7071,而且名次真的变了
交叉编码器无法预先算,所以只用在前 k 条上书里那段分工说明

作者只是断言、没有给证据的:

说法缺什么
那个配方叫「层级检索」代码里只有一层,和它自己给的定义打架
查询扩展「能改善检索」只给了分数普遍升高,没有比较过名次对不对 —— 而分数升高恰恰伴随区分度下降
混合检索「能同时兼顾找得准和找得全」全书没有量过这两件事中的任何一件(怎么量,第 12 章讲)
那个配方的按词面找结果输出与代码对不上,分数一位不差地重复了上一个配方

10. 边界与局限

  • 书里那个融合配方印出来的代码跑不出印出来的结果: 演示区里两个 for 循环被注释掉了,而循环体里的打印还留着并且缩进错位 —— 照这段代码跑,后两节各只会重复打印一次上一节的最后一条。
  • RRF 一个字没提。 这是多路融合最通用的做法。
  • 重排分数没有上下限,也不是「有多大把握」那种数,书没说。 读者很容易把 8.4985 当成「84.985% 的把握」。
  • 没有一处说明该取前几条做重排。 取 5 条还是 50 条,代价和收益完全不同。
  • 这一章后半段悄悄换了嵌入模型(从 384 个数的那个换成 768 个数的那个),书没有交代。
  • 书把「按意思 + 按标签过滤」也叫过混合检索,同一个词在同一本书里两个意思。

11. 可带走的

全章那条走查,一行写完:

同一个问题「How does RAG reduce hallucinations?」→ 按意思找 0.3682 / 0.0452 / −0.0133 → 按词面找 1.2374 / 0.0000 / 0.0000(后两名并列,名次随机)。

两路各缩到 0 到 1 再各占一半 → 1.0000 / 0.1570 / 0.0949(第一名恒等于 1)→ 把问题写长 → 0.5879 / 0.4707 / 0.3435(分数涨了,区分度反而降了)。

(换语料)粗排前五混进烹饪和旅行 → 重排 → +8.4985 对 −11.34 一线

  1. 两条路的死角互补: 按意思找怕型号和罕见词,按词面找怕换个说法;
  2. 稠密和稀疏这两个名字来自表示的形状,不是效果好坏;
  3. BM25 比数词频多做三件事: 词频饱和、罕见词加权、按文档长度扣分;
  4. 分数并列就意味着名次是随机的 —— 按词面找那一路一地都是并列的 0.0000;
  5. 两路分数量纲不同,不能直接加;书里做对的一次是各自缩到 0 到 1 再加权;
  6. min-max 缩放的副作用:同一条在两路都第一时,融合分必然恰好 1.0000 —— 那不是置信度;
  7. 业界更稳的做法是只看名次不看分数(RRF),对量纲免疫,而这本书从未提及;
  8. 双编码器可以离线把全库算好所以快,交叉编码器算不了预先所以只能用在前几条上;
  9. 重排的效果大到不像话: 相关的 +8.4985,四条无关的挤在 −11.34 到 −11.42; 但这些分没有上下限,也不是「有多大把握」那种数;
  10. 把问题写长会让分数普遍升高、区分度下降 —— 判据是看名次之间的间距,不是绝对分数;
  11. 书里那个「层级检索」用错了名字,它其实是「粗排 + 精排」。

12. 原文地图

主题原书章原文位置
稠密检索是什么CHAPTER 9 Effective Search for RAG Systemstext/16-fm-introduction.txt:65(搜「represents both queries and documents as dense vectors」)
BM25 的三件事(全书唯一一次讲清算法)同上text/16-fm-introduction.txt:204(搜「term frequency saturation」)
按意思找:三个分数,第三名是负的同上text/16-fm-introduction.txt:196(搜「score=0.3682」) · text/16-fm-introduction.txt:200(搜「score=-0.0133」)
按词面找:第二三名并列 0.0000同上text/16-fm-introduction.txt:306(搜「score=1.2374」) · text/16-fm-introduction.txt:308(搜「Dense retrieval uses vector embeddings instead of keyword matching」)
融合的算法与 alpha同上text/16-fm-introduction.txt:444(搜「(x - x.min()) / (x.max() - x.min() + 1e-8)」) · text/16-fm-introduction.txt:452(搜「hybrid_scores = alpha * dense_norm + (1 - alpha) * bm25_norm」)
融合结果:第一名恰好 1.0000同上text/16-fm-introduction.txt:514(搜「score=1.0000」) · text/16-fm-introduction.txt:516(搜「score=0.1570」)
查询扩展:词表与扩展后的分数同上text/16-fm-introduction.txt:741(搜「Expanded Terms: document matching, query understanding, retrieval」) · text/16-fm-introduction.txt:755(搜「score=0.5879」)
粗排把烹饪和旅行放进前五同上text/16-fm-introduction.txt:1061(搜「Coarse Retrieval (5 results)」) · text/16-fm-introduction.txt:1065(搜「Cooking recipes often require precise measurements and timing.」)
重排:+8.4985 对 −11.34 一线同上text/16-fm-introduction.txt:1075(搜「score=8.4985」) · text/16-fm-introduction.txt:1083(搜「score=-11.4207」)
「层级检索」的定义(和代码打架)同上text/16-fm-introduction.txt:928(搜「documents, sections, and passages」)
双编码器 vs 交叉编码器的分工CHAPTER 6 Efficient Retrieval from Vector Storetext/13-fm-introduction.txt:566(搜「cross-encoders take the query and candidate passage together」) · text/13-fm-introduction.txt:568(搜「they are typically applied only to the top-k retrieved candidates」)
另一批语料上的重排:名次真的变了同上text/13-fm-introduction.txt:756(搜「Initial FAISS Results」) · text/13-fm-introduction.txt:776(搜「Score: 9.0629」) · text/13-fm-introduction.txt:788(搜「Score: 1.7071」)

Footnotes

  1. 出处:「CHAPTER 9 Effective Search for RAG Systems」第 65 段(text/16-fm-introduction.txt:65,搜「represents both queries and documents as dense vectors」)与第 204 段(text/16-fm-introduction.txt:204,搜「BM25 is a classic sparse retrieval method」)。补充(不在书里,来自通用知识): 「稠密」「稀疏」这两个名字来自各自表示的形状 —— 前者是几百个几乎全非零的小数,后者概念上是一份词表长度的表、绝大多数位置是 0。书用了这两个词,但没有解释名字的由来。

  2. 出处:同章第 204 段(text/16-fm-introduction.txt:204,搜「term frequency saturation」)。原文完整地列了三件事:词频饱和(防止重复词分数无限涨)、逆文档频率(罕见且有区分力的词权重更高)、按文档长度归一(免得长文档天然占便宜);并说它「只看表面的关键词重合,抓不住深层语义」。 2

  3. 出处:同章第 444 段(text/16-fm-introduction.txt:444,搜「(x - x.min()) / (x.max() - x.min() + 1e-8)」)与第 452 段(text/16-fm-introduction.txt:452,搜「hybrid_scores = alpha * dense_norm + (1 - alpha) * bm25_norm」);输出在第 514 段(text/16-fm-introduction.txt:514,搜「score=1.0000」)。alpha 的含义写在代码的文档字符串里(text/16-fm-introduction.txt:430,搜「alpha=0 → BM25 only, alpha=1 → Dense only」)。

  4. 出处:同章第 595 段(text/16-fm-introduction.txt:595,搜「EXPANSION_CANDIDATES」)—— 这是一张写死的十五个候选词的表;挑出来的三个词与扩展后的分数在第 741 段(text/16-fm-introduction.txt:741,搜「Expanded Terms: document matching, query understanding, retrieval」)与第 755 段(text/16-fm-introduction.txt:755,搜「score=0.5879」)。

  5. 出处:同章第 1061 段(text/16-fm-introduction.txt:1061,搜「Coarse Retrieval (5 results)」)、第 1065 段(text/16-fm-introduction.txt:1065,搜「Cooking recipes often require precise measurements and timing.」)、第 1075 段(text/16-fm-introduction.txt:1075,搜「score=8.4985」)、第 1083 段(text/16-fm-introduction.txt:1083,搜「score=-11.4207」)。七条语料写在第 974–994 段(text/16-fm-introduction.txt:991,搜「Traveling to new countries helps you learn about culture and」)。

  6. 补充(不在书里,依据我们的 frontier 书架):重排模型在只输出一个分数时,那个分数是没有上下限的原始打分,不是概率;要变成 0 到 1 的数需要另外套一层压缩函数。所以跨模型比绝对值没有意义。 依据: shelf=ai-frontier-reference/sentence-transformers#04-cross-encoder.md @652c8e8a70b9bef7161405ac7e6d8f3b2a828aed 事实=文档写着「num_labels=1 的分数是 logit,不是概率。没套 Sigmoid 时分数可正可负(如 8.4/−4.3),跨模型比绝对值没有意义」。

  7. 补充(不在书里,依据我们的 agent 书架):当两路分数量纲不可比时,业界的标准做法是丢掉分数只看名次 —— 每一路里第 r 名贡献 1/(r+1) 分,同一篇文档在各路的贡献相加。这个办法叫 Reciprocal Rank Fusion(RRF)。 依据: shelf=ai-agent-reference/txtai#04-search-and-fusion.md @20f818f72cacbdc7ea01912788a4b988db029c5e 事实=文档给出一张对照表:「未归一化的原始 BM25 分数量纲乱、范围不定 → 用 RRF,不信任分数绝对值,只信排名;已缩到 0~1 的两路可比 → 用凸组合直接加权求和」,并说融合策略由分数是否归一化自动选定。

  8. 出处:「CHAPTER 6 Efficient Retrieval from Vector Store」第 566 段(text/13-fm-introduction.txt:566,搜「cross-encoders take the query and candidate passage together」)与第 568 段(text/13-fm-introduction.txt:568,搜「they are typically applied only to the top-k retrieved candidates」)。原文说交叉编码器「建模了查询与文本之间的完整交互」,所以排序更准,但计算更重。

  9. 出处:同章第 756 段(text/13-fm-introduction.txt:756,搜「Initial FAISS Results」)、第 776 段(text/13-fm-introduction.txt:776,搜「Score: 9.0629」)、第 788 段(text/13-fm-introduction.txt:788,搜「Score: 1.7071」)。用的重排模型写在第 674 段(text/13-fm-introduction.txt:674,搜「cross-encoder/ms-marco-MiniLM-L-6-v2」),查询在第 656 段(text/13-fm-introduction.txt:656,搜「What are the applications of machine learning?」)。

  10. 出处:「CHAPTER 9 Effective Search for RAG Systems」第 928 段(text/16-fm-introduction.txt:928,搜「documents, sections, and passages」)。原文的定义是:先定位最相关的高层单元(文档或章节),再往下缩到更细的片段。而配方的代码里只有一层语料(text/16-fm-introduction.txt:1004,搜「def coarse_retrieve(query, top_k=5)」)。