AI编程——Skill原理
Skill 是一个带 YAML frontmatter 的 Markdown 文件,靠 frontmatter 三字段决定”叫什么名、何时自动触发、是否只能手动调”,正文是工程纪律的步骤化沉淀——本质是用确定性指令驯化 LLM 的随机性。
简介
在 AI 编码 agent 的语境里,Skill 是一种把”工程纪律”固化成 agent 可读、可触发指令的机制。它不教模型写代码,而是给模型套上一层操作规程:什么时候该走 TDD、什么时候该先盘问对齐、审查 diff 时按哪几条轴走。
这套机制最知名的公开实践是 Matt Pocock 开源的 mattpocock/skills(MIT 协议),副标题”Skills for Real Engineers. Straight from my .agents directory.”——把他自己日常用的 .agents 目录抽出来开源。本文以它为范例讲清 skill 的原理与写法。
skill 的总目标是用确定性去驯化随机系统(原文:wrangle determinism out of a stochastic system)。LLM 是概率模型,同一个输入可能给不同输出;skill 用”触发判据+步骤化正文+反模式清单”把其中需要稳定的那部分钉死。
文件结构
每个 skill 是 <目录>/<名字>/SKILL.md,外加可选的同目录引用文件(如 GLOSSARY.md、tests.md)。SKILL.md 本质就是一个带 YAML frontmatter 的 Markdown:
1 |
|
frontmatter 三个字段决定了 skill 的一切行为:
| 字段 | 作用 |
|---|---|
name |
skill 标识符,也是 /命令 的名字(tdd → /tdd) |
description |
模型自动触发的判据——模型读这行判断”当前任务要不要自动加载这个 skill”。写得极讲究:前置触发词、每个分支一个触发条件、不重复正文已有信息 |
disable-model-invocation |
true = 只能用户手动 /xxx 调用;false 或省略 = 模型可自动触发 |
两条触发路径
- 用户触发(user-invoked):你敲
/grill-me,对应 skill 全文被注入上下文,agent 按里面的步骤执行。它是”编排器”,可以再调别的 model-invoked skill,但不能调另一个 user-invoked skill,防止编排器互相套娃。 - 模型触发(model-invoked):你不需要点名,模型读到自己
description命中当前任务时,自动加载并遵循。比如你让它”修个 bug”,它自己命中diagnosing-bugs的 description,就走上那套”复现→最小化→假设→插桩→修→回归测试”的纪律。
两条路径的分工:user-invoked 负责”编排工作流”,model-invoked 负责”沉淀可复用的工程纪律”。
图里两条路径最终都汇到 MI(model-invoked skill):左边敲 /命令 走 user-invoked,把 skill 全文注入上下文后按步骤执行工作流,期间可下调 model-invoked 但不能套另一个 user-invoked;右边普通任务由模型扫描各 skill 的 description,命中就自动加载对应的 model-invoked,没命中则无约束自由发挥。一句话——user-invoked 是你主动拉来的编排器,model-invoked 是模型按需自动戴上的纪律。
信息分层
正文不是一坨,而是分三层控制上下文成本,这是”渐进披露(progressive disclosure)”原则:
- in-skill steps:核心步骤,写在正文里,每次必读;
- in-skill reference:同目录的
tests.md/mocking.md等,按需读; - external reference:链接出去的外部文档,仅在需要时取。
分层的意义:LLM 上下文是稀缺资源,把每次必读的核心步骤和”只在某分支才需要”的细节分开装,避免无关内容吃掉 token。
写作纪律
写 skill 不是写文档,而是写”给模型看的操作规程”。mattpocock/skills 里有个专门的元 skill writing-great-skills 沉淀了这套写法,核心几条如下。
写 description
description 是自动触发的判据,写它有三条规矩:
- 前置触发词:把最关键的动作词放最前面,让模型扫一眼就命中;
- 一个分支一个触发:每个触发条件对应一种任务分支,别把多种场景揉成一句;
- 不重复正文:description 只说”何时触发”,正文里已经讲清的细节不抄进来。
何时拆分
skill 膨胀时要拆,有两种拆法:
- 按调用方式拆(by invocation):一个步骤该由用户主动发起、还是该由模型自动触发,分属 user-invoked 和 model-invoked,别混在一个文件里。
- 按序列拆(by sequence):一段流程里前后顺序明确的步骤,拆成可串起来的多个 skill,让编排器按顺序调。
剪枝与 no-op 测试
每段都要过 no-op 测试:删了这句,skill 还能正常工作吗?能就删。所以 skill 写得极简,每句都有用。剪枝时遵循”单一真源”——一个规则只在一处定义,别在多处复述。
leading words
正文里用加粗的术语(如 completion criterion、seam)锚定行为,这些是”预训练概念”的浓缩:一个术语背后挂着一整套模型已经懂的知识,用一个词就能调起,省掉大段复述。
六种失败模式
写 skill 会踩六种坑,writing-great-skills 把它们点出名:
| 失败模式 | 含义 |
|---|---|
| premature completion(过早完成) | 还没把决策树问完就草草收场,该盘问的没问到位 |
| duplication(重复) | 同一规则在多处复述,维护时易漂移,违反单一真源 |
| sediment(沉淀) | 老内容不再有用却没清掉,像淤泥占着上下文 |
| sprawl(蔓延) | skill 越写越大、什么都往里塞,失去焦点 |
| no-op(空操作) | 某段删了不影响执行——剪枝漏网 |
| negation(否定式表述) | 用”不要做 X”而非”要做 Y”来约束,模型对否定式遵循更差 |
其中 no-op 和 duplication 是剪枝的主要猎物,negation 是写法上的反模式——尽量用正向指令替代”禁止做某事”。
样例:tdd
挑 tdd 展开一个 skill 的真实写法,因为它最能体现”用 skill 沉淀纪律”的思路。
frontmatter:name: tdd,description 命中”red-green-refactor / 集成测试”等触发词,disable-model-invocation: false(可被模型自动触发)。
正文要点:
- 好测试:通过公共接口验行为、读起来像规约、扛得住重构。
- Seam(缝):只在”预先约定并和用户确认过”的公共边界测,不测未确认 seam;任何测试动手前先写下 seam 并与用户确认。
- 三大反模式:
- 实现耦合——mock 内部协作者或测私有方法,特征是”重构即崩但行为没变”;
- 同义反复——断言把代码逻辑重算一遍,永远不可能挂,期望值必须来自独立真值源;
- 横切——先写全测试再写全实现。正确做法是垂直切片:一条测试→一段最小实现→重复,每条测试都是示踪子弹。
- 循环三规:先红后绿(只写够过测试的代码,不投机)、一切一片、重构不在循环内(归
code-review)。
可见 tdd 把教科书里的 TDD 纪律,拆成了”触发判据+可执行步骤+反模式清单”,agent 读到就照着走,而不是自由发挥。
与本仓库的关系
这套机制和本仓库 .claude/skills/ 下的 knowledge-split、knowledge-learn、tech-blog 是同一套 Agent Skills 标准(都是 Claude Code 的 skill 机制)。理解了本文的原理,自己那几个 skill 的运作方式也就通了:它们的 frontmatter 同样决定触发方式,正文同样按”分步骤+可按需引用”组织。相关 skill 清单与速查,详见 MattPocock学习及Skills速查.md。
小结
| 要点 | 记住 |
|---|---|
| 本体 | 一个带 frontmatter 的 SKILL.md |
| 三字段 | name(命令名)、description(自动触发判据)、disable-model-invocation(是否只能手动调) |
| 两路径 | user-invoked 编排工作流、model-invoked 沉淀纪律 |
| 三层信息 | in-skill steps / in-skill reference / external reference |
| 收紧原则 | no-op 测试——删了不影响工作就删 |
| 写法禁忌 | 六种失败模式,尤其 negation(用正向指令替代否定式) |
延伸方向:想看 skill 的全集清单与安装使用,读 MattPocock学习及Skills速查.md;想自己写 skill,直接读仓库里的 writing-great-skills 那个元 skill,它是这套规范的源头。