同一个问题问八个参考实现:一个agent怎么把活交给另一个agent,交出去以后还能不能管。
八个维度逐条对照,每条都覆盖全部八家,没有这个功能的如实标"没有"。
我们自己的机制在Agent协作:工具面那页,
设计正文是agent-collaboration.md。
这页装第二层和第三层。第一层是我们已经跑起来的机制,它有自己的一页,两页互相链接。
二进制 @250599082这种形式。
两边不一致的地方在下面逐条点出来。
八个维度一屏看完。每格都有下面的小节展开,含代码位置。
| 生子agent | 直接通信 | 任务追踪 | 级联取消 | 防环手段 | 裁工具 | 寻址 | 规模 | |
|---|---|---|---|---|---|---|---|---|
| 我们 | 进程内,深度1 | 能,任意对任意 | task_id,三个工具 | 级联 | 八种 | 按剩余余额摘 | 分支名 / SID:HEAD | 4.5k |
| Claude Code | 进程内,深度3 | 能,跨机器也能 | agentId,三个工具 | 级联 | 五种 | 按深度摘,再调用时拒 | 名字@团队名 | 24k |
| codex-cli | 进程内,V1深度1,V2无上限 | 能,兄弟直连 | ThreadId,四个工具 | 只有一半 | 两种 | 摘,V1额外再拒 | 路径式 /root/x | 7k |
| openclaw | 进程内或外部进程,深度1 | 能,但要开开关 | runId,只有list | 级联 | 八种 | 摘,spawn时再查一次 | session key | 17k |
| 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 |
进程内线程池,默认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 |
问的是有没有一条不经过父级的通道。有的家只有"父派活、子交活"这一条线,有的家兄弟能互相寻址。
| 能不能直连 | 怎么连的,怎么投递 | |
|---|---|---|
| 我们 | 能 | send_message(to=),任何agent对任何未归档的分支,跨会话同一条路径,不做归属检查。
目标正忙就进它会话的收件箱排队,本轮结束后消费
functions/tools/send_message/send_message/send_message.py:82,154;地址解析 addressing.py:77-156;收件箱 agent/inbox.py |
| Claude Code | 能,而且能跨机器 | SendMessage。to可以是队友名字、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_message和followup_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 |
四问:有没有任务id、能不能查状态、结果怎么回来、能不能取消。加上第五问:停掉父的会不会停掉子孙。
| 模型拿得到任务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_agents、get_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 | runId | subagents工具只有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 |
把各家用的手段摆成一张矩阵。空格表示这家确实没有这个手段,不是没查到。
| 关键常量和它在哪 | |
|---|---|
| 我们 | 深度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 |
一共只有两种做法:从清单里摘掉,或者留在清单里等调用时再拒。差别在于模型看不看得见这个工具。
| 做法 | 摘掉哪些,条件是什么 | |
|---|---|---|
| 我们 | A按余额,另有B兜底 | 唯一的判定是delegation_budget_left():消息预算没余额了,
才把agent、task_output、task_stop移出清单。
每种派发都要交出一条消息,消息用完这三个工具做不成任何事。代数用完不摘工具,因为代数用完还能把活派给已存在的agent。
send_message、list_agents、archive_agent、list_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 Code | A和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-cli | V1是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 |
| openclaw | A加B | 两张拒绝名单:所有子agent一律不给网关、agent列表、会话状态、定时任务、跨agent发消息这五个; 到了叶子层再加上子agent列表、会话列表、会话历史、派生这四个。 即便派生工具已经被摘掉,真到派生时还会再查一次深度 agents/pi-tools.policy.ts:42-51,57-62,170-175;再查一次 agents/subagent-spawn.ts:786 |
| opencode | A和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 |
有没有名字,怎么找到一个已经存在的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、-3,
main是保留名 |
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 |
四个域各管一个名词,词不重叠。整套工具面建在一个原语上:往一条分支投递内容,触发它跑一轮,结果自动回送。 工具面本身在另一页展开,这里只说它和上面八家的关系。
手写的清单。改它不会让任何agent运行。
一个task_id、一个状态、一份必回的结果。可取消,取消级联。
一个agent就是会话图里的一条分支。新建、给已有的派活、列出、归档。
发一条消息,不产生任务记录;或者读任意分支的全文,不触发对方运行。
| 我们的选择 | 取自谁 | 为什么这么定 |
|---|---|---|
工具名字对齐 Claude Codeagent 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)目前只挂在网页入口上,命令行按停的路径不会清。