Skills 不是 markdown - claude_0x05

4 阅读9分钟

目录概要

  1. 从按钮到工具——视角切换
  2. Skill 是一个"文件夹"
  3. 13 字段 frontmatter:跟 Command 的异同
  4. 描述字段的"触发哲学"
  5. 渐进式揭露——Anthropic 把几百 token 砍掉的办法
  6. Anthropic 内部把 skill 分成的 9 类
  7. 写好 skill 的 9 条经验(Thariq 版)
  8. 两种 skill 调用模式回顾
  9. On-demand hooks:为什么 /careful 这种设计值得抄
  10. 如何度量你的 skill 值不值得留
  11. 踩过的坑:写得太细、拿不出手的 description、文件放错地方

1. 从按钮到工具——视角切换

上一篇把 Command 拆成了"会自动抓现场的按钮"。这一篇换个主角——Skill

按钮和工具最大的区别在于谁决定什么时候用它

graph LR
    subgraph Cmd[Command 按钮]
        U1[用户] -->|主动按| C1[/ 触发]
    end
    subgraph Skl[Skill 工具]
        U2[用户描述需求] --> Claude[Claude 扫描工具箱]
        Claude -->|识别到对应工具| S1[拿出来用]
    end

    style C1 fill:#afa
    style S1 fill:#aaf

按钮只会在"按下去那一刻"工作。工具不一样——工具自己会喊"这活儿我来"。工具箱里摆着十几把工具,Claude 开工前扫一眼工具箱,觉得某一把能用就拿出来。

这就是 Skill 最核心的心智模型。这篇就按"工具"这个视角展开——它长什么样、模型怎么找到它、怎么确保它被挑中、怎么让它好用。


2. Skill 是一个"文件夹"

很多人用了一周 Skill 还以为它是"单个 markdown 文件"。这是最常见的误解。

Anthropic 内部做 skills 的同学 Thariq 原话:

A common misconception is that skills are "just markdown files", but the most interesting part is that they're folders that can include scripts, assets, data, etc. — things the agent can discover, explore, and manipulate.

Skill 的磁盘布局:

.claude/skills/my-skill/
├── SKILL.md            ← frontmatter + 简述 + "看什么时候用"
├── references/
│   ├── api.md          ← 详细 API 文档
│   └── schema.md       ← 数据结构
├── scripts/
│   ├── validate.py     ← 可执行脚本
│   └── export.sh
├── examples/
│   ├── case_01.md
│   └── case_02.md
├── assets/
│   └── template.svg
└── config.json         ← skill 自己的配置(可选)

这不是一个 prompt 文件,这是一个带代码、带资产、带样例的小包

SKILL.md 只是这个包的"索引页"。模型最开始只看到索引页——真要用的时候再去读 references/、跑 scripts/、参考 examples/

为什么这样设计?马上讲。但先看它的 frontmatter。


3. 13 字段 frontmatter:跟 Command 的异同

Skill 的 frontmatter 也是 13 个字段——这个数字跟 Command 一样,是刻意的。Anthropic 把两者做到"字段级一致",只有几个字段的默认值和语义不同。

---
name: my-skill
description: Do X when user asks for Y
argument-hint: [file-path]
disable-model-invocation: false
user-invocable: true
allowed-tools: Read, Grep, Glob
model: sonnet
effort: medium
context: fork
agent: general-purpose
hooks:
  PreToolUse: [...]
paths: "src/**/*.py"
shell: bash
---

跟 Command 的字段清单放一起对比:

字段CommandSkill差异
name缺省取文件名缺省取目录名Skill 是文件夹,name 来自目录
description可选但强烈推荐关键——模型扫描用决定 skill 能否被模型选中
argument-hint一致一致
disable-model-invocation默认 false默认 false
user-invocable默认 true预加载 skill 常设为 false见第 8 节
allowed-tools激活时的本地白名单
model运行时模型
effort
contextfork 切子上下文
agent
hooks仅 command 生命周期仅 skill 会话周期on-demand hooks(见第 9 节)
pathsglob 激活glob 自动激活(无需用户输入)Skill 的 paths 是自动找工具的触发点
shellbash/powershell

字段一致是好事——你学 Command 付出的学习成本,到 Skill 继续用。只有几处语义不同要小心:

  • user-invocable: false —— Command 里很少用(藏起来没意义),Skill 里很常见(agent-preloaded skill 都这样)
  • paths —— Command 只是"在补全里出现",Skill 是"自动被选中"(后者更强的触发力)

4. 描述字段的"触发哲学"

Skill 的 description 跟 Command 的表面上一样,但职责重得多。Thariq 在 Tip 6 里讲得很直白:

The description field is not a summary — it's a description of when to trigger this skill. Write it for the model.

意思是:description 不是写给用户看的"这个 skill 是干啥的",是写给模型看的"什么情况下该用这个 skill"

4.1 一个对比

反例:

description: A skill for formatting release notes

这是在写"我是谁"。模型扫到这段没有关键触发点——它只能靠 "release notes" 几个词被动匹配。

正例(03 篇里 release-notes-formatter 的真实 description):

description: Formats categorized commit data into a polished
  RELEASE_NOTES_<version>.md following the project's house style. Writes
  to release-notes/RELEASE_NOTES_<version>.md and release-notes/output.md.

差别是什么?正例在告诉模型:

  1. 触发场景:有分桶好的 commit 数据要排版时
  2. 具体产出:RELEASE_NOTES 文件 + output.md 文件
  3. 路径决定性:写到 release-notes/RELEASE_NOTES_<version>.md

模型扫到这段,看到用户说"给我排一份 release notes"——match;看到用户说"把 commit 列表整理成发布日志"——match;看到当前对话里已经有 feat/fix 分桶数据——更强 match。

4.2 一条实用经验

写 description 时的套路:

动词 + 触发条件 + 产出 [+ 边界说明]
  • ❌ "Formats Python code"

  • ✅ "Formats Python code with Black configuration from pyproject.toml when user asks for formatting or before committing"

  • ❌ "Database query helper"

  • ✅ "Runs read-only SQL queries against the analytics warehouse. Use when user asks for metrics, counts, or data aggregations. Does not run DDL or writes."

第二种写法的好处:模型不仅知道能用这个 skill,还知道什么时候不该用("Does not run DDL" 是负面触发条件,避免滥用)。


5. 渐进式揭露——Anthropic 把几百 token 砍掉的办法

回到"为什么 Skill 是文件夹"这个问题。

Claude Code 启动时做一件事:把所有 skill 的 SKILL.md 扫一遍,把 description 塞到系统 prompt 里。只有 description,不是全部内容。

graph TB
    Start[Claude Code 启动] --> Scan[扫描所有 .claude/skills/*/SKILL.md]
    Scan --> Desc[只读取每个 skill 的 description]
    Desc --> Sys[注入系统 prompt<br/>'你有这些 skill 可用:<br/> - skill-a: 描述<br/> - skill-b: 描述']

    Use[模型决定用 skill-a] --> Full[读 skill-a 的完整 SKILL.md]
    Full --> Ref{需要更多信息?}
    Ref -->|需要 API 细节| R1[读 references/api.md]
    Ref -->|需要跑脚本| R2[exec scripts/validate.py]
    Ref -->|参考样例| R3[读 examples/case_01.md]

    style Sys fill:#afa
    style Full fill:#ff9
    style R1 fill:#aaf
    style R2 fill:#aaf
    style R3 fill:#aaf

这套机制叫 Progressive Disclosure——渐进式揭露。

5.1 数学账

假设你有 50 个 skills,每个 skill 平均 2000 token。把全部内容都塞进系统 prompt,起步就 10 万 token 消耗。

用渐进式揭露:

  • 启动时:50 个 description × 每个 50 token ≈ 2500 token
  • 用到某个 skill:+ 2000 token 完整 SKILL.md
  • 用到某个 reference:+ ? token

节省 95% 以上。而且随着你 skill 数量的增长,这个比例只会越来越好。

5.2 三层揭露

03 篇里 release-notes-formatter 的目录是典型范例:

.claude/skills/release-notes-formatter/
├── SKILL.md        ← 总是加载(其实只有 description 加载,正文待唤醒)
├── reference.md    ← 需要 Markdown 模板时才读
└── examples.md     ← 需要对照 OSS 样例时才读

SKILL.md 正文里引用了这两个文件:

## Additional resources

- For Markdown template, category headers, and tone examples, see [reference.md]
- For example input/output pairs, see [examples.md]

模型看到 SKILL.md 再决定"我需要 Markdown 模板吗?"——需要就读 reference.md。不需要的话这部分 token 永远不花。

5.3 跟 Commands 的对比

Command 为什么不用这套机制?因为 Command 是用户主动按的——用户按下 /xxx 的那一刻就肯定要用,token 该花就花,没必要搞分层。

Skill 是模型自主挑的——它只看 description 就要做决定,决定前不该让整个 skill 占满上下文。所以 Skill 有目录结构,Command 只有单文件。

这是一个典型的设计权衡——选择机制决定了成本模型。按钮是"只有按下才扫描",工具是"先扫描再决定按",扫描过程必须足够廉价。


6. Anthropic 内部把 skill 分成的 9 类

如果只看官方默认的 5 个 skill(simplify batch debug loop claude-api),你没法理解 Skill 的真正能力上限。Anthropic 内部用了几百个 skill 之后,Thariq 把它们归纳成 9 个大类:

mindmap
  root((9 种 skill 类型))
    1 Library & API Reference
      billing-lib
      internal-platform-cli
      frontend-design
    2 Product Verification
      signup-flow-driver
      checkout-verifier
      tmux-cli-driver
    3 Data Fetching & Analysis
      funnel-query
      cohort-compare
      grafana
    4 Business Process
      standup-post
      create-ticket
      weekly-recap
    5 Code Scaffolding
      new-workflow
      new-migration
      create-app
    6 Code Quality & Review
      adversarial-review
      code-style
      testing-practices
    7 CI CD Deployment
      babysit-pr
      deploy-service
      cherry-pick-prod
    8 Runbooks
      service-debugging
      oncall-runner
      log-correlator
    9 Infrastructure Ops
      resource-orphans
      dependency-management
      cost-investigation

九类里真正值得十年以上经验的架构师先投入的是哪几类?我的排序:

优先类型为什么值得先做
1Library & API ReferenceClaude 在陌生 API 上容易瞎编,一个 skill 能立刻止血
2Product Verification花一周时间让 skill 会自动验证产出——Thariq 说这个 ROI 最高
3Runbooks把线上值班的"第一步、第二步、第三步"交给 skill,减少人肉查 wiki
4Code Quality & Review结合 hooks 自动跑,团队质量门禁
5Infrastructure Ops危险操作带 guardrail,新人误操作率大降

最被低估的是 Runbooks——传统团队都有 wiki / confluence / notion 写 runbook,但人真去 oncall 时要么懒得翻要么翻错。Skill 的优势是主动匹配——报警一到,Claude 直接选中对应 runbook skill,人只需要审 Claude 的判断,不需要自己查文档。


7. 写好 skill 的 9 条经验(Thariq 版)

Thariq 同一份公开分享里给了 9 条 skill 编写经验。这里把每条配上"十年经验架构师视角的翻译"——哪些是"废话常识"、哪些是"非做不可"。

7.1 不要写废话

Don't State the Obvious

Claude 已经知道怎么写 React 组件、怎么用 Python pandas、怎么写 SQL。你的 skill 如果只是重复这些,负价值——既占 token 又稀释描述的信号度。

值得写进 skill 的

  • 内部库 / 私有 CLI 的用法
  • 跟"行业默认做法"相反的团队约定("我们不用 Inter 字体,不要紫色渐变")
  • Claude 经常犯错的边界条件

7.2 Gotchas 是全 skill 信号密度最高的段落

Build a Gotchas Section

Gotchas / 踩坑 / 注意事项——放在 skill 末尾。内容不是凭空想的,是实际用 Claude 时它反复踩的点

迭代逻辑:

Claude 用 skill 出错 → 人工看原因 → 把这个原因加进 Gotchas → 下次不再踩

这是 skill 从 "v1 能跑" 到 "v10 真好用" 的核心路径。

7.3 用文件系统做渐进式揭露

第 5 节讲过了。要点重复一次:SKILL.md 简短、细节拆到子文件、模型按需读

7.4 不要给 Claude 写死步骤

Avoid Railroading Claude

新手最容易踩——写 skill 时担心 Claude 不按规矩来,就把"第 1 步、第 2 步、第 3 步"写得死死的。结果是:

  • 场景稍微变化,Claude 僵硬套模板
  • Skill 复用性急剧下降

对比两种写法:

# 写法一(railroading,不推荐)

## Instructions
1. Read `config.yaml`
2. Extract the `database_url` field
3. Call `psql $database_url -c "SELECT * FROM users"`
4. Parse the output
5. Print as JSON
# 写法二(goal + constraints,推荐)

## Goal
给定 config.yaml,查 users 表并返回 JSON。

## Constraints
- 数据库 URL 在 config.yaml 的 `database_url` 字段
- 只读查询,不做 DDL
- 输出 JSON schema:`{users: [{id, email, created_at}]}`

## Useful tools
- `scripts/run_query.sh` —— 带连接重试的查询脚本

写法二让 Claude 有判断余地——config 格式变了它能跟着调整;JSON 出错它能排查。这是 skill 可持续的关键。

7.5 Setup 要先想清楚

Think through the Setup

有些 skill 需要用户配置才能跑(数据库地址、API key、dashboard ID)。配置存哪里?

Anthropic 的推荐:skill 目录下的 config.json,不存在时用 AskUserQuestion 工具让用户填一遍。

skill/
├── SKILL.md
├── config.json         ← 存 setup 结果
└── references/...

SKILL.md 里写:

## Setup
If config.json does not exist, use AskUserQuestion to gather:
- database_url
- default_schema

Then write to config.json and confirm with user.

第一次用 skill 触发 setup,之后永远不用重配。

7.6 description 是写给模型看的

第 4 节展开过。

7.7 Skill 可以自己存记忆

skill 可以自己维护数据文件——append-only log、JSON、甚至 SQLite。但直接存在 skill 目录里会被升级覆盖

正确做法:用 ${CLAUDE_PLUGIN_DATA} 环境变量指向稳定目录:

## Memory
Append a line to `${CLAUDE_PLUGIN_DATA}/runs.log` every time this skill completes:
  `timestamp\tuser\toperation\tresult`

下次启动还能看到历史,skill 升级也不会丢。

7.8 把代码塞进 skill

Store Scripts & Generate Code

这是 Skill 相比 prompt-only 扩展最爆炸的能力。

LLM 擅长"组合、决策、解释",不擅长"重复生成复杂模板代码"。Skill 目录里放 shell / python 脚本,Claude 去调用它们、组合它们,而不是自己写一份。

例子——一个数据查询 skill:

data-query/
├── SKILL.md
├── scripts/
│   ├── run_query.py        ← 带 auth、带 retry、带 timeout 的查询封装
│   ├── format_csv.py       ← 标准化 CSV 输出
│   └── format_markdown.py  ← 转 markdown 表格
└── references/
    └── schema.md

SKILL.md 告诉 Claude:

用户要查数据时:
1. 看 references/schema.md 找对表
2. 组合 scripts/run_query.py 的参数
3. 根据用户想要 csv 还是 markdown,pipe 到对应 format_* 脚本

Claude 的工作变成"组装 pipeline",不是"从 0 写查询"。稳定性、速度、token 消耗都好一个量级。

7.9 On-demand hooks

第 9 节展开。


8. 两种 skill 调用模式回顾

第 03 篇讲过 release-notes 生成器同时用了两种 skill 调用方式。这里从 Skill 自己的视角再说一遍,因为这是"什么时候该设 user-invocable: false"的判断依据。

模式例子触发user-invocable何时用
Agent Skill(预加载)git-log-readerAgent 启动时读入false 隐藏skill 是某个 agent 的"必备知识",不独立存在
Direct Skill(直接调用)release-notes-formatterSkill 工具 / /name / 模型自动选true 或省略skill 是"可复用的独立操作"

判定是否要 preload 的一个硬标准:

如果少了这个 skill,这个 agent 就彻底没法工作 → preload 如果 skill 能在多个 agent 间复用 → 直接调用

git-log-reader 是"怎么解析 conventional commits"——只有 release-notes-agent 用得到,没有它 agent 就瞎了,preload

release-notes-formatter 是"把分桶数据排版成 Markdown"——以后做 changelog agent、做 monthly digest agent 都能用这个 skill,直接调用

8.1 跨 skill 调用

Skill 还能引用另一个 skill:

file-upload skill 把这份 CSV 上传到 S3。

模型看到这行,知道去找 file-upload skill 并执行。没有 native 的依赖管理——靠的是 skill 名字自然语言引用。

这种自由度带来的风险是:名字改了引用就断。所以实战里我们会在 SKILL.md 顶部列一段 "Dependencies":

## Dependencies (skills this skill calls)

- `file-upload` — needed to push final CSV to S3
- `data-query` — needed to fetch source data

肉眼能看到依赖,改名时可以 grep 出来。


9. On-demand hooks:为什么 /careful 这种设计值得抄

Skill 的 hooks 字段跟 Command 的 hooks 字段看起来一样,但生效时机不同

  • Command 的 hooks:command 激活期间生效
  • Skill 的 hooks:skill 被调用开始直到会话结束都生效

这个区别催生了 Anthropic 内部最好玩的两个 skill:

9.1 /careful——临时穿上防护服

---
name: careful
description: Enable careful mode  blocks destructive operations for this session
---

When invoked, register PreToolUse hooks that block:
- rm -rf commands
- DROP TABLE / DROP DATABASE in SQL
- git push --force / --force-with-lease
- kubectl delete
- terraform destroy

用户说 "/careful" 之后,整个会话都处于"禁毁模式"。会话结束 hook 消失。

为什么不把这个变成默认 hook?因为平时做 dev 没必要阻挡 rm -rf build/——它会频繁报错。/careful显式切入的更严格模式,类似 "sudo -i 进了 root 就要小心模式"。

9.2 /freeze——写入范围冻结

---
name: freeze
description: Freeze file writes to a specific directory for this session
---

When invoked, prompt user for allowed directory (e.g., "src/api/").
Register PreToolUse hook on Edit/Write that blocks any path outside that directory.

用途:正在改某个模块,不想让 Claude "顺手也动一下" 其他地方。/freeze src/api/ 之后它就被关在笼子里了。

9.3 这个设计模式的通用性

On-demand hooks 的本质是:用户声明模式,模式改变工具行为。这跟 Vim 的 normal/insert/visual 模式、IDE 的"只读模式"是一个路数。

架构师可以借鉴的场景:

  • /prod-debug —— 在生产调试时自动加 audit log
  • /review-mode —— 禁止任何 Edit/Write,只能 Read/Grep
  • /deploy-window —— 只在工作时间允许 deploy 类工具

这些都不需要改全局 settings,一个 skill 解决。下一篇讲 subagents 时会看到 subagent 也有 hooks 字段,能做更精细的 per-role 限制。


10. 如何度量你的 skill 值不值得留

写了 20 个 skill,怎么知道哪些被用、哪些是僵尸?

Thariq 分享了 Anthropic 内部做法:用 PreToolUse hook 给 skill 埋点

# .claude/settings.json
hooks:
  PreToolUse:
    - matcher: "Skill"
      hooks:
        - type: command
          command: python3 .claude/hooks/log_skill_use.py

log_skill_use.py 记录:

  • 时间戳
  • 用户
  • 被调用的 skill 名
  • 调用上下文摘要
  • 执行时长
  • 最终是否成功

积累一两周之后看数据:

  • 高频 + 高成功率 → 明星 skill,值得继续投入
  • 高频 + 低成功率 → 需要迭代(加 Gotchas、换 description、拆目录)
  • 低频 → 要么 description 写得没辨识度、要么这个场景根本不常见
  • 零调用 → 删掉或进存档

这是 skill 生态从"个人玩具"走向"团队基础设施"的关键步骤——没有度量就没有迭代。


11. 踩过的坑

坑 1:description 写成功能描述而不是触发条件

前面展开过。再贴一条反例:

  • description: Git commit helper
  • description: Creates conventional commits from staged changes. Invoke when user asks to commit, when changes are staged without a commit message, or before a PR.

坑 2:SKILL.md 写了 500 行

很常见——想"把能说的全说了"。结果每次 skill 被激活都吃 2000 token。

正确做法:SKILL.md 保持在 100 行以内,其他都拆到 references/。

坑 3:references/ 里放的 markdown 跟 SKILL.md 重复

SKILL.md 写"API 用法见 references/api.md";翻开 references/api.md,发现前一半又抄了 SKILL.md 的简述。纯浪费。

references/ 的内容只应该是 SKILL.md 没写的细节,不要解释"这个 skill 是干啥的"。

坑 4:把 scripts/ 写成 "pseudo-code"

skill 的脚本是要执行的,不是写给人读的伪代码。遇到过把 validate.py 写成注释比代码还多、最后不 runnable 的。

验证办法:python3 .claude/skills/my-skill/scripts/validate.py 能直接跑通才合格。

坑 5:paths 写成正则

  • paths: "\\.py$"
  • paths: "**/*.py"

paths 接收的是 glob,不是 regex。踩这坑的人意外多。

坑 6:混用 skills: 和 Skill 工具触发

Agent 里写了 skills: [git-log-reader](预加载),结果 command 里又写 Skill(skill: "git-log-reader") 想"再调一次"。会搞乱——agent 已经把 skill 内化为知识,不是一个可调用工具。

规则:同一个 skill,要么 preload 要么 direct,不要两条路都用


把工具放回柜子

Skill 的完整图景比 Command 大一个量级——它不是"另一个按钮",是"一个会被模型主动挑选、带着脚本资产、能做渐进式揭露的工具包"。

三个最该记住的点:

  • 文件夹不是文件 —— SKILL.md 是索引页,其他文件按需读
  • description 是触发说明书,不是功能介绍 —— 写给模型看
  • Gotchas 是 skill 的质量标尺 —— 真踩过的坑比理论上的注意事项值钱 100 倍

顺着"按钮 → 工具"的类比,下一个该拆的就是"员工"——Subagent。如果 Skill 是"一把等你来拿的工具",Subagent 就是"有独立人格、自己拿工具干活、带记忆的人"。下一篇把 Subagent 的 frontmatter 15+ 字段、memory 三种作用域、isolation 的几种模式摊开讲。


Footnotes

外部链接