公开实现: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
- 主路径不可用时切换到较简单但语义明确的备用行为