教学架构 · 最近审阅:2026-10。下面的例子完全虚构,不描述任何公司的生产配置。
“用户点刷新”“作者改了帖子”“模型训练完了”都可能改变你看见的内容,但它们不是同一件事。把这 3 件事挤在一条长箭头里,很容易误以为每次请求都会训练模型、重新编码全库。
这一篇先不选模型。先把谁在什么时候做什么,以及结果交给谁讲清楚。
1 · 先按发生时间分开
| 路径 | 什么时候运行 | 产生什么 | 常见问题 |
|---|---|---|---|
| 一次刷新 · online | 用户打开页面或请求下一屏 | 在时限内返回合规、有用的一组内容 | 候选(candidate)不足、服务响应慢、过滤出错 |
| 内容更新 · batch / nearline | 发布、编辑、删除或定期刷新 | 较新的特征、向量与索引 | 旧事件覆盖新内容,删除没有及时生效 |
| 模型学习 · offline | 一轮数据和实验准备好 | 新模型与能否发布的证据 | 标签错误、时间泄漏、只看整体指标 |
这里的 offline 是“不在这次请求的关键路径上”,不是“模型永远不更新”。nearline 也不是固定的秒数,而是异步响应变化;多快才算够,要由业务需求和成本一起决定。
2 · 用一条新帖子把 3 条线接起来
想象一个分享摄影和编程教程的小社区。一位作者刚发了“星空延时摄影”,读者随后打开首页。
- 作者发帖。 更新路径保存内容版本,生成可复用的表示,让它进入可搜索的集合。这不是为每个读者单独跑一次内容编码。
- 读者刷新。 系统结合当前上下文找候选。召回先缩小范围,排序再花更多计算比较,最后组成一屏;资格检查可能不止做一次。
- 读者真的看到了。 曝光(exposure)、位置和后续行为进入数据。未展示不是“不喜欢”,没动作也不一定是拒绝。
- 下一轮训练。 团队重新定义样本或目标、训练模型、比较新旧结果。通过验证后,再发布兼容的新部件,不直接把 checkpoint 扔给旧索引。
“先召回,再排序”是常见分工,TensorFlow Recommenders 的官方教程有一个可运行的基础例子。这里的社区与更新流程是教学设计,不是该教程的部署配置。
3 · 功能、模型和工程组件,不是同一层概念
| 你正在决定什么 | 例子 | 别和什么混淆 |
|---|---|---|
| 功能职责 | 找候选、预测行为、控制重复 | 一项职责不一定是一台服务 |
| 模型结构 | 双塔(dual-encoder)、序列 encoder、多任务 ranker | “用了 Transformer”没有说明它放在哪一层 |
| 算法与数据结构 | 点积(inner product)、HNSW、IVF-PQ | 近邻搜索准确,不代表用户偏好预测准确 |
| 工程实现 | 批处理任务、缓存、RPC、索引服务 | 用了某个框架,不等于已有可靠的完整系统 |
例如,Faiss 可以实现索引搜索,但不自动替你解决权限、事件乱序或发布回滚。先写清功能边界,再选库。索引实现的区别可以查 Faiss 的官方列表;具体的更新、删除和过滤能力要核对所选实现。
4 · 向量能对上维度,还不够
不是双方都输出 256 个数,就能接起来。每一条箭头至少要能回答:
- 含义是什么? 这是候选 ID、查询向量,还是某个行为的概率?一个余弦分数不是概率。
- 属于哪个版本? 用户塔(user tower)与内容索引是否在兼容的表示空间?预处理和归一化是否一致?
- 什么时候有效? 内容编辑之后,旧特征最多能继续用多久?删除能否及时生效?
- 失败怎么算? 返回空结果、超时和版本不兼容,不应该被记成同一种“没找到”。
例如,旧内容向量搭配新用户向量,点积仍然能算,但分数可能已失去原来的含义。此时效果下降,应该先查版本配套,而不是立刻重新训练。
5 · 改一个组件,会影响哪些地方?
别急着画一个更复杂的框。拿一个小变化做追踪:
| 变化 | 会影响哪里 | 第一项检查 |
|---|---|---|
| 换了内容 encoder | 内容向量、查询兼容性、索引 | 两侧能否仍用同一种分数比较 |
| 召回多加一路 | 合并、去重、候选预算、后续打分成本 | 固定预算下是否补到独有相关内容 |
| 加大多样性(diversity)惩罚 | 列表选择,不一定改变模型参数 | 相关性与内容重复分别怎么变 |
| 换了训练标签 | 目标含义、数据覆盖、评估口径 | 旧指标还能看见新增信号吗 |
看架构图时,可以拿这些变化逐个追踪。能说清需要连带修改和检查哪些地方,比记住每个框的名字更有用。
用三个时间戳找一次错误
下面是虚构的排错记录。10:00 发帖,10:02 用户刷新,10:05 新版内容向量才进入索引。离线重放 10:02 的请求时,如果直接查“现在的最新特征”,就会拿到当时并不存在的向量。
| 你记录的时间 | 含义 | 这次例子 |
|---|---|---|
| 事件时间(event time) | 内容或行为何时发生 | 10:00 发布 |
| 处理完成时间 | 编码任务何时完成 | 10:04 |
| 可查询时间 | 索引何时真的能返回它 | 10:05 |
因此,“10:02 没找到”可能是更新路径慢,而不是 encoder 不理解内容。先看各环节延迟和失败重试,再决定要不要换 encoder。离线构造训练特征时也一样:只用决策发生前已可用的信息,避免把未来信息带回来。
一个简单做法是保留可重放的索引/特征快照,以及请求所用的版本 ID。代价是存储和维护增加,所以不一定保存每秒全量副本;可以根据业务用周期快照加增量事件。但无论怎么存,都要能回答“当时系统究竟看到了什么”,不能用今天的数据替昨天解释。
自己走一遍
新帖子搜不到,先换更强的 ranker 吗?
先确认它是否进入可检索(retrieval)集合、编码与索引是否更新、资格检查是否通过。ranker 只能比较拿到的候选,不能弥补上游根本没交来的内容。
换了内容塔,但向量维度没变,可以直接沿用用户塔吗?
不能只凭维度判断。要有经过验证的兼容关系、固定样本对照和发布协议;否则看似正常的点积,可能已经不再表达训练时的关系。
接着读 01 · 一次刷新,把这套理解用到公开代码里。想直接动手,去 06 的向量实验或 05 的评估实验。
本页术语 7
- 候选 candidate
- 召回阶段捞出来、等待精排挑选的内容
- 曝光 exposure
- 内容真的被展示给用户这件事,和「被检索到」不是一回事
- 双塔 dual-encoder
- 用户和内容各一个编码器,映到同一向量空间靠内积召回
- 点积 inner product
- 两个向量对应分量相乘再求和,双塔的打分方式
- 用户塔 user tower
- 双塔检索中把用户信息编码成 query 向量的模型分支
- 多样性 diversity
- 结果之间在主题、来源或其他指定维度上有多大差异
- 检索 retrieval
- 从外部语料里找出相关内容