本指南说明如何为 agents 编写专业级 skill、用 LLM 进行验证,以及如何保持精简的上下文窗口。

这份指南浓缩了创建 agent skills 的最佳实践。如果你需要更完整的文档,请看 Claude 的文档

要评估你的 skill 是否表现良好并防止回归,请查看 skillgrade

核心原则#

Skill 的结构#

每个 skill 都必须遵循下面的目录结构:

输出模板、JSON schema、图片skill-name/
├── SKILL.md # 必需:元数据 + 核心指令(<500 行)
├── scripts/ # 用于确定性任务的可执行代码(Python/Bash)
├── references/ # 补充上下文(API 文档、cheatsheet、领域逻辑)
└── assets/ # 输出模板、JSON schema、图片

优化 frontmatter 以提升可发现性#

SKILL.md 里的 frontmatter 中,namedescription 是 agent 在触发 skill 之前唯一能看到的字段。如果它们没有针对可发现性进行优化,或者不够具体,这个 skill 就等于“隐身”。

渐进式披露与资源管理#

通过“按需加载”信息来保持干净的上下文窗口。SKILL.md 是高层逻辑的“主脑”;把细节卸载到子目录中。

发给 Agent:当前 SKILL.md 太长了,缺乏 progressive-disclosure 机制,但是也请不要滥用。

Skills 是给 agents 用的,不是给人看的。为了保持上下文精简并避免不必要的 token 消耗,不要创建:

内容准则#

给 LLM 写指令,而不是给人写说明。

在引用 skill 文件中的概念时,要保持具体且一致。

为重复操作打包确定性脚本#

不要每次运行 skill 时,都让 LLM 从头编写复杂的解析逻辑或样板代码。

常见模式#

Skill 的组合#

你可以组合 skills(也叫 router skills),如果你想条件性地包含另一个 skill,或者想创建一个由多个 subskill 组成的 skill。示例:

---
name: build_project
description: ...
---
## 概览
...
## Build targets
### Client
要把 client 构建成可部署二进制文件,请看 [path to skills]。
### Server
要把 server 构建成可部署二进制文件,请看 [path to skill]。
...

验证指南#

因为 LLM 会使用你的 skills,所以我找到的确保它们有用的最好方法,就是和 LLM 协作。

为 skill 做 eval 非常关键,这样才能确保你做的修改是正向的,并且不会引入回归。一个常见的 skill 基准是 SkillsBench,可以给你一些灵感。

当你完成 skill 的初稿后,可以按下面步骤验证:

发现性验证#

Agent 严格依据 YAML frontmatter 加载 skill。要单独测试 LLM 如何理解你的 description,避免误触发(比如本来是 Angular 的 skill,却被 React 应用触发了)。

把下面的文本原样粘贴到一个全新的 LLM 对话里:

我正在基于 agentskills.io 规范构建一个 Agent Skill。agents 会完全根据下面的 YAML 元数据来决定是否加载这个 skill。

name: angular-vite-migrator
description: 将 Angular CLI 项目从 Webpack 迁移到 Vite 和 esbuild。用户希望更新 builder 配置、用 rollup 等价物替换 webpack 插件,或加速 Angular 编译时使用。

仅根据这段描述:

  1. 生成 3 个真实的用户提示词,我要 100% 确认它们应该触发这个 skill。
  2. 生成 3 个看起来相似、但不应该触发这个 skill 的用户提示词(例如,迁移 React app 到 Vite,或者只是更新 Angular 版本)。
  3. 批评这段描述:它是不是太宽泛了?给出一个优化后的改写版本。

另外,也可以给 agent 分配你预期会触发 skill 的任务,并读取和检查它的思考过程。和 agent 来回互动,找出它为什么选择(或没有选择)某些 skill。

逻辑验证#

确保你的逐步指令是确定性的,不要强迫 agent 去臆造缺失的步骤。

把你的整个 SKILL.md 和支持文件的目录结构喂给 LLM:

下面是我的 SKILL.md 草稿以及其支持文件的目录树。

├── SKILL.md
├── scripts/esbuild-optimizer.mjs
└── assets/vite.config.template.ts

[把你的 SKILL.md 内容贴在这里] 现在,请把自己当作一个刚刚触发这个 skill 的自主 agent。针对“将我的 Angular v17 app 迁移到 Vite”这个请求,按步骤模拟你的执行过程。

对于每一步,请写出你的内部独白:

  1. 我具体在做什么?
  2. 我在读取或运行哪个具体文件/脚本?
  3. 标出任何执行阻塞(Execution Blockers):指出到底是哪一行让我不得不猜测或臆造,因为我的指令不够清晰(例如,如何把 Angular 的环境文件映射到 Vite 的 import.meta.env)。

边界情况测试#

从漏洞、未支持的配置,以及 web 工具固有的失败状态出发,强迫 LLM 对你的逻辑发起攻击。

让 LLM 来“攻击”你的逻辑:

现在,切换角色。把自己当作一个无情的 QA 测试员。你的目标是破坏这个 skill。 请就边界情况、失败状态或 SKILL.md 中缺失的兜底逻辑,向我提出 3 到 5 个高度具体、具有挑战性的问题。重点关注:

  • 如果 scripts/esbuild-optimizer.mjs 因为遗留的 CommonJS 依赖而失败怎么办?
  • 如果用户的 angular.json 里包含了大量自定义的 Webpack builder(@angular-builders/custom-webpack),而 Vite 不支持怎么办?
  • 我是否对用户的 Node 环境做了什么隐含假设?

现在不要修复这些问题。只需按编号提出问题,然后等我回答。

SkillOpt 测试与进化#

  1. Rollout(前向传播)冻结的目标模型使用当前的技能文档去执行一批任务,记录下完整的执行轨迹和得分,作为”证据”。
  2. Reflect(反向传播):一个独立的优化器模型分析失败和成功的轨迹,找出系统性问题,并提出具体的修改建议(add/delete/replace)。这相当于计算”文本空间的梯度”。
  3. Aggregate & Select(梯度裁剪):将相似的修改建议合并,然后根据”文本学习率”(lr,默认每步最多4个编辑操作)进行排名和截断,防止单次改动过大导致”灾难性遗忘”。
  4. Update & Gate(参数更新与验证):应用选中的修改,并在一个独立的验证集(Selection Split)上重新评估新的技能文档。只有验证集表现提升的修改才会被最终接受,否则被拒绝。这是防止过拟合的关键。

第一步:用模型跑任务,收集数据

我们拿100道编程题,让模型带着这份skill去解题。每道题模型会输出答案,我们检查答案对不对,给每个题打分(0或1)。同时把模型的完整思考过程(CoT)和最终代码都保存下来。

这100条记录就是“证据”。比如其中30题做对了,70题做错了。错的题里,有40题是因为没加logging导致调试失败。

第二步:分析错误,生成修改建议

我们启用第二个模型(优化器),把上面100条记录、每道题的得分、模型思考过程,全部作为输入喂给它。

优化器分析后输出具体的编辑指令,比如:

这些指令就是“文本梯度”,指向具体怎么改skill文档。

第三步:筛选并合并修改建议

假设优化器一口气提了20条修改建议。系统会按“如果只改这一条,预估能挽回多少分”排序,然后根据预设的步长上限(比如每轮最多改4条),只取前4条。

同时检查这4条有没有冲突(比如一条要删某段,另一条要改同一段),有冲突就合并或舍弃。

第四步:用验证集验证修改,决定是否采纳

我们手里还留了另一组没见过的50道题(验证集),这50道题之前任何阶段都没用过。

把修改后的新skill文档给模型,重新跑这50道题,算出新得分。拿新得分和修改前在这50道题上的得分对比:

这个门控机制保证每次进化都是“真实有效”的。

额外机制:防止重复犯错

系统里有个“被拒修改黑名单”,记录所有被验证门控拒绝的修改指令。下一轮优化器再生成建议时,我们会把黑名单也喂给它,明确告诉它:“这3种改法以前试过,没用,别再提了。”

最终产出

重复上面1-4步,比如跑20轮。每轮结束后,我们始终保存的是在验证集上得分最高的那份skill文档。最后部署时,直接把这份文档塞进目标模型的系统提示词里,模型推理时按这些规则执行,整个过程没有任何额外开销。

反模式#

检查清单#

发布 skill 前逐项确认:

npx skills 进行 SKILL 维护#

Skill 101:可以自动更新的 Skill-diagram-2