「設計一個 Feed / 搜尋排序系統」和上一篇的推薦系統漏斗是親戚,但面試的追問重心不同。推薦系統考的是「為什麼要分層」;排序系統考的是更貼近 production 的三件事:特徵怎麼算、線上線下怎麼保持一致、效果怎麼量化。新手最常栽在「講完模型就沒話講」,而資深的分水嶺就在這三題。
這篇聚焦排序層本身(假設候選已由召回層給出),用 Feed / 搜尋排序貫穿,把重點放在特徵體系、訓練/服務一致性、資料閉環、A/B test,每段附面試官最常追問的問題。
⏱️ 延遲預算:排序對數百~數千候選打分,通常 < 100ms。特徵取用是延遲大頭,所以「特徵存哪、怎麼取」是設計核心,不是模型。
Step 1 — 釐清:Feed 與 Search 的排序目標差在哪
- Search(搜尋排序):有明確 query,相關性(relevance)是第一位。目標常是「query-doc 相關性 + 使用者滿意度(點擊、停留、無 reformulation)」。
- Feed(資訊流排序):沒有 query,靠用戶畫像與 context 推測興趣。目標常是「長期參與度與留存」,要小心過度優化即時點擊。
- 共同點:兩者都框成 learning to rank——對候選打分後排序,指標都分離線/線上兩套。
🎤 面試官追問:「Search 和 Feed 的 label 定義差在哪?」(Search 可用點擊 + 人工相關性標註;Feed 多用隱式回饋——看完、點讚、分享、負回饋。)「為什麼不能只優化 CTR?」(標題黨、回音室、傷長期留存。)
Step 2 — 把問題框成 Learning to Rank
- 輸入:
(user, query?, candidate_doc, context)的特徵向量。 - 輸出:一個排序分數(或多目標分數融合)。
- 訓練目標的三種典型:
- Pointwise:對每個 doc 預測互動機率(如 CTR),最簡單,但忽略「同一頁 doc 之間的相對順序」。
- Pairwise(如 LambdaMART / RankNet):學「doc A 該排在 doc B 前面」,直接優化相對序。
- Listwise(如 LambdaRank 對 NDCG):直接優化整個列表的排序指標。
- 實務上 GBDT(LambdaMART)在搜尋排序仍是強 baseline;深度模型擅長吃大量稀疏特徵與序列。
🎤 面試官追問:「Pointwise vs Pairwise 怎麼選?」(在意絕對機率/校準 → pointwise;只在意排序品質 → pairwise/listwise,通常 NDCG 更好。)
Step 3 — 特徵體系(這題的真正考點)⭐
排序系統的成敗,七成在特徵。面試務必把特徵按來源和時效分類講清楚:
| 類別 | 例子 | 算在哪 | 時效 |
|---|---|---|---|
| Query 特徵 | query 長度、意圖分類、嵌入 | 線上即時 | 請求當下 |
| Doc / Item 特徵 | 內容嵌入、品質分、歷史 CTR | 離線批次 | 小時~天 |
| User 特徵 | 長期興趣畫像、人口屬性 | 離線批次 | 天 |
| Cross 特徵 | query-doc 相似度、user-item 親和 | 線上即時拼接 | 請求當下 |
| Context 特徵 | 時間、裝置、地理、當前 session | 線上即時 | 請求當下 |
關鍵分野——離線 vs 即時:
- 離線批次特徵:算得起重、覆蓋廣(嵌入、長期統計),預先寫進 feature store,線上低延遲讀取。
- 即時特徵:當前 session 行為、即時 query——必須 on-the-fly 算。
🎤 面試官追問:「歷史 CTR 這種特徵怎麼避免 feedback loop(自我強化)?」(位置偏差校正、加探索、用 counterfactual/IPS 加權。)「冷啟 doc 沒有歷史 CTR 怎麼辦?」(退回內容特徵 + 探索曝光。)
Step 4 — 線上 / 離線架構與「訓練-服務一致性」⭐
這是排序系統面試的送分題也是地雷題。核心是一張雙路徑圖:
離線(訓練) 線上(服務)
┌──────────────┐ ┌──────────────┐
│ 行為日誌 │ │ 請求 (user, │
│ → 樣本拼接 │ │ query, ctx) │
│ → 特徵計算 │ 同一套特徵邏輯 │ → 取特徵 │
│ → 訓練模型 │ ◄───── 共用 ─────► │ → 模型打分 │
│ → 離線評估 │ │ → 排序回傳 │
└──────────────┘ └──────────────┘
│ │
└──── 模型 + 特徵版本一起上線 ───────┘
- 訓練-服務一致性(training-serving skew):同一套特徵計算邏輯必須在訓練與線上共用。離線用 Spark 算、線上用 Java 重寫一遍——只要有一點不一致,線上就掉分。解法:共用特徵庫 / feature transformation 同一份程式碼、或用 feature store 統一產出。
- Point-in-time 正確性:訓練樣本的特徵值,必須是「當時那個時間點」的值,不能用未來資訊(label leakage)。
- 即時特徵的線上拼接:靜態特徵查 store、session 特徵即時算,在打分前組裝成完整向量。
🎤 面試官追問(經典):「離線 AUC/NDCG 變好,線上卻沒漲甚至掉,怎麼查?」(依序查:training-serving skew → 特徵線上線下不一致 → label 定義/取樣偏差 → 離線指標與業務脫鉤 → A/B 設計或 novelty effect。)
Step 5 — 資料閉環:Log-and-Train
排序系統會自己生產訓練資料,但這裡藏著最深的坑:
- 記錄什麼:每次排序的所有候選 + 特徵快照 + 展示位置 + 使用者回饋,而不只記被點的那個。
- 位置偏差(position bias):排在前面的本來就更容易被點,直接拿點擊當 label 會讓模型學到「位置」而非「相關性」。校正法:隨機化實驗估 propensity、或加 position 當特徵但服務時固定。
- 未曝光的候選:沒被展示的 doc 沒有回饋,造成選擇偏差——需要探索流量補資料。
🎤 面試官追問:「你的訓練資料是模型自己選出來的,怎麼避免越練越偏?」(探索 + 去偏:position-debias、IPS 加權、保留隨機 holdout 流量收無偏資料。)
Step 6 — 評估與 A/B Test(量化效果)⭐
離線只是篩選器,線上 A/B 才是判決。
- 離線指標:搜尋用 NDCG@k、MAP、MRR;Feed 用 AUC/LogLoss + ranking 指標。不要用 accuracy。
- 線上 A/B test 設計要點:
- 隨機分流單位:通常按 user 分桶(避免同一人時而 A 時而 B),用穩定 hash 分流。
- 核心指標 + guardrail:主指標(如 session 滿意度、長期留存)+ 護欄(延遲、多樣性、申訴率、收入)——避免「贏了點擊、輸了體驗」。
- 統計顯著性:算好樣本量與實驗時長,跑滿一個週期(涵蓋週末/週間),用顯著性檢定,別偷看提早下結論(peeking 會膨脹假陽性)。
- novelty / primacy effect:新功能初期可能短暫變好/變差,要跑夠久看穩態。
- 上線後監控:特徵分布漂移、模型 staleness、各分位延遲、A/B 與全量一致性。
🎤 面試官追問:「A/B 主指標漲了 0.5% 但延遲也漲了,你上不上?」(談 trade-off 與 guardrail:延遲對留存的長期傷害 vs 短期指標;可能要先優化延遲再上。)「怎麼確定不是 novelty effect?」(延長實驗、看指標隨時間的曲線是否回落。)
一張表收束
| 環節 | 重點 | 面試送分點 |
|---|---|---|
| 框問題 | learning to rank(point/pair/list-wise) | 講清楚為什麼 pairwise 優化 NDCG |
| 特徵 | 按來源/時效分類,離線 vs 即時 | feature store、避免 feedback loop |
| 線上/離線 | 訓練-服務一致性、point-in-time | 解釋離線好線上掉的排查順序 |
| 資料閉環 | log-and-train、位置偏差 | 去偏 + 探索流量 |
| A/B test | user 分桶、guardrail、顯著性 | 不偷看、防 novelty effect |
回答框架口訣
被問「設計排序系統」時照這條線走:
- 釐清是 Search 還是 Feed(有無 query、優化什麼目標)
- 框成 learning to rank(point/pair/list-wise + 離線指標)
- 重點講特徵(按來源時效分類、離線 vs 即時、feature store)
- 講線上/離線架構(訓練-服務一致性是核心,point-in-time)
- 講資料閉環(log-and-train + 位置偏差去偏)
- 收在 A/B test(user 分桶、guardrail、顯著性、防 novelty)
記住一句話:排序系統的難點從來不是模型,而是「特徵的一致性」與「效果的可量化」——把這兩件事講透,你就贏了這題。