Q&A 客服 Agent (Design Q&A Support Agent)

一句話定位 + TL;DR(含 35 分鐘心智時鐘)

設計一個掛在訂房平台(Booking.com / Agoda)上的 AI 客服 Q&A Agent:使用者用自然語言問「如何取消訂房?」「這間能帶寵物嗎?」「我的訂單狀態?」,系統用 RAG(檢索後生成) 先撈平台真實資料、再讓 LLM 基於檢索結果作答。系統 延遲敏感(端到端 2-3 秒)準確性是生死線(不得幻覺)、要支援 多輪對話、且 知識要即時
招牌取捨:答案必須 完全 grounded 在檢索到的 context——當檢索品質不夠(similarity 低於閾值)或 grounding check 失敗時,系統 主動拒答並轉接真人客服,而不是讓 LLM 拿預訓練知識硬補。用「會拒答的準確」換掉「什麼都答的流暢」,因為在客服場景捏造一條取消政策的代價是信任崩潰與客訴。其餘決勝點:Intent routing + 平行檢索(結構化訂單 DB + 非結構化向量庫)、conversation-aware query rewriting(把「那間能帶狗嗎」改寫成自包含查詢)、hybrid search + rerank 提升 recall、streaming(TTFT) 把感知延遲從 2-3 秒壓到數百毫秒、離線 ingestion pipeline 保知識即時性。
配速(35 min 預算):~5 min 釐清需求 → ~4 min 容量估算 → ~3 min API + ~4 min 資料模型 → ~6 min 高階架構(RAG online path)→ ~9 min deep dive(chunking/indexing、hybrid query、防幻覺、多輪、freshness、協定)→ ~3 min 擴展瓶頸 → ~1 min 收尾。開場先一句話複述題目、宣告配速、把 chunking / 防幻覺標記為「待會 deep dive」——宣告配速本身就是高分動作,替你爭取「先框架、後細節」的許可。

1. 釐清需求與範圍 (Clarify & Scope ~5 min)

開場後先問清楚邊界,別急著畫 RAG box(LLM 題最常見死因:直接畫「query → LLM → answer」卻不問資料從哪來、要不要 grounded)。建議問題:

釐清問題 為何要問 / 影響到哪個決策 本題假設(依來源)
要回答哪幾類問題? 決定要接哪些資料來源、要不要 router 三類:平台通用 FAQ / 飯店特定政策 / 使用者訂單
答案可以用 LLM 的預訓練知識嗎? 直接決定整個 RAG + 防幻覺架構 不行,必須 grounded 在平台真實資料
延遲要求?哪段最敏感? 決定要不要 streaming 端到端 2-3 秒;感知延遲靠 streaming 壓 TTFT
需要多輪對話嗎?要記上下文嗎? 決定 Conversation Store + 改寫 需要 multi-turn,能理解上下文
答不出來時怎麼辦? 決定 fallback / 轉真人 明確告知無法回答 + 提供轉接真人客服
知識會變嗎?多快要反映? 決定離線 ingestion pipeline (旺季改政策、FAQ 新增),需合理時間內反映
要不要顯示出處 / 來源? 決定 response 帶 sources、可追溯 (回傳 sources 供使用者驗證)
defer 話術

怎麼 chunking、怎麼防幻覺 我先標記,等 deep dive 再展開——現在先把 RAG 當成一個 black box:給它一個自包含 query,它回傳 top-K relevant context。這樣我們能先把『提問 → 理解意圖 → 檢索 → 生成』的端到端 online path 串起來。」把細節推遲,保住敘事連貫。

2. 功能性與非功能性需求 (FR / NFR 與設計北極星)

設計北極星(NFR 直接導出後續所有選型的判準):

NFR 導出的設計北極星 後續對映選型
高準確 / 不得幻覺 答案只能來自檢索 context;品質不足寧可拒答 RAG、hybrid search + rerank、grounding check、fallback
低延遲 2-3 秒 感知延遲優先:先讓字逐字出現 LLM streaming(TTFT)、平行檢索、gRPC HTTP/2
多輪上下文 送進檢索前先把問題改寫成自包含 Conversation Store + conversation-aware query rewriting
知識即時性 online path 不負責更新,獨立離線 pipeline 處理 Ingestion Pipeline、webhook、queue、write-then-delete
高可用 第一接觸點不可掛;外部用即時協定承載推播 WebSocket、stateless service、轉真人 fallback
一句話北極星

「這是一個 grounded、低感知延遲、上下文感知 的 RAG 系統」——記住這三個詞,後面每個選型都能從它推回去。準確性 > 流暢度 是本題與一般 chatbot 最大的分野。

3. 容量估算 (Back-of-Envelope ~4 min)

本題來源未硬給規模數字,估算的目的不是算到精確,而是 驅動 LLM / vector store / cache 的瓶頸判斷(估算 → 選型的 hand-off)。回連 02-Distributed-Systems/05-Numbers-to-Know。以訂房平台量級做合理假設:

請求流量(chat QPS)

假設 DAU ≈ 1,000 萬,其中 ~2% 當天會用客服 = 20 萬 對話 session/day
每 session ≈ 4 輪問答 → 80 萬 次 /day
QPS = 800,000 ÷ 86,400 s ≈ 9 req/sec(平均);尖峰抓 5x ≈ 45 req/sec

每次請求的成本結構(延遲預算分解)

端到端 2-3 秒 拆解:
  query rewriting (1 次 LLM call)   ≈ 200-400 ms
  embedding query + ANN 檢索        ≈ 50-150 ms
  (HYBRID 還要平行打 Booking DB)     ≈ 20-50 ms(與檢索平行,取 max)
  rerank (cross-encoder)            ≈ 50-100 ms
  LLM 生成(到 first token)         ≈ 200-500 ms ← streaming 起點
  LLM 生成(完整答案,逐字 stream)   ≈ 1-2 s(使用者邊看邊讀)

知識庫 / 向量儲存

假設知識庫 100 萬 chunks × 1536 維 float32 ≈ 100萬 × 6 KB ≈ 6 GB 向量
→ 單一 vector store 實例可承載;瓶頸不在容量,而在 LLM 推論吞吐

下表數字多為 推導值 / 合理假設(來源僅明確給「2-3 秒端到端」「不得幻覺」「多輪」「知識即時」等輸入,未給 DAU / chunk 數)。重點是 延遲預算分解「瓶頸是 LLM 推論而非儲存」 這兩個結論。

維度 數字 對後續決策的意義
Chat QPS 尖峰 ≈ 45 /sec(推導值) 不算高,但每 req 含 2 次 LLM call,LLM 是成本中心
延遲預算 2-3 s 端到端(來源) LLM 生成佔大頭 → 必須 streaming 壓 TTFT
LLM call/req ≥ 2(改寫 + 生成;防幻覺可能 +1) LLM 推論吞吐是真瓶頸,需 12-Ops-and-Reliability/02-Overload-Protection
向量儲存 ≈ 6 GB / 100 萬 chunks(推導值) 容量非瓶頸;檢索品質(recall)才是準確性命門
估算的最大收穫

瓶頸不在儲存、不在 QPS,而在 LLM 推論吞吐與延遲。所以優化重心是 (1) streaming 壓感知延遲、(2) 減少 / 並行 LLM call、(3) 過載保護——而非無腦 sharding 向量庫。

4. 用到的積木 / Building Blocks Used

架構即目錄:先給導覽圖,再展開。下表每個決策都對映一個既有 concept note(無 alias 連結);本表即「本案例練到哪些積木」的單一事實來源(single source of truth)。

設計決策 積木 在本題的作用(≤1 行)
容量 / 延遲預算分解 02-Distributed-Systems/05-Numbers-to-Know 拆 2-3 秒預算,認定 LLM 推論是瓶頸而非儲存
檢索後生成(核心架構) 08-Search-and-AI/02-RAG 提問前先檢索平台真實資料,再 grounded 生成
語意檢索 / ANN top-K 08-Search-and-AI/04-Vector-Database chunk embedding 存向量庫,query 轉向量做 ANN
hybrid search(向量+關鍵字) 08-Search-and-AI/03-Elasticsearch 關鍵字精確匹配(飯店名 / 政策編號)補向量 recall
搜尋系統 / rerank 概念 08-Search-and-AI/01-Search-System top-K 後用 cross-encoder reranking 排序最相關
結構化訂單查詢 06-Database-Tech/01-Database Booking DB 以 user_id / booking_id 查訂單狀態
多輪對話歷史儲存 07-Caching-Storage/04-Redis Conversation Store 用 Redis(TTL 自動過期 session)
知識更新緩衝 09-Messaging-Coord/02-Queue ingestion 更新事件入 queue,避免 pipeline 過載
離線知識處理流水線 09-Messaging-Coord/01-Data-Pipeline chunk→embed→寫向量庫 的離線 ingestion pipeline
外部即時雙向通訊 01-Networking/05-Realtime-Protocols WebSocket 承載逐字回覆 + 轉真人推播
即時更新 / 推播模式 10-Design-Patterns/05-Real-time-Updates streaming 逐 token、typing indicator、轉接通知
內部 service 同步呼叫 03-API-Design/04-RPC-and-gRPC Chat↔內部服務用 gRPC(HTTP/2 二進位、強型別 contract)
LLM 過載保護 12-Ops-and-Reliability/02-Overload-Protection LLM 推論吞吐有限,需限流 / 排隊 / 降級
答不出 / 拒答的可靠回退 12-Ops-and-Reliability/03-Reliable-Delivery grounding 失敗 / 低分時安全回退轉真人,不丟使用者
知識空窗 / 無結果監控 12-Ops-and-Reliability/04-Observability 監控「無相關結果」比例、文件 last_updated 告警

5. API 設計 (API Design ~3 min)

對話型系統,核心只有一個 POST /chat,但回應要帶 session_id(多輪)與 sources(可追溯)。理據見 03-API-Design/01-API-Design-Framework

Method Path 用途 重點
POST /chat 送出一則訊息並取得回答 body { session_id?, user_id, message };首次可省 session_id,由系統產生

Request / Response 形狀(依來源)

POST /chat
Body: {
  session_id,   // optional,首次對話可省略,由系統產生
  user_id,
  message
}
Response: {
  session_id,
  response,                          // agent 的回答(實際以 stream 逐字送達)
  sources: [ { title, url, snippet } ] // 引用的資料來源,供使用者驗證
}
細節話術

response 在 spec 上是一個欄位,但 實際傳輸是 streaming——透過 WebSocket / SSE 逐 token 推送,最後再補一則帶 sources 的訊息。把「API 合約」與「傳輸方式」分開講,是展示你懂 streaming 的關鍵動作(詳見 deep dive 6)。sources 不是裝飾——它是 可追溯性(groundedness 的對外證據),呼應防幻覺北極星。

6. 資料模型與儲存選型 (Data Model & Storage Choice ~4 min)

本題的特殊性:同時有結構化(訂單)與非結構化(知識)兩種資料,分別落在不同儲存,由 router 決定查哪個。

Vector Store(非結構化知識:FAQ / 飯店政策 / 幫助文件)
--------------------------------------------------
chunk_id       PK
embedding      VECTOR(1536)   ← ANN 檢索用
text           TEXT           ← chunk 原文
metadata: {
  hotel_id,        ← 飯店特定政策才有,供 metadata 過濾
  policy_type,     ← cancellation / pet / check-in ...
  source_title, source_url,
  version, last_updated,      ← 版本管理 + freshness
  expires_at                  ← 時效性內容(促銷截止)自動過濾
}

Booking DB(結構化訂單:關聯式)
--------------------------------------------------
booking_id   PK
user_id      INDEX            ← 依使用者查訂單
hotel_id     FK
status, check_in_date, check_out_date, plan, payment_status ...

Conversation Store(多輪對話歷史)
--------------------------------------------------
session_id   PK
user_id
history: [ {role, message, ts}, ... ]   ← 使用者訊息 + agent 回覆
(Redis + TTL 自動過期;需長期保存時落關聯式 DB)

儲存選型:

資料 選擇 理由
非結構化知識 Vector Store(pgvector / Pinecone / Weaviate) 語意檢索靠 embedding + ANN;metadata 支援 hotel_id / policy_type 過濾 → 08-Search-and-AI/04-Vector-Database
結構化訂單 關聯式 Booking DB 訂單狀態 / 日期是精確查詢,以 user_id / booking_id 直查 → 06-Database-Tech/01-Database
對話歷史 Redis(TTL) session 有自然生命週期,TTL 自動過期省維護;需持久保存才落 DB → 07-Caching-Storage/04-Redis
關鍵字索引(hybrid) 倒排索引 / Elasticsearch 飯店名、政策編號這類精確匹配,向量搜尋抓不準 → 08-Search-and-AI/03-Elasticsearch04-Database-Indexing/06-Inverted-Index
邊界

「知識 = 向量、訂單 = 關聯式」這個 雙儲存切分 是本題的地基。不要把訂單塞進向量庫(精確狀態查詢用 ANN 是災難),也 不要把政策文件硬塞關聯式 DB 做 LIKE(語意檢索抓不到)。讓 router 決定查哪邊(deep dive 2)。

7. 高階架構 (High-Level Design ~6 min)

把前面的積木串成端到端流程。RAG 系統有兩條路徑:online path(提問即時回答)offline path(知識 ingestion)——High-Level 只畫 online,offline 留到 deep dive 1。

(A) Online path:提問 → 理解 → 檢索 → 生成(三步)

Client(使用者送出訊息)
  │  POST /chat { session_id?, user_id, message }
  ▼
Chat Service ──(讀對話歷史)──► Conversation Store (Redis)
  │  若無 session_id → 建新 session
  ▼
Query Processor  ← 接收「使用者訊息 + 對話歷史」
  ├─ Intent detection:FAQ? 飯店政策? 訂單? → 決定查哪些來源
  └─ Query rewriting:結合歷史,把「那間能帶狗嗎」改寫成
        「台北 A 飯店是否允許攜帶寵物入住?」(self-contained)
  ▼
Retrieval Service  ← 依 intent 路由
  ├─[KNOWLEDGE / HYBRID]─► Vector Search → Vector Store(ANN top-K chunks)
  └─[BOOKING  / HYBRID]─► Booking DB Lookup(by user_id / booking_id)
        │  (HYBRID 時兩者「平行」發出,結果合併)
        ▼  ranking / rerank → top-K context
  ▼
LLM Service  ← 組 prompt:
  │   System prompt(角色/規範/格式)
  │ + 檢索 context(chunks + 訂單資料)
  │ + 對話歷史 + 當前問題
  │  → LLM 基於 context 生成;context 不足 → 回「無法回答,建議轉真人」
  ▼
回傳 { response(逐字 stream), sources }  →  Client

(B) Offline path:知識 ingestion(保 freshness,deep dive 1/5)

知識來源(FAQ 系統 / 飯店後台 / CMS)
  │  內容變更 → webhook / change event
  ▼
Queue(緩衝更新事件,避免 pipeline 過載)
  ▼
Ingestion Pipeline: chunking → embedding → 寫入 Vector Store
  │  write-then-delete:先寫新版 chunks,確認後再刪舊版(避免知識空窗)
  ▼
Vector Store(標記 version / last_updated / expires_at)

(C) Scaled 架構(端到端,含 streaming pipeline)

                         ┌──────── 外部:WebSocket(雙向)────────┐
   Client ──────────────►│  逐字回覆 + typing indicator + 轉真人推播 │
                         └──────────────────┬─────────────────────┘
                                            ▼
                                  Load Balancer (L4 / sticky)
                                            │
                                            ▼
                              ┌──────── Chat Service (stateless) ────────┐
                              │  讀歷史 → 改寫 → 檢索 → 生成(嚴格循序)   │
                              └───┬───────────┬───────────┬──────────────┘
                       gRPC (HTTP/2 同步)     │           │ gRPC server-streaming
                          ▼            ▼      ▼           ▼ (逐 token)
                  Query Processor  Retrieval Service   LLM Service
                                    ├─► Vector Store (ANN)     │
                                    ├─► Elasticsearch (keyword)│ ← 推論吞吐瓶頸
                                    └─► Booking DB (訂單)       │   需 overload protection
                                                               │
   Conversation Store (Redis, TTL) ◄──── Chat Service ────────┘
                                       (逐 token 經 WebSocket 即時轉發給 Client)

   [離線]知識來源 → webhook → Queue → Ingestion Pipeline → Vector Store

關鍵 hand-off:外部 WebSocket(承載 streaming + 推播)→ Chat Service(編排循序流程)→ 內部 gRPC(低延遲同步)→ LLM Service 用 gRPC server-streaming 逐 token 吐回,Chat Service 即時經 WebSocket 轉發給 Client。online 與 offline 兩條路徑徹底解耦——這是 RAG 系統的標準骨架(見 08-Search-and-AI/02-RAG)。

8. 深入探討 (Deep Dives ~9 min)

不重複原則(link-don't-restate)

通用機制(ANN 原理、embedding、queue 語意、gRPC vs REST 細節)只在 concept note 解釋;這裡只講「本題怎麼用、選哪個、為什麼」。任一機制超過 ~3 行就砍掉改連結。本題無 home note 的新材料(query rewriting、grounding check、query router、write-then-delete reindex)才在此 inline 教學並登記到 new_concepts_introduced

8.1 知識庫文件如何 chunking 與 indexing

問題:怎麼把 FAQ / 政策 / 長文切塊並建索引,才能被有效檢索?
為何是瓶頸:chunk 太大 → 含太多不相關資訊、稀釋語意;chunk 太小 → 失去上下文、檢索到也讀不懂。chunking 直接決定檢索品質,而檢索品質決定準確性。

chunking 策略 做法 適合場景
Fixed-size + overlap 固定字數(如 500 字)切,相鄰重疊(如 100 字) 通用、結構不明的長文檔
Structure-based 依結構切(FAQ 每個 Q&A pair 一塊、政策每段一塊) 結構化文件(FAQ、條款)
Semantic chunking 依語意邊界切(句間語意相似度下降時切割) 長篇、主題多樣的文件

本題建議做法(按文件型態分流)

Embedding 與索引:每個 chunk 經 embedding model 轉向量,連同 metadata 寫入 Vector Store;檢索時 query 同樣轉向量,用 ANN(approximate nearest neighbor) 找 top-K。
→ 積木:08-Search-and-AI/04-Vector-Database08-Search-and-AI/02-RAG

8.2 問題同時需要結構化 + 非結構化資料時如何處理(Query Router + 平行檢索)

問題:很多真實問題跨兩種資料。例「我還能免費取消嗎?」需要 訂單(入住日期、飯店 ID)+ 政策(免費取消截止為入住前 X 天)。
為何是瓶頸:若只查一邊,答案必然不完整或錯誤。

新材料:Query Router / Intent 分類(本案例首次 introduce,無 atomic note → 已登記 new_concepts_introduced)

Query Processor 分析問題後,把 intent 分為三類:

  • KNOWLEDGE_ONLY:純知識(「退款通常幾天?」)→ 只查 Vector Store。
  • BOOKING_ONLY:純訂單(「我的訂單編號?」)→ 只查 Booking DB。
  • HYBRID:混合(「我還能免費取消嗎?」)→ 同時平行查兩者,結果合併後一起餵 LLM。

Intent 分類可用 LLM(few-shot prompting)輕量分類模型 完成。

方案 做法 取捨
A. 一律全查 不分類,每次都打向量庫 + 訂單庫 簡單;但多餘查詢浪費延遲與成本
B. Router + 平行檢索 先分類 intent,HYBRID 時兩查詢平行發出 省掉不必要查詢;HYBRID 取兩者 max 而非 sum 延遲

選擇與理由:採 BHYBRID平行(非循序)發出 Vector Search + DB Lookup,總延遲取兩者 max,不疊加;分類用 LLM few-shot 兼顧彈性。
→ 積木:08-Search-and-AI/04-Vector-Database06-Database-Tech/01-Database

8.3 如何防止 LLM 幻覺(招牌取捨:寧可拒答)

問題:客服場景捏造一條取消政策 → 使用者照做出錯 → 信任危機與客訴,後果特別嚴重。
為何是核心:這直接對應 NFR「不得幻覺」與本題 core_tradeoff——準確 > 流暢

多層防護(四層,由前到後)

機制 目的
1. Prompt system prompt 明令「只能依 context 回答,不足就說『我無法確定,建議聯繫真人客服』,不得捏造」;要求引用具體 source 約束 LLM 行為、可追溯
2. Retrieval hybrid search(向量 + 關鍵字)提升 recall;rerank(cross-encoder)把最相關 chunks 排前 確保 LLM 收到正確 context
3. Validation grounding check:驗證回答中的關鍵事實(日期 / 金額 / 政策)是否出現在 context;可用另一個 LLM call 或規則引擎 攔截已生成的幻覺
4. Fallback 當 similarity score 低於閾值 → 主動拒答,不把低品質 context 餵 LLM;明確提供轉真人選項 守住底線:寧可不答也不答錯
新材料:Grounding check(本案例首次 introduce → 已登記 new_concepts_introduced)

對 LLM 回答做事後驗證:「以下回答是否完全基於提供的 context?有無 context 未提及的事實?」失敗則 回退安全回答。這是「會拒答的準確」的最後一道閘——比第 1 層 prompt 更可靠,因為 prompt 是軟約束、grounding check 是硬檢查。

選擇與理由:四層 疊加,不是擇一。第 2 層(retrieval 品質)是 根本——若 context 本身不相關,LLM 即使守規矩也答不好;第 4 層(低分拒答)是 底線——用「拒答」換「不答錯」,正是本題的招牌取捨。
→ 積木:08-Search-and-AI/01-Search-System(rerank)、08-Search-and-AI/03-Elasticsearch(hybrid keyword)、12-Ops-and-Reliability/03-Reliable-Delivery(fallback 轉真人)

8.4 如何處理多輪對話(conversation-aware query rewriting)

問題:後續問題常含代詞(「那間」「它」)或省略主語。直接把「那可以帶寵物嗎?」送進檢索 → 缺上下文 → 撈不到相關文件。
為何是瓶頸:檢索品質崩 → 整個 RAG 崩。

新材料:Conversation-aware query rewriting(本案例首次 introduce → 已登記 new_concepts_introduced)

每次提問時,Query Processor 把 對話歷史 + 當前問題 一起送 LLM,要求改寫為 self-contained query

原始:「那可以帶寵物嗎?」
改寫:「台北 A 飯店是否允許攜帶寵物入住?」

改寫後的 query 再送 Retrieval Service,檢索品質不受代詞 / 省略影響。

對話歷史管理(避免 token 爆炸 / 延遲上升)

→ 積木:07-Caching-Storage/04-Redis(Conversation Store + TTL)

8.5 如何確保知識即時性(Ingestion Pipeline + freshness)

問題:飯店政策隨時變(旺季調整、新增設施),FAQ 持續新增。知識庫沒更新 → 使用者收到過時答案。
為何是瓶頸:grounded 在過時資料 = grounded 在錯資料,一樣傷準確性。

Ingestion Pipeline 設計

新材料:Write-then-delete reindex(本案例首次 introduce → 已登記 new_concepts_introduced)

每個 chunk 標 version / last_updated。更新時 先寫入新版 chunks,確認成功後再刪舊版——避免更新中途出現「新的還沒好、舊的已刪」的 知識空窗。時效性內容(促銷截止)標 expires_at,檢索時自動過濾過期 chunk。

監控與告警:追蹤各文件 last_updated,超過更新週期(如飯店政策 90 天未更)→ 告警提醒內容團隊;監控 retrieval「無相關結果」比例,突升代表有新類型問題尚未被知識庫覆蓋。
→ 積木:09-Messaging-Coord/01-Data-Pipeline09-Messaging-Coord/02-Queue12-Ops-and-Reliability/04-Observability

8.6 Client 與內部服務的通訊協定選擇

問題:外部(Client ↔ Chat Service)與內部(Chat Service ↔ 其他服務)需求截然不同。

外部:WebSocket vs SSE

WebSocket SSE(Server-Sent Events)
通訊方向 全雙工(雙向) 單向(server → client)
LLM streaming response
Server 主動推播 ✅ 原生支援 ❌ 需額外機制
實作複雜度 較高(連線生命週期 / heartbeat / 重連) 較低(建在標準 HTTP 上)
LB 相容性 需支援 WebSocket(sticky / L4 LB) 標準 HTTP 即可

選擇:若只需「使用者問 → agent 逐字回」的簡單互動,SSE 足夠且更簡單(ChatGPT / Claude 採此)。但訂房客服 agent 需要轉接真人客服等 server 主動推播,故 偏向 WebSocket

內部:gRPC。Chat Service 流程嚴格循序(讀歷史 → 改寫 → 檢索 → 生成),是標準同步 request-response。gRPC 相較 REST:HTTP/2 二進位延遲更低protobuf 強型別 contract 降低跨團隊溝通成本。

例外:Chat ↔ LLM Service 需 streaming。LLM 逐 token 生成;若等完整答案才回 Client,使用者經歷 2-3 秒空白。改用 gRPC server-streaming:LLM 逐 token 推給 Chat Service,Chat Service 再即時經 WebSocket 轉發 Client。效果是 文字逐字出現,TTFT 從 2-3 秒降到數百毫秒,大幅降低 感知延遲
→ 積木:01-Networking/05-Realtime-Protocols10-Design-Patterns/05-Real-time-Updates03-API-Design/04-RPC-and-gRPC

9. 擴展與瓶頸 (Scaling & Bottlenecks ~3 min)

主動找出系統會先在哪裂開並補強(訓練 drive 而非被動回答)。

瓶頸 / 風險 補強手段 積木
LLM 推論吞吐 / 成本成天花板 限流 + 排隊 + 降級(尖峰時用較小模型 / 縮短 context);快取常見問答 12-Ops-and-Reliability/02-Overload-Protection07-Caching-Storage/01-Caching
感知延遲(2-3 秒空白) streaming(gRPC server-stream + WebSocket 轉發) 壓 TTFT 到數百毫秒 10-Design-Patterns/05-Real-time-Updates01-Networking/05-Realtime-Protocols
檢索 recall 不足 → 幻覺 hybrid search + rerank;metadata 過濾;低分拒答 08-Search-and-AI/03-Elasticsearch08-Search-and-AI/01-Search-System
知識過時 → grounded 在錯資料 webhook + queue 觸發 ingestion;write-then-delete;expires_at 過濾;last_updated 告警 09-Messaging-Coord/01-Data-Pipeline12-Ops-and-Reliability/04-Observability
對話歷史過長 → token 爆炸 / 延遲上升 sliding window(最近 N 輪)+ 早期對話摘要 07-Caching-Storage/04-Redis
WebSocket 連線多 → 連線狀態管理 sticky session / L4 LB;stateless Chat Service,連線狀態外置 01-Networking/06-Load-Balancing
向量庫資料量 / QPS 暴增(100x) 向量庫分片 + 副本;快取熱門 query embedding 與檢索結果 08-Search-and-AI/04-Vector-Database10-Design-Patterns/01-Scaling-Reads
drive 的關鍵句

「系統 先在 LLM 推論裂開(每 req ≥2 次 LLM call、推論吞吐有限)→ 所以我用 過載保護 + 常見問答快取 + streaming 壓感知延遲;準確性會 先在檢索 recall 裂開 → 用 hybrid search + rerank + 低分拒答 補。儲存與 QPS 不是當前瓶頸。」先指出最可能裂開處再補強,比逐項羅列更有說服力。

10. 取捨總表與收尾 (Trade-off Recap & Wrap-up ~1 min)

決策點 選擇 放棄了什麼 為什麼值得
答案來源 完全 grounded + 低分拒答轉真人 LLM 預訓練知識的「什麼都答」 客服捏造政策的代價是信任崩潰(招牌取捨
防幻覺 四層防護(prompt/retrieval/grounding/fallback) 單靠 prompt 的簡單 prompt 是軟約束,需 retrieval 根本 + grounding 硬檢查
多源查詢 Query Router + HYBRID 平行檢索 「一律全查」的簡單 省多餘查詢;平行取 max 延遲不疊加
多輪上下文 conversation-aware query rewriting 直接送原始問題 代詞 / 省略會讓檢索崩,改寫成自包含才檢索得到
感知延遲 streaming(gRPC stream + WebSocket) 等完整答案的簡單 TTFT 數百毫秒,把 2-3 秒空白變成逐字出現
外部協定 WebSocket(非 SSE) SSE 的簡單 需 server 主動推播(轉真人 / typing)
知識更新 離線 ingestion + write-then-delete online path 即時改 online/offline 解耦;更新無知識空窗
收尾話術

「整題的單一錨點是 grounded:答案只能來自我們檢索到的平台真實資料,檢索品質不夠就拒答轉真人——我寧可少答,也不讓 LLM 用預訓練知識捏造政策。其餘都是把這個錨點撐起來的標準打法:RAG 雙儲存 + router 平行檢索 拿到對的 context、hybrid search + rerank + grounding check 守準確、query rewriting 撐多輪、streaming 壓感知延遲、離線 pipeline 保即時。瓶頸在 LLM 推論而非儲存,所以過載保護與快取常見問答是主要擴展手段。」引用既有決策用連結,不重新推導。

後續追問演練 (Interviewer Follow-ups — "What if…")

為什麼這個系統一定要用 RAG,而不是直接讓 LLM 回答?

「我還能免費取消嗎?」這題為什麼要同時查兩個資料來源?

grounding check 是什麼?放在哪一層?

流量突然放大 100x,架構哪裡先撐不住?怎麼補?

某個熱門 FAQ(如「如何取消訂房」)一天被問幾十萬次,怎麼省 LLM 成本?

LLM Service 整個掛了,使用者還能用客服嗎?

為什麼外部選 WebSocket 而 ChatGPT 那種選 SSE 就夠?判斷準則是什麼?

內部服務間為什麼用 gRPC 而不是 REST?又為什麼 LLM 那段是例外?

知識庫更新時若直接「改寫」chunk 會有什麼問題?為什麼要 write-then-delete?

怎麼知道知識庫「沒覆蓋到」某類新問題?

主動回想:白板重建 (Whiteboard from Scratch)

不看上文,照下面提示把整個設計重建一次。每格都該能 30 秒內口述。

常見陷阱 (Exam Traps for This Problem)