我们书接上回,在上一篇文章中,我们介绍了 Skill 是什么、它为什么重要,以及 Agent 如何通过渐进式披露加载 Skill。
不过,只知道这些概念还不够。要真正理解 Skill,最直接的办法还是亲手创建一个。
在这篇文章中,我们会从一个具体需求出发,一步一步创建可以实际运行的 Skill。它会把杂乱、缺少结构的会议记录,整理成一份清晰、方便分享的会议纪要。
完成之后,你将得到一个真正可以使用的 Skill,而不只是一份看起来正确的 SKILL.md 示例。
那么,点一杯咖啡,我们开始吧。☕
先确定使用场景
创建 Skill 的第一步,不是新建目录立马编写 description,而是先明确一个问题:
你到底希望这个 Skill 解决什么需求?
如果这个问题还没有想清楚,后面的目录结构和指令写得再完整,也很容易变成一套没有明确用途的提示词。
开始之前,可以先问自己几个问题:
- 我想自动完成什么任务?
- 这个任务是否包含多个固定步骤?
- 执行过程中是否需要工具?如果需要,是内置工具还是 MCP 提供的工具?
- 是否需要把某些领域知识、团队规范或最佳实践放进 Skill?
同时,还可以判断它属于上一篇提到的哪一类 Skill。
我们举个例子,假设我们的需求是:
把原始会议记录整理成结构清晰的摘要,提取行动项,让团队中的任何人都可以继续跟进。它属于第 1 类 Skill——文档与资产创建,因为最终目标是生成一份文档。
这些问题看起来很简单,却可以帮助我们在真正编写文件之前,先把工作流的边界确定下来。
怎样判断这个 Skill 是否成功
确定使用场景之后,接下来要定义成功标准。
当然,你也需要确认这个 Skill 的目标本身可达成。你不大可能写一个 Skill,就让它去解决哥德巴赫猜想——这并不现实。当然,我这里用词很谨慎:我说的是“不大可能”,万一将来真的有可能呢?
这是整个过程里非常重要的一步。因为如果没有标准,我们只能凭感觉判断 Skill “好像可以用”,却不知道它究竟能否稳定工作。
成功标准通常可以分成两类。
定量指标
定量指标用可以测量的数字描述结果,例如:
- 在 10 条相关请求中,至少有 9 条可以正确触发;
- 能够完成工作流中的全部步骤,不会遗漏其中某一步。
定性指标
定性指标关注 Skill 实际表现出来的行为,例如:
- 每次都能生成结构化结果;
- 不会虚构原始记录中不存在的信息。
对于这个会议纪要 Skill,我们可以把成功标准写得更具体一些:
定量指标:
在固定宿主、模型版本和可用 Skill 集合的前提下,至少 10 条包含原始会议记录,或者包含“整理这些记录”“清理我的笔记”“编写会议纪要”等表达的消息中,至少有 9 条能够自动触发 Skill,不需要用户主动说出 Skill 的名称。
定性指标:
第一次使用的人只需要粘贴一段杂乱的会议记录,就能得到一份可以直接发送给团队的摘要,不需要再次修改;其中不能出现虚构信息,并且每个行动项都要标明负责人;无法确定时则明确标为“未分配”。
这些指标现在还不会立即派上用场。到了第 3 篇,我们会用它们测试 Skill,确认它是不是真的按照预期工作。
使用场景和成功标准都确定之后,就可以开始创建 Skill 了。不过在写具体内容之前,还要先看一下它的目录结构。
Skill 的目录结构
最终的目录大致如下:
meeting-notes-formatter/
├── SKILL.md
├── references/
│ └── formatting-rules.md
└── assets/
└── meeting-template.md
其中只有 SKILL.md 是必需文件,references/、scripts/ 和 assets/ 都是根据需要添加的可选目录。
SKILL.md:Skill 的核心
大多数 Skill 只需要一个 SKILL.md 就可以工作。其他文件和目录,主要用来承载更详细的资料、脚本和模板。
因此,SKILL.md 是否写得正确非常重要。Agent 需要依靠它判断:
- 什么时候应该使用这个 Skill;
- 这个 Skill 可以完成什么任务;
- 触发之后应该按照什么步骤执行。
一个 SKILL.md 通常由两部分组成:
- **Frontmatter:**文件顶部的一小段 YAML 元数据,前后使用
---包裹; - **指令正文:**Frontmatter 下面的 Markdown 内容,定义真正的工作流程。
---
name: your-skill-name
description: 说明这个 Skill 能做什么,以及应该在什么情况下使用。
---
# Skill 名称
从这里开始,下面的内容都是具体指令……
Agent 通常会先看到 Skill 的名称和描述,用它们判断当前任务是否匹配。只有确定需要使用这个 Skill 之后,才会继续读取完整的指令正文。
这也是为什么 Frontmatter 虽然很短,却直接影响 Skill 能否被正确发现和触发。
下面先从它开始。
Frontmatter:Agent 最先看到的部分
Frontmatter 是 SKILL.md 顶部的 YAML 元数据。对于一个最小的 Skill,至少需要包含 name 和 description:
---
name: your-skill-name
description: 说明它能做什么,以及用户提出哪些请求时应该使用。
---
字段要求如下:
description:提高被正确触发的概率
description 究竟有什么作用?
它是一段用于发现和路由 Skill 的元数据。Agent 不需要先加载所有完整指令,只看这段简短描述,就可以判断当前请求是否应该使用这个 Skill。
一段比较完整的 description,通常包含三部分:
[它能做什么] + [什么时候使用] + [它包含哪些关键能力]
这三部分都应该尽量清楚。对比下面几种写法:
# ✅ 较好:说明了能做什么、什么时候使用,以及包含哪些能力
description: 把粗略需求整理成可以进入 Sprint 的 Agile User Story。当用户需要在 Sprint Planning 之前编写、完善、拆分或验证 User Story 时使用。包括验收标准、INVEST 检查、边界情况、依赖项、子任务以及可以直接写入 Jira 的输出。
# ❌ 过于模糊
description: 帮助处理 Agile。
# ❌ 只说了能做什么,没有说明什么时候使用
description: 创建包含验收标准和子任务的 User Story。
# ❌ 在 description 中加入了没有必要的 XML 标签
description: <skill>创建 User Story 和验收标准</skill>
这里有一个很实用的判断方法:
如果你无法想象用户会输入什么内容来触发这个 Skill,那么 Agent 通常也很难准确判断。
description 应该保持简洁,同时使用真实用户可能说出的表达。不要堆砌只有 Skill 作者自己才会使用的技术术语。
如果希望兼容 Agent Skills 开放规范,还要注意字段长度等约束。例如,description 不应超过 1024 个字符;name 应使用 1 至 64 个小写字母、数字和连字符,不能以连字符开头或结尾、不能连续使用两个连字符,并与父目录同名。不同宿主还可能有额外限制,实际使用时应以目标 Agent 的文档为准。
对于我们的会议纪要 Skill,Frontmatter 可以写成这样:
---
name: meeting-notes-formatter
description: 把原始会议记录整理成结构清晰的摘要,包括参会者、关键决策、行动项和后续步骤。当用户粘贴杂乱的会议记录,或者要求“整理会议记录”“清理这些笔记”“编写会议纪要”“总结会议”时使用。
---
确定 description 之后,接下来就该编写真正的执行指令了。
指令正文:Skill 触发之后应该做什么
Frontmatter 之后的内容都是普通 Markdown。在这里,我们需要告诉 Agent:Skill 被触发之后,究竟应该怎样完成任务。
可以按照下面的结构组织:
---
name: your-skill
description: [...]
---
# Skill 名称
## 操作步骤
### 第 1 步:[第一个主要步骤]
## 示例
## 常见问题
编写指令时,最重要的原则是:具体,并且可以执行。
# ❌ 过于模糊
继续之前先验证数据。
# ✅ 具体并且可以执行
运行 `python scripts/validate.py --input {filename}` 检查数据。
如果验证失败,常见问题包括:
- 缺少必填字段:在 CSV 中补充对应字段;
- 日期格式无效:统一使用 YYYY-MM-DD。
模糊的要求容易得到不一致的结果;明确、可执行的步骤,才更容易被稳定地重复执行。
还有一点很容易被忽略:最重要的指令应该放在前面,而不是藏在文件底部。
如果某个验证步骤或者必要检查非常关键,就应该让 Agent 尽早看到它。
现在,把这些原则应用到会议纪要 Skill 中。完整的指令正文如下:
# 会议纪要整理器
把粗略、缺少结构的会议记录整理成清晰、方便分享的会议摘要。
## 操作步骤
### 第 1 步:阅读原始记录
识别会议目的、参会者,以及记录中提到的大致日期。
### 第 2 步:提取四类核心信息
提取下面的内容:
- **已做出的决策:**会议最终确定了什么;
- **行动项:**由谁负责什么任务,需要在什么时候完成;
- **待解决问题:**还有哪些问题没有结论;
- **关键讨论内容:**哪些上下文值得保留。
如果原始记录中没有某类信息,保留对应章节,但不要虚构内容。
### 第 3 步:应用格式规范
生成结果之前,读取 `references/formatting-rules.md`,确认:
- 章节顺序;
- 缺失信息的处理方式;
- 行动项的格式;
- 边界情况的处理方法。
### 第 4 步:填写模板
严格按照 `assets/meeting-template.md` 中的结构生成结果,并使用从会议记录中提取的内容替换方括号中的占位符。
### 第 5 步:提交之前检查
确认:
- 每个行动项都有负责人;如果无法确定,则标记为“未分配”;
- 没有虚构任何内容;
- 不添加与会议无关的内容,同时保留已做出的决策、行动项和必要上下文。
## 示例
**用户输入:**
> “和 Sarah、Tom 讨论了产品发布。我们决定推迟到 3 月 15 日。Sarah 会在下周五之前完成新闻稿。Tom 需要和法务沟通,但还不知道什么时候完成。我们还在讨论是否把移动端功能放进 v1。”
**输出:**
# 产品发布规划会议
**参会者:**Sarah、Tom
**日期:**未说明
## 已做出的决策
- 发布日期调整为 3 月 15 日。
## 行动项
- [ ] Sarah——起草新闻稿(截止时间:下周五)
- [ ] Tom——与法务沟通(截止时间:待定)
## 待解决问题
- 是否应该在 v1 中发布移动端功能?
## 常见问题
**会议记录过于模糊,无法提取行动项。**
不要猜测。补充提示:“没有识别到明确的行动项,建议与参会者进一步确认。”
**同一段文本中包含多场会议。**
拆分之前先请用户确认,或者将每场会议分别整理成独立章节。
现在,SKILL.md 的主体已经完成了。不过从上面的内容可以看到,它还引用了两个外部文件:references/formatting-rules.md 和 assets/meeting-template.md。
这就涉及 Skill 的第三层内容:随 Skill 一起提供的资源文件。
配套文件
还记得上一篇提到的三层结构吗?
- Frontmatter 是第一层;
SKILL.md的指令正文是第二层;- 配套文件是第三层,只有工作流确实需要时才会继续读取或执行。
这些内容分别放在 Skill 的可选目录中:
references/:存放文档、规范、指南和示例。Agent 可以在工作流执行到相应步骤时读取;scripts/:存放 Python、Bash 等可执行脚本,适合需要确定性计算或文件处理的步骤;assets/:存放输出中需要使用或复制的模板、字体、图标等资源。
这里有一条很重要的规则:不要只把文件放进目录,还要在 SKILL.md 中明确告诉 Agent 何时使用它。
假设目录中存在 references/style-guide.md,但指令里从未提到这份文件,就不能指望 Agent 一定会主动发现并读取它。
# ✅ 正确:在指令中明确引用
开始写作之前,读取 `references/style-guide.md`,确认语气和格式规范。
# ❌ 不可靠:文件虽然存在,但没有任何指令引用它
(Agent 没有明确理由在工作流中读取该文件)
正因为这些文件不会在一开始全部塞进上下文,即使一个 Skill 包含许多配套资料,也可以保持相对轻量。Agent 会按照指令,在需要时继续查找相应内容。
到这里,创建一个 Skill 所需的内容已经全部准备好了。
创建的完整 Skill
我们先确定了需求和成功标准,然后建立目录,编写 Frontmatter,最后又补充了具体、可执行的指令和配套文件。
完整示例可以在下面的仓库中找到:
接下来还有一个实际问题:Skill 应该放在哪里,Agent 才能找到它?
不同 Agent 使用的目录可能不同。例如:
- Claude Code:通常使用
.claude/skills/; - OpenAI Codex:可以使用
.codex/skills/,也兼容.agents/skills/;例如,把通用 Skill 放在~/.agents/skills/,即可供当前 Codex 环境识别。具体的发现范围和优先级仍可能随 Codex 版本及运行环境而变化,应以当前官方文档为准。 - Trae:当前可以使用
.trae/skills/以及~/.trae-cn/skills/。
另外,部分 Agent 工具也会兼容其他约定的 Skill 目录;但这不是统一保证,不能假定所有工具都会扫描并加载其他 Agent 的目录。
如果希望同一份 Skill 被多个 Agent 使用,~/.agents/skills/ 是一个实用的共享位置,当前 Codex 可以识别它。对于其他 Agent,仍应查看各自文档,确认是否需要配置或链接到该目录。
当然,即便如此,这里没有一个可以无条件适用于所有 Agent 的安装目录。即使 Skill 的文件格式遵循同一套开放规范,各个宿主如何发现和加载 Skill,仍然可能不同。
因此,最可靠的做法是查看目标 Agent 的最新文档,再把 Skill 放到它实际扫描的位置。
让 Skill 真正运行起来
把 Skill 目录放到目标 Agent 能够扫描的位置之后,就可以验证它是否已经被识别。
对于会议纪要示例,可以粘贴一段杂乱的会议记录,或者输入“整理这些记录”。如果 description 能够正确匹配请求,Agent 就应该加载 SKILL.md,按照其中的步骤执行,并生成与示例结构相近的结果。
如果没有触发,可以从下面几个方面检查:
description过于模糊:Agent 无法判断什么时候应该使用。补充更具体的使用场景和用户可能输入的表达;- 名称或目录名不符合要求:为兼容 Agent Skills 开放规范,
name应与父目录同名,并使用 1 至 64 个小写字母、数字和连字符,不能以连字符开头或结尾、不能连续使用两个连字符;例如meeting-notes-formatter,不要写成Meeting_Notes_Formatter; - 文件名不正确:必须写成
SKILL.md,并注意大小写; - Skill 放错了目录:查看目标 Agent 的文档,确认它实际扫描的位置;
- Agent 还没有识别到变更:按目标 Agent 的文档重新加载或重启会话后再试。
当这一步成功之后,Skill 才算真正从一份文件变成了 Agent 可以发现并使用的工作流。
下一篇:测试 Skill
现在,我们已经有了一个可以运行的 Skill。
但它是否会在应该触发时触发,又能否在不相关的请求中保持安静?面对不同输入,它还能不能持续生成结构一致、内容可靠的结果?
这些问题,正是下一篇要解决的内容。
接下来我们将讨论:
- 测试:测量触发准确率、处理边界情况、检查输出一致性,确认 Skill 不只偶尔成功一次,而是能够持续按照预期工作;
- 扩展:分享、版本管理和团队部署,以及怎样避开常见问题。
创建 Skill 最困难的部分已经完成了。
下一篇,我们来确认它是否足够可靠。