资讯详情

资讯详情

Algolia搜索排序机制解析:从匹配到排名的确定性管线

做这个 Algolia 官方博客的中文翻译系列到这一篇已经是第十一期了。前面十期我多半会挑怎么实现某个功能这类能直接落地、看完就能去项目里抄作业的主题但第十一期我特意换了一种口味选了一篇讲搜索排序机制的官方博客。原因很简单最近在好几个项目里看到类似的困惑——索引数据没问题、搜索 API 也调通了但用户总抱怨搜得到的内容排得太靠后。很多人第一反应是往索引里塞更多字段、加筛选条件甚至怀疑这个搜索引擎根本不行。官方博客里那篇《搜索排序机制解析》恰好把为什么某些结果会排在前面这件事讲清楚了。这篇文章会先把原文的核心思路重新梳理成中文再补上原文没有展开、但你在真实项目里几乎一定会碰到的细节和坑。1. 为什么第十一篇要选排序机制这篇博客1.1 官方博客想解决的那种搜索不好用你别看 Algolia 一直标榜自己快其实官方工程团队在博客里反复强调一件事快只是基础搜索体验里真正难的是让对的内容出现在对的位置。如果你只是把数据同步进索引然后原地起飞大概率得到的是一个什么都搜得到、但什么都不太对的结果。这篇官博文章核心想解决的问题是纠正一个常见的错误认知结果排序不是一个模糊的综合评分问题而是一条透明、可解释、可调试的排序管线。很多人觉得排序是玄学因为搜索引擎内部可能有一个黑盒模型在打分你只能靠猜。Algolia 的做法恰恰相反——它把排序拆成一组互相独立的判断条件一条一条往下执行每条只回答一个具体的问题比如这次拼写有没有错查询词命中了几个多个词在文本里离得近不近。这对我做翻译的人来说很有意思。因为英文原文里反复出现deterministic确定性这个词它想表达的是搜索结果不该依赖某个神秘的数学公式而应该能被任何一个人逐条推演出来。你一旦理解这条管线后面所有调优动作都会变得非常具体不再是提高相关性这种空话而是我要调整哪一条判断条件、把它换成什么规则。1.2 翻译时最费功夫的一段相关性不是一个数原文有一句话我记得很清楚相关性不是由某个单一分数决定的而是由记录之间的逐个比较决定的。这句话翻译起来容易但理解起来很容易走偏。我最初也习惯性地把搜索排序想象成一个打分系统每条记录算出 86 分、92 分然后按分数倒序排列。官方文章里的模型完全不是这样。它更像我们相亲时候的筛选流程先看预算是否达标预算内再看有没有共同话题共同话题一样再看居住地远近——每一步都是一个独立的、只有通过/不通过或简单大小比较的关卡而不是把所有因素换算成分数相加。这个设计对外行人来说有点反直觉但对工程者来说极其合理。我用一个生活化的类比来解释假设你在选手机A 款便宜但内存小B 款贵但内存大。如果用综合评分你可能把价格和内存都折算成百分制再加权最后得到一个 87 比 81 的结论。但用关卡制规则就是先看预算预算内留下再看内存内存大的胜出。A 和 B 根本不会走到比内存这一步因为 A 在预算关就被淘汰了。Algolia 的排序机制正是这种关卡制——不匹配查询的记录在匹配阶段就直接出局了根本不会进入后面的排序环节。剩余记录再沿着固定顺序的排序条件互相比较。这个思路带来的好处和原文里面也有几个速度快不需要为每条候选计算复杂分数、可解释任何一条结果的位置都能说清依据、易调试改一个条件就能观察到影响。我现在觉得理解这个模型比记住任何 API 参数都重要。2. 原文拆解Algolia 的排序管线到底怎么工作2.1 匹配阶段与排序阶段的分工原文把整个过程划分成两个阶段这是全文的骨架。第一阶段叫匹配matching搜索引擎在索引里找出所有可能与查询词相关的记录主要靠倒排索引、前缀匹配、拼写容错这些能力。第二阶段叫排序ranking对第一轮留下来的记录按照预设的排序条件逐条比较排出先后顺序。很多人包括刚开始用 Algolia 的我容易把这两个阶段混在一起以为匹配阶段有个相关性打分分数高的排在前面。实际上匹配阶段更接近一个漏斗记录要么命中、要么不命中不命中就立刻离开候选池后面无论你再怎么调整 customRanking 之类的东西它都不会被捞回来。为什么要区分得这么清楚因为这直接影响调优策略。如果你的问题是完全搜不到某条记录那多半在匹配阶段就出局了这时候去折腾排序配置Ranking毫无意义应该去调可搜索属性Searchable Attributes、词干化、同义词规则这类匹配层参数。如果问题是能搜到但排太靠后那才轮到排序阶段上场。原文里用了很大篇幅强调这个先后关系我自己翻译完之后最大的收获就是排查问题之前先确定问题发生在哪个阶段。举个例子一条商品记录可能长这样{ title: RGB 机械键盘, description: 红轴版本适合长时间输入带白色背光, category: 外设, sales_last_30d: 1200, stock: 46 }这里title、description、category如果是可搜索属性那么用户搜机械键盘时记录就靠title里的机械键盘进入候选池。如果用户搜的是茶轴键盘而 description 里写了红轴版本没有茶轴字样那它可能在匹配阶段就过滤掉了——除非你配置同义词把红轴/青轴/茶轴关联起来或者放开部分匹配。2.2 默认排序条件逐条翻译原文花了不少篇幅解释 Algolia 默认的排序条件顺序我在翻译的时候做了一张表它可能比原文的纯文字描述更容易一眼看懂顺序排序条件它实际回答的问题1typo拼写错误更少的结果排在拼写错误更多的结果前面2words命中查询词数量更多的结果放在命中更少的结果前面3proximity多个查询词在文本里离得更近的结果排在离得更远的结果前面4attribute查询词命中了优先级更高的属性时排名更靠前5exact完全精确匹配的结果排在部分匹配的结果前面6custom按业务自定义字段排序如销量、上架时间等这六条就是官方默认的排序管线翻译成大白话就是先看谁错得少再看谁词更全然后看谁词更近接着看谁命中的字段更重要然后看谁匹配得更精确最后才轮到业务指标。我第一次看到这个顺序时很惊讶custom竟然排在最后。这意味着如果你只在索引里填了商品数据不对排序做任何配置那么销量 1200 的商品永远排在销量只有 20 的商品后面——前提是前面的文本相关性条件能够分出胜负。后来我理解了这个设计其实很符合搜索的本质搜索不是卖货而是找东西。用户输入一个查询词他表达的信息需求优先级天然高于你的运营指标。销量再高如果文本相关性差一截默认引擎仍然认为找得准更重要。当然在实际业务里这个默认顺序几乎不可能原样上线这就是后面章节要讨论的。2.3 一次真实查询的排序推演原文后面配了一个很强的示例我也是靠它才彻底搞懂逐条比较的含义。假设用户输入red keyboard候选记录简化成两条记录 Atitle: red mechanical keyboarddescription: red keycaps included记录 Btitle: mechanical keyboarddescription: keyboard with red backlight第一关 typo两条记录的词都没有拼写错误平手。第二关 words两条记录都完整命中了red和keyboard两个词平手。第三关 proximity记录 A 的red和keyboard之间隔着一个mechanical距离为一个词记录 B 的red出现在 description 里而keyboard出现在 title 里同一个属性内没有直接相邻关系距离更远。所以这一关记录 A 胜出。第四关 attribute如果title的搜索权重高于description记录 B 在 title 里命中了keyboard在 description 里命中red记录 A 在 title 里同时命中了两个词。按属性优先级记录 A 也占优。第五关 exact查询词red keyboard在记录 A 的 title 里并不是一个连续短语中间夹了别的词记录 B 的 description 里red backlight也不是精确短语。两条都算不上严格精确匹配。到这里其实已经不需要比了记录 A 在第三关已经胜出。这个推演过程告诉我们Algolia 的排序不是先算分再比较而是一关一关地淘汰。前面的条件一旦分出胜负后面的条件根本不会执行。这也是为什么排序速度快、结果稳定的重要原因。3. 原文没明说但你必须知道的排序和业务的拉扯3.1 默认排序顺序为什么不能直接上线官方博客这篇文章的语气很工程化它只解释了默认怎么排没有太强调默认往往不满足业务诉求。我自己在实际项目里的体感是文本相关性优先的默认排序在内容型网站还好在电商和商业化场景里基本撑不住。原因很简单文本相关性不在乎库存、销量、毛利率、运营位。一个滞销了三年的老款只要标题精准匹配了用户搜索词照样能压过当季爆款排到第一。用户可能只是搜索机械键盘他没有表达任何价格偏好但你的业务目标是卖出更多高毛利商品两者天然打架。解决途径是引入customRanking自定义排序把业务指标插到管线里。比如设定customRanking为sales_last_30d降序那么在文本相关性难分高下的时候销量就会成为决定性因素。注意这个机制不是替换掉文本相关性而是在所有文本相关性条件之后再按业务指标微调——官方术语里的定位非常准确不伤害文本相关性前提下的业务排序。这里给一个建议如果要做电商优先把sales_last_30d、stock、margin、is_promoted这类字段都索引进去并且想清楚到底是谁参与排序。我见过有人把所有数值字段都塞进 customRanking结果排序优先级互相冲突反而乱套。本质上你要回答的是当两条记录在文本上一样相关时我们希望用什么业务规则来决定胜负只选一两个指标最多不要超过三个。3.2 把销量放进排序之后的事故现场光说理论不够我讲一个真实翻过车的情况。某个电商项目运营要求销量优先我照做把sales_last_30d配置成了 customRanking。上线后效果确实好——搜键盘这种泛词的时候爆款都排到了前面。但是很快有人报了一个诡异 Bug搜索某品牌名加红轴时排第一的不是红轴款而是该品牌销量最高的普通款。问题出在哪我拆开查询推演了一遍用户输入的是某品牌 红轴两条记录都完整命中了这两个词typo、words 平手。proximity上普通款在其他位置也是某品牌 红轴相邻不实际上我在字段设计里把品牌和型号分开存储导致普通款的 description 里没有红轴两个字匹配量少。真正让它胜出的是它整体文本相关性和红轴款持平但销量高出太多在 customRanking 这一关直接逆袭。从用户角度这非常荒谬——我明确搜了红轴你却给我一个非红轴的爆款。问题根源是customRanking排在整个管线最后只在文本相关性完全打平的情况下才生效但文本相关性打平的判断并没有那么精细两个词命中、词序接近、字段权重相同就已经平手了它不会理解用户意图是红轴这个语义。想解决这种问题光靠排序配置是做不到的。我最后的处理是用查询规则Query Rules把品牌 红轴这类组合词强制关联到对应商品的过滤器同时给爆款降权才把体验拉回来。这个事故让我学到的一个道理排序只是最后一公里前面的字段设计、同义词、规则配置其实一直在决定排序的条件是否合理。3.3 给运营干预留一个走后门的口子业务方不会满足于纯算法排序。今天想置顶某个活动商品明天想屏蔽某条的曝光后天想让特定搜索词下展示一个专题。如果每次都用 customRanking 来做你会发现指标被改得乱七八糟。官方博客里跟我实际经验最匹配的部分是它对确定性规则的强调所有排序条件最终都是确定性判断你可以预判任何一条记录的结果。这给运营干预提供了非常优雅的落地方式——不要改全局排序而是针对特定查询词设置例外规则。实操时我的做法是给运营提一套高级配置入口里面是几个固定的规则模板。比如当查询词完全等于 A 时把记录 X 固定在第一位并把记录 Y 降到最后一位当查询词包含 B 时强制追加一个筛选条件。这些规则翻译到底层其实就是把某条记录置顶pinned、给记录加负向权重、或者临时改查询条件。它共享同一条排序管线不破坏默认排序逻辑只是在前面的关卡里人为地插了一个插队节点。这个方法比在代码里硬写逻辑要稳得多因为它能被搜索控制台的调试工具清晰地看到也能随时回滚不会出现为什么线上结果突然变了的悬案。4. 中文语境下的落地我实际调优时踩过的坑4.1 中文分词不要直接照搬英文的字段设计原博客是用英文场景写的里面大量例子都基于英文空格分词。翻译成中文发布之后我接到的读者反馈里最集中的问题就是中文分词带来的排序异常。英文天然有空格单词边界清楚中文没有空格搜索引擎要把一段连续汉字切成有意义的词。Algolia 对中文有自己的分段处理策略但分词结果和你预想的不一定一致。比如商品标题机械键盘红轴静音版你希望它被切分成机械键盘 / 红轴 / 静音版但实际切分可能把键盘红或红轴静音当成别的组合导致用户搜机械键盘时匹配权重被稀释。我的实践建议是在建立索引的字段设计阶段就让每个属性承载语义单一的片段。宁可多用几个字段比如brand、series、model、color、switch_type也不要全部堆在title一个字段里。字段拆得越细attribute这个排序条件越能发挥作用——用户搜红轴时它能明确知道是switch_type字段命中而不是 title 里某个汉字组合恰好命中。如果你已经有一堆老数据、不方便改字段结构另一个补救措施是同义词配置。把产品库里常见的口语说法、简称、别名统一做成同义词表。注意同义词不是越多越好配置太激进会导致搜索太松用户搜一个精确型号时反而召回了一堆不相关的近似词。好的做法是先收集搜索日志里的高频失败词再针对性建表。4.2 typo tolerance 在中文场景里要特别小心默认的 typo 容错拼写容错在英文里很好用用户把keyboard打成keybord少一个字母编辑距离很小依然能命中。但中文的错误不太一样。中文输入错误很多来自拼音输入法的同音字比如把青轴打成轻轴把机械打成机戒。这两个词的编辑距离和英文拼写错误完全不同汉字之间不是一个字母一个字母的差异而是整个字被替换。如果完全依赖默认 typo 容错你会发现同音字错误经常救不回来如果你为了救同音字把拼写容错阈值调得过大又会引入大量不相关匹配。我的处理方式通常是不要大幅提高 typo 容错的强度而是把中文常见误输词维护进同义词规则。从产品效果看这比提高编辑距离阈值更可控、更好解释。还有一个容易被忽略的点typo tolerance 影响的是匹配阶段而中文的搜索词往往很短一两个字的变化就可能让命中记录集合完全不一样。所以排查为什么搜不到时先看控制台返回的 matched words判断是不是 typo 容错把某个词容忍掉了导致它没有被当作核心关键词来匹配。4.3 用调试工具看每一条命中的判决理由我见过太多人调排序靠猜改一个配置看整体结果顺眼不顺眼然后反复试。官方博客那篇文章最实用的部分反而是它提到的调试思路每一轮排序都有一份判决记录你要学会把它展开来看。在 Algolia 控制台的查询调试器里每一条返回记录都会给出详细的匹配信息命中了哪些词、分别在哪个属性命中、rankingInfo里记录了每个排序条件的结果。我调优时基本流程是输入一个真实查询看排前三的记录逐一检查它们赢在哪一关、输在哪一关。如果排第一的记录是因为exact赢的而业务上你更希望attribute权重生效那就去调属性顺序如果一条记录在proximity输掉了那不是排序问题是文本内容本身不同你可能需要通过分词字段设计去改变它。这个方法的好处是不靠感觉。我经常在排查时发现真正的问题根本不在排序阶段而是某个字段值本身脏了、有错别字、或者类型不对导致没有被匹配。调试器能帮你把这类问题从排序问题里剥离出来。4.4 从搜索日志反推优化方案前面的方法偏从查询到调试器但实际产品里优先级最高的工作应该是用搜索日志找出最痛的那批查询再去做针对性优化。搜索分析数据会告诉你哪些查询返回了 0 条结果、哪些查询点击率高但转化低、哪些查询下用户反复翻页。我在一个内容站里做过这样的优化日志显示大量用户搜入门教程但列表页排序把最新文章放在最前面旧的经典入门文章沉到底部。记录本身是命中的但排序规则明显没匹配用户意图。后来我把分类字段和标签字段纳入排序权重调整并且针对入门教程这类词写了一条查询规则让标签为新手的内容优先。最终该查询的点击率提升非常明显。排错类优化和体验类优化要做区分无结果查询优先修匹配有结果但没人点优先修排序有结果也有人点但转化差那已经不属于搜索问题而是内容着陆页的问题。这个优先级判断帮我省了很多时间也推荐给所有做站内搜索的人。5. 如果从头做一遍相关性调优我建议按这个顺序来5.1 把业务目标翻译成排序条件动手改任何配置之前先坐下来回答一个问题用户搜完我们希望谁赢这不是技术问题而是业务问题。把目标明确之后翻译成配置才快。业务目标推荐手段注意点卖得好的优先customRanking 使用销售额字段注意泛词场景下的误伤新品优先customRanking 使用上架时间避免新但不相关的产品霸屏品牌词必须精准精确匹配 提升 exact 条件权重小心同品牌其他型号混入强运营活动Query Rules 置顶/降权规则要可回滚、可解释内容生态丰富度字段权重 分类过滤避免头部内容垄断全部展示这张表不是固定公式但它能帮你很快把一个模糊诉求拆成具体配置。没有业务目标就去调排序就像没有终点去跑马拉松最后一定被需求方反复推翻。5.2 整理一组真实查询样本建立基线我强烈建议在上线大改之前先建一个查询金样本集。操作方法是从搜索日志里抽出 30 到 50 条高频、有代表性的真实查询覆盖导航型查询搜品牌名、交易型查询搜商品属性、调查型查询搜比较类内容三类。然后定义你期望的结果顺序哪怕手动标注其中前三条都行。有了这组样本和标注每次配置改动后都跑一遍对比理想排序和实际排序之间的差距。指标可以很简单满意结果出现在前三位的比例、无结果率、以及主观判断的明显错排个数。这套方法不需要写复杂代码用控制台就能手工完成但效果远远好过我觉得好像变好了。5.3 一次只改一个变量并记录每一次实验调优时最容易犯的错误是同时改多个参数。今天又改了同义词又调了属性权重又加了规则结果搜索效果变好了但你完全说不清是哪个改动在起作用变差了也一样难以回滚。我的习惯是给每一轮实验写一个非常轻的记录格式就三行改了什么、预期效果、实测结果。比如修改把销量加入 customRanking预期泛词查询下爆款整体前置品牌词下不受影响实测品牌词下出现爆款逆袭非目标款需要加规则修正。这个习惯对我帮助非常大尤其是当你需要向团队成员解释一个排序问题时这份记录基本就是完整的排查日志。5.4 别忘了空结果和兜底体验排序优化通常关注的是有结果时怎么排但用户感知最强的往往是没有结果时怎么办。搜索日志里的无结果查询是性价比最高的优化对象。你可以先看这些查询是不是因为配置太严格导致漏匹配如果确实没有内容那就做推荐兜底——展示热门内容、同类商品、或者提示用户改一个更短的关键词。官方博客里对这类兜底讨论不多很可能是因为它默认你会用搜索分析发现这个问题。我自己每次上线搜索项目都会设一个规则不允许任何真实查询直接面对空白结果页。这比任何 fine-tune 排序参数都更能立竿见影地提升用户满意度。兜底方案可以是简单的推荐列表也可以是 query suggestions查询建议关键是让页面有东西可看给用户下一步动作的路径。5.5 把搜索调试器当作日常工具而不是应急工具最后一个建议听上去最小但实际影响最大。很多开发者只在项目新上线或者用户投诉时才打开调试器平时完全不看。我建议把每个搜索词都去调试器里点开排名详情变成一个例行动作。每周花二十分钟随机抽几个真实查询展开前五条结果的rankingInfo问问自己这个结果如果我是用户会觉得合理吗如果觉得不合理是哪条排序条件导致的这个动作让我发现过很多隐藏问题——比如某个字段因为数据导入异常全是空值导致搜索时大量结果只靠 description 匹配又比如两个属性权重差距太大导致所有查询都偏向某一个属性命中的内容整个搜索结果的多样性变差。这些问题在日志和统计里不一定显眼但在调试器里一眼就能看到。说白了排序机制本身是确定性的你只要愿意去读它的判断过程几乎所有结果都能找到合理的原因真正需要警惕的是那些说不出为什么排前面的记录它们往往就是隐藏问题的入口。翻译完这一篇我最大的收获不是记住了那几个排序条件而是学会了像搜索引擎一样思考——每次看到一个结果先问它在哪一关赢的。我觉得这个思路很值得推荐给你。如果你正在用 Algolia 做站内搜索又总觉得结果差点意思不妨从今天开始打开调试器输入一条你业务里最典型(或最赚钱)的查询把前五条结果的排序依据全部看一遍。你可能会发现问题根本不在排序而在字段设计、数据质量或者匹配阶段。找到真正的问题比急着调参重要得多。
觉得有用,分享给同行:

为您的企业打造数字门面

稳重轻奢商务风格,端正雅致视觉,长效耐看不易过时。

立即咨询 →