DeepAgents(十):委派用 isolated 还是 fork

2026-10-14 04:350 条评论168 次阅读

委派用 isolated 还是 fork

《task 工具怎么把活派给子 Agent》把那件工具的脸看清楚了,还剩一个更实际的问题:装配子 Agent 的时候,mode 那一格填什么。

不填就是 isolated,另一个选项叫 fork。仓库里对 fork 的说明写得很客气,说它继承你的完整对话和系统提示词。

我拿同一条父对话、同一条任务描述,派给 isolated 和 fork 各走一遍,把开局拿到的东西摊开对照。

isolated 和 fork 的差别落在子 Agent 开局那一眼:isolated 子只拿到你写的那句任务描述,fork 子会先把你这边的对话从头读一遍。选哪个,看你那句描述写不写得清楚,也看你愿不愿意付这份入场费。

全程不用 API key,读完约 6 分钟。

一、装配的时候,清单上就多一行尾巴

一个父 Agent 能同时挂好几个子 Agent,各派各的活。它们的规格写起来只差一个字段:

subagents=[
  {"name": "iso1",
   "description": "独立干活的"},
  {"name": "fk1",
   "description": "接对话的",
   "mode": "fork"},
]

mode 写 fork 的那一个,会去改写 task 工具的描述。父 Agent 派活之前读的那份清单,尾巴上会多出 86 个字符,大意是「它继承你的完整对话和系统提示词,这里不用重述背景」。

这一句不是装饰。仓库源码的注释里管它叫承重结构:少了它,「每次调用都是无状态的」就是模型能看到的唯一信号,模型可能干脆不肯把活派给 fork 子。

清单之外,两边手上的工具也不一样:

isolated 子 fork 子
清单行尾巴 就是你写的那句 多 86 字符一句注
手上工具 7 个,没有 task 8 个,task 在
装同一条任务时描述长度 1443 字符 1525 字符

少的那一个工具就是 task。这件事在《子 Agent 为什么不能继续往下派》里还会展开,这里先记一笔:isolated 子想接着派人,手上根本没这个工具。

二、开局那一眼,两边差一个数量级

现在是正题。我让父先干 2 步活,再把同一句任务描述派出去,看子 Agent 第一次被调用时,模型手上有什么。

任务描述只写了一句:「把刚才那个订单号报给我」。

isolated 子的开局是 2 条消息:一条它自己的提示词,一条任务描述。合计 12 token。

fork 子的开局是 9 条消息:父的整段对话一条不落,末尾才接上那条任务描述。合计 326 token。

isolated 子 fork 子
开局消息条数 2 条 9 条
输入合计 12 token 326 token
里面装的是 提示词 + 任务描述 父的整段对话 + 任务描述

同一条任务描述派出去

isolated 子:12 token

fork 子:326 token

开局只有那句话

先读一遍父的对话

父这边付出的代价倒是完全一样。发起调用的那条 AI 消息,两边都是 36 token,因为派出去的参数一模一样。

也就是说,从父的角度看不出任何区别,差别全在子那边热身的成本上。这笔成本具体多少,第四节算。

三、提示词是拼出来的,不是替换

第 2 条消息那个位置,fork 子拿到的是什么,值得单独看一眼。

我拿父的提示词和子自己写的提示词做了 4 组组合,结果是这样:

父的提示词 子自己写的 fork 子拿到 isolated 子拿到
有 20 字符 没写 20 字符(父那句) 空
有 20 字符 有 11 字符 33 字符(拼在一起) 11 字符
没写 有 11 字符 11 字符 11 字符
没写 没写 空 空

fork 子的提示词是父那一份加一个空行、再接上自己那一份,33 就是 20 加 2 加 11。它没有替换掉父的提示词,两句话同时在场。

isolated 子那边完全没有继承这回事,父写什么它都看不到。

这里有个容易踩空的地方。默认那个叫 general-purpose 的子 Agent,自带 286 个字符的提示词,主题是「调用方只看得到你的最终回复,请把完整答案放进去」。很多人会把这句当成「子 Agent 都有的待遇」。

它不是。自己声明一个子 Agent 又不写 system_prompt,这段提示词就没了,子 Agent 的最终回复很可能是半截。这个坑跟选 isolated 还是 fork 无关,但既然是自己声明子 Agent,顺手写上更稳。

四、入场费:父干得越多,fork 读得越多

把父干的活从 1 步加到 8 步,两边开局的输入长这样:

父干了 isolated 子 fork 子 差额
1 步 12 token 217 token 205 token
4 步 12 token 388 token 376 token
8 步 12 token 616 token 604 token

isolated 子那一列纹丝不动,12 就是那句任务描述加一份空提示词的价钱。父干多少活,它都不知道,也不用付钱。

fork 子那一列每多 1 步就涨一点,平均一步 57 token 上下。父干过的活它全要读一遍,读多少付多少。

所以「fork 省了重述的功夫」这句话有个价钱。父这边的对话越长,这笔入场费越贵,而且它跟子 Agent 真正要干的那件事没有关系。

五、任务描述该写多细

入场费买到了什么,用一件事就看得出来。

父那边把「订单号是 A-7731」写进了一份工作文件。派出去的任务描述故意只写「把上面那个订单号报给我」,没带号。

isolated 子的开局上下文里,A-7731 出现 0 次。它手上只有那一句描述,父说过什么它一概不知,只能猜或者回头问。

fork 子的开局上下文里,A-7731 出现 1 次,就在父那次写文件的参数里。它不用你重述,自己翻得到。

任务描述省掉的那句话

isolated 子:捞不到

fork 子:翻得到

一句话任务描述能不能自己站住,直接决定了这个子 Agent 该用哪种模式。写不清楚的活,用 fork;写得清楚的活,用 isolated 省 token。

顺序别搞反。挑模式这件事发生在写描述之前,而描述该写多细,取决于你挑了哪一种。

六、挡什么、塞什么、压缩掉什么

状态通道上的取舍,两边也不一样。

isolated 子那边挡掉 4 个键:对话历史、待办清单、结构化输出、还有一个内部标记。fork 子那边只挡 3 个,摘要相关的通道占了 2 个。

也就是说,待办清单在 fork 子那边是不挡的,它能看到父手上的清单。messages 这个键两边都要特殊处理,isolated 子是整段换掉,fork 子是拿父的重新拼。

fork 子还会被多塞一个键进去,一个布尔标记。它的作用是给递归委派留路标:子在内部要是想再派人,靠这个标记认出自己是子,然后被拒。isolated 子没有这个标记,因为它手上连工具都没有。

回传这边,两边一模一样。派一次活,父收到的就是一条回执,内容都是子 Agent 最后那句有正文的话。子中间看了什么、跑了几步、报了哪些错,一条都不进父的上下文。

子写的文件倒是都回传,两边我都拿到了。落盘是共用的,这一点跟模式无关。

最后一件容易忽略的事:fork 子继承的是「有效历史」。父要是压缩过上下文,子拿到的就是摘要加剩下的部分,父那边被压掉的原文它同样看不到。

好处是它不会接到一段被砍断的对话,代价是它看不到的部分跟父一样多。这个行为在源码里由摘要事件驱动,事件本身就 3 个字段:切在第几条、摘要那条消息、原文卸载去了哪。

七、怎么选

把上面的账合成一张表:

isolated 子 fork 子
开局消息 2 条 9 条(跟着父涨)
继承父的提示词 不继承 继承
能看到父的来龙去脉 不能 能
能自己继续派活 手上没这个工具 有,但一调就被拒
父这边多出的痕迹 一条回执 一条回执

用一句话能把这件活说清楚的,用 isolated,开局 12 token,子 Agent 干净得像新拆的。说不清楚、必须让它自己翻前文的,用 fork,入场费按父的对话长度计价。

拿不定主意的时候,我习惯先用 isolated。等发现子 Agent 老是回头问同一个背景,说明该交给 fork 了。

isolated 让子 Agent 从一张白纸开始,fork 让它接着你的上下文往下走。mode 那一格填什么,说到底是对「这件活能不能用一句话说清」的一次表态。