本教程面向已经会用 Claude Code 跑日常编码任务、但还没写过自定义 Skill 的开发者。我们将从一张空白 SKILL.md 开始,完整走通「写描述 → 编写工作流 → 拆分参考文件 → 触发测试 → 评估打磨」的全流程,最终产出一个数据库迁移安全守卫 Skill:它能在你提交 ALTER TABLE、DROP COLUMN、批量回填等高风险 SQL 时自动介入,按破坏性等级给出审查意见与锁表风险评估。学完后你将掌握 Skill 的三层渐进式披露结构、自动触发描述的写法,以及如何用 Evals 闭环验证一个 Skill 是否真的「该出手时才出手」。
很多人把 Skill 想得太重。一句话定义:Skill 是一个带 YAML 头的 Markdown 文件(SKILL.md),里面写着「什么时候用我、按什么流程做」,Claude 会按需加载它。
它和另外两个常被混淆的概念的区别:
| 概念 | 加载时机 | 适合放什么 |
|---|---|---|
CLAUDE.md |
每个会话都全量加载 | 项目级常驻规则、技术栈约定 |
Skill(SKILL.md) |
需要时才加载(描述命中用户意图) | 可复用的专项工作流 |
| Subagent / MCP | 显式调用或工具发现 | 独立上下文 / 外部数据源 |
关键认知:Skill 的正文不会一直占用上下文。Claude Code 启动时只读取每个 Skill 的 frontmatter(描述部分),只有当判断「这次任务和某个 Skill 相关」时,才会把该 Skill 的完整正文加载进来。这就是官方文档反复强调的 渐进式披露(Progressive Disclosure)。
第一层:frontmatter 的 description ← 永远在上下文里(要短、要准)
第二层:SKILL.md 正文 ← 命中后才加载(决策流程、关键规则)
第三层:同目录的参考文件(references/) ← 用到才读取(完整清单、模板、样例)
理解这三层,是写好 Skill 的全部秘密。下面我们直接动手。
Skill 可以放在三个层级,加载优先级从高到低:
<项目>/.claude/skills/<skill-name>/SKILL.md——只在当前项目生效~/.claude/skills/<skill-name>/SKILL.md——对你所有项目生效我们的「迁移安全守卫」和具体项目耦合(要识别项目里的迁移文件),放项目级最合适。
先建好目录骨架:
mkdir -p .claude/skills/migration-guardian/references
cd .claude/skills/migration-guardian
最终的目录结构会是:
.claude/skills/migration-guardian/
├── SKILL.md # 第二层:决策流程 + 关键规则
└── references/
├── destructive-patterns.md # 第三层:完整破坏性模式清单
├── lock-risk-matrix.md # 第三层:锁表风险矩阵
└── rollback-template.md # 第三层:回滚脚本模板
SKILL.md 的开头是 YAML frontmatter,用三连横线包裹。这是整个 Skill 最该反复打磨的部分——因为它是唯一永远在上下文里的内容,写不好 Skill 就永远不被触发(或被错误触发)。
| 字段 | 必填 | 作用 |
|---|---|---|
name |
是 | Skill 唯一标识,全小写连字符,同时也是斜杠命令 /migration-guardian |
description |
是 | 触发依据。写明「这个 Skill 做什么、什么场景该用它」 |
allowed-tools(或 allowed_tools) |
否 | 限制该 Skill 可调用的工具白名单,收敛权限 |
metadata |
否 | 任意键值,便于组织/检索 |
来自社区对 650 次会话的实测:很多 Skill 不触发,根因就是 description 写成了「名词介绍」而非「触发条件」。
---
name: migration-guardian
description: Reviews database migration files for destructive or lock-heavy DDL before they are applied. Use when the user edits, stages, or asks about migration files (*.sql, Alembic, Rails migrations, Flyway, Liquibase, Prisma migrate, Knex) — especially operations like DROP TABLE/COLUMN, ALTER TABLE, TRUNCATE, renaming columns, NOT NULL without default, large backfills, or creating indexes without CONCURRENTLY. Do NOT use for seed data, fixture generation, or application-level SQL queries in code.
allowed-tools: Read, Grep, Glob, Bash
---
注意这 30 行字里塞进了多少触发信号:文件后缀(*.sql、Alembic、Rails、Prisma……)、具体危险操作(DROP、ALTER、TRUNCATE……)、边界排除(seed、fixture、应用层查询)。这一段写得越具体,自动触发越准。
常见问题:我的 Skill 写完了但从不自动触发? 排查:90% 是 description 缺少用户会实际输入的词。把你的描述读给同事听,问他「你会这样描述需求吗」。
正文只在 Skill 被触发后加载。这里要给 Claude 一个清晰、可执行的决策流程,而不是泛泛而谈。一个好的 Skill 正文像一份「带判断分支的检查清单」。
Scan ...、Classify ...、Report ...,不要寒暄# 数据库迁移安全守卫
你是一个数据库迁移审查助手。目标是:在迁移被应用到生产前,识别破坏性操作和高锁表风险,并给出可执行的缓解建议。
## 工作流程
1. **定位迁移文件**
- 优先审查用户当前正在编辑 / 已 `git add` 的迁移文件。
- 若用户未指定,用 Glob 找到最近的迁移目录(`db/migrate/`、`alembic/versions/`、`prisma/migrations/`、`migrations/`、`src/main/resources/db/changelog/` 等)。
2. **识别 DDL 类型**(用 Grep 扫描关键字,忽略大小写):
`DROP`, `TRUNCATE`, `ALTER`, `RENAME`, `MODIFY`, `ADD COLUMN`, `CREATE INDEX`, `NOT NULL`
3. **按破坏性等级分类**,参考 `references/destructive-patterns.md` 获取完整清单:
- 🟥 **高危**:DROP TABLE/COLUMN、TRUNCATE、重命名列/表、删约束
- 🟧 **中危**:NOT NULL 无默认值、加唯一索引、改列类型
- 🟨 **低危但有锁风险**:普通 CREATE INDEX(无 CONCURRENTLY)、大表 ADD COLUMN + 回填
4. **评估锁表风险**
- 读取 `references/lock-risk-matrix.md`,结合表行数(如能从代码/文档推断)给出 `低/中/高` 评级。
5. **输出审查报告**,格式固定:
- 风险摘要表(文件、行号、等级、一句话原因)
- 每个高危/中危项的**缓解建议**(如:先加可空列 → 分批回填 → 后续再设 NOT NULL)
- 是否需要**停机窗口**的明确结论
6. **回滚预案**:对每个高危变更,参考 `references/rollback-template.md` 生成对应的正向 + 回滚 SQL。
## 关键规则(不可违反)
- **永不自动修改迁移文件**。只读、只建议。`allowed-tools` 已限定为只读工具。
- **不确定时按更严重的等级判定**,并标注「需人工确认」。
- **Postgres / MySQL / SQLite 差异**要识别:例如 `CREATE INDEX CONCURRENTLY` 仅 Postgres 支持,不要给 MySQL 项目提这个建议。
- 若迁移文件用了 ORM DSL(如 Rails 的 `remove_column`、Alembic 的 `op.drop_column`),按等价 DDL 判定。
## 何时停止
- 报告交付后,等待用户决定是否按建议修改。不要主动重写迁移。
到这里,SKILL.md 第二层就完整了。注意我们把完整模式清单和锁表矩阵都外链到了 references/——这就是渐进式披露的精髓:正文只留决策骨架,细节按需加载。
常见问题:正文该写多长? 答:以「能照着执行」为准,社区经验是正文控制在 5000 词以内为佳。超出的部分就该拆到 references。
参考文件是普通 Markdown,只有当正文明确引用、且 Claude 判断需要细节时才会被读取。这意味着你可以放心地把长清单、大表格、模板塞进来,而不会污染主上下文。
# 破坏性 DDL 模式完整清单
## 🟥 高危(需停机或灰度)
| 模式 | 原因 | 典型缓解 |
|------|------|---------|
| DROP TABLE | 数据丢失,不可逆 | 先重命名(`_archived`)→ 观察期 → 真删 |
| DROP COLUMN | 旧代码可能仍读该列 | 先停写引用 → 再删 |
| TRUNCATE | 清空数据 | 同 DROP TABLE |
| RENAME COLUMN/TABLE | 旧代码/查询断裂 | 视图别名过渡 / 双写期 |
| 删除索引/约束 | 查询计划突变、写入报错 | 评估查询依赖后再删 |
## 🟧 中危(需灰度或分批)
| 模式 | 原因 | 缓解 |
|------|------|------|
| ADD COLUMN ... NOT NULL 无 default | 锁全表回填 | 先加可空列 → 回填 → 改 NOT NULL |
| ADD UNIQUE INDEX | 大表长锁 + 冲突失败 | 用 `CREATE UNIQUE INDEX CONCURRENTLY`(PG)|
| MODIFY 列类型 | 重写表、长锁 | 新列 + 双写 + 切换 |
| 收紧 CHECK 约束 | 已有数据可能违规 | 先扫描存量数据 |
## 🟨 低危但需注意
| 模式 | 注意点 |
|------|--------|
| CREATE INDEX(无 CONCURRENTLY)| 大表写锁,建议 `CONCURRENTLY` |
| ADD COLUMN(nullable,有 default)| PG 11+ 不重写表,MySQL 视版本而定 |
| ADD 默认值为非常量(如函数)| 部分方言行为不一致 |
# 锁表风险矩阵
风险 = f(操作类型, 表行数量级)。结合项目实际行数评级。
| 操作 | < 10万行 | 10万–1千万 | > 1千万 |
|------|---------|-----------|---------|
| DROP TABLE | 低(瞬时元数据锁)| 低 | 低 |
| ALTER ADD COLUMN(PG 11+)| 低 | 低 | 低 |
| ALTER ADD COLUMN w/ default 重写 | 低 | 🟧 中 | 🟥 高 |
| CREATE INDEX 普通 | 低 | 🟧 中 | 🟥 高 |
| CREATE INDEX CONCURRENTLY | 低 | 低 | 低(注意失败遗留 invalid index)|
| ADD UNIQUE INDEX | 🟧 中(冲突检查)| 🟥 高 | 🟥 高 |
| NOT NULL + 回填 | 低 | 🟧 中 | 🟥 高 |
| MODIFY 列类型 | 🟧 中 | 🟥 高 | 🟥 高(重写表)|
> 行数无法确定时:默认按「> 1千万」保守评级,并提示用户确认。
# 回滚脚本模板
每个迁移应提供 forward(上卷)与 rollback(回滚)两段。
## 加列(安全模式)
```sql
-- forward
ALTER TABLE orders ADD COLUMN status_new VARCHAR(20);
-- rollback
ALTER TABLE orders DROP COLUMN status_new;
-- Step 1 forward: 加可空列
ALTER TABLE orders ADD COLUMN email_verified BOOLEAN;
-- Step 1 rollback
ALTER TABLE orders DROP COLUMN email_verified;
-- Step 2 forward: 回填 + 收紧(必须在 Step 1 全量上线后)
UPDATE orders SET email_verified = false WHERE email_verified IS NULL;
ALTER TABLE orders ALTER COLUMN email_verified SET NOT NULL;
-- Step 2 rollback
ALTER TABLE orders ALTER COLUMN email_verified DROP NOT NULL;
到这一步,三层结构全部就位:**frontmatter 永驻、正文按需、references 深挖**。
---
## 六、验证:手动触发与自动触发
### 手动触发(最直接)
在项目里运行 Claude Code,输入:
/migration-guardian
应看到 Claude 开始按正文流程执行:先用 Glob 找迁移文件、再 Grep 关键字、再调取 references 分类。
### 自动触发(才是 Skill 的价值)
新建一个故意带坑的迁移文件 `db/migrate/20260622_drop_users_avatar.sql`:
```sql
-- 删掉头像列,反正没人用了
ALTER TABLE users DROP COLUMN avatar_url;
TRUNCATE TABLE user_avatar_history;
CREATE INDEX idx_users_email ON users(email); -- 大表无 CONCURRENTLY
然后对 Claude 说一句完全不提 skill 名的自然话术:
我准备把这周的迁移上生产,帮我看一眼有没有问题。
预期结果:Claude 不需要你点名,会自动识别这是「审查迁移文件」场景,加载 migration-guardian,产出类似下面的报告:
🟥 高危
- db/migrate/20260622_drop_users_avatar.sql:3 DROP COLUMN avatar_url
→ 旧代码可能仍在读取,建议先双写/停引用,本轮不要删
- db/migrate/...:4 TRUNCATE user_avatar_history
→ 数据不可逆,建议先 RENAME 为 _archived 观察
🟨 低危但锁风险
- db/migrate/...:5 CREATE INDEX 无 CONCURRENTLY
→ users 为大表时写锁显著,建议改 CREATE INDEX CONCURRENTLY
结论:本批次不建议直接上线,需停机窗口或拆分为多步灰度迁移。
回滚预案见已生成的 forward/rollback 脚本。
常见问题:自动触发没成功? 排查清单: 1. description 里有没有出现用户原话里的关键词?(如「迁移」「上线」「生产」) 2. 是否把 Skill 放对了
.claude/skills/<name>/SKILL.md路径? 3. 启动时是否重新加载?(改动 frontmatter 后建议重启会话) 4. 是否与另一个 Skill 的 description 重叠导致歧义?给本 Skill 加更尖锐的边界。
Skill 写完不算完,要验证两件事:该触发时必触发(召回)、不该触发时不乱入(精确)。Claude Code 的 skill-creator / Evals 能力支持你跑一组带预期结果的用例。
设计两类用例:
# evals 用例示例
should_activate:
- input: "帮我看看 alembic/versions/ 下新加的迁移安不安全"
expect: migration-guardian 被加载,输出风险报告
- input: "prisma migrate 这次的 down 迁移会不会锁库"
expect: 加载并评估锁表风险
should_not_activate:
- input: "帮我写一个查最近订单的 SQL 查询"
expect: 不加载(这是应用层查询,非迁移)
- input: "生成一批测试用的 seed 数据"
expect: 不加载(属 seed,已在 description 排除)
跑完看通过率。召回不足就去补 description 关键词;误触发多就去收紧边界词。这是把 Skill 从「能用」打磨到「好用」的标准动作。
flowchart TD
A[用户发消息] --> B{Claude 扫描所有 Skill 的 frontmatter}
B -->|描述命中意图| C[加载该 Skill 正文<br/>第二层]
B -->|未命中| Z[按普通对话处理]
C --> D[正文流程引用 references?]
D -->|是| E[按需读取参考文件<br/>第三层]
D -->|否| F[直接执行正文步骤]
E --> F
F --> G{是否破坏性/锁风险?}
G -->|有| H[输出分级报告+缓解建议+回滚]
G -->|无| I[给出低风险确认]
H --> J[用户决定是否修改]
I --> J
J --> K[结束<br/>不自动改文件]
style B fill:#e8f0fe,stroke:#1a73e8
style C fill:#fef7e0,stroke:#f9ab00
style E fill:#e6f4ea,stroke:#34a853
Skill 在交互式会话里好用,但「上线前审查」这种事更适合卡在 CI。借助 Claude Code 的无头模式(-p / --print),可以把这个 Skill 变成 PR 检查:
# 在 CI 的迁移文件变更时调用
changed_migrations=$(git diff --name-only origin/main...HEAD -- \
'db/migrate/*.sql' 'alembic/versions/*.py' 'prisma/migrations/**/migration.sql')
if [ -n "$changed_migrations" ]; then
claude -p "审查以下迁移变更的安全性与锁表风险,只输出 JSON:{risk: high|medium|low, items:[...], block: bool}:$changed_migrations" \
--allowed-tools "Read,Grep,Glob"
fi
block: true 时 CI 直接失败,挡住破坏性迁移进主干。这样 Skill 的知识资产就从「个人助手」升级为「团队门禁」,且逻辑只有一处,维护成本极低。
code.claude.com/docs/en/skills——结构与最佳实践权威来源/skill-creator 直接脚手架 + 跑 Evals,省去手搓骨架/skill 调用的边界与排查[!tip] 51学通信知识星球 星主:爱卫生(微信:gprshome201101)——公众号「51学通信」创办者,19 年通信行业运维与讲师经验,著有《GPRS网络信令实例详解》《EPC原理与实践》《5G核心网原理与实践》三本纸质书(各大电商平台有售)。 星球定位:深耕 5G 核心网、IMS、NFV/AI、云原生等 ICT 领域,主打运维岗位与信令分析。 当前规模:5200 篇主题 / 990 个回答,198 元/年。 加入福利:加入星球后请加爱卫生微信,拉您进知识星球年度陪伴与答疑群——不限量回复您的各种问题,另有群内分享。 AI 加持:51学通信知识星球已作为 skill 接入 WorkBuddy,使用 deepseek-4-flash 在海量星球帖子中精准找贴、找视频、找 FAQ 和案例,做总结、找文档和 PPT 等场景全覆盖。
