跳到內容

使用 Command Queue 解決 Agent 併發衝突

你有沒有遇過這種狀況?當多個使用者同時傳訊息給你的 Agent,或是同一個人在短時間內連發好幾封訊息,你的後台就開始打架了。LLM 呼叫不只昂貴,如果多個程序同時搶著讀寫同一個 session 檔案或 log,很容易導致系統混亂,甚至讓你直接撞上 API 的 rate limit。

我們透過一個輕量化的 in-process 佇列來序列化 inbound 的自動回覆流程。這能確保同一個 session 內的操作乖乖排隊,同時又允許不同 session 之間保持安全的並行運作。

  • 使用 Gateway 回覆管線的 inbound 頻道(如 WhatsApp, Telegram, Slack, Discord, Signal, iMessage, webchat 等)
  • 專案中已實作 runEmbeddedPiAgent

預設情況下,所有頻道都會使用 collect 模式。如果你想自定義全域設定或特定頻道的行為,可以在設定檔中加入以下內容:

{
messages: {
queue: {
mode: "collect",
debounceMs: 1000,
cap: 20,
drop: "summarize",
byChannel: { discord: "collect" },
},
},
}

如果你想在特定對話中即時切換模式,直接在對話框輸入指令即可:

  • /queue collect
  • /queue steer
  • /queue reset(恢復預設值)

這個佇列採用 lane-aware FIFO 機制,根據 session key(lane session:<key>)來排隊,保證同一個 session 同一時間只有一個活動中的 run。

接著,這些 session run 會被放入 global lane(預設名稱為 main),整體的並行數量受到 agents.defaults.maxConcurrent 的限制。當訊息進入佇列時,如果該頻道支援,系統會立即觸發「輸入中」狀態(typing indicators),所以使用者不會覺得 Agent 沒反應。

你可以根據需求決定新訊息如何影響目前的執行流程:

  • collect (預設):將所有排隊中的訊息合併成一次後續回覆。如果訊息來自不同頻道,則會分開處理以確保路由正確。
  • steer:立即介入目前的執行(在下一個 tool 邊界後取消掛起的工具呼叫)。如果不支援 streaming,則會退回到 followup 模式。
  • followup:在目前的執行結束後,再排隊進行下一次 Agent turn。
  • steer-backlog:立即介入目前的執行,同時保留該訊息供後續回覆使用。
  • interrupt (舊版):中止該 session 目前的執行,直接跑最新的訊息。

這些選項適用於 followup、collect 與 steer-backlog:

  1. debounceMs: 等待一段安靜時間後才開始後續動作,避免 Agent 不斷回覆「請繼續」。
  2. cap: 每個 session 允許排隊的最大訊息數。
  3. drop: 當超過容量上限時的處理策略,可選擇 old(丟棄舊訊息)、new(丟棄新訊息)或 summarize。
  4. summarize: 會將被丟棄的訊息整理成簡短的點列清單,作為合成的提示詞加入後續回覆中。
  • 指令看起來卡住了:開啟 verbose logs,尋找 “queued for …ms” 字樣。這能幫你確認佇列是否正在消耗中。
  • 想確認佇列深度:同樣開啟 verbose logs,觀察佇列的時間線資訊。

這套機制完全基於 TypeScript 與 Promise 實作,不依賴外部資料庫或背景 worker 執行緒,非常輕量。

如果你在設定過程中遇到問題,可以詢問 AI Setup Assistant 獲取即時協助。

OpenClaw

OpenClaw Expert

還是卡住了?

如果這篇文件沒有解決你的情境,直接問 OpenClaw Expert,拿到可執行步驟。