☰
生成式召回在交易搜索中的原理、落地与踩坑实践
2026/11/3 5:30:21 网站建设 项目流程

1. 先看清现状:传统召回为什么够了又不够

这几年的搜索架构圈,提到召回言必称向量检索。尤其交易搜索,感觉不做个embedding召回都不好意思出门。但真正在线上打磨过搜索链路的人心里都清楚:向量检索是很强,可它解决的是语义近邻问题,对于很多交易场景里颗粒度很细、约束条件很多的 query,向量检索其实一直在靠粗排和重排给它兜底。另一个方向开始被认真讨论,就是生成式召回。它换个玩法,不比较向量距离,而是让模型把商品 ID 当成目标序列直接生成出来。这篇文章我会从得物交易搜索这类标品、潮流品交易平台的视角,把生成式召回的原理、难点、工程落地和踩坑过程讲清楚。

1.1 倒排索引负责稳定,但稳定不等于懂你

倒排索引在交易搜索里面依然是不可动摇的地基。用户搜"西装外套",倒排能精确命中标题里的西装、外套;搜"nike dunk low",品牌词和型号词命中。它最大的优点是可控,返回结果是可解释的,词项权重也好调。

但问题也很明显,query 一旦口语化、带情绪、带场景就抓瞎。用户搜"通勤穿的不累脚的单鞋",倒排结果大概率只有"单鞋"匹配,而"通勤""不累脚"这些词在商品标题里几乎不存在。再比如错别字"高绑鞋"和"高帮鞋",倒排拿到的是完全不同的词项,召回直接掉底。

还有更麻烦的是,交易搜索的属性很多都是结构化字段,颜色、尺码、价格带、品牌,这些在倒排里要靠额外字段过滤或嵌进 query 里,索引就越来越重。我见过不少团队把倒排索引里的 term 加到几个 G,还不是因为商品多,而是为了把各种属性都塞进词表,结果长尾 query 的 docFreq 越来越大,相关性排序越来越难做。所以说倒排负责的是"稳定召回",它不是不懂你,而是它只用字面来懂你。

1.2 向量检索补上了语义,但露出了更大的成本

向量检索解决了字面不匹配的问题。"不累脚的单鞋"可以 embedding 到一个和"舒适 单鞋"相近的位置,召回一批标题里带"舒适""软底"的商品,这是倒排做不到的。

但向量检索并不是没有代价。第一,它做的是全局相似度比较,Query 向量和几百万个商品向量一起算距离,即使有 ANN 结构,本质上还是"在空间里找近邻",对结构化约束无能为力。用户说"800 元以下的黑白 AJ1",query 向量根本表达不清这个筛选条件,只能把它压到一个稠密向量里,过滤全扔给下游。

第二,向量模型的训练数据天然有头部倾斜。点击和成交集中在爆款上,商品向量对头部门类学得好,尾部商品、新品没有那么充分的行为信号,表征质量就差。结果就是向量召回往往召回的还是那些本身就容易买到的商品。

第三,工程上 embedding 要维护双塔,商品侧索引要增量更新,模型要定期重训,链路越长,监控面越多。这不是说向量检索不好,而是想说明:它在交易搜索里并没有解决所有问题,只是把最难的那部分往后挪了。

1.3 生成式召回:把检索从"找相似"变成"写答案"

生成式召回的核心思路,是把商品 ID 当成一个目标序列,模型输入 query,输出一个有序的 ID 序列。前提是有一个足够好的 ID 表示,以及一个足够大的模型来记忆 query 到商品 ID 的映射。

你可以这样理解:倒排索引像翻字典,向量检索像在地图上找最近的点,生成式召回更像是一个熟悉商品的导购,你问一句"下雨天能穿的高帮鞋",它直接告诉你对应的商品编码。

这不是一个纯理论概念。业界已经有“可微搜索索引”这类工作,并且已经有平台把生成式召回从论文搬到线上。对交易搜索来说,它最有价值的地方在于:生成过程不是单纯求相似度,它可以隐含地把用户意图、商品属性和上下文一起编码进生成目标。所以严格说,它不是来替代向量检索的,它是给召回链路注入一个之前没有的信息维度。

2. 交易搜索里的生成式召回,第一步是把商品 ID 变成模型能生成的东西

2.1 为什么不能直接让模型生成百万级商品 ID

一开始最容易踩的坑,就是直接让 seq2seq 模型去生成几十万甚至几百万的商品 ID。问题很明显:输出词表太大,训练样本稀疏,模型很难收敛。而且商品 ID 本身是随机分配的,模型要死记硬背"AJ1 对应 13820001"这种映射,学不到泛化能力。

所以现在比较通用的做法,是先把商品空间做聚类和分层,生成"语义 ID 路径"。比如"鞋子>篮球鞋>实战篮球鞋>商品ID",或者"品牌>系列>类目>商品ID",商品在路径上越深,模型需要预测的 token 越少。

这一步做到位的标准是:同一个 query 下相关的商品尽量共享一条较短的 ID 前缀路径,路径上的每一级都有明确的业务含义。这样模型不需要把每个商品的独有 ID 都记住,只需要预测到叶子类目或 cluster;如果商品 ID 空间太大,也可以只生成 cluster 级 ID,再用一个传统的倒排/向量召回在这个 cluster 内取商品。等于让生成式模型先做好"意图到目录分类",把精细匹配交给后面的检索,这是目前工程上最高性价比的玩法。

2.2 如何构造语义 ID:聚类、层级和映射表缺一不可

构造语义 ID 不是拿商品 ID 随便拼个前缀,它需要一套完整的映射体系。我的建议是至少包含三层:

层级含义示例作用
第一层业务类目男鞋-篮球鞋-实战鞋表达品类意图
第二层高频属性簇品牌-系列-风格-价格带表达交易筛选条件
第三层商品级 ID商品独立编码或共享簇 ID定位到具体商品

第二层是交易搜索特别需要的,因为用户经常搜"联名""黑武士配色",这些词跟类目不是一个维度,得单独做出来。第三层不应该全量给每个商品一个独立 ID,否则词表又膨胀了。工程上会做“冷门商品共享 cluster ID”的处理:把所有商品按向量或内容先聚类到几十万个簇里,每个簇给一个 ID,线上再用一个就近映射表把商品挂到簇下面。新商品上线时可能还没有独立簇,先挂到相似商品簇,这样冷启动问题也缓解了一部分。

映射表要支持动态更新,比如类目调整、商品资料修改。我见过直接把语义 ID 拼接字符串存到数据库的,上线一个月之后类目改名,商品全找不到了,这就是没有把映射表当基础设施来设计的下场。

2.3 训练数据的构建:不能直接拿点击日志当标准答案

生成式召回的本质是监督学习,目标序列是商品 ID 路径,那训练数据从哪来?直接拿点击日志的话,模型学到的多半是"热门商品比我相关",而不是"这个 query 真正该召回什么"。

我的做法是把训练样本按行为强度分级:点击给基础权重,收藏/加购权重高一档,下单支付权重再高一档。同一个 query 下,如果用户点击了 A 但买了 B,那 B 的样本 loss 权重应该大于 A。而且要控制一个 query 下的正样本数量,不能把整个点击列表都灌进去,否则模型会试图在一个生成序列里包含所有点过的商品,最终学成"推荐一堆互不相关的爆款"。

除了正样本,负样本也很重要。同一个类目下的曝光未点击商品是很好的硬负样本,其他类目的随机商品可以作为软负样本。损失函数上,主流还是用序列交叉熵,但光交叉熵会让模型只优化 next token 预测,忽略商品之间的排序关系。有条件的话,可以叠加一个 pairwise loss,让生成序列里相关商品的概率排序跟用户真实偏好一致。这一步做不做,直接决定生成式召回到线上之后是"能看"还是"能打"。

训练样本构造这部分,可以用一段伪代码来理解重加权的思路:

# 伪代码:训练样本重加权 def build_training_sample(query, behaviors, base_hit_set): weight = 0.0 for hit in behaviors: if hit.is_order: weight = 3.0 elif hit.is_cart: weight = 2.0 elif hit.is_click: weight = 1.0 # 旧链路已能召回,降低学习权重 if (query, hit.item_id) in base_hit_set: weight *= 0.3 else: # 旧链路漏掉的成交样本,重点学习 weight *= 2.0 return (query, semantic_id_path(hit.item_id), weight)

这段代码不能直接跑,但思路很关键:让模型去学"现有链路漏掉的成交",而不是学一个全量点击分布的复读机。

3. 模型训练和服务端设计:从能生成到快生成

3.1 生成式召回跟向量检索不是互斥,训练目标里要写清楚

我见过很多团队评估生成式召回,习惯性地拿它和向量检索的 Recall@K 做对比,比不过就说方案不行。这其实犯了一个方向性错误。生成式召回的目标应该是"在向量检索和倒排检索拿不到的高相关候选里,补充出新的成交机会"。

所以训练的时候,正样本不应该是全库的正样本,而应该是"在现有链路中没有被召回到但最终成交了"的样本。换句话说,生成式模型要学的不是模仿一个更好的向量召回,而是学习现有召回链路的盲区。如果直接把全量曝光点击样本拿过来,模型很容易变成向量检索的孪生兄弟,线上融合之后重叠度高,没有任何增量。

这一点想明白之后,整个训练流程就不一样了:你需要先生成现有 Base 线的训练集,把 Base 能够召回的 query-doc 对从训练集里去掉或者降权,把 Base 漏掉的成交样本提权。这听起来有点绕,但它才是"范式跃迁"里“跃迁”二字真正的含义——不是同一个目标的精度提升,而是换一个目标函数。

3.2 服务端不能直接架一个大模型做 beam search,延迟扛不住

生成式召回看起来很美,但服务端直接套一个 T5 规模的 seq2seq 模型,线上 p99 延迟分分钟过百毫秒,交易搜索根本接受不了。这里有几道绕不开的坎。

第一,输出空间不是纯文本。你需要用 constrained decoding,保证每一步生成的都是合法 ID 路径上的 token,否则模型随便生成一个不存在的类目组合,下游映射不到商品。工程上可以维护一棵前缀树,解码时把非法分支直接 mask 掉。

第二,beam search 不能太大。beam=4 和 beam=16 延迟差很多,收益却不一定差很多。我自己的经验是,生成式召回在交易搜索里先做"意图到类目路径"的预测,beam size 只用 4 到 8 就够,后面接向量检索去取商品,等于承担的是粗略但是准确的过滤。如果把生成式直接做商品级,beam 开到 32 甚至更高,延迟不可控,线上压力很大。

第三,必须做模型量化和服务化。实际操作中,int8 量化能省一半显存和 30% 左右延迟,效果损失在 1% 以内。如果还想再快,就蒸馏一个 student 小模型专门做商品 ID 预测,teacher 负责复杂 query 的意图生成,student 负责快速解码。小模型在线上的吞吐量提升非常明显。

3.3 在线链路怎么融合:给生成式一路候选,而不是让它当唯一主干

线上最稳的接入方式,是把生成式召回作为一路新的候选源,跟倒排、向量并行,最后在粗排/精排阶段做融合。具体配合上,我推荐两种模式。

第一种模式是"生成式只出目录,向量在目录内取商品"。生成式输出几个叶子类目或商品簇之后,由向量检索在限制条件下做近邻搜索,候选被限制在更相关的局部空间,准确率更高。

第二种模式是"生成式直接出商品候选",适合那些意图非常明确的 query,比如搜具体的型号、联名款,生成式可以直接吐出 5 到 10 个高相关的商品 ID,跳过向量这一跳。

线上实际跑的时候,我会给生成式这一路单独一个召回配额,比如总候选里给 10% 到 20% 的配额。这个配额不能拍脑袋,要看增量效果。初期可以先所有 query 都接,之后按 query 意图分流:用户搜明确型号词时给生成式更多配额,搜宽泛风格词时少给一些。这个过程需要做好埋点,区分每一路召回的来源、位置和成交归因,否则后面根本说不清楚到底哪一路在真正贡献增量。

4. 落地过程中踩过的大坑和排查实录

4.1 新商品几乎永远不被生成:冷启动要从 ID 设计就解决

生成式召回上线后,第一个被业务挑战的问题就是:刚上新的商品,怎么一条都生成不出来?这是预料之中的,因为训练数据里新品没有行为记录,模型没有见过它的 ID,beam search 时它的概率天然很低。

但这个坑其实是 ID 设计的问题。我做第一版时把"商品 ID"作为最终的生成目标,新品一多,词表就要扩容,老模型完全没有能力预测新 ID。后来改成"类目/属性簇 ID+商品挂接"的方式后,新品只要挂到了已有簇下面,模型生成簇时就能把新品带出来。尤其对于鞋类这种上新节奏快的品类,这个改动基本解决了冷启动问题。

另外还需要做"新 ID 映射兜底":生成式模型如果输出了一个簇 ID,在线通过映射表去拉商品列表时,直接把该簇最新上架且库存充足的商品排进去。相当于模型不需要认识每个新商品,只需要对上位目录有信心,剩下的事情交给系统。

4.2 生成结果越来越像"复读机",爆款霸屏严重

上线一段时间后,运营反馈搜索页太单调,同一个 query 下生成式召回的 5 个结果里,可能有 3 个是同一个畅销款。这个问题本质是样本不均衡:成交样本里爆款占了绝大多数,模型只要不停输出高概率爆款,loss 就能降得很快。

我这里有两个处理手段。一是对同一类目下的训练样本做数量上限约束,把每个叶子类目下的正样本数压在某个阈值内,避免爆款类目样本量把其他类目压死。二是在 beam search 解码时加多样性策略,比如已经选了某个商品或某个簇之后,对同一个簇内的其他候选做降权惩罚。这个惩罚系数需要调,太强会伤相关性,太弱等于没加。我通常从 0.8 开始试,看线上翻页率和多样性指标的变化。

还有一个容易被忽略的点:业务方可能把"多样性"误解成"均匀分配",结果到处都有但都不是用户想要的。多样性的正确目标是"在同样相关的前提下,给用户多几种可能",而不是牺牲相关性硬凑。做评估时要单独统计"生成式新召回里的成交率",而不是只看展示多样性。

4.3 生成式召回的候选和向量召回重叠率超过 60%,等于白做

有一次我们做融合效果分析,发现生成式召回的候选和向量召回重叠率很高,新增成交几乎没有。复盘之后,问题出在训练数据上——我用了全量点击行为作为正样本,这等于让模型去拟合一个和向量模型"差不多的语义相似度函数",从数据分布上就没法产生增量信息。

后来训练样本改成"Base 召回遗漏但实际产生成交/加购"的样本为主,并且专门给这类样本更高的 loss 权重。这样生成式模型才会把注意力放在那些向量模型觉得"没那么像"但交易价值很高的序列上。调整之后,重叠率降到 30% 左右,单路召回率虽然看着没涨,但融合之后的成交转化明显提升。

这里也想给团队提个醒:评估生成式召回,不要单看召回率。因为它的贡献是用"低重叠的高质量候选"去给排序模型提供新输入。如果一个候选在向量召回里已经有了,排序模型早都见过了,信息量是零;反而那些向量召回没有、生成式独有的候选,哪怕质量看起来普通,才可能是真正的增量。

4.4 线上延迟和算力成本:量化蒸馏之外,还能做 query 分流

生成式召回上线后,算力和延迟永远是躲不开的成本。模型量化、蒸馏、beam 裁剪都做了之后,还可以在 query 分流上下功夫。

实际线上,有一部分 query 是极度热门的,比如"dunk""空军一号",这些词倒排和向量召回已经非常准,用生成式去生成是典型的过度设计。可以让生成式模型只处理长尾、口语化、属性叠加复杂的 query,热词直接走原有链路。

判断条件可以做一个轻量分类器,核心维度大致是这样:

分流维度适合生成式召回保留原链路
是否包含明确品牌型号词否是
是否包含多个属性词是否
过去 7 天该 query 在 Base 链路无成交比例高低
query 长度长短
意图口语化程度高低

按这些维度把 query 分成"适合生成式"和"保留原链路"两拨,线上 A/B 后你会发现整体延迟指标下降一大截,而成交没有折损,甚至由于分摊了算力,高价值 query 反而获得了更多计算资源。

5. 评估体系:不是只看召回率,交易搜索要盯住增量成交

5.1 离线阶段:合法率、目录准确率、增量 Recall 缺一不可

离线评估生成式召回,如果只看 Recall@K,很容易被带偏。我建议至少同时看三个指标:

指标说明目标
合法率生成的 ID 路径是否真实存在于商品映射表大于 99.5%
目录准确率生成的叶子类目和 query 意图是否一致越高越好
增量 RecallBase 线召回不到的成交商品里,生成式召回到的比例重点看趋势

合法率如果低于 99.5%,说明 constrained decoding 有问题,或者是语义 ID 字典和线上映射表不同步。目录准确率是中间目标,可以用 Fine-grained 的意图标注样本去评估。增量 Recall 才是生成式召回存在的意义。

有些团队还会看生成序列的稳定一致性,比如同一 query 在近重复样本下输出是否抖动。生成模型天然有随机性,如果 beam search 的 top1 经常变,会让排序模型以为用户意图变了,所以线上最好固定 seed 或使用确定性解码。

5.2 线上阶段:A/B 实验要单独拆出增量成交

线上评估最怕的是把生成式召回塞进一个大的搜索实验里,整体效果看涨了,但说不清是哪一路的作用。更麻烦的是,整体效果没涨,但某一细分人群明明有提升,结果被大盘稀释掉。

我的建议是做双层实验:先做"生成式召回开关"的 A/B 实验,即对照组走原链路,实验组在原链路上多并一路生成式召回,这样能看到全局增量;再做"分桶配额"实验,在开启生成式召回的基础上,把生成式候选配额从 10% 调到 20%,看边际收益是否递减。

核心指标不要只盯点击率。交易搜索最好直接看"新增召回带来的支付成交额"和"新增召回商品的次留/复购"等。有些生成式召回出来的商品,用户没点,可能只是时机不对,不能否定它的价值。至少要给这个实验跑一到两周,覆盖不同的消费节奏,不要跑一两天就下结论。

5.3 从交易视角理解"召回率悖论"

交易搜索里有一个经常被忽视的现象:纯召回率指标越高,不一定 GMV 越高。原因在于旧链路召回来的候选大多是爆款,排序模型对爆款具备很强的转化预估能力,而召回率稍低一点可能反而把位置让给了更有购买意图的长尾新品。

生成式召回真正的贡献是提高候选集合的信息量,而不是提高候选集合的"平均相似度"。评估它的时候,要用"排序模型在拿到生成式候选后能否学到新信号"来定义价值,而不是把每一路召回的结果单独拿出来和全部成交样本算 Recall。想明白这一点,你才会理解为什么我说"生成式不是第二个向量检索"。

6. 我个人的实践体会和后续可以做的方向

6.1 别把范式跃迁理解成推翻旧链路

这篇文章写到最后,想强调一点:生成式召回在交易搜索里落地,最高效的路径不是一步到位用生成模型取代整个召回链路,而是把它变成一条独立的、有增量信号的候选通路,跟倒排和向量互相配合。它真正的优势不是"比向量更懂语义",而是"换了一种目标函数,能发现旧链路看不到的交易机会"。

我在实际项目中的体会是,生成式召回的前期投入主要在数据清洗和语义 ID 设计上,模型本身反而不是最难的。如果一上来就调模型结构,忽略 ID 表示和训练样本构造,大概率上线之后会发现它只是一个花钱很多的向量召回替代品,没有任何新意。

6.2 下一步值得尝试的方向

从得物交易搜索这样的结构化商品场景出发,我觉得有三个方向很有潜力。

一是把用户历史行为序列拼到 query 前缀里,让生成式模型直接做个性化召回,生成的商品 ID 会更贴近个人偏好。二是引入多模态输入,用户用图片搜相似款、搜"这件衣服的同款不同色",图片特征和文本特征一起作为 encoder 输入,生成式的灵活性比向量融合更强。三是用强化学习直接优化交易目标,把成交、客单价放进解码策略里,让生成结果不只看相关性,还看商业价值。

这些东西短期内不一定都能在线上落地,但它们的共同点是从"模型记住映射"走向"模型理解交易意图"。对做搜索的人来说,这才是真正有意思的部分。

最后分享一个小技巧:如果你也想在小规模流量上验证生成式召回,先别急着搭完整训练平台,拿一周的搜索日志,把 Base 线漏掉的成交样本统计出来,看看这类样本在类目分布上是否有明显的结构性特征。如果答案是"有",那生成式召回就值得做;如果答案是没有,那说明问题可能不在召回,而在排序或商品供给,你先别折腾模型。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询