公开实现:Twitter 2023 快照。图和例子是教学简化,不表示当前线上参数。

先跟着一条帖子走

在架构导读的 3 条线里,这篇只跟 online 请求。先不讨论模型怎么训练,也不把每个逻辑环节都假定成一个独立服务。

假设你喜欢摄影。一位没关注过的作者发了拍星空的教程。它能不能出现在首页,至少有 3 道问题:系统有没有找到它?找到以后怎么比较?最后这一屏是否还留得下它?

任何一步失败,单看最终点击率都不知道原因。

请求里到底经过什么

2023 的 Home Mixer 文档把候选(candidate)获取、特征补齐、打分、过滤与混排拆成不同环节。Product Mixer 提供组织这些 pipeline 的框架,不是某个召回模型(retriever)。公开说明

flowchart TD
 A["刷新:首页请求"] --> B["多路候选"]
 B --> C["合并与去重"]
 C --> D["补特征:用户、帖子、上下文"]
 D --> E["模型给出预测"]
 E --> F["过滤、列表约束与混排"]
 F --> G["服务端返回结果"]
 G -. "客户端确认实际展示" .-> H["记录曝光,再关联反馈"]

这是用于理解的数据流图,不是源码逐行执行顺序。资格检查可能在多个阶段发生;广告等产品元素也有独立逻辑,不能简单当成普通帖子的预测分。

每条箭头都应有一个约定

用虚构帖子 post-17 跟一次请求:

阶段 你希望看见什么 缺了会怎样
召回 候选 ID、来源、来源内分数 不知道是没找到,还是后面被删掉
特征补齐 特征值、时间戳、缺失标记 用默认值与真实值混在一起,难定位回归
打分 模型版本与各目标预测 不知道变化来自模型还是权重配置
选择与返回 淘汰原因、返回的内容与位置 不知道最后在哪一步被淘汰
实际展示 客户端曝光(exposure)事件、对应请求与内容 ID 把“返回了但没看到”误读成“不喜欢”

这张表是阅读和调试时的检查清单,不声称公开仓库都按这个字段名记录。

为什么要拆,而不是全部交给一个大模型

拆开后,不同阶段能用不同成本解决不同问题:召回尽量快,排序对少量候选花更多计算,列表阶段再处理重复与约束。某一路超时,也可以局部降级(fallback)。

代价是接口更多:候选会丢字段,特征可能过期,配置也可能和模型不匹配。因此工程上的“正确”不只是一个函数算对,还包括信息在各阶段没有变味。

列表怎么越走越短了?

假设两路候选分别是 [A,B,C,D] 和 [B,C,E,F],最终想返回 4 条。以下数量都是教学示意:

环节 剩余候选 为什么变少
来源返回 8 个条目 同一内容可能出现多次
按内容 ID 去重 A、B、C、D、E、F B、C 各合并一次,但保留两个来源
当前资格检查 A、B、C、E、F D 已删除
特征补齐 A、B、C、E F 缺少必需特征,按本例约定淘汰
列表作者约束 A、C、E B 与 A 同作者,达到本例上限

只剩 3 条,不能为了凑 4 条又把已删除的 D 加回来。可以用剩余 deadline 做一次有上限的补取,也可以明确允许短列表;选择取决于延迟预算和产品要求。继续加大每路召回数量不是免费的,会增加查特征和打分成本。

定位时记录每层的数量与淘汰原因,而不是只在最后打一个“候选不足”。另外,返回了 A、C、E 不等于用户看到了三条:用户可能只停留在第一屏。服务端返回记录与客户端曝光记录需要不同事件,后续训练才能区分没看见和看见没动作。

打开仓库先读哪儿

先读 Home Mixer 的流程说明,找到请求入口和候选 pipeline 的关系;再沿一个来源往下看。第一遍只回答:它拿什么输入,吐什么输出,失败时谁兜底?

没召回到一条好帖子,换更强的排序模型能救吗?

在排序仍只处理同一个候选集合的前提下,不能。先检查召回覆盖,而不是把所有问题归给 ranker。

一条帖子没被点击,能直接记成不相关吗?

先确认是否真正曝光、位置如何、有没有观察到反馈。未展示、展示但没操作、明确拒绝,是不同的证据。

下一期:候选从哪里来。

本页术语 4
候选 candidate
召回阶段捞出来、等待精排挑选的内容
召回模型 retriever
第一阶段,从海量内容里低成本捞出一批候选
曝光 exposure
内容真的被展示给用户这件事,和「被检索到」不是一回事
降级 fallback
主路径不可用时切换到较简单但语义明确的备用行为