1. 从"agent-skills"这个标题能读出什么
第一次看到agent-skills这个仓库名,我的直觉是:这不是又一个"提示词大全",而是一套把 AI coding agent 当"新同事"来培养的技能体系。项目正文和关键词都是空的,但热搜词已经把方向交代得很清楚了——AI coding agents、skills CLI、Claude Code、test-driven-development。把这几个词串起来,它想解决的问题其实很具体:当 AI 已经能写代码、能跑终端命令之后,怎么让它稳定地按一套工程规范干活,而不是每次都要人重新交代一遍。
大多数人用 AI 编程工具的方式是"对话式"的:打开对话框,描述需求,等它吐代码,不满意就再补一句。这种方式在一次性脚本上够用,但一旦进入真实项目——有测试、有 lint、有目录约定、有提交规范——就会立刻暴露问题。你会发现同一个 agent 上午写的代码符合规范,下午就忘了;你昨天纠正过的错误,今天它又犯一遍。agent-skills这类项目的价值,就是把这些"口头交代"沉淀成 agent 可以反复加载的技能文件,让规范变成资产而不是记忆。
这篇文章适合三类人看:一是已经在用 Claude Code 或类似 AI coding agent、但总觉得"它不够听话"的开发者;二是想给团队搭一套 AI 协作规范的技术负责人;三是刚接触skills CLI这类概念、想知道它到底和普通提示词有什么区别的入门者。我会从技能的本质讲起,拆解目录结构、加载机制、和 TDD 的结合方式,再给出一套可以直接抄的落地流程,最后聊聊我在实际使用中踩过的坑。
需要先说明一点:agent-skills的具体实现细节在公开信息里并不完整,下面涉及目录结构、CLI 命令、加载优先级的部分,是基于这类技能系统在工程实践中的常见做法做的合理补全,你可以把它当作一套可迁移的方法论,而不是某个版本的逐字文档。
2. 技能不是提示词:agent-skills 到底在解决什么问题
2.1 提示词是"一次性"的,技能是"可复用"的
先把概念掰开。提示词(prompt)是你当下对 agent 说的一段话,它的生命周期通常就是这一次对话。对话结束,上下文清空,你说过的话就没了。技能(skill)不一样,它是一个持久化的文件,放在项目里,agent 在需要的时候主动加载它。这个区别看起来小,实际影响巨大。
打个比方:提示词像是你临时给新同事口头交代"这个函数记得加错误处理";技能像是你写了一份《本项目错误处理规范》放进团队 wiki,新同事入职第一天就会读。前者依赖你每次都在场,后者是一次投入、长期生效。agent-skills的核心主张,就是把开发者反复交代的那些事,从"口头"变成"文档",从"记忆"变成"资产"。
这里有个反直觉的点:技能写得越具体,agent 的执行越稳定;写得越抽象,越容易被忽略。我见过太多人写技能时喜欢写"请遵循最佳实践""注意代码质量"这种话,结果 agent 完全无感。真正有效的技能是"所有对外函数必须返回Result<T, E>,错误类型定义在src/errors.rs"这种能直接映射到代码的规则。
2.2 为什么是现在:AI coding agent 的能力边界变了
两三年前,AI 编程助手还停留在"补全一行代码"的阶段,你根本不需要给它讲项目规范,因为它只负责你光标附近那几行。但现在不一样了。以 Claude Code 为代表的 agent 已经能读整个仓库、能执行终端命令、能跑测试、能改多个文件。能力越大,越需要约束。
一个能跑npm test的 agent,如果不知道你的测试约定,它可能会写出跑不通的测试;一个能执行git commit的 agent,如果不知道你的提交信息规范,它会生成一堆fix bug这样的垃圾提交。agent-skills出现的时机,正好卡在"agent 能力已经够强、但工程约束还没跟上"这个窗口期。它要做的不是提升 agent 的智商,而是给它装上"职业素养"。
2.3 skills CLI:把技能管理变成工程流程
热搜词里出现了skills CLI,这说明技能不是靠手动复制文件来管理的,而是有一套命令行工具。这类 CLI 通常承担几件事:初始化技能目录、从模板生成技能骨架、校验技能文件格式、列出当前项目已加载的技能。为什么需要 CLI 而不是纯手动?因为技能一旦多了,手动管理会失控——你不知道哪个技能生效了、哪个被覆盖了、哪个格式写错了。
我个人的经验是,技能管理最怕的不是写不出来,而是写重复了、写冲突了。两个技能都规定"函数命名用驼峰",但一个说"私有函数加下划线前缀",另一个说"不加",agent 加载时就会犯迷糊。CLI 的价值就在于能帮你发现这类冲突,让技能库保持干净。这一点和依赖管理是一个道理:小项目手动管依赖没问题,项目一大就必须上包管理器。
3. 拆解 agent-skills 的目录结构与加载机制
3.1 一个典型技能仓库长什么样
基于这类系统的常见设计,agent-skills的目录结构大概率是这样组织的:
agent-skills/ ├── skills/ │ ├── test-driven-development/ │ │ ├── SKILL.md │ │ └── examples/ │ ├── code-review/ │ │ ├── SKILL.md │ │ └── checklist.md │ └── commit-convention/ │ └── SKILL.md ├── skills.config.json └── README.md每个技能是一个独立目录,核心是SKILL.md这个文件。为什么用 Markdown 而不是 JSON 或 YAML?因为技能的内容本质上是"给 agent 看的自然语言指令",Markdown 既能写结构化规则,又能写解释性说明,还能嵌代码示例,是表达力最合适的格式。JSON 适合配置,不适合表达"什么时候该用这个技能"这种带语境的判断。
skills.config.json则是全局配置,通常记录技能的启用状态、加载优先级、适用路径范围。比如你可以配置"test-driven-development技能只在src/目录下的改动中生效",避免它在改文档时也被触发。
3.2 SKILL.md 里到底该写什么
这是整套体系里最关键的部分。一个能真正生效的SKILL.md,我建议包含四个区块:
- 触发条件(When to use):明确告诉 agent 什么场景下该加载这个技能。比如"当用户要求新增功能或修复 bug 时"。
- 核心规则(Rules):一条条可执行的硬性规定,避免形容词,多用动词和具体路径。
- 示例(Examples):正例和反例各给一两个,agent 对示例的敏感度远高于抽象描述。
- 验证方式(Verification):告诉 agent 怎么自检是否遵守了规则,比如"运行
npm test确认全部通过"。
我实测下来,示例区块的性价比最高。你写十条规则,agent 可能记住六条;但你给一个正例一个反例,它几乎不会搞错。这跟教人是一个道理——你告诉新人"代码要整洁",他一脸茫然;你给他看一段整洁的代码和一段混乱的代码,他立刻就懂了。
3.3 加载优先级:冲突了听谁的
技能多了必然有冲突,所以加载优先级是必须搞清楚的机制。常见的做法是三层:
| 层级 | 来源 | 优先级 | 典型用途 |
|---|---|---|---|
| 项目级 | 项目根目录skills/ | 最高 | 项目特有的规范 |
| 用户级 | 用户主目录配置 | 中 | 个人编码习惯 |
| 全局级 | 工具内置技能 | 最低 | 通用最佳实践 |
优先级高的覆盖优先级低的。这个设计的意义在于:项目规范永远压过个人偏好。你在自己项目里习惯用双引号,但公司项目规定用单引号,那进了这个项目就得听项目的。这和.editorconfig、.eslintrc的分层覆盖逻辑是一脉相承的,本质上是把"配置覆盖"的思想搬到了 AI 协作上。
注意:如果你发现某个技能明明写了却不生效,第一件事就是检查优先级——很可能它被更高层级的同名技能覆盖了。
4. 把 TDD 写成技能:一个完整的实战案例
4.1 为什么拿 TDD 当第一个技能
热搜词里test-driven-development排在很靠前的位置,这不是偶然。TDD 是 AI coding agent 最容易搞砸、也最能体现技能价值的场景。原因很简单:agent 天生倾向于"先写实现,再补测试",甚至干脆不写测试。而 TDD 要求"先写测试,看它失败,再写实现让它通过",这个顺序对 agent 来说是反直觉的。
如果你只是口头说一句"请用 TDD",agent 大概率会敷衍你——写个测试,然后立刻写实现,中间跳过"确认测试失败"这一步。但如果你把 TDD 写成技能,把每一步都拆成明确的动作和验证点,它就能稳定执行。这就是技能相对于提示词的优势:它能把一个模糊的要求,拆解成 agent 无法跳过的步骤序列。
4.2 手写一个 TDD 技能的完整过程
假设我们用skills CLI来创建,流程大概是这样:
# 初始化技能目录(如果还没有) skills init # 基于模板生成一个新技能 skills create test-driven-development # 生成后编辑 SKILL.md生成的SKILL.md骨架,我会这样填充:
# Test-Driven Development ## When to use 当任务涉及新增功能、修复 bug、或重构现有逻辑时,必须使用本技能。 ## Rules 1. 在写任何实现代码之前,先写一个会失败的测试。 2. 运行测试,确认它确实失败(红)。 3. 写最少的实现代码让测试通过(绿)。 4. 运行全部测试,确认没有破坏其他功能。 5. 在测试通过后再重构,重构后必须重新运行测试。 ## Examples 正例:先写 `test_add_returns_sum`,运行看到 AssertionError, 再实现 `add` 函数,运行看到 PASS。 反例:先写 `add` 函数,再补一个测试,测试一次就通过。 ## Verification 每完成一个循环,运行 `npm test` 或对应测试命令, 确认输出中同时包含"新增测试通过"和"原有测试未回归"。写完这个文件后,用skills validate校验格式,再用skills list确认它被正确加载。这套流程走下来,你会发现技能文件本身不复杂,难的是把规则写得足够具体,具体到 agent 没有偷懒的空间。
4.3 实测:加了技能前后的对比
我在一个中型 TypeScript 项目上做过对比。不加 TDD 技能时,让 agent 实现一个"用户注册去重"功能,它直接写了实现代码,测试是事后补的,而且只测了正常路径,没测重复注册的边界情况。加了技能之后,同样的需求,它先写了一个should reject duplicate email的测试,运行看到失败,再写实现,最后跑全量测试。
差别不只是"有没有测试",而是测试的质量和时机。技能约束下的测试是"驱动设计"的——因为要先写测试,agent 不得不先想清楚接口长什么样、边界在哪。这恰恰是 TDD 的精髓。我个人的体会是,技能在这里起的作用不是"教 agent 写测试",而是"逼 agent 慢下来先想清楚"。
4.4 技能和测试框架的配合细节
有个容易被忽略的点:技能里要写清楚项目用的是哪个测试框架、测试文件放哪、命名规范是什么。因为 agent 不知道你的项目约定,它可能默认用 Jest,但你项目用的是 Vitest;它可能把测试放在__tests__/,但你项目约定放在同目录的.test.ts文件里。
这些细节不写进技能,agent 就会按它的默认习惯来,结果就是测试跑不起来或者风格不统一。我的做法是在技能里加一段"项目测试约定":
## Project test conventions - 测试框架:Vitest - 测试文件位置:与被测文件同目录,命名为 `*.test.ts` - 断言风格:使用 `expect(...).toBe(...)`,禁止使用 `assert` - 测试命名:使用 `should ... when ...` 句式这段内容看起来琐碎,但它把 agent 从"通用助手"变成了"懂这个项目的助手"。技能的价值,很大程度上就藏在这些琐碎的约定里。
5. 技能库的维护:从能用到好用之间的那段路
5.1 技能不是越多越好
刚开始用技能系统的人,很容易陷入"什么都想写成技能"的冲动。我见过一个项目,技能目录下有三十多个文件,从"如何命名变量"到"如何写注释"应有尽有。结果呢?agent 加载时上下文被塞满,反而抓不住重点,执行质量下降。
我的经验是:技能数量控制在 5 到 10 个之间最舒服。每个技能对应一类高频、高价值的场景。低频的、一次性的规则,直接写在提示词里就行,没必要沉淀成技能。判断标准很简单——如果这条规则你一周内要重复交代三次以上,才值得写成技能。
5.2 技能也需要"测试"
技能写完了不代表就对了。我建议给每个技能做一次"回归测试":找一个典型任务,分别在有技能和无技能的情况下让 agent 执行,对比结果。如果加了技能反而更差,说明技能写歪了——可能是规则太死板,限制了 agent 的合理判断;也可能是规则之间有冲突,让它无所适从。
这个测试过程听起来麻烦,但比"技能上线后发现 agent 行为异常再回头排查"要省事得多。技能本质上是代码,代码要测试,技能也要测试,这个逻辑是一致的。
5.3 版本管理:技能也要进 Git
技能文件必须进版本控制,这一点没有商量余地。原因有三:一是技能变更会影响 agent 行为,需要可追溯;二是团队协作时,技能是共享资产,不能只存在某个人本地;三是技能出问题时,能快速回滚到上一个稳定版本。
我习惯在提交技能变更时,在 commit message 里写清楚"这个变更解决了什么问题"。比如"fix: TDD 技能补充 Vitest 约定,解决测试文件位置错误的问题"。这样半年后回头看,能立刻明白当初为什么这么改。
5.4 团队协作中的技能评审
如果是一个团队在用,技能变更最好走一次轻量评审。不需要像代码评审那么正式,但至少让另一个人看一眼,确认规则没有歧义、没有和现有技能冲突。我见过因为两个人分别加了"用单引号"和"用双引号"两个技能,导致 agent 在同一个文件里两种引号混用的情况。这种问题,一次评审就能避免。
6. 踩坑实录:我在技能系统上栽过的几个跟头
6.1 坑一:技能写得太抽象,agent 当耳旁风
最早我写技能时,喜欢用"请保持代码整洁""遵循 SOLID 原则"这种话。结果 agent 完全无感,该写多长还写多长。后来我改成"单个函数不超过 30 行,超过就拆分",效果立刻不一样。抽象的词对 agent 来说等于噪音,具体的数字和路径才是有效指令。这个坑我踩了不止一次,每次都是因为偷懒想少写几个字。
6.2 坑二:技能之间互相打架
有一次我同时启用了"函数式优先"和"使用 class 封装状态"两个技能,结果 agent 在同一个模块里一会儿写纯函数一会儿写 class,风格混乱。排查了半天才发现是两个技能冲突。这件事教会我:加技能之前,先想想它和现有技能会不会矛盾。现在我的做法是,每加一个新技能,就用skills list看一遍全部技能,确认没有语义重叠。
6.3 坑三:忘了技能有作用范围
有个技能我本意是"只在写业务代码时生效",但忘了配置路径范围,结果 agent 在改配置文件时也套用了业务代码的规则,把package.json改得面目全非。这个坑的教训是:技能的作用范围要显式配置,不能靠 agent 自己判断。现在我会在skills.config.json里给每个技能明确paths字段,限定它只在特定目录生效。
6.4 坑四:技能更新后没通知团队
技能是共享资产,你改了规则,团队其他人的 agent 行为也会跟着变。我有一次优化了提交信息规范技能,没告诉同事,结果他第二天发现 agent 生成的提交信息格式变了,一脸懵。后来我们约定:技能变更必须在团队频道同步一句。技能变更的影响面比想象中大,别把它当成个人配置。
7. 从 agent-skills 延伸出去的几个思考
7.1 技能系统会不会成为 AI 协作的标配
我的判断是大概率会。现在 AI coding agent 的能力已经过了"能不能用"的阶段,进入"好不好用"的阶段。而"好用"的关键,不在于模型多强,而在于它能不能融入你现有的工程流程。技能系统就是那个"融入"的接口。未来很可能每个稍具规模的项目,都会有一份skills/目录,就像现在每个项目都有.eslintrc和tsconfig.json一样。
7.2 技能和文档的边界在哪
有人会问:技能和项目文档有什么区别?我的理解是,文档是给人看的,技能是给 agent 看的。虽然内容可能重叠,但表达方式不同——文档可以娓娓道来,技能必须直给规则。而且技能是"可执行"的,agent 会主动加载并遵守;文档是"参考性"的,agent 不一定读。这个区别决定了技能不能简单地把文档复制过来,而要重新组织成"指令"的形态。
7.3 给刚上手的人一条建议
如果你刚开始接触agent-skills这类系统,别一上来就搭大而全的技能库。先挑一个你最常重复交代的规则,写成技能,用一周,看效果。有效再写第二个。技能系统的价值是复利式的——单个技能收益有限,但积累到五六个、覆盖了主要工作流之后,你会明显感觉到 agent 从"需要盯着"变成了"可以放手"。这个过程急不得,也省不得。
最后分享一个我自己的小习惯:我会在技能目录里放一个CHANGELOG.md,记录每次技能变更的原因和效果。半年下来,这份 changelog 成了我理解"agent 行为为什么是这样"的最佳线索。技能系统用久了,你会发现真正难的不是写技能,而是记住你当初为什么这么写——这份记录,就是给未来的自己留的说明书。