🐎 第3章 授人以渔:Skills 工程实践
📄 一句话概括

拿四条老原则过一遍六个技能:单一职责、开闭、接口隔离、依赖倒置。六个里合格的只有三个。 四条原则指向同一个动作:把「什么时候用」「做什么」「具体条目」三件事分开写。

📝 本章和前面几章的关系

这一节是第 3 章的收尾,本章小结并在本篇末尾。 软件工程那几条原则不是给技能发明的, 但技能的形态恰好把它们照出来了——因为一个技能就是一个小模块,模块的毛病和函数、类是一回事。

一、单一职责:一个技能一件事

最直接的毛病是一个技能覆盖几个不相关的任务。演示里六个技能,职责数为 1 的只有三个。 monthly-reconcile 管对账加汇总两件事,偏高;dev-helper 审查、提交、解释、部署全干,该拆; all-in-one 什么都能干,五个职责。 职责多的技能有两个坏处:触发时要把无关的段落一起读进来,而且描述必须写得很大才能覆盖所有场景。

一写大,就变成前面说过的「谁都不敢用」——描述越宽,越容易被误判成过触发。 所以单一职责在技能里不只是「好不好维护」,它直接影响触发准确率和触发成本。

二、开闭:加场景不改流程

一个技能要支持新场景就必须改正文,说明它的结构是硬编码的。 好的做法是把场景差异放进 description 和 refs:加一个新说法只改描述,正文那段流程一个字不动。 演示里列了三处改动的影响面:改正文影响主流程、最容易改坏;改描述只影响「什么时候用」; 加 refs 只影响「具体怎么做」。

所以一个成熟技能的正文,改版频率应该远低于描述。 如果反过来——正文天天改而描述一直没动——多半是把场景判断写进流程里了。 这是最难改的一种结构,因为每次加场景都要动主流程。

三、接口隔离:工具只给必需的

allowed-tools 就是接口。给多了是风险,给少了干不成。 演示里六个技能的工具数:三个「够用」(一到三个),两个是零——没写,等于没约束。 要注意「没有配置」和「配置得很宽」是两种不同的风险:前者靠全局权限兜底,后者把范围放到了全局之上。 all-in-one 那种什么都不配的,最容易出事。

接口隔离的原则在代码里是「不该让调用方依赖它不需要的方法」, 在技能里就是「不该让这个技能拥有它不需要的能力」。 差别在于:代码里多一个接口只是耦合,技能里多一个工具是权限。

四、依赖倒置:正文依赖抽象

全内联的技能,正文里塞满了具体条目,改一条就要动正文。 有引用的技能,条目在 refs 里,正文只负责「什么时候去读哪一份」。 这就是依赖倒置:正文依赖的是「有一份清单」这个抽象,不是清单里的每一条。

演示里三个技能有外部引用(一到两个),三个全内联。全内联的那三个,正文里装的都是具体条目。 依赖倒置的收益在变化的时候最明显:清单更新了,正文不动; 反之每次更新都要改正文,改正文就要重新测触发。

五、四条合成一句

把四条并排看,它们其实指向同一个动作:把「什么时候用」「做什么」「具体条目」三件事分开写。 「什么时候用」进 description——它常驻、要短、要能判定。 「做什么」进正文——它是流程,是判据,是这次执行的骨架。

「具体条目」进 refs——它大、它变、它只在需要时读。 分成三处之后,四条原则各自有了落脚点:单一职责看正文的职责数,开闭看改场景时动哪一处, 接口隔离看 allowed-tools 的长度,依赖倒置看正文里有多少具体条目。 混在一份正文里,四条全都无从谈起——因为你连「哪部分算职责、哪部分算条目」都分不清。

六、本章小结

第 3 章讲技能。十二节收在一句话上:技能是「用到才读」的知识,它的成败在描述,它的成本在常驻。 3.1 划出分界线:常用而短的进 CLAUDE.md,偶尔用而长的进技能。 3.2 拆结构:一个目录、一份 SKILL.md、一段 YAML 头、四条硬规则——不满足就静默消失。

3.3 算三级披露:元数据按个数付、正文按触发付、refs 按步骤付。 3.4 讲触发:模型读描述做判断,描述要写三段,第三段排除条件最值钱。 3.5 讲正文:它是路由器,不是仓库;用不上的内容指个路就行。

3.6 讲权限:allowed-tools 是第一道筛子,不是安全边界。 3.7 到 3.9 是加法:参数与预取、四层作用域、三类实战形状。 3.10 到 3.12 是抽象:四种设计模式、测试迭代的办法、四条工程原则。

下一章换一层:技能管的是「一个能力的执行细节」,子智能体管的是「一段工作的上下文」。 两者的分界线是「要不要独立的上下文窗口」。

七、动手 Demo:把六个技能按软件工程那几条原则过一遍

六个技能,四个指标:职责数(覆盖几个不相关任务)、工具数、外部引用数、是否只做一件事。 逐条过四条原则,看哪几个合格、哪几个该拆。

SOLID = [
    (u"code-review", 1, u"只审不改", 3, 2),
    (u"git-commit", 1, u"只管提交", 2, 0),
    (u"monthly-reconcile", 2, u"对账加汇总两件事", 1, 2),
    (u"dev-helper", 4, u"审查、提交、解释、部署全干", 0, 0),
]
# 单一职责看 duty,接口隔离看 tools,依赖倒置看 refs

实跑输出:

--------------------------------------------------------------------
把六个技能按软件工程那几条原则过一遍
--------------------------------------------------------------------


  ① 单一职责:一个技能覆盖几个不相关的任务
  --------------------------------------------------------------------
| 技能 | 覆盖了什么 | 职责数 | 结论 |
|---|---|---|---|
| code-review | 只审不改 | 1 | 合格 |
| git-commit | 只管提交 | 1 | 合格 |
| monthly-reconcile | 对账加汇总两件事 | 2 | 偏高 |
| dev-helper | 审查、提交、解释、部署全干 | 4 | 该拆 |
| doc-writer | 只写文档 | 1 | 合格 |
| all-in-one | 什么都能干 | 5 | 该拆 |

  六个里合格的只有三个。剩下三个的职责数都超过一,其中 dev-helper 一个顶四个。
  ! 职责多的技能有两个坏处:触发时要把无关的段落一起读进来,
  而且描述必须写得很大才能覆盖所有场景——一写大,就变成前面说过的「谁都不敢用」。

  ② 开闭:加一个新场景,要不要改正文
  --------------------------------------------------------------------
  一个技能如果要支持新场景就必须改正文,说明它的结构是「硬编码」的。
  好的做法是把场景差异放进 description 和 refs:加一个新说法只改描述,正文那段流程一个字不动。
| 加场景时动哪里 | 影响面 |
|---|---|
| 改正文 | 每次加场景都要动主流程,容易改坏 |
| 改描述 | 只影响「什么时候用」,不动「怎么做」 |
| 加 refs 文件 | 只影响「具体怎么做」,不动入口 |

  所以一个成熟技能的正文,改版频率应该远低于描述。
  如果反过来,正文天天改而描述一直没动,多半是把场景判断写进流程里了。

  ③ 接口隔离:allowed-tools 给多了没有
  --------------------------------------------------------------------
| 技能 | 工具数 | 评 |
|---|---:|---|
| code-review | 3 | 够用 |
| git-commit | 2 | 够用 |
| monthly-reconcile | 1 | 够用 |
| dev-helper | 0 | 没约束 |
| doc-writer | 2 | 够用 |
| all-in-one | 0 | 没约束 |

  「没有配置」和「配置得很宽」是两种不同的风险:前者靠全局权限兜底,后者把范围放到了全局之上。
  all-in-one 那种什么都不配的,最容易出事。

  ④ 依赖倒置:正文是内联还是引用
  --------------------------------------------------------------------
| 技能 | 外部引用 | 评 |
|---|---:|---|
| code-review | 2 | 有引用 |
| git-commit | 0 | 全内联 |
| monthly-reconcile | 2 | 有引用 |
| dev-helper | 0 | 全内联 |
| doc-writer | 1 | 有引用 |
| all-in-one | 0 | 全内联 |

  全内联的技能,正文里塞满了具体条目,改一条就要动正文。
  有引用的技能,条目在 refs 里,正文只负责「什么时候去读哪一份」。

  ⑤ 四条原则合起来看
  --------------------------------------------------------------------
| 原则 | 在技能里是什么意思 | 怎么看出来 |
|---|---|---|
| 单一职责 | 一个技能一件事 | 职责数等于 1 |
| 开闭 | 加场景不改流程 | 改描述就能扩场景 |
| 接口隔离 | 工具只给必需的 | allowed-tools 短而具体 |
| 依赖倒置 | 正文依赖抽象 | 条目放 refs,正文只指路 |

  ! 四条其实指向同一个动作:把「什么时候用」「做什么」「具体条目」三件事分开写。
  分成三处之后,每一条原则都有了落脚点;混在一份正文里,四条全都无从谈起。

①单一职责:六个里合格的只有三个,剩下三个职责数都超过一,其中 dev-helper 一个顶四个、 all-in-one 顶五个。 职责多的两个坏处:触发时把无关段落一起读进来;描述必须写大才能覆盖所有场景——一写大就变成过触发。 ②开闭:改正文影响主流程、最容易改坏;改描述只影响「什么时候用」;加 refs 只影响「具体怎么做」。 所以成熟技能的正文改版频率应该远低于描述。 反过来——正文天天改而描述没动——多半是把场景判断写进流程里了。 ③接口隔离:四个技能工具数够用(一到三个),两个是零。 「没有配置」和「配置得很宽」是两种风险,前者靠全局权限兜底,后者把范围放到全局之上。 all-in-one 那种什么都不配的,最容易出事——它连「我只需要读文件」这句话都没说。 ④依赖倒置:三个有引用、三个全内联。 全内联的正文里塞满具体条目,改一条就要动正文,动正文就要重测触发。 有引用的正文只负责「什么时候去读哪一份」,依赖的是「有一份清单」这个抽象。 ⑤四条合起来看,都指向同一个动作:把「什么时候用」「做什么」「具体条目」分三处写。 分开之后每条原则都有落脚点,混在一起四条全都无从谈起。

30 秒实验

  1. 把 dev-helper 拆成两个技能(审查加解释、提交加部署),重跑第 ① 步。职责数和工具数各变成多少? 拆完之后描述能写短吗?
  2. 给两个工具数为零的技能各补一份最小权限清单,重跑第 ③ 步。这一步除了降风险,还能带来什么好处?
  3. 把 code-review 的两处外部引用改成内联,重跑第 ④ 步。正文 token 涨多少? 清单以后要改的时候,代价是什么?

常见误区

  1. 把一个技能写出四五个职责,理由是「反正都是代码相关的事」。 职责一多,描述必须写大,过触发就来了。
  2. 加新场景就改正文。这说明结构是硬编码的,改一次就要重测一次触发。
  3. allowed-tools 干脆不写。不写等于继承全局权限,比写宽了更危险。
  4. 把具体条目全内联进正文,理由是「省得再去读一份文件」。改一条就要动正文,动正文就要重测。
  5. 把四条原则当成评审打分表,逐条挑毛病。它们是同一个动作的四个侧面,核心只有一句:三件事分开写。
自测三问(点开对答案) 1. 技能职责多,为什么会连带影响触发准确率? —— 因为描述必须写得足够大才能覆盖所有场景,一写大就谁都像、谁都不像,变成过触发。 所以职责数不只是维护性问题。 2. 「正文改版频率应该远低于描述」说明了什么? —— 说明场景差异被放进了描述,正文那条流程是稳的。反过来就是硬编码:每次加场景都要动主流程。 3. 四条原则合成的那一句话是什么? —— 把「什么时候用」「做什么」「具体条目」三件事分开写:描述、正文、refs 各管一处。

第 3 章到此收尾。下一章讲子智能体:技能管「一个能力怎么做」,它管「一段工作用什么上下文」。

相关笔记