Listen

Description

Agent Harness 的 Swarm 管理

我們在 Arize 內部構建了自己的 harness 管理工具,同時觀察到像 @cognition 的 Devin 開始管理其他 Devin、@AnthropicAI 的託管 Agent,以及 Cursor 的 @leerob 所開發的長期運行 Agent,有一件事變得顯而易見:Swarm 管理是 AI 領域下一個真正的系統性難題。

不是單一 Agent,也不是一次性的工具呼叫,而是管理長期運行的 Agent Swarm。

大多數 Agent 框架已經跨過了第一道門檻:它們可以生成子 Agent。

但那並不是 Swarm 管理。

那只是問題的開端。

有趣的問題在於子 Agent 產生之後會發生什麼?它住在哪裡?誰擁有它?它能被定址嗎?它能被引導嗎?當父 Agent 結束任務後,它還能繼續執行嗎?如果程序重啟,系統知道還有什麼在運行嗎?

這是 Agent harness 之上的下一個層級。Harness 讓一個 Agent 可以呼叫工具、讀取文件、執行指令並保持迴圈運作。委派工具(Delegation tool)讓一個 Agent 可以借用工作者。而 Swarm 管理器則擁有一支艦隊。

Agent harness 的核心功能是圍繞工具的迴圈。而 Swarm 管理器則是圍繞運行中 harness 的迴圈,確保它們持續推進。



這種區別聽起來很學術,直到你審視真實的系統。

Hermes 有一個非常好的委派原語(primitive)。它的 delegate_task 工具可以建立子 AIAgent 實例、並行運行它們、串流傳輸進度、應用逾時、中斷它們,並將結構化的摘要回傳給父 Agent。簡潔、實用、易懂。

但子 Agent 存在於父 Agent 的工具呼叫內部。

當我們在生態系統中尋找真正運作中的 Swarm 管理範例時,最棒的例子之一其實一直就在眼前:OpenClaw。

OpenClaw 擁有一個紮實的 Swarm 管理系統。它的子 Agent 會成為閘道會話(gateway sessions)。它們擁有持久的會話金鑰、運行 ID、生命週期記錄、父子血緣關係、清理策略,以及一條基於推送(push-based)的回傳路徑,將結果送回請求者。

這就是架構上的分界線。

委派(Delegation)問的是:一個 Agent 如何拆分工作?而 Swarm 管理問的是:一個執行環境如何長期擁有並管理多個 Agent?

這篇文章將花費大量篇幅,根據 OpenClaw 中的許多概念,強調我們認為 Swarm 管理中必須具備的要素。

Agent 需要一個 ID

Swarm 管理的第一個要求是識別性(identity)。

如果你無法定址一個子 Agent,你就無法管理它。你可以等待它完成,可以取消本地的 future,也可以要求父 Agent 總結發生了什麼。

但你無法操作一支艦隊。

在 OpenClaw 中,生成的子 Agent 會獲得一個會話金鑰:

agent:<targetAgentId>:subagent:<uuid>

這個金鑰正在發揮真正的作用。



它將子 Agent 變成了閘道可以識別的物件。子 Agent 可以被列出、被修補、被刪除,也可以連結回父 Agent。它能與一般的聊天會話、Cron 會話和 ACP 會話並列顯示。

子 Agent 還會獲得一個運行 ID(run ID)。會話金鑰命名了子 Agent 的居住地,而運行 ID 則命名了當前的執行過程。

這兩者缺一不可。

Swarm 管理器必須掌握基本資訊:子會話金鑰、運行 ID、請求者、控制器、深度、角色、workspace、任務標籤、清理策略、時間戳記和結果。這些元資料(metadata)回答了執行環境無法含糊帶過的問題。

是誰生成了這個子 Agent?它還在運行嗎?它有後代嗎?它應該作為會話保留,還是完成後刪除?結果真的送達了嗎?

如果這些答案只存在於模型的 context window 中,執行環境就無法管理 Swarm。

完成不只是一個回傳值

大多數委派系統都有一個簡單的合約:

父 Agent 呼叫工具。
子 Agent 運行。
父 Agent 阻塞(等待)。
子 Agent 回傳摘要。
父 Agent 繼續。

這對於有界限的委派來說是一個很好的合約。

但一旦父 Agent 不僅僅是在等待同一個呼叫堆疊時,這個合約就會失效。

在真正的 Swarm 中,父 Agent 可能處於活躍狀態、閒置狀態、可能是另一個子 Agent、可能已重啟,或者已經消失。子 Agent 可能擁有自己的子 Agent。結果可能需要作為私有的編排上下文(orchestration context),而不是使用者可見的訊息。

OpenClaw 使用基於推送的模型來處理這個問題。sessions_spawn 會回傳接受狀態與簿記資訊。結果稍後會透過註冊表(registry)和宣告流程(announce flow)送達。

大致流程如下:

父 Agent 透過 sessions_spawn 生成子 Agent。
OpenClaw 建立一個子會話。
子 Agent 的運行被註冊。
子 Agent 在自己的會話中運行。
註冊表等待生命週期完成。
OpenClaw 擷取子 Agent 的最新輸出。
它建立一個內部的 task_completion 事件。
它將該事件路由回請求者會話。

關鍵部分在於這個事件:

`json
{
type: "task_completion",
source: "subagent",
childSessionKey,
childSessionId,
taskLabel,
status,
result,
replyInstruction
}
`

這就是父子合約:捕捉完成狀態、保留來源資訊、將其路由到正確的會話,並讓該會話決定下一步做什麼。



交付層(delivery layer)具有策略。它可以引導活躍的請求者會話、將宣告排入佇列以供稍後處理、直接呼叫閘道 Agent 方法、傳送使用者可見的頻道訊息、重試,或退回到直接發送。大多數子 Agent 系統都跳過了這部分。

它們將完成視為一個回傳值。但在 Swarm 管理器中,完成是一個路由問題。

佇列比 Prompt 更重要

一旦 Agent 可以生成 Agent,執行環境就必須以非常實際的方式關注併發性。

你需要會話內的嚴格順序。兩條訊息不應該在同一個活躍的 Agent 迴圈中發生衝突。你還需要跨會話的並行性,否則艦隊就會變成單行道。

這就是為什麼 Swarm 管理看起來越來越不像 Agent 框架,而更像是執行環境基礎設施。

主 Agent 工作是一條通道,子 Agent 工作是另一條,Cron 或背景工作可能是另一條。每條通道都可以有自己的併發限制,同時每個會話仍然序列化其自身的活躍運行。

當 Agent 忙碌時,使用者發送了後續指令;當父 Agent 還在運行時,子 Agent 完成了任務;子 Agent 還有自己的子 Agent;Cron 工作完成並需要通知會話。當模型正在串流傳輸時,一條引導訊息到達了。

如果所有這些都只是訊息,系統很快就會變得混亂。



有些訊息應該引導活躍的運行,有些應該作為後續指令排入佇列,有些應該中斷當前工作,有些則應該在積壓過多時被總結或丟棄。答案存在於佇列策略中,而不是更好的 Prompt。

取消功能太過薄弱

最簡單的系統可以取消一個 future,這只是基本門檻。

一個真正的 Swarm 管理器需要能夠引導、中斷、終止和級聯(cascade)。其中「引導」是最有趣的。



當子 Agent 執行錯誤時,你不一定想殺掉它並丟失會話,你可能想重新導向它。

在 OpenClaw 中,引導一個受控的子 Agent 是一種控制平面(control-plane)操作。它會標記當前運行以進行「引導-重啟」(steer-restart),抑制過時的完成宣告,中止正在進行的運行,清除佇列,等待中止穩定,發送新的 Agent 訊息,並將註冊表從舊運行重新映射到新運行。系統正在告訴子 Agent 停止當前工作並進行轉向。

「終止」(Kill)則不同。終止應該結束運行、標記會話狀態、抑制不適當的完成宣告,並選擇性地級聯到後代。

級聯之所以重要,是因為 Swarm 是一棵樹。殺掉一個編排者(orchestrator)卻讓它的工作者活著通常是錯誤的,但有時這正是你想要的。執行環境需要足夠了解圖結構,才能做出這種區分。

這就是僅靠 Prompt 進行協調會失敗的地方。

你無法要求模型記住每一個活躍的子 Agent 並手動清理樹狀結構。執行環境必須擁有這張圖。

角色是一種安全機制

扁平化的 Swarm 無法擴展。如果每個子 Agent 都可以無限地生成其他子 Agent,系統最終會變得毫無用處甚至危險。

簡單…