Agent协作:八家实现对照

同一个问题问八个参考实现:一个agent怎么把活交给另一个agent,交出去以后还能不能管。 八个维度逐条对照,每条都覆盖全部八家,没有这个功能的如实标"没有"。 我们自己的机制在Agent协作:工具面那页, 设计正文是agent-collaboration.md

00 三层怎么分 01 八家总表 02 能不能生子agent 03 能不能直接通信 04 追踪和级联取消 05 防死循环 06 子agent的工具集怎么裁 07 身份寻址和实现规模 08 我们计划怎么做

00三层怎么分第一层

这页装第二层和第三层。第一层是我们已经跑起来的机制,它有自己的一页,两页互相链接。

第一层 我们现在怎么做 一个原语:往一条分支投递内容,触发它跑一轮,结果自动回送。四个域十二个工具。 另一页:Agent协作 · 工具面 四域词汇 · 权力模型 · 三个预算 · 归档 · 典型场景 第二层 人家怎么做 八个参考实现,八个维度。这页从01到07全是这一层。 claude-code codex-cli openclaw opencode hermes-agent pi-ai pi-mono weclaw 第三层 我们计划怎么做 四域词汇和刚定的工具面从哪来、哪里刻意跟别人不一样、设计已定但代码还没跟上的还剩什么。第08节。
怎么核实的。七个仓库直接读源码,给文件路径加行号。Claude Code 是两个来源交叉验证: 泄露源码树只有适配层(最后一次提交是 2026 年 4 月),本机装的 2.1.226 二进制里才有真正的实现, 两者的设计已经不一样了。凡是取自二进制的结论都标字节偏移,写成二进制 @250599082这种形式。 两边不一致的地方在下面逐条点出来。

01八家总表第二层

八个维度一屏看完。每格都有下面的小节展开,含代码位置。

生子agent直接通信任务追踪级联取消 防环手段裁工具寻址规模
我们 进程内,深度1能,任意对任意task_id,三个工具级联 八种按剩余余额摘分支名 / SID:HEAD4.5k
Claude Code 进程内,深度3能,跨机器也能agentId,三个工具级联 五种按深度摘,再调用时拒名字@团队名24k
codex-cli 进程内,V1深度1,V2无上限能,兄弟直连ThreadId,四个工具只有一半 两种摘,V1额外再拒路径式 /root/x7k
openclaw 进程内或外部进程,深度1能,但要开开关runId,只有list级联 八种摘,spawn时再查一次session key17k
opencode 进程内,隐式深度1不能子session id级联 三种按规则形状,摘或拒agent类型名0.4k
hermes-agent 进程内线程池,深度1树内不能,看板间接模型拿不到id级联 五种工具集级摘匿名,一次性2.9k
pi-mono 子进程,无深度上限不能没有只级联一层 两种白名单摘磁盘上的 .md 名1.3k
pi-ai 没有没有没有不适用 没有不适用没有0
weclaw 没有不能没有不适用 一种不适用@名字1.1k
一眼能看出的三件事。生子agent几乎人人都有,而且都收敛到同一个形状:进程内、深度上限1。 直接通信只有四家做,其中一家还默认关着。任务追踪的差别最大:从"模型能拿到task_id并且能取消" 一路到"模型连id都拿不到"。

02能不能生子agent第二层

三问:能不能生、同进程还是子进程、有没有层数限制。

从"没有"到"进程内且不限层数",八家落在四个位置 中间那一格挤了六家:进程内派生、深度上限压到很低。这是目前的共识做法。 没有这个概念 派生的入口就不存在 pi-ai 只是一份协议快照,没有agent循环 weclaw 起外部CLI子进程,但那是顶层agent 有,但不在核心里 核心明确不做,示例扩展做 pi-mono 子进程:每个子agent 起一个新的 pi 进程 没有层数上限,而且子进程会 重新加载同一个扩展 进程内派生,有层数上限 六家都在这里,上限都定得很低 我们 线程池,深度1,设0即不限 Claude Code 嵌套查询循环,深度3 codex-cli V1 tokio任务,深度1 openclaw 深度1,配置可放到5 opencode 没有计数器,靠权限注入 hermes-agent 线程池,深度1,可配到3 进程内, 没有层数上限 codex-cli V2 算出了子层数, 但从不去检查它 默认关闭 还有三家在进程内之外另开了一条子进程通道:openclaw 的 ACP 运行时跑在宿主机上、hermes 的 ACP 外部CLI 和看板每卡片一个进程、Claude Code 的 tmux 队友和云端 agent。 这些通道解决的是隔离,不是层数:层数照样由上面那个上限管。
能不能同进程还是子进程层数限制,以及在哪儿判
我们能,agent 进程内线程池,默认4个worker agent.max_spawn_depth默认1,设0即不限。越界返回"自己动手做"。 另有一条一轮之内的扇出上限agent.max_spawn_fanout默认8 functions/tools/agent/agent/agent.py;配置默认 config_schema.py
Claude Code能,Agent(旧名Task 进程内嵌套查询循环。另有 tmux/iTerm 子进程队友,和isolation:"remote"的云端agent 深度3,环境变量CLAUDE_CODE_MAX_SUBAGENT_SPAWN_DEPTH可改;并发上限20。 泄露源码树里根本没有深度计数器,这个上限只存在于二进制 二进制 @250599082 var WJu=3;判定 @258438030;并发 @250622624 yny=20
codex-cli能,spawn_agent 进程内 tokio 任务,复用同一个线程管理器,没有任何进程创建 V1 深度1;V2 算出了子层数却从不检查,等于不限,只剩并发上限兜底 core/src/config/mod.rs:284;V1 判定 handlers/multi_agents/spawn.rs:65-71;V2 计算 multi_agents_v2/spawn.rs:62
openclaw能,sessions_spawn 两种运行时:subagent进程内,acp是宿主机上的外部进程,沙箱里禁用 深度1,配置可放到5。层数从 session key 里数:subagent:段得出 config/agent-limits.ts:8;判定 agents/subagent-spawn.ts:784-791
opencode能,task建子session 进程内,同一个 prompt 循环重入,不起进程 没有数字上限。给子session注入一条task: * deny,这条规则把task工具 从子的schema里删掉,于是深度实际是1。agent自己写permission.task=allow就无限 tool/task.ts:133-135;agent/subagent-permissions.ts:18,25
hermes-agent能,delegate_task 默认进程内线程池。另两条子进程路径:ACP 外部CLI、看板每张卡片起一个 hermes 进程 MAX_DEPTH=1,配置钳在[1,3] tools/delegate_tool.py:133,136-137;判定 :1958-1972
pi-mono核心刻意不做,示例扩展做 子进程:每个子agent起一个新的pi --mode json -p --no-session 没有。而且子进程会重新加载同一个扩展目录,其中worker这个agent不声明工具白名单, 于是它拿得到派生工具,构成一条无界递归路径,官方的实现流程恰好用的就是它 README.md:476"No sub-agents";examples/extensions/subagent/index.ts:329-333;agents/worker.md:1-5
pi-ai没有不适用 不适用。整个仓库是 pi-mono 里 LLM 传输层的只读快照,不编译也不被导入 README.md:1-19
weclaw没有 它确实起子进程,但起的是用户配置的 agent CLI 本体,属于顶层agent 不适用,没有嵌套 agent/cli_agent.go:140,159;接口只有四个方法 agent/agent.go:79-95

03agent之间能不能直接通信第二层

问的是有没有一条不经过父级的通道。有的家只有"父派活、子交活"这一条线,有的家兄弟能互相寻址。

三种拓扑 没有agent之间的通道 A B weclaw:一条微信消息可以同时发给多个agent, 但它们互相看不到对方的回复。pi-ai 连agent都没有。 weclaw · pi-ai 只经父级 A B 父派活、子交活,兄弟之间没有地址。 opencode · pi-mono · hermes-agent(委派树内) 可以直连 A B 别的 会话/机器 兄弟能互相寻址,父不在这条路径上。 我们 · Claude Code · codex-cli · openclaw(要开开关) 四家能直连的里面,三家的地址还能越出本进程:Claude Code 能发给本机另一个会话和另一台机器上的会话,我们能发给任意别的会话,openclaw 靠 session key 跨会话。 hermes 是个特例:委派树里子agent之间没有通道,但看板那条线的 worker 之间靠共享数据库上的评论交换信息,属于间接通信。
能不能直连怎么连的,怎么投递
我们 send_message(to=),任何agent对任何未归档的分支,跨会话同一条路径,不做归属检查。 目标正忙就进它会话的收件箱排队,本轮结束后消费 functions/tools/send_message/send_message/send_message.py:82,154;地址解析 addressing.py:77-156;收件箱 agent/inbox.py
Claude Code能,而且能跨机器 SendMessageto可以是队友名字、main(回主对话)、*(广播)、 uds:/路径.sock(本机另一个会话)、bridge:会话id(另一台机器)、裸agentId。 队友走文件信箱~/.claude/teams/{团队}/inboxes/{名字}.json,进程内子agent走内存队列。 给一个已经结束的agent发消息会把它从记录里唤醒接着跑 tools/SendMessageTool/SendMessageTool.ts;utils/peerAddress.ts:8-21;utils/teammateMailbox.ts:4-8,56-66;唤醒 tools/AgentTool/resumeAgent.ts:42-67;地址表 二进制 @258816708
codex-cli能,兄弟直连 send_messagefollowup_task直接投到目标线程,父不在路径上。 地址是路径式的,接受绝对路径,所以一个兄弟可以直接寻另一个兄弟。两种投递:只排队,或者立刻唤醒对方跑一轮 handlers/multi_agents_v2/message_tool.rs:11-24,51-131;绝对路径 protocol/src/agent_path.rs:59-71;投递 core/src/agent/control.rs:171
openclaw能,但默认关着 sessions_send跨agent要求配置里打开开关,还要逐对allowlist放行。 对话是有界的来回循环。子agent被禁用这个工具,只能走通告队列回父 agents/tools/sessions-send-tool.ts:193,249-256;来回循环 sessions-send-tool.a2a.ts:94;子agent禁用 agents/pi-tools.policy.ts:49-50
opencode不能 全部是父子往返。内置工具表里没有任何消息类工具。父可以给还在跑的子追加消息,方向仍然是父到子 tool/registry.ts:217-236 是完整的内置工具表;父到子 tool/task.ts:242-257
hermes-agent委派树内不能,另一条线间接能 子只向父汇报,send_message写在"子绝不能有"的名单里。 但看板那条线不一样:每张卡片一个 worker 进程,它们靠共享数据库的卡片评论交换信息, 根卡片上还有一块约定格式的公共区 黑名单 tools/delegate_tool.py:50;看板 toolsets.py:252-269;公共区 hermes_cli/kanban_swarm.py:11-14,26,226-268
pi-mono不能 父到子只在派生时给一段prompt,子到父只在退出时给一段stdout。 串行模式下上一个的输出用字符串替换塞进下一个的任务描述,这是兄弟之间仅有的数据通道 examples/extensions/subagent/index.ts:530,569;并行任务之间完全隔离 :618-639
pi-ai没有没有agent,也就没有通信
weclaw不能 只有人对多个agent的广播:@a @b 消息把同一条原文分发给几个agent, 每个的回复各自加前缀发回给人。agent之间看不到对方 messaging/handler.go:456-486;命令表只有7条 :686-698

04派出去的活怎么追踪,取消是不是级联第二层

四问:有没有任务id、能不能查状态、结果怎么回来、能不能取消。加上第五问:停掉父的会不会停掉子孙。

取消传播的三种形态 级联到底 我们 · Claude Code · openclaw · opencode · hermes-agent 只停一个 codex-cli 的 interrupt_agent,V2 里这是唯一的取消动词 停一层,再下一层成孤儿 孤儿 孤儿 pi-mono:子进程没登记进统一清理表,父被强杀就留下孤儿 五家做级联的都用同一套办法:沿父指针走一遍,用一个已访问集合防住畸形环。差别只在走的是内存里的对象树还是持久化的任务记录。
模型拿得到任务id吗能查状态吗结果怎么回来模型能取消吗取消级联吗
我们 t_开头list_tasks 自动接到派发方会话后面,派发方下一轮读到 task_stop,带归属检查 级联。沿parent_task_id广度优先,已访问集合防环 agent/task/runner.py:336-380;回流 :1075-1186;归属 functions/tools/agent/_ownership.py:21-58
Claude Code agentId任务注册表;TaskOutput取结果 同步子agent直接进工具结果;后台agent写磁盘再发一条通知 TaskStop,带归属检查,主会话可以停任何一个 二进制里级联,泄露源码树里不级联:那边后台agent刻意不挂父的中止信号, 为的是用户按ESC不误杀后台工作 级联 二进制 @265089223,祖先判定 @253677780;不级联 tools/AgentTool/AgentTool.tsx:694-696
codex-cli ThreadId加路径list_agentsget_status,还能订阅推送 完成监视器合成一条消息投给父;wait_agent可阻塞等,默认30秒上限1小时 interrupt_agent;V1还有close_agent 只有一半close_agent级联,interrupt_agent只停一个。 V2根本没有close_agent,所以V2只剩不级联的那个 级联 core/src/agent/control/legacy.rs:86-101,后代枚举 control.rs:732-756;不级联 control.rs:247-257
openclaw runIdsubagents工具只有list一个动作 推送不轮询:sessions_yield结束本轮,结果作为下一条消息到达 模型没有取消工具,取消是聊天命令/subagents kill和一个HTTP接口 级联。递归下去,用已见集合防环,每个子还清队列并落一条中止标记 agents/subagent-control.ts:205,232,238,256;kill命令 auto-reply/reply/commands-subagents/action-kill.ts:27
opencode 子session id,可以拿它复用之前那个子session 没有查询工具,提示词明确禁止轮询状态 前台是工具返回值,后台是往父会话塞一条合成消息 模型没有取消工具,取消在宿主侧 级联。中止信号加上对后台任务元数据的广度优先遍历,走到不动点为止;删session也递归 session/run-state.ts:116-148;tool/task.ts:296-306;测试 test/tool/task.test.ts:870
hermes-agent 拿不到。返回里只有序号、状态、摘要、用量,内部id只给运维看 没有模型工具,状态查询是网关的RPC 全同步阻塞,一次性返回全部子任务的结果 模型没有,中断是网关RPC 级联。中断递归进活跃子对象。协作式,不硬杀线程 结果字段 tools/delegate_tool.py:1684-1720;递归 run_agent.py:1690-1730
pi-mono 没有。工具拿到的调用id直接被丢弃 没有。进度只能靠回调推给界面 读子进程stdout的事件流,取最后一段助手文本,单任务截到50KB 没有句柄,只有环境里那个中止信号 只一层:中止信号触发SIGTERM,5秒后SIGKILL。子进程没登记进统一清理表,父被强杀就留孤儿 丢弃id examples/extensions/subagent/index.ts:466;杀进程 :393-403;未登记 对比 src/utils/shell.ts:172
pi-ai 没有没有没有没有 不适用。只有一个平的中止信号给单次HTTP请求
weclaw 没有没有同步返回值,按微信用户id关联 没有,命令表里就没有取消 不适用,没有层级。只有进程关停时的统一取消 messaging/handler.go:541-556;命令表 :686-698

05防死循环怎么做第二层

把各家用的手段摆成一张矩阵。空格表示这家确实没有这个手段,不是没查到。

防死循环:每家点亮了哪几格 格子里是这家实际用的数值。空格表示这家没有这个手段,不是没查到。 深度上限 扇出上限 并发上限 消息计数 信箱上限 去重 限速 超时·轮次 我们 1 8 4 8 50 60秒 50轮 Claude Code 3 20 50 按uuid codex-cli V1=1 V2无 6 / 4 openclaw 1 5 8 / 4 20 按键 2秒 重试3 opencode 隐式1 连3次 步数∞ hermes-agent 1 3 3 600秒·50轮 pi-mono 8 4 pi-ai weclaw 5分钟 一眼可见的两件事:深度上限是唯一一个几乎人人都做的手段,而消息计数只有三家做。 codex-cli 的 V2 通道同时去掉了深度上限、保留了兄弟之间的直接寻址,两个 agent 互相派活没有任何计数拦得住。
关键常量和它在哪
我们 深度1(只数创建出来的代数)、一条链的消息预算8(派生、发消息、派活、回流全都计数)、 一轮的扇出上限8、并发4、每轮50次工具调用、收件箱上限50丢最旧、60秒内同发送方同内容视为重复。 自己发给自己一律拒,不受预算影响。三个预算都能设0取消。 每个数字的取值理由写在常量旁边,对照的就是这一页 MAX_MESSAGES=8 send_message/depth.py;MAX_SPAWN_DEPTH=1 与 MAX_SPAWN_FANOUT=8 agent/agent.py;两个计数器各一个ContextVar depth.py; 每轮50次 agent/agent_loop.py:246;上限50和60秒 agent/inbox.py;自发拒 send_message.py:139-144
Claude Code 深度3、并发20、信箱50条环形丢最旧、按消息uuid去重、fork递归用一个布尔开关拦、 队友名册是平的不许队友再生队友、预算到顶就不许起新agent。没有发消息的限速,也没有消息条数上限 二进制 @250599082 深度3;@250622624 并发20;@253677687 信箱50;@253677581 uuid去重; fork拦截 tools/AgentTool/forkSubagent.ts:78-89;队友平铺 AgentTool.tsx:273
codex-cli 只有两个上限:深度(V1=1,V2没有)和并发(全局6,V2每会话4)。 没有去重、没有消息计数、没有限速、没有环检测。V2既去掉了深度上限又允许兄弟互相寻址, 两个agent互相派活没有任何计数拦得住 core/src/config/mod.rs:212-213,284;并发判定 core/src/agent/control/execution.rs:68-76
openclaw 层数最多的一家:深度1(可配到5)、每个agent最多5个子(可配到20)、并发8和4、 跨agent对话默认5轮上限20轮、插话限速2秒、通告重试上限3次、列表按子会话键去重,另有一个默认关闭的通用工具环检测器 config/agent-limits.ts:3-8;来回轮次 agents/tools/sessions-send-helpers.ts:15-16; 限速 agents/subagent-control.ts:45,507-516;重试 agents/subagent-registry-helpers.ts:35
opencode 深度靠权限注入实现,没有数字。另有一个重复调用检测:最近3次是同一个工具且入参逐字节相同就弹权限询问。 步数上限默认是无限 DOOM_LOOP_THRESHOLD=3 session/processor.ts:35,判定 :519-546;步数 session/prompt.ts:1281
hermes-agent 深度1(可配到3)、并发子3、每个子最多50轮迭代、每个子硬超时600秒加心跳检测、 一个全局的派生暂停开关。没有去重,没有消息计数。另外从结构上禁递归:派生工具集从叶子身上剥掉 tools/delegate_tool.py:132-133,136-137,512-523;暂停开关 :149-172;剥工具集 :672-680
pi-mono 只有扇出上限8和并发4,加上单任务输出截到50KB。没有深度上限、没有去重、没有限速,主循环也没有轮次上限。 实际存在一条无界递归路径 examples/extensions/subagent/index.ts:27-30;主循环 packages/agent/src/agent-loop.ts:170
pi-ai全都没有。没有工具调用循环,也就没有环可防
weclaw 只有两处去重:微信消息id去重5分钟,一次广播内的agent名去重。 没有深度、没有计数、没有限速。环在结构上不可能出现,因为agent没有回到处理器的通道 messaging/handler.go:273,63-64;名字去重 :246-253

06子agent的工具集怎么裁第二层

一共只有两种做法:从清单里摘掉,或者留在清单里等调用时再拒。差别在于模型看不看得见这个工具。

两种做法 摘掉的代价是工具定义变了,命不中提供商的提示缓存;留着再拒的代价是模型会先浪费一轮去调。 做法A · 从清单里摘掉 构造请求时就把工具过滤掉,模型压根看不见它 read write bash agent 模型的下一轮里不会出现这个名字,所以不会去调它。 openclaw · hermes-agent · pi-mono · codex-cli V2 做法B · 留在清单里,调用时拒 工具定义保持不变,判定挪到执行入口 read write bash agent 调用被拒,附理由 好处是工具定义逐字节不变,提示缓存还能命中。 Claude Code 的 fork 路径 实际用起来是三种组合 只做A 摘掉就完事。工具定义每层不一样, 缓存跟着分层。 openclaw · hermes · pi-mono · codex V2 A和B都做 摘掉,并且在执行入口再拒一次, 防的是定义过期的那一轮。 Claude Code · codex V1 · openclaw · opencode 我们:按剩余余额决定摘不摘 两个预算都花光才摘,只要还剩一个就全留着。 刻意是二值的,不按层数分档。 分档会让每层的工具定义都不一样,缓存全废。
做法摘掉哪些,条件是什么
我们A按余额,另有B兜底 唯一的判定是delegation_budget_left():消息预算没余额了, 才把agenttask_outputtask_stop移出清单。 每种派发都要交出一条消息,消息用完这三个工具做不成任何事。代数用完不摘工具,因为代数用完还能把活派给已存在的agent。 send_messagelist_agentsarchive_agentlist_tasks从不摘。 判定刻意是二值的,代码里写了理由:工具定义必须逐轮逐字节相同才命中提供商的提示缓存, 一层一套定义会把缓存打碎。越界的调用另有各自的拒绝文案 send_message/depth.py delegation_budget_left;挂点 agent/agent.py、task_output.py:37、task_stop.py:32; 清单过滤 functions/_helpers.py:141-147;拒绝文案 agent/agent.py 与 send_message.py
Claude CodeA和B都做,而且做法换过一次 泄露源码树里是无条件摘:Agent写死在"子agent一律不给"的集合里。 2.1.226 二进制改成按深度摘:过滤函数里那一行是"深度小于上限才留着Agent", 同时调用入口还会再拒一次,防的是工具定义过期的那一轮。 fork 路径刻意反过来:fork 出来的子拿到的是父的全套工具,包括Agent本身, 为的就是工具定义和父一模一样好命中缓存,递归只在调用时拒 无条件摘 src/constants/tools.ts:36-46,过滤 tools/AgentTool/agentToolUtils.ts:94-95; 按深度摘 二进制 @256834811;fork保全套 tools/AgentTool/forkSubagent.ts:44-71; fork拒绝文案 AgentTool.tsx:333 与 二进制 @258441552
codex-cliV1是A加B,V2只有A 构造工具清单时判一个开关,到深度就不给协作工具。V1的处理函数里还会再拒一次,V2只摘不拒。 角色之间的差异不是靠允许名单,而是给每个角色叠一层配置文件 core/src/tools/spec_plan.rs:561-572,1099;V1再拒 handlers/multi_agents/spawn.rs:67-71; 角色配置 core/src/agent/role.rs:37,249
openclawA加B 两张拒绝名单:所有子agent一律不给网关、agent列表、会话状态、定时任务、跨agent发消息这五个; 到了叶子层再加上子agent列表、会话列表、会话历史、派生这四个。 即便派生工具已经被摘掉,真到派生时还会再查一次深度 agents/pi-tools.policy.ts:42-51,57-62,170-175;再查一次 agents/subagent-spawn.ts:786
opencodeA和B按规则形状分 拒绝规则的模式是"*"就走摘掉:构造请求时把工具从schema里删; 带具体模式的拒绝规则就走留着再拒,调用时抛异常,异常文本把命中的规则原样贴给模型 摘掉 permission/index.ts:215-225 与 session/llm/request.ts:198-204; 再拒 permission/index.ts:86-90,文案 packages/core/src/v1/permission.ts:84-90
hermes-agent只做A,粒度是工具集 子的工具集等于它申请的和父拥有的交集,再剥掉派生、追问、记忆、代码执行四个工具集。 因为工具根本不在schema里,所以不需要拒绝文案。有一处缺口send_message写在"子绝不能有"的名单里,但那个名单只用来生成文档, 没有对应的工具集剥离,继承了消息工具集的子照样拿得到它 tools/delegate_tool.py:945-961,672-680;名单 :45-53;缺口对照 toolsets.py:190
pi-mono只做A 把agent声明的工具白名单转成命令行参数传给子进程,子进程按白名单过滤工具注册表, 不在名单里的工具连可执行包装都不构造,系统提示也按存活下来的工具重建 core/agent-session.ts:2253-2342;白名单来源 core/sdk.ts:280-286;传参 examples/extensions/subagent/index.ts:290
pi-ai不适用没有工具执行器,工具定义只是传输格式的一部分
weclaw不适用 自己一个LLM工具都不定义,工具全在被它包装的那个CLI里,对它完全不透明 全仓只有一处 tool 字样,是展示用途 agent/acp_agent.go:139

07agent的身份和寻址,以及实现规模第二层

有没有名字,怎么找到一个已经存在的agent。分水岭是"agent是活的实体"还是"agent只是一份定义"。

有名字吗怎么找到已经存在的地址长什么样
我们 有。建分支时先给一个24字符的占位名,之后后台用LLM在第1、6、16、40轮改成更贴切的 list_agents,三个视图:本会话、全部会话、已归档。每条给出可直接粘的地址、轮数、大致字数和末尾预览 SID:HEAD、分支名、名字的唯一前缀。地址落在分支当前的末端,所以旧的head也是有效地址 list_agents.py:100-179;地址解析 send_message/addressing.py:77-156,200-208;改名 agent/dispatcher/titles.py:339-405
Claude Code 有。队友id是名字@团队名,可复现可读、不用查表就能算出来; 子agent的名字来自派生时的name参数,重名的话注册表自动加-2-3main是保留名 ListAgents(旧名ListPeers)。 列的是"你能发消息的对象":自己派生的子agent、本机别的会话、云端会话、连上远程控制时还有别的机器 名字 / main / * / uds:路径 / bridge:会话id / 裸agentId。 重名取最新,另有一个后缀用来消歧 utils/agentId.ts:36-57;注册表 二进制 @254742111,@254743413;ListAgents 提示 二进制 @250608059
codex-cli 有,两套。机器用的是路径式地址,根是/root; 人看的是从一份101个科学家名字的表里分配的昵称 list_agents,可以按路径前缀过滤。另外活着的子agent会被动注入进环境上下文,不查也看得见 路径式,接受绝对路径所以兄弟能寻兄弟;也接受裸ThreadId protocol/src/agent_path.rs:15-19,54-71,129-146;昵称表 core/src/agent/agent_names.txt;列表 core/src/agent/control.rs:383-395,361-381
openclaw 有。agent在配置文件里定义,不是markdown前言 agents_list,但这个工具在子agent那里被摘掉了 session key,形如agent:<id>:…:subagent:…;也接受标签加agentId sessions/session-key-utils.ts:108-126;agents-list-tool.ts:37;子agent禁用 pi-tools.policy.ts:45
opencode 有名字,但那是"agent类型"不是活实例 没有独立的列表工具。可用的agent名字被烘进task工具的描述里。 找不到"某个正在跑的agent",只能用之前拿到的id重连自己那个子session 裸名字字符串 tool/task.ts:46,116;名字烘进描述 tool/registry.ts:252-265;agent定义 agent/agent.ts:35-55
hermes-agent 委派出去的子agent匿名且一次性,用一段目标文本寻址 没有列表工具。内部有一个sa-序号-随机的id,只暴露给运维接口 没有。看板那条线不一样,那边用配置档名当卡片的负责人 参数表 tools/delegate_tool.py:2675-2751;内部id :920-922;看板 hermes_cli/kanban_db.py:5674-5679
pi-mono 有,但那是磁盘上的markdown定义,不是活进程 找不到。每次调用重新扫目录,而且子进程带着"不留会话"启动,跑完什么都不剩 名字字符串,线性扫描匹配。没有列表工具,模型只能从报错信息里知道有哪些名字 examples/extensions/subagent/agents.ts:52-56,97-116;启动参数 index.ts:288;解析 index.ts:272-286
pi-ai没有没有没有
weclaw 有,配置里的键名,另有内置和自定义别名 没有列表工具。/info只报当前那一个,而且那是给人用的命令不是agent能调的工具 @名字/名字 config/config.go:16-33;解析 messaging/handler.go:206-265;别名 :169,187
这块大概多少代码 各家的量法不同(有的按目录、有的按文件行段),只看数量级。均不含测试。 Claude Code 24,385 行 泄露源码树,AgentTool + SendMessageTool + Task*Tool + swarm + tasks + coordinator openclaw 17,240 行 73 个非测试 ts 文件 codex-cli 7,059 行 多 agent 核心,另有 3676 行支撑代码、13114 行测试 我们 4,523 行 协作工具 1991 + task 包 1759 + inbox 343 + sub_agent_run 430 hermes-agent 2,900 行 委派子系统,另有约 1600 行的 Kanban 多 agent 那条线 pi-mono 1,310 行 示例扩展整目录,其中真正的机制约 190 行 weclaw 1,050 行 agent 相关全部,多 agent 路由核心约 150 行 opencode 415 行 委派专属,另有约 365 行复用的后台任务设施 pi-ai 0 行 没有这块代码
规模这条要小心读。各家的边界不一样:openclaw 那17240行里包含界面命令和注册表, Claude Code 那24385行来自泄露源码树而真正在跑的是二进制,codex-cli 的7059行不含它那13114行测试。 真正稳的结论只有一条:这块功能的合理体量在几千行,超过一万行的两家都把队友、跨机器、通告队列这类东西一起算了进去。

08我们计划怎么做第三层

四个域各管一个名词,词不重叠。整套工具面建在一个原语上:往一条分支投递内容,触发它跑一轮,结果自动回送。 工具面本身在另一页展开,这里只说它和上面八家的关系。

计划

todo

写下来的计划

手写的清单。改它不会让任何agent运行。

todo_createtodo_updatetodo_list
执行

task

正在运行的任务

一个task_id、一个状态、一份必回的结果。可取消,取消级联。

list_taskstask_outputtask_stop
实体

agent

执行任务的实体

一个agent就是会话图里的一条分支。新建、给已有的派活、列出、归档。

agentlist_agentsarchive_agent
通讯

message

发消息,和读历史

发一条消息,不产生任务记录;或者读任意分支的全文,不触发对方运行。

send_messageread_conversation
我们的选择取自谁为什么这么定
工具名字对齐 Claude Code
agent list_agents send_message task_output task_stop
Claude Code 认识那批名字的模型就已经认识这批工具。task_output连参数形状都照抄
计划清单改叫todo_*刻意跟 Claude Code 不同 那边的TaskList是计划板不是运行中任务的视图。改前缀之后list_tasks保住了字面意思:正在跑的任务
地址是一条分支,不是一个进程
SID:HEAD或分支名
和 codex-cli 的路径式地址、openclaw 的 session key 同类 agent的对话本身就是会话图里的分支,(会话id, 头节点)已经唯一定位它,不需要再造一个agent实体表
任何agent能给任何agent发消息,不做归属检查 比 openclaw 松,和 codex-cli V2、Claude Code 一档 发消息不产生任务记录、不能取消、不会级联,最坏结果是对方不回。openclaw 给子agent关掉这个工具,我们不关
派活和发消息分成两件事
agent(to=)产生任务,send_message不产生
八家里没有第二家这么分 结果必回、可取消、取消级联这三条承诺绑在任务上,所以任务要查归属;消息不带这三条,所以谁都能发
防环用一条链的消息预算
深度1、消息8,回流也计数;两个计数器分开
消息计数这一手只有 openclaw 和我们做 光有深度上限拦不住两个已存在的agent互相派活。codex-cli V2恰好就缺这一条,兄弟能直连又没有计数。 8锚在openclaw上:它的agent之间来回循环默认5次交替回复、最多20次, 我们的消息计数还要为派生和派活买单,所以落在两者之间。 回送这一跳重新绑定已完成task的计数而不是加一,一个来回花1,8条够走八个来回。 代数是另一个计数器,只有创建agent才涨,回送时退回派发方的计数: 读结果不算创建,协调者读完一批结果还能再派一批
深度不跟Claude Code的3
五家都是1,我们也是1
和openclaw、codex-cli V1、hermes、opencode一档 Claude Code的3不能照搬:泄露源码树里没有深度计数器,Agent在每个子agent的 工具池里被摘掉,除非USER_TYPE=ant,所以外部用户在那边的实际深度就是1; 它的异步工具白名单里也没有Agent,后台子agent无论计数器写几都不能再派生。 深度3只作用于同步嵌套,那条路上父的工具调用一直阻塞到孩子跑完、人还在看着。 我们的无人值守路径是run_in_background=True,在那条路上他们用的正是1 泄露源码树 src/constants/tools.ts:36-46 与 :55-71; 过滤 tools/AgentTool/agentToolUtils.ts:94-113
补了一条一轮之内的扇出上限
agent.max_spawn_fanout默认8
真正的扇出上限只有openclaw有 派生把计数交给孩子、自己那份不动,所以两个链上计数器都数不到兄弟:在这条上限之前, 一轮可以一直调agent直到50次迭代上限把它停下。 openclaw是每个父最多5个活着的孩子(可配1到20),单位可以照搬; hermes的3和pi-mono的8校验的是一次调用里那个批量参数的长度,不能照搬, 因为agent一次只创建一个孩子。8是我们四个worker的两倍宽度, 一轮填满池子、后面再排一波,第9次被拒并被指回它已经有的那8个
不加插话限速,不加子任务硬超时 openclaw有2秒限速,hermes有600秒超时 2秒限速守的是openclaw的steer通道:那条路中止孩子正在跑的一轮、清空队列、再重启, 所以两次steer挨太近会互相中止;它那条不中断的send通道完全没有限速。 我们的send_message属于不中断的那种,目标忙就进收件箱,没有可打断的东西。 hermes的600秒是调用方一侧的Future.result(timeout=…),不是杀: 到点只设一个协作式中断标志然后放弃线程,卡在阻塞I/O的孩子照样跑。 这两半我们都有且更强,task_output(timeout=)是同样的等待、 task_stop还会杀运行时并在30秒后强制置终态
裁工具按剩余余额,而且刻意是二值 其他家要么无条件摘,要么按深度分档 按深度分档会让每一层的工具定义都不一样,提供商的提示缓存全部作废。 Claude Code 的 fork 路径出于同样的理由反过来选了"全给,调用时再拒"
多三个工具
list_tasks archive_agent read_conversation
Claude Code 没有对应物 模型要能自己查后台在跑什么;agent不归档,agent列表就一直堆积早做完的worker; 读别人的历史要有可读文字稿而不是原始会话文件
设计已定、代码还没跟上的三条,对着代码逐条核过: 结果超长时截断头尾并把全文落文件(agent-collaboration.md §5.6),在回流路径上还没有实现; 内部分支不进会话选择器的可见性标记(§5.9),分支元数据里目前只有归档相关字段; 会话级取消清空收件箱(§5.3)目前只挂在网页入口上,命令行按停的路径不会清。
一句话收尾:生子agent是共识,进程内加一个很低的深度上限;直接通信是分水岭,只有四家做; 防死循环是最参差的一条,从八种手段到一种都没有。我们在防环上比多数家密, 在裁工具上是八家里唯一按剩余余额决定摘不摘的,而且刻意做成二值,理由是保住提示缓存。 这一页的数字反过来校准了我们自己的:深度和消息保持1和8,补上一条一轮之内的扇出上限8, 插话限速和子任务硬超时查清楚各自防的是什么之后没有采用。理由逐条写在常量旁边。