剛進大公司的工程師常常有個疑問:「明明兩天就寫得完的功能,為什麼要先寫設計文件、開好幾次會?」
老實說,我以前也這樣覺得。直到踩過幾次雷:功能上線才發現用戶根本不用、雲端帳單比省下的錢還多、半夜被叫起來處理一個沒人看得懂的系統。那時我才慢慢理解,在大公司,寫 Code 通常是最簡單的部分。難的是在寫之前,先把「會出事的地方」想清楚。
這篇文章把我自己設計系統時會問的問題,整理成三個階段:
- 設計系統之前:先搞清楚到底要解決什麼問題
- 設計時:用經營者的角度做決策
- Design Review 時:常被問的問題,以及它們背後想抓的風險
每個問題我都會講「為什麼要問」。了解問題背後要解決什麼,才能針對回答,不然就只是在背 checklist。
為了不要太抽象,我用同一個例子從頭講到尾:
公司有 1,000 位客服人員,每通電話結束後,要花 3 分鐘在 CRM 手打通話紀錄。老闆希望做一個「通話錄音自動摘要」功能:電話一掛掉,系統就自動把錄音轉成文字、產生摘要、填回 CRM。
PART 1一、設計系統之前
了解在開始設計前,需要考慮哪些問題。
1. 為什麼要實作這項功能?
這題最容易被跳過,但也最重要。工程師很容易一聽到需求就開始想架構:「要用哪個模型?要不要上 Kafka?」但如果連誰要用、解決什麼問題都不清楚,很可能會做出一個技術很漂亮、卻沒人想用的東西。
我會把它拆成三個小問題:
這個功能的用戶是誰?
「客服」只是第一層答案,再往下挖會發現其實有三種人:
- 客服人員:希望少打字,摘要要準
- 客服主管:客訴來的時候,想快速知道那通電話到底發生什麼事
- 品管(QA)人員:要抽查通話品質
三種人要的東西不一樣,權限也不一樣(客服不該看到別人的通話),這些都會直接影響後面的設計。
解決用戶什麼問題?
表面上是「省打字時間」,但實際算一下:每小時接 5 通、每通省 3 分鐘,等於每小時省 15 分鐘,一天就是 2 小時。1,000 位客服加起來,每天省下 2,000 小時的人力。
這個數字很重要,它決定了公司願意為這個功能花多少錢,後面算成本時會一直用到。
用戶以前是怎麼做的?
這題最常被忽略,卻最常救命。以前客服是在 CRM 的備註欄手打紀錄,主管要查客訴就去抽聽錄音。
這告訴我們一件事:摘要最後一定要回到 CRM。如果做成一個獨立網站,客服就得在兩個系統之間複製貼上,等於只是把「打字」換成「複製貼上」,用不到一個月就沒人用了。
避免做出「技術上成功、商業上失敗」的功能。用戶的舊習慣,就是新系統的限制條件。
2. 要如何實作這項功能?
功能核心在哪?Endpoint / Cloud / Local Server
同一個功能,核心放在不同地方,結果差非常多:
| 核心位置 | 以通話摘要為例 | 好處 | 代價 |
|---|---|---|---|
| Endpoint(客服電腦) | 每台電腦自己跑語音辨識模型 | 錄音不用上傳,隱私最好 | 公司電腦沒有 GPU,跑不動;1,000 台電腦的模型更新是場惡夢 |
| Cloud | 錄音上傳到雲端處理 | 開發最快,要擴容就加機器 | 錄音含個資,資安或法遵可能不准資料出公司 |
| Local Server(公司機房) | 在機房架 GPU 伺服器 | 資料不出公司 | 要自己採購、維運硬體,擴容得等採購流程 |
客服錄音裡常常有客人念出身分證字號、信用卡號,所以這題通常不是工程師自己能決定的,要先問資安和法務。記得是先問,不要做完才問。
介面是什麼?Desktop App / Mobile App / Web
客服整天開著 CRM 網頁,直覺上做成 CRM 裡的 Web 外掛就好。
但這要看錄音從哪裡來。如果公司的電話系統(PBX)本來就會錄音,後端直接去拿檔案,前端只要做 Web 外掛顯示摘要。可是如果電話系統沒有錄音功能,得從客服電腦上擷取軟體電話(softphone)的聲音,瀏覽器就做不到了,只能做 Desktop App。而 Desktop App 代表要處理安裝、自動更新,還有公司電腦的權限管控。
一個看起來很小的需求細節,就決定了整個前端要怎麼做。
主要的技術框架是什麼?
語音辨識和 LLM 的生態系幾乎都在 Python,所以處理音訊的 worker 用 Python 很合理;負責大量併發請求的 API 服務,用 Go 可能更省資源。如果前端是 CRM 外掛,就得遵守那個 CRM 平台規定的開發框架,這部分沒得選。
核心放哪、介面是什麼,決定了資料怎麼流、合不合規、維運成本多高,這些都是之後很難改的決定。
3. 用戶量級有多大?使用頻率?資料量體?
一定要有數字。「很多人用」不是規模,「1,000 人、每人每小時 5 次、每次 10MB」才是。
用我們的例子實際算一次:
- 使用者:1,000 位客服,只在上班時間(8 小時)使用
- 頻率:每人每小時約 5 通電話
- 檔案:10 分鐘的錄音,約 10MB
(每小時 1,000 × 5 = 5,000 次)
(1.4 × 10MB ≈ 14 MB/s)
(5,000 × 8 小時 × 10MB)
(以 22 個工作天計)
有趣的地方來了:每秒 1.4 個請求,小到一台機器就扛得住;但每個月 8.8TB 的錄音,一年就超過 100TB。
這個系統的挑戰根本不在 QPS,而是頻寬、儲存成本和處理時間。沒算數字之前,很容易把力氣花在錯的地方,例如花兩週去調 API 效能。
「只在上班時間用」也是很重要的資訊:
- 晚上和週末可以把機器縮到最少,省錢
- 但早上 9 點大家同時上線,要注意冷啟動(cold start)
- 月初帳單寄出後,客服電話量可能暴增好幾倍,尖峰要另外估
規模決定架構。1,000 人和 100 萬人的系統是完全不同的設計,沒有數字的討論都是空談。
4. 系統有哪些限制?
每種技術都有先天的瓶頸,最好在設計前就知道,而不是上線後才撞牆。
資料庫的 CAP 定理
分散式系統裡,一致性(Consistency)、可用性(Availability)、分區容錯(Partition tolerance)三者只能取其二。不過實務上網路一定會斷,分區一定會發生,所以真正的選擇其實是:網路出問題的時候,你要「資料一定正確」,還是「服務一定能回應」?
- 帳務、庫存:寧可暫時不能用,也不能算錯,所以選 C
- 按讚數、瀏覽數:數字晚幾秒更新沒關係,但頁面不能掛,所以選 A
訊息大小與逾時限制
在我們的例子裡這點特別關鍵,因為每個錄音檔都有 10MB:
| 元件 | 常見限制 | 對 10MB 錄音的影響 |
|---|---|---|
| AWS API Gateway(REST) | Payload 上限 10MB,整合逾時預設 29 秒 | 檔案大小剛好卡在邊緣;轉錄一通 10 分鐘錄音一定超過 29 秒 |
| AWS Lambda(同步呼叫) | Request / Response payload 上限 6MB | 直接塞不進去 |
| Kafka | 單筆訊息預設上限約 1MB | 塞不進去 |
| DynamoDB | 單筆 item 上限 400KB | 一份長一點的逐字稿就可能超過 |
知道這些限制之後,設計方向自然就出來了:
- 檔案不要走 API,改用 Pre-signed URL 讓客戶端直接上傳到 S3
- Queue 裡只傳檔案路徑,不傳檔案本身(這叫 Claim-Check pattern)
- 轉錄一定超過 29 秒,所以必須設計成非同步處理
避免設計到一半,甚至上線後,才發現某個元件根本扛不住,整個架構要打掉重來。
5. 每位使用者的成本是多少?
市場售價是多少?我們的目標是多少?就算是內部工具也要算,因為老闆一定會拿它跟「直接買現成的 SaaS」比。
- 市面上類似的通話摘要服務,每位使用者每月約 30 美元
- 我們每通電話的語音辨識加 LLM 摘要,成本約 0.02 美元
- 每位客服每月約 880 通電話(5 通 × 8 小時 × 22 天)
運算成本:880 × 0.02 = 17.6 美元/人/月
儲存成本:每人每月新增 8.8GB,第一個月很少,但不刪的話會一直累積
每位使用者每月成本 ≈ 18 美元起跳,而且逐月上升
跟 30 美元比,看起來還有空間。但加上維運人力、機器閒置、儲存逐月累積之後,很可能就跟直接買差不多了。這時候設計方向就會跟著改變:
- 能不能只對超過 2 分鐘的通話做摘要?(短電話通常是轉接或打錯)
- 能不能先用比較小的模型做分類,只有複雜的通話才交給大模型?
- 錄音能不能 30 天後就刪除,只留文字摘要?
如果是要賣給外部客戶的產品,SaaS 常見的毛利目標在 70%~80%,換算下來,每位使用者的服務成本大約要壓在售價的兩到三成以內,才養得起團隊。
功能做得出來,不等於應該做。成本決定這個功能能不能活下去,也會反過來決定架構:要不要自架、要不要快取、資料留多久。
6. 要達到正式上線的品質,需要投入多少人力與時間?
Demo 做出來,大概只完成了一半。
正式上線還需要監控告警、權限控管、資安審查、壓力測試、錯誤處理、on-call 手冊、文件……這些常常佔掉另一半甚至更多的時間,卻最常被忘記算進時程裡。
估時程時,我會把工作分成兩類:
有明顯 Gap、需要自己 build 的
例如客服電話裡常常國台語、中英文夾雜,現成的語音辨識準確率可能不夠。這種「目前沒有現成方案」的工作是研究型的,可能兩週就搞定,也可能三個月還在調。
這一塊一定要獨立拉出來估,並且講清楚它的不確定性,不要跟一般開發工作混在一起算。
有折衷方案可以暫時填補,但之後要改的
- v1 只處理國語通話,辨識信心太低的就標記「請人工確認」
- v1 摘要先不自動寫回 CRM,由客服按「確認」才寫入,避免 AI 寫錯資料
- v1 先每 5 分鐘批次處理一次,之後再改成即時
折衷沒有問題,但一定要寫進文件、開好 ticket、約好什麼時候還。沒被記錄下來的「暫時」,最後都會變成「永久」。
讓主管對時程有合理的預期,也讓大家清楚知道有哪些已知的技術債。
7. 有哪些既有系統需要相容?
大公司不是一張白紙,你設計的東西要放進一個可能已經運作十幾年的環境裡。公司偏好的語言、系統、軟體、合作廠商,都是設計的前提。
公司偏好的程式語言
如果公司的人都是 Python / Golang 專家,就算你覺得 Elixir 超適合寫這個功能,也要三思。原因很現實:
- Code review 時沒人看得懂,review 變成形式
- 你請假時系統出事,沒人能接手 debug
- 之後要交接,沒人願意接
選大家熟悉的語言,出問題時很快就有人能 review、能幫忙。
公司既有的平台與資料格式
例如公司的數據平台用 Starburst,偏好 Iceberg 格式。那通話摘要的歷史資料就應該落地成 Iceberg table,數據團隊馬上就能拿去分析,不用另外寫一套 ETL 轉換。
其他常見的相容性問題
- 登入:公司已經有 SSO(Azure AD / Okta),就不要自己做帳號密碼
- 監控與日誌:公司統一用 Datadog 或 ELK,就接進去,不要自己再架一套
- 合作廠商:公司跟某家雲端簽了企業合約有折扣,換一家可能要重跑好幾個月的採購與法務流程
- CRM:要寫回 CRM,就要先確認它的 API 有沒有速率限制(rate limit)
降低維運和溝通成本。系統不是寫完就結束,而是要有人長期照顧,要選公司「養得起」的技術。
PART 2二、設計時
用經營者的角度做決策
設計系統時,我覺得最重要的心態轉換是:想像這個功能是你自己開的公司,每個月的雲端帳單會寄到你家,半夜出事會打你的手機。
用這個角度看,你自然會在意下面這幾件事。
1. 可擴充性(Scalability):量變大時,加機器就好嗎?
公司明年併購了另一個客服中心,客服從 1,000 人變成 3,000 人。好的設計是「worker 從 5 台加到 15 台」就結束;不好的設計是「資料庫扛不住,要停機兩天做資料遷移」。
2. 可延展性(Extensibility):加新功能時,要重寫嗎?
可擴充性是「量」的問題,可延展性是「功能」的問題。上線三個月後,主管一定會說:「既然都有逐字稿了,可以順便做情緒分析嗎?可以自動判斷客訴類別嗎?」
如果轉錄完成時會發出一個「逐字稿已產生」的事件,新功能只要訂閱這個事件就能加上去。如果轉錄、摘要、寫回 CRM 全部寫死在同一個函式裡,每加一個功能都要動到核心流程,改一次就可能壞一次。
3. 費用可接受
前面算過的每位使用者成本,設計時要一直拿出來檢查。每加一個元件(快取、Queue、read replica)都要問自己:它帶來的好處,值得它的價錢嗎?
4. 維護難度可接受
自己架一套 Kafka + Kubernetes + Cassandra 聽起來很厲害,但如果團隊只有三個人,光是升級版本、處理節點掛掉,就會把整個團隊的時間吃光。
很多時候,用雲端的 managed service 多花一點錢,換團隊晚上能好好睡覺,是非常划算的交易。
5. 讓 Review 的人有選擇
這點很多人會忽略:至少設計兩個方案,並比較優缺點。
為什麼?如果你只給一個方案,reviewer 只能回答「好」或「不好」,而且他心裡一定會想:「你是不是沒想過別的做法?」接下來整場會議就會變成每個人各自提自己腦中的方案,討論完全發散。
如果你攤開兩個方案,討論就會聚焦在「我們要哪一種取捨」:
| 方案 A:雲端 Managed 服務 | 方案 B:公司機房自架模型 | |
|---|---|---|
| 上線時間 | 約 2 個月 | 約 5 個月(含 GPU 採購) |
| 每月成本 | 隨用量線性成長 | 前期硬體投入高,量大時比較便宜 |
| 個資合規 | 需要資安審查錄音能不能上雲 | 資料不出公司 |
| 維運負擔 | 低 | 高,需要有人顧 GPU 和模型 |
| 擴容彈性 | 高,隨時可以加 | 低,要等採購 |
| 台語辨識 | 受限於廠商支援程度 | 可以自己 fine-tune |
最後再加上「我推薦哪個方案,為什麼」。被淘汰的方案也值得留一行說明原因,半年後有人問「當初怎麼沒選 B?」,文件就是答案。
(接下來 Design Review 的例子,都假設我們推薦方案 A:資安審查通過錄音可以上雲,先用雲端服務快速上線。)
PoC 是驗證路走得通,不是做一套系統
這是非常常見的坑。
PoC(Proof of Concept)要回答的是一個很具體的問題:「這條路走得通嗎?」例如:
拿 50 通真實的客服錄音(包含台語、背景噪音、客人很生氣在大吼),跑語音辨識加摘要,看準確率能不能到 85%。
這個 PoC 不需要登入、不需要資料庫、不需要漂亮的 UI,一個 script 加一份結果報告就夠了,一到兩週內應該要有結論。做完之後 code 整包丟掉也完全沒關係。
常見的失敗劇本是這樣的:PoC 做著做著加了 UI、加了資料庫,拿去 Demo 給老闆看,老闆說:「這不是做好了嗎?下個月上線吧!」然後一個沒有權限控管、沒有監控、沒有錯誤處理的東西,就這樣上了 production。
Prototype 則是在設計決定之後,照著選定的架構做出來,給一小群真實用戶試用的版本。它要驗證的是流程與體驗:客服真的會看摘要嗎?摘要放在 CRM 哪個位置最順手?
順序是:
用最少的成本排除最大的不確定性,同時避免「半成品意外上線」。
PART 3三、Design Review 時
Design Review 不是來刁難你的,是一群踩過雷的人在幫你找系統的風險。
而且目標不是零風險。零風險的系統不存在,就算存在也貴到做不起。真正的目標是:把「不知道的風險」變成「知道、而且選擇承受的風險」,並講清楚代價是什麼。
例如:
- 「主管的統計報表可能延遲 1 分鐘」:可以接受,代價很小
- 「A 客服可能看到 B 客服的通話內容」:不能接受,一定要解決
下面是 Design Review 常被問到的問題。每一題我都附上「它背後想抓什麼風險」,準備時先想清楚這個,回答起來會有方向很多。
Q1系統要解決什麼問題?提供哪些功能?預期的規模有多大?
背後想抓的大家講的是不是同一件事。
這題通常第一個被問。如果一兩句話講不清楚,後面的討論都會失焦。好的回答要有用戶、有價值、有數字:
「讓 1,000 位客服在每通電話結束後 1 分鐘內拿到摘要,省下每通 3 分鐘的手打時間。尖峰每小時約 5,000 通,每月約 8.8TB 錄音。」
另外建議明確寫出這一版不做什麼(Non-goals),例如「不做即時字幕、不做情緒分析」。不寫的話,會議開到一半一定有人說:「那順便做 XX 應該不難吧?」
Q2有哪些可選擇的技術方案?各自的優缺點是什麼?
背後想抓的你是真的比較過,還是先有答案才去找理由。
這就是前面「至少兩個方案」的比較表。Reviewer 會特別注意比較是不是公平:如果推薦方案的優點寫滿滿、另一個方案只寫缺點,大家很快就看得出來那是稻草人。
誠實寫出推薦方案的缺點,反而更有說服力。
Q3任務如何分派?同步還是非同步?需要佇列嗎?佇列長度與逾時怎麼管理?
背後想抓的尖峰時系統會不會崩、使用者要等多久、任務會不會掉或重複執行。
轉錄一通 10 分鐘的錄音可能要 30~60 秒,前面提過 API Gateway 預設 29 秒就逾時,所以同步處理行不通。流程會變成:
用了 Queue 之後,接著一定會被追問:
- 佇列長度:月初電話量暴增 3 倍,Queue 積了 2 萬筆怎麼辦?要設告警(例如「最舊的任務已經等超過 5 分鐘」),也要先決定積壓太多時要不要降級,例如先只做轉錄,摘要晚點再補。
- 逾時:Queue 的 visibility timeout(任務被取走後,多久沒完成就放回佇列)一定要比最長的處理時間還長。如果設 30 秒但處理要 60 秒,任務會被第二個 worker 再拿一次,結果同一通電話在 CRM 出現兩份摘要。這真的是很常見的 bug。
- 一直失敗的任務:重試 3 次還失敗就丟進 Dead Letter Queue(DLQ),不要讓它無限重試卡住整個佇列,同時設告警讓人去查。
Q4核心工作負載是 Stateless 還是 Stateful?使用者狀態怎麼記錄、保留與還原?需要 Session Sticky 嗎?
背後想抓的機器能不能隨便加、隨便砍?機器掛了,使用者會不會掉資料?
Stateless 代表任何一台機器都能處理任何一個請求,機器掛了換一台就好,要擴容就加機器。Stateful 代表有些資料只存在某台機器的記憶體裡,那台一掛,資料就跟著不見。
舉例:如果 worker 把「這通錄音處理到第幾段」記在記憶體裡,autoscaling 一縮容把機器砍掉,這通就得從頭來。正確做法是把進度寫進資料庫或 S3,讓 worker 本身不留任何狀態。
比較麻煩的是通知客服「摘要好了」這件事。如果用 WebSocket 推播,連線本身就綁在某台機器上,天生就是 stateful 的。常見做法有兩種:
- Session Sticky:Load Balancer 用 cookie 把同一個使用者固定導到同一台機器。做法簡單,但那台機器一掛,上面所有人一起斷線;縮容時也得等連線慢慢結束(drain)才能關機。
- 把連線和狀態分開:Worker 處理完,發一則訊息到 Pub/Sub(例如 Redis),所有連線節點都收得到,由持有該使用者連線的節點推送出去。任何節點掛了,客戶端重連到別台就好。
至於還原:客服斷線重連時,前端用 call_id 去資料庫查最新狀態,不依賴「剛才那條連線記得什麼」。
Q5隨著使用者與資料成長,系統要如何擴容?Stateless 橫向擴展,還是 Sharding?哪些指標會觸發擴容?
背後想抓的成長的時候是「加機器」就好,還是得「重新設計」?
成長有兩種,要分開看。
使用者變多(請求變多):Stateless 橫向擴展
客服從 1,000 人變成 3,000 人,只要 API 和 worker 都是 stateless,加機器就解決了。
資料變多(單表變大):Partition、冷熱分離、Sharding
一年後資料庫裡有上千萬筆通話紀錄,主管查「過去一年所有客訴」越來越慢。這時候加 API 機器沒有用,瓶頸在資料庫。可以依序考慮:
- 依月份做 partition,查詢只掃需要的月份
- 超過一年的冷資料搬到 Iceberg,用 Starburst 查(剛好是公司既有的平台)
- 真的扛不住寫入量,才考慮 sharding
Sharding 最關鍵的是 shard key 怎麼選。例如用「日期」當 shard key,今天的資料全部落在同一個 shard,那個 shard 永遠最熱,其他 shard 閒著沒事,等於沒切。改用「客服中心 ID」或「客服 ID」會平均很多。Shard key 選錯之後幾乎沒辦法改,所以 reviewer 通常會問得很細。
觸發擴容的指標
很多人直覺用 CPU,但在我們的例子裡這是錯的。Worker 大部分時間都在等外部語音辨識 API 回應(I/O bound),CPU 可能只有 20%,Queue 裡卻已經積了 2,000 筆。用 CPU 當觸發條件的話,永遠不會擴容。
比較好的指標是:
- Queue 深度 ÷ worker 數量(每台 worker 平均背了多少任務)
- 最舊任務的等待時間
- API 的 p95 延遲
Q6系統需要快取嗎?TTL 設多久?如何避免資料不一致?
背後想抓的快取是「用一致性換速度」,你知道自己換掉了什麼嗎?
先講一件事:不是每個系統都需要快取。1,000 人的內部系統,資料庫可能完全扛得住。多一層快取,就多一個資料不一致的來源。先量測,確定有需要再加。
如果真的需要,要依資料性質決定:
- 主管的「今日通話統計」:每次都算要掃大量資料,快取 60 秒完全沒問題,晚一分鐘沒人在意
- 通話摘要內容:客服剛改完,畫面卻還顯示舊的,他會以為沒存到又改一次,甚至去報 bug
後者的做法是寫入時主動清掉快取(更新資料庫後刪掉對應的 cache key),TTL 只當作最後一道保險。
另外還有一個常見的坑:快取雪崩。如果大量 key 在同一時間寫入、TTL 又都一樣(例如早上 9 點批次載入、TTL 都是 1 小時),10 點整所有快取同時失效,流量全部打到資料庫。解法是在 TTL 上加一點隨機值(jitter),例如 55~65 分鐘,讓過期時間錯開。
Q7資料庫怎麼設計?CAP 如何取捨?讀寫分離、Sharding、備份與還原、Multi-AZ、I/O 容量?
背後想抓的資料庫是系統裡最難事後修改的部分,一開始沒想好,之後的代價最高。
CAP 取捨
同一個系統裡,不同的資料可以有不同的選擇。「一通電話只能有一份正式摘要」這種對應關係必須強一致;統計報表則可以接受最終一致。
讀寫分離
主管跑大報表很吃資源,丟到 read replica 去跑,才不會拖慢客服的寫入。但要注意 replica 會有延遲(replication lag):客服剛存完摘要,畫面馬上去 replica 讀,會發現「咦,怎麼沒存到?」所以讀自己剛寫入的資料要走 primary。
備份與還原
「有備份」不等於「能還原」。Reviewer 很可能會問:你實際還原過嗎?還原 2TB 要幾個小時?這段時間系統怎麼辦?
沒演練過的備份,通常到出事時才會發現少備了某張表,或是還原要 12 小時。
Multi-AZ
一個機房(Availability Zone)斷電或斷網時,資料庫能不能自動切到另一個機房?代價是成本大約翻倍,所以要評估「停機一小時」的損失有沒有比這筆錢高。
I/O 容量
雲端硬碟有 IOPS 上限(例如 AWS gp3 的基準是 3,000 IOPS)。常見的狀況是:半夜的批次報表把 IOPS 吃滿,線上寫入跟著變慢,看起來像資料庫當掉了,但 CPU 其實很閒。沒在看 I/O 指標的話,會查很久。
Q8資料同步怎麼設計?如何控制多人同時存取資料?
背後想抓的兩個人同時改同一筆資料時,會不會有人的修改默默消失?
客服正在修改某通電話的摘要,同一時間主管在同一筆紀錄上加註記。主管先存,客服後存,主管的註記就被蓋掉了,而且沒有任何人發現。這叫 Lost Update。
常見解法:
- 樂觀鎖(Optimistic Locking):每筆資料加一個
version欄位,更新時條件寫WHERE id = ? AND version = ?。如果版本號已經被別人改過,更新就會失敗,提示使用者「這筆資料已被其他人修改,請重新整理」。 - 把資料拆開:主管註記根本不需要跟摘要放在同一筆,拆成另一張表,衝突自然就消失了。
跨系統的資料同步又是另一個問題:摘要要寫回 CRM,但如果 CRM 的 API 剛好掛了呢?
- 先確定誰是 Source of Truth:是我們的資料庫,還是 CRM?
- 寫入失敗要能重試,而且重試必須是冪等的(idempotent)。用
call_id當 idempotency key,就算重試十次,CRM 裡也只會有一份摘要。
Q9日誌怎麼收集?可以分析哪些錯誤?能查詢系統異常事件嗎?使用者操作有保留稽核日誌嗎?
背後想抓的出事的時候,你查得出來發生了什麼事嗎?
情境:客服反映「我昨天下午那通電話沒有摘要」。如果 log 只是一行一行的文字,沒有任何 ID 串起來,你就只能在幾百萬行 log 裡大海撈針。
幾個基本原則:
- 結構化日誌(JSON 格式),方便搜尋和統計
- 一個 trace ID 貫穿整個流程:上傳 → Queue → Worker → CRM,用一個 ID 就能看到整條路徑
- 錯誤要分類:辨識失敗、LLM 逾時、CRM 寫入失敗,各自統計,才知道該先修哪個
- Log 裡不要寫個資:身分證字號、電話號碼一旦寫進 log,log 系統本身就變成個資外洩的來源
稽核日誌(Audit Log)則是另一回事。通話錄音含有客戶個資,資安稽核或發生外洩事件時,一定會被問:「誰、在什麼時間、聽了哪一通錄音、下載了哪份摘要?」這類日誌要跟一般 log 分開存、不能被竄改,保存期限依公司政策或法規決定。
Q10系統要監控哪些指標?CPU、記憶體、磁碟、網路、請求量、快取命中率、連線池大小與延遲?
背後想抓的你能不能比使用者更早知道系統出事了?
我會把指標分成兩層:
- 資源指標(CPU、記憶體、磁碟、網路):告訴你「機器」好不好
- 服務指標(請求量、錯誤率、延遲、Queue 深度、快取命中率、連線池使用率):告訴你「使用者」的感受好不好
幾個真實會發生的例子,說明為什麼這些指標很重要:
- 連線池:每台 worker 開 20 條資料庫連線。尖峰時 worker 從 10 台自動擴到 50 台,連線數變成 1,000 條,超過資料庫上限。擴容反而把系統搞掛了。沒監控連線池的話,會完全想不通為什麼加機器會出事。
- 延遲要看 p99,不要只看平均:平均延遲 800ms 看起來很漂亮,但 p99 是 15 秒,代表每 100 通電話就有 1 位客服要等很久。平均值會把問題藏起來。
- 快取命中率:從 90% 掉到 20%,通常是資料庫快被打爆的前兆。
- 磁碟:log 把磁碟寫滿,服務什麼都寫不進去就掛了。這個雷非常經典,幾乎每個人都踩過。
Q11認證與授權怎麼運作?如何避免使用者存取他人的資料?支援哪些登入方式?帳號怎麼註冊?
背後想抓的資安漏洞,而且是最常見、最嚴重的那一種。
先分清楚:認證(Authentication)是「你是誰」,授權(Authorization)是「你可以做什麼」。
最常見的漏洞叫 IDOR:客服打開 /calls/12345/summary 看自己的通話,好奇把網址改成 12346,結果就看到別人的通話了。
只在前端把按鈕藏起來是沒用的,每一支 API 都要在後端檢查這個人有沒有權限看這筆資料。例如:是自己的通話,或者身分是主管、且這通是自己組員的通話。
登入方式:企業內部系統請直接接公司的 SSO(用 SAML / OIDC 接 Azure AD、Okta)。自己做帳號密碼最大的問題是:員工離職後 AD 帳號停用了,但你系統裡的帳號還在,他照樣能登入。
帳號註冊:內部系統通常不開放自行註冊,而是從 AD 群組同步(例如透過 SCIM),人事異動時權限會自動跟著變。
Q12系統成本是多少?可承受多少流量?每個服務單元可支援多少用量?
背後想抓的你對系統容量有「實測過的數字」,還是只是用猜的?
「服務單元」指的是一台 worker、一個 pod 能處理多少量。這個數字要用壓測量出來,有了它,擴容和成本才算得出來:
壓測結果:一台 worker 每分鐘可以完成 12 通 10 分鐘的錄音
平常尖峰:5,000 通/小時 ≈ 83 通/分鐘 → 需要 7 台
預留容錯(掛 2 台還撐得住)→ 9 台
月初 3 倍流量:約 250 通/分鐘 → 需要 21 台以上
有了台數,乘上單價就是每月成本,再除以使用者人數,就回到前面「每位使用者成本是多少」的問題。
也要講清楚超過可承受流量時會怎樣:排隊變慢?限流(rate limit)直接拒絕?還是降級成只做轉錄?
Q13各系統元件的故障轉移機制是什麼?恢復需要多久?
背後想抓的每個元件都會壞,壞掉的時候使用者會看到什麼?
有兩個常用的指標:
- RTO(Recovery Time Objective):多久之內要恢復服務
- RPO(Recovery Point Objective):最多可以接受遺失多久的資料
建議把每個元件列出來,一個一個回答:
| 元件 | 故障情境 | 故障轉移方式 | 恢復時間 |
|---|---|---|---|
| API Server | 其中一台掛掉 | 多台放在 Load Balancer 後面,自動剔除 | 幾乎無感 |
| Worker | 處理到一半掛掉 | 任務逾時後回到 Queue,由其他 worker 接手 | 該通延遲幾分鐘 |
| 資料庫 | Primary 掛掉 | Multi-AZ 自動切換 | 約 1~2 分鐘,期間寫入要能重試 |
| 錄音檔(S3) | 單一機房故障 | S3 本身跨 AZ 儲存,耐久性極高 | RPO 必須是 0,錄音絕對不能丟 |
| 外部語音辨識 / LLM API | 廠商服務中斷 | 任務留在 Queue 等恢復,或切換到備援廠商 | 看廠商;期間客服先手打 |
最後一列是最常被忘記的。外部依賴壞掉的時候,你完全修不了,只能事先想好備案。另外,摘要壞了可以重新產生,錄音不見了就沒了,所以兩者的 RPO 要求完全不同。
Q14錯誤處理策略是什麼?輸入要驗證或限制嗎?輸出要分頁嗎?失敗要重試嗎?重試幾次、間隔多久?
背後想抓的永遠不要相信輸入,永遠假設呼叫會失敗。
輸入驗證
客服忘記掛電話,錄了一整晚,產生一個 3GB 的錄音檔。Worker 載入時記憶體不足(OOM)當掉,任務回到 Queue,被另一台 worker 拿到又當掉……一台接一台掛,整個 Queue 卡死。這種會一直讓處理者當掉的訊息,叫做 Poison Message。
所以輸入一定要設限制:
- 檔案大小上限(例如 50MB)
- 錄音長度上限(例如 2 小時,超過就走特殊流程)
- 檔案格式:檢查實際的檔頭,不要只看副檔名
輸出分頁
主管查「本月所有通話」,API 一次回傳 10 萬筆,結果 API 逾時、瀏覽器卡死。清單類的 API 一定要分頁;資料量大時,建議用 cursor-based pagination,而不是 OFFSET(越往後翻越慢)。
重試策略
- 只重試有機會成功的錯誤:逾時、
429 Too Many Requests、503值得重試;400參數錯誤重試一百次也不會成功 - Exponential Backoff + Jitter:間隔 1 秒、2 秒、4 秒……逐次拉長,再加上隨機值,次數上限例如 5 次
- 避免重試風暴:外部 API 變慢時,如果所有 client 同時、立刻重試,流量瞬間變成好幾倍,會把原本只是變慢的服務徹底打死。除了 jitter,也可以加上 Circuit Breaker(失敗率太高就先暫停呼叫一段時間)
- 重試的操作必須冪等:前面提過,重試不能造成重複寫入
Q15支援哪些平台?需要響應式設計(Responsive Design)嗎?
背後想抓的每多支援一個平台,就多一份開發、測試和維護的成本,你確定真的需要嗎?
- 客服都用公司配發的 Windows 電腦加 Chrome:做 Web 就夠了
- 主管想在手機上看報表:做 Responsive Web 就好,不需要做原生 App
- 但如果需求變成「外勤人員在客戶現場用手機錄音」:就要處理離線、背景上傳、iOS 對背景執行的限制,工作量完全是另一個等級
明確寫出「不支援」的平台也很重要,例如「不支援手機上傳錄音」,免得上線後被當成 bug 回報。
Q16系統有哪些限制?在什麼情況下會觸發?
背後想抓的你知道自己系統的極限在哪嗎?撞到的時候會發生什麼事?
設計前問的「限制」,是外部技術的限制;這裡問的是你自己系統的限制。每一條都要寫清楚「什麼情況會觸發」,以及「觸發時系統會怎麼反應」:
| 限制 | 什麼時候會觸發 | 觸發時的行為 |
|---|---|---|
| 單檔上限 50MB(約 50 分鐘) | 技術支援電話常常超過 1 小時 | 拒絕處理,提示「請手動紀錄」 |
| 處理量上限每分鐘約 250 通 | 月初帳單寄出後 | 排隊,摘要延遲產生 |
| LLM API 每分鐘 token 上限 | 尖峰時段 | 收到 429 後退避重試 |
| 只支援國語與英語 | 台語通話 | 標記「辨識信心低,請人工確認」 |
把這些寫下來,半夜的 incident 就會變成「文件裡寫好的已知行為」,客服主管也能事先知道要怎麼應對。
Q17要如何排查系統中的問題?系統記錄了哪些關於使用者和系統狀態的資訊?
背後想抓的系統的「可除錯性」有沒有在設計時就考慮進去,還是出事了才來補 log。
用一個真實的排查過程,來看看需要哪些資訊。
- 用小美的使用者 ID 加上時間,找到那通電話的
call_id - 用
call_id找到 trace:上傳成功、Queue 有收到、轉錄也完成了,但逐字稿長度是 0 - 看錄音檔資訊:檔案大小正常,但取樣率是 8kHz,前一天之前都是 16kHz
- 看當時的系統狀態:電話系統前一晚升級,預設錄音格式被改掉,語音辨識吃到不支援的格式,回傳空結果卻沒有報錯
- 結論:不是模型壞了,是上游格式變了。而且我們的系統「安靜地失敗」,這本身也是要修的問題:逐字稿為空應該視為錯誤,並觸發告警
要能順利走完這條路,事先就要記錄:
- 關於使用者:使用者 ID、角色、所屬客服中心、客戶端版本、作業系統與瀏覽器
- 關於請求:
call_id、trace ID、檔案大小、錄音長度與格式、每個階段的時間戳與結果 - 關於系統狀態:部署版本、Feature Flag 狀態、由哪台 worker 處理、當時依賴服務的健康狀態
特別是版本資訊。「只有 2.3.1 版的客戶端會出問題」、「電話系統升級後才開始壞」這種結論,沒記版本的話永遠查不出來。
Q18資料保存時效是多久?系統可以接受嗎?
背後想抓的資料不刪,成本會無限成長;資料留太久,法規和外洩的風險也跟著變大。
回頭看前面算的數字:每月 8.8TB 錄音,如果永久保存,一年後超過 100TB,三年後超過 300TB。以 S3 Standard 大約每 GB 每月 0.023 美元粗估(實際價格依區域而定),一年後光是錄音儲存,每個月就要兩千多美元,而且還在持續增加。
常見做法是依照資料的使用頻率分層:
| 時間 | 儲存方式 | 用途 |
|---|---|---|
| 30 天內 | 一般儲存(S3 Standard) | 客服、主管常常需要回聽 |
| 30 天~1 年 | 低頻或封存儲存 | 客訴爭議時調閱,單價便宜很多 |
| 超過保存期限 | 自動刪除(Lifecycle Policy) | 降低成本與個資風險 |
保存期限不是工程師自己定的,要找法務和資安一起決定:
- 太短:可能不符合稽核或產業法規的要求(有些產業規定紀錄要保存一定年限)
- 太長:個資留越久,外洩時影響越大,也不符合個資法「特定目的消失或期限屆滿時應刪除」的精神
最後一個常被忽略的細節:刪除要刪乾淨。錄音刪了,但備份、log、衍生出來的逐字稿、摘要、搜尋索引都還在,其實就等於沒刪。
WRAP-UP寫在最後
這些問題看起來很多,但不需要每一題都拿滿分。它們的目的不是讓設計文件變厚,而是幫你做到三件事:
先搞懂問題
誰用、解決什麼、以前怎麼做、規模多大、成本多少。
給出選擇
至少兩個方案,誠實比較取捨,寫下推薦的理由。
把風險攤開
哪些風險可以承受、代價是什麼,哪些絕對不能妥協。
在大公司,一個設計決定會影響好幾個團隊、好幾年的維運。花一兩週把這些問題想清楚,換來的是上線後少踩好幾個月的雷。
下次寫設計文件卡住的時候,不妨先把自己當成 reviewer,把這些問題問自己一輪。答得出來的,寫進文件;答不出來的,那就是你還沒想清楚的地方。