1. 搜推一体,到底在解决什么问题?
先说结论:我说的搜推一体,不是把搜索框和推荐流硬塞进同一个页面,而是把搜索和推荐背后那套"理解用户、生成候选、排序供给"的底层能力统一起来。这两年业内讲搜推一体讲得很凶,各家大厂都在往这个方向调架构,核心原因很简单:搜索和推荐过去各自为战,重复造轮子造到肉疼。
先复盘一下传统的双栈架构是什么样的。搜索团队维护一套倒排索引、一套查询理解模块、一套相关性排序模型;推荐团队又是另一套用户画像、另一套向量召回、另一套CTR预估模型。两边各自有数据管道、各自有特征平台、各自有AB实验平台,甚至同一个用户的行为日志要分别清洗两遍,存两份。结果就是消耗双倍的计算资源,模型效果却未必双倍,因为两边对同一份用户行为数据的理解是割裂的。
真正推动大家下决心做搜推一体,还有一层更深的需求:用户的预期已经被"刷"出来了。用户在信息流里被推荐惯坏了,来到搜索框,也不再满足于"搜到什么看什么",而是希望搜索结果也像推荐一样懂我。反过来,推荐流里大量用户其实带着模糊的找东西意图,光靠兴趣探索效率太低,需要借力搜索的强意图信号。这种情况下,两种形态的能力必须互相借力,而不是各搞各的。
所以搜推一体并不是要把"搜索"和"推荐"这两个产品形态合并成一个,而是统一底座、统一数据、统一召回、统一排序,再在应用层按场景差异化输出。把"搜"的强意图理解和"推"的个性化预估装进同一套系统里,这才是这个标题想聊的核心。
2. 四层架构:从数据到策略的统一骨架
搜推一体落地通常都绕不开一套四层架构,每一层都有自己的边界和职责。我习惯把它拆成数据与特征层、召回与候选生成层、排序与重排层、服务与策略应用层。下面逐层拆解。
2.1 数据与特征层:一切模型的地基
这一层最枯燥,但也最决定成败。搜推一体能不能真正"一体",首先看用户行为数据是不是一份、特征是不是一套。
很多团队在立项时容易犯一个错:把搜索和推荐的行为日志各保留各的,搜索日志有query、有点击位置、有翻页深度,推荐日志有曝光、有停留时长、有滑走速度,两边的schema对不上。等到要做融合模型的时候,样本拼接就变成一场灾难。正确做法是统一行为日志标准,所有行为统一抽象成"用户在什么场景、什么时间、对什么item、做了什么事、结果如何",搜索里的query当成一个场景上下文,推荐里的session也当成一个场景上下文,行为字段统一补齐。
特征层面,搜推一体化之后,最值钱的是共享特征。用户长期兴趣、实时兴趣、统计特征、上下文特征,这些在搜索和推荐里是可以完全复用的。我见过一个比较落地的方案:特征平台统一管理特征,分在线特征和离线特征两套通道,在线特征走Redis或者本地缓存,离线特征走Hive/Spark批量产出,特征口径完全一致。
离在线一致性是这一层最容易翻车的地方。离线训练时特征是从日志里回放的,在线服务时特征是从线上请求实时算的,两边一旦口径不一致,模型上线就是一场玄学。比较稳妥的做法是做特征快照校验,每天抽样比对离线回放特征和线上记录特征,偏差超过阈值就报警。
还要注意一个细节:embedding统一。搜索侧的query embedding和推荐侧的用户兴趣embedding、item embedding,必须在同一个向量空间里训练,否则后面做向量召回融合的时候维度都对不上。统一embedding的做法一般是拿用户行为序列和query-item交互数据联合训练双塔,这属于召回层的内容,但数据基础在这一层就要铺好。
2.2 召回与候选生成层:把"搜"和"推"放进同一张候选池
召回层的痛点是"搜"和"推"天然有不同的候选生成逻辑。搜索强依赖倒排索引和字面匹配,用户搜"iPhone 15 价格",你给他推热门手机壳属于找死。推荐侧强依赖向量召回和协同过滤,用户看了一堆数码评测,你得能从海量视频里捞出来他可能感兴趣的。这两套逻辑,过去是两套代码、两套索引、两套服务,融合的关键是把它们统一进一个候选池管理框架。
比较务实的架构是:多路召回统一抽象,每一路召回只负责产出候选集,然后由候选池管理器做合并、去重、过滤、打分。倒排索引召回可以作为其中一路,向量召回作为另一路,热门兜底召回再作为一路。每一路都有自己的超时预算和召回数量上限,最后由统一接口吐给排序层。
这里有个关键设计:query和用户向量要不要统一?我倾向于不强行统一,而是做成两个入口、一个出口。搜索场景以query为中心,向量化query去检索item;推荐场景以用户为中心,向量化用户去检索item。底层的向量检索服务可以是同一个,但索引可以分场景构建,也可以共用一套item向量,只是查询向量的来源不同。
多路召回融合时,我踩过的坑是重复候选没有做好归一化。同一篇文档被倒排召回、又被向量召回,如果两条路都是同一个item,合并时要去重,但去重要保留召回来源信息,因为来源对排序层的特征非常有价值。你在特征里写上"is_from_vector"、"is_from_inverted",排序模型就能学到不同召回路线的质量差异。
召回层的验收标准不是召回率越高越好,而是"性价比召回":候选集数量控制住,排序层才有时间做精细计算,整体延迟才压得住。一般建议召回总量控制在几百到一两千的量级,具体看业务场景。
2.3 排序与重排层:相关性和个性化怎么掰手腕
排序层是搜推一体里最微妙的一层。搜索侧过去最看重相关性,用户搜"Java并发编程",你不能因为他平时爱看美食就把川菜谱排上来。推荐侧过去最看重个性化,用户喜欢什么,就多推什么。搜推一体之后,目标函数必须同时容纳这两股力量,否则融合就是个笑话。
我的做法是多目标建模,把相关性目标和个性化目标放进同一个模型,通过样本权重和loss设计调节两者的平衡。相关性目标可以用query-item的语义匹配度、点击率、深度转化来刻画,个性化目标可以用用户维度的留存、时长、关注等指标。模型结构上可以做成shared-bottom + 多个task tower,底层共享特征表达,上层各自输出预估值,最后用乘积或者加权的方式融合。
相关性和偏好打架最典型的例子是:用户是数码博主,他搜"口红",是真的要买口红送人,还是随手搜着玩?传统搜索模型会因为他的历史行为全是数码内容,把相关数码产品排上来,这其实是相关性和个性化错位了。解决思路是引入"当前query意图区分"机制,如果query带有明确找物意图,相关性权重拉高;如果query语义模糊,个性化权重拉高。这个机制可以是一个轻量级意图分类模型,也可以干脆用规则根据query形态判断。
重排层也别忽略。用户从排序层拿到一个精排列表之后,还需要做多样性打散、已读过滤、商业坑位插入、去重保底。重排层是搜推可以共享策略最多的地方,因为两种形态都面临"不能连续推同类内容"、"不能把用户已经看过的东西反复排前面"这类问题。重排层建议做成独立的可配置服务,每个场景一套参数,而不是在排序模型里写死规则。
2.4 服务与策略应用层:网关、实验、场景化干预
最上面这层,是搜推一体能不能平滑落地的关键。服务层要做的事情很多:统一接入网关、统一RPC协议、统一超时控制、统一降级策略、统一AB实验分流。
我建议搭建一个统一推荐/搜索网关,接收客户端请求,解析场景ID,根据场景ID去请求不同的策略组合。搜索请求和推荐请求都走这个网关,网关内部再路由到召回服务、排序服务、重排服务。好处是两边的流量调度、限流、熔断、降级都在一个地方管控,不会出现搜索和推荐各自出故障、各自发告警、互相甩锅的情况。
延迟指标也要在这一层定清楚。搜索场景用户站在搜索框前等结果,P99延迟一般要控制在200毫秒以内;推荐场景用户是在信息流里滑动,P99可以放宽到几百毫秒甚至秒级。但这两个场景不应该各自搭一套服务,而应该共用底层服务,只是通过链路裁剪来控制延迟。搜索场景可以少跑几路召回、排序模型用小一点的模型;推荐场景可以多跑几路召回、排序模型更复杂一些。
AB实验平台最好也是统一的。搜推一体之后,一个模型改动可能同时影响搜索和推荐,如果实验平台还是两套,你没法判断效果变化到底是搜索侧引起的还是推荐侧引起的。统一实验平台之后,可以做到同一模型在多个场景同时分流,灰度发布和回滚都走一套流程。
3. 四阶段落地:从现状到搜推一体的实操路径
四层架构是"目标态",真正落地不可能一晚上推翻重来。我通常把落地路径拆成四个阶段,每个阶段都有明确的交付物和验收标准,团队按阶段推进,风险可控。
3.1 阶段一:统一基础设施与数据管道
第一阶段不做任何模型融合,先把地基打平。目标是让搜索和推荐使用同一套数据管道、同一套日志规范、同一套特征平台。
实操上,第一步是盘点现状:搜索和推荐各自有哪些数据源、哪些日志表、哪些特征任务、哪些离线管道。第二步是设计统一schema,把两边行为日志统一到一张宽表,场景字段用scene_id区分。第三步是迁移数据管道,让两边都往统一的目标表里写数据,旧管道并行跑一段时间,校验数据一致性之后再下线。
这个阶段最容易被业务方挑战:为什么折腾半天,线上效果一点没变?我的回答是:这个阶段不追求效果提升,追求的是"效率提升",为后面的模型融合铺管道。但为了让团队有感知,可以在统一特征平台上线后,先给搜索侧和推荐侧各做一个特征对齐的小实验,哪怕只是让两个团队用同一套特征调试工具,也算阶段产出。
验收标准建议:行为日志统一率达到100%,离线特征任务统一率达到80%以上,离在线特征一致性校验通过率超过99%。
3.2 阶段二:召回层融合,先让候选集共享起来
第二阶段做召回融合,这是搜推一体最容易出效果的一步,因为召回层面共享候选集,几乎不影响线上主链路,风险低、见效快。
具体做法是搭建统一的候选池服务,把搜索的倒排召回、推荐的向量召回、以及一些公共的策略召回(热门、新品、运营强插)都接入进来。搜索请求进来,除了走倒排召回,也可以跑一路向量召回,用query向量取回一些字面不匹配但语义匹配的item;推荐请求进来,除了走用户向量召回,也可以根据用户最近的搜索词引导一路搜索式召回。这样搜索能借推荐做语义扩展,推荐能借搜索做意图聚焦,两边都受益。
阶段二有个隐蔽的坑:搜索和推荐的候选质量分布差异巨大。搜索候选集中大量item与query高度相关,推荐候选集则是按用户兴趣扩散的,直接把两边候选混合,会导致排序分布不稳定。解决思路是在候选池里给每个item打上场景来源标签和召回分数归一化信息,排序模型用好这些特征,而不是在召回层强行抹平分布。
验收标准:统一召回服务覆盖率超过90%,至少两路召回被搜索和推荐场景同时使用,搜索场景的语义召回带来的增量指标(如点击率、无结果率下降)有显著正向。
3.3 阶段三:排序模型一体化,用多目标打通目标函数
第三阶段是做排序模型的融合。这是技术含量最高、也是最容易翻车的一步,因为搜索和推荐的历史样本分布、标签定义、评估指标完全不一样。
我的建议是做两件事。第一件事是统一样本生成逻辑,搜索样本和推荐样本都从统一行为日志里生成,样本特征完全一致,区别只在于场景标签。第二件事是训练一个多目标、多场景的排序模型,模型结构上底部共享,上层按场景和任务分tower,用多任务学习来平衡相关性和个性化。
训练时要特别关注样本权重。搜索样本相对稀疏但意图明确,推荐样本量大但噪声多,直接混合训练,模型会被推荐样本带偏。我习惯的做法是给搜索样本更高的权重,或者在loss里给搜索场景的任务单独加权重,让模型既学到推荐侧的泛化能力,又不牺牲搜索侧的相关性精度。
评估方式也要改。不要只看整体的离线AUC,要按场景拆开看,搜索场景单独看搜索指标(如NDCG、Query点击率),推荐场景单独看推荐指标(如时长、点击率),同时看跨场景的泛化指标。如果搜索指标掉了,哪怕推荐指标涨了,也不能上线,因为搜索场景的用户感知更敏感。
验收标准:离线评估中搜索场景和推荐场景核心指标均不低于各自基线,在线实验至少一个场景显著正向,另一个场景不显著负向。
3.4 阶段四:策略平台化,让AB实验和实时迭代跑起来
最后一个阶段是平台化建设。搜推一体真正跑起来之后,你会发现模型迭代、策略调整、场景配置的频率越来越高,如果没有统一的策略管理平台,运维成本会爆炸。
策略平台至少要做三件事:场景配置管理、实验分流管理、实时监控告警。场景配置管理是指每个业务场景(搜索框、信息流、猜你喜欢、相关推荐)都能通过后台配置召回策略组合、排序模型版本、重排规则参数,不需要改代码发版。实验分流管理是支持一个模型同时跑多个实验组,流量可以按场景、按用户、按百分比灵活切分。实时监控告警是对延迟、超时、降级率、CTR、无结果率等核心指标做分钟级监控,异常自动告警并支持一键回滚。
阶段四的额外价值在于沉淀实时反馈闭环。搜索和推荐的行为数据统一之后,可以实时回流到特征平台,让模型感知用户当前几分钟内的行为。比如用户刚搜了某个品牌,推荐流马上把这个品牌相关的item排上来,这种体验才是搜推一体带给用户的真正感知。
验收标准:新策略从配置到上线不超过一天,实验分流误差小于1%,线上故障平均恢复时间降到分钟级。
4. 落地中的常见问题与排查实录
搜推一体的架构说起来很顺,真落地的时候坑不少。我挑几个自己踩过的、或者帮别人排查过的高频问题,整理成实录,希望能帮大家少走弯路。
4.1 搜索点击样本被推荐"带偏"怎么办
现象:统一训练多目标模型之后,离线评估时搜索场景的AUC下降,线上搜索点击率也跌了,但推荐场景指标在涨。排查下来发现,模型把推荐侧的"点击偏好"学到了搜索侧,导致排序结果偏向用户历史兴趣,偏离query相关性。
处理方案:第一,给搜索场景的样本单独升权,让相关性目标在loss里占比更大;第二,在模型结构上给搜索场景的任务独立出一个tower,不让所有信息都走共享层;第三,在评估集上单独切出一块搜索场景数据,每次迭代都盯紧这块数据的NDCG指标,一旦下降立刻回滚。
这个问题的本质是样本分布迁移。搜索样本里相关性负样本占比很高,推荐样本里几乎都是正样本主导,混合训练时模型会被推荐侧的"什么都喜欢点"带偏。所以样本构建阶段就别忘了在推荐样本里加入适当的负采样,让模型见过足够多的"不喜欢"。
4.2 训练与线上延迟不一致导致的"效果幻觉"
现象:离线实验指标大幅提升,上线之后效果却回吐大半,甚至负向。排查后发现是特征延迟问题。离线训练时用的特征是从完整行为日志里回放的,包括用户点击之后才产生的后续行为特征,但线上请求时这些特征根本还没生成,模型被迫用缺失特征做推理,效果当然差。
这其实是所有实时推荐系统都会遇到的问题,搜推一体之后更明显,因为搜索场景对延迟更敏感,实时特征通道常常为了压延迟被砍掉。
处理方案:第一,建立特征时效性清单,把特征分成"实时可用"和"离线可用"两档,离线训练时只能用线上实时可用的特征,禁止回放未来特征;第二,做特征延迟模拟,训练时故意把最新一段时间的特征截断,模拟线上特征延迟;第三,对实时特征增加兜底值,超时未返回就用最近一次缓存值,特征服务保证可用性。
4.3 超时、降级与流量突刺怎么处理
现象:大促或者热点事件时,搜索和推荐的流量同时暴涨,共用的召回和排序服务被打爆,出现大量超时,甚至互相拖垮,搜索和推荐一起降级,用户体验雪崩。
处理方案:第一,在统一网关层做分级降级,优先保证搜索主链路,搜索可以砍掉向量召回只保留倒排召回,推荐可以砍掉复杂排序模型改用轻量模型;第二,给每个场景设置独立的超时预算,搜索请求总预算200毫秒,召回阶段只给50毫秒,超时直接走兜底;第三,做流量隔离,虽然底层服务共用,但给搜索和推荐各分配独立的线程池或者容器实例,避免互相争抢资源。
这里我特别想说一个理念:搜推一体不是所有服务物理上共用一个实例,而是逻辑上统一、物理上按需隔离。共用的是数据、特征、模型、策略,不代表搜索和推荐的故障域也必须绑在一起。降级预案要在平时就演练,别等流量突刺来了才手忙脚乱。
5. 常见问题速查表
| 问题现象 | 可能原因 | 排查思路 | 解决方案 |
|---|---|---|---|
| 搜索效果被推荐样本拉低 | 样本分布不均衡,相关性权重不足 | 分场景评估指标,对比搜索场景AUC和NDCG | 搜索样本加权,独立task tower |
| 离线指标涨,线上跌 | 特征延迟,训练用了未来特征 | 检查特征时效性清单 | 截断训练特征,建立实时特征兜底 |
| 大促时服务超时雪崩 | 共用服务资源争抢 | 查看网关监控,确认超时阶段 | 分级降级,流量隔离,独立线程池 |
| 多路召回重复候选太多 | 未做候选归一化 | 检查召回来源日志 | 候选池统一去重,保留召回来源特征 |
| 向量召回效果差 | query和item向量空间不一致 | 检查embedding训练样本 | 联合训练双塔,统一向量空间 |
| 实验分流不准确 | 分流key不一致 | 核对用户ID和实验层配置 | 统一分流服务,按场景分层实验 |
这个表是给团队排查用的,实际工作中还能列更多,但核心思路是一致的:搜推一体之后,问题往往不是出在单点上,而是出在跨系统的连接处。排查时不要只盯着自己的模块,要从请求链路全局去看。
6. 一些掏心窝的经验
搜推一体这套架构,我从一开始的怀疑到后来坚定推进,最大的体会是:架构融合的难度不在技术,而在组织和数据治理。技术上的模型结构、召回策略,都有成熟方案可以抄,真正难的是让搜索团队和推荐团队放下各自的地盘意识,用一个统一的数据口径、一套共同的目标函数去思考问题。
如果你所在的公司还在搜推各自为战的阶段,不要急着直接上四层架构,先找一个小场景做试点。比如先做"搜索词引导推荐召回",用搜索的兴趣信号去增强推荐流,这个改动小、见效快、双方团队都能接受。跑通一个小闭环之后,再逐步扩展到数据层、排序层,步子别迈太大,否则业务方不接受、团队也容易返工。
另外,搜推一体的价值评估一定要看长周期。短期看,可能搜索指标和推荐指标各自都没有明显提升,但系统整体的迭代效率、资源利用率、新场景接入速度,这些看不到的隐性收益才是真正的红利。我见过太多项目死在"为什么融合了效果还没涨"这个问题上,其实融合的第一年,效果持平就是胜利,第二年第三年才是收割期。
最后分享一个小技巧:搜推一体之后,线上监控的看板一定要做一张"双场景联合看板",把搜索无结果率、推荐点击率、共享服务延迟、超时降级率放在同一张图上。这张看板的价值在于,它能帮你第一时间发现跨场景的互相影响,不等用户投诉,自己先发现问题。这个习惯,比任何架构设计都值钱。