搜索文章

输入关键词开始搜索

Skill工程架构

AI#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 开始,而是先完成能力边界、执行契约和验证设计。