狀態條為介面示意,非即時資料。
一則訊息從進來到回出去,要經過七個節點。
下面這張圖是整套系統的全部。接下來四個段落會把其中四個節點放大,講清楚它壞掉時會發生什麼事,以及我們用什麼接住它。
各家第三方客服 / 社群 / 遊戲平台
壞掉會怎樣不受你控制,這是前提不是問題
驗簽章、驗租戶 token、驗 payload、落地
壞掉會怎樣訊息靜默消失,沒有任何錯誤訊息
各平台格式 → 單一內部訊息格式
壞掉會怎樣每加一個平台,下游全部要改一次
排隊處理,失敗的進死信佇列
壞掉會怎樣一則爛訊息卡住整條線
會話接手 / 釋放 / 關閉 + 即時同步
壞掉會怎樣兩個客服同時回同一個人
依會話來源路由回正確平台
壞掉會怎樣回錯人、回錯平台
第三方 API key 加密存放與輪替
壞掉會怎樣客戶的帳號被別人拿去用
NODE 02
平台送了,但你沒收到——而且它不會再送一次。
這是所有 webhook 整合的根本問題,而且它沒有錯誤訊息。第三方平台把事件 POST 給你,收到 2xx 就當作送達;你的服務剛好在重啟、剛好在 GC、剛好網路抖了一下,這則訊息就永遠不存在了。你的系統一切正常,log 乾乾淨淨,客訴半小時後才到。
對策:一條主線,一條補償線。
| 項目 | 主線:webhook 接收 | 補償線:輪詢 worker |
|---|---|---|
| 觸發 | 平台主動推送 | 依排程主動去撈 |
| 延遲 | 秒級 | 取決於輪詢間隔 |
| 前置檢查 | 簽章驗證 → 企業 token 驗證 → payload 驗證 → 持久化 | 只挑活躍會話去撈,不整批掃 |
| 保護 | healthcheck 端點 | rate-limit 追蹤 + 批次入列 |
| 角色 | 正常情況下的唯一路徑 | webhook 掉了的時候補資料 |
輪詢不是「無腦重試」。這支 worker 會追第三方平台的速率上限,撈之前先挑出目前活躍的會話、批次入列,避免補償機制自己把你的 API 額度打爆——webhook 補償最常見的翻車方式就是這個。
poll-fallback-worker · poll-loop / rate-limit-tracker / batch-enqueue / select-active-conversations
NODE 03
每個平台一種訊息格式。下游只該認識一種。
每接一個新平台,就多一種 JSON 結構、多一組欄位命名、多一套時間格式、多一種「已讀」的定義。如果下游直接吃原始 payload,第三個平台接完你就會開始在收件匣的程式碼裡寫 if (platform === 'xxx'),第五個平台接完就沒人敢改它了。
正規化層是唯一知道平台差異的地方。
RAW
平台原始 payload,欄位命名各自為政
NORMALIZE
對映欄位、統一時間、統一會話識別、標記來源平台
INTERNAL
單一內部訊息格式,下游只認識這一種
好處在加平台的時候才會顯現:新增一個來源平台,改動範圍是一個 adapter,不是整個收件匣。 收件匣、即時同步、回覆分派、稽核日誌完全不需要知道今天多了哪一家。
等客戶給憑證不該阻塞開發——第三方平台的正式憑證通常要等客戶走完流程才拿得到,而那往往是專案後期。這套系統把 mock 模式做進了資料庫層的開關,另外附一台獨立的 Docker mock server,所以在真實憑證到位之前,正規化、收件匣、即時同步整條線都已經跑完並測過。這是交付方法,不是玩具——它決定了憑證延誤時專案還能不能往前走。
NODE 04
處理失敗的訊息,不會消失,會排在一個你看得到的地方。
一則格式異常的訊息、一次資料庫短暫斷線、一個沒預期到的欄位型別——在多數自幹的接收端裡,這些情況的處理方式是 catch 起來寫一行 log,然後訊息就沒了。問題不是它失敗,是沒有人知道它失敗過,也沒有辦法在修好之後把它救回來。
死信佇列(DLQ):失敗是一種狀態,不是一次事件。
驗證或處理失敗則轉
「重放」聽起來很小,但它決定了修 bug 的姿態:沒有重放,你修完 bug 只能跟客戶說「那段時間的訊息我們遺失了」;有重放,你修完 bug 按一顆按鈕,那段時間的訊息就補回來了。
POST /api/v1/admin/webhook-events/{payload_id}/replay / …/ignore
驗證或處理失敗的事件連同原始 payload 一起進死信佇列,不覆寫、不刪除
後台有 DLQ 頁面,逐筆列出失敗事件與原因,不需要進資料庫查
每筆可一鍵重放(用原始 payload 重跑),或標記忽略並留下紀錄
NODE 07
客戶的 API key 不會明文躺在你的資料表裡。
做第三方整合就一定要保管客戶的憑證。最常見的做法是開一個 credentials 欄位直接把 token 存進去——資料庫備份、log、匯出報表、離職的工程師,任何一個環節外洩,受損的是客戶的帳號,而你在合約上是有責任的。
憑證加密後才落地。GCM 同時提供機密性與完整性驗證,被竄改過的密文解不開
加密金鑰不與資料同放,來自部署環境而非資料庫
有專屬輪替流程。「換一把金鑰」是可執行的操作,不是要停機重寫資料
另外兩件同一等級的事:稽核日誌的寫入權限被單獨收緊過(有一支專門的資料庫層 policy migration 在做這件事,不是靠應用層自律);個資在寫進 log 之前就被遮罩,不是事後清理。登入端另有 IP 速率限制、登入嘗試紀錄與 email 遮罩。
SCOPE / 誠實區
這一層要的不是聰明,是不掉訊息。
這套系統裡沒有任何一行呼叫大型語言模型的程式碼,我們也不打算含糊帶過。市面上很多「AI 客服」賣的是回話的品質,而客戶上線後最常遇到的問題不是回得不夠好,是訊息根本沒進來、同一個客人被兩個客服各回一次、掉了的訊息救不回來。這些是佇列問題、狀態問題、憑證問題,不是模型問題。加一顆再聰明的模型也不會變好。
| 這套系統做的 | 這套系統不做的 |
|---|---|
| 訊息可靠接收、失敗可重放 | 自動回覆客戶問題 |
| 多平台格式正規化 | 意圖辨識、情緒分析 |
| 會話接手 / 釋放 / 關閉與即時同步 | 建議回覆、自動草稿 |
| 憑證加密保管與輪替 | 知識庫問答 |
| 稽核日誌與 PII 遮罩 | 任何形式的模型推論 |
那 AI 可以接在哪裡?
可以,但要說清楚它現在不存在:系統內已有一套話術庫(每位客服有自己的擁有範圍與排序),以及一層會話同步流程——這兩者分別是「語料」與「插入點」,未來要加建議回覆或知識庫問答,架構上有位置可以放。但那一層目前一行都還沒寫,需要另行開發與計價。我們寧可現在講清楚,也不想在 POC 的時候被你發現。
所以正確的說法是——這是 AI-ready 的客服基礎設施,不是 AI 客服。
可以查證的部分,只寫可以查證的。
- 1089
- 單元測試全數通過
- 30
- Playwright 端對端測試通過
- 18
- 資料庫 migration,已套用於雲端環境
- 3
- 獨立部署的 worker,各有 Dockerfile
以上數字出自來源系統 README 與 repo 內容,非行銷估算。
目前狀態:開發完成,待客戶端 UAT。 Wave 0–8 全數完成、已部署至雲端環境。尚未進入正式營運的原因在客戶端——真實平台憑證與使用者驗收測試尚未完成。我們不會把這個狀態說成「已上線服務」。
如果你正在接第三個平台,我們可以聊。
最適合開始談的時機有兩個:一是你已經接了兩三個平台、開始感覺到程式碼裡的 if 越來越多;二是你已經被 webhook 掉包咬過一次,正在找有沒有更成熟的做法。
本頁描述的系統為客戶專案交付成果,客戶名稱未經授權不予揭露。頁面內所有介面示意為重繪,非客戶實際資料。