1. 从 agent-skills 说起:为什么它值得单独拿出来聊
第一次看到agent-skills这个词,是在翻 Claude Code 相关生态的时候。当时我的第一反应是:这不就是把 prompt 模板换个马甲吗?但真正上手用了一段时间之后,我发现这个判断错得离谱。agent-skills本质上是一套面向 AI coding agents 的能力封装规范,它把「一个 agent 在特定场景下应该怎么干活」这件事,从散落在各处的提示词、脚本、配置文件里抽出来,变成可复用、可组合、可版本管理的独立单元。
说得再直白一点:以前你让 Claude Code 帮你写测试,你得在对话里反复交代「用 TDD 的方式」「先写失败测试」「跑一遍确认红」「再写实现」「跑绿」「重构」这一整套流程。每次开新会话都得重来一遍,稍微换个项目,上下文一变,agent 的行为就开始飘。agent-skills要解决的就是这个问题——把这些「怎么干活」的知识固化成 skill,让 agent 在需要的时候自动加载、按规范执行。
它适合谁?三类人最该关注。第一类是天天用 Claude Code、Cursor 这类 AI coding agents 干活的开发者,你会立刻感受到行为一致性的提升;第二类是在团队里推 AI 辅助开发流程的技术负责人,agent-skills给了你一个标准化抓手;第三类是对 agent 工程化感兴趣、想自己搭一套 skill 体系的人,这套东西的设计思路本身就很有参考价值。
我下面会从设计思路、核心机制、实操落地、踩坑排查几个角度,把agent-skills这套东西拆开讲清楚。涉及 Claude Code 的部分,我会说明哪些是官方行为、哪些是我基于常见实践补全的推断,避免误导。
2. agent-skills 的整体设计与思路拆解
2.1 它到底解决的是什么问题
要理解agent-skills的价值,得先理解当前 AI coding agents 的一个核心痛点:能力与知识是耦合在上下文里的。
你打开 Claude Code,它默认知道怎么读文件、怎么跑命令、怎么改代码。但「怎么用 TDD 写一个功能」「怎么按团队的 commit 规范提交」「怎么在改数据库 schema 前先做备份检查」这些领域知识,它默认是不知道的。传统做法是把这些写进CLAUDE.md或者项目根目录的说明文件里,但问题是:
- 所有知识塞在一个文件里,agent 每次都要全量加载,token 浪费严重
- 不同任务需要不同知识,混在一起容易互相干扰
- 没法按需触发,agent 不知道什么时候该用哪条规则
- 团队协作时,这个文件会变成一锅粥,谁都不敢删
agent-skills的思路是解耦:把每一类「怎么干活」的知识封装成一个独立的 skill,每个 skill 有自己的触发条件、自己的内容、自己的依赖。agent 在执行任务时,根据当前上下文匹配到相关 skill,才把它加载进来。
这个设计思路其实和微服务很像。单体应用(一个大 CLAUDE.md)能跑,但扩展性差、维护成本高;拆成微服务(多个 skill)之后,每个单元职责单一、可以独立演进。当然,拆得太碎也有代价,后面会讲。
2.2 skill 的组成结构
一个典型的 skill 通常包含这几个部分,我按重要性排序:
- 元信息(metadata):skill 的名字、描述、触发关键词。这部分决定了 agent 能不能在正确的时机找到它。描述写得含糊,skill 就永远不会被触发,等于白写。
- 指令主体(instructions):具体告诉 agent 该怎么做。这是 skill 的核心,质量高低全看这里。
- 示例(examples):可选,但对复杂 skill 极其重要。给一两个「输入是什么、期望输出是什么」的例子,agent 的执行准确率会明显提升。
- 依赖与工具声明:这个 skill 需要哪些工具、需要读取哪些文件、有没有前置条件。
我实测下来,元信息里的描述是最容易被忽视、但影响最大的部分。很多人写 skill 时把精力全放在指令主体上,描述随便写一句「用于测试」,结果 agent 根本不知道什么时候该调用它。描述应该写成「当用户要求为新功能编写测试、或提到 TDD/测试驱动开发时使用」,把触发场景说清楚。
2.3 为什么选择「按需加载」而不是「全量注入」
这是agent-skills设计里最关键的一个取舍,值得展开说。
全量注入的意思是:把所有 skill 的内容都塞进 system prompt,agent 每次都能看到全部知识。好处是简单直接,坏处是三个:
第一,上下文窗口是稀缺资源。Claude Code 这类工具虽然上下文窗口不小,但你要读代码、要保留对话历史、要放工具返回结果,留给「知识」的空间其实很有限。塞太多 skill,真正干活的空间就被挤没了。
第二,知识之间会打架。比如你有一个 skill 说「提交前必须跑全量测试」,另一个 skill 说「小改动可以跳过测试直接提交」,两个都注入,agent 就懵了。按需加载能让 agent 在特定场景下只看到相关的规则。
第三,成本。每次请求都带上全部 skill,token 消耗是实打实的。按需加载能显著降低平均 token 用量。
按需加载的代价是:匹配可能失败。如果 agent 没识别出当前场景需要某个 skill,它就不会加载,行为就退化成没有 skill 的状态。所以 skill 的触发描述必须写得足够好,这是整个体系能否跑通的关键。
2.4 和 CLAUDE.md 的关系
很多人会问:有了agent-skills,还需要CLAUDE.md吗?
我的实践结论是:两者是互补的,不是替代关系。CLAUDE.md适合放那些「永远适用」的项目级信息——项目结构说明、技术栈、代码风格约定、常用命令。这些内容量不大,但每次都需要。agent-skills适合放那些「特定场景才用」的流程性知识——TDD 流程、发布流程、数据库迁移流程。
打个比方,CLAUDE.md是员工手册,agent-skills是岗位操作手册。员工手册人人必读,岗位手册按岗取用。把两者混在一起,要么手册太厚没人看,要么岗位知识缺失。
3. 核心细节解析与实操要点
3.1 skill 的目录组织方式
agent-skills的落地,第一步是决定 skill 放哪、怎么组织。常见的做法是在项目里建一个专门的目录,比如:
your-project/ ├── .claude/ │ └── skills/ │ ├── tdd-workflow/ │ │ └── SKILL.md │ ├── commit-convention/ │ │ └── SKILL.md │ └── db-migration/ │ └── SKILL.md ├── CLAUDE.md └── src/每个 skill 一个目录,目录里放一个主文件(通常叫SKILL.md或类似名字),需要的话可以带辅助文件(脚本、模板、示例数据)。
为什么用「一个 skill 一个目录」而不是「所有 skill 塞一个文件」?因为目录结构天然支持:
- 每个 skill 可以带自己的辅助资源,不会互相污染
- 可以单独对某个 skill 做版本管理、单独 review
- 复制、迁移、分享单个 skill 时,直接拷目录就行
注意:目录名和 skill 的元信息名字最好保持一致,否则时间一长你自己都记不清哪个目录对应哪个 skill。我见过有人用
skill1、skill2这种命名,三个月后完全不知道哪个是哪个。
3.2 触发描述怎么写才有效
这是整个agent-skills体系里最考验功力的地方。触发描述写得好,skill 该出现的时候出现、不该出现的时候不出现;写得差,要么永远不触发,要么到处乱触发。
我总结了一个「三段式」写法:
- 场景描述:什么情况下用这个 skill。用用户可能说的话、可能做的事来描述,而不是用抽象概念。
- 能力描述:这个 skill 能做什么。
- 排除条件(可选):什么情况下不要用。
举个例子,一个 TDD 工作流的 skill,描述可以这样写:
当用户要求实现新功能、修复 bug,并且希望采用测试驱动开发方式时使用。 本 skill 指导 agent 按「先写失败测试 → 确认失败 → 写最小实现 → 确认通过 → 重构」的流程工作。 如果用户明确表示不需要测试、或只是做探索性原型,不要使用本 skill。对比一下常见的错误写法:「用于 TDD」。这种描述信息量太低,agent 匹配时没有足够的语义线索,触发率会很低。
实操心得:写完描述后,做一个小测试——把描述单独拿出来,问自己「如果我是 agent,看到用户说 X,我会不会想到这个 skill」。如果答案模糊,就继续改描述,直到答案明确。
3.3 指令主体的写法:具体、可执行、有边界
指令主体是 skill 的「肉」。我见过太多 skill 的指令主体写得像散文,读起来很优美,但 agent 执行起来完全不知道从哪下手。
好的指令主体应该满足三个标准:
具体:不要写「编写高质量的测试」,要写「为每个公开函数编写至少一个正常路径测试和一个边界条件测试」。
可执行:每一步都应该是 agent 能直接执行的动作。比如「运行npm test并检查输出」比「验证测试通过」更可执行。
有边界:明确告诉 agent 什么时候停下来、什么时候该问用户。比如「如果测试连续失败三次,停止并询问用户,不要继续盲目修改」。
我拿 TDD 这个场景举个例子,指令主体可以这样组织:
## 工作流程 1. 理解需求:先复述一遍用户要的功能,确认理解无误 2. 写测试:在测试文件中添加一个会失败的测试用例 3. 运行测试:执行测试命令,确认新测试确实失败(红色) 4. 写实现:编写能让测试通过的最小代码 5. 运行测试:再次执行,确认测试通过(绿色) 6. 重构:在测试保持绿色的前提下优化代码 7. 重复:回到第 2 步,处理下一个测试用例 ## 约束 - 每次只处理一个测试用例,不要一次性写完所有测试 - 第 3 步必须真的运行测试,不能假设它会失败 - 如果第 5 步测试没通过,回到第 4 步,不要修改测试来迁就实现这种写法,agent 拿到之后基本能照着执行,不需要额外猜测。
3.4 skill 之间的组合与依赖
单个 skill 能解决单一场景的问题,但真实任务往往是多场景交织的。比如「实现一个新功能并提交」这个任务,可能同时涉及 TDD skill、commit 规范 skill、代码审查 skill。
agent-skills处理这种情况的方式通常是让 agent 自己组合:每个 skill 独立定义,agent 在执行任务时按需加载多个。这比在 skill 里硬编码「调用另一个 skill」要灵活得多。
但组合也带来一个问题:顺序和优先级。如果两个 skill 的指令有冲突,agent 该听谁的?我的做法是在 skill 里显式声明优先级,或者在项目级的CLAUDE.md里写一条总原则:「当 skill 指令冲突时,以更具体的 skill 为准」。
踩过的坑:我曾经有两个 skill,一个说「提交前跑全量测试」,一个说「提交前跑相关测试」。结果 agent 每次提交前都纠结,有时候跑全量有时候跑相关,行为完全不可预测。后来我把「跑什么测试」这件事统一到一个 skill 里,冲突就消失了。能合并的 skill 尽量合并,别为了拆而拆。
4. 实操过程与核心环节实现
4.1 从零搭一个 TDD skill 的完整过程
我拿最经典的 TDD 场景,走一遍完整流程,你可以直接抄作业。
第一步:确定 skill 的边界。TDD skill 只负责「怎么写代码」,不负责「怎么提交」「怎么发布」。边界清晰,后面才不会和别的 skill 打架。
第二步:创建目录和文件。
mkdir -p .claude/skills/tdd-workflow touch .claude/skills/tdd-workflow/SKILL.md第三步:写元信息。这是决定 skill 能否被正确触发的关键。
--- name: tdd-workflow description: 当用户要求实现新功能、修复 bug,并希望采用测试驱动开发方式时使用。指导 agent 按红-绿-重构循环工作。用户明确表示不需要测试时不要使用。 ---第四步:写指令主体。参考 3.3 节的结构,把工作流程、约束、示例都写清楚。
第五步:加示例。给一个完整的「输入-过程-输出」示例,让 agent 有参照。
## 示例 用户输入:实现一个函数,判断字符串是否为回文 期望过程: 1. 写测试:assert isPalindrome("racecar") === true 2. 运行测试,确认失败(函数还不存在) 3. 写实现:function isPalindrome(s) { return s === s.split('').reverse().join('') } 4. 运行测试,确认通过 5. 补充边界测试:空字符串、单字符、大小写混合 6. 重构:处理大小写和空格第六步:验证。开一个新的 Claude Code 会话,让它「用 TDD 方式实现一个简单函数」,观察它是否加载了这个 skill、是否按流程执行。如果没加载,回去改描述;如果加载了但流程不对,回去改指令主体。
4.2 参数与配置的关键选择
搭 skill 体系时,有几个配置项需要做决策,我把我的选择和理由列出来。
| 配置项 | 我的选择 | 理由 |
|---|---|---|
| skill 存放位置 | 项目内.claude/skills/ | 跟项目走,团队共享方便,不依赖个人环境 |
| 单文件 vs 多文件 | 简单 skill 单文件,复杂 skill 多文件 | 简单 skill 拆多文件反而增加维护成本 |
| 描述长度 | 2-4 句话 | 太短触发不准,太长浪费 token |
| 是否带示例 | 复杂流程必带,简单规则可不带 | 示例对流程类 skill 提升明显 |
| 命名风格 | kebab-case,动词开头 | tdd-workflow比TDDWorkflow更易读 |
关于「项目内 vs 全局」这个选择,我补充一句。全局 skill(放在用户主目录下)的好处是跨项目复用,坏处是不同项目的约定可能冲突。我的做法是:通用流程放全局,项目特定约定放项目内。比如 TDD 流程是通用的,放全局;但「本项目的测试命令是pnpm test:unit」这种,放项目内。
4.3 让 skill 真正被用起来
skill 写好了,不代表 agent 就会用。我总结了几个提升触发率的实操技巧:
技巧一:在 CLAUDE.md 里做一次「导航」。加一段话告诉 agent「本项目有以下 skills,遇到相关场景时主动加载」。这相当于给 agent 一个索引,触发率会明显提升。
技巧二:用用户会说的话写描述。用户不会说「请执行测试驱动开发流程」,用户会说「帮我写个功能,要有测试」。描述里要包含这些自然语言表达。
技巧三:定期清理不触发的 skill。我每个月会 review 一次 skill 列表,把连续一个月没被触发过的 skill 拿出来看——要么改描述,要么删掉。留着不触发的 skill 只会增加维护负担。
技巧四:给 skill 加版本号。在元信息里加一个version字段,改 skill 时递增。这样出问题时能快速定位是哪个版本引入的。
4.4 一个完整的 skill 文件长什么样
把上面的东西拼起来,一个可用的 TDD skill 大概是这样:
--- name: tdd-workflow version: 1.2.0 description: 当用户要求实现新功能、修复 bug,并希望采用测试驱动开发方式时使用。指导 agent 按红-绿-重构循环工作。用户明确表示不需要测试时不要使用。 --- ## 工作流程 1. 理解需求,复述确认 2. 写一个会失败的测试 3. 运行测试,确认失败 4. 写最小实现让测试通过 5. 运行测试,确认通过 6. 重构,保持测试绿色 7. 重复 2-6 ## 约束 - 一次只处理一个测试用例 - 第 3 步必须真的运行测试 - 测试没通过时改实现,不改测试 - 连续失败三次停下来问用户 ## 示例 (见 4.1 节示例)这个文件不大,但信息密度足够,agent 拿到之后基本能稳定执行。
5. 常见问题与排查技巧实录
5.1 skill 不触发怎么办
这是最高频的问题。排查顺序我建议这样走:
先看描述。90% 的不触发问题出在描述上。把描述单独拿出来读,问自己「这句话能不能让一个不了解背景的人判断出什么时候该用」。如果答案是否定的,改描述。
再看加载机制。确认你的 skill 目录结构、文件命名符合工具的要求。不同工具对 skill 的识别方式不一样,有的要求特定文件名,有的要求特定目录层级。翻一下官方文档确认。
然后看上下文。有时候 skill 没触发是因为当前对话的上下文里已经有足够的信息让 agent 觉得「不需要额外知识」。这种情况可以手动提示 agent「参考 tdd-workflow skill」,看它能不能找到。如果手动提示能找到,说明是自动匹配的问题;如果手动提示也找不到,说明是加载机制的问题。
最后看冲突。如果项目里有多个 skill,描述语义相近,agent 可能匹配到了另一个。检查一下有没有描述重叠的 skill。
5.2 skill 触发了但行为不对
这种情况通常是指令主体的问题。排查思路:
- 指令太抽象:把「编写高质量代码」改成具体的、可执行的动作
- 约束不明确:agent 不知道边界在哪,就会自由发挥。补上「不要做什么」
- 缺少示例:复杂流程一定要给示例,agent 靠示例理解意图比靠文字描述更准
- 指令太长:超过一定长度后,agent 可能只执行前半部分。把 skill 拆小,或者把次要内容移到辅助文件
我遇到过一个典型案例:一个 skill 的指令主体写了 2000 多字,结果 agent 每次只执行前三分之一。后来拆成三个小 skill,每个 500 字左右,行为立刻稳定了。skill 不是越长越好,能短则短。
5.3 多个 skill 冲突怎么处理
冲突分两种:语义冲突和执行冲突。
语义冲突是两个 skill 对同一件事有不同规定。比如一个说「用 4 空格缩进」,一个说「用 2 空格缩进」。这种冲突必须在源头解决——要么合并,要么明确优先级。我的做法是在CLAUDE.md里写一条总原则:「当 skill 指令冲突时,以更具体的 skill 为准;无法判断具体性时,询问用户」。
执行冲突是两个 skill 都要执行,但顺序有讲究。比如「先跑测试再提交」和「先格式化再提交」。这种冲突通常不严重,agent 自己会排顺序。如果顺序很重要,就在 skill 里显式声明依赖关系。
5.4 常见问题速查表
| 现象 | 可能原因 | 排查动作 |
|---|---|---|
| skill 完全不触发 | 描述太模糊 | 重写描述,加入用户会说的自然语言 |
| skill 偶尔触发 | 描述有歧义 | 检查是否有语义相近的其他 skill |
| 触发了但流程不对 | 指令太抽象或缺示例 | 补充具体步骤和示例 |
| 只执行了一部分 | 指令过长 | 拆分 skill |
| 多个 skill 打架 | 职责重叠 | 合并或明确优先级 |
| 改了 skill 没生效 | 缓存或未重载 | 重启会话,确认文件已保存 |
5.5 几个我踩过的坑
坑一:把 skill 当文档写。一开始我写 skill 像写技术文档,追求全面、严谨。结果 agent 读起来抓不住重点。后来我改成「像给新同事交代任务」的口吻,行为准确率明显提升。skill 是给 agent 看的操作指令,不是给人看的说明书。
坑二:skill 之间互相引用。我试过在 skill A 里写「参考 skill B 的做法」,结果 agent 经常找不到 B,或者找到了但没正确组合。后来我改成每个 skill 自包含,需要共享的内容就复制一份。虽然有点冗余,但稳定性高得多。
坑三:忽视版本管理。早期我改 skill 很随意,改完也不记录。结果某天行为突然变了,排查了半天才发现是三天前改的一个 skill 导致的。现在每个 skill 都有版本号,改动都记在 git 里,出问题能快速回滚。
坑四:skill 太多。有一阵子我什么场景都想封装成 skill,搞了二十多个。结果 agent 匹配时经常选错,维护成本也高。后来砍到八个,只保留高频、高价值的场景,整体体验反而更好。skill 的数量不是越多越好,覆盖核心场景就够了。
6. 关于 agent-skills 的一些延伸思考
agent-skills这套东西,表面上是「怎么让 AI coding agent 更好地干活」,往深了看,其实是在探索人和 AI 协作的接口应该长什么样。
传统的接口是自然语言对话——你说一句,agent 做一步。这个接口灵活,但不可靠、不可复用、不可管理。agent-skills提供的是另一种接口:把「怎么干活」的知识结构化、模块化,让 agent 按规范执行。这个接口牺牲了一部分灵活性,换来了稳定性和可复用性。
我个人的判断是,这两种接口会长期共存。探索性的、一次性的任务,用对话就够了;重复性的、有规范要求的任务,用 skill 更合适。关键是知道什么时候用哪种。
如果你刚开始接触agent-skills,我的建议是从一个 skill 开始。挑一个你每天都在重复交代的流程,把它封装成 skill,用一周,感受一下行为一致性的变化。如果有效,再逐步扩展。别一上来就搭一套完整的 skill 体系,那样大概率会半途而废。
最后分享一个小技巧:写 skill 的时候,把你自己想象成一个刚入职的新同事。你会怎么跟他交代这个任务?把那个交代的过程写下来,基本就是一个合格的 skill 了。这个视角切换,比任何方法论都管用。