搜索的三级火箭:召回、粗排、精排,一次搜索背后到底发生了什么
上周有个做后端的朋友问我:"搜索不就是匹配吗?用户输入一个词,去库里把含这个词的都找出来,按相关性排个序,完事。为什么大厂招聘动不动就要'召回、粗排、精排全链路',搞得跟造火箭一样?"
我反问他:"你们公司招人,简历来了一万份,HR 是不是每一份都让 CTO 亲自面一轮?"
他笑了:"那不可能,CTO 一天面八个就累死了。先用关键词筛一遍,再让组长初面,最后才轮到 CTO 终面。"
"那你已经懂了搜索的全部秘密。"我说。
你每天在淘宝搜"跑鞋"、在抖音刷视频、在大众点评找餐厅——背后的库里躺着上亿条候选。而留给整个系统的时间,通常只有 几十到一百毫秒。在这么短的时间里,从一亿里挑出最好的那十个给你,靠"一个模型算到底"是绝对不可能的。
真正的做法,是把这件事拆成一条流水线:先海选、再初筛、最后终面。这三步,工业界分别叫它召回(Matching)、粗排(Coarse Ranking)、精排(Fine Ranking)。它们不是三个并列的算法,而是一支"三级火箭"——每一级解决的问题、用的武器、追求的指标,完全不同。
这篇文章,我想带你把这支火箭拆开看。看完你会明白:为什么搜索系统宁可"分三次算",也不肯"一次算到底"。
一、搜索为什么不能"一步到位"?
先算一笔账。
假设你的物料库有 1 亿条内容。你手里有一个非常聪明的精排模型,它判断"这条内容用户会不会喜欢"特别准,但代价是——给一条内容打一次分,要算几百万次浮点运算,耗时大约 1 毫秒。
那么,给 1 亿条全部打一遍分,需要多久?答案是 10 万秒,差不多一天半。
而用户能忍受的等待,是 100 毫秒。你超了 100 万倍。
这就是搜索系统最底层的矛盾:"准"和"快"是一对天生的冤家。模型越准,往往越慢;越快的模型,又往往不够准。你想要又准又快,物理上做不到。
那怎么办?人类社会早就给出了答案——分层漏斗。海选阶段不追求精准,只追求"别把好苗子刷掉",用最便宜的方式把一亿压到几千;越往后候选越少,才越舍得上"贵"模型精雕细琢。
你看这个漏斗,每往下一层,候选数量砍掉一到两个数量级,而单条候选能分到的算力反而翻倍上涨。这不是浪费,这是把昂贵的计算精准地花在刀刃上。
所以,搜索的本质,是在"又快又准"这道做不到的难题面前,玩了一手分期付款:先用便宜的方式付个首付(召回),再慢慢加码(粗排、精排),把算力花得越来越值。
那这三级火箭,每一级具体是怎么烧的?我们从第一级——召回——开始拆。
二、召回:怎么从一亿里捞出几千个,还不能漏?
召回是整条链路的第一关,也是最被低估的一关。
它的任务听起来简单:从一亿条里,挑出几千条"可能相关"的,交给后面。但这里藏着一个反直觉的点——召回追求的不是"准",是"全"。
为什么?你想想:如果一条用户真正想要的内容,在召回这一步就被漏掉了,那后面粗排、精排再聪明也没用——它压根没机会看到这条。召回漏掉的,整条链路就永远找不回来了。
所以召回的 KPI 是"召回率"(别把好东西漏掉),而不是"精确率"。它宁可多捞回来一些不太相关的(反正后面会过滤),也不能漏掉那个对的。
办法一:倒排索引——最古老也最可靠的"文字匹配"
最经典的召回,是基于倒排索引(Inverted Index)的文本匹配。这也是 Elasticsearch、Lucene 这些搜索引擎的看家本领。
打个比方:一本书后面的"索引页",告诉你"区块链"这个词出现在第 3、17、42 页。倒排索引就是给全网内容建了这么一张超大索引表:每个词,对应一个"包含这个词的文档列表"。用户搜"跑鞋",系统不用遍历一亿条,直接查表,瞬间拿到所有含"跑鞋"的文档。
光匹配上还不够,得知道"匹配得有多好"。经典的打分函数是 BM25,它的直觉很朴素:一个词在某篇文档里出现得越多,这篇越相关;但如果这个词在全网到处都是(比如"的""你"),那它就不值钱。越稀有、越扎堆的词,权重越高。
倒排索引强在精确、可解释、快。但它有个致命软肋:它只认字面,不懂语义。你搜"番茄炒蛋怎么做",一篇标题写着"西红柿炒鸡蛋教程"的文章,一个字都没对上,就被它无情漏掉了。可这明明是最该出现的结果。
这个"词不对、意思对"的难题,逼出了召回的第二种武器。
办法二:向量召回——让"意思相近"的浮上来
既然字面匹配不行,那就把"意思"也变成可以计算的东西。这就是向量召回(Embedding-based Retrieval)的思路。
核心想法是:用一个模型,把每条内容、每个查询,都映射成一个高维空间里的向量(embedding)。训练的目标是——让"语义相近"的东西,在这个空间里离得近。于是"番茄炒蛋"和"西红柿炒鸡蛋"两个向量几乎重叠,哪怕它们一个字都不一样。
这样,召回就从"查字典"变成了"找最近的邻居":把用户 query 变成一个向量,然后在亿级向量里找离它最近的几千个。
而这里,藏着整个工业界召回的"题眼"——双塔模型(Two-Tower Model)。它脱胎于微软 2013 年的 DSSM,如今是 Google、Facebook、YouTube 召回的标配。
双塔的精妙,全在那个"塔"字上。用户走一座塔,物料走另一座塔,两座塔在最后一刻才相遇——只用一个简单的向量内积算相似度。
这个"分开"的设计,带来一个杀手级好处:物料向量可以提前离线算好。一亿条物料的向量,今天夜里慢慢算完,存进索引。用户来了,我只需要实时算一个用户向量,剩下的就是在亿级向量库里"查最近邻"。
"查最近邻"还是太慢?工业界用 ANN(近似最近邻)解决——以 HNSW、IVF-PQ 这类算法为代表,用 FAISS 这样的库实现。它的哲学是:我不保证给你找到"绝对最近"的那个,但能在毫秒级给你找到"几乎最近"的那几千个。用一点点精度,换几个数量级的速度。在召回这一层,这笔交易太划算了。
办法三:多路召回——一个网捞不全,那就撒好几张网
讲到这你可能会想:那到底用倒排还是用向量?
答案是——全都要。工业界的真实做法叫多路召回(Multi-channel Retrieval):同时撒好几张网,每张网逻辑不同、各有侧重,最后把各路结果合并去重。
- 文本召回:倒排 + BM25,保字面精确匹配
- 向量召回:双塔 + ANN,补语义相关
- 协同过滤召回:买了这个的人还买了什么,抓行为关联
- 热门/兜底召回:万一前面都没召回到,至少给点热门的,别开天窗
为什么要这么多路?因为没有任何单一信号能覆盖所有场景。新内容没有行为数据,协同过滤抓瞎,得靠文本和向量;冷启动用户没有历史,得靠热门兜底。多路召回,就是用"互补的视角"去织一张更密的网,最大限度地不漏。
到这里,第一级火箭烧完了:我们用极低的成本,从一亿压到了几千。但"几千"对那个动辄 1 毫秒一条的精排模型来说,还是太多了——几千毫秒,用户早就划走了。
于是,中间这一层登场了。
三、粗排:既然有精排,为什么还要中间这一层?
粗排是三层里最"尴尬"、也最容易被外行忽略的一层。
很多人第一反应是:召回给几千条,精排不就直接挑吗?要粗排干嘛,多此一举?
问题就出在那个"几千"上。召回出来的几千条,精排一条条算下去要好几秒,扛不住。而召回给的分数又太粗糙(倒排是 BM25,向量是内积),粗到不足以直接定生死。中间缺了一个"承上启下"的环节——比召回更懂好坏,又比精排便宜得多。
这就是粗排的定位:用精排十分之一的成本,干一件"把几千砍到几百"的脏活累活。
粗排的两难:又要像精排,又要像召回
粗排最早的做法,几乎就是召回双塔的"加强版"——还是用户塔、物料塔分开,物料向量离线算好,在线只做内积。便宜、快。
但双塔有个先天缺陷:用户和物料到最后一刻才用一个内积相遇,中间完全不交互。这意味着它学不到"用户A特别在意价格、用户B特别在意品牌"这种交叉特征。表达能力,天花板很低。
于是粗排开始往"更像精排"的方向进化。阿里提出的 COLD 是个标志性方案:它不再死守双塔,而是允许用户和物料特征做一定交叉,同时用特征筛选(只留最有用的特征)和工程优化(低精度计算、并行)把成本压住。一句话——在"算力预算"这条紧箍咒下,尽可能换更强的表达力。
蒸馏:让粗排"偷师"精排
粗排还有一招特别漂亮,叫知识蒸馏(Knowledge Distillation)。
逻辑是这样:精排是个"学霸",懂得多但跑得慢;粗排是个"学渣",跑得快但懂得少。那能不能让学霸把它的判断"教"给学渣?
具体做法是——拿精排模型当老师(teacher),让它给海量样本打分,再让粗排模型(student)去学着模仿这些分数。爱奇艺在优化短视频粗排时就是这么干的:以精排为 teacher 指导粗排训练,最后得到一个结构简单、参数量小,但判断力不弱的粗排模型。
这就好比,学渣不用自己从头啃完所有教材,只要把学霸的错题本和答题思路抄熟,考试也能拿个不错的分。蒸馏的本质,是把"贵模型"的智慧,廉价地复制进"便宜模型"里。
粗排这一级烧完,几千条变成了几百条。现在,终于轮到那个最贵、最聪明的角色登场了。
四、精排:几百个里排出顺序,凭什么这么"贵"?
精排是整条链路的"终面官"。前面几层千辛万苦的筛选,都是为了把最该被认真对待的几百条,端到精排面前。
到了这一层,候选已经不多,系统终于舍得花钱了。精排可以上最复杂的模型、最丰富的特征、最精细的交叉,把每一条候选都掰开揉碎地审视一遍。
那精排到底"精"在哪?我认为,是三件召回粗排做不到的事。
精在一:把特征"交叉"起来看
还记得双塔的硬伤吗?用户和物料最后才用内积相遇,学不到交叉。精排彻底放开了这个限制——它把用户特征、物料特征、上下文特征全部塞进同一个模型里充分交互。
为什么交叉这么重要?举个例子:单看"用户是男性"没什么信息量,单看"这是条口红"也没什么。但"男性 × 口红 × 临近情人节"三个特征一交叉,立刻就有了强烈的购买信号。真正值钱的信息,往往不在单个特征里,而在特征的组合里。
从 Wide&Deep、DeepFM 到 DCN,这一系列精排模型的核心进化,说白了就是一句话:用越来越聪明的方式,自动地、高效地做特征交叉。
精在二:盯着"这一次"的兴趣,而不是"平均"的你
精排还有一个杀手锏,叫用户行为序列建模,代表作是阿里的 DIN(深度兴趣网络)。
传统模型描述一个用户,是用一个固定的向量——仿佛你的兴趣是一成不变的"平均值"。但 DIN 发现:你的兴趣是随当下看的东西而变的。
它的机制叫注意力(Attention):当系统要判断"你会不会喜欢这条裙子"时,它会回头翻你的历史行为,然后重点关注你过去看过的那些女装、配饰,而忽略你买过的电钻、手机壳。同一个用户,面对裙子和面对电钻,被"激活"的历史是完全不同的。
这就像一个顶级销售:他不会用一套说辞应付所有人,而是先瞄一眼你刚才在看什么,再决定跟你聊什么。精排预测的不是"你这个人喜欢什么",而是"此时此刻的你,对这一条会不会动心"。
精在三:同时伺候好几个目标
最后,也是精排最现实的一层复杂度——多目标(Multi-task)。
真实业务从来不是只看"会不会点击"。电商要同时优化点击率、转化率、客单价;短视频要同时优化点击、完播、点赞、评论、转发。这些目标之间还经常打架——标题党能拉高点击,却拉低完播和满意度。
怎么办?工业界的主流方案是 Google 提出的 MMOE:用一组共享的"专家网络"提取底层特征,再为每个目标配一个"门控",让不同目标各取所需。一个模型,同时吐出"点击概率""完播概率""点赞概率"好几个分数。
但模型预测出一堆分数后,最终只能排一个序。这就需要多目标融合——把好几个预估值,合成一个最终的排序分。最常见的是一个加权公式:
# 把精排预测的多个目标分数,融合成一个最终排序分
final_score = (
p_ctr ** alpha # 点击概率
* p_finish ** beta # 完播概率
* (1 + p_like * gamma) # 点赞概率
* (1 + value * delta) # 商业价值/时长
)
# 关键在那几个指数 alpha/beta/gamma:它们就是业务的"价值观"
# 调大 beta,系统就更偏爱"耐看"的内容;调大 ctr 权重,就更偏爱"吸睛"的
别小看这个公式。那几个权重,本质上是把"产品想要什么"翻译成了数学。调一调指数,整个平台推什么、不推什么的"性格"就变了。这也是为什么大厂里专门有团队,常年就为这几个超参数做 A/B 实验、甚至用模型去自动学习这些权重(让每个用户的权重都能个性化)。
讲完三层,你可能以为故事结束了。但真正让全链路出问题的,往往不是某一层不够强,而是它们之间"没对上暗号"。
五、三层怎么配合,才不会各唱各的调?
我见过太多团队,把召回、粗排、精排各自当成独立项目,各自优化各自的指标,结果整体效果反而越调越差。
问题出在哪?出在它们是一条接力赛,而不是三个独立短跑。
接力赛最怕什么?不是某一棒跑得慢,而是交接棒时掉棒。召回觉得好的,粗排看不上;粗排选上来的,精排成片毙掉——每一层都在自嗨,每一次交接都在损耗。这就是全链路最隐蔽、也最致命的敌人:目标不一致。
所以现代搜索系统有一条铁律:三层的优化目标,必须尽量对齐。如果精排升级成了 MMOE 多目标,粗排最好也跟着上多目标;精排开始重视"完播率",粗排的蒸馏老师就得把这个偏好传下去。让三层"想到一块儿去",比单独把任何一层做强,回报都更高。
说个行业里的"呼吸点"花絮。早些年,很多公司其实没有粗排这一层——召回直接怼精排。随着模型越来越重、候选越来越多,精排被几千条候选活活拖垮,粗排才作为"减压阀"被硬生生造了出来。
有意思的是,最近两年又出现了反向的声音:随着算力和工程优化(比如更快的推理、更省的特征)的进步,有些团队开始尝试"召回-精排"两段式,把粗排砍掉,或者反过来把粗排做得越来越像精排,让边界变得模糊。
这件事我的判断是这样:分几层,从来不是教条,而是当下算力、候选规模、业务复杂度之间的一道动态平衡题。候选少、算力足,就少分层;候选爆炸、模型变重,就多加一层缓冲。三级、四级(再加个"重排")甚至两级,都只是这道平衡题在不同约束下的解。
所以别背"搜索必须分三层"这种死结论。要理解的是它背后那套"用分层来化解快与准矛盾"的思想——这套思想,比具体分几层重要一万倍。
六、总结:一次搜索,是一场精心设计的"分期付款"
我们从朋友那个"搜索不就是匹配吗"的问题出发,一路拆到了三级火箭的内部。回头看,整件事的逻辑其实异常清晰。
- 召回——比谁漏得少:用倒排索引、双塔+ANN、多路召回,以极低成本从亿级压到千级。它的命门是"全",漏掉的永远找不回。
- 粗排——比谁折中得好:用轻量模型 + 知识蒸馏,从千级砍到百级。它存在的唯一意义,是当好精排的"减压阀",并和精排保持排序一致。
- 精排——比谁排得准:用特征交叉、行为序列、多目标融合,在百级候选里精雕细琢。它预测的不是"你喜欢什么",而是"此刻的你会怎么做"。
- 全链路——比谁配合得好:三层是接力赛不是三场短跑,目标一致性和样本偏差,才是决定上限的隐形战场。
我的看法:很多人学搜索/推荐,一上来就扎进某个模型的公式里——双塔怎么算损失、DIN 的注意力怎么推导。这没错,但容易只见树木不见森林。真正的高手,脑子里装的是那张漏斗图:他清楚每一层在"快与准"的天平上站在哪、为谁服务、被谁约束。理解了"为什么分层",你再去看任何一个具体模型,都会知道它是为了解决漏斗里哪一格的问题而生的。
下一步建议:
- 下次你在淘宝或抖音搜东西,刻意观察一下结果:哪些是"字面精确"被召回的,哪些明显是"语义相关"补进来的,哪些又是"猜你喜欢"的多目标产物。把抽象的三层,对到你每天用的真实产品上。
- 如果你想动手,用 FAISS 搭一个最小的向量召回 demo——把几万条文本编码成向量、建 HNSW 索引、做一次最近邻查询。亲手跑通一次,你对"召回"的理解会比读十篇文章都深。
说到底,一次搜索的背后,不是一个无所不能的超级模型,而是一条懂得"把钱花在刀刃上"的精明流水线。它先用便宜的方式付首付,再一步步加码精算——用分期付款的智慧,解开了"又快又准"这道本来无解的题。
参考资料
- Deep Learning to Rank in Industrial Search Engines, Recommender Systems, and Online Advertising: An Overview and New Perspectives (TOIS)
- A Comprehensive Survey on Retrieval Methods in Recommender Systems (arXiv 2407.21022)
- The Two-Tower Model for Recommendation Systems: A Deep Dive — Shaped
- 如何提升链路目标一致性?爱奇艺短视频推荐之粗排模型优化历程 — InfoQ
- 推荐系统:精排多目标融合与超参数学习方法 — 知乎
- 推荐系统技术演进趋势:从召回到排序再到重排 — 卢明冬的博客
- Awesome Deep Learning Papers for Search / Recommendation / Advertising — GitHub