
做交易搜索做到第三年我越来越觉得“向量检索”这四个字被高估了。尤其在得物这类交易场景里用户搜“球鞋”“配饰”“潮玩”时早就不再是“query和商品文本够不够像”的问题而是“用户脑子里想的那层意思商品标题里根本没有出现过”。我们曾经把召回从词匹配一路升级到向量召回、多路召回并联CTR涨了一截但长尾query依然大量落空。真正让召回效果出现肉眼可见的跃迁的是一套完全不同的思路——生成式召回。这篇文章想把这套思路完整拆开它到底在生成什么、工程上怎么落地、评测怎么做、又有哪些坑在等着你。1. 交易搜索到底难在哪为什么卷完向量检索还是不够1.1 交易搜索和通用搜索不是一回事很多做推荐和搜索的同学一上来就把网页搜索那套方法论搬到交易搜索里。这是第一个容易踩的坑。通用搜索的query和信息内容之间核心关联是“知识型匹配”用户搜“怎么泡发海参”你给他一篇讲干货的文章就算成功。但交易搜索的本质是“货架上的生意”用户搜一句话背后是明确的购买意图系统要做的不是“回答”而是“在商品库里捞出真正值得花钱的候选”。这个差异带来了几个非常现实的问题。第一query极短短到经常没有上下文。用户搜“篮球鞋”已经算完整更多的是一两个词加一个特殊的品牌缩写比如“AJ1倒钩”“小闪电”这种圈内黑话。第二商品侧的title和描述是运营和商家写出来的不是用户语言。商家写“Nike Air Force 1 空军一号 男鞋 板鞋”用户心里想的是“百搭小白鞋”两边用的根本不是同一套词典。第三交易场景对“相关”的定义更苛刻。搜“礼物”不能只召回标题里带“礼物”两个字的东西而是要让用户觉得“这个东西确实像是能当礼物的”这里面涉及场景、价格带、品牌心智。通用搜索的语义匹配模型很难同时照顾到这三层。所以交易搜索的第一性是把“用户想要的东西”和“商家卖的东西”用某种方式对齐。传统做法是不断堆特征、堆不同的匹配通道但无论怎么堆本质上都还是在“已有的文本和已有索引”里面捞。问题恰恰就出在这里如果用户的表达方式和商家的表达方式之间根本没有显式交集任何基于匹配的算法都无能为力。1.2 多路召回和向量检索的天花板过去几年召回方向的迭代大体是这么一条路径。先是词权重、BM25这种统计路线接着是双塔向量模型靠query和doc的embedding相似度做召回再往后是多路召回并联关键词一路、向量一路、热度一路、类目一路最后汇总去重再给精排。这套体系在大多数场景里确实是有效的但它的天花板也很明显。先说向量检索的问题。双塔模型的核心假设是query和doc在同一个语义空间里各自编码距离近就代表相关。但这个假设在交易场景里经常站不住脚。商品标题往往是名词堆叠没有完整句式比如“冬季加厚男士羽绒服 连帽 保暖”这种文本很难编码出用户query“约会穿什么”所对应的场景语义而用户侧的“送男朋友”又包含关系、场合、预算等多组意图双塔向量会把它们压成一个稠密向量丢得干干净净。加上领域黑话和新词天然稀缺embedding模型对“倒钩”“Yeezy 斑马”“绿尾”这类词的表征能力非常有限向量召回头部效果尚可一到长尾就稀碎。多路召回并联解决的问题是“单路失灵时还有别的路能捞上鱼”。但多路并联不等于多路互补实际我在项目里见到最多的情况是几路召回返回的结果高度重叠都在吃头部流量真正让长尾受益的新文档、新语义、新场景各路都搞不定。原因很简单多路召回的路由设计、各路权重、各路索引结构都是围绕着“已有query的分布”去优化的你没法召回一个“历史上从未被写成索引词”的语义。也就是说你卷到头卷的也是“怎么在同一批旧语义里挖得更深”而不是“如何引入全新语义”。1.3 长尾query到底长在哪里既然聊长尾就得说清楚交易搜索的长尾query到底长在什么地方。我观察下来大概有三类。第一类是口语化的场景式表达比如“见女朋友家长穿什么鞋”“健身后吃什么补充体力”这类query和商品标题之间几乎没有共同词传统召回基本只能靠运气。第二类是圈内黑话和新兴概念比如球鞋圈的“倒闭款”“起飞款”、潮玩圈的“隐藏款”“大娃”这些东西更新速度极快等embedding模型训练语料覆盖到热度早就过了。第三类是组合型意图比如“1000块以内送男友的偏正式运动鞋”query里同时包含了类目、预算、使用场景、送礼属性任何一个单独维度都不能漏漏一个就是一次糟糕的购物体验。这三类长尾的共同点是什么是“用户的显式表达不够但隐式意图非常丰富”。传统搜召回做的都是“表达层”的匹配查不到就是查不到。而如果能让系统先把用户没说出口的意图“补出来”或者让商品提前“长出”一批用户会说的说法再去做检索问题就从“查不到”变成了“接得住”。这就是生成式回召回出现的逻辑起点。2. “生成式”召回的核心思路让商品先发言2.1 生成式召回的“生成”到底生成了什么很多人一听“生成式召回”脑子里蹦出来的第一个想法是让大模型直接输入query、输出一个商品ID。这个方向确实有不少团队在研究但真放在交易搜索这个量级和延迟要求下现阶段很难作为主力通道。我更愿意把生成式召回理解成一种“扩写与对齐机制”它不负责直接给出最终商品而是负责把语义表达层补全让后续的检索和排序有得捞。具体来说生成式召回会产生两类东西。一类是商品侧的“候选说法”也就是给每个SPU生成一批用户可能使用的query文本另一类是用户侧的“意图扩展”也就是把线上的一条query拆解、改写或补全成一小组备选的检索表达式。两类东西最后都进索引、进检索、进精排跟传统召回通道做好融合。这样做的核心价值是打破了“用户表达”和“商品表达”之间的词表壁垒把一个匹配问题先变成生成问题再变回匹配问题。这个“生成→再匹配”的阶梯其实就是召回范式从“被动等撞”到“主动创造交集”的跃迁。2.2 商品侧离线生成让SPU学会用户会说的话商品侧离线生成是我觉得投入产出比最高的一环。做法很简单在每个SPU入库或者每天批量更新时把商品标题、类目、品牌、卖点描述、用户评论里的高频描述词糅成一个上下文丢给大模型让它回答一个固定问题这个商品用户可能会用什么口吻、什么叫法、什么场景来表达生成的结果就是一批“伪query”。这个做法本质上借鉴了传统信息检索里“doc2query”的思想但生成式模型让这件事的上限高得多。过去做doc2query是用一个序列到序列模型把文档标题改写成一个问句能力有限改出来的表达还是贴着原标题词汇。现在的LLM不一样它能把“适合当礼物”这种抽象商品属性直接写成“男朋友生日送什么”“毕业礼物推荐”这类真实用户会敲的query也能把黑话、俗称、场景标签一次性补齐。我在实际项目里让模型对一个普通的白色板鞋生成候选说法它产出了“小白鞋”“百搭休闲鞋”“送男友的平价礼物”“上班通勤穿什么鞋”等好几组完全不同语义方向的表达。这个广度是传统规则和单纯向量扩充做不到的。商品侧离线生成的另一个优势是成本可控。一天几百万商品哪怕用中等规模的模型批量推理分摊到每件商品上的成本也远低于在线调用。而且生成结果可以设置有效期比如商品有了新的用户评价、新的穿搭场景第二天再重新生成覆盖就行。对于上新频繁、标题质量差的商品这套“让商品先说话”的机制尤其有效。2.3 query侧在线扩展把一句话拆成六句话光有商品侧还不够线上query侧的扩展决定了这套体系能不能实时跟上用户需求的变化。在线扩展的思路是用户输入一条query后先用一个轻量级的触发条件判断哪些query值得走生成式扩展值得走的通过LLM生成几组“语义变体”每一条变体都可以当成独立的query拿去检索。举个例子。用户输入“送对象的东西”直接拿这句话去倒排和向量索引里捞结果一定很惨。但如果在线先把这句话扩展成“送女朋友礼物”“送男朋友礼物”“纪念日礼物”“情人节礼物”“生日礼物”这组说法再分别检索召回集合的质量会完全不一样。我更喜欢管这一步叫“意图槽位补全”因为系统生成的不是同义词而是把一个宽泛表达拆成多个具体检索项每个检索项负责命中一类真实商品。在线扩展要控制好两个问题。第一个是扩展数量不是越多越好。我实践下来每个query生成4到8条扩展是收益最稳定的区间超过10条之后召回边际增益开始趋缓而计算和精排压力直线上升。第二个是扩展的“脱轨风险”也就是生成结果与原始query意图偏离。生成出来的每条扩展都需要经过一个“意图一致性过滤器”打分低于阈值的直接丢弃绝不让它进入检索链路。2.4 为什么不能全链路让大模型直接给商品ID我遇到过不少同行聊起生成式召回时最兴奋的点是大模型“什么都知道”干脆让它跳过检索直接输出商品ID列表。但在交易搜索里这么做短期内有三个迈不过去的坎。第一是幻觉问题。LLM很容易“一本正经”地输出一个看起来合理、其实并不存在或者已经下架的商品名这在搜索链路里是不可接受的。第二是知识时效。大模型的知识截止日期和线上商品库的实时状态永远存在差距今天新上架的爆款、明天调价的活动款模型根本不知道。第三是可控性。交易搜索要评估的是点击、加购、成交任何一个召回的改动都要能快速回滚、精细分层让模型端到端直接出ID相当于把召回变成了一个黑盒出了问题很难定位。所以在我的实践经验里“生成式”在这个阶段的正确打开方式是把生成结果当作“新的检索入口”而不是“最终的召回结果”。生成的东西是query、是标签、是槽位最后还是回到索引里去捞商品。这样既吃到了大模型对语义世界的理解能力又保住了搜索引擎对实时性和可控性的掌控力。3. 系统落地离线管道、在线链路与精排兜底3.1 离线生成管道怎么搭离线管道的第一步是先把商品侧的数据备齐。我建议用一张宽表包含SPU ID、标题、类目、品牌、关键属性、最近30天的用户搜索点击数据、以及经过清洗的用户评论片段。用户评论特别有用它是天然的“用户语言”语料能帮模型学会这个商品在真实场景里被怎么称呼。把这些字段拼成一个稳定的prompt模板调大模型批量生成候选query然后做清洗。清洗这一步很多人会忽略但它直接决定线上效果。生成出来的候选query里会有明显的幻觉词、对其他商品的描述、甚至风格完全跑偏的表达。我常用的过滤规则有这么几条候选query必须在商品库的搜索日志里有过一定曝光或点击否则可能太偏候选query与原始标题的字符重叠度太低时需要额外审核防止完全脱轨候选query里涉及价格、优惠、规格等强属性信息的必须和商品真实属性比对不一致就丢弃。经过这批规则之后剩下的候选query与SPU形成映射表导入向量索引和倒排索引。这里有个工程上的细节值得展开商品侧生成的候选query不要直接替换商品原标题而是作为“附加可检索字段”建索引。因为召回时我们希望各种通道都能命中但精排阶段仍然需要依赖原始标题、类目、价格等真实特征排序如果检索字段和排序特征混在一起容易让精排觉得“这个商品相关”但说不出为什么相关。把生成字段单独存放、单独打标签后续做A/B和归因都会轻松很多。3.2 在线检索链路怎么改在线链路的改造核心是低成本地接入query侧生成式扩展。常见的做法是分层触发头部query直接走缓存不调用大模型中长尾query里有比较明确的意图扩展诉求的才走生成式扩展。怎么判断一条query值不值得扩展我在项目里用的是“失信度”指标大概由三个因素组成这条query在过去一段时间内的无点击率、无成交率、以及扩展前后向量召回结果的差异度。如果无点击率高同时又存在历史扩展样本就倾向于让它走生成式扩展。真正调用模型时要注意两点。第一在线生成必须用异步或超时保护不能让一次生成阻塞整个检索RT。我的办法是把生成式扩展和传统检索并行发起模型先返回的先参与融合超时未返回就跳过生成结果保证主链路延迟不受影响。第二模型产出的扩展query要和原始query一起统一送进多路召回各路的返回结果再做归一化打分、按SPU聚合去重。聚合去重这一步特别重要否则同一个商品会被变体query反复召回压制了其他商品的曝光空间。融合层的打分不要拍脑袋。我给扩展query的结果单独设置一个小权重比如原始query召回的结果权重为1.0生成式扩展召回的结果权重为0.8这样既给新语义开了口子又不会让“跑偏”的结果一下子冲到头部。这个权重可以在精排特征里再加一个字段让精排模型根据用户行为自己学习该给多少分数。3.3 精排和兜底如何配合召回侧做了这么多如果精排不配合效果会打折扣。因为生成式召回带来的结果是用户历史上很少见过的表达对应的商品精排模型没见过这类样本很容易给低分导致召回了却排不出去。我在实践中是这么处理的在精排特征里显式加入“召回通道标识”和“生成扩展得分”让精排模型知道当前商品的来源同时离线积累生成式召回结果和用户反馈样本持续训练重排模型对这些特征的拟合能力。上线初期如果精排权重来不及调可以先给生成式召回结果一个固定的保底位比如在相关品类页的第四、第五位各放一个让流量慢慢验证。兜底层面还要处理一种情况用户query生成式扩展后虽然召回了更多商品但数量仍然不足或者生成的扩展结果质量不稳定。这时候需要一条安全网通常是“类目兜底 热度兜底”。类目兜底是把query映射到最可能的叶子类目再返回该类目下的热销商品热度兜底则是在判断用户query与商品库风格差距较大时返回平台整体的高热度商品。生成式召回解决的是“语义通路”问题不负责解决“无货可卖”问题兜底还是得靠传统且成熟的入口机制。4. 评测效果与踩坑实录4.1 效果评测别只盯着离线召回率生成式召回上线前评测方案就决定了一半的成败。离线召回率当然要看但它真的不够。因为生成式召回的初衷是引入新语义如果评测集还是老的那些点击日志模型召回的老结果自然多。所以我把评测集分成三块老query集合、长尾query集合、由运营标注的“意图迁移”query集合。第三块很关键它是专门挑选出来的“用户表达与商家表达不一致”的真实案例比如“送导师的毕业礼物”“能跑步的休闲鞋”用这部分样本衡量生成式扩展是否真正把语义缺口补上了。在线指标上我重点关注三个方向。第一是无结果率生成式召回上线后无结果页率的下降是最能说明问题的第二是长尾query的加购率和曝光后的互动率看新增的召回结果到底是“能看”还是“能买”第三是整体GMV和毛利率的波动防止为了召回多样性稀释了转化效率。把这三组指标并在一起看而不是单看召回率或CTR才不会得出“ROI很高但业务不想上”的尴尬结论。4.2 我实测过的几个关键坑第一个坑是幻觉会导致“假富集”。模型生成一批候选query后直接进索引如果没做线上命中校验你会看到离线评估里的召回率涨得很漂亮可线上完全没变化。原因很简单生成出来的很多表达在用户真实搜索里根本不存在或没有对应需求。解决方式很朴素每个生成的候选query必须回查一段周期内的真实搜索日志在日志中有过一定行为才允许上线。第二个坑是扩展结果“泛化过度”。给“篮球鞋”生成扩展时模型可能会写出“运动鞋”“休闲鞋”“男生礼物”这种大范畴词导致召回结果瞬间跑向全域商品相关性崩掉。我后来在prompt里加了“必须保留原始query的核心类目约束”和“生成太多泛化词时降低权重”的规则才压住这个问题。线上那段时间相关性bad case肉眼可见地降下来了。第三个坑是精排模型不认账。生成式召回结果送进精排后精排分数普遍偏低导致召回了但排不出去业务指标当然没有变化。这个坑花了我将近两周才定位清楚本质是特征分布错位。解决办法就是前面说的把召回通道标识和扩展得分作为特征重新训练精排并且在初始期给部分结果保底位置让模型有样本可以学习。第四个坑是关于冷启动和更新节奏的。商品侧生成结果不是一劳永逸的商品价格变了、场景变了、用户评价里出现了新的叫法都需要重新生成。我建议生成任务做成增量更新至少每天跑一次重点覆盖三类新入库商品、热度显著上升的商品、近30天无点击但标题被大范围修改过的商品。让“商品会说话”这件事保持新鲜度否则时间一长生成的候选query又会变成新的“旧词表”。5. 这套方案适合谁适用范围与演进方向5.1 什么样的场景值得做生成式召回最适合的是用户表达高度自由、商品侧文本质量差、同时又有大量场景化需求的交易搜索场景。典型特征是有大量口语化query、有大量“意图型query”比如“送人”“通勤”“穿搭”、商品标题普遍短、分类体系不够细。得物这类主打潮流消费的交易场景几乎每条都中用户搜“夜跑鞋”“压马路”“情侣鞋”“小众品牌”这些词在商品标题里很难出现但它们是真实且高频的需求。这类场景上线生成式召回提升的不只是几个百分点的召回率而是整个召回体系对“语义多样性”的承接能力。另外一个适合的场景是新品冷启动。新上架的商品没有点击数据、没有长尾query积累传统召回只能靠类目和标题硬匹配很难被用户发现。生成式召回让新品提前拥有多组“用户说法”等于给新品配了一个虚拟的点击历史让它在没有真实用户行为之前就能通过语义通道触达潜在需求。这一点对SKU丰富的电商意义很大。5.2 什么场景我不建议上也有一些场景我不建议硬上生成式召回。首先强SKU精确搜索场景要慎重用户搜“iPhone 15 Pro Max 256G 原色钛金属”商品侧再生成一万句话也不如精确属性匹配管用加了生成式反而可能因为扩展词干扰精确匹配精度。其次延迟极其苛刻、用户对结果稳定性要求极高的场景比如抢购秒杀、金融行情等生成式扩展带来的不确定性会是个麻烦。最后内容合规审核要求非常严格的行业大模型生成结果需要额外过一道“审核回路”这会让生成式召回的成本和复杂度都变成不可忽视的问题。所以我的建议是生成式召回适合作为“增量通道”去做而不是作为“替代通道”去推。它可以成为召回池的一个新供给来源和关键词、向量、热度这几条老路并存等到运行稳定、评估体系成熟后再逐步提高它在融合层的权重。5.3 后续演进从生成扩展到生成排序如果再往后看一步我认为生成式召回的下一次演进会从“生成检索词”走向“生成排序理由”。现在的做法还是生成query去撞索引未来的做法可能是让模型直接参与候选排序模型根据用户query的细粒度意图给每个候选商品生成一条“相关性解释”再把这个解释与商品的真实属性做一致性打分最终形成排序信号。这个方向能更好地解决精排阶段“召回了但排不出去”的问题因为它把生成模型的语义理解能力从召回层延伸到了排序层。另一个演进方向是和个性化结合。同一个query男生和女生、一线城市和下沉市场意图可能完全不同。生成式扩展可以结合用户画像为同一query生成多套不同侧重的扩展表达再按人群分别检索。这个方向对交易搜索来说价值很大因为它直接触及了“千人千面”的召回层实现而不只是排序层的个性化。说回这套方案的本质它并没有否定向量检索也不是要把大模型变成搜索引擎里的一个巨大黑盒。它做的最重要的一件事是把“用户没写出来的话”和“商品没写出来的标签”都补齐了让召回池里第一次有了足够多的“交集”。我在实际项目里最深的体会是上线生成式召回之后再去分析bad case会发现一个很有意思的现象曾经的很多“查不到”问题变成了“查到了但排不好”问题。从召回视角看这已经是质的进步了。另一个很实用的经验是如果你准备在自己的交易搜索场景里尝试这套思路不要一开始就改造整条链路先把商品侧离线生成跑起来配上一条轻量的query侧扩展只对最典型的意图型query生效用一个小流量实验验证“长尾无结果率”是否真的下降。只要这个方向上看到了业务收益再逐步放大范围也不迟。走得慢一点反而能把地基打得更扎实。