agent-skills 技能工程化:用 CLI 管理 AI coding agent 的 TDD 能力 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 变成只会执行固定流程的机器。这个度需要根据实际任务反复调。最后说个具体的如果你刚开始接触这套东西别一上来就搞复杂的技能库。先挑一个你每天都在重复的工作流把它写成技能跑通用一周再决定要不要扩展。从一到多容易从零到一最难。