1. 从"agent-skills"说起:一个被低估的工程化命题
第一次看到agent-skills这个词,很多人会下意识地把它理解成"给 AI 智能体写提示词"。这个理解不算错,但太浅了。真正在 AI coding agents 这条线上摸爬滚打过一段时间的人会明白,agent-skills本质上是一套把人类工程经验沉淀成可复用、可组合、可测试的能力单元的方法论。它解决的不是"怎么让 AI 更聪明",而是"怎么让 AI 在具体项目里稳定地做对事"。
我接触 Claude Code 这类终端型 AI coding agent 有一段时间了,从最早的"哇它能直接改文件"到后来的"为什么它老是改错地方",中间踩的坑基本都指向同一个根因:agent 缺的不是智力,是技能边界。你让它写一个 React 组件,它能写得有模有样;但你让它按你团队的规范写、按你项目的目录结构写、按你既有的测试风格写,它就开始飘了。agent-skills要处理的,正是这个"飘"的问题。
这篇文章适合三类人看:一是已经在用 Claude Code、Cursor、各类 AI coding agent,但总觉得输出不稳定、想找一套系统化约束方法的人;二是团队里负责搭建 AI 辅助开发流程、需要把个人经验变成团队资产的人;三是对skills CLI、test-driven-development这类关键词感兴趣,想搞清楚它们和 agent 能力建设之间关系的人。不管你是刚装完 Claude Code 的新手,还是已经能熟练用cc switch切换不同模型的老手,下面这些内容应该都能给你一些可以直接抄作业的东西。
我会从设计思路讲到具体实现,从技能拆解讲到测试驱动,尽量把"为什么这么设计"讲透,而不是只丢一堆配置让你照抄。因为agent-skills这东西,抄配置只能解决 30% 的问题,剩下 70% 靠的是你对自己项目工作流的理解。
2. 核心设计思路:为什么 agent 需要"技能"而不是"提示词"
2.1 提示词的天花板在哪里
先说个我自己的真实经历。早期我用 Claude Code 的时候,习惯在项目根目录放一个CLAUDE.md,把项目规范、技术栈、注意事项全塞进去。一开始挺爽,agent 确实听话了不少。但项目一复杂,问题就来了:这个文件越写越长,从 200 行涨到 800 行,最后 agent 开始"选择性失忆"——它记住了前半段,忘了后半段;或者把 A 模块的规范套用到 B 模块上。
这不是模型不行,是上下文的结构问题。提示词是线性的、扁平的,而真实项目是模块化的、有层次的。你把所有规则平铺在一个文件里,agent 每次都要在噪音里找信号,效率自然低。
agent-skills的核心思路就是把这个扁平结构打散,变成按需加载的能力包。每个 skill 是一个独立单元,有自己的触发条件、自己的上下文、自己的验证方式。agent 在处理某个具体任务时,只加载相关的 skill,而不是把整个知识库都塞进上下文。这个思路和微服务拆分、和前端按需加载是同一个逻辑——降低单次决策的认知负荷。
2.2 一个 skill 应该长什么样
我理解的合格 skill,至少包含四个部分:
- 触发描述:什么情况下该用这个 skill。比如"当需要新增一个 API 端点时"。
- 操作步骤:具体怎么做,最好是可执行的、有顺序的。
- 约束条件:不能做什么,边界在哪里。这部分最容易被忽略,但恰恰最重要。
- 验证方法:做完之后怎么确认做对了。这一步直接关联到
test-driven-development。
举个具体例子。假设你的项目是 Node.js + Express,你想让 agent 规范地新增 API。一个粗糙的提示词写法是"新增 API 时请遵循 RESTful 规范"。这句话对 agent 来说几乎等于没说,因为"RESTful 规范"在不同项目里含义完全不同。
而一个 skill 化的写法是这样的:
## Skill: 新增 API 端点 ### 触发条件 用户要求新增、修改或删除 HTTP 接口时。 ### 操作步骤 1. 在 `src/routes/` 下找到对应的路由文件,不存在则新建 2. 路由处理函数统一放在 `src/controllers/`,路由文件只做转发 3. 请求参数校验使用 `zod`,schema 定义在 `src/schemas/` 4. 数据库操作统一走 `src/services/`,controller 不直接调 ORM 5. 新增端点后,在 `tests/api/` 下补充对应的集成测试 ### 约束条件 - 禁止在 controller 里写 SQL 或 ORM 查询 - 禁止跳过参数校验直接使用 req.body - 错误响应统一使用 `src/utils/errorHandler.js` 的格式 ### 验证方法 - 运行 `npm run test:api`,新增端点的测试必须通过 - 用 `curl` 手动打一次,确认返回结构符合 `{ code, data, message }` 格式你看,这个 skill 里没有一句废话,全是可执行、可验证的东西。agent 拿到它,不需要"理解"你的项目哲学,只需要按步骤执行、按约束检查、按验证确认。这就是agent-skills和普通提示词的本质区别:前者是操作手册,后者是价值观宣言。
2.3 为什么这套东西值得工程化
有人可能会问:我直接写在CLAUDE.md里不行吗?行,但有几个问题。
第一是复用性。你团队有 5 个项目,技术栈类似但细节不同。如果每个项目都写一份完整的规范,维护成本极高。skill 化之后,公共部分抽出来共享,项目特有的部分单独覆盖,改一处就能影响所有项目。
第二是可测试性。这是test-driven-development和agent-skills结合的关键点。一个 skill 写得好不好,不能靠感觉,要靠测试。你可以设计一组任务,让 agent 在加载 skill 前后分别执行,对比通过率。skill 是可以被"单元测试"的,提示词不行。
第三是版本管理。skill 是文件,可以进 git,可以 code review,可以回滚。你改了一个 skill 导致 agent 行为变差,git revert就完事了。而提示词散落在各种对话里,改坏了你都不知道是哪次改的。
3. 核心细节解析:skills CLI 与目录结构设计
3.1 skills CLI 到底解决什么问题
skills CLI这类工具的出现,本质是为了解决 skill 的分发和加载问题。你可以手动把 skill 文件放到项目里,但当 skill 数量上到几十个、需要在多个项目间同步、需要区分全局 skill 和项目 skill 时,手动管理就崩了。
CLI 通常提供几个核心能力:安装 skill(从本地或远程源)、列出已安装 skill、启用/禁用某个 skill、更新 skill 版本。这听起来很朴素,但实际用起来差别很大。我见过有人把 skill 直接 commit 到项目仓库,结果每次更新都要手动同步到所有项目;也见过有人用 CLI 管理,一条命令搞定所有项目的 skill 更新。
提示:选 CLI 工具时,重点看它是否支持"项目级覆盖全局级"的机制。因为团队公共 skill 和项目特有 skill 经常冲突,没有覆盖机制的话,你只能二选一。
3.2 目录结构怎么设计才不混乱
我试过几种目录结构,最后稳定下来的方案是这样的:
.agent-skills/ ├── global/ # 全局通用 skill │ ├── git-commit.md │ ├── code-review.md │ └── error-handling.md ├── project/ # 项目特有 skill │ ├── api-endpoint.md │ ├── db-migration.md │ └── deploy-check.md ├── config.json # skill 加载配置 └── tests/ # skill 的验证用例 ├── api-endpoint.test.md └── db-migration.test.md这个结构的关键在于分层。global/放的是跨项目通用的能力,比如"怎么写 commit message"、"怎么做 code review";project/放的是这个项目独有的,比如"我们的 API 端点怎么加"、"数据库迁移走什么流程"。config.json控制加载顺序和优先级,tests/放验证用例。
为什么要把测试单独放?因为 skill 的测试和代码测试不一样。代码测试跑的是断言,skill 测试跑的是"给 agent 一个任务,看它输出是否符合预期"。这种测试更像集成测试,需要单独组织。
3.3 skill 的粒度怎么把握
这是最容易踩坑的地方。粒度太粗,一个 skill 管一大片,agent 还是抓不住重点;粒度太细,skill 数量爆炸,加载和维护都成负担。
我的经验是:一个 skill 对应一个"可独立验证的工作单元"。判断标准很简单——如果这个 skill 做完之后,你能用一句话说清楚"做完了什么、怎么验证",那粒度就合适。如果说不清楚,说明它太粗;如果一句话里包含了好几个"然后",说明它太细。
举个例子。"新增 API 端点"是一个合适的粒度,因为它有明确的输入(需求)、明确的输出(可用的端点)、明确的验证(测试通过)。而"处理用户相关逻辑"就太粗了,它可能包含注册、登录、权限、资料修改等一堆事。反过来,"在路由文件里加一行 import"就太细了,这种细节应该写在"新增 API 端点"的步骤里,而不是单独成 skill。
4. 实操过程:从零搭建一套 agent-skills 体系
4.1 第一步:盘点你项目里的高频操作
别急着写 skill,先花半天时间做一件事:记录你和 agent 协作时,最常让它做的 10 件事。不用很精确,凭印象列就行。我自己的清单大概是这样的:
- 新增/修改 API 端点
- 写数据库迁移脚本
- 修复 bug(尤其是测试报错)
- 重构某个模块
- 补充单元测试
- 写 commit message
- 处理依赖升级
- 写文档注释
- 排查构建/部署问题
- 代码 review
这 10 件事里,前 5 件是高频且高价值的,优先给它们写 skill。后 5 件可以先用通用 skill 兜着,后面再细化。
4.2 第二步:为每个高频操作写 skill 草稿
写 skill 有个技巧:先写验证方法,再写操作步骤。因为验证方法决定了这个 skill 的边界,边界清楚了,步骤自然就好写。
以"修复 bug"为例。验证方法是什么?最直接的是"相关测试从红变绿"。那 skill 的边界就清楚了:它处理的是"有测试覆盖的 bug"。如果 bug 没有测试覆盖,那这个 skill 的第一步应该是"先补一个能复现 bug 的测试",然后再修。
草稿可以这样写:
## Skill: 修复有测试覆盖的 Bug ### 触发条件 用户报告某个测试失败,或某个功能行为不符合预期且已有测试覆盖。 ### 操作步骤 1. 运行相关测试,确认失败现象,记录错误信息 2. 阅读测试代码,理解测试期望的行为 3. 定位到实现代码,分析失败原因 4. 修改实现代码,最小化改动范围 5. 重新运行测试,确认通过 6. 运行完整测试套件,确认没有引入回归 ### 约束条件 - 禁止为了让测试通过而修改测试代码(除非测试本身写错了) - 禁止大范围重构,bug 修复只做必要改动 - 如果发现是测试写错了,必须明确说明理由 ### 验证方法 - 目标测试从失败变为通过 - 完整测试套件全部通过 - 改动范围不超过 3 个文件(超过则需说明理由)这个 skill 里,"禁止修改测试代码"这条约束特别重要。我见过太多次 agent 为了让测试通过,直接把断言改了,表面上"修好了",实际上把 bug 藏起来了。这条约束就是防这个的。
4.3 第三步:用 test-driven-development 验证 skill
skill 写完不是终点,是起点。接下来要做的是用测试驱动的方式验证 skill 是否有效。
具体怎么做?设计一组任务,每个任务对应一个 skill,然后让 agent 在两种条件下执行:不加载 skill 和加载 skill。对比结果。
我拿"新增 API 端点"这个 skill 做过测试。任务是"给用户模块新增一个查询用户列表的接口"。不加载 skill 时,agent 的输出是这样的:
- 直接在路由文件里写了处理逻辑
- 参数校验用了手写的 if-else
- 数据库查询直接写在路由里
- 没有补测试
加载 skill 后:
- 路由文件只做转发,逻辑在 controller
- 参数校验用了 zod schema
- 数据库查询走了 service 层
- 补了集成测试
差别非常明显。这个对比过程本身就是 skill 的"测试报告",你可以把它记录下来,作为 skill 有效性的证据。
注意:skill 测试不要只测一次。模型会更新,你的项目会变化,skill 的有效性也会漂移。建议每个月跑一次回归测试,尤其是升级 Claude Code 版本或切换模型之后。
4.4 第四步:把 skill 接入 Claude Code
Claude Code 加载 skill 的方式,取决于你用的版本和配置。常见做法有两种:一种是通过CLAUDE.md引用 skill 文件,另一种是通过 CLI 工具自动注入。
我倾向后者,因为手动引用容易漏。配置大概长这样:
{ "skills": { "global": [".agent-skills/global/*.md"], "project": [".agent-skills/project/*.md"], "priority": "project-over-global" } }priority这个字段很关键。当全局 skill 和项目 skill 冲突时,项目 skill 优先。比如全局 skill 说"commit message 用英文",项目 skill 说"用中文",那这个项目就用中文。
接入之后,建议做一次冒烟测试:随便让 agent 做一件小事,看它有没有按 skill 走。如果没走,检查两个地方——skill 文件路径对不对,触发条件写得够不够明确。
4.5 第五步:迭代和沉淀
skill 体系不是一次搭完就完事的。我的做法是每周花半小时做一次"skill 复盘":回顾这周 agent 做错的事,看是不是某个 skill 没覆盖到,或者覆盖了但写得不够清楚。
复盘时我会问三个问题:
- 这个错误是 skill 缺失导致的,还是 skill 存在但没触发?
- 如果是没触发,触发条件是不是写得太窄了?
- 如果是触发了但做错了,是步骤不清楚还是约束不够?
这三个问题能帮你精准定位问题,而不是笼统地"再改改 skill"。
5. 常见问题与排查技巧实录
5.1 skill 不生效的几种典型情况
这是被问得最多的问题。我整理了一个排查表,按出现频率排序:
| 现象 | 可能原因 | 排查方法 |
|---|---|---|
| agent 完全无视 skill | skill 文件没被加载 | 检查 config.json 路径,确认文件存在 |
| agent 偶尔遵守偶尔不遵守 | 触发条件太模糊 | 把触发条件改得更具体,加上关键词 |
| agent 遵守了但做错 | 步骤描述有歧义 | 把步骤拆得更细,每步只做一件事 |
| agent 遵守了但过度执行 | 约束条件缺失 | 补充"禁止"类约束 |
| 多个 skill 冲突 | 优先级没配好 | 检查 priority 配置,明确覆盖关系 |
我遇到最多的是第二种——触发条件太模糊。比如写"当需要处理数据时",这个"处理数据"太宽泛了,agent 根本不知道什么时候该用。改成"当需要新增、修改或删除数据库记录时",命中率立刻上去了。
5.2 skill 写多长才合适
这个问题没有标准答案,但有个经验值:单个 skill 控制在 50 到 150 行之间。低于 50 行,通常说明粒度太细或者内容太单薄;高于 150 行,agent 的注意力会分散,执行质量下降。
如果你的 skill 超过 150 行,考虑拆成两个。拆分点通常在这几个地方:操作步骤超过 8 步、约束条件超过 6 条、或者出现了明显的"阶段划分"(比如"先做 A 阶段,再做 B 阶段")。
5.3 怎么处理 skill 和项目现有规范的冲突
这是个组织问题,不是技术问题。我的建议是:skill 应该反映项目实际规范,而不是理想规范。如果项目现有代码风格很乱,你写一个"理想风格"的 skill,agent 会按理想风格写新代码,结果新老代码风格不一致,反而更乱。
正确做法是分两步走:先写一个"跟随现有风格"的 skill,让 agent 模仿周围代码;等团队决定统一风格后,再更新 skill。这样过渡更平滑。
5.4 独家避坑技巧
分享几个我踩坑踩出来的经验。
第一个坑:别在 skill 里写"尽量"、"最好"这类词。agent 对这类模糊词的处理很不稳定。要么写"必须",要么写"禁止",中间态的词只会让 agent 犹豫。
第二个坑:skill 里的示例代码要能跑。我见过有人在 skill 里贴了一段伪代码,结果 agent 照着伪代码写,生成了一堆跑不通的东西。示例代码必须是真实可运行的,哪怕简化过。
第三个坑:定期清理僵尸 skill。项目演进后,有些 skill 已经过时了,但还挂在配置里。这些僵尸 skill 会干扰 agent 的判断。建议每季度清理一次,把不再适用的 skill 归档。
第四个坑:skill 的命名要一致。我一开始命名很随意,有的叫"新增API",有的叫"add-endpoint",有的叫"API开发"。后来统一成"动词-名词"格式,比如"新增-API端点"、"修复-Bug"、"重构-模块",管理起来清爽多了。
6. 影响范围与延展思考
agent-skills这套东西的价值,其实超出了"让 AI 写代码更准"这个层面。它真正改变的是团队知识的组织方式。
传统上,团队经验散落在文档、代码注释、老员工脑子里。新人来了,靠口口相传。agent-skills提供了一种新的载体:把经验写成 agent 能执行、能验证的 skill。这些 skill 既是给 agent 看的,也是给新人看的——因为一个写得好的 skill,本身就是一份高质量的操作手册。
从更长的视角看,这套方法会推动两件事。一是开发流程的显性化。很多团队的工作流是隐性的,大家"凭感觉"做事。写 skill 的过程,就是把这些隐性流程逼出来的过程。二是质量标准的可执行化。以前说"代码要写得好",现在得说清楚"好"的标准是什么,因为 agent 需要明确的判断依据。
我个人的体会是,搭这套体系前期投入不小,大概需要两三周才能跑顺。但一旦跑起来,收益是复利的——每写一个 skill,后面所有相关任务都受益。而且 skill 是可以跨项目复用的,你在这个项目沉淀的经验,下个项目直接拿来用。
最后分享一个小技巧:如果你不确定某个 skill 该怎么写,先别写,先观察。让 agent 做几次相关任务,记录它做对和做错的地方,然后把这些观察整理成 skill。这样写出来的 skill 最接地气,也最有效。