「設計一個推薦系統」是 ML 系統設計面試最高頻的題目之一。新手最常犯的錯,是一上來就講要用 Two-Tower 還是 DeepFM。但面試官真正想聽的是:面對上億個候選物品、又要在 100ms 內回應,你為什麼必須把系統拆成「漏斗」?每一層在解什麼問題?
這篇用業界通用的三層漏斗架構貫穿——候選生成(Candidate Generation)→ 排序(Ranking)→ 重排(Re-ranking)——每層講清楚目標、模型、延遲預算、取捨,並附上面試官最常追問的問題。延續系統設計系列的拆解方式,先對齊需求,再逐層往下。
⏱️ 整體延遲預算:假設端到端 < 200ms。候選生成 ~10–20ms、排序 ~50–80ms、重排 ~10ms,其餘留給網路與 feature 取用。延遲預算是你做每個取捨的尺。
Step 0 — 先釐清需求(別急著畫架構)
- 業務目標:最大化什麼?點擊?觀看時長?長期留存?(選後者通常更高分——避免短期標題黨。)
- 場景:首頁 feed、相關推薦、還是搜尋後推薦?決定了 context 與候選來源。
- 規模:例如 10 億物品、1 億日活、每次請求回 10–20 條。
- 約束:延遲 < 200ms、物品幾分鐘就有新內容(新鮮度)、需要可解釋與多樣性。
🎤 面試官追問:「成功指標到底是什麼?」「為什麼不能一個大模型對所有物品打分就好?」(答案就是下面整篇——算不完。)
為什麼要漏斗?一個數字就懂
假設用一個精排模型對每個物品打分,單次推論 1 微秒,10 億物品 = 1000 秒。直接出局。
漏斗的本質是用便宜的模型快速砍掉 99.99%,再用昂貴的模型精算剩下的:
10 億物品
│ ① 候選生成 (Candidate Generation):便宜、高召回
▼
~ 1000 候選
│ ② 排序 (Ranking):昂貴、高精度
▼
~ 100~200 條已排序
│ ③ 重排 (Re-ranking):業務規則、多樣性、去重
▼
最終 10~20 條
🎤 面試官追問:「每層各砍掉多少?為什麼候選生成要追求 recall 而排序追求 precision?」(候選生成漏掉的就永遠回不來了,所以寧可多放、不可漏放;排序負責把放進來的精準排好。)
① 候選生成(Candidate Generation)— 便宜、高召回
目標:在 10ms 內,從上億物品裡選出幾百幾千個「值得進一步看」的候選。指標是 Recall@k,不是精度。
做法(通常多路召回並聯):
- 協同過濾 / 雙塔(Two-Tower):把 user 和 item 各自編碼成向量,離線算好所有 item 向量灌進 ANN 向量索引(FAISS / ScaNN / HNSW),線上只算 user 向量再做近似最近鄰檢索。這是最主力的一路。
- 熱門 / 趨勢召回:補足新用戶與探索。
- 基於規則 / 標籤召回:同作者、同類別、地理位置。
- 圖召回:user-item 二部圖上的鄰居擴散。
多路召回的結果做聯集去重,一起送進排序層。
🎤 面試官追問:「雙塔為什麼能做到上億物品的即時檢索?」(item 向量離線算好建索引,線上只剩一次 user 編碼 + ANN 查詢,把 O(N) 變成近似 O(log N)。)「為什麼用多路召回而不是一路?」(單一信號有盲區;多路互補才能撐住 recall。)
② 排序(Ranking)— 昂貴、高精度
目標:對候選生成送來的 ~1000 條,用重型模型精準預測互動機率,排出順序。指標是 AUC / LogLoss(離線) 與 CTR / 觀看時長(線上)。
做法:
- 特徵:這層才用得起豐富特徵——user 側(歷史行為、人口屬性)、item 側(內容嵌入、統計)、交叉特徵、以及 context(時間、裝置、當前 session)。
- 模型:從 GBDT(XGBoost/LightGBM)到深度模型(DeepFM、DCN、DIN/DIEN 等序列模型)。深度模型擅長自動學交叉特徵與用戶興趣的序列演化。
- 多目標:真實系統常同時預測「點擊、完播、點讚、分享」多個 head,再用加權公式融合成單一排序分——權重直接對齊業務目標。
🎤 面試官追問:「離線 AUC 變好、線上 CTR 卻掉了,怎麼查?」(經典題:查 training-serving skew、feature 線上線下不一致、label 定義、A/B 設計、feedback loop。)「為什麼用多目標而不是只優化 CTR?」(只追 CTR 會養出標題黨,傷長期留存。)
③ 重排(Re-ranking)— 業務規則與多樣性
目標:排序分數不是最終順序。重排在 ~10ms 內,套上純機率模型顧不到的業務與體驗約束。
做法:
- 多樣性 / 去冗餘:避免連續 10 條都是同一作者/同一主題,用 MMR 或 DPP 之類的方法平衡相關性與多樣性。
- 業務規則:置頂、廣告插入、已看過/已購買過濾、合規與安全下架。
- 新鮮度與探索:給新內容一點曝光(探索),避免系統陷入「熱門越推越熱」的回饋迴圈。
🎤 面試官追問:「為什麼多樣性不能直接在排序模型裡學?」(排序是逐條獨立打分,看不到「這一整頁的組合」;多樣性是列表級(list-wise) 的性質,得在重排這種看得到整個列表的階段處理。)
橫切關注點:Feature Store 與線上/離線一致
漏斗講完,面試官幾乎一定會往工程基礎設施追問。核心是一張圖:
- 離線:批次算好 item 向量、user 長期特徵、統計特徵,寫進 feature store 與 ANN 索引。
- 線上:低延遲讀取特徵(Redis / 專用 feature store),即時特徵(當前 session 行為)on-the-fly 計算。
- 一致性:同一套特徵計算邏輯必須在訓練與線上共用,否則 training-serving skew 會讓線上表現崩掉——這是最常見的線上掉分元兇。
🎤 面試官追問:「即時特徵(剛剛點了什麼)怎麼進到排序?」(線上 feature 拼接:靜態特徵查 store + session 特徵即時算,在排序前組裝。)「特徵版本怎麼管?」(feature store 做版本化 + point-in-time 正確性,避免標籤洩漏。)
冷啟動:使用者 / 物品 / 系統
- 新使用者:沒有歷史 → 靠熱門召回、人口屬性、開場問卷、context(裝置/地理)。
- 新物品:沒有互動 → 靠內容特徵(文字/圖像嵌入)進雙塔,並給探索曝光累積信號。
- 新系統:沒有數據 → 先上熱門/規則版,邊收數據邊訓練,逐步替換。
🎤 面試官追問:「新物品如何快速獲得曝光又不傷整體指標?」(探索預算 / bandit:用小比例流量做探索,平衡 explore-exploit。)
評估與上線:離線指標 → A/B
- 離線:候選生成看 Recall@k;排序看 AUC / NDCG / LogLoss。不要用 accuracy——推薦是排序、樣本極度不平衡。
- 線上:CTR、平均停留/觀看時長、留存,一律用 A/B test 驗證,並監控 guardrail 指標(多樣性、申訴率、延遲)。
- 監控:特徵分布漂移、模型 staleness、各層延遲 SLA、feedback loop 健康度。
🎤 面試官追問:「離線贏了就能上線嗎?」(不行——離線只是篩選器,最終以線上 A/B 為準;離線與線上脫鉤本身就是要監控的問題。)
一張表收束三層漏斗
| 層 | 目標 / 指標 | 典型模型 | 候選量級 | 延遲預算 | 取捨重點 |
|---|---|---|---|---|---|
| 候選生成 | 高召回 / Recall@k | 雙塔 + ANN、多路召回 | 10 億 → ~1000 | ~10–20ms | 寧可多放,不可漏放 |
| 排序 | 高精度 / AUC、CTR | DeepFM/DCN/DIN、多目標 | ~1000 → ~100 | ~50–80ms | 豐富特徵、線上線下一致 |
| 重排 | 體驗 / 多樣性、規則 | MMR/DPP、規則引擎 | ~10ms | 列表級、業務約束 |
面試框架口訣
回答「設計推薦系統」時,照這條線走永遠不會冷場:
- 先釐清目標與規模(長期留存?10 億物品?延遲?)
- 解釋為什麼要漏斗(一個大模型算不完——用延遲數字證明)
- 逐層拆:候選生成(recall)→ 排序(precision)→ 重排(list-wise)
- 補橫切面:feature store、線上/離線一致、冷啟動
- 收在評估:離線指標篩選、線上 A/B 拍板、監控漂移
記住一句話:推薦系統的設計不是選模型,是設計一條「從上億到十條」的漏斗,每一層用對的成本解對的問題。