搜索引擎里搜"AI Skill 怎么写",出来的教程大部分只教你怎么写 CLAUDE.md。好一点的教你怎么写 SKILL.md 的 description 字段和 ## 规则 部分。但真正用好 Skill 的人都知道——Skill 不是一篇 prompt,是一套工程系统。
这篇文章把这套系统拆开讲。每个模块解决一个真实的问题,每个问题都来自实际的踩坑经验。看完之后,你的 Skill 不会只是"有时候好用有时候不行",而是"每次任务稳定触发、行为可预期、越用越好"。
一、Skill 是什么,不是什么
先把概念说清楚。
你让 AI 写代码,AI 看到的全部信息就是它的上下文窗口。上下文窗口里的内容分两类:一类是始终在线的,一类是触发了才加载的。这两类各有各的入口,各有各的用途。
| 机制 | 入口 | 加载方式 | 用途 |
|---|---|---|---|
| 项目指令 | AGENTS.md / CLAUDE.md | 始终在线 | 代码规范、构建命令、项目约定 |
| 技能 | SKILL.md | 按需触发 | 专项工作流(部署、审查、测试生成) |
| 外部工具 | MCP | 按需调用 | 数据库、API、第三方服务 |
| 子智能体 | subagent | 按需派遣 | 独立子任务隔离执行 |
| 分发包 | 插件 | 按需安装 | 跨项目复用技能包 |
问题在于:很多人在"项目指令"里塞了全部东西。代码规范、部署流程、审查清单、测试模版——全扔进一个 CLAUDE.md。后果是 AI 不管做什么任务都背着一堆跟当前工作无关的信息,上下文窗口被挤满。等它真正需要记住类型约束或构建命令时,已经想不起来了。
Skill 的机制不同。它用渐进式加载,启动时只暴露 name 和 description。匹配到任务之后才读完整内容。不会白白占用上下文。这套机制在 agentskills.io 上有完整的开放标准定义,所有主流平台都遵循。

下面从零开始构建一套 Skill 工程系统。
二、三根柱子:Prompt × Context × Harness
Skill 能不能工作,不只看你写的规则好不好。三个维度同时决定了效果:
- Prompt(指令):定义 AI 做什么。写什么规则,给什么约束,输出什么格式。
- Context(上下文):决定 AI 能看到什么。你写的规则再好,如果在 AI 执行的那一刻看不到,就等于没写。
- Harness(验证层):度量改了之后变好了还是变坏了。没有这一层,你永远在猜"是不是 description 写错了"。
大多数人 90% 的时间花在 Prompt 上:反复调规则措辞、加约束、改步骤。发现不对又回去调 Prompt。
问题往往不在 Prompt。可能规则写了但 Context 太挤被挤掉了。可能 Agent 跨多轮任务时走了错误的路由分支。可能 /compact 压缩后关键指令消失了。可能换了一个工具入口(Claude Code → Cursor)根本没有读到同一份 Skill。
Harness 就是用来把这些变成"有据可查"的东西。
三、description:只有一次机会
SKILL.md 的 frontmatter:
---
name: code-review
description: 代码审查工具
---
这是最容易被写废的一行。
AI 启动时扫描所有 Skill,记住它们的 name 和 description。用户说话之后,用说的话去和每条 description 做匹配。匹配上了,去读完整的 SKILL.md。没匹配上,Skill 永远不会被加载。
“代码审查工具"是分类标题,不是触发条件。用户说"帮我看看这段代码”,AI 不会把"看看"理解成"审查"。跨度太大。
agentskills.io 的原话:“The description carries the entire burden of triggering. If the description doesn’t convey when the skill is useful, the agent won’t know to reach for it.”
改为:
description: >
代码审查。当用户要求"review 代码"、"审查"、"检查代码"、
"看看有没有问题"、"过一遍代码"、"code review"时激活。
多列具体说法,覆盖用户怎么说,而不是你想它怎么说。
还有一个细节:Skill 列表只能占模型上下文的大约 2%。如果列表太长,后面的 description 可能被截断。所以最关键的触发词必须在最前面。
反面案例:过度宽泛导致误触发
触发词不是越多越好。如果你写的 description 是"当用户提到代码、质量、检查、review、审查、查看、分析、改 bug、优化、重构时激活",AI 在大量无关场景下也会触发这个 Skill。用户说"帮我检查一下网络配置"命中"检查",说"审查一下账户权限"命中了"审查"。这些都不是代码审查,但 Skill 被激活了。
agentskills.io 把这类叫"near-misses"——共享了关键词但不需要这个 Skill 的场景。测试时不仅要测"该触发时触发了吗",还要测"不该触发时有没有误触发"。

显式调用模式
有些 Skill 只应该在用户明确说的时候才激活,比如部署。可以在 SKILL.md 旁边加 agents/openai.yaml:
policy:
allow_implicit_invocation: false
设成 false 后,只有 $skill-name(Codex CLI)或 /skill-name(Claude Code)的显式调用才会激活。
四、解剖一个 SKILL.md
SKILL.md 不是百科全书。它是一张路由表,告诉 AI “什么情况读什么文件”。
一个完整的 SKILL.md 有四个模块:
---
name: code-review
description: >
代码审查。当用户要求审查代码、检查代码质量、
检查变更、review PR 时激活。
primary: true
---
## Always Read
每次任务必须读以下文件:
1. references/checklist-base.md
2. rules/coding-standards.md
## Session Discipline
每个新任务——即使是同一会话的第 N 轮——必须重读本文件,
重新匹配 Task Routing,重读该路由列出的所有必读文件。
"I already read it" 不是有效的跳过理由。
## Task Routing
根据任务类型读对应文件:
- 前端代码 → references/frontend-checklist.md
- 后端代码 → references/backend-checklist.md
- 部署脚本 → references/deploy-rules.md
- 其他任务 → 只读 Always Read
## Known Gotchas
- Filter 必须在 app init 之前注册,否则首次渲染空白
→ see references/gotchas.md#filter-registration
- 弹窗 Tabs + service 只打首层接口
→ see references/gotchas.md#nested-service-tabs
逐个板块说。
Always Read:放任何任务都必须遵守的基础约束,控制在 2-3 个文件。领域特定规则不放在这里,交给 Task Routing 按需加载。
Session Discipline:这是很多人忽略的一个要点。同一会话里 AI 处理了多轮任务,第一轮读了 SKILL.md,第二轮就凭记忆干了。但第二轮的任务类型可能完全不同,需要的路由文件也不一样。AI 的记忆——尤其是在上下文压缩之后——靠不住。Session Discipline 强制每个新任务走一遍完整路由。“I already read it” 被显式声明为无效理由。
Task Routing:5-10 条规则,每条必须带精确的文件路径。最后必须有兜底规则(“其他任务 → 只读 Always Read”),否则 AI 在路由表里找不到匹配时会随机行动。
Known Gotchas:价值密度最高的板块。只放坑点的一句话摘要 + 锚点,详细说明放 references。为什么不能全量放?——SKILL.md 是路由表,不是坑点百科,太长会稀释注意力。为什么不能只放 references?——只放 references 的话,AI 在任务路径上永远不会"碰巧"看到这些坑,除非触发了对应路由。
五、三层渐进式加载
全部内容堆在一个文件里,AI 读到后面的能力越来越差。这是 Transformer 注意力机制决定的,不是幻觉。
渐进式加载分三层:
第一层:name + description。 AI 启动时只加载这个。用于判断"要不要激活这个 Skill"。
第二层:SKILL.md 正文。 被激活后才加载。Always Read + Session Discipline + Task Routing + Known Gotchas。路由表和关键摘要。
第三层:references/ 和 rules/。 在 Task Routing 里被明确引用了才加载。领域检查清单、部署规则、详细 gotcha 说明。

典型目录结构:
code-review/
├── SKILL.md
├── references/
│ ├── frontend-checklist.md
│ ├── backend-checklist.md
│ └── gotchas.md
├── rules/
│ └── coding-standards.md
└── scripts/
└── check-secrets.sh
scripts/ 的使用条件: 只在需要确定性行为或外部依赖时才用。一个 shell 脚本扫描代码里的 API key 泄露,每次输出一致,适合 scripts。格式校验和检查清单这种需要模型判断的东西,留给指令。
agentskills.io 有个重要提醒:Skill 引用了一个工具但当前平台没有这个工具,它会静默失败——不报错,用散文替代。如果 Skill 要跨平台(Claude Code + Cursor + Codex CLI),scripts/ 里依赖的工具必须在每个平台上都可用。
六、Session 纪律:为什么读到的东西会消失
先看一个真实场景:
第 1 轮:用户说"帮我修一下 UserService 里的空指针 bug"
→ AI 读 SKILL.md
→ 匹配 Task Routing 的 "修 bug" → 读 rules/fix-bug.md
→ 按 workflow 修好
第 4 轮:用户说"顺便加个导出 Excel 的接口"
→ AI 觉得"我知道这个项目的规则了"
→ 跳过 SKILL.md
→ 直接开写 Controller
但是:
- "加导出接口"实际匹配的是 Task Routing 里的 "加 Controller"
- 对应的 rules/backend-rules.md 里有一条:"导出必须走 async 队列,同步响应会超时"
- AI 没读到这条 → 写了同步导出
- 小数据量测试通过,生产数据量一大直接超时
问题根源不是规则没写。规则一直在那里。问题是 AI 跨任务时没重走路由。
三个原因叠加导致失效:
- 跨任务记忆污染:第 1 轮的路由结果被当成"所有任务都该这么走"。
- 上下文压缩:/compact 触发后,SKILL.md 被压缩成摘要,文件路径信息丢失。
- 平台差异:某些工具在长会话里会丢弃早期系统指令。
解法:Section Discipline 必须在 SKILL.md 里显式声明,不能只靠"记得这件事"。同时还需要在下一步的薄壳里再加一层冗余——因为 /compact 甚至可能把 SKILL.md 本身的 Session Discipline 也压掉。

七、薄壳:跨工具兼容的最后防线
一个 Skill 要在 Claude Code、Cursor、Codex CLI、Gemini CLI 里都能用。最笨的办法是把 SKILL.md 复制四份,各自放在对应工具的目录下。但这样改了任何一条规则都要同步四个地方,早晚会漂移。
解法是在每个工具的入口文件里放一层薄壳。薄壳不包含完整规则,只包含三样东西:路由表、自动触发器列表、Red Flags 拦截信号。
以 Claude Code 为例,项目根目录的 CLAUDE.md(薄壳):
# CLAUDE.md
Formal docs live under skills/. Read skills/*/SKILL.md — default to
primary: true skill; only switch when task clearly matches another.
## Quick Routing (survives context truncation)
| Task | Required reads | Workflow |
|------|---------------|----------|
| Fix bug | rules/project-rules.md + rules/coding-standards.md | workflows/fix-bug.md |
| Add API endpoint | rules/backend-rules.md | workflows/add-controller.md |
| Multi-subtask (≥3 independent) | rules/project-rules.md | workflows/subagent-driven.md |
| Other | rules/project-rules.md + rules/coding-standards.md | Check workflows/ for closest match |
## Auto-Triggers
- New task in same session → re-read skills/*/SKILL.md, re-match Task Routing,
re-read all required files. "I already read it" is not valid.
- Before declaring any non-trivial task complete → run Task Closure Protocol.
- Skip only for: formatting-only, comment-only, dependency-version-only,
behavior-preserving refactors.
## Red Flags — STOP
- "Just this once I'll skip the AAR" → stop
- Task declared "complete" without running 30-second post-task scan → stop
- Same class of bug fixed twice but rules not updated → stop
薄壳设计的核心逻辑:不写"去读 SKILL.md",因为 /compact 之后自然语言指令会被当成普通描述丢弃。但结构化表格(路由表)、清单(Auto-Triggers)、前置拦截(Red Flags)在压缩后留存的概率更高。Agent 拿到新任务时可以当场查表。
反例——常见的错误写法:
# CLAUDE.md
Please read skills/my-skill/SKILL.md before starting any task.
It has all the rules and workflows you need.
短会话里能用。长会话 /compact 后,这行字被摘要掉了。Agent 看到新任务,没有路由表可查,凭感觉动手。输出看起来合理,只是少了几条关键约束。用户察觉不到,直到出 bug。
各工具的薄壳入口:
| 工具 | 薄壳文件 |
|---|---|
| Claude Code | 项目根 CLAUDE.md |
| Cursor | .cursor/rules/workflow.mdc + .cursor/skills/{name}/SKILL.md |
| Codex CLI | 项目根 AGENTS.md + .codex/instructions.md |
| Gemini CLI | 项目根 GEMINI.md |
每个工具的薄壳格式略有不同,但内容结构相同:Quick Routing + Auto-Triggers + Red Flags。

八、对抗上下文压缩:SessionStart Hook
薄壳扛住了 /compact 后的场景,但 /clear 直接把上下文擦干净了,薄壳也得从磁盘重读。
可以在 Claude Code / Cursor 里配置 SessionStart hook:
#!/bin/bash
# session-start.sh — 在 startup / clear / compact 时自动重注 SKILL.md
skill_md=$(find skills/*/SKILL.md | head -1)
if [ -z "$skill_md" ]; then exit 0; fi
content=$(jq -Rs '.' "$skill_md")
echo "{\"prompt\": $content}" # Claude Code 特定的注入格式
这个脚本在三个事件触发:startup(新会话)、clear(用户清上下文)、compact(自动压缩)。每次触发时自动读取 SKILL.md 并注回上下文。
Hook 不是万能的。它只负责把 SKILL.md 塞回去,不保证 Agent 会认真读。也不保证 Agent 按照 Task Routing 走了正确分支。那是 Session Discipline 和薄壳 Routing Table 的职责。
三者分工:
- Session Discipline(SKILL.md 里):跨任务时的重读触发逻辑
- 薄壳 Routing Table(CLAUDE.md 等入口里):压缩后仍可查的路由兜底
- SessionStart hook:清空/压缩后自动把 SKILL.md 重新加载
三层叠加才能扛住长会话 + 多任务 + 多次 compact 的真实工作流。
九、任务闭环:做完不意味着做完了
AI 经常把"代码写完 + 测试跑通"当成任务结束。但真正的结束还差一步:扫一遍刚才的工作,有没有踩到新坑、发现新规则、暴露已有规则的漏洞。
完整闭环包含四个步骤:
步骤一:AAR 扫描(30 秒)
每完成一个任务,回答四个问题:
- 用到了没有记录的 pattern 或约定?
- 碰到了不提前知道就会浪费大量时间的陷阱?
- 因为缺少某条规则导致了弯路?
- 已有规则已经不准确了吗?
任何一个答案是"是",就往下走。全部"否",到此结束。
步骤二:录入标准(2/3 门槛)
不是所有发现都值得记。三条过滤标准:
- 可重复吗?(换个时间、换个人,还会再踩吗)
- 代价高吗?(踩一次导致的调试时间 ≥ 30 分钟 或 影响了生产)
- 代码不可见吗?(从代码本身看不出,依赖时序、配置或暗知识)
至少 2/3 通过才录入。
一个通过的例子:“Filter 必须在 app init 之前注册,否则首次渲染空白”。可重复(每个新页面都可能犯),代价高(30 分钟以上的调试),代码不可见(时序依赖从静态代码看不出来)。3/3,录入。
一个不通过的例子:“Atom 命名约定用 xxxAtom 后缀”。可重复(是),代价不高(命名不一致不会造成 bug),代码可见(已有 atom 已经展示了模式)。1/3,不录入。
步骤三:录入后激活
光记下来不够。必须在 Agent 下次走正常任务路径时会自然"撞见"它:
- 高代价陷阱 → 同时出现在 SKILL.md Known Gotchas 和对应 routing 的 workflow 里
- 新增规则 → 更新对应 rules/ 文件,确保 Task Routing 能路由到
- 宽泛教训 → 泛化成通用描述,加进 references/
判断标准:下次 Agent 为类似任务走 Task Routing 时,能读到这一条吗?不能 => 只是"存了",还没"生效"。
步骤四:Red Flags 前置拦截
以下情况出现时立刻停下:
- 发现自己在想"这次 AAR 就算了"
- 任务声明完成但没跑 30 秒扫描
- 把坑写进了 reference 但没更新对应的 routing
- 同一类 bug 修了两次但规则文件没变化
Red Flags 必须同时出现在薄壳里,因为 workflow 文件在压缩后会丢失,薄壳是最后一道防线。

十、不要让 Skill 变成日记本
Agent 在执行 AAR 时容易过度解读"记录"——把整个会话存档成一个 markdown 文件,扔进 references/。一个月下来,references/ 下出现了 2026-04-14-session-notes.md、2026-04-15-debugging-log.md 等十多份同质文件。
这种文件会毁掉 Skill 的可维护性。它不是规则、不是工作流、不是可复用知识——它是项目叙事。
位置判断表:
| 内容类型 | 目标位置 |
|---|---|
| 稳定约束 / 通用规则 | rules/ |
| 陷阱、架构坑、生命周期依赖 | references/ |
| 有序步骤 / 检查清单 | workflows/ |
| 会话历史 / 调试过程 | 不写进 Skill |
如果确实需要会话日志,放在 docs/ 而不是 references/。Skill 不是 git 的替代品。
十一、怎么测
AAR 回答了"这次任务学到了什么"。但 Skill 本身怎么测?
测触发率。 每次改 description 之后,开新对话,逐条输入准备的目标语句。记录激活次数。一半以上没激活,回去改。
脚本化更好——写一个 test-trigger.sh,从 Task Routing 的每个条目自动生成用户可能说的 prompt,批量跑验证。这样每次改完 description 之后跑一下就知道是不是变好了。
测结构中低级错误。 写一个 smoke-test.sh,自动检查:
- Task Routing 里引用的每个文件是否真的存在
- SKILL.md 的 description 和所有薄壳入口的 description 是否一致
{{NAME}}之类的占位符是否还有残留- SKILL.md 行数是否超标
- 每个薄壳入口文件是否都包含 Quick Routing 表
这些检查不需要 AI。一个 shell 脚本就能做完。80% 的翻车来自路径写错、文件没建、入口不一致这类遗忘型问题。
测真实任务。 用自己项目的实际代码跑一次完整的 Skill 执行。肉眼过输出,看 AI 遵守了每一条 checklist 没有,有没有编造规则,有没有绕过步骤。
脚本能抓遗忘,真实任务能抓理解偏差。两层都过,Skill 才算验收合格。
十二、一个 Skill 只做一件事
Skill 用久了会膨胀。description 列了 10 多个互不相关的触发词,common tasks 有 15 条以上,gotchas 文件按领域自然分成两半。
这时候该拆了。
拆之前确认三个信号:两个领域的 Task Routing 完全没有交集,description 要覆盖的场景已经跨领域了,坑点文件按领域已经自然分裂。三个信号任意一个出现,拆。
拆分后每个 Skill 的 SKILL.md 各自控制在 ≤100 行,description 各自精准。AI 激活的是"正好对应当前任务的那一个",而不是"什么都管的那个"。
多 Skill 共存时,primary: true 标记默认 Skill。SessionStart hook 自动读 primary Skill 的 SKILL.md。跨 Skill 共享的通用规则放在 skills/shared/ 下,各 Skill 的 Always Read 指向它。
十三、不要用 LLM 直接生成整个 Skill
用 ChatGPT 生成 Skill 是 agentskills.io 明确标记的常见反模式。原话:“A common pitfall is asking an LLM to generate a skill without providing domain-specific context — relying solely on the LLM’s general training knowledge. The result is vague, generic procedures.”
生成出来的结果通常是这样的:
## 代码审查要点
- 确保代码遵循行业最佳实践
- 检查常见安全漏洞
- 关注性能瓶颈
每一条都是正确的,每一条都没用。“行业最佳实践"是哪个行业?“常见安全漏洞"具体指什么?“关注性能瓶颈"怎么关注?
正确的做法:先用一个 Skill 只覆盖你项目中真实踩过的 3-5 个坑。比如上周三次都是 N+1 查询导致超时——“检查 N+1"写进 checklist。跑了几天输出稳定了,再加从 AAR 里淘出的新条目。Skill 的规则只能来自真实经验,不能来自"我猜 AI 应该检查这些”。
构筑一个能用的 Skill
从零开始做一个 code-review Skill。按上面的每个模块来。
目录结构:
.agents/skills/code-review/
├── SKILL.md
├── agents/
│ └── openai.yaml
├── references/
│ ├── checklist-base.md
│ ├── frontend-checklist.md
│ └── gotchas.md
├── rules/
│ └── coding-standards.md
└── scripts/
└── smoke-test.sh
SKILL.md:
---
name: code-review
description: >
代码审查。当用户要求审查代码、检查代码质量、
检查变更、review PR 时激活。触发词包括"review"、
"审查"、"code review"、"检查代码"、"过一遍"、"帮我看看"。
primary: true
---
## Always Read
1. references/checklist-base.md
2. rules/coding-standards.md
## Session Discipline
每个新任务必须重读本文件、重新匹配 Task Routing、
重读该路由列出的所有文件。"I already read it" 不是有效理由。
## Task Routing
- 前端代码 → references/frontend-checklist.md
- 后端代码 → references/backend-checklist.md(TODO:建立后解注释)
- 部署脚本 → references/deploy-rules.md(TODO:建立后启用)
- 其他任务 → 只读 Always Read
## Known Gotchas
- 跨层交互:弹窗 Tabs + service 只打首层接口
→ see references/gotchas.md#nested-service-tabs
## 执行流程
1. 读 references/checklist-base.md
2. 根据代码类型,通过 Task Routing 读对应领域检查文件
3. 逐条检查,输出每个问题:
- 严重程度:高(必然出 bug)/ 中(特定条件下出 bug)/ 低(代码异味)
- 涉及文件 + 行号
- 修改建议(一两句话)
- 对应 checklist 编号
4. 所有问题检查完毕,给出总体评价
5. 有"高"严重级别问题时标注"建议修复后合并"
## 约束
- 不编造本文件里不存在的检查项
- 某条不适用于当前代码时注明"不适用"并说明原因
- 输出中文,术语保留英文
references/checklist-base.md:
1. 类型安全:新代码有类型标注,不允许 implicit any
2. 重复代码:无连续三行以上重复代码(框架模板除外)
3. 错误处理:覆盖超时、空数据、鉴权失败、第三方服务异常
4. 日志:关键路径记录入参/出参/耗时,不记敏感信息
5. 输入校验:所有用户输入经过校验或转义
6. 权限:敏感操作检查用户权限
7. N+1:循环内无数据库查询
8. 分页:大列表查询带分页或游标,有总数上限
9. 配置:硬编码字符串用常量或配置文件代替
10. 测试:新增逻辑有对应单元测试
薄壳(CLAUDE.md):
# CLAUDE.md
Formal docs under skills/. Read skills/code-review/SKILL.md for
all code review tasks.
## Quick Routing (survives context truncation)
| Task | Required reads | Check against |
|------|---------------|---------------|
| Code review | references/checklist-base.md + coding-standards.md | SKILL.md checklist |
| Other tasks | — | Default project rules only |
## Auto-Triggers
- New task in same session → re-read SKILL.md, re-match Task Routing.
- Post-review → 30s AAR: new patterns? new traps? missing rules? stale rules?
## Red Flags — STOP
- "This review looks fine, I'll skip the detailed check" → stop
- Skipping AAR after review → stop
跑一遍验证:
grep -rn "FILL:" skills/code-review/确认没有遗留占位符test-trigger.sh code-review验证触发率smoke-test.sh code-review验证结构完整性- 用一段真实代码跑一次完整审查
- 看输出,把漏掉的检查加进 checklist
后续每发现一个新坑,走 30 秒 AAR → 通过 2/3 门槛 → 更新对应文件 → 确认在 Task Routing 路径上可见。Skill 不是一次写好的,是被失败喂大的。
想跟着学更多 AI 编程实战?关注公众号"全栈之巅-梦兽编程”,每周更新从工具配置到工程落地的完整内容。

也可了解 梦兽编程 AI 编程助手服务 ,帮你把 AI 编程工具用到生产环境。
FAQ
AI Skill 和普通提示词有什么区别?
提示词是一次性指令;Skill 是一套工程系统:SKILL.md 按需触发加载、description 决定触发时机、指令分层渐进加载、跨工具兼容(Claude Code/Codex CLI/Cursor),并可测试和持续演进。
SKILL.md 的 description 为什么重要?
description 是模型判断何时加载该 Skill 的唯一依据,只有一次机会。它应写明触发场景、具体能力和边界条件,避免描述过宽导致误触发、过窄导致不触发。
Skill、AGENTS.md 和 MCP 是什么关系?
三者是上下文的三种入口:AGENTS.md/CLAUDE.md 始终在线(项目规范与命令),SKILL.md 按需触发(专项工作流),MCP 按需调用(外部工具与数据)。Skill 解决工作流问题,MCP 解决连接问题。