Skip to Content

动手写一个 Skill

Skill 就是一个 SKILL.md 文件:YAML 前置元数据 + Markdown 指令内容。放到对应目录,Claude Code 自动发现,需要时加载。

Claude Code 已经把「自定义命令」(.claude/commands/)合并进了 skill 体系。.claude/commands/foo.md.claude/skills/foo/SKILL.md 都生成 /foo,写法相同。Skill 的额外能力:附加文件目录、frontmatter 控制谁能触发、Claude 可以根据 description 自动调用。

写之前:想清楚四个问题

  1. 这个 skill 让用户能做什么? — 之前做不到、现在能做到的事
  2. 什么时候触发? — 用户会说哪些话、输入什么命令
  3. 期望的输出格式是什么? — 纯文本回复?代码 diff?生成文件?
  4. 能不能客观验证输出? — 能验证(比如「必须生成 .json 文件」)就值得写测试;输出主观(写作风格、设计品味),测试意义不大

文件结构

my-skill/ ├── SKILL.md # 主指令(必需) ├── scripts/ # 辅助脚本(可选,被执行而非读入上下文) ├── references/ # 参考资料(可选,按需加载) └── assets/ # 模板等资源(可选)

目录名就是 skill 名,输入 /目录名 即可触发。

Skill 内容的两种类型

Reference 型:给 Claude 加知识——约定、模式、风格指南、领域常识。会与当前会话一起被引用。

--- name: api-conventions description: API 设计约定 --- 写 API 端点时: - 用 RESTful 命名 - 错误格式统一 - 加请求校验

Task 型:一组明确的步骤——部署、提交、生成代码。通常你想自己 /skill-name 触发,加 disable-model-invocation: true 防止 Claude 自作主张调用。

--- name: deploy description: 部署到生产 context: fork disable-model-invocation: true --- 部署应用: 1. 跑测试 2. 构建 3. 推到部署目标

正文要简短:skill 一旦被调用,内容会一直留在上下文里跨多轮,每行都是持续的 token 成本。能写「做 X」就别写「你应该做 X 因为……」。

Frontmatter 字段

字段说明
nameskill 名称。小写字母 / 数字 / 连字符,≤ 64 字符
description最关键——告诉 Claude 这个 skill 做什么、何时触发。description + when_to_use 合计被截到 1536 字符,把核心场景写前面
when_to_use补充触发场景或例句,追加在 description 后
argument-hint自动补全时显示的参数提示,比如 [issue-number]
arguments命名参数列表,内容里用 $参数名 引用
disable-model-invocationtrue 则只能手动 / 触发,Claude 不会自动调
user-invocablefalse 则从 / 菜单隐藏,只能 Claude 自动调
allowed-toolsskill 激活期间免确认的工具,如 Bash(git *)
modelskill 激活期间使用的模型,或 inherit
effort强度档位:low / medium / high / xhigh / max
contextfork 则在独立 subagent 中运行,不污染主会话上下文
agent配合 context: forkExplore / Plan / general-purpose.claude/agents/ 中的自定义 agent
hooks此 skill 生命周期内生效的 hooks
pathsglob 匹配,只在操作特定文件时自动加载
shellbash(默认)或 powershell

字符串替换

Skill 内容支持几种动态替换:

变量含义
$ARGUMENTS调用 skill 时传入的全部参数
$ARGUMENTS[N] / $N第 N 个参数(0-based),$0 是第一个
$namearguments 里命名的参数
${CLAUDE_SESSION_ID}当前会话 ID
${CLAUDE_EFFORT}当前 effort 档位
${CLAUDE_SKILL_DIR}当前 skill 所在目录——bash 注入命令里引用打包脚本时用这个,跨安装位置都能解析对

带空格的参数用引号包起来,shell 风格 quoting。/my-skill "hello world" second$0 = hello world$1 = second

最简示例

总结未提交改动的 skill:

1. 建目录

mkdir -p ~/.claude/skills/summarize-changes

2. 写 SKILL.md

--- description: 总结 git 未提交的改动,标出风险点。用户问「改了什么」「帮我写 commit message」时自动触发。 --- ## 当前改动 !`git diff HEAD` ## 指令 用两到三个 bullet 总结上面的改动,然后列出你注意到的风险:缺少错误处理、硬编码值、需要补测试的地方等等。如果 diff 为空,直接说没有未提交改动。

3. 测试

在任意 git 项目里开 Claude Code,输入 /summarize-changes,或者直接问「我改了什么」,Claude 会自动匹配 description 并加载 skill。

注入动态上下文

!`命令` 语法在执行 skill 前先跑 shell 命令,输出直接替换占位符。Claude 看到的是命令结果,不是命令本身:

--- description: 查看 PR 详情并总结 context: fork agent: Explore allowed-tools: Bash(gh *) --- ## PR 信息 - 改动文件:!`gh pr diff --name-only` - Diff:!`gh pr diff` - 评论:!`gh pr view --comments` ## 指令 总结这个 PR 的改动内容和潜在风险。

多行命令用 ```! 代码块:

## 环境 ```! node --version npm --version git status --short ```

想让 skill 在响应里多想一会儿,正文里加 ultrathink

参数传递

--- name: fix-issue description: 修一个 GitHub issue disable-model-invocation: true --- 修复 GitHub issue $ARGUMENTS: 1. 读 issue 描述 2. 理解需求 3. 实现修复 4. 写测试 5. 创建 commit

输入 /fix-issue 123$ARGUMENTS 被替换为 123。如果 skill 没写 $ARGUMENTS,Claude Code 会把输入以 ARGUMENTS: <值> 追加到末尾。

Skill 存放位置

位置路径作用范围
企业级由 managed settings 配置组织内所有用户
个人~/.claude/skills/<name>/SKILL.md所有项目
项目.claude/skills/<name>/SKILL.md当前项目
插件<plugin>/skills/<name>/SKILL.md启用插件的项目

同名 skill 的优先级:企业 > 个人 > 项目。插件 skill 走 plugin-name:skill-name 命名空间,不会冲突。如果 .claude/commands/.claude/skills/ 出现同名,skill 优先。

实时变更:Claude Code 监听 skill 目录,会话期间增删改 skill 立刻生效,不用重启。但如果是新建一个之前根本不存在的 .claude/skills/ 目录,要重启才会被监听。

Monorepo 友好:项目 skill 从启动目录沿父级目录一路向上查找到仓库根;当 Claude 操作子目录里的文件时,也会按需加载该子目录下的 .claude/skills/

控制谁能调用

默认你和 Claude 都能调。两个字段限制行为:

Frontmatter你可调Claude 可调上下文加载时机
(默认)description 常驻;触发时加载正文
disable-model-invocation: truedescription 不进上下文;你触发时加载正文
user-invocable: falsedescription 常驻;触发时加载正文

/permissions 里也能精细控制:

Skill(commit) # 允许 commit Skill(review-pr *) # 允许 review-pr 加任意参数 Skill(deploy *) # 禁用 deploy Skill # 禁掉全部 skill

Skill 内容的生命周期

Skill 被调用时,渲染后的 SKILL.md 作为一条消息进入对话,之后整个会话都留在上下文里——Claude Code 不会在后续轮次重新读文件。所以写「全局生效的指引」用 standing instructions 的语气,而不是一次性步骤。

自动 compaction 会按 token 预算保留最近调用过的 skill:每个 skill 保留前 5000 token,所有 skill 共享 25000 token 池,从最近调用的往回填。如果 compaction 后某个 skill 不再影响行为,通常是模型选择了别的工具——加强 description,或重新 / 触发一次。

包脚本

Skill 可以打包任意语言的脚本,比 prompt 多一档能力——生成可视化 HTML、运行检查、调用本地工具。脚本路径用 ${CLAUDE_SKILL_DIR} 引用,跨安装位置都能解析:

--- name: codebase-visualizer description: 生成代码库可折叠树状可视化 allowed-tools: Bash(python3 *) --- 跑可视化脚本: ```bash python3 ${CLAUDE_SKILL_DIR}/scripts/visualize.py .
## 写测试用例 如果 skill 输出可以客观验证,按以下格式写 `evals/evals.json`: ```json [ { "id": "basic-usage", "prompt": "用户的真实 prompt…", "files": { "src/app.ts": "相关文件内容" }, "assertions": [ { "id": "generates-config", "description": "生成了 .claude/settings.json 文件" } ] } ]

先写 prompt,断言可以后面补。好的断言用描述性的 id,表达可以被客观验证。

评估与迭代

  1. 跑测试用例 — 用 subagent 分别跑 skill 版和 baseline(无 skill 或旧版本),对比差异
  2. 写断言 — 跑的同时补充断言,好的断言是「客观可验证」的
  3. 分析结果 — 看哪些断言没通过、哪些没区分度、哪些不稳定
  4. 改进 skill — 四个方向:
    • 泛化:是不是过拟合到测试用例了?
    • 精简:有没有不产生价值的冗余指令?
    • 解释为什么:把死规则改成原因说明
    • 提取重复:所有测试里 subagent 都写了同样的辅助代码,就打包进 scripts/
  5. 重复 — 改进 → 重跑 → 对比 → 直到满意

优化 description

skill 写完、内容稳定后,回头调优 description 触发准确率:

  1. 生成 20 个 prompt(8-10 个应触发,8-10 个不应触发——后者要写「接近但不该触发」的,不是明显无关)
  2. 人工确认这些 prompt 合理
  3. 脚本自动跑多轮,找 test score 最高的 description
  4. 把最优 description 写回 SKILL.md

用 skill-creator 自动化整个流程

上面这套「写草稿 → 跑测试 → 看结果 → 改进 → 优化 description」的流程,Anthropic 把它封装成了一个叫 skill-creator 的 skill——一个「专门用来写 skill 的 skill」。装好之后你跟它说「想做一个 X 的 skill」,它会引你走完整套流程。

它做的事比手搓多两层:

  • 用 subagent 并行跑测试:每个测试 prompt 同时 spawn 两个 subagent,一个带 skill、一个不带(baseline),结果存到 <skill-name>-workspace/iteration-1/eval-<id>/
  • 量化 + 肉眼双轨评估:grader subagent 对照断言打分生成 benchmark.json(pass_rate / token / 耗时),同时启动 eval-viewer/generate_review.py 在浏览器里逐 case 看输出、留评论。用户提交后写到 feedback.json,下一轮迭代直接读

核心循环:

  1. Capture Intent — 追问四个问题:能做什么、何时触发、输出格式、是否需要客观验证
  2. 写草稿 — 生成第一版 SKILL.md,description 倾向「pushy」一点(当前 Claude 偏向 undertrigger)
  3. 写 2-3 个真实测试 prompt 存到 evals/evals.json,先不写断言
  4. 同一轮里并行 spawn 所有 run(with-skill + baseline),别先跑一组再跑另一组
  5. 跑 grader、跑 scripts.aggregate_benchmark 聚合,启动 viewer 给用户看
  6. 读 feedback、改 skill、进入 iteration-2,重复直到用户满意或反馈都为空

特别提一个细节:改进 skill 时,宁可解释 why 也不要堆 ALL CAPS 的 MUST/NEVER——当代 LLM 理解力够强,硬规则反而限制泛化。如果发现所有测试用例里 subagent 都独立写了同一段辅助脚本,就把它打包进 scripts/

单独跑 description 优化

skill 内容稳定后,跑 description 优化器:

python -m scripts.run_loop \ --eval-set <trigger-eval.json> \ --skill-path <path-to-skill> \ --model <当前会话用的 model id> \ --max-iterations 5 --verbose

工作方式:自动生成 20 条 trigger 测试 prompt(8-10 条应触发 + 8-10 条「接近但不该触发」的 near-miss),拆 60% train / 40% held-out test,每条跑 3 次取触发率,迭代多轮、用 test 集得分选 best description(避免在 train 上过拟合)。结束后浏览器里弹 HTML 报告。

什么时候用 skill-creator

  • skill 输出能客观验证(生成文件、转换数据、固定步骤)——评估才有意义
  • 你愿意花至少 1-2 轮迭代认真做这个 skill,而不是临时凑一个 prompt 应付

写作风格类(写作风格、设计品味)skill 跳过定量评估,直接走肉眼 review 那一条线就行。

故障排查

Skill 该触发没触发

  • description 里加用户自然会说的关键词
  • 问一句「有哪些 skill 可用?」看是否列出
  • 换个说法接近 description
  • 直接 /skill-name 触发

Skill 触发太频繁

  • description 写更具体
  • disable-model-invocation: true 只允许手动

description 被截短

skill 名一定会进上下文,但 description 有总字符预算(默认按上下文窗口的 1%)。skill 多的时候,调用频率低的会先被截短/doctor 能看到当前预算情况。提高预算用 skillListingBudgetFraction(比如 0.02 = 2%)或 SLASH_COMMAND_TOOL_CHAR_BUDGET;不重要的 skill 在 skillOverrides 里设 "name-only" 让出预算。单条 description+when_to_use 上限 1536 字符,可用 maxSkillDescriptionChars 调。

打包发布

python -m scripts.package_skill <path/to/skill-folder>

参考 Claude Code 官方 Skills 文档 

Last updated on