Skip to content

子代理派生

源码版本v2026.7.20

職責

delegate_tool 讓主 agent 把一個子任務派生給隔離的子代理:子代理有自己的對話、自己的終端、自己的工具集,跑完只把最終結果交回父代理。這實作了「零上下文成本」的流水線——父代理上下文 (context) 不被子任務的過程細節污染。子代理在 ThreadPoolExecutor worker 裡執行,危險命令預設 auto-deny(可設定 auto-approve)。

設計動機

長流水線會爆,不是因為模型不行,而是因為父代理看完了所有工具呼叫的中間過程。grep 出 200 行、patch 失敗的 diff、試錯性的 build——這些細節塞進父代理上下文後,後面決策就被稀釋。delegate_task 的賭注是「子代理的過程對父代理不可見」,只讓最終文字回填。代價是失去可觀測性,所以補了一套觀測層:DaemonThreadPoolExecutor 讓子代理執行緒不阻塞程序退出,_active_subagents 註冊表 (registry) 讓 TUI 能列出/暫停/打斷活著的子代理,max_concurrent_childrenmax_spawn_depth 給並發與深度硬上限。隔離邊界是用「能看見、能停」換來的。

關鍵檔案

資料流

  1. 主迴圈 provider 回傳 tool_call: delegate
  2. delegate_task:2426 被呼叫,拿到子任務描述與可選 toolsets。
  3. DaemonThreadPoolExecutor:2651 裡起一個 worker:
  4. 子代理完成後只把最終文字交回,delegate_task 經 tool_result:798 封裝回填給父代理。
  5. 父代理上下文只增長一條 tool 結果,不被過程污染。

為什麼要裝審批回調?註解裡寫得直白:CLI 的互動式審批回調存在 terminal_tool.pythreading.local() 裡,worker 執行緒不繼承,不裝回調會 fallback 到 input(),從 worker 執行緒讀 stdin 會和父代理的 prompt_toolkit TUI 死結。所以預設是 deny:

python
def _subagent_auto_deny(command: str, description: str, **kwargs) -> str:
    """Auto-deny dangerous commands in subagent threads (safe default)."""
    logger.warning(
        "Subagent auto-denied dangerous command: %s (%s). "
        "Set delegation.subagent_auto_approve: true to allow.",
        command, description,
    )
    return "deny"

派生執行用 DaemonThreadPoolExecutor 而不是標準庫,理由在 daemon_pool.py 模組開頭:標準庫的 worker 是非守護執行緒,_python_exit 會無條件 join,一個卡死的 worker 就能讓程序退出卡幾分鐘。Daemon 版本 spaw daemon 執行緒並跳過 _threads_queues 註冊。派生入口 delegate_task 第一步是查暫停開關 is_spawn_paused(),這個開關由 TUI 的 p 鍵或 delegation.pause RPC 觸發,發現失控派生樹時凍結新派生但不動已經在跑的子代理:

python
if is_spawn_paused():
    return tool_error(
        "Delegation spawning is paused. Clear the pause via the TUI "
        "(`p` in /agents) or the `delegation.pause` RPC before retrying."
    )

深度也硬限,預設 MAX_DEPTH = 1,父(0)到子(1)就到底,孫子會被拒,要開多級得顯式配置 delegation.max_spawn_depth

邊界與失敗

  • 預算洩漏:子代理有自己的 max_iterations,但 token 花費對父代理不可見,批量派生 N 個子代理時,N 倍 provider 費用直接堆上去。max_concurrent_children 預設 3 是軟上限,設定高了會一次性燒穿預算 (budget)。
  • 巢狀死結:預設深度是 1,顯式調高 max_spawn_depth 後,orchestrator 角色的子代理能再派生,任意中間層卡住都會拖住整條鏈。DaemonThreadPoolExecutor 只解決程序退出卡死,不解決執行時掛起。
  • 審批回呼叫錯人:subagent_auto_approve=true 是 process 級設定,開了之後所有子代理都 YOLO。沒有 per-job 粒度,批量場景容易誤開。
  • 結果可信度:父代理只拿最終文字,中間過程不在上下文裡。子代理把幻覺當事實寫進最終回覆,父代理沒辦法基於過程糾偏,長鏈路派生時幻覺會逐層放大。

小結

子代理派生 = 上下文隔離 + 危險命令管控。父代理只看結果,不看過程,這讓長流水線不被過程雜訊撐爆。安全預設 auto-deny,批量/cron 場景可 opt-in auto-approve。終端執行落在沙箱後端

非官方社群學習站,內容以 MIT 授權的 NousResearch/hermes-agent 原始碼為依據。