1. 从"skills"这个模糊词说起:它到底指什么
第一次看到"skills"这个标题,很多人会懵。这词太泛了,泛到几乎没法直接对应到任何一个具体的技术点上。但结合热搜词里反复出现的 Claude Code、Codex、agents、plugin 这些词,方向其实就清晰了——这里说的 skills,指的是 AI 编程助手生态里那套"技能包"机制,也就是让 AI agent 具备特定领域能力的可插拔模块。
我最早接触这个概念是在折腾 Claude Code 的时候。当时我以为 skills 就是普通的 prompt 模板,写几段话丢进去就完事了。结果实际用下来才发现完全不是一回事。Skills 更像是一种结构化的能力封装:它有自己的目录结构、有元数据描述、有触发条件、有执行逻辑,甚至还能调用外部工具和脚本。你可以把它理解成给 AI 助手装的"插件",但这个插件不是改 UI 的,而是直接扩展 AI 能做的事情边界。
为什么这个概念现在这么火?因为大家逐渐意识到一个事实:通用大模型再强,落到具体工作场景里总是差那么一口气。你让 Claude 帮你写个 React 组件它没问题,但你让它按照你们团队特定的代码规范、特定的目录结构、特定的 API 封装方式来写,它就开始自由发挥了。Skills 要解决的就是这个"最后一公里"的问题——把领域知识、团队约定、操作流程固化下来,让 AI 每次都能按你要的方式来干活。
这篇文章适合谁看?如果你是刚听说 Claude Code 或者 Codex、想搞清楚 skills 到底怎么用的人,那这篇能帮你建立完整的认知框架。如果你已经在用这些工具但总觉得"差点意思",那这篇里关于 skill 设计思路和踩坑经验的部分应该对你有用。如果你是想自己开发 skill 分享给别人的人,那后面关于结构设计和测试的部分值得细看。
需要先说明一点:skills 这个生态目前还在快速演进中,不同工具(Claude Code、Codex 等)对 skill 的支持方式和实现细节有差异。我下面讲的内容主要基于我实际用过的几个工具,以及社区里比较通行的做法。具体到你用的版本,可能有些细节需要对照官方文档确认。
2. Skills 和普通 Prompt 的本质区别在哪
2.1 从"一次性对话"到"可复用能力"的跨越
大多数人用 AI 编程助手的方式是这样的:打开对话框,敲一段需求,等它生成代码,不满意就继续对话调整。这种方式在单次任务里没问题,但一旦你发现自己反复在做类似的事情——比如每次新建组件都要交代一遍命名规范、每次写 API 都要说明错误处理格式——就说明你需要把这些东西沉淀下来了。
普通 prompt 的问题在于它是"一次性"的。你这次对话里说了一大堆要求,下次开新对话又得重新说一遍。而且 prompt 是线性的,你很难在里面表达复杂的条件逻辑,比如"如果是 React 组件就走这套规范,如果是工具函数就走那套规范"。
Skills 的核心价值就在于它把"能力"从"对话"里抽离出来了。一个 skill 是一个独立的、自包含的单元,它有自己的触发条件、自己的知识库、自己的执行流程。你不需要每次都说,AI 在合适的场景下会自动调用它。这就像你团队里有个专门负责某块业务的同事,你不用每次都跟他从头解释背景,他知道自己该干什么。
2.2 结构化带来的确定性
我举个实际例子来说明这个差异。假设你要让 AI 帮你写数据库迁移脚本。用普通 prompt 的话,你可能会说:"帮我写一个迁移脚本,要加一个 users 表的 email 字段,类型是 varchar(255),不能为空,要加索引。"
AI 会给你生成一段代码,但每次生成的风格可能不一样。有时候用原生 SQL,有时候用 ORM 的迁移 API,索引命名规则也不固定。
如果你把这个需求做成一个 skill,情况就不同了。你可以在 skill 里定义:迁移脚本必须用哪个框架的 API、文件命名必须遵循什么格式、索引命名必须符合什么规则、必须包含回滚逻辑、必须写注释说明变更原因。这些约束是写死在 skill 里的,AI 每次调用都会遵守。
这里的关键认知是:skill 不是让 AI "更聪明",而是让 AI "更可控"。它牺牲了一部分灵活性,换来了确定性和一致性。在团队协作场景里,这个交换通常是值得的。
2.3 触发机制:AI 怎么知道该用哪个 skill
这是很多人容易忽略的一个点。Skill 不是你想让它触发就触发的,它需要一套触发机制。目前主流的做法有两种:
一种是基于描述的自动匹配。每个 skill 都有一个描述字段,说明这个 skill 是干什么的、什么场景下应该使用。AI 在处理你的请求时,会先看一遍所有可用 skill 的描述,判断哪个跟当前任务相关,然后自动加载。
另一种是显式调用。你在对话里直接点名要用哪个 skill,比如"用 database-migration 这个 skill 来写"。
两种方式各有优劣。自动匹配更自然,但依赖描述写得好不好;显式调用更精确,但需要你记住 skill 的名字。实际使用中,我建议两者结合:把描述写清楚让自动匹配能工作,同时在团队文档里列出常用 skill 的名字方便显式调用。
3. 一个 Skill 的内部结构长什么样
3.1 目录组织与核心文件
虽然不同工具的具体实现有差异,但一个典型的 skill 通常包含这几个部分:
my-skill/ ├── SKILL.md # 主描述文件,定义 skill 的元信息和核心逻辑 ├── references/ # 参考资料目录,放领域知识文档 │ ├── style-guide.md │ └── api-conventions.md ├── scripts/ # 可执行脚本,skill 可以调用这些脚本 │ ├── validate.py │ └── generate.sh └── assets/ # 静态资源,比如模板文件 └── template.tsxSKILL.md是整个 skill 的入口。它通常包含两部分:头部是 YAML 格式的元数据,定义 skill 的名称、描述、触发条件等;正文是 Markdown 格式的指令,告诉 AI 在执行这个 skill 时应该怎么做。
元数据部分大概长这样:
--- name: react-component-generator description: 按照团队规范生成 React 组件,包括文件结构、命名规范、样式方案和测试文件 trigger: 当用户要求创建新的 React 组件时使用 ---描述字段特别重要。它不仅是给人看的,也是给 AI 看的。AI 在判断是否调用某个 skill 时,主要依据就是这个描述。所以描述要写得既准确又具体,不能太泛("帮助写代码"这种等于没写),也不能太窄(只覆盖一个非常具体的场景,导致稍微变一下就匹配不上)。
3.2 指令正文的写法
正文部分是你真正告诉 AI "怎么做"的地方。这里有个常见的误区:很多人把它当成 prompt 来写,堆一大堆形容词和要求。但 skill 的正文更适合写成"操作手册"的风格——分步骤、有条件分支、有明确的输入输出定义。
我自己的写法通常是这样的结构:
先写清楚这个 skill 的适用范围和前置条件。比如"这个 skill 用于生成符合团队规范的 React 函数组件,假设项目使用 TypeScript 和 CSS Modules"。
然后写执行步骤。每一步都要具体到可操作的程度。比如"第一步:确认组件名称,使用 PascalCase 命名。第二步:在 src/components/ 下创建同名目录。第三步:生成 index.tsx 文件,包含组件实现……"
最后写验证和边界情况。比如"生成后检查是否所有 props 都有类型定义,如果没有则补充。如果组件涉及异步数据获取,额外生成 hooks 文件。"
一个实操心得:指令正文里尽量用"必须""禁止"这样的强约束词,少用"建议""尽量"。AI 对强约束词的遵守度明显更高。你写"建议使用函数组件",它可能给你写 class 组件;你写"必须使用函数组件,禁止使用 class 组件",它就老实了。
3.3 references 目录的价值
这是我觉得 skill 机制里最被低估的部分。references 目录让你可以把大量的领域知识以文档形式放进去,AI 在执行 skill 时会参考这些文档。
举个例子,你们团队有一份 50 页的前端代码规范文档。你不可能把这 50 页全塞进 SKILL.md 的正文里,那样太臃肿了。但你可以把它拆成几个文件放在 references 里,然后在正文里写"生成代码时参考 references/style-guide.md 中的命名规范和 references/component-patterns.md 中的组件模式"。
这样做的好处是知识可以复用。多个 skill 可以引用同一份 references 文档,改的时候只改一处。而且 references 文档可以写得很详细,不用担心占用正文的篇幅。
4. 实际用起来:从安装到跑通第一个 Skill
4.1 环境准备中最容易卡住的地方
不管你用的是 Claude Code 还是 Codex,安装环节本身通常不复杂,官方文档写得也清楚。但有几个地方是新手特别容易卡住的。
第一个是 Node.js 版本问题。这类工具通常对 Node 版本有要求,太老的版本会报各种奇怪的错。我建议直接用 nvm 或者 fnm 装一个 LTS 版本,别用系统自带的。装完之后用node -v确认一下版本号符合要求。
第二个是网络相关的配置。有些工具在初始化时需要下载依赖或者连接服务,如果你的网络环境有特殊配置,可能会在这一步失败。遇到这种情况先检查代理设置是否正确,再看错误信息里具体是哪个域名连不上。
第三个是权限问题。在 Linux 或者 macOS 上,全局安装可能需要 sudo,但用了 sudo 之后又可能导致后续的文件权限问题。我的建议是尽量用局部安装,或者把 npm 的全局目录配置到用户目录下,避免权限纠缠。
4.2 创建第一个 Skill 的完整流程
假设我们要创建一个最简单的 skill:按照团队规范生成 Git commit message。
第一步,确定 skill 的存放位置。不同工具查找 skill 的路径不一样,常见的有项目根目录下的.skills/文件夹,或者用户主目录下的~/.config/xxx/skills/。你需要确认你用的工具是从哪里加载 skill 的。
第二步,创建目录和文件:
mkdir -p .skills/commit-message touch .skills/commit-message/SKILL.md第三步,编写 SKILL.md:
--- name: commit-message description: 生成符合 Conventional Commits 规范的 Git commit message,自动识别变更类型和影响范围 trigger: 当用户要求生成 commit message 或提交代码时使用 --- # Commit Message 生成规范 ## 格式要求 必须使用以下格式: <type>(<scope>): <subject> ## type 取值 - feat: 新功能 - fix: 修复 bug - docs: 文档变更 - style: 格式调整(不影响代码逻辑) - refactor: 重构 - test: 测试相关 - chore: 构建或工具变更 ## 规则 1. subject 使用中文,不超过 50 个字符 2. subject 结尾不加句号 3. scope 为可选项,表示影响范围 4. 如果有 breaking change,在 body 中说明 ## 示例 feat(user): 添加用户头像上传功能 fix(api): 修复分页参数越界问题 docs: 更新部署文档第四步,测试。在对话里说"帮我生成一个 commit message,我改了用户登录的逻辑,加了一个验证码校验",看 AI 是否按照规范生成了类似feat(auth): 添加登录验证码校验的内容。
4.3 验证 Skill 是否生效的方法
Skill 写完不代表就能用。我踩过的坑里,最常见的就是 skill 没被正确加载。
验证方法有几个:一是看工具的启动日志,通常会打印加载了哪些 skill;二是在对话里直接问"你现在有哪些可用的 skill";三是故意触发一下,看 AI 的行为是否符合 skill 定义。
如果 skill 没生效,排查顺序是:先确认文件路径对不对,再确认 SKILL.md 的格式是否正确(特别是头部的 YAML 有没有语法错误),最后确认描述字段是否足够清晰让 AI 能匹配上。
5. 设计一个好用的 Skill:我总结的几条原则
5.1 单一职责:一个 Skill 只做一件事
这是最重要的原则,也是最容易违反的。很多人一开始热情很高,想做一个"万能 skill",把代码生成、代码审查、文档编写全塞进去。结果就是 AI 在执行时不知道该走哪条路径,输出质量反而下降。
正确的做法是拆。生成组件的 skill 就只管生成组件,审查代码的 skill 就只管审查。如果两个场景有共享的知识,抽到 references 里共用。
拆到什么粒度合适?我的经验是:如果一个 skill 的 SKILL.md 正文超过 200 行,就该考虑拆了。因为太长的指令会让 AI 在执行时"注意力分散",前面的要求可能被后面的覆盖掉。
5.2 描述字段要像"搜索关键词"一样写
前面提过描述字段的重要性,这里展开说一下写法。
好的描述应该包含:这个 skill 做什么、什么场景下用、有什么特殊要求。比如:
description: 生成符合团队规范的 React 函数组件,包含 TypeScript 类型定义、CSS Modules 样式和单元测试文件。适用于新建组件场景,不适用于修改现有组件。这个描述里,"React 函数组件"是做什么,"新建组件场景"是什么时候用,"不适用于修改现有组件"是边界。AI 拿到这个描述,就能比较准确地判断该不该调用。
差的描述长这样:
description: 帮助生成代码这种描述等于没写,AI 根本不知道什么时候该用它。
5.3 给例子比给规则更有效
这是我在实际使用中发现的规律。你在 skill 里写十条规则,不如给两个正例一个反例。
比如你要规范 API 错误处理,与其写"所有 API 调用必须捕获错误并返回统一格式",不如直接给一段示例代码:
// 正确写法 try { const data = await fetchUser(id); return { success: true, data }; } catch (error) { logger.error('Failed to fetch user', { id, error }); return { success: false, error: 'USER_FETCH_FAILED' }; } // 错误写法 - 不要这样 const data = await fetchUser(id); return data;AI 对示例的模仿能力远强于对抽象规则的理解能力。你给的例子越具体、越贴近真实场景,生成的结果就越符合预期。
5.4 留出"逃生舱口"
再好的 skill 也不可能覆盖所有情况。如果约束太死,遇到边界情况时 AI 会强行套用规则,生成不合理的结果。
我的做法是在 skill 里加一条兜底规则:当遇到 skill 未覆盖的情况时,先向用户确认,而不是自行决定。比如:
如果组件需要使用的第三方库不在 references/approved-libs.md 列表中,暂停生成并向用户确认是否可以使用该库。
这条规则看起来简单,但能避免很多"AI 自作主张引入了一个不该用的依赖"的问题。
6. 踩坑实录:那些让我折腾半天的典型问题
6.1 Skill 冲突:两个 Skill 抢同一个任务
我遇到过这样的情况:同时装了react-component和frontend-code-gen两个 skill,结果让 AI 生成组件时,它有时候走第一个,有时候走第二个,输出风格不一致。
根本原因是两个 skill 的描述有重叠,AI 匹配时随机性就上来了。解决办法是明确边界:把frontend-code-gen的描述改成只针对非组件的通用前端代码,把组件场景完全让给react-component。
如果两个 skill 确实有功能重叠且都需要保留,可以在描述里加优先级提示,比如"当与其他 skill 功能重叠时,优先使用本 skill"。
6.2 路径问题:Skill 在本地能用,换台机器就找不到
这个坑很隐蔽。有些工具支持从多个路径加载 skill,包括项目级和用户级。你在自己机器上把 skill 放在用户目录下,用得好好的。同事拉了这个项目,他的用户目录下没有这个 skill,就报错了。
解决方案是把项目相关的 skill 放在项目仓库里(比如.skills/目录),跟着代码一起版本控制。只有那些跨项目通用的个人 skill 才放在用户目录下。
6.3 脚本执行失败:环境变量和依赖缺失
Skill 里调用脚本时,最容易出问题的就是环境。你的脚本依赖某个命令行工具,但你换了个环境,那个工具没装,脚本就挂了。
我的做法是在 skill 的正文里明确写出依赖要求,并且在脚本开头加检查:
#!/bin/bash if ! command -v jq &> /dev/null; then echo "Error: jq is required but not installed." exit 1 fi这样至少报错信息是清晰的,不会让人一头雾水。
6.4 描述写得太好导致"过度触发"
这个坑比较反直觉。有段时间我发现 AI 动不动就调用我的code-reviewskill,哪怕我只是让它解释一段代码。原因是我的描述写得太宽泛了,把"解释代码"也覆盖进去了。
修正方法是给描述加上明确的排除条件:"仅在用户明确要求审查代码质量时使用,不适用于代码解释、代码生成等场景。"
7. 进阶玩法:让 Skills 组合起来干活
7.1 Skill 链式调用
单个 skill 的能力有限,但多个 skill 可以串起来用。比如一个完整的功能开发流程可能是:requirement-analysis→api-design→component-generator→test-generator→code-review。
目前大多数工具不直接支持 skill 之间的自动链式调用,但你可以通过对话引导来实现。比如先用需求分析 skill 产出接口定义,然后说"基于上面的接口定义,用 component-generator 生成组件"。
7.2 用 Skill 封装团队工作流
这是 skills 在团队场景里最有价值的地方。把团队的代码规范、审查清单、部署流程都做成 skill,新同事入职时不用花大量时间读文档,直接通过 AI 助手就能按规范干活。
我见过一个做得比较好的实践:把 code review 的检查项做成 skill,每次提 PR 前让 AI 先跑一遍,把常见问题(命名不规范、缺少错误处理、测试覆盖不足)先筛一遍,人工 review 时只看业务逻辑。这样 review 效率提升很明显。
7.3 跨工具复用 Skill 的思路
Claude Code 和 Codex 的 skill 格式不完全一样,但核心思路是相通的。如果你同时用多个工具,可以考虑把 skill 的核心内容(领域知识、规则、示例)抽成独立的 Markdown 文档,然后针对不同工具写薄薄的适配层。
这样维护成本最低,改规则的时候只改一处,各个工具的适配层不用动。
8. 关于 Skill 测试和迭代的一些经验
Skill 写完只是开始,真正花时间的是测试和迭代。我的做法是建一个测试用例集,每次改完 skill 都跑一遍。
测试用例不需要很复杂,就是几个典型的输入和期望输出。比如对于 commit message skill,测试用例可能是:
| 输入场景 | 期望输出 |
|---|---|
| 新增了用户注册功能 | feat(user): 添加用户注册功能 |
| 修复了登录页面的样式问题 | fix(login): 修复登录页面样式异常 |
| 更新了 README | docs: 更新 README |
每次改完 skill,把这些场景跑一遍,看输出是否符合预期。如果不符合,就调整 skill 里的指令或示例。
迭代时有个原则:每次只改一个地方。如果你同时改了描述、指令和示例,结果输出变好了,你也不知道是哪个改动起的作用。一次改一处,观察效果,再决定下一步。
另外,skill 不是越详细越好。我早期写的 skill 动辄几百行,后来发现很多内容 AI 根本用不上,反而增加了理解成本。现在我的原则是:能用示例说清楚的就不用文字描述,能用一句话说清楚的就不用三句话。
9. 我个人的一些使用体会
用了这段时间的 skills 机制,最大的感受是它改变了我跟 AI 协作的方式。以前我是"每次都要从头交代",现在是"一次配置好,后面自动生效"。这个转变带来的效率提升,在重复性任务上特别明显。
但也要清醒地认识到 skills 的局限。它擅长的是"有明确规则和流程"的任务,对于需要创造性判断的场景,skill 能帮的忙有限。你不能指望一个 skill 帮你做出架构决策,那还是得人来。
还有一个体会是:写 skill 的过程本身就是在梳理团队的知识。很多时候你以为团队已经有一套规范了,真让你写下来的时候才发现到处都是模糊地带。写 skill 逼着你去把这些模糊地带明确化,这个价值可能比 skill 本身还大。
最后分享一个小技巧:如果你不确定某个 skill 该怎么设计,先别急着写 SKILL.md。先用普通对话的方式跟 AI 协作几次,把有效的 prompt 和示例记录下来,等积累够了再整理成 skill。这样写出来的 skill 更接地气,也更实用。