ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

生成式召回崛起:交易搜索如何突破向量检索的约束天花板

生成式召回崛起:交易搜索如何突破向量检索的约束天花板 1. 为什么说向量检索卷到头了搜索圈现在聊生成式召回总有一种在聊“下一代搜索引擎”的感觉。我在电商搜索这个方向泡了快十年最近一年被问得最多的一个问题就是向量检索都这么成熟了还有必要折腾生成式吗我自己的答案是如果做内容搜索向量检索还能再吃几年红利但如果是做交易搜索比如像得物这类以商品交易为核心、带着强约束条件的场景向量检索的天花板已经肉眼可见了。这篇就聊聊交易搜索为什么需要把召回从“查”变成“生”以及生成式召回落地时真正要过的几道坎。1.1 向量召回的本质与天花板先简单回顾向量召回的本质。双塔模型把 query 和 item 分别编码成稠密向量在线用近似最近邻ANN引擎比如 Faiss、HNSW 算内积拿 TopK。这套方案的核心优势是“语义相似性”可以被向量距离近似表达配合足够好的负样本和难样本就能在很多场景里达到不错的召回效果。但它的天花板也很明确向量空间是“平均语义”的容器而不是“可组合逻辑”的容器。你输入“黑色 AJ1 42码 2000以内”双塔模型会把这句话压缩成一个固定维度的向量这个向量能表达“我要篮球鞋”但很难精确表达“价格小于2000”和“尺码42”这种硬约束。线上工程常见的做法是向量召回一大筐再用数据库过滤尺码、价格、库存但问题在于过滤发生在召回之后如果向量召回阶段就没有把符合硬约束的商品拉进来后面怎么过滤都找不回来。另一个问题是向量索引对“精确标识”不敏感。商品 ID、SKU ID、卖家 ID 这类标识性信息在 embedding 空间里往往被语义淹没。用户搜“反钩 AJ1 43”向量召回能把“反钩”的语义学出来但 43 和 42.5 在语义空间中离得很近最终排序打分时很难严格区分。这正是交易搜索最在意的部分。1.2 交易搜索的三个隐形痛点得物交易搜索并非传统意义上的“浏览式搜索”它有很强的交易属性。用户搜一双鞋可能在意的不是“相关文章或同款视频”而是“这个尺码有没有货、什么价、成色和验货状态如何”。这类查询天然带着约束条件而我看到的很多向量召回工程普遍卡在三个隐形痛点上。第一个痛点是多意图叠加。交易搜索里 query 经常是“品牌品类款式尺码价格区间成色”的复合表达。向量模型很难同时满足多个维度的约束强行训练多任务又会让各个任务之间互相打架。你可以把约束做成后过滤但召回基数不够时过滤完只剩几百个甚至几十个商品整个漏斗就从源头塌掉了。第二个痛点是长尾新品。电商商品更新快得物上的球鞋、潮牌、数码产品每天都有大量上新。新商品没有足够行为数据embedding 基本是随机初始化或者靠图文信息冷启动。向量召回对这种新品天然不友好不是语义学得不好而是行为信号太弱索引里的位置不稳定容易被老爆款挤掉。第三个痛点是交易信号时效性。价格波动、库存变化、卖家发货状态这些是交易搜索的实时核心信号。向量索引如果要包含这些信号就得高频重建或增量更新成本和复杂度都极高。很多团队做出来的实际效果是离线指标涨了在线成交没涨因为在线生效的索引还是旧的。我自己做过一个对比实验在同一批交易搜索 query 上分别跑纯向量召回和加了约束生成的混合召回只看“约束满足率”召回商品中符合尺码、价格、发货状态的占比纯向量组大约在 58% 到 65% 之间波动混合组能稳定到 85% 以上。这也让我意识到不是向量检索不好而是它只适合做“broad recall”不适合做“constrained recall”。召回方式语义相似性多约束满足新品上线速度交易信号实时性倒排检索弱强快中向量检索强弱慢弱生成式召回中强强中强2. 生成式召回到底在干什么很多人一听“生成式召回”第一反应是“用大模型直接写答案”然后怀疑这东西怎么用到搜索引擎里。其实这里的思路完全反过来了不是让模型生成自然语言回答而是让模型直接生成“该召回哪些商品”。2.1 从“查候选集”到“直接生成结果”传统召回是“先建候选集再检索匹配”。生成式召回则是把语料库里的所有文档在电商场景里就是商品编码进模型参数训练一个序列生成模型输入用户 query输出一组商品标识序列。这个思想最早可以追溯到 2021 年前后提出的 Differentiable Search IndexDSI当时研究者想用一个 Transformer 模型直接记住文档内容输入问题输出文档 ID跳过倒排和 ANN 索引。在交易搜索里商品标识不只是商品 ID而是一段“结构化的商品描述序列”。比如一双鞋可以表示成[品类] 运动鞋 [系列] AJ1 [款式] 黑武士 [尺码] 42 [价格] 1899 [商品ID] S2309F007让模型学的是从“用户需求描述”到“商品属性序列”的映射。解码时用 beam search 生成多个候选每个候选就是一条商品路径。这样做的直接好处是模型在生成过程中就显式考虑了品类、尺码、价格等约束而不是先模糊匹配再做后过滤。2.2 别把它和 RAG、LLM 聊天混为一谈这里必须先做一个小澄清。RAG 是“检索增强生成”核心是先检索出来再喂给模型生成答案检索还是那个检索。生成式召回是“把检索本身变成生成”两者方向不同解决的问题也不同。交易搜索落地时也不会真上一个巨大的对话式 LLM 来做召回。线上的 QPS 高、延迟要求低用几百 B 的大型模型做逐条生成根本不现实。实际工程里更多用的是中小规模的 encoder-decoder 模型可能只有几亿到几十亿参数甚至基于 BERT 塔做一个轻量 decoder 头。关键是“目标函数”变了而不是“模型规模”变了。所以别再纠结“生成式召回是不是必须用大模型”。它不是大模型的附属品而是索引结构层面的改变。模型把商品索引“记”在参数里在线解码时不再依赖外置向量索引而是靠生成过程完成候选构造。2.3 为什么交易搜索是生成式召回最好的试验田我判断一个场景适不适合生成式召回主要看三件事目标对象是不是“可枚举标识”、query 是否带强结构化约束、有没有足够强的反馈信号来构造训练样本。交易搜索恰好三条都满足。商品 ID 虽然量很大但它毕竟是有限集合每个商品都能用结构化属性描述。用户的搜索行为也足够密集点击、收藏、成交多级反馈都能用来构造“query 到 item 最佳路径”的训练对。更重要的是交易搜索的“正确性”定义比内容搜索清晰成交就是成交尺码不对就是不对约束满足与否是可判定的。而在生成式解码阶段我们可以做“受限解码”在解码的每一步用一个商品前缀树校验当前生成的 token 序列是否合法再叠加价格、库存、发货状态等硬过滤条件。这一步相当于把业务规则直接嵌进了召回过程。向量检索做不到这一点因为它是在高维空间接近后再过滤规则晚了一步。3. 得物交易搜索生成式召回的落地拆解前面讲了很多理念下面进入工程落地。以得物交易搜索为例一套可落地的生成式召回方案通常分四步训练数据构造、模型结构设计、解码规则注入、双路召回协同。3.1 训练数据把用户行为转成“查询-商品”对生成式召回的训练数据核心是“query 文本 用户上下文”到“目标商品序列”的映射。第一步就是在日志里抽取行为对。我一般用曝光、点击、收藏、成交四个层级权重分别设为 0.1、0.3、0.5、1.0成交行为的样本权重最高因为成交是最强烈的正反馈。负样本怎么选很关键。普通的随机负样本会让模型学会“生成爆款”造成结果同质化。我建议做“混合负采样”一部分从全局随机采一部分从同一 query 下曝光但未点击的商品里采一部分从相似款但尺码或价格不匹配的商品里采。第三步尤其重要它逼着模型学到“属性约束”而不是光学个语义相似。样本格式建议如下{ query: 黑武士 AJ1 42 一千八左右, context: { user_age: 24, user_city: 上海, recent_click_items: [S2309F007, S2310A012] }, target_item: { tokens: [运动鞋, AJ1, 黑武士, 42, 1899, S2309F007], weight: 1.0, label_type: trade } }这里有个细节商品 ID 必须放在属性序列的最后。原因是解码早期先生成品类和属性可以帮助模型稳定学到“分层决策”的逻辑最后再锁定具体商品。如果上来就生成 ID模型很容易退化成“背诵商品字典”泛化能力极差。3.2 模型结构query 端编码 商品端解码模型我用的是经典的 encoder-decoder 结构。query 编码端使用一个轻量文本编码器可以复用粗排阶段的语义模型decode 端是一个 6 层 Transformer decoder。整体参数量控制在 3 亿左右线上单次解码延迟可控。{ model: { encoder: bert_base_share_embedding, decoder_layers: 6, decoder_heads: 8, max_decode_length: 32, beam_size: 8, diverse_beam_groups: 4, diverse_beam_strength: 0.3, loss: cross_entropy_with_label_smoothing, label_smoothing: 0.1 }, training: { batch_size: 256, learning_rate: 5e-5, warmup_steps: 2000, max_steps: 120000, mixed_precision: true } }loss 就是常规的交叉熵但 label smoothing 必须开不然模型对商品 ID 记忆过强遇到新 query 容易输出乱码式 ID。所谓“乱码式 ID”就是模型生成一个看起来像商品号的 token 序列但实际在索引字典里不存在。线上一定要加前缀树约束来兜底。3.3 解码期的业务规则注入这一步是交易搜索区别于内容搜索的关键。我想强调一点生成式召回不要等生成完再去过滤而是把过滤条件直接融合到解码的每一步。具体做法是维护一棵商品前缀树trie树的每一条路径对应一个商品的合法 token 序列。解码每一步时先用 top-k 拿到候选 token然后过滤掉那些无法匹配当前前缀树路径的 token同时把价格、库存、发货状态这些硬约束转成 scorer对候选 token 重新打分。def constrained_decode_step(logits, prefix, trie, hard_filter): top_token_ids top_k(logits, k200) valid_scores [] for token_id in top_token_ids: next_prefix prefix [token_id] if not trie.has_prefix(next_prefix): continue if not hard_filter.check(next_prefix): continue valid_scores.append((token_id, logits[token_id] rule_bonus(next_prefix))) return renormalize_top_k(valid_scores, k8)这里rule_bonus是我自己的做法如果某个商品路径的价格接近用户 query 里的价格区间就加 0.1 分尺码精确匹配再加 0.05 分。它不改变模型学到的排序偏好只是在约束空间内做微调。实测下来硬过滤不加会产出垃圾路径加了之后“生成结果可交付率”从 78% 提升到 96% 以上。3.4 双路召回生成式不是替代是互补落地时不要脑子一热把向量召回下掉。更稳妥的做法是“双路召回、混合分发”一条路保留现有向量召回兜底保证长尾覆盖另一路上生成式召回专门吃复杂约束 query 和交易意图强的 query。我的线上配置大概是向量召回出 500 个候选生成式召回出 20 个候选两路做去重后统一进粗排和精排。生成式这路如果不足 5 个就直接用向量召回结果补位保证该路最低供给。同时在 query 侧放一个规则开关当 query 里检测到尺码、价格区间、型号组合等显式约束时才把生成式召回的流量权重调高普通泛搜词还是以向量召回为主。链路召回数量核心作用兜底策略向量召回500泛语义、语义相似款全局覆盖生成式召回20多约束、交易意图、新品冷启不足补向量混合结果500粗排精排使用去重、过滤、规则加权这里要特别提一句线上同时跑两条链路不代表工作量乘以二。向量链路和生成链路在特征层面可以共享尤其在 query 编码阶段复用同一个 encoder能省不少机器资源。4. 落地过程中的关键问题与排查实录生成式召回写 demo 容易上线难。我在实际项目中踩过四个典型问题每一个都值得单独拿出来讲。4.1 Beam Search 结果同质化第一次把生成式召回跑通之后我看了线上 case 差点没晕过去用户搜“黑武士 AJ1”生成式召回返回的 10 个商品里有 7 个是同一款式的不同尺码价格也都挤在同一区间。原因是 beam search 在最大化序列概率时天然会走向少数几条高概率路径模式坍塌非常明显。解决思路不是把 beam search 换成贪心采样而是做“多样性约束解码”。我用的是 diverse beam search把 beam 分成几组每组内部正常打分组间加互斥惩罚项再叠加一个“n-gram 阻断”同一商品属性序列里不允许出现重复的品类 token、系列 token。调整参数后前 10 个结果的覆盖款式数从 2~3 个提升到 7~8 个。4.2 新品冷启动模型“记不住”新商品生成式召回的本质是把商品索引存进模型参数但模型参数更新有滞后新上架商品天然吃亏。我试过几种方案最有效的是“属性显式化 向量侧新品补充”在商品序列输入中加入商品 embedding 作为 side feature模型不需要从头“记住”新 ID只要学会“相似属性对应相似决策”新商品就能通过属性泛化被生成出来。同时新品在销量和点击量不足的情况下单纯靠生成式召回很难出头。所以我把新品分成了“策略新品”和“自然新品”前者在召回阶段直接加固定加分后者走正常生成路径。这个划分逻辑写入配置中心运营可以随时调不用重新训练模型。4.3 延迟和算力成本不能为范式跃迁把预算打穿生成式召回的线上延迟压力主要在解码。第一版上线时单次 beam search 平均耗时接近 120ms线上根本扛不住。后来做了三个优化第一beam size 从 12 降到 8第二解码模型的层数从 12 层砍到 6 层第三商品 ID 字典的 token 化改成前缀分段生成相当于先生成品类、再生成系列、最后生成具体的 ID 后缀每一步都能共用前缀树减少无效解码路径。优化后 p99 延迟降到了 35ms 左右放在召回阶段可以接受。另外生成式召回不需要为每个 query 全量扫描商品库它的计算量正比于解码步长和 beam size而不是商品总量这一点和向量索引相比反而更有优势。4.4 评估指标失灵离线涨了在线没涨这是最坑的一点。按传统离线召回指标看比如 Recall100、Hit20生成式召回通常不如向量召回因为它的候选总量小。但如果只看成交导向指标生成式又明显好。最后我的做法是拆成两层来看。离线层用“约束满足召回率”和“有效成交召回率”代替传统 Recall100。所谓有效成交召回率是指最终进入精排且在线上产生成交的商品里有多少比例是全链路模型在召回阶段就能覆盖到的。这个指标更贴近业务结果。在线层则重点看搜索成交转化率、GMV 贡献、长尾商品曝光占比。我在 A/B 实验中看到过这样的曲线生成式召回在 Click-through Rate 上小幅上涨但成交转化率涨得更快原因是它生成的商品更符合尺码和价格约束用户点进去就能下单。指标层级指标名称波动方向说明离线约束满足召回率显著提升硬约束过滤后的有效覆盖离线传统 Recall100可能下降候选总量小不要用它做最终判断在线搜索成交转化率正向生成式召回的最终检验指标在线长尾曝光占比正向新品和长尾款更容易进入候选池5. 最后聊聊范式跃迁的边界我看很多团队在折腾生成式召回时容易陷入两个极端要么觉得它是银弹要么觉得它只是论文里的玩具。真实情况是生成式召回适合做“精确意图的具象化”不适合做“大规模模糊召回的替代品”。交易搜索的下一步大概率是向量召回兜广度、生成式召回兜精度、倒排索引兜确定性三者互相补位。我个人在实际操作中的体会是范式跃迁不等于推倒重来。最先要做的不是在线上轰轰烈烈换引擎而是先把离线评测体系改成“约束满足率 成交召回率”的组合再用小流量 A/B 验证生成式召回在复杂 query 上的增量。数据质量和目标函数设计永远比模型参数量更重要。你能把“用户找什么”具象化成一段可生成的商品属性路径就已经赢过了绝大多数还在卷向量距离的团队。
返回列表