搜索相关性排序这件事很多团队都经历过一个相似的阶段一开始觉得把关键词匹配做好就够了后来发现用户搜苹果手机壳返回一堆苹果水果的百科再后来加了点击率、销量这些信号又发现热门商品把长尾精准需求全压死了。问题的根子在于相关性从来不是一个单点问题而是一条链路——召回负责别漏排序负责别错两者缺一不可。这篇文章就围绕召回加排序的双阶段方法把搜索相关性优化这件事拆开讲透从为什么要分两阶段、召回怎么保覆盖、排序怎么定优劣到特征工程、冷启动、线上调优的实操细节尽量给到能直接抄作业的思路。不管你是刚接手搜索业务的工程师还是想系统梳理相关性链路的算法同学都能从中找到可落地的部分。1. 为什么搜索相关性必须拆成召回和排序两阶段1.1 单阶段打分的算力天花板先算一笔账。假设一个中等规模的电商平台商品库有 500 万 SKU用户每次搜索请求如果要对全库做一次精细打分哪怕单次打分只花 0.1 毫秒500 万次就是 500 秒。这显然不可能。真实场景里一个搜索请求的响应时间预算通常在 100 到 300 毫秒之间留给相关性计算的时间可能只有几十毫秒。所以工程上必须做漏斗先用一个轻量的召回层把候选集从百万级压到几百到几千级再用一个重量的排序层对这几百个候选做精细打分。这就是双阶段方法存在的根本原因——不是算法偏好而是算力约束下的必然选择。提示召回和排序的分工本质上是粗筛和精排的关系。召回追求的是不漏掉可能相关的结果排序追求的是把最相关的排到最前面。两者的优化目标不同评价指标也不同。1.2 召回和排序的目标函数差异很多人把召回和排序当成同一件事的两个步骤其实它们的目标函数是冲突的。召回阶段如果追求高准确率就会漏掉大量长尾相关结果排序阶段如果追求高召回又会把大量弱相关结果排到前面。正确的做法是让召回阶段尽量保覆盖允许一定的噪声把宁可错杀一千不可放过一个作为原则排序阶段则要在这个候选集里做精细区分把真正相关的顶上去。用一个生活化的类比召回像是招聘时的简历初筛HR 不会因为一份简历不够完美就扔掉而是尽量把可能合适的人都放进面试池排序像是面试打分要在这些人里排出谁最合适。如果初筛就把人筛没了面试官再厉害也没用。1.3 两阶段方法在工业界的普遍性从公开的技术分享来看主流搜索系统基本都是双阶段甚至多阶段架构。召回层常见的有倒排索引召回、向量召回、图召回等排序层则从早期的 LR 模型演进到 GBDT、DeepFM、DIN、双塔精排等。阶段越多每一层的候选集越小模型越复杂算力分配越合理。这里要强调一个容易被忽略的点双阶段不是简单的先粗后细而是每一阶段都有自己的特征体系和模型目标。召回层的特征通常偏轻量比如文本匹配度、类目匹配、基础热度排序层的特征则要丰富得多包括用户历史行为、上下文、商品侧质量分、交叉特征等。把排序层的特征硬塞到召回层只会拖慢召回速度得不偿失。2. 召回阶段怎么做到不漏掉可能相关的结果2.1 倒排索引召回的基本原理与调优倒排索引是文本召回最经典的手段。它的核心思想是建立词到文档的映射用户查询分词后直接查每个词对应的文档列表再做交集或并集。听起来简单但实际调优空间很大。第一个关键点是分词粒度。以中文为例苹果手机壳如果切成苹果/手机/壳召回范围会很大可能把水果苹果相关的商品也召回来如果切成苹果手机壳整体召回又太窄。常见的做法是多粒度索引既建细粒度词索引也建短语索引查询时根据查询长度和意图动态选择。第二个关键点是同义词和扩展词。用户搜笔记本电脑和笔记本计算机应该召回同一批结果。这需要维护同义词词典并在索引和查询两侧都做归一化。注意同义词扩展要控制范围扩得太猛会引入大量噪声反而拉低排序层的效率。第三个关键点是字段权重。标题、类目、品牌、属性这些字段的重要性不同倒排索引可以给不同字段设置不同权重查询时按字段加权计算基础相关性分。这个分数虽然粗糙但作为召回层的排序依据已经够用。2.2 向量召回语义匹配的补充手段倒排索引的短板是字面匹配用户搜适合送女友的生日礼物字面上和浪漫礼盒没有重叠词倒排就召不回来。向量召回就是来解决这个问题的。它的思路是把查询和文档都映射到同一个向量空间用余弦相似度或内积来衡量语义接近程度。向量召回的核心是双塔模型一个塔编码查询一个塔编码文档两边各自算出向量后做相似度计算。双塔的好处是文档向量可以离线算好线上只需要算查询向量然后做近邻搜索速度很快。缺点是查询和文档没有交互语义捕捉能力弱于交叉编码模型。实操中向量召回通常作为倒排召回的补充通道而不是替代。两个通道各召回一批结果合并去重后送入排序层。这样既保住了字面匹配的准确性又补上了语义匹配的覆盖面。注意向量召回的效果高度依赖训练数据。如果训练样本里正样本太少或者负样本采样太随意向量空间会学偏导致召回结果看似语义相近实则不相关。建议至少准备百万级的点击或转化样本并做好难负样本挖掘。2.3 多路召回融合与去重策略真实系统里召回往往是多路的倒排一路、向量一路、热门一路、个性化一路。多路召回的结果需要融合常见做法有两种一种是按路加权打分每路给一个基础分加权求和后取 TopN另一种是分路保底每路各取前 K 个再合并去重。去重是个细节活。同一个商品可能被多路召回去重时不能简单按 ID 去重还要考虑不同路召回的同款不同 SKU 问题。比如用户搜iPhone 15 手机壳不同颜色不同型号的壳可能被不同路召回去重时如果按 SPU 去重会丢掉用户可能想要的特定款式如果按 SKU 去重又可能重复展示。常见做法是在排序层做聚合把同 SPU 的结果聚在一起展示时选一个代表。融合后的候选集大小也要控制。太小排序层没有发挥空间太大排序耗时增加。一般控制在 500 到 2000 之间比较合理具体取决于排序模型的复杂度和响应时间预算。3. 排序阶段把真正相关的结果顶上去3.1 排序模型的特征体系怎么搭排序层的核心是特征。特征搭得好模型效果差不了特征搭得烂换再复杂的模型也白搭。排序特征通常分四类查询侧特征查询长度、查询意图分类、查询词权重、查询是否包含品牌词等。文档侧特征商品标题质量分、类目、品牌、销量、评分、库存状态等。交叉特征查询与标题的文本匹配度、查询词在标题中的位置、查询类目与商品类目的匹配度、查询品牌与商品品牌的匹配度等。用户侧特征用户历史点击类目偏好、价格偏好、品牌偏好、实时行为序列等。交叉特征是相关性排序里最关键的。文本匹配度可以用 BM25、编辑距离、词重叠率等指标衡量类目匹配度可以用层级相似度计算品牌匹配则是布尔特征。这些特征直接决定了这个结果和这个查询到底有多相关。用户侧特征则决定了对这个用户来说哪个相关结果更合适。同一个查询不同用户的偏好不同排序结果也应该不同。这就是个性化排序的价值所在。3.2 从 LR 到深度模型的演进逻辑早期排序模型用 LR优点是简单、可解释、训练快。但 LR 只能学线性关系特征交叉需要人工设计成本高且容易遗漏。后来 GBDT 流行起来它能自动做特征组合效果比 LR 好不少。再后来深度模型兴起DeepFM、DIN、DIEN 这些模型能自动学习高阶特征交叉和用户行为序列效果进一步提升。但要注意模型不是越复杂越好。深度模型需要大量训练数据如果样本量不够容易过拟合线上推理耗时也更高可能拖垮响应时间。很多团队的实际选择是先用 GBDT 打底把特征工程做扎实再逐步引入深度模型做增量。这样风险可控效果也能稳步提升。3.3 排序目标的设定相关性不只是相关排序模型的目标函数直接决定了它学什么。如果只用点击率作为目标模型会偏向那些标题党、图片吸引人但实际不相关的商品如果只用转化率又会偏向低价走量商品忽略相关性。常见的做法是多目标融合相关性分、点击率、转化率、加购率等各占一定权重综合排序。相关性分通常来自人工标注或规则计算作为独立特征输入模型。点击率、转化率则来自用户行为日志。多目标融合时权重设置很关键。相关性权重太低排序会跑偏太高又会压制个性化效果。这个权重需要通过线上 AB 实验反复调整。提示建议把相关性作为硬约束而不是软权重。比如先过滤掉相关性分低于阈值的商品再在剩余商品里按综合分排序。这样能避免完全不相关的结果因为其他信号高而排到前面。4. 特征工程与样本构造中的实操细节4.1 训练样本的标注与采样排序模型的训练样本通常来自用户行为日志曝光未点击作为负样本点击作为正样本转化作为强正样本。但这样构造样本有几个坑。第一个坑是曝光偏差。用户只看到排在前面的结果后面的结果即使相关也没机会被点击直接当负样本会误导模型。解决办法是做位置纠偏或者用 Bandit 算法做探索。第二个坑是样本不平衡。曝光量远大于点击量正负样本比例可能达到 1:100 甚至更极端。需要做负样本降采样或者用 Focal Loss 之类的损失函数来缓解。第三个坑是相关性标注样本稀缺。纯行为样本学出来的是用户喜欢什么而不是什么相关。要提升相关性还需要人工标注一批 query-doc 相关性样本作为补充训练数据。4.2 特征穿越与线上线下一一致性特征穿越是排序模型最隐蔽的 bug 之一。简单说就是训练时用到了未来信息导致离线评估很好线上效果拉胯。比如用商品当天的销量作为特征但训练样本是当天早上的曝光那时候销量还没产生这就是穿越。避免特征穿越的核心原则是训练样本的特征值必须和线上预测时能拿到的特征值一致。具体做法包括特征按时间窗口计算只用历史数据离线特征和线上特征用同一套计算逻辑上线前做特征一致性校验对比离线特征和线上特征的分布差异。线上线下一一致性还体现在特征版本管理上。特征计算逻辑变更后要同步更新离线训练和线上服务否则会出现训练用新特征、线上用旧特征的错位。4.3 冷启动与长尾 query 的处理冷启动分两种新商品冷启动和新 query 冷启动。新商品没有历史行为排序模型给不出高分容易沉底。解决办法是用内容特征兜底比如标题质量、类目匹配、图片质量等让新商品至少能获得基础曝光。同时可以设置探索流量给新商品一定比例的展示机会积累行为数据。长尾 query 的问题是没有足够的历史行为训练个性化模型。这时候要退回到相关性为主把文本匹配度、类目匹配度这些不依赖行为的特征权重调高保证长尾 query 也能返回相关结果。等长尾 query 积累了一定行为数据后再逐步引入个性化特征。5. 线上调优与效果评估的完整链路5.1 离线评估指标的选择与陷阱离线评估常用 NDCG、MAP、MRR 这些指标。NDCG 考虑了排序位置越相关的结果排越前分越高是比较常用的指标。但离线指标有个陷阱它依赖标注数据而标注数据往往只覆盖头部 query长尾 query 的评估不充分。另一个陷阱是离线指标和线上效果不一致。离线 NDCG 提升线上点击率未必提升因为用户行为受很多因素影响不只是相关性。所以离线评估只能作为参考最终还是要看线上 AB 实验。5.2 线上 AB 实验的设计要点AB 实验的关键是控制变量。实验组和对照组只能有一个变量不同否则无法归因。比如要测试新的排序模型那召回逻辑、特征计算、展示样式都要保持一致只换排序模型。实验流量要足够大才能统计显著。一般建议每组至少几万次曝光具体取决于指标波动程度。实验周期要覆盖完整的行为周期比如电商要覆盖工作日和周末避免周期性偏差。评估指标不能只看点击率。相关性优化可能提升点击率但也可能降低转化率如果用户点进去发现不相关。所以要综合看点击率、转化率、跳出率、长尾 query 覆盖率等多个指标。5.3 相关性 badcase 的归因方法线上效果不好时要能快速定位是召回问题还是排序问题。一个实用的方法是分层排查先看 badcase 的 doc 是否在召回集里如果不在就是召回漏了如果在召回集里但排得很后就是排序问题。召回漏了要查是倒排没覆盖还是向量没召回针对性补充召回通道。排序排后了要查是特征缺失还是模型学偏针对性补特征或调样本。这个排查链路要固化下来形成工具才能持续优化。6. 一些踩过坑之后才明白的经验做搜索相关性这些年有几个教训是踩过坑才真正理解的。第一个是不要过早追求模型复杂度。我见过团队一上来就上深度模型结果特征工程一塌糊涂效果还不如 GBDT。特征才是排序的天花板模型只是逼近这个天花板的手段。第二个是召回和排序要联动优化。只优化排序召回漏了的结果永远出不来只优化召回排序排不准用户还是看不到。两个阶段要一起看 badcase一起定优化方向。第三个是相关性没有绝对标准。不同业务、不同场景对相关性的定义不同。电商搜索里用户搜苹果可能想买手机也可能想买水果要看用户历史行为判断意图内容搜索里相关性更多是语义匹配。所以不要照搬别人的方案要根据自己的业务特点定义相关性再设计对应的召回和排序策略。第四个是线上效果要持续监控。搜索相关性不是一次优化就一劳永逸的商品库在变、用户偏好在变、query 分布也在变。要建立日常监控机制发现指标异常及时排查定期做 badcase 复盘才能保持效果稳定。最后分享一个实用技巧在做召回融合时给每路召回设置动态权重而不是固定权重。比如向量召回在长尾 query 上表现好倒排召回在头部 query 上表现好就可以根据 query 的热度动态调整两路权重。这个改动不大但效果提升往往很明显。