1. 从"agent-skills"这个仓库名说起:它到底在解决什么问题
第一次看到agent-skills这个名字,很多人会以为是某个 AI Agent 框架的插件市场,或者是一堆 prompt 模板的集合。实际翻一遍它的定位就会发现,它想干的事情更底层:把"AI coding agent 应该具备哪些能力"这件事,从口头约定变成可复用、可组合、可测试的工程资产。
换句话说,它不是在教你"怎么让 AI 写代码",而是在回答一个更前置的问题——当你在 Claude Code、Cursor、各类 CLI agent 里反复调教出一套好用的工作流之后,怎么把这套工作流沉淀下来,让它在不同项目、不同模型、不同人手里都能稳定复现。
这个需求是真实存在的。我自己在过去大半年里,光是"让 agent 先写测试再写实现"这一条规则,就在不同项目里重写过至少五遍。每次换一个仓库、换一个模型,之前调好的那套东西就得重新来一遍。agent-skills这类项目的价值,就在于把这种重复劳动收敛成一份声明式的技能定义。
关键词里出现的skills CLI、test-driven-development、AI coding agents,其实已经把这个项目的三个核心维度点出来了:技能怎么被调用(CLI)、技能内容长什么样(TDD 是典型例子)、技能服务于谁(coding agent)。下面我会按这个脉络,把它的设计逻辑、落地方式、以及实际用起来会踩的坑,一层层拆开讲。
提示:本文讨论的是通用的 agent 技能工程化思路,涉及的具体工具和命令以你本地实际安装的版本为准,不同版本之间参数可能有差异。
2. 技能(Skill)到底是什么:拆解 agent-skills 的核心抽象
2.1 技能不是 prompt,而是"带约束的能力单元"
很多人第一反应是把 skill 等同于一段写得比较长的 prompt。这个理解会带来一系列后续问题:prompt 是无状态的、是纯文本的、是没有边界的。而agent-skills里的 skill,本质上是一个有输入契约、有执行步骤、有验收标准的能力单元。
打个比方,prompt 像是你跟同事口头说"帮我看看这段代码",而 skill 像是你递给同事一份 SOP:输入是什么、第一步做什么、什么情况下要停下来问、做完之后怎么验证。前者依赖对方的理解力,后者依赖流程本身。
一个典型的 skill 定义通常包含这几块:
- 触发条件:什么场景下这个 skill 应该被激活。比如"当用户要求新增一个函数时"。
- 前置检查:执行前需要确认什么。比如"确认测试文件已存在"。
- 执行步骤:有序的操作序列,每一步都有明确的输入输出。
- 验收标准:怎么判断这次执行是成功的。这是最容易被忽略、但最关键的部分。
- 失败处理:步骤失败时是重试、回退还是上报。
把这五块写清楚,一个 skill 才算完整。只写执行步骤的,那还是 prompt。
2.2 为什么用 CLI 来管理技能
关键词里的skills CLI指向一个很实际的问题:技能定义写完之后,怎么让 agent 用上?
最朴素的做法是把技能文件放在项目根目录,然后在 agent 的配置文件里引用。但项目一多,就会出现"这个项目用的是 v2 版本的技能,那个项目还在用 v1"的混乱。CLI 的价值在于把技能的安装、版本、作用域这三件事标准化。
我实测下来,CLI 管理技能主要解决三个痛点:
第一是作用域隔离。全局技能和项目级技能要能区分。比如"提交信息格式规范"这种是全局的,"这个项目的数据库迁移流程"是项目级的。CLI 通过不同的安装路径来区分,避免全局污染。
第二是版本可追溯。技能也是会迭代的。今天你觉得"先写测试"这条规则没问题,明天发现它对某些场景不适用,改了之后怎么知道哪个项目用的是哪版?CLI 的版本机制让这件事可查。
第三是跨 agent 复用。同一个技能定义,理想情况下应该能同时喂给 Claude Code、其他 CLI agent、甚至 IDE 插件。CLI 作为中间层,把技能定义和具体 agent 解耦。
2.3 TDD 为什么成了最典型的示例技能
test-driven-development出现在关键词里不是偶然。TDD 是检验一个 agent 技能系统设计得好不好的试金石,因为它对流程的约束要求极高。
一个真正的 TDD 技能,必须强制 agent 遵守"红-绿-重构"的顺序:先写一个会失败的测试,确认它失败,再写最小实现让它通过,最后重构。这里面的难点在于,agent 天然倾向于"一步到位"——它会直接写出实现和测试,然后告诉你"都通过了"。
要让它老老实实先写失败测试,技能定义里就必须有强制的中断点:写完测试后必须停下来运行,把失败输出展示出来,确认失败原因符合预期,才能进入下一步。这种"强制停顿"是普通 prompt 做不到的,必须靠技能框架来支撑。
我在实际项目里试过,如果不加这个中断点,agent 有大概七成的情况会跳过"确认失败"这一步。加上之后,这个比例能降到一成左右。这个数据因模型而异,但方向是一致的。
3. 一个技能从定义到被 agent 调用,中间发生了什么
3.1 技能文件的组织方式
技能在磁盘上通常是一组文件的集合,而不是单个文件。这样设计的原因是技能往往需要附带辅助资源:示例代码、检查脚本、参考文档。
一个常见的目录结构长这样:
skills/ test-driven-development/ SKILL.md # 技能主定义 examples/ # 示例 scripts/ # 辅助脚本 checklist.md # 验收清单SKILL.md是入口,里面用结构化的方式描述触发条件、步骤、验收标准。其他文件按需引用。这种组织方式的好处是,技能可以被当作一个独立单元来分发和版本管理。
注意:技能目录的命名建议用 kebab-case,且保持和技能名一致。我见过用下划线和驼峰混用的项目,CLI 解析时容易出问题。
3.2 技能是怎么被"注入"到 agent 上下文里的
这是很多人好奇的点:agent 怎么知道有哪些技能可用?
主流做法有两种。一种是全量注入,把所有可用技能的元信息(名称、描述、触发条件)放进系统提示,agent 根据当前任务自己判断该用哪个。另一种是按需检索,agent 先通过一个检索步骤找到相关技能,再把完整定义加载进来。
全量注入的优点是简单直接,缺点是技能一多就会挤占上下文窗口。按需检索的优点是省 token,缺点是多了一次检索的往返,且检索本身可能不准。
agent-skills这类项目通常采用混合策略:元信息全量注入(因为很短),完整定义按需加载。这样既保证了 agent 知道"有哪些能力可用",又不会一次性把所有细节塞进去。
实测下来,当技能数量在 20 个以内时,全量注入元信息的开销是可以接受的。超过这个数量,就得考虑分层或者检索了。
3.3 触发条件的写法直接决定技能好不好用
我踩过最大的坑就在这里。早期我写的触发条件特别宽泛,比如"当需要写代码时"。结果就是 agent 几乎每个任务都会激活这个技能,包括那些根本不需要的场景,白白消耗上下文。
后来我总结出一个原则:触发条件要写得像"排除法"而不是"包含法"。与其说"什么时候用",不如说清楚"什么时候不用"。
举个例子,一个代码审查技能的触发条件,宽泛写法是"当用户要求审查代码时"。改进后的写法是:
- 当用户明确要求审查,且提供了具体的文件或 diff 时激活
- 当用户只是问"这段代码怎么样"这种开放式问题时,不激活,先澄清范围
- 当任务处于实现阶段、还没到审查阶段时,不激活
这样写之后,误触发的比例明显下降。
4. 把 TDD 技能真正跑起来:完整实操链路
4.1 环境准备里最容易被忽略的一步
在动手之前,有一件事必须先确认:你的 agent 是否支持在执行过程中运行命令并读取输出。TDD 技能的核心是"运行测试、看失败、再改",如果 agent 只能生成文本、不能执行命令,那这个技能就是空转。
关键词里提到的claude code如何直接执行终端命令正好对应这个点。你需要确认 agent 有终端执行权限,并且执行结果能回传到它的上下文里。这一步没确认就往下走,后面全是白费功夫。
确认方法很简单:让 agent 执行一个无害命令,比如列出当前目录,看它能不能拿到输出并基于输出做下一步。如果它只是"假装"执行然后编一个结果,那说明执行链路没通。
4.2 技能定义的关键字段怎么写
下面是一个 TDD 技能定义的骨架,我用注释标出了每个字段的意图:
# Skill: test-driven-development ## 触发条件 - 用户要求新增功能或修复 bug,且项目已有测试框架 - 不适用于:纯配置修改、文档更新、一次性脚本 ## 前置检查 - 确认测试命令(从 package.json / pyproject.toml 等读取) - 确认测试文件命名约定 ## 执行步骤 1. 写一个测试,描述期望行为 2. 运行测试,确认它失败,且失败原因是"功能未实现"而非"语法错误" 3. 写最小实现让测试通过 4. 再次运行,确认通过 5. 重构,保持测试通过 ## 验收标准 - 每一步都有实际的命令输出作为证据 - 第 2 步的失败输出必须被展示 - 最终测试全绿 ## 失败处理 - 若第 2 步测试直接通过,说明测试没测到点子上,回到第 1 步重写 - 若第 3 步实现后测试仍失败,检查失败原因,不要盲目改测试这里最关键的是第 2 步的强制性和验收标准里的"证据"要求。没有这两条,agent 会想方设法跳过验证。
4.3 跑通之后的第一个坑:agent 会"优化"你的流程
技能跑通之后,我以为万事大吉。结果发现 agent 会在执行几次之后,开始"聪明"地合并步骤——把写测试和写实现合并成一次输出,然后声称"测试通过了"。
这不是模型在偷懒,而是它在做它认为的"效率优化"。解决办法是在技能定义里明确禁止合并,并且把"展示每一步的独立输出"写进验收标准。
我后来加了一条硬性规则:每一步操作必须对应一次独立的命令执行,且执行输出必须原样展示。加上之后,合并行为基本消失了。
4.4 不同模型下的表现差异
同一个 TDD 技能,在不同模型上的表现差异是很大的。我做过一个粗略的对比,用同一个技能定义跑同一批任务:
| 模型类型 | 遵守步骤顺序 | 主动展示失败输出 | 跳过验证的比例 |
|---|---|---|---|
| 强推理型 | 高 | 高 | 低 |
| 通用型 | 中 | 中 | 中 |
| 轻量型 | 低 | 低 | 高 |
这个表不是说轻量模型不能用,而是说:技能定义的严格程度要跟模型能力匹配。模型越弱,技能里的约束就要写得越死、越具体。指望一个轻量模型靠"理解"来遵守流程,是不现实的。
关键词里提到的使用cc switch 接入 deepseek v4, qwen, glm等模型反映的正是这种多模型切换的需求。切换模型时,技能定义往往需要跟着调整约束强度。
5. 技能工程化里那些文档不会告诉你的坑
5.1 技能之间的冲突比想象中更常见
当你装了十几个技能之后,冲突就来了。比如一个技能说"修改代码前先写测试",另一个技能说"快速原型阶段跳过测试"。当任务同时匹配两个技能时,agent 该听谁的?
我遇到过的真实场景:一个"代码格式化"技能和一个"最小改动"技能打架。格式化技能要求把整个文件按规范重排,最小改动技能要求只动必要的行。结果 agent 在两个技能之间反复横跳,输出质量反而下降。
解决办法是给技能加优先级,并且在技能定义里写明"当与 XX 技能冲突时,以本技能为准"或"本技能让位于 XX 技能"。这件事必须在定义阶段就想清楚,不能指望 agent 自己权衡。
5.2 技能的"上下文成本"要算清楚
每个被激活的技能都会占用上下文窗口。一个写得啰嗦的技能定义,可能光定义就吃掉几千 token。技能一多,留给实际任务的上下文就少了。
我的经验是:技能主定义控制在 500 字以内,详细内容放到被引用的附属文件里。主定义只保留触发条件、步骤骨架、验收标准。示例、边界情况、详细说明都放外面,需要时再加载。
这样做的另一个好处是,技能更容易维护。改一个示例不用动主定义,降低改坏的风险。
5.3 验收标准写成"可观测"的,而不是"可感受"的
"代码质量高"这种验收标准等于没写。agent 没法判断什么叫"高",你也没法验证它是否达标。
好的验收标准是可观测的:测试通过率、lint 是否报错、特定命令的退出码、输出里是否包含某个字段。把验收标准写成可以自动检查的形式,技能才真正可复现。
我现在的习惯是,每个技能的验收标准都尽量对应一条可以跑的命令。跑完看退出码,比让 agent 自己"感觉"靠谱得多。
5.4 技能不是写完就完事,要定期"体检"
技能会随着项目演进失效。比如项目换了测试框架,技能里写死的测试命令就不对了。或者项目结构调整,技能里引用的路径失效了。
我建议每隔一段时间做一次技能体检:把所有技能跑一遍,看哪些还正常工作,哪些已经报错。这件事听起来麻烦,但比在关键时刻发现技能失效要划算得多。
6. 从单技能到技能库:规模上去之后怎么管
6.1 技能分类的实用维度
技能一多,就得分类。我试过几种分类方式,最后觉得按作用阶段分最实用:
- 规划类:需求澄清、任务拆解、方案对比
- 实现类:TDD、重构、代码生成
- 验证类:代码审查、测试补全、边界检查
- 收尾类:提交信息、文档更新、变更说明
按阶段分类的好处是,agent 在任务的不同阶段能快速定位到相关技能,减少误触发。
6.2 技能的组合与编排
单个技能解决单点问题,但真实任务往往是多阶段的。这时候就需要技能编排:先规划、再实现、再验证、最后收尾。
编排有两种做法。一种是显式编排,在技能定义里写明"本技能执行完后,建议激活 XX 技能"。另一种是隐式编排,靠 agent 根据任务阶段自己选。
显式编排更可控,但灵活性差。隐式编排灵活,但依赖 agent 的判断力。我的做法是:关键路径用显式编排,辅助路径用隐式。
6.3 团队协作下的技能管理
一个人用技能和一群人用技能,复杂度完全不是一个量级。团队场景下,技能需要:
- 统一的存放位置:不能每个人本地一份
- 变更评审:技能改动要像代码一样走 review
- 版本锁定:项目要能锁定使用的技能版本,避免"昨天还好好的今天就不对了"
这几点其实和依赖管理是一个道理。技能本质上就是 agent 的依赖,管理方式可以参考依赖管理的成熟经验。
7. 我实际用下来的一些体会
技能工程化这件事,最大的价值不在于"让 agent 更聪明",而在于"让 agent 的行为更可预测"。聪明是模型的事,可预测是工程的事。你不能控制模型怎么想,但你可以通过技能定义约束它怎么做。
另一个体会是,技能定义的质量比数量重要得多。我见过有人一口气写了五十个技能,结果常用的就三五个,剩下的全是摆设。与其铺量,不如把最常用的几个技能打磨到极致。
还有一点,技能不是越严格越好。约束太死,agent 遇到边界情况就卡住了,反而需要人工介入。约束的目的是保证关键步骤不被跳过,而不是把 agent 变成只会执行固定流程的机器。这个度需要根据实际任务反复调。
最后说个具体的:如果你刚开始接触这套东西,别一上来就搞复杂的技能库。先挑一个你每天都在重复的工作流,把它写成技能,跑通,用一周,再决定要不要扩展。从一到多容易,从零到一最难。