子 Agent 的运行:委派、并发与回收
它派了一个任务出去,工具调用立刻返回:已启动。
子 Agent 的运行:委派、并发与回收
它派了一个任务出去,工具调用立刻返回:已启动。
界面上那个工具块停在那里,没有结果。十分钟后,父会话里突然多出一条消息,说任务完成了,附了一份结论。
这中间发生了什么,用户看不到。
拆的理由前面讲过——一个窗口装不下。这篇讲拆出去之后的部分:谁在跑、跑多久、什么时候回来。
一、委派默认是异步的
最直觉的委派是同步的:调用工具,等子任务跑完,拿到结果,继续。
问题是时间。一个子任务可能跑五分钟、二十分钟。同步等待意味着主循环整个卡住,用户既不能继续对话,也不能取消,只能看着一个转圈的块。
所以默认走异步:工具调用立刻返回”已派发”,子会话在后台跑,跑完把结果作为一条回调消息注入父会话。
同步模式通常也保留一个开关(wait=true),用在”下一步必须依赖这个结果”的场景。但默认值选异步,是因为长任务不应该阻塞交互。
这也带来一个副作用:父会话在子任务运行期间可以继续干活,甚至再派几个。于是就必须管并发。
二、子会话里有什么
一个子 Agent 不是”同一个模型换个提示词”,它是一次完整的会话:
- 独立的对话历史,从零开始;
- 独立的工具集(通常和父会话一致);
- 独立的轮次上限;
- 独立的模型绑定,可以指定某个组,也可以跟着父会话走;
- 自己的系统提示,加上用户在花名册里写的指令。
轮次上限的差距值得注意:主会话是 500 轮,子会话通常只有 25 轮左右。
不是抠门,是任务性质不同。主会话要陪着用户来回几十轮,子任务应该是一个边界清楚的活——如果需要 25 轮以上,多半是任务描述有问题,或者它一直在试错。
上限的作用不是省资源,是暴露问题。
三、轮次快用完时,不是硬砍
直接在第 25 轮切断,会得到一个半成品:工具调了一堆,结论一个字没有。
所以实现上会在接近上限时改变策略:撤掉工具,要求它用剩下的轮次把交付物写出来。
系统提示里写得很直接:
你最多有 N 轮工具调用;到最后一轮,工具会被收回,你被要求用已经掌握的信息写出交付物,所以在用完之前就要开始写。
这个设计承认了一个事实:子 Agent 最常见的失败不是”做错了”,是”做不完”。与其让它跑到断头台,不如提前告诉它终点在哪,让它自己分配预算。
和它配套的还有一句:如果任务确实超出了当前能力,在最后一条消息里写 [ESCALATE] 原因。
四、子 Agent 可以承认自己不行
[ESCALATE] 这个标记是整个设计里最有意思的一处。
它给了子 Agent 一个主动上报的通道:我做不到,原因是什么。父会话收到后,可以决定换一个更强的模型重跑,或者改变任务描述。
这和”失败”不是一回事。失败是报错、超时、崩溃;上报是”我完成了我能做的部分,剩下的超出我的层级”。
如果没有这个通道,子 Agent 只有两个选择:硬着头皮编一个看起来完整的答案,或者直接失败。前者更糟——父会话会把它当成真的结果继续用。
让模型说”我不会”,比让它编一个答案便宜得多。
五、并发要限,排队要有边界
一个父会话最多同时跑三个子任务,超出的进队列,队列最多十个。
这两个数字都是硬的,而且拒绝的时候要说清楚:不是”稍后重试”,是”没有排队”。
排队还有一个细节:队列里存的是原始工具参数,不是预先构造好的任务对象。原因是排队期间用户可能改了花名册——换了模型、改了指令、删了这个子 Agent。存参数,真正启动时重新解析,改动才能生效;存对象,等于把入队那一刻的配置冻结了。
这里的原则是:延迟执行的东西,不要在入队时求值。
六、状态必须可见
子任务跑二十分钟,界面上如果只有一个”运行中”,用户无法判断它是正常干活还是卡死了。
所以要有进度:调用了哪些工具、跑到第几轮、消耗多少 token、最新写出的一段话。
这些信息不需要实时推送每一个细节,但至少要能回答三个问题:它在干什么、干了多久、有没有产出。
有个常见的错误是把子 Agent 做成黑盒:父会话只知道”开始了”和”结束了”。中间二十分钟完全是空白,出问题时连从哪查都不知道。
七、取消要级联
用户点停止,停的应该不只是父会话的循环。
还在跑的子任务如果不一起停,会留下一堆孤儿:占用并发配额、继续烧 token、完成后还要往一个已经结束的会话里注入回调。
所以取消必须沿着委派关系往下传。实现上通常给每个子任务一个 parent 引用,取消父任务时按引用递归取消。
同样的问题也出现在 App 被杀之后:重启时发现有些子任务的状态停在”运行中”,但进程早就没了。恢复逻辑要能识别这种残留,要么标记失败,要么重新入队。
八、回收
子任务结束后,它的会话不应该继续存在。
完成的,把结论写回父会话的工具块,子会话销毁;失败的,保留摘要(调用列表、错误、最后一段输出),供父会话判断要不要重试。
这里有个成本上的考虑:子 Agent 的 token 消耗算在谁头上。多数实现会把它归到父会话的账单里,因为用户看到的是一个任务,不是两个。
小结
子 Agent 的价值在隔离,成本在管理。
隔离解决的是”一个窗口装不下”,管理要解决的是”多个窗口同时开着”:谁先跑、跑多久、什么时候停、失败怎么办、取消传到哪。
前者是设计,后者是运维。很多 Agent 把前者做得很漂亮,后者一团糟——结果就是用户看到一堆”运行中”,既不敢关,也不知道什么时候能好。