Agent Skill 设计
Skill可以理解为提供给Agent的可复用工作流程和领域知识,它不是一份越长越好的Prompt。
Agent Skills目前已经形成了比较统一的目录和加载方式。Agent平时只需要看到Skill的name和description,确认任务匹配后才读取SKILL.md,其它资料继续按需加载。
flowchart LR A[User Task] --> B{name + description} B -->|No Match| C[Normal Handling] B -->|Match| D[Load SKILL.md] D --> E[Execute Workflow] E --> F{Need Extra Resources?} F -->|Knowledge| G[references] F -->|Deterministic Tasks| H[scripts] F -->|Output Resources| I[assets]
这种设计的核心就是减少上下文污染,让Agent只在需要的时候读取需要的信息。
基本结构
一个标准Skill最少只需要一个SKILL.md:
skill-name/
├── SKILL.md
├── scripts/
├── references/
└── assets/其中只有SKILL.md是必须的:
SKILL.md:核心工作流和规则scripts/:稳定、重复、适合程序执行的操作references/:API、Schema、规范、领域知识assets/:模板、图片、代码骨架等输出资源
如果是在OpenAI Codex中使用,还推荐增加:
skill-name/
├── SKILL.md
├── agents/
│ └── openai.yaml
├── scripts/
├── references/
└── assets/agents/openai.yaml是OpenAI自己的扩展,主要用于UI信息和工具依赖,不属于Agent Skills开放规范的核心部分。
如果希望Skill尽量跨Agent兼容,真正的执行逻辑应该留在
SKILL.md,平台相关配置单独处理。
SKILL.md
SKILL.md由YAML Front Matter和Markdown正文组成:
---
name: code-review
description: Review code changes for correctness and maintainability. Use when the user asks to review a diff, pull request, patch, or modified source files.
---
# Code Review开放规范要求name和description必须存在。
name
name需要满足:
- 最长64字符
- 只能使用小写字母、数字和
- - 不能以
-开头或结尾 - 不能连续出现
-- - 应与Skill目录名称一致
一般直接使用kebab-case:
code-review
testing-code
processing-pdfs
managing-databases不要使用过于宽泛的名称:
helper
utils
tools
files名称本身最好就能说明Skill负责什么能力。
description
description实际上承担了Skill的触发器作用,因为Agent主要通过它判断是否需要读取这个Skill。
例如下面这种写法太宽泛:
description: Helps with code.更合适的写法:
description: Review code changes for correctness, security, maintainability, and project conventions. Use when the user asks to review a diff, pull request, patch, or modified source files.基本可以按照:
做什么 + 处理什么对象 + 什么情况下使用来编写。
如果Skill容易和其它Skill产生冲突,还可以明确写出排除条件。OpenAI自己的security-best-practices就明确说明只在用户要求安全审查时触发,而普通Code Review和Debug不应该触发。
触发条件必须尽量写在
description里,而不是只写在正文中;因为正文只有Skill已经触发以后才会被读取。
如果Skill经常没有被调用,首先应该检查的通常就是description。
渐进加载
Skill最重要的设计原则之一是Progressive Disclosure,也就是渐进加载。
flowchart TD A["name + description<br/>Always Visible"] B["SKILL.md<br/>Loaded After Trigger"] C["references / scripts / assets<br/>Loaded or Executed On Demand"] A --> B --> C
Agent Skills规范建议SKILL.md控制在500行以内,并推荐正文保持在约5000 tokens以内。
所以SKILL.md应该更接近操作手册和导航,而不是完整知识库。
比如一个数据库Skill同时支持MySQL、PostgreSQL和SQLite,可以这样组织:
database/
├── SKILL.md
└── references/
├── mysql.md
├── postgresql.md
└── sqlite.md然后在SKILL.md中只负责选择:
根据当前数据库读取对应资料:
- MySQL:读取`references/mysql.md`
- PostgreSQL:读取`references/postgresql.md`
- SQLite:读取`references/sqlite.md`这样处理SQLite时就不会把其它数据库资料一起放入上下文。
引用层级也不要太深,最好都由SKILL.md直接引用:
SKILL.md
├── references/a.md
├── references/b.md
└── references/c.md而不是让a.md再引用b.md,然后继续向下套。
正文设计
Agent本身已经具有大量通用知识,所以Skill正文最值得写的是Agent无法可靠推断出来的东西,例如:
- 项目自己的约定
- 固定工作流程
- 特殊边界条件
- 容易犯错的地方
- 必须执行的验证步骤
- 某个工具应该在什么情况下使用
类似“什么是JSON”“为什么测试很重要”这种通用知识通常没有必要展开。
OpenAI当前的skill-creator还建议正文尽量使用直接的命令式表达,例如:
1. Read the project configuration.
2. Inspect the modified files.
3. Run the validator.
4. Fix reported errors.
5. Re-run validation before finishing.相比“Ensure quality”“Follow best practices”这种抽象要求,明确步骤会稳定很多。
条件流程
如果不同输入应该走不同路径,应直接写明判断条件:
1. 判断任务类型:
- 创建新文件 -> 使用创建流程
- 修改已有文件 -> 使用编辑流程
2. 根据对应流程继续执行。如果分支越来越复杂,就把具体流程拆到references/,让SKILL.md只负责选择。
默认路径
Skill最好提供一个明确默认方案,而不是给Agent很多平级选择。
推荐:
默认使用A。
只有输入为扫描件时使用B。
只有A无法保留格式时才使用C。不推荐:
可以选择A、B或者C,根据情况决定。前者能够减少不必要的选择,也让多次执行更加稳定。
自由度
Skill应该根据任务本身的风险决定约束程度。
flowchart LR A[High Freedom] --> B[Medium Freedom] --> C[Low Freedom] A --- A1[Analysis / Review / Writing] B --- B1[Fixed Workflow + Tunable Parameters] C --- C1[Migration / Release / Deletion / Conversion]
高自由度
任务允许很多正确方案时,只需要规定目标、原则和检查点。
例如Code Review只需要说明:
- 检查代码结构。
- 检查Bug和边界条件。
- 检查可维护性。
- 检查项目约定。
中自由度
如果已经存在推荐处理方式,但参数需要根据任务变化,可以规定:
固定工作流 + 默认工具 + 参数选择规则低自由度
对于迁移、发布、批处理、文件格式修改等容易出错的操作,最好提供确定性的脚本和明确顺序:
python scripts/migrate.py --verify --backup然后直接规定:
不要跳过验证。
验证失败时先修复问题,再继续后续步骤。这类任务越稳定越好,没有必要让Agent每次重新设计执行过程。
scripts
如果Agent每次执行Skill都需要重新写一段几乎相同的代码,就应该考虑放入scripts/。
比较典型的情况:
- 文件转换
- 数据校验
- Schema验证
- 固定格式生成
- 批量处理
- 对参数和顺序敏感的操作
例如:
skill-name/
├── SKILL.md
└── scripts/
├── validate.py
└── convert.py然后在SKILL.md中直接调用:
python scripts/validate.py output/脚本除了节省Token,更重要的是把不应该由LLM自由发挥的部分变成确定性的程序。
能稳定用程序解决的问题,没有必要让Agent每次重新推理一次。
references
references/比较适合存放“需要理解,但不是每次都需要理解”的内容:
references/
├── api.md
├── schema.md
├── conventions.md
└── error-codes.md关键在于SKILL.md需要明确说明什么时候读取:
- 涉及数据库查询时读取`references/schema.md`
- 涉及API调用时读取`references/api.md`
- 修改业务逻辑前读取`references/conventions.md`不要只写:
更多资料位于references目录。因为Agent并不知道应该什么时候读哪个文件。
对于比较长的reference,也应该尽量保持一个文件只负责一个主题。
assets
assets/中的内容一般不是为了让Agent理解规则,而是用于最终产物。
例如:
assets/
├── logo.png
├── report.docx
├── slides.pptx
└── project-template/如果一个文件主要用于让Agent理解知识,应该放references/;如果主要用于复制、修改或嵌入最终结果,则更适合放assets/。
验证闭环
优秀Skill通常不会停在“操作完成”,而是会要求Agent验证结果。
flowchart LR A[Execute] --> B[Validate] B -->|Failed| C[Fix] C --> B B -->|Passed| D[Deliver]
例如:
1. 修改文件。
2. 运行validator。
3. 如果失败,读取错误并修复。
4. 再次运行validator。
5. 只有验证通过后才能完成任务。如果没有验证脚本,也可以根据references/style-guide.md之类的规则文档进行检查。
验证最好紧跟在容易出错的操作后面,而不是最后再一次性检查全部内容。
防错规则
Skill真正有价值的内容,很多时候不是通用最佳实践,而是Agent“看起来做得合理,实际上却会做错”的地方。
例如:
## 注意
- 用户表使用软删除,普通查询必须过滤`deleted_at IS NULL`。
- `/health`只表示Web服务启动,完整健康检查使用`/ready`。
- 数据库中的`user_id`和认证服务中的`uid`表示同一个用户。实际使用Skill后,如果Agent出现一个可能重复发生的问题,就应该考虑把它沉淀成:
规则
工作流步骤
reference
validator
script而不是继续增加大段解释。
优秀Skill的设计范式
从OpenAI和Anthropic现有Skill中,大致可以看到几种比较稳定的设计方式。
精确触发
OpenAI的security-best-practices会在description中同时说明:
什么时候使用
什么时候不要使用这种方式特别适合多个Skill能力相近的情况,可以减少误触发。
工具优先
OpenAI的jupyter-notebook会优先要求使用已经准备好的模板和辅助脚本,而不是让Agent手写.ipynb JSON。
这种模式适合文件格式复杂、手工生成容易出错的任务。
Gate Skill
OpenAI的openai-platform-api-key更像一个前置检查,它不是主要业务Skill,而是负责在后续API工作开始前处理凭据相关流程。
因此Skill并不一定需要直接产生最终结果,也可以负责:
前置检查
安全门
环境检测
路由核心流程 + 按需资料
复杂Skill不会把所有领域知识写进正文,而是:
SKILL.md -> 判断当前任务 -> 读取对应reference -> 执行这也是最适合大型Skill长期维护的方式。
Skill设计流程
实际设计一个Skill时,可以按照下面的顺序:
- 先用真实任务跑Agent。
- 找出它容易失败、重复推理或者浪费工具调用的地方。
- 确定Skill负责什么,以及明确不负责什么。
- 先写好
name和description。 - 将稳定主流程写进
SKILL.md。 - 将详细知识拆到
references/。 - 将重复且确定的操作做成
scripts/。 - 给关键步骤增加验证。
- 再使用真实任务测试。
- 根据实际失败继续修改。
flowchart LR A[Real Tasks] --> B[Observe Problems] B --> C[Minimal Skill] C --> D[Use in Practice] D --> E{Stable?} E -->|No| F[Add Rules / References / Scripts] F --> D E -->|Yes| G[Keep It Simple]
Anthropic官方也推荐先建立真实评测场景,再增加最少的指令解决这些问题,而不是一开始预测所有可能情况。
触发测试
除了测试Skill执行结果,还需要单独测试它是否会正确触发。
准备两组Prompt即可:
应该触发
不应该触发例如一个安全审查Skill:
应该触发:
- 做一次安全审查
- 检查这段Go代码有没有安全问题
不应该触发:
- Review一下这段代码
- 这个NullPointerException怎么修如果经常漏触发,一般说明description太窄;如果几乎什么任务都触发,则说明范围太宽。
这里最好重点测试与其它Skill比较接近的边界场景,而不是只测试最明显的Prompt。
Pi Agent
Pi实现了Agent Skills标准,整体设计仍然遵循前面的通用原则:启动时只收集Skill的name和description,匹配任务后再读取完整SKILL.md。
Pi本身强调最小核心和可组合扩展,因此设计Skill时,需要先判断需求到底属于Skill,还是应该放到其它机制中。
flowchart TD A[New Requirement] --> B{Always Needed?} B -->|Yes| C[AGENTS.md] B -->|No| D{User Invoked Prompt?} D -->|Yes| E[Prompt Template] D -->|No| F{Needs Runtime Capability?} F -->|Yes| G[Extension] F -->|No| H[Skill] C --> I[Pi Package] E --> I G --> I H --> I
Skill、AGENTS.md、Prompt Template和Extension
Pi中的几个机制解决的是不同层级的问题:
| 需求 | 更适合的机制 |
|---|---|
| 始终需要生效的项目规范、命令和约定 | AGENTS.md |
| Agent根据任务按需加载的流程和知识 | Skill |
| 用户主动执行的一段可复用Prompt | Prompt Template |
| 新工具、事件拦截、UI、MCP、权限控制等运行时能力 | Extension |
| 将多个资源一起安装和分发 | Pi Package |
例如:
始终使用Java 21
提交前运行./gradlew test
项目使用Google Java Format这类内容应该放AGENTS.md,因为每次任务都可能需要。
而:
发布新版本时按照固定流程检查版本号、生成Changelog、运行测试并创建Tag更适合做成Skill,因为只有发布任务才需要加载。
如果只是需要一个用户主动调用的Review Prompt,例如:
/review src/main/java使用Prompt Template会比Skill更简单。
需要注册一个新的http_request工具、连接MCP Server、拦截危险命令或者提供交互UI时,则应该使用Extension,而不是试图通过SKILL.md实现运行时能力。
Skill负责告诉模型“如何使用能力”,Extension负责真正“提供能力”。
Skill加载位置
Pi可以从多个位置发现Skill:
Global:
~/.pi/agent/skills/
~/.agents/skills/
Project:
.pi/skills/
.agents/skills/
Package:
skills/
package.json -> pi.skills
Other:
settings.json -> skills
--skill <path>其中.agents/skills/更适合作为跨Agent共享位置;.pi/skills/则适合Pi专用Skill。
项目级Skill只有在项目被信任后才会加载,因此涉及第三方仓库时,不应该假设项目Skill一定已经启用。
如果Skill需要同时在Pi、Codex、Claude Code等环境使用,优先遵循Agent Skills开放标准,不依赖Pi专有行为。
Pi中的加载过程
Pi启动时会扫描Skill,只把名称和描述加入系统上下文;模型认为任务匹配后,再通过read读取完整的SKILL.md。
flowchart LR A[Startup] --> B[Scan Skills] B --> C[Inject Metadata] C --> D{Task Matches?} D -->|No| E[Continue Normally] D -->|Yes| F[Read SKILL.md] F --> G[Follow Workflow]
这种方式意味着大量Skill同时安装时,虽然不会一次加载全部正文,但所有可自动触发Skill的元数据仍然会占用一部分系统上下文。
因此更适合:
- 全局只保留高复用Skill
- 项目专用Skill放
.pi/skills/ - 避免安装大量功能重叠的Skill
description保持准确,不写无关背景- 不需要自动触发的Skill改为显式调用
显式调用
Pi会将Skill注册为:
/skill:<name>例如:
/skill:pdf-tools extract
/skill:release这可以绕过模型是否主动选择Skill的问题。
Pi还支持:
---
name: release
description: Release the current project using the repository release workflow.
disable-model-invocation: true
---设置disable-model-invocation: true后,Skill不会出现在模型自动选择的Skill列表中,只能通过/skill:release显式调用。
这比较适合:
- 发布
- 部署
- 删除
- 数据迁移
- 高成本API调用
- 需要用户明确表达意图的操作
对于这类Skill,与其不断调整description防止误触发,不如直接关闭模型自动调用。
触发可靠性
Pi官方文档明确说明模型并不一定总会主动读取匹配的Skill,因此不能把Skill发现机制理解成严格路由器。
关键流程需要更高确定性时,可以按照优先级处理:
- 优化
description - 在
AGENTS.md中说明特定场景应使用哪个Skill - 使用
/skill:name显式调用 - 对必须自动执行的行为使用Extension,而不是依赖模型自行选择
例如项目中固定存在数据库迁移Skill,可以在AGENTS.md中加入:
数据库Schema发生变化时,使用`database-migration`Skill,不要手工执行迁移流程。这里仍然保持Skill按需加载,只是在始终存在的项目上下文中增加一个轻量路由规则。
disable-model-invocation
这个字段属于Pi支持的Skill Front Matter扩展:
disable-model-invocation: true它解决的是“Skill可以存在,但不希望模型自动调用”的需求。
可以按照下面的方式分类:
| Skill类型 | 自动调用 |
|---|---|
| 文档查询、代码分析、格式转换 | 通常开启 |
| 测试、Lint、普通构建 | 根据项目决定 |
| 发布、部署、迁移、删除 | 通常关闭 |
| 产生费用或修改外部系统 | 通常关闭 |
对于跨Agent Skill,不应该依赖这个字段保证安全,因为其它Agent可能不支持它。
真正危险的操作仍然应该由脚本、权限、Extension确认机制或外部系统进行限制。
Skill + Extension
Pi中特别适合使用的一种设计是:
Extension = Capability
Skill = Workflow例如MCP:
flowchart LR A[Extension] -->|Expose Tools| B[Agent] C[Skill] -->|Usage Rules| B B --> D[MCP Server]
Extension负责:
- MCP连接
- 工具注册
- 参数Schema
- 生命周期
- 错误处理
- 权限和确认
Skill负责:
- 什么情况下使用这些工具
- 调用顺序
- 默认策略
- 多工具组合方式
- 失败后的处理方式
- 结果如何验证
这样可以避免把工具实现和使用策略耦合在一起。
例如一个IntelliJ MCP集成,可以由Extension暴露IDE工具,而Skill只描述:
修改代码前先读取文件问题。
能够使用IDE Refactoring时优先使用Refactoring。
修改完成后执行项目Build。
Build失败时读取Problems并继续修复。如果后续更换MCP实现,只要工具语义保持一致,Skill通常不需要整体重写。
Pi Package
当Skill需要依赖Extension时,最好一起打包成Pi Package。
my-pi-package/
├── package.json
├── extensions/
│ └── index.ts
└── skills/
└── my-skill/
├── SKILL.md
└── references/
└── usage.mdpackage.json可以声明:
{
"name": "my-pi-package",
"keywords": ["pi-package"],
"pi": {
"extensions": ["./extensions"],
"skills": ["./skills"]
}
}Pi也支持约定目录自动发现,因此简单Package不一定需要显式填写pi字段;但公开发布时,显式声明会更容易看出Package包含什么资源。
这种组合适合:
Extension提供工具
Skill提供工具使用方法
Package负责安装和版本管理如果Skill只是纯Markdown工作流,没有运行时依赖,则没有必要为了Skill单独创建Extension。
名称冲突
Pi允许从全局、项目、Package和自定义路径同时加载Skill,因此更容易出现同名Skill。
发现同名Skill时Pi会给出警告,并保留先发现的Skill。
因此名称最好不要过于通用:
不推荐:
review
deploy
database
更合适:
java-code-review
docker-image-release
sqlite-schema-migration即使Pi当前对Skill名称和父目录名称的匹配比开放规范更宽松,为了跨Agent兼容,仍建议保持:
skills/sqlite-schema-migration/SKILL.md
name: sqlite-schema-migrationPi专用Skill的设计原则
在通用Skill原则之外,Pi环境还可以增加下面几条:
- 始终生效的规则放
AGENTS.md,不要塞进Skill。 - 用户主动调用的简单Prompt优先使用Prompt Template。
- 需要新增能力时使用Extension,Skill只描述如何使用能力。
- 高风险Skill优先使用
disable-model-invocation和显式/skill:name。 - 项目专用Skill优先放项目目录,减少全局Skill数量。
- Skill与Extension强相关时,通过Pi Package一起分发。
- 仍然遵循Agent Skills标准,Pi专有字段只作为附加能力。
- 不把模型自动选择Skill当作严格可靠的路由机制。
对Pi而言,比较理想的结构不是“所有东西都做成Skill”,而是让
AGENTS.md负责常驻规则、Skill负责按需工作流、Extension负责运行时能力,再由Pi Package负责组合和分发。
其它注意事项
开放规范允许在Front Matter中增加:
license:
compatibility:
metadata:
allowed-tools:其中allowed-tools目前仍属于实验字段,不同Agent实现的支持程度可能不同。
OpenAI当前的skill-creator则更倾向于让SKILL.md只保留:
---
name:
description:
---OpenAI自己的UI信息、图标以及MCP依赖则放进agents/openai.yaml。
另外还有几个比较实用的原则:
- 文件引用尽量使用相对路径,并保持一层引用。
- 不要在Skill中增加无关的
README.md、CHANGELOG.md等说明文件。 - 不要写大量容易过时的版本和时间判断,过时方案需要保留时单独放到Legacy部分。
- 同一个概念尽量保持同一种名称,不要在一份Skill里来回更换术语。
- 示例应该展示真正需要固定的格式或行为,不需要为了“看起来完整”而堆很多例子。
最终可以把Skill设计归纳成一句话:
description负责让Agent在正确的时候找到Skill,SKILL.md负责告诉Agent怎么做,references补充需要的知识,scripts把不应该自由发挥的部分固定下来。