FDE 面試準備指南(四):System Design 實戰

System Design 是 FDE 面試最能展現工程深度的地方。
面試官不是要你「寫出可以跑的程式碼」,
他想看的是:你在有限資訊下,怎麼做設計決策、怎麼說清楚 trade-off。


面試情境

面試官:「請你設計一個供企業內部使用的 AI 知識庫問答系統,員工可以用自然語言查詢公司政策、產品說明和技術文件。第二題:設計一個 AI Copilot,讓員工用自然語言查詢公司內部數據,例如『今年 Q3 的營收比 Q2 成長了多少?』」


一、System Design 面試的本質

面試官考系統設計,不是要標準答案,是要看三件事:

考點 1:釐清問題的習慣
  你有沒有在設計前先問問題?
  → 不假設,先問。沒有釐清需求就開始畫圖是大扣分。

考點 2:Trade-off 思維
  你說的不是「最好的方案」,而是:
  「在這個場景下,我選 X 而不是 Y,因為...」

考點 3:生產環境的現實感
  Auth、RBAC、Scale、Cost、Failure Mode——
  有沒有考慮到,是高手和普通人的分水嶺。

二、第一題:企業知識庫 Chatbot

步驟一:釐清需求(你要主動問)

你應該問的問題:

需求面:
  ├── 同時使用的用戶數量級?(100 人 vs 10 萬人,架構差很多)
  ├── 文件量多大?(1GB vs 1TB)
  ├── 回答需要引用文件來源嗎?
  └── Latency 要求?(秒級 vs 毫秒級)

安全面(這是 FDE 常被忽略的):
  ├── 不同部門能看的文件不同嗎?→ 決定要不要 RBAC
  ├── 有合規要求嗎?(GDPR、SOC 2)
  └── 資料能放在公有雲嗎?

這些問題決定你的架構複雜度。
先問,再設計。

步驟二:完整系統架構

┌─────────────────────────────────────────────────────────────┐
│                       用戶層                                 │
│   Browser / Slack Bot / Mobile App                          │
└───────────────────────────┬─────────────────────────────────┘
                            │ HTTPS
┌───────────────────────────▼─────────────────────────────────┐
│                   API Gateway 層                              │
│   ├── SSO Token 驗證(Google Workspace / Okta)              │
│   ├── Rate Limiting(防止濫用)                               │
│   └── 請求日誌(Audit Log 起點)                              │
└───────────────────────────┬─────────────────────────────────┘
                            │ 已驗證的用戶 context
┌───────────────────────────▼─────────────────────────────────┐
│                   Chatbot Service 層                          │
│                                                              │
│   ┌────────────────────┐   ┌─────────────────────────────┐  │
│   │  Query Processor   │   │       RBAC Module           │  │
│   │  ├── 意圖分類       │   │  「這個用戶能看哪些文件?」   │  │
│   │  └── Query Rewrite │   │  在查詢前過濾,不是查完再過濾 │  │
│   └────────────────────┘   └─────────────────────────────┘  │
│                                                              │
│   ┌──────────────────────────────────────────────────────┐  │
│   │                   RAG Engine                          │  │
│   │   Query → Embedding → [Vector DB + RBAC Filter]      │  │
│   │          → Reranker → Context Injection → LLM        │  │
│   └──────────────────────────────────────────────────────┘  │
└───────────────────────────┬─────────────────────────────────┘
                            │
┌───────────────┬───────────▼──────────┬──────────────────────┐
│  Cache Layer  │   Response Generator  │  Logging & Monitoring│
│  (相同問題)  │  (加入引用來源)      │  (審計 + 成本追蹤)   │
└───────────────┴───────────────────────┴──────────────────────┘

設計決策一:Authentication 的選型

選項比較:

                API Key          JWT + SSO
────────────────────────────────────────────────────────
適合場景        機器對機器         人類用戶登入
身分追蹤        無(共用 key)     有(每個 token 帶用戶身分)
RBAC 支援       無(要額外設計)    原生(token payload 帶角色)
Token 輪換      麻煩               自動(SSO refresh)
企業整合        困難               直接接 Google Workspace / Okta

結論:企業知識庫選 JWT + SSO
     token payload 帶 user_id、department、roles,
     後面所有服務直接讀,不需要每次去查資料庫

面試官最想聽到的一句話:

「JWT token 的 payload 裡我會放 user_id 和 roles,這樣 RBAC 過濾不需要額外的資料庫查詢,每個 request 都能 stateless 地做權限判斷。」


設計決策二:RBAC 過濾的位置

這是面試最常被追問的細節。

兩種方案:

方案 A:查完向量 DB 後過濾(Post-filter)

  向量 DB 查 Top-20
       ↓
  過濾掉無權限的文件
       ↓
  剩下 Top-3(品質差,因為前幾名都被移除了)

  問題:
  ├── 給 LLM 的 context 品質不可預測
  ├── 如果高 rank 的文件都是機密,剩下的是低相關的文件
  └── 可能造成 LLM 答錯(資料不夠)

方案 B:查詢時就過濾(Pre-filter)← 正確做法

  帶著權限 filter 去查向量 DB
  (只在用戶有權限的 namespace / collection 裡搜尋)
       ↓
  查到的 Top-5 都是有權限且相關的文件
       ↓
  Context 品質穩定

  代價:
  └── 如果 filter 太嚴,搜尋空間縮小,
      ANN 的 recall 可能略降
      → 解法:per-role 的 collection 或 namespace 隔離

設計決策三:Cache 策略

Cache 的三個邊界問題:

問題 1:Cache key 怎麼設計?
  ❌ 只用問題文字 → 相同問題但不同角色,看到的答案不同
  ✅ hash(問題 + 用戶角色清單) → 不同角色分開 cache

問題 2:哪些問題適合 Cache?

  適合:                    不適合:
  「年假幾天?」             「我的訂單狀態?」(個人化)
  「請假流程是什麼?」        「今天的庫存量?」(即時資料)
  「公司福利有哪些?」

  判斷標準:
  ├── 答案是否與用戶身分無關(除了角色)
  └── 答案是否在 TTL 內不會改變

問題 3:文件更新時怎麼 invalidate?
  文件更新 → 需要主動清除所有引用此文件的 cache
  設計:每份文件 embed 進 DB 時,記錄它影響哪些 cache tag
         文件更新時,按 tag 批次清除 cache

設計決策四:Query Rewriting

為什麼要 Query Rewriting?

用戶原始問題:「我剛入職,想知道年假怎麼算」
                ↑
         包含無關的個人背景,
         直接拿去做向量搜尋,效果差

改寫後:「年假計算方式 員工請假規定」
          ↑
         聚焦核心 intent,搜尋 recall 更高

什麼時候值得做 Query Rewriting?

  值得:                  不值得:
  ├── 問題很口語            ├── 問題已經是關鍵字形式
  ├── 問題包含個人化背景    ├── 低流量系統(額外 LLM 呼叫成本不划算)
  └── RAG 召回率明顯不足    └── Latency 要求嚴格(Query Rewriting 增加 1 次 LLM 呼叫)

三、第二題:Internal AI Copilot

這題比知識庫 Chatbot 難,因為要即時查詢結構化資料,不是搜尋文件。

核心差異

知識庫 Chatbot:
  問題 → 查文件(非結構化)→ RAG → LLM 回答

Internal Copilot:
  問題 → 理解數據意圖 → NL2SQL → 查資料庫 → LLM 組合回答

難度增加的地方:
  ├── SQL 可能查錯(幻覺變成「查錯資料」)
  ├── 多來源 JOIN 的邏輯複雜
  ├── 資料欄位層級的 RBAC(不只是文件層級)
  └── 大查詢的成本控制(BigQuery 按掃描量計費)

完整系統架構

┌────────────────────────────────────────────────────────────────┐
│                         用戶輸入                                 │
│  「今年 Q3 的營收比 Q2 成長了多少?」                             │
└───────────────────────────┬────────────────────────────────────┘
                            │
┌───────────────────────────▼────────────────────────────────────┐
│                    Intent Classifier                             │
│  分類:數據查詢 / 文件查詢 / 混合查詢                             │
│  本題 → 數據查詢,走 NL2SQL 路徑                                 │
└───────────────────────────┬────────────────────────────────────┘
                            │
┌───────────────────────────▼────────────────────────────────────┐
│                    NL2SQL Agent                                  │
│                                                                  │
│  輸入:自然語言問題 + Schema Context + 用戶權限                  │
│  輸出:SQL(只允許 SELECT,有語法驗證)                          │
│                                                                  │
│  錯誤處理:                                                      │
│  SQL 語法錯誤 → 把錯誤訊息反饋給 LLM → 自我修正(最多 3 次)    │
└───────────────────────────┬────────────────────────────────────┘
                            │
┌───────────────────────────▼────────────────────────────────────┐
│                    Tool Router(並行執行)                        │
│                                                                  │
│   ┌────────────────┐  ┌────────────────┐  ┌──────────────────┐ │
│   │  BigQuery Tool  │  │   CRM Tool     │  │   ERP Tool       │ │
│   │  財務/銷售數據  │  │  客戶/訂單     │  │  庫存/供應鏈     │ │
│   └───────┬────────┘  └───────┬────────┘  └────────┬─────────┘ │
│           └──────────────────┬┘                    │           │
│                              └────────────────────┘            │
│                                     ↓                           │
│                         Data Aggregator(整合多來源)             │
└───────────────────────────┬────────────────────────────────────┘
                            │ 結構化資料
┌───────────────────────────▼────────────────────────────────────┐
│               LLM(把數字轉成自然語言回答)                       │
│  回答:「Q3 營收為 $4.2M,較 Q2 的 $3.8M 成長 10.5%」           │
│         + 標注:數據來源:BigQuery sales_quarterly               │
└────────────────────────────────────────────────────────────────┘

設計決策一:NL2SQL 的安全邊界

最重要的安全設計:

限制 1:只允許 SELECT
  └── 用 AST 解析,不能用 keyword 比對(可被繞過)
  └── 所有查詢用 read-only 的 Service Account 執行

限制 2:Schema 資訊最小化
  └── 只給 LLM 看用戶有權限的 table 的 schema
  └── 敏感欄位(員工薪資)從 schema context 裡排除

限制 3:執行前 Dry Run(BigQuery 支援)
  └── 先估算掃描量,超過上限拒絕執行
  └── 防止惡意或失控的 SQL 造成高額帳單

限制 4:SQL 審計
  └── 每條執行的 SQL 都記錄到 Audit Log
  └── 可以事後追查「AI 查了什麼資料」

設計決策二:NL2SQL 的失敗模式

失敗模式分類:

類型 A:SQL 語法錯誤
  原因:LLM 生成的 SQL 有語法問題
  處理:捕捉錯誤 → 把錯誤訊息回饋給 LLM → 讓它自我修正
        最多重試 3 次,3 次都失敗 → 回報「無法處理此查詢」

類型 B:SQL 語法正確但語意錯誤
  例子:用戶問「Q3 業績」,LLM 查了 Q4 的 table
  原因:LLM 誤解了 schema 或問題
  處理:這是最難自動偵測的失敗
        → 方案:執行後讓 LLM 對照問題驗證結果合理性(Self-check)
        → 根本解:讓用戶 review 生成的 SQL 再執行(Human-in-the-loop)

類型 C:問題超出 Schema 範圍
  例子:用戶問「競品的市佔率?」
  處理:LLM 偵測到 schema 裡沒有這個資料 → 明確說「資料庫裡沒有此資訊」
        不能亂猜,不能幻覺

失敗率預期(實務參考):
  簡單單表查詢:成功率 ~90%
  複雜多表 JOIN:成功率 ~60-70%
  → 這個失敗率對生產系統意味著什麼?你要告訴客戶。

設計決策三:多來源查詢的並行策略

場景:用戶問「哪個銷售員今年業績最好,他的客戶滿意度怎麼樣?」
需要:BigQuery(銷售業績) + CRM(客戶滿意度)

策略比較:

順序執行:
  BigQuery(500ms)→ CRM(300ms)→ 合計 800ms

並行執行(正確做法):
  BigQuery(500ms)┐
                   ├→ 合計 500ms(節省 37%)
  CRM(300ms)     ┘

但並行有前提:
  ├── 兩個查詢互相獨立(不需要 A 的結果才能做 B)
  └── 如果需要 A 的結果才能決定 B 的 query → 不能並行

依賴關係判斷:
  NL2SQL Agent 輸出查詢計畫時,同時輸出依賴關係圖(DAG)
  執行引擎按 DAG 決定哪些查詢可以並行

四、兩題的對比分析

知識庫 ChatbotInternal Copilot
資料類型非結構化(文件)結構化(資料庫)
核心技術RAG + Vector DBNL2SQL + Tool Router
幻覺形式答案與文件不符SQL 查錯資料
RBAC 難度中(文件分類)高(欄位層級控制)
Latency較低(向量搜尋快)較高(SQL 查詢可能慢)
Cache 效益高(政策問題重複率高)低(數據每日更新)
成本主要來源LLM token + Vector DBLLM token + BigQuery 掃描量

五、面試官地雷題

地雷 1:「RBAC 過濾為什麼要在查詢前而不是查詢後?」

答:Pre-filter 讓向量 DB 只在用戶有權限的文件空間裡搜尋。
    Post-filter 可能讓 Top-K 的高相關文件全被過濾掉,
    最後 LLM 只拿到低相關的文件,回答品質不穩定。

地雷 2:「NL2SQL 的 SQL 錯了,你怎麼辦?語法錯和語意錯分別怎麼處理?」

答:語法錯可以捕捉 error message 讓 LLM 自我修正(Self-Healing Loop)。
    語意錯更難——SQL 跑成功了但查的是錯的資料。
    對於高風險查詢,需要 Human-in-the-loop:
    先把生成的 SQL 給用戶確認,再執行。

地雷 3:「Cache 用 role 做 key,但一個用戶可能有多個 role,組合爆炸怎麼辦?」

答:不用所有 role 的全排列,用「能看到的 permission set」做 cache key。
    同樣 permission set 的用戶共享 cache。
    或者,只 cache role 明確定義的場景,
    避免過度複雜的 cache 設計。

地雷 4:「BigQuery 按掃描量計費,AI 生成的 SQL 可能全表掃描,你怎麼控制成本?」

答:執行前先做 Dry Run(BigQuery 支援),
    估算掃描量,超過閾值拒絕執行並提示用戶縮小範圍。
    另外,在 schema context 裡加入 partition 和 clustering 欄位的提示,
    引導 LLM 生成有過濾條件的 SQL(WHERE 指定時間範圍 / 地區)。

六、面試回答完整示範

面試官期待聽到的回答結構:

第一分鐘(釐清需求):
「在開始設計之前,我想先確認幾個關鍵需求。
 不同部門能看的文件不同嗎?這決定我是否需要 RBAC。
 Latency 要求大概是多少?這影響我的 Cache 策略。
 資料有合規要求嗎?這影響部署在哪個 Region。」

第二到五分鐘(高層架構):
「好,基於這些需求,我的架構分四層:
 API Gateway 負責 Auth 和 Rate Limiting,
 Chatbot Service 負責 RBAC 和 RAG 邏輯,
 Cache Layer 用 role-aware key 避免資料洩漏,
 Logging 記錄每個 request 的 user_id 和文件引用,供審計用。」

第三部分(關鍵設計決策):
「我想特別說兩個設計決策:
 第一,RBAC 要在向量搜尋前就過濾,不是搜完再過濾——
 原因是 post-filter 會破壞 Top-K 的相關性。
 第二,Cache key 要帶用戶的 role set——
 同一個問題,Manager 和 Employee 看到的答案不同。」

最後(Trade-off 和 Failure):
「這個設計最可能出問題的地方有兩個:
 第一,文件更新後 Cache invalidation 的時間窗口——
 用戶可能看到舊的回答,我的方案是文件更新時主動 purge 相關 cache。
 第二,RBAC pre-filter 縮小了搜尋空間,
 如果某個 role 的文件很少,recall 可能下降——
 監控 per-role 的回答準確率可以及早發現。」

FDE 的核心不是把架構圖畫得滿,
而是說清楚每個設計決策背後的 trade-off,
以及你是在什麼場景條件下做了那個選擇。

Yen

Yen

Yen