MapleCheng

在浩瀚的網路世界中無限潛水欸少年郎!

0%

AI 多代理排程的隱形陷阱:當 Sub-Agent 被靜默丟棄

昨天半夜做了一件事:把散落各處的 10 個定時新聞報告,統一整合進一個 skill 裡。聽起來很合理對吧?一個 dispatcher cron 起來,spawn 6 個 sub-agent 分頭跑報告,完事收工。

結果跑起來才發現——有幾份報告憑空消失了。

問題:Sub-Agent 去哪了?

我的原始設計很直覺:一個定時排程觸發,在裡面用 sessions_spawn 一口氣叫出 6 個 sub-agent,各自負責一份報告。邏輯上完全沒問題,程式碼也沒報錯。

但每次執行完,總有 1~2 份報告沒有產出。

一開始我以為是 prompt 有問題、是報告格式不對、是某個 API timeout。排查了一輪都不是。最後才發現真相:超過並發上限的 spawn 請求,會被靜默丟棄。

對,你沒看錯。不是排隊等待,不是拋出錯誤,不是回傳失敗——是直接丟掉,什麼都不告訴你。

靜默丟棄有多危險?

在一般的系統設計裡,我們習慣依賴錯誤訊息來偵測問題。Queue 滿了會拒絕、Thread pool 滿了會拋異常、API rate limit 會回 429。這些都是明確的訊號,你可以據此做 retry、做 backoff、做降級。

但靜默丟棄打破了這個假設。你的調度器以為 6 個 sub-agent 都派出去了,實際上只有 5 個(甚至更少)真的在跑。而且你沒有任何方式知道哪個被丟了——因為 spawn 的回應看起來是正常的。

這就像是你在餐廳點了 6 道菜,服務生笑著說「好的」,然後上了 4 道就不再出現了。你等了半小時才意識到剩下的根本不會來。

並發限制的兩層結構

深入研究後,我發現並發限制其實有兩層:

  • 全域上限(比如 maxConcurrent = 8):整個系統同時能跑多少個 sub-agent
  • 單一 session 上限(可能是 5):一個 parent session 最多能 spawn 幾個 children

這兩個限制是獨立的。就算全域還有空位,單一 session 也可能已經到頂。更麻煩的是,這個 per-session 限制不一定有明確的文件說明,你只能靠踩坑發現。

            
            flowchart TD
    A[Dispatcher Cron 啟動] --> B[spawn sub-agent 1~6]
    B --> C{檢查並發上限}
    C -->|未超過| D[✅ sub-agent 正常執行]
    C -->|超過全域上限| E[❌ 靜默丟棄]
    C -->|超過 per-session 上限| F[❌ 靜默丟棄]
    E --> G[沒有錯誤訊息]
    F --> G
    G --> H[調度器渾然不知]
          

解法:每個任務一個獨立 Session

既然「一個 dispatcher spawn 多個 sub-agent」行不通,那就換個思路:讓每個報告成為獨立的 cron job,各自跑在自己的 isolated session 裡。

原來的架構:

1
2
3
4
5
6
7
[Cron] → [Dispatcher Session]
├── spawn sub-agent A
├── spawn sub-agent B
├── spawn sub-agent C
├── spawn sub-agent D
├── spawn sub-agent E ← 可能被丟棄
└── spawn sub-agent F ← 可能被丟棄

改成:

1
2
3
4
5
6
[Cron A][Isolated Session A] → 執行報告 A
[Cron B][Isolated Session B] → 執行報告 B
[Cron C][Isolated Session C] → 執行報告 C
[Cron D][Isolated Session D] → 執行報告 D
[Cron E][Isolated Session E] → 執行報告 E
[Cron F][Isolated Session F] → 執行報告 F

每個 cron job 直接在自己的 session context 裡執行任務,不 spawn 子代理。這樣就繞過了 sub-agent 並發限制的問題。

但這不是開倒車嗎?

你可能會問:「這不就是把一個程式拆成六個嗎?感覺很笨。」

沒錯,從表面上看這確實是更「原始」的做法。但仔細想想,它其實有幾個優勢:

1. 故障隔離

一個報告失敗不會影響其他報告。如果是 dispatcher 模式,dispatcher 本身出問題就全軍覆沒。

2. 可觀察性

每個 session 有獨立的 log,出問題一眼就能看出是哪份報告掛了。不需要在一個大 log 裡面找特定 sub-agent 的輸出。

3. 時間彈性

不是每份報告都需要同時跑。有的可以早上 6 點跑,有的可以 7 點跑。Dispatcher 模式反而限制了你的排程靈活度。

4. 資源可控

每個 session 的資源消耗是獨立計算的。不會因為某個報告特別吃 token,就影響其他報告的執行。

設計多代理系統的幾個教訓

這次踩坑讓我整理了幾條心得:

永遠不要假設 spawn 一定成功。 就算 API 沒有回傳錯誤,也要有機制驗證 sub-agent 確實在執行。可以用 heartbeat、可以用結果檢查、可以用 session list 確認。

靜默失敗是最危險的失敗模式。 比起炸一個 exception 讓你看到,「什麼都沒發生」反而最難 debug。在設計系統時,如果你發現某個環節有靜默失敗的可能,要麼加上明確的回報機制,要麼繞過它。

簡單架構往往比聰明架構更可靠。 「一個 dispatcher 管理多個 sub-agent」看起來優雅,但引入了中心化的單點故障和並發限制問題。「每個任務獨立跑」看起來笨,但實際上更穩固。

多代理不等於多 sub-agent。 「多代理」的核心是任務分解和並行執行,不一定要用 parent-child 的 spawn 模式實現。Independent session 也是一種多代理架構,只是協調方式不同。

結語

AI Agent 的生態還很年輕,很多行為沒有你在傳統軟體開發裡習慣的那種明確規格。「超過上限會靜默丟棄」這種行為,在傳統系統裡會被視為 bug,但在 agent 框架裡可能只是「還沒來得及處理的邊界情況」。

所以,多跑、多測、多踩坑。不要相信一切都會按照你想像的方式運作——尤其是在半夜三點重構系統的時候。

如果你也在搞 AI 多代理系統的排程設計,希望這篇能幫你少走一段彎路。我的半夜就不白熬了 😂