AI 客服系統該用同步還是非同步?
EnglishOn this page本頁目錄
設計 AI 客服系統的時候,該用同步還是非同步架構?
這個問題很容易被講成「哪一種比較先進」。從一個具體的場景切入會有用得多:
客戶 A 正在等 LLM 產生回應,客戶 B 要不要也跟著等?
答案完全取決於併發怎麼處理,而兩種架構給出的答案不一樣。
一個同步 worker 到底在做什麼
單一同步 worker 處理一次請求,長這樣:
客戶 A → 撈資料 → 呼叫 LLM → 等 → 回傳結果
在這次請求跑完之前,這個 worker 碰不到客戶 B。客戶 B、以及排在他後面的所有人,都在佇列裡等。不是因為系統忙,而是因為那個唯一能服務他們的東西,正閒著卡在一次網路呼叫上。
加 worker 當然有用:四個 worker 可以同時處理四個請求。這是實打實的改善,而且在一段時間內是夠的。但天花板還在。當四個 worker 全都在等 OpenAI、等資料庫、等 CRM、等訂單系統,第五個請求還是得排隊——即使那四個 process 除了握著一條連線之外什麼也沒做。
這一點值得停下來想一下。同步聊天機器人的瓶頸通常不是運算,是 worker 全被 I/O 停在原地。
非同步改變了什麼
非同步架構切斷了「這個請求還沒完成」和「有一個 worker 專屬於它」之間的綁定。當一個請求在等外部服務時,worker 就去做別的:
- 客戶 A 在等 LLM
- 客戶 B 在撈訂單
- 客戶 C 正在接收串流回應
一個 event loop 就能協調這三件事,不需要為每個等待中的請求配一個 worker。A 的回應到了,它的 coroutine 從斷點接著跑。
這對 AI 客服特別重要,因為聊天機器人幾乎是純 I/O-bound 的工作。看一下時間實際花在哪:
- LLM API
- 向量資料庫與知識庫
- CRM 與訂單系統
- 庫存、物流、定價服務
- 對話記錄與分析
幾乎全部都在等別人的網路。這正是非同步被發明出來要解決的那種形狀的問題。
非同步沒有改變什麼
兩點澄清,因為這兩點最常在興奮中被略過。
非同步不會讓 LLM 變快。 一次 completion 要四秒就是四秒,客戶 A 的等待時間完全沒變。改善的是併發與資源利用率——在那四秒裡,系統可以去服務 B 和 C,而不是站著不動。
標了 async 的 endpoint 一樣會被卡住。 這是最容易踩到的坑,值得講精確一點。把 handler 宣告成 async,並不會讓裡面的東西變成非阻塞:
@app.post("/chat")
async def chat(req: ChatRequest):
order = db.query(...) # 同步 driver —— 卡住 event loop
reply = openai_client.chat(...) # 阻塞式 HTTP —— 又卡一次
return reply
這個 endpoint 比同步版本更糟。coroutine 裡的一次阻塞呼叫,不只是延遲了它自己那個請求——它凍結了整個 event loop,也就是凍結了 loop 正在照顧的其他所有請求。
行為符合你原意的版本:
@app.post("/chat")
async def chat(req: ChatRequest):
order = await db.fetch(...) # 非同步 driver
reply = await client.chat(...) # 非同步 HTTP client
return reply
同樣的陷阱也適用於 CPU-bound 的工作。在 coroutine 裡解析一份大 PDF、在本地算 embedding、或壓縮一張圖片,都會把 loop 佔住整段時間。那種工作屬於 thread pool 或另一個 process,不屬於 event loop。
規則講起來很簡單,做起來很容易違反:非同步路徑上的每一件事,要嘛可以 await,要嘛必須外包出去。 一個同步的資料庫 driver 就足以讓整套設計失效。
四個元件,四個不同的問題
生產級的 AI 客服系統,我通常會用到四樣東西。它們常被當成互相替代的選項討論,但各自解決的問題其實不同:
| 元件 | 解決什麼 | 不解決什麼 |
|---|---|---|
| 非同步 API | 大量併發連線而不需要一人一個 worker | 模型延遲 |
| 串流回應 | 體感等待時間 | 總完成時間 |
| 多個 worker | CPU 平行度、故障隔離 | worker 內部的阻塞 |
| 背景佇列 | 把非即時工作移出請求路徑 | 任何客戶正在等的東西 |
串流值得多講一句。它不會改變任何吞吐量數字,卻常常是對產品體感提升最大的一項。400 毫秒出現第一個字,讀起來是「有反應」;四秒的沉默之後一次吐出完整答案,讀起來是「壞掉了」——即使兩者在同一刻結束。
背景佇列也值得一句。對話記錄、分析、檔案處理、CRM 同步——沒有一項需要在客戶看到回覆之前完成。把它們移出請求路徑,通常是你能拿到的最便宜的延遲改善,而且跟 API 是同步還非同步無關。
同步不是錯的答案
對一個流量不高、流程簡單的 MVP 來說,同步比較好寫、好測、也明顯好除錯。堆疊追蹤是線性的,沒有 event loop 要推理,也不用擔心哪個函式庫在偷偷阻塞。如果你還在驗證客戶到底要不要這個東西,這份清晰度比你目前用不到的吞吐量值錢。
情況會在整合變多之後改變。當機器人接上多個市場、知識庫、訂單、庫存、物流和 CRM,每一個整合都多一次外部呼叫、多幾百毫秒的等待,也多一個讓 worker 被卡住的機會。到那時候,非同步就不再是一項優化,而是讓你能夠不必線性地買 process 來擴容的前提。
而且先做非同步遠比事後改造容易。要把一份成熟的同步程式碼搬過去,意味著換掉請求路徑上的每一個 driver 和 HTTP client,然後在生產環境裡找出漏掉的那幾個。
真正該問的問題
有用的問題不是:
同步和非同步哪個比較先進?
而是:
當 10 個、100 個、1000 個客戶同時進來,系統能不能可靠地服務他們,而不是把 worker 浪費在等待外部服務上?
對著你自己的流量和整合數量誠實回答這一題,架構的選擇基本上就自己浮出來了。
Last updated最後更新 Sep 6, 20262026年9月6日