如何写好一个 Skill?
如果只从表面看,Skill 很容易被理解成更长的 Prompt。但实际用下来,Skill 更像是给 Agent 装上的一块可复用能力模块:它告诉模型什么时候应该使用这套方法、具体该怎么执行、执行到什么程度算完成、如何验证自己做对了。
因此,写好一个 Skill 的关键,不是把经验全部塞进 SKILL.md,而是把一类任务中真正会影响结果的知识,组织成 Agent 能稳定调用的工作流。
暂时无法在飞书文档外展示此内容
Skill 是什么?
在 Agent Skill 的定义里,一个 Skill 本质上是一个文件夹,里面至少包含一个 SKILL.md 文件。SKILL.md 里会写元信息,比如 name 和 description,也会写具体的执行说明。除此之外,一个 Skill 还可以附带脚本、参考文档、资产文件等资源。
一个典型结构大概是这样:
my-skill/
├── SKILL.md
├── scripts/
├── references/
└── assets/
这个结构背后有一个很重要的设计思想:渐进式披露。
Agent 启动时不会把所有 Skill 的完整内容都塞进上下文,而是只读取每个 Skill 的 name 和 description。当用户任务匹配某个 Skill 的描述时,Agent 才会加载完整的 SKILL.md。如果执行过程中还需要更详细的资料,再按需读取 references/、assets/ 或执行 scripts/ 里的内容。
所以,Skill 的第一个门槛不是“内容写得多不多”,而是:它能不能在正确的时机被触发。
description 是触发器,不是简介
Skill 写得再好,触发不了等于零。Agent 只在启动时加载每个 Skill 的 name 和 description,靠这两行决定"要不要把这个 Skill 拉进来"。描述写不好,Skill 永远不会被调用。
很多人会把 description 写成 Skill 简介:
description: 这个 Skill 用于代码 Review。
这类描述太弱了。它只说了 Skill 是什么,却没有告诉 Agent 什么时候该用它。 更好的写法是把它写成触发条件:
description: 当用户要求 review 当前改动、检查 PR diff、评估实现风险、发现潜在 bug、指出缺失测试、确认行为是否符合需求或给出可执行修改建议时使用本 Skill。
一个好的 description 通常有几个特点:
- 使用“当用户……”这类面向 Agent 决策的表达。
- 描述用户意图,而不是描述内部实现。
- 明确哪些场景应该触发,必要时也说明哪些场景不该触发。
- 足够具体,但不要写成一篇小作文。
这里最容易犯的错误有两个。
第一个是过窄。比如只写“用于检查 PR 代码”,结果用户说“这次改动会不会引入回归”“这个分支能不能合并”时,Skill 可能不会触发。
第二个是过宽。比如写“任何需要代码审查的时候就使用”,结果用户只是想让 Agent 补一个函数、改个代码逻辑或案时,也可能误加载代码评审 Skill,浪费上下文。
因此,description 本质上不是摘要,而是触发条件。
SKILL.md 要写流程,不要堆原则
一个 Skill 被触发后,Agent 会读取完整的 SKILL.md。这时最重要的是让 Agent 能直接行动。
不太好的写法是:
## 指导原则
- 谨慎处理。
- 遵循项目既有最佳实践。
- 确认结果正确。
- 使用合适的工具。
这些话太抽象,Agent 很难据此改变行为。 更好的写法是:
## 工作流程
1. 检查输入文件格式,识别表头、分隔符、编码和行数。
2. 如果文件大于 50MB,使用 `scripts/stream_csv.py` 中的流式解析器。
3. 使用 `references/column_aliases.md` 中的映射关系规范化列名。
4. 将转换结果写入新文件,禁止覆盖原始输入。
5. 在报告完成前运行 `scripts/validate_output.py <输出文件>`。
这类内容能直接改变 Agent 的行为,因为它给出了顺序、默认工具、边界条件和验证方式。
写 SKILL.md 时,可以优先考虑这几类内容。
1. 易错点
易错点是 Skill 里最值得写的部分,也就是那些“不写明,模型就容易猜错”的事情。 例如:
- 导出文件中的 `account_id` 字段对应 API 响应中的 `uid`。
- 不要使用 `/health` 验证数据库是否就绪,应使用 `/ready`。
- 不要覆盖源文件,始终写入 `outputs/` 目录。
易错点不需要长篇解释,关键是具体、可执行、能阻止常见错误。这块内容需要持续迭代。 所以 skill 不是写完就完了,而是一个随着使用不断进化的活文档。
2. 写方法论而不是具体答案
Skill 是教 Agent 怎么解决一类问题,不是给某个具体问题的答案。例如一个日志排查 skill:
- 反例:去
pod-123这台机器 grep Panic,把 14:22 那条stack trace贴出来。—— 换个日志就作废。 - 正例:
- 从告警里提取 实例名 + 时间窗 + 错误关键词;
- 在时间窗口 ±5 分钟内 grep 错误关键词,按实例聚合;
- 输出 “受影响实例数 / 首次出现时间 / 代表性 stack trace” 三栏摘要。
后者任何分析查询都能复用。
3. 输出模板
如果任务对输出格式有要求,直接给模板,不要只用自然语言描述。 例如:
按以下结构返回结果:
```text
摘要:
[一段话概括]
发现:
- [发现 1]
- [发现 2]
下一步:
1. [行动项]
2. [行动项]
```
模型对具体样例和模板的遵循通常比抽象描述更稳定。
4. 验证闭环
一个好的 Skill 不应该只告诉 Agent 怎么做,还应该告诉它怎么确认自己做对了。 例如:
生成输出后:
1. 运行 `scripts/validate.py outputs/result.json`。
2. 如果验证失败,阅读错误信息并修正输出。
3. 重复执行,直到验证通过。
4. 只有在验证通过后,才能报告任务完成。
从真实任务里提炼 Skill
写 Skill 最好的原材料,不是凭空让模型生成一份“最佳实践”,而是从真实任务里提炼。 一个常见的过程是:
- 先让 Agent 完成一类真实任务。
- 在过程中观察它哪里走偏、哪里需要人工纠正。
- 让 Agent 把成功路径、必要约束、反复出现的纠正点提炼出来。
- 再使用 skill-creator 写成 Skill,并用新的任务验证它是否真的减少了错误。
比如我们想沉淀一个代码 Review 的 Skill。 不要新建对话直接写“/skill-creator 请帮我生成一个代码评审的 Skill”。这样生成 skill 通常质量不高,输出格式也无法满足自己的诉求。 更好的方式是,先让 Agent 先真实 review 几个 PR 或 diff。 第一次 review 时,Agent 可能只会泛泛地说:
- 代码结构还可以
- 建议补充测试
- 注意异常处理
然后我们在任务过程中不断纠正它:
- “不要先总结,先列具体风险。”
- “每个问题都要带文件路径和行号。”
- “每个问题都需要按照 Critical、Medium、Low 进行优先级排序”
- …
直到 Agent 的输出结果符合我们预期后,我们就可以通过 /skill-creator 根据当前会话上下文创建一个 skill。
控制上下文
Skill 有一个很容易被忽略的成本:上下文占用。
一旦 Skill 被触发,SKILL.md 会进入 Agent 的上下文窗口。里面每多一段无关内容,都会和用户当前任务的上下文竞争模型的注意力。
- 写 Agent 不知道的东西:项目特有的约定、领域工作流、非显然的边界情况、特定工具的用法。
- 不要解释常识:HTTP 怎么工作、Protobuf 协议是 xxx、什么时候用Redis什么时候用MySQL。
- 大型 Skill 用渐进式披露:
SKILL.md应该保持克制,SKILL.md只放每次执行都会用到的内容。详细参考材料(API 文档、错误码表)放进references/,并在主文件里写清什么时候去读它,例如当 API 返回非 200 时读references/api-errors.md。
好的写法是:
仅当 API 返回非 200 响应时,读取 `references/api-errors.md`。
当用户要求正式报告时,使用 `assets/report-template.md`。
不好的写法是:
更多细节见 `references/`。
后者的问题是触发条件不明确。Agent 不知道什么时候该读,也不知道读哪一个文件,模型大概率会把所有文件都读一遍,即使无关内容也都会全塞进上下文。
什么时候需要脚本?
Skill 不一定要带脚本,但当你发现 Agent 每次都在重复做同一类机械工作时,就应该考虑把它固化为脚本。
适合写进 scripts/ 的场景包括:
- 把Markdown转成PDF。
- 校验输出 JSON 是否符合诉求。
- 通过外部 API 获取相关数据。(如天气预报 skill 获取天气信息)
脚本的价值在于将模型不擅长的机械一致性工作交给确定性程序。 一个经验判断是:如果你希望 Agent 每次都按完全相同的规则判断某件事,那么它大概率应该是脚本,而不是自然语言说明。
Skill 的评测
Skill 写完后,如果直接拿用例测试一遍跑通了,就以为 Skill 可用了。但 Skill 的目标是稳定改善一类任务,所以至少要评估两件事。
- Skill 是否能在该触发的时候触发,不该触发的时候不触发。
可以准备一组触发测试:
[
{
"query": "帮我清洗这个 sales.csv,并输出收入最高的 3 个月",
"should_trigger": true
},
{
"query": "CSV 是什么格式?",
"should_trigger": false
}
]
由于模型行为存在随机性,同一条 query 最好跑多次,观察触发率,而不是只看单次结果。
- Skill 是否真的改善了输出质量。
更可靠的评估方式是做对照实验:
- 不使用 Skill 跑一遍,作为基线。
- 使用 Skill 跑一遍。
- 对比输出质量、耗时、token 成本和错误率。
评估时不要只写“输出要更好”。应该把成功条件拆成可检查的断言:
{
"assertions": [
"输出内容是 JSON",
"输出内容至少包含 3 条候选建议",
"必须有一条最推荐的建议并说明理由"
]
}
以上的测试流程不需要我们自己编写和操作,通过 A\ 的 skill-creator 这个 skill 就能完成。
总结
写好一个 Skill,核心不是“把提示词写长”,而是把真实任务中的经验压缩成 Agent 能调用的能力模块。 一个好的 Skill 至少要做到四件事:
- 触发准确:
description能让 Agent 在正确场景加载它。 - 流程清晰:
SKILL.md给出可执行步骤,而不是泛泛原则。 - 上下文克制:遵循渐进式披露,核心说明短而准,长资料按需加载。
- 经过评测:通过触发测试和输出质量测试证明它真的有用。