1. 从“skills”这个标题说起:它到底指什么
第一次看到“skills”这个标题,很多人会以为是某个泛泛的能力清单,或者一份简历上的技能罗列。但结合热搜词里高频出现的 Claude Code、Codex、agents、plugin 这些词,基本可以判断,这里说的 skills 不是人类职场技能,而是围绕 AI 编程代理(coding agent)构建的一套可插拔能力单元。简单说,就是给 Claude Code、Codex 这类命令行 AI 助手装上“技能包”,让它在特定任务上表现得更专业、更稳定、更符合你的项目规范。
我接触这套东西的起点很朴素:用 Claude Code 写代码时,发现它默认什么都懂一点,但什么都不精。你让它按团队规范写一个 React 组件,它可能给你三种不同风格;你让它处理数据库迁移,它可能忽略你项目里已有的 migration 工具。问题不在于模型不够强,而在于它缺少针对你项目上下文的“技能约束”。skills 就是来解决这个问题的——它把某类任务的最佳实践、工具调用方式、输出格式固化成一个可复用的模块,代理在需要时自动加载。
这套机制能做什么?往小了说,你可以写一个“生成符合 ESLint 规范的提交信息”的 skill;往大了说,你可以构建一整套“前端开发 skills”,覆盖组件生成、样式规范、测试编写、构建优化。适合谁来参考?三类人最受益:一是日常用 Claude Code 或 Codex 写代码的开发者,二是想给团队统一 AI 辅助编码规范的 tech lead,三是正在研究 agent 架构、想理解“能力插件化”思路的工程师。哪怕你只是刚装好 Claude Code 的新手,理解 skills 也能让你少走很多弯路。
2. 核心思路拆解:为什么是“技能”而不是“提示词”
2.1 提示词的天花板在哪里
大多数人用 AI 编程工具的方式,是在对话里临时写一段提示词:“帮我写一个登录页面,用 React,样式用 Tailwind,要响应式。”这种方式在单次任务里够用,但一旦任务重复出现,问题就暴露了。你每次都要重新描述一遍规范,稍有遗漏,输出就不一致。更麻烦的是,提示词是无状态的,它不会记住你上次纠正过的错误,也不会自动调用你项目里的工具链。
我试过把常用提示词存成文本片段,用的时候粘贴进去。短期有效,但很快发现两个问题:一是片段越来越长,维护成本高;二是不同任务需要不同片段组合,手动拼接容易出错。这本质上是在用“人肉”做技能调度,效率低且不可靠。
2.2 skills 的本质:把能力封装成可发现、可加载的模块
skills 的思路完全不同。它把一类任务的处理方式写成一个独立文件(通常是 Markdown 或特定格式的配置),放在约定目录下。代理启动时或执行任务前,会扫描这些 skills,根据任务描述匹配最合适的技能,然后加载对应的指令、工具定义和示例。这个过程是自动的、可组合的、可版本控制的。
打个比方:提示词像是你每次做饭前口头告诉厨师“少放盐、多放醋、火候中等”;skills 则是把菜谱写成标准卡片,厨师看到“糖醋排骨”就知道该拿哪张卡,按上面的步骤和用量操作。菜谱可以不断迭代,可以分享给其他厨师,也可以组合成套餐。
2.3 为什么 Claude Code 和 Codex 都往这个方向走
Claude Code 和 Codex 作为命令行代理,核心交互是“自然语言指令 → 工具调用 → 结果反馈”。如果所有逻辑都塞进系统提示词,会导致上下文膨胀、匹配精度下降。skills 机制把能力外置,代理只需要知道“有哪些技能可用”以及“什么时候调用”,具体细节在加载时才注入。这样既保持了核心提示词的简洁,又让能力可以无限扩展。
从工程角度看,这符合关注点分离原则。代理框架负责调度和工具执行,skills 负责领域知识和操作规范。团队可以独立开发、测试、发布自己的 skills,而不需要改动代理本身。这也是为什么热搜里会出现“skills开发”“claude agent skills: a first principles deep dive”这类词——大家已经意识到,这不是简单的配置技巧,而是一种新的扩展范式。
3. 核心细节解析:一个 skill 到底由什么组成
3.1 元数据:让代理知道“我是谁、什么时候用我”
每个 skill 都需要一段元数据,通常包括名称、描述、触发条件、适用场景。名称要短且唯一,描述要精准概括能力边界。触发条件是关键——它决定了代理在什么情况下会考虑加载这个 skill。比如一个“前端组件生成”skill,触发条件可能是“用户要求创建或修改 UI 组件”,而不是“用户提到前端”。
我踩过的坑是描述写得太宽泛。早期我写了一个“代码审查”skill,描述是“帮助审查代码”。结果代理在任何涉及代码的对话里都试图加载它,导致上下文被无关指令占满,反而降低了响应质量。后来改成“当用户明确要求审查特定文件的代码质量、安全检查或性能问题时使用”,匹配精度立刻提升。
3.2 指令正文:把“怎么做”写到可执行的程度
元数据之后是技能正文,这是真正干活的部分。好的 skill 正文不是泛泛而谈,而是步骤化、可执行、带示例的。以“生成符合团队规范的 React 组件”为例,正文应该包含:文件命名规则、组件结构模板、样式方案(CSS Modules 还是 Tailwind)、状态管理约定、测试文件生成要求、导出方式等。
这里有个经验:能写成检查清单的,就不要写成段落。代理在执行时更容易逐条对照。比如:
- 组件文件使用 PascalCase 命名,如
UserProfile.tsx - 默认导出组件,同时具名导出 Props 类型
- 样式优先使用 Tailwind 类名,复杂样式抽离到
styles.module.css - 必须包含至少一个基础渲染测试
这种结构让代理的输出稳定性大幅提升。实测下来,同一任务连续执行十次,输出结构一致性从原来的六成提升到九成以上。
3.3 工具与依赖声明:告诉代理“你可以用什么”
如果 skill 需要调用外部工具(如执行测试命令、调用 API、读写特定文件),需要在 skill 中声明。这类似于给代理一张“工具许可证”,避免它越权操作或使用未授权的命令。比如一个“数据库迁移”skill 可能声明需要psql、prisma和文件写入权限。
注意:工具声明要遵循最小权限原则。只声明完成该技能必需的工具,不要图省事把所有工具都打开。权限越大,代理误操作的风险越高。
3.4 示例与反例:比指令更有说服力的锚点
人类学习新技能时,看示例比看说明书快。代理也一样。在 skill 中嵌入一两个输入输出示例,能显著提升匹配和执行准确率。示例要覆盖典型场景,反例则说明“什么情况下不要用这个技能”或“常见错误输出长什么样”。
我习惯在 skill 末尾放一个“常见错误”小节,列出三到五个典型问题。比如“不要生成 class 组件”“不要在组件内直接写 fetch 调用”“不要省略 PropTypes 或 TypeScript 类型”。这些反例往往比正面指令更能约束代理行为。
4. 实操过程:从零搭建一个可用的 skill
4.1 环境准备与目录约定
不同代理对 skills 的存放位置有不同约定。Claude Code 通常识别项目根目录下的.claude/skills/或用户主目录下的配置目录;Codex 则可能使用.codex/skills/或通过插件机制加载。具体路径以你所用版本的文档为准,但核心逻辑一致:代理会在启动时扫描约定目录,加载所有合法 skill 的元数据。
我的做法是在项目根目录建一个skills/文件夹,然后在代理配置中指向它。这样 skills 可以随项目一起版本控制,团队成员拉取代码后自动获得相同的技能集。对于跨项目通用的 skills,再单独放到用户级目录。
# 项目级 skills 目录结构示例 project-root/ skills/ react-component/ SKILL.md examples/ input.md output.md commit-message/ SKILL.md db-migration/ SKILL.md templates/ migration.sql4.2 编写第一个 skill:以“规范提交信息”为例
选这个例子是因为它足够简单,又能立刻看到效果。团队里提交信息格式不统一是常见痛点,用 skill 约束代理生成规范的 commit message 很实用。
先写元数据:
--- name: conventional-commit description: 根据代码变更生成符合 Conventional Commits 规范的提交信息 trigger: 当用户要求生成提交信息、commit message 或总结代码变更时 ---然后是正文:
## 任务 根据 git diff 内容生成一条提交信息。 ## 格式 <type>(<scope>): <subject> ## 类型 - feat: 新功能 - fix: 修复缺陷 - docs: 文档变更 - style: 格式调整 - refactor: 重构 - test: 测试相关 - chore: 构建或工具变更 ## 规则 - subject 使用中文,不超过 50 字 - 不以句号结尾 - scope 可选,用模块名 - 如果变更涉及多个类型,选择影响最大的一个 ## 示例 输入:新增了用户登录接口和对应的单元测试 输出:feat(auth): 新增用户登录接口及单元测试写完放到skills/commit-message/SKILL.md,重启代理或触发重新加载。之后你让代理“帮我写个提交信息”,它就会自动匹配这个 skill,按规范输出。
4.3 参数计算与选择:触发阈值的调整
有些代理框架允许设置 skill 的匹配阈值或优先级。比如当多个 skill 都可能匹配时,优先级高的先加载。这个参数没有统一标准,需要根据实际使用调整。我的经验是:专用 skill 优先级高于通用 skill。比如“React 组件生成”比“前端代码生成”更专用,应该优先匹配。
如果发现代理频繁加载错误的 skill,先检查触发描述是否重叠。两个 skill 都写“当用户要求写代码时”,必然冲突。解决办法是细化触发条件,或者合并成一个更通用的 skill,在内部根据任务类型分支处理。
4.4 测试与迭代:怎么知道 skill 生效了
最直接的验证方式是构造一个典型任务,观察代理是否加载了目标 skill,以及输出是否符合预期。Claude Code 和 Codex 通常会在执行日志中显示加载了哪些 skill。如果没有日志,可以通过输出格式反推——如果代理突然开始严格遵循你定义的模板,说明 skill 生效了。
我习惯为每个 skill 建一个测试用例文件,记录三到五个典型输入和期望输出。每次修改 skill 后跑一遍,确保没有回归。这听起来有点重,但对于团队共享的 skills 来说,这点投入非常值得。
5. 常见问题与排查技巧实录
5.1 skill 不生效的几种典型原因
| 现象 | 可能原因 | 排查方法 |
|---|---|---|
| 代理完全不加载 skill | 目录路径不对或文件格式错误 | 检查代理文档中的约定路径,确认文件扩展名和元数据格式 |
| 加载了但输出不符合预期 | 指令不够具体或示例有歧义 | 增加检查清单和反例,把模糊描述改成可验证的规则 |
| 多个 skill 冲突 | 触发条件重叠 | 细化触发描述,或设置优先级 |
| 时好时坏 | 触发描述过于宽泛 | 收窄触发条件,增加否定条件 |
| 加载后代理变慢 | skill 正文过长 | 拆分 skill,把非核心内容移到引用文件 |
5.2 触发描述写不好的三个坑
第一个坑是用技术词描述用户意图。比如写“当用户输入包含 useState 时触发”,但用户可能说“帮我加个计数器”,根本不提 useState。触发描述应该匹配用户的自然语言表达,而不是技术实现。
第二个坑是否定条件缺失。只写“当用户要求写代码时”,没写“但不要用于代码审查或调试”。结果代理在审查代码时也加载生成 skill,输出一堆新代码而不是审查意见。
第三个坑是描述太长。触发描述是给代理做匹配用的,不是给人读的文档。控制在两三句话内,把核心意图和边界说清楚就够了。
5.3 团队协作中的 skill 管理
当多人维护 skills 时,命名冲突和职责重叠是主要问题。我们的做法是建立命名规范:<领域>-<功能>,比如frontend-component、backend-migration、devops-deploy。每个 skill 指定一个 owner,修改需要 review。定期清理不再使用的 skill,避免代理加载一堆僵尸技能。
另外,skills 的版本要和项目代码版本关联。如果项目升级了框架版本,对应的 skill 也要更新。我们会在 skill 元数据里加一个compatibleWith字段,记录适用的项目版本范围,方便排查兼容性问题。
5.4 性能与上下文占用的平衡
skills 不是越多越好。每个加载的 skill 都会占用上下文窗口,影响代理的响应速度和推理质量。我的经验是:单个任务加载的 skill 不超过三个,每个 skill 正文控制在 500 到 1500 字之间。如果某个 skill 经常需要和其他 skill 一起用,考虑合并成一个组合 skill。
对于内容特别多的领域知识,不要全塞进 skill 正文,而是放到外部文件里,在 skill 中声明引用路径。代理需要时再读取,避免一次性占满上下文。
6. 进阶玩法:从单技能到技能编排
6.1 组合技能解决复杂任务
单个 skill 解决单点问题,但真实开发任务往往是多步骤的。比如“新增一个 API 接口”可能涉及:生成路由、写控制器、加数据校验、写测试、更新文档。你可以为每个步骤写一个 skill,然后让代理按顺序调用。更优雅的方式是写一个“编排 skill”,在正文中定义步骤顺序和依赖关系,代理加载后自动按流程执行。
我试过一个“全栈功能开发”编排 skill,内部依次调用数据库迁移、后端接口、前端组件、测试生成四个子 skill。实测下来,完成一个简单 CRUD 功能的时间从原来的多轮对话缩短到一次指令,且输出一致性明显提升。
6.2 动态参数与条件分支
高级 skill 可以接受参数,根据参数值走不同分支。比如一个“生成组件”skill,参数style可以是tailwind或css-modules,代理根据参数选择不同的样式方案。这需要在 skill 中定义参数 schema 和分支逻辑,具体语法取决于代理框架的支持程度。
目前 Claude Code 和 Codex 对参数化 skill 的支持还在演进中,但基本思路是:在元数据中声明参数,在正文中用条件语句描述不同参数下的行为。如果你的代理版本不支持,可以用多个专用 skill 替代,比如component-tailwind和component-css-modules。
6.3 与插件系统的关系
热搜里同时出现了 skills 和 plugin,两者容易混淆。简单区分:plugin 通常是代理级别的扩展,可以添加新工具、新命令、新界面;skill 是任务级别的能力封装,主要影响代理在特定任务中的行为。plugin 解决“代理能做什么”,skill 解决“代理怎么做”。
有些框架把 skill 作为一种 plugin 类型来加载,有些则分开管理。理解这个区别有助于你决定把某个能力写成 plugin 还是 skill。如果它需要注册新工具或修改代理核心行为,写成 plugin;如果只是约束输出格式和操作步骤,写成 skill 更轻量。
7. 我踩过的坑和最后分享几个实用技巧
第一个坑是过早追求大而全的 skill。我一开始想写一个“前端开发全能 skill”,结果正文写了三千多字,代理加载后反而不知道该聚焦什么,输出质量下降。后来拆成组件、样式、测试、构建四个独立 skill,每个都短小精悍,效果立竿见影。skill 的粒度应该和任务粒度对齐,而不是和领域对齐。
第二个坑是忽略 skill 的维护成本。skills 是代码,会过时、会有 bug、需要测试。如果团队没有把 skill 纳入代码审查和 CI 流程,它们很快就会变成没人敢动的遗留配置。我们的做法是把 skills 放在项目仓库里,和代码一起 review,修改 skill 必须附带测试用例更新。
第三个坑是在 skill 里写死具体路径和版本号。项目结构一变,skill 就失效。尽量用相对路径和变量引用,把环境相关的配置抽到外部。
最后分享一个实用技巧:给每个 skill 加一个“自检清单”。在正文末尾列出三到五条验证规则,代理生成结果后可以自己对照检查。比如“确认文件扩展名是 .tsx”“确认导出了 Props 类型”“确认没有使用 any”。这个自检步骤能拦截大部分低级错误,减少来回修改的次数。我实测下来,加了自检清单的 skill,一次通过率能提升三成左右。
还有一个技巧是用真实项目中的失败案例来迭代 skill。每次代理输出不符合预期,不要只改这一次的结果,而是回头想想 skill 里缺了什么约束。把这次的问题转化成一条新规则或一个反例,skill 就会越来越稳。这本质上是在用真实反馈训练你的技能库,比凭空设计有效得多。