Skill工程架构
最近我一直在写 Skill。
写得多了以后,我开始感觉这件事没有想象中那么简单。一开始我把 Skill 理解成一种更完整的 Prompt:把流程写清楚,把注意事项补全,再加上一些示例,效果应该就会越来越好。
按照这个思路继续写下去,很容易出现一个结果:SKILL.md 越来越长,规则越来越多,但 Skill 的稳定性并没有同步提高。
有时它在当前对话里表现很好,换一个上下文就失效;有时能生成不错的结果,却无法证明结果是否正确;还有一些 Skill,单独看都合理,放到同一个能力库里却开始互相抢触发。
这让我意识到,Skill 不是一段写得更完整的 Prompt。它实际上是一种工程架构。
第一性原理:Skill 究竟是什么
如果先不考虑现在已有的 Skill 格式和目录约定,只从它要解决的问题出发,我会把 Skill 定义成:
一个面向非确定性执行者的、可触发的局部控制系统。
这里的执行者就是 Agent。
Agent 本身已经足够聪明,但它仍然有几个明显限制:
- 上下文有限,不可能每次都携带所有项目经验;
- 执行具有随机性,同一个任务可能选择不同路径;
- 容易受到当前对话影响,把临时信息当成稳定规则;
- 可以生成看起来合理的结果,却不一定主动验证;
- 知道很多通用知识,但不知道项目里的真实边界和约束。
Skill 的作用,不是继续给 Agent 塞入更多知识,而是在一个具体能力范围内降低这些不确定性。
所以 Skill 首先应该是一种能力边界,然后才是文字。它需要回答:
- 什么时候应该启动,要解决什么问题?
- 接受什么输入,最终应该产生什么?
- 哪些决定可以自由判断,哪些步骤必须严格执行?
- 如何证明任务真的完成了?
- 失败以后如何降级或停止?
从这个角度看,一个高质量 Skill 主要看四个因子:触发、执行、验证和可迁移性。触发不准,能力再强也不会被正确调用;执行不稳,写得再完整也只能偶尔成功;没有验证,Agent 只能证明“做过”,不能证明“做对”;严重依赖作者的机器和历史上下文,就很难在其他项目和客户端复用。这四个因子任何一个掉下来,整个 Skill 的质量都会明显下降。
从 68 个 Skill 里看到的问题
我对 AgentInfra 项目下现有的 Skill 做了一次盘点。
目前仓库里有 68 个注册 Skill,SKILL.md 加起来大约一万行。其中:
- 41 个带有
references/ - 31 个带有
scripts/ - 30 个带有
evals/ - 3 个
SKILL.md超过了 500 行 - 还有一个已经注册为 active 的 Skill,实际上只有 frontmatter,没有任何执行正文
这些数字放在一起很有意思。
它说明这个仓库里的 Skill 已经不只是一些提示词了。里面有路由器、脚本、模板、验证器、评测案例、跨客户端分发和 profile 管理,已经具备了软件资产的很多特征。
但它也暴露了问题。
比如一个 Skill 只要 frontmatter 字段完整,就可能通过基础结构检查。至于它有没有真正的执行能力、是否与其他 Skill 冲突、能不能在新上下文里工作,目前并不一定能够被自动发现。
一开始我以为,后续应该继续补充更多规范。后来才发现,这条路很危险。规则越写越多,不等于能力越来越强。很多问题根本不应该靠增加文字解决。
从当前仓库抽象出的 Skill 八层架构
结合这些 Skill 的实际结构,我把一个完整的 Skill 拆成了八层。
这八层不代表每个 Skill 都要创建八套文件,而是设计时应该把八类问题想清楚。简单 Skill 可以把多层合并在一个入口里,复杂 Skill 则需要逐步拆开。
第一层:资格层
首先判断这段经验是否值得成为 Skill。
要看它是否重复出现,核心流程是否稳定,触发是否清晰,能不能形成输出契约,以及是否真的会在多个任务或项目中复用。
如果只发生过一次,或者结果根本无法评价,就不应该急着注册成公共 Skill。它可能更适合先成为案例、候选方案或者普通笔记。
第二层:接口层
这一层负责解决“什么时候调用”。
除了名称,还要设计:
- 正向触发场景;
- 用户可能使用的自然语言;
- 相邻但不应该触发的请求;
- 和已有 Skill 的区别;
- 找不到精确能力时的降级路线。
只有正向触发,没有 non-trigger,很容易导致多个 Skill 争抢同一个任务。
第三层:契约层
契约层负责定义 Skill 的输入、输出、边界和完成标准。
如果输出只是“生成一份报告”,这个契约还不够。报告放在哪里、必须包含什么、哪些结论需要证据、哪些检查没有运行时不能声称完成,这些才是真正的执行边界。
这一层还要明确非目标,防止 Skill 在执行过程中不断扩大范围。
第四层:控制层
控制层决定具体怎么执行。
开放性的分析任务可以给 Agent 较高自由度。涉及发布、删除、迁移、账号操作和验证的流程,则需要更明确的顺序、checkpoint、停止条件与失败处理。
关键不是步骤写得多细,而是自由度是否与风险匹配。
第五层:知识层
这一层存放 Agent 无法仅靠通用能力可靠推断的内容,例如:
- 项目架构;
- 业务规则;
- 数据结构;
- API 和 Schema;
- 评分标准;
- 安全策略;
- 领域术语。
这些内容通常应该进入 references/,按需要加载,而不是全部塞进 SKILL.md。
第六层:确定性层
凡是要求稳定、可重复、容易出错的操作,都应该考虑从自然语言下沉为:
scripts/templates/assets/- validator
如果一段代码每次执行 Skill 时都需要 Agent 重新编写,大概率说明它还没有被真正工程化。
第七层:证明层
这一层负责回答“怎么知道 Skill 真的有效”。
至少要区分:
- 结构验证;
- 脚本运行验证;
- 真实任务行为验证;
- 失败案例回归;
- held-out 案例;
- non-trigger 测试;
- 多 Skill 组合后的集成验证。
格式正确只能证明这个 Skill 可以被读取,不能证明它具备真实能力。
第八层:治理层
当 Skill 开始跨项目和跨客户端使用以后,还需要治理:
- canonical source 在哪里;
- 属于哪个 profile;
- 支持哪些 target;
- 如何生成或同步客户端入口;
- 是否包含私有路径和环境依赖;
- 谁负责维护;
- 如何根据真实案例继续演进。
如果多个客户端各自复制一份,后续一定会发生漂移。源头只能有一个,其他位置应该是生成结果或发现镜像。
真正需要设计的是能力边界
八层架构里,我认为最容易被忽略的是前面三层。
很多时候我们直接开始写执行步骤,却没有先搞清楚这段能力是不是应该成为 Skill,也没有判断它和现有能力的区别。
AgentInfra 里有两个很接近的 Skill:
codebase-learning-course负责分析源码并生成学习课程;project-onboarding-skill-builder负责生成项目级 Skill Pack。
它们都和“接手陌生项目”有关。如果只看主题,很容易把它们合成一个大 Skill。但它们的产物、输出目录和验收方式完全不同,所以应该拆开。
这个例子让我形成了一个新的判断标准:
是否拆成两个 Skill,不看主题是否相似,而看触发条件、产物所有权和验收标准是否相同。
反过来,能力库越来越大以后,还需要 Router。
Router 的目的不是一次调用更多 Skill,而是帮助 Agent 找到最小可用组合。一个按钮样式修改,直接改就可以了,不需要为了显得工程化调用五个能力。
Skill 越多,越要克制。
SKILL.md 应该是控制入口
过去我容易把所有重要经验都写进 SKILL.md,觉得只有这样 Agent 才不会漏掉。
后来发现,这种做法会导致另一个问题:每次触发 Skill,Agent 都要加载大量与当前任务无关的内容。
更合适的结构应该是:
SKILL.md
├── 触发与边界
├── 核心决策流程
├── 资源路由条件
├── 输出与验证契约
└── references / scripts / templates / evals
SKILL.md 是控制面,不是资料仓库。
真正应该判断的也不是文件是否超过某个行数,而是:
当前任务是否被迫加载了与它无关的知识?
如果答案是“是”,就应该做渐进式加载。入口只保留路由和核心契约,具体分支需要时再读取对应 reference。
Skill 应该从真实失败中成长
我现在最警惕的一种改进方式,是看到一次失败以后,马上在 SKILL.md 里增加一句“注意”。
这种修改看起来很快,长期却很容易形成提示词债务。规则不断增加,真正的问题却可能出在脚本、环境、输入契约、验证器或者 Skill 边界上。
后续再遇到失败,我会先找最早偏离的位置:
- 是根本没有触发?
- 是输入不完整?
- 是理解错了目标?
- 是执行步骤不稳定?
- 是没有验证?
- 还是环境根本不支持?
找到原因以后,再把改动放到最窄的资产里。
知识缺了补 reference;重复操作写成 script 更省事。输出老缺字段就加 template,完成声明不可信的话,再加一道 validator。只有确实属于执行判断的问题,才应该修改 SKILL.md。
而且一个案例得出的经验只能先算暂定规则。它至少还要经过旧案例回归、失败案例回归和一个没有见过的新案例,才能说明这次修改不是只针对一道题打补丁。
后续我准备怎么做
经过这次整理,我后面创建 Skill 时会先回答几个问题:
- 这个能力是否真的重复出现过?
- 为什么现有 Skill 不能覆盖?
- 正向触发和 non-trigger 分别是什么?
- 输入、输出和完成标准能否写清楚?
- 哪些判断交给 Agent,哪些动作必须下沉到脚本?
- 如何验证真实能力,而不只是验证文件格式?
- 这次经验来自一个案例,还是已经具备可复用证据?
如果这些问题回答不出来,我不会急着创建新的 Skill。
写 Skill 最容易走的弯路,是把所有经验都写进去。
后面我准备把这套经验继续固化成一个专门的 skill-architecture-designer,再配一个静态 validator。这样以后创建 Skill 时,就不是从空白的 SKILL.md 开始,而是先完成能力边界、执行契约和验证设计。