用 Skills 定制你的 AI 工作流

动手开发 MCP 工具 里我们讲了怎么把工具做成远程服务,但很多时候你的需求没那么重,就是想把本地一些反复要做的操作想固化下来,每次一句话就能执行。这就是 Skill 要解决的问题。

Skills 最早由 Claude Code 推出,把重复的工作流写成一个 Markdown 文件(SKILL.md),AI 按需加载,一个 / 命令就能触发。因为确实好用,现在几乎所有主流 AI 编程工具都跟进了,包括 Codex、Cursor、GitHub Copilot 等,基本成了标配。

虽然目录名不同,但 Cursor 和 GitHub Copilot 都内置了对 .claude/skills/ 的兼容,会自动扫描 Claude Code 的 Skill 目录。唯一需要手动处理的是 Codex CLI,加一个软链接就行:

做一个图片优化 Skill

一个 Skill 就是 .claude/skills/ 下的一个文件夹,唯一必须的文件是 SKILL.md,其他文件(脚本、模板、参考资料)都是可选的。

我们来做一个图片优化 Skill,把图片压缩、缩放、转成 WebP。

Skill 放在 ~/.claude/skills/ 下是个人级的,所有项目可用;放在项目的 .claude/skills/ 下是项目级的,只在当前项目生效。

整个 Skill 的内容如下,你可以点开文件查看:

SKILL.md:Skill 的核心

SKILL.md 分两部分:

YAML 头部(两行 --- 之间)声明元信息:Skill 叫什么名字、什么时候该被触发、需要哪些权限。

Markdown 正文--- 下方)是给 Claude 看的具体指令,告诉它拿到这个 Skill 之后该怎么做。可以理解为一个操作手册。

YAML 头部各字段的含义后面会展开讲,这里先关注整体结构:头部定义「我是谁」,正文定义「怎么做」。

固定的流程用脚本

代码脚本主要用来封装固定的流程,比如这个例子里 SKILL.md 引用了 scripts/optimize.py 这个代码文件,用 Pillow 库做图片缩放、压缩和格式转换。

如果让 Claude 自由发挥,它肯定也能自己写个脚本完成任务,单每次结果可能不一样,而且消耗 token。把这些不变的流程沉淀成脚本直接让 Claude Code 调用,既能保证一致性,又能节省 token,只需在 SKILL.md 里告诉它脚本怎么用就行了。

但代码脚本并不是必须的,如果流程本身比较简单,纯文本描述就够了。

比如我们写一个 git 提交规范检查 Skill:

disable-model-invocationallowed-tools 这几个参数的作用后面会讲,这里先看整体结构:没有脚本,没有额外文件,一个简单的文本文件,Claude Code 照样能准确执行。

小技巧

以我的经验来看,主要有两种情况需要给 Skill 搭配脚本:

1、比较复杂的数据处理,且对结果的准确性要求比较高的场景。比如上面举例的图片处理工具。

2、需要对 Claude 生成的内容做校验的场景。比如你可以写一些简单的校验脚本,要求 Claude 在完成任务后执行这些脚本进行自检,确保生成的内容格式符合要求。

如何使用 Skill

文件保存好就行,不需要重启 Claude Code,它会自动检测到新的 Skill。

Skill.md 的 description 字段内容会加载到上下文,每次会话都会携带这些信息,所以直接跟 Claude Code 说「帮我把 screenshots 下的图片都压缩一下」,Claude Code 会自动加载合适 Skill 并按照文档的要求执行。

但是以我的实际经验来看,如果你的 description 写的太模糊宽泛、Skill 比较多、或者存在多个功能相近的 Skill 时,Claude Code 可能不会自动加载,甚至加载错误的 Skill。

所以我一般都是直接使用 /<skill-name> 明确指定 Skill 名称来触发,例如这样:

Skill 的渐进式加载机制

你可能会想:Skill 越写越多,启动时全加载进来,上下文窗口不会撑爆吗?

实际上不会。Skill 用的是三级渐进式加载,不是一股脑把所有内容塞进上下文,而是按需分层:只在需要的时候,才加载需要的那部分。

下面这张时序图展示了从启动到执行 Skill 的完整过程:

Level 1:元数据(启动时加载)

Claude Code 启动时,只读取每个 Skill 的 namedescription,放进上下文。

每个 Skill 大约几十到一百 token(取决于 description 长度),十几二十个 Skill 在上下文窗口里基本可以忽略。

不过所有 Skill 的描述共享一个总预算(约为上下文窗口的 1%),如果你定义了特别多 Skill 或者每个 description 都写得很长,排在后面的可能被截断。

所以每个 Skill 的 description 要简明扼要,把最关键的信息放前面

这些描述信息相当于一份目录。Claude 知道有哪些 Skill 可用、每个大概做什么,但 SKILL.md 的正文一个字都没加载。

Level 2:指令正文(触发时加载)

你发了一条指令,Claude Code 能判断是否需要加载某个 Skill 来完成任务。

如果匹配上了(或者你直接输入 /skill-name 明确指定了某个 Skill),Claude 才从文件系统读取 SKILL.md 的内容追加到上下文。

读取 SKILL.md 后,Claude Code 会根据 SKILL.md 的正文内容判断是否需要加载附属资源(脚本、模板、参考文档)。

官方建议 SKILL.md 仅作为「操作手册」,内容控制在 500 行以内,具体的执行细节放在脚本、参考文档中作为附属资源

Claude Code 是如何匹配 Skill 的?

如果你理解了前文 实现一个 chatbot 中 LLM 的调用过程,应该就很容易理解这里面的原理。

Claude Code 判断是否需要 Skill,并不是一个独立的关键词匹配算法,而是 LLM 自身的语言理解能力:

Claude Code 的 System Prompt 里面肯定写清楚了 Skill 是个什么东西,并写明了如何加载和使用 Skill。

所有 Skill 的 description 和用户的指令都追加到了上下文(messages 数组)中,所以 Claude Code 可以判断是否需要加载某个 Skill 来完成任务。

比如前面举例的 img-optimize 的 description 写「压缩、缩放图片并转换为 WebP 格式」,你随口说「帮我把截图弄小点」,Claude Code 也能理解并调用 img-optimize Skill 来完成任务。

Level 3+:附属资源(按需加载)

Skill 目录里的脚本、模板、参考文档,只在执行过程中需要时才读取。

关键在于,Claude 并不需要「读懂」一个脚本才能用它。SKILL.md 的正文就是操作手册,里面写清楚了这个脚本干什么、接受哪些参数、怎么调用。Claude 照着手册执行就行,脚本源码本身不需要加载到上下文里。

比如前面 img-optimize 的 SKILL.md 里写了 uv run ${CLAUDE_SKILL_DIR}/scripts/optimize.py <图片路径>,Claude 只需要知道这行命令和参数含义,不需要读 optimize.py 的 Python 代码。执行后也只有脚本的输出结果进入上下文,源码始终不占位置。

Skill 脚本的编写技巧

针对 Skill 的这种加载机制,我总结了几条编写 Skill 脚本的最佳实践:

  • 脚本内容不会进入上下文,所以 SKILL.md 中记录的脚本用法一定要正确,脚本最好提供 -h 显示详尽的用法说明,供 Claude Code 使用时参考。
  • 如果脚本出现异常,一定要说明异常的原因,以及排查方法。这是个非常有用的技巧,你不需要在 SKILL.md 中穷举所有可能的异常情况,而是把脚本的报错输出作为 SKILL.md 的补充,这样就能辅助 Claude Code 更快更准确地解决问题。

SKILL.md 的关键细节

前面已经展示了 Skill 的基本用法和加载原理,这里集中讲一下 YAML 头部几个重要的字段。

allowed-tools 和 ${CLAUDE_SKILL_DIR}

Claude Code 在执行 bash 命令等操作前,默认会暂停等你确认。allowed-tools 可以让指定的操作跳过确认,直接执行。格式是 工具名(匹配模式),其中 Bash 是 Claude Code 内置的命令行工具,括号里用 * 做通配符。比如 Bash(uv run *) 允许所有 uv run 开头的命令,Bash(git *) 允许所有 git 命令。

${CLAUDE_SKILL_DIR} 是 Claude Code 加载 Skill 时自动展开的内置变量,指向 SKILL.md 所在的目录。它不是系统环境变量,只在 SKILL.md 里有效。

disable-model-invocation 和 $ARGUMENTS

disable-model-invocation: true 禁止 Claude Code 自动触发这个 Skill,只能手动用 /name 调用。从加载角度看,设了这个标记后 Level 1 直接跳过它,description 不进上下文,Claude 完全不知道这个 Skill 的存在。部署、发布这类有副作用的操作适合这么设。

$ARGUMENTS 是正文里的占位符,接收用户传入的参数。比如 /fix-issue 42 会把 $ARGUMENTS 展开为 42

根据我的实践,这个 $ARGUMENTS 变量用的不多,因为编写 Skill 不需要像 shell 脚本那样精确,毕竟 LLM 的理解力是很强的。

直接用 /<skill-name> 的方式指定 Skill,然后用自然语言描述清楚想用 Skill 做什么,Claude Code 就能准确的理解并调用。

用 skill-creator 自动生成 Skill

手写 SKILL.md 当然没问题,但有个更省事的办法。

Anthropic 官方维护了一个 skill-creator Skill,装上之后你只需要用自然语言描述想让 Claude Code 做什么,它会自动创建完整的 Skill 目录结构、写好 SKILL.md,需要脚本的地方也会帮你生成。

安装方式很简单,从 GitHub 仓库复制到个人 Skill 目录:

装好后,在 Claude Code 里输入 /skill-creator,然后描述你的需求就行:

Claude Code 会分析你的需求,决定哪些部分适合用脚本固化、哪些用文本描述,然后自动生成完整的 Skill。你看一眼生成的内容,觉得合适就直接用。

skill-creator 效果不错,因为它的 SKILL.md 里沉淀了不少写 Skill 的最佳实践:description 怎么写匹配率更高,什么时候该用脚本,文件怎么组织。

你也可以直接读一下它的 SKILL.md,本身就是一份很好的 Skill 编写指南。

如果你现在已经想到了几个可以做成 Skill 的重复性流程,装上 skill-creator,描述清楚需求,让 Claude Code 自己搞定吧!

上一篇
下一篇