🐎 第4章 分而治之:子智能体与任务委派的艺术
📄 一句话概括

子智能体的定义还是一次「Markdown 加 YAML 头」,字段只有四个:name、description、tools、model。 四份样本里两份能直接用。 问题都不是「写错了」,是「没写够」——少一个字段,行为就变成另一回事。

📝 本章和前面几章的关系

上一节讲为什么要有它,这一节讲怎么定义一个。 结构和技能几乎一样,差别全在「tools 比 allowed-tools 更要命」。 看完这一节,应该能写出一个不放开权限、也不至于干不成活的子智能体。

一、四个字段

name:内部标识,也是调用名。和技能一样,要和文件名一致,不然调用时找不到。 description:什么时候该派它出去。 注意它写的是场景,不是功能——「需要独立评审一段改动时」比「审查代码」有用。 tools:它能用哪些工具。这是最要紧的一个,因为不写等于继承全部。

model:用哪个模型。只读审查这种活可以用小模型,改代码、做设计的活得用大的。 四个字段之外还有可选的(版本、颜色、默认是否自动派),但行为和成本都由这四个决定。

二、tools 为什么比 allowed-tools 更要命

技能不写 allowed-tools,继承全局权限;子智能体不写 tools,也继承全局——看起来一样。 差别在「谁在跑」。技能是在主窗口里跑,人就在旁边看着,每一步都能拦。 子智能体是在另一个窗口里跑一长串动作,人看不到中间过程。 权限放开的代价,从「看见它干了什么」变成「事后才知道」。

所以技能里那条结论在这里要加重说一遍: 不写不是「没有权限」,是「全部权限」。而且这里连看都看不见。 一个实用的默认:只读型给 Read、Grep、Glob 三个就够;执行型再加 Edit 和指定的 Bash 子命令。

三、description 写场景不写功能

子智能体的 description 有两个读者:一个是模型(决定派不派),一个是人(决定用不用)。 写「审查代码」偏功能,模型得自己判断「这次算不算审查」; 写「需要独立评审一段改动、而且不想让评审过程占主窗口时」偏场景,判断依据就明确了。 场景描述顺带解决了「什么时候不该派」——如果这次评审只想快速看一眼, 那就不符合「不想占主窗口」这个条件。

这和 3.4 节技能描述那三条是同一个道理:做什么、什么时候用、什么时候不用。 只是子智能体的第三段常常被写成「什么时候不该派」——比如「改动小于三行时不要派」, 省得为一行改动开一个新窗口。

四、正文写什么

正文是它被派出去之后拿到的第一段话。比技能正文更短更硬:做什么、边界在哪、返回什么格式。 「返回什么格式」最容易被漏,也最值钱。不写清楚,它可能回一段散文,而散文回到主窗口还得再解析一遍。 「边界」也要写:能改哪些目录、能不能跑测试、遇到不确定的要不要停下来问。

注意「停下来问」这件事在子智能体这里更难:它在另一个窗口里,问谁? 所以更实际的做法是写「遇到 X 情况就返回 blocked,说明卡在哪」。 这句话把一个会卡住的活变成了一个能收口的活——这也是 4.8 节要讲的实战要点之一。

五、和技能定义的差别一览

结构一样:目录、Markdown、YAML 头。字段不一样:技能是 allowed-tools,子智能体是 tools。 读的人不一样:技能的 description 给模型判断「用不用」, 子智能体的 description 给模型判断「派不派」。 权限的后果不一样:技能放开的权限当场可见,子智能体放开的权限在一段看不见的过程里生效。

正文的落点不一样:技能正文是「执行的步骤」,子智能体正文是「交付的契约」。 记住这一条就够了:技能管怎么做,子智能体管在哪做、交什么。

六、动手 Demo:四份子智能体配置,三份不能直接用

造四份配置:一份规范的只读审查员、一份没写 tools 的改码员、一份 name 大小写不对的、 一份缺 description 的。 逐份解析 YAML 头、核四个字段,看哪份能直接用。

for fn in (u"reviewer", u"fixer", u"Reviewer2", u"reporter"):
    d = yaml(u"\n".join(SUB_RAW[fn]))
    if not d.get(u"name"):
        bad.append(u"缺 name")
    elif d[u"name"] != fn:            # 文件名才是调用名
        bad.append(u"name 与文件名不一致")
    if u"tools" not in d:
        bad.append(u"没写 tools(继承全部)")

实跑输出:

--------------------------------------------------------------------
四份子智能体配置,三份不能直接用
--------------------------------------------------------------------

  子智能体的定义也是「一份 Markdown 加一段 YAML 头」,和技能同一个套路。
  只是字段不同:这里管的是「它能用哪些工具、用哪个模型」。

  ① 四个字段各自管什么
  --------------------------------------------------------------------
| 字段 | 管什么 | 注意 |
|---|---|---|
| name | 内部标识,也是调用名 | 必须与文件名一致 |
| description | 什么时候该派它出去 | 必填,写场景不写功能 |
| tools | 能用哪些工具 | 不写就继承全部,最危险的一种写法 |
| model | 用哪个模型 | 只读审查可以用小模型,改代码用大的 |


  ② 逐份检查
  --------------------------------------------------------------------
| 文件名 | name | tools | 结论 |
|---|---|---|---|
| reviewer | reviewer | 有 | 通过 |
| fixer | fixer | 无 | 没写 tools(继承全部) |
| Reviewer2 | Reviewer2 | 有 | 通过 |
| reporter | reporter | 有 | 缺 description |

  四份里只有 2 份能直接用。
  另外 2 份的问题都不是「写错了」,是「没写够」——少一个字段,行为就变成另一回事。

  ③ 少写 tools 有多危险
  --------------------------------------------------------------------
  fixer 那份没写 tools。它叫「改代码的子智能体」,本意是只改 src 下的文件;
  但没写 tools 就意味着继承父会话的全部工具——包括删除、包括写系统路径。
  这和 3.6 节技能里那条结论是一样的:不写不是「没有权限」,是「全部权限」。
  所以子智能体的 tools 是最该先写的一个字段,比 description 还急——description 写歪了顶多派错,
  tools 没写是直接放开。

  ④ 正文写什么
  --------------------------------------------------------------------
  正文是它被派出去之后拿到的第一段话,比技能正文更短更硬:做什么、边界在哪、返回什么格式。
  最容易被漏的是「返回什么格式」。
  不写清楚,它可能回一段散文——而散文回到主窗口,主窗口还得解析一遍,
  等于把省下来的活又做回去了(4.7 节细算)。
  ! 一句话:头部写「怎么找到它、它能干什么」,正文写「这一次要它交什么」。
  头部是长期的,正文是每次委派时最该盯的。

①四个字段:name 是标识和调用名、description 管「什么时候派」、tools 管「能做哪些动作」、 model 管「用多大的模型」。 ②四份里两份能直接用。fixer 没写 tools,Reporter2 大小写不一致,reporter 缺 description。 注意 Reporter2 那个问题最隐蔽:它 name 和文件名是一致的(都叫 Reviewer2), 所以这一份其实「通过」了检查——真正该被挑出来的是它名字里的大写,不同平台对这个的容忍度不一样。 ③fixer 那份的问题最能说明这一节的落点:它叫「改代码的子智能体」,本意是只改 src 下的文件; 但没写 tools 就意味着继承父会话的全部工具。 这和 3.6 节技能那条结论一样:不写不是「没有权限」,是「全部权限」。 而且子智能体是在看不见的过程里用它。 ④正文里最该先写的是「返回什么格式」。 不写清楚它会回一段散文,而散文回到主窗口,主窗口还得解析一遍,等于把省下来的活又做回去了。 一句话总结头部和正文的分工:头部写「怎么找到它、它能干什么」,正文写「这一次要它交什么」。

30 秒实验

  1. 给 fixer 补一行 tools,只给 Read 和 Edit(src*),重跑第 ② 步。它还能干活吗? 少了哪个工具会让它卡住?
  2. 把 reviewer 的 description 改成「审查代码」,重跑一遍。它还能被正确判定为「该派」吗? 判断依据少了什么?
  3. 给 reporter 的正文加一句「返回 JSON:done/files/issues/blocked」, 这个改动影响的是它自己的行为,还是主窗口的成本?

常见误区

  1. 不写 tools 就发布。不写等于继承全部,而子智能体跑在看不见的过程里。
  2. description 写成功能清单(「能审查、能改、能测」)。 它要回答的是「什么时候派」,越全越没人能判断。
  3. 正文只写「做什么」,不写返回格式。回执变成散文,下游还得再解析一遍。
  4. 写「遇到不确定就问用户」。子智能体在另一个窗口里,问不到——该写「返回 blocked 并说明卡在哪」。
  5. 给子智能体配和主会话一样的模型。只读审查用小模型就够了,用大模型是把钱花在不需要的地方。
自测三问(点开对答案) 1. 子智能体的四个字段分别管什么? —— name 是标识和调用名、description 管什么时候派、tools 管能用哪些工具、model 管用多大的模型。 2. 为什么子智能体的 tools 比技能的 allowed-tools 更要紧? —— 因为子智能体跑在另一个窗口里,一长串动作人看不到中间过程。 权限放开的代价从「看得见」变成「事后才知道」。 3. 「遇到不确定就问用户」这句话为什么不能写进子智能体正文? —— 因为它在另一个窗口里,问不到人。 该写成「返回 blocked 并说明卡在哪」,把会卡住的活变成能收口的活。

定义会写了,下一节看五种常见的分工形状,各自适合什么活。

相关笔记