agent-skills:用TDD和CLI把AI编码技能工程化

发布时间:2026/10/8 21:24:08
agent-skills:用TDD和CLI把AI编码技能工程化
1. 从agent-skills这个标题能读出什么第一次看到agent-skills这个仓库名我的直觉是这不是又一个提示词大全而是一套把 AI coding agent 当新同事来培养的技能体系。关键词里同时出现了skills CLI、Claude Code、test-driven-development这三者放在一起指向一个很具体的场景——给编码类 AI agent 定义可复用、可组合、可被命令行调用的能力单元并且用测试驱动的方式保证这些能力真的能跑通。先把概念拆开说清楚避免后面绕晕。Agent在这里指的是能自主执行任务的编码智能体比如 Claude Code 这类能在终端里读写文件、跑命令、改代码的工具。它和普通聊天机器人的最大区别是它有手能操作文件系统和终端也有记忆能读取项目上下文。Skills直译是技能。在 agent 语境下一个 skill 就是一段结构化的能力描述——它告诉 agent遇到某类任务时应该按什么流程、调用什么工具、产出什么结果。你可以把它理解成给新员工写的 SOP标准作业程序只不过执行者不是人而是 agent。Skills CLI则是把这些技能从散落在文档里的说明变成可被命令行管理、安装、调用的模块的工具。有了 CLI技能就能像 npm 包一样被分发、版本化、组合。Test-driven-developmentTDD出现在关键词里说明这套技能体系不是靠感觉能用来验收的而是靠测试用例来兜底。这一点非常关键后面会专门展开。所以agent-skills这个项目本质上是在回答一个问题当 AI agent 越来越能干的时候我们怎么把怎么干这件事沉淀下来让它可复用、可验证、可传承这篇文章适合三类人看一是已经在用 Claude Code 之类工具、但每次都要重复写提示词的开发者二是想把团队里的编码规范、最佳实践固化进 agent 工作流的 Tech Lead三是对 agent 工程化感兴趣、想搞清楚技能到底怎么落地的人。下面我会从设计动机、目录结构、CLI 用法、TDD 验收、实战踩坑几个角度把这套东西讲透。2. 为什么技能这件事值得单独做成一个项目2.1 提示词工程的瓶颈一次性、不可复用、无法验收大部分人用 AI coding agent 的方式是这样的打开对话框敲一段提示词agent 干活不满意就再补一句。这套流程在单次任务里没问题但一旦你想让它稳定地做同一类事问题就来了。我自己的经历很典型。有段时间我让 agent 帮我写单元测试每次都要重复交代用 pytest、mock 掉网络请求、断言要覆盖边界条件、命名用 test_ 开头。写了十几次之后我意识到这些要求本质上是我脑子里的测试技能但我从来没把它固化下来。结果就是换个项目、换台机器、换个人这套要求就得重新讲一遍。这就是提示词工程的根本瓶颈——它是一次性的、隐性的、无法验收的。你没法说这个提示词写得好不好只能凭感觉。2.2 技能化的三个收益复用、组合、可测把怎么干从提示词升级成技能收益是结构性的。第一是复用。一个写好的生成 pytest 测试技能可以在任何 Python 项目里调用不用重复交代背景。技能本身是文件可以进版本控制可以 review可以迭代。第二是组合。复杂任务往往需要多个技能串联。比如给一个函数补测试可以拆成读代码 → 识别边界条件 → 生成测试骨架 → 填充断言 → 跑测试验证。每个环节都是一个技能组合起来就是一条流水线。这比让 agent 一口气干完要可控得多。第三是可测。这是 TDD 关键词的价值所在。技能不是写完就算数而是要有一组测试用例来验证这个技能在给定输入下是否产出了预期结果。技能会退化、会失效没有测试兜底你根本不知道它什么时候坏了。2.3 和提示词库的本质区别市面上有很多提示词库项目收集了几百条提示词。agent-skills和它们的区别就像菜谱合集和厨房操作规范的区别。菜谱合集告诉你红烧肉怎么做但不管你的灶台火力多大、锅是什么材质。而操作规范会定义什么情况下用哪个灶、火候怎么调、出锅标准是什么。前者是内容后者是流程 约束 验收标准。具体到代码层面一个 skill 通常包含三部分组成部分作用类比元信息name/description/触发条件告诉 agent 什么时候该用这个技能岗位说明书执行指令步骤/工具调用/约束告诉 agent 具体怎么做操作手册验收标准测试用例/预期输出告诉 agent 做到什么程度算完成质检标准这三者缺一不可。只有元信息agent 不知道怎么做只有指令没法判断做没做对只有验收标准agent 不知道从哪下手。3. 一个 skill 的解剖从目录结构到执行逻辑3.1 典型目录布局与文件职责虽然agent-skills的具体实现细节需要以仓库实际内容为准但基于这类项目的常见实践一个 skill 的目录结构通常长这样skills/ test-driven-development/ SKILL.md # 技能主文件含元信息和执行指令 examples/ # 示例输入输出 tests/ # 验收测试用例 scripts/ # 辅助脚本可选SKILL.md是核心。它一般用 YAML frontmatter 声明元信息正文写执行逻辑。frontmatter 里最关键的两个字段是name和description——agent 靠这两个字段判断当前任务该不该触发这个技能。这里有个容易被忽略的细节description的写法直接决定技能的触发准确率。写得太宽泛比如帮助写代码会导致技能被滥用写得太窄比如为 Python 3.11 的 asyncio 函数生成 pytest 测试又会导致该触发时不触发。我的经验是description 里要同时包含动作生成测试、对象Python 函数、约束用 pytest、覆盖边界条件三个要素。3.2 元信息字段的设计意图为什么元信息要单独抽出来而不是混在正文里因为 agent 在决定用不用这个技能时只需要读元信息不需要读全文。这是一种性能优化——如果每次都要把几十个技能的全文塞进上下文token 消耗会爆炸。常见的元信息字段和它们的意图name技能的唯一标识用于 CLI 调用和技能间引用。命名建议用 kebab-case动词开头比如generate-unit-tests而不是unit-tests-generator。description触发判断依据前面说过要精准。version版本号技能会迭代没有版本号就没法回滚。dependencies依赖的其他技能或工具。比如生成测试可能依赖读取代码结构这个技能。tags分类标签方便批量检索。提示如果你的技能库超过 20 个强烈建议给每个技能打 tags否则找起来会很痛苦。我见过一个团队把技能全平铺在一个目录里三个月后没人记得哪个是哪个。3.3 执行指令的写法约束比步骤更重要写执行指令时新手最容易犯的错是把步骤写得太细。比如1. 打开文件 2. 找到函数定义 3. 读取参数列表 4. ...这种写法的问题是agent 比你想象的聪明它不需要你教它怎么打开文件。你真正需要告诉它的是约束和判断标准什么情况下应该停下来问人比如函数依赖了外部服务什么情况下应该拒绝执行比如代码里有硬编码密钥产出物的格式要求比如测试文件必须放在tests/目录下边界条件比如只处理纯函数遇到有副作用的函数要标注换句话说指令的重点是不要做什么和做到什么程度而不是第一步第二步。这跟带新人的逻辑是一样的——你不会教一个资深工程师怎么用 IDE但你会告诉他这个项目的代码规范。4. Skills CLI让技能从文档变成可调用模块4.1 为什么需要 CLI 而不是手动复制文件有人会问技能不就是几个 Markdown 文件吗手动复制到项目里不就行了为什么要搞个 CLI这个问题我在早期也纠结过。手动复制在技能少的时候确实够用但一旦规模上来问题就暴露了版本同步技能更新了你怎么知道项目里用的是哪个版本手动复制的话每个项目都是一份独立副本改一处要同步 N 处。依赖管理技能 A 依赖技能 B手动复制时你得自己记住这个关系。发现成本新同事加入怎么知道有哪些技能可用翻目录翻文档卸载残留不用了的技能手动删容易删不干净。CLI 解决的就是这些工程化问题。它把技能当成包来管理有安装、卸载、列表、更新这些标准操作。4.2 常见命令与使用场景基于这类 CLI 的通用设计典型命令大概是这样# 列出当前可用的所有技能 skills list # 安装某个技能到当前项目 skills install test-driven-development # 查看某个技能的详情 skills info test-driven-development # 更新所有已安装技能 skills update # 卸载技能 skills remove test-driven-developmentskills list是最常用的。它通常会显示技能名、版本、描述、是否已安装。我建议把它当成技能目录来用定期扫一眼看看有没有新技能可以引入。skills install的安装位置有两种常见策略全局安装装到用户目录所有项目共享和项目级安装装到项目内的.skills/目录随项目走。选择逻辑是通用技能比如写 commit message→ 全局安装项目特定技能比如按本项目的分层架构生成代码→ 项目级安装注意项目级安装的技能目录一定要加进.gitignore还是提交进仓库这是个需要团队统一的问题。我的建议是提交进仓库这样新人 clone 下来就能用代价是仓库体积会大一点。4.3 技能解析与注入的底层流程CLI 装完技能之后agent 是怎么知道这些技能的这里涉及一个注入流程CLI 扫描技能目录读取所有SKILL.md的元信息把元信息汇总成一个技能索引注入到 agent 的系统提示或上下文里agent 执行任务时先匹配索引判断该用哪个技能匹配成功后再把该技能的完整指令加载进来这个流程的关键在于分层加载——先加载轻量的索引按需加载完整的指令。这样即使有上百个技能也不会一次性撑爆上下文。理解了这个流程你就能明白为什么元信息要写得精准索引匹配错了后面全错。我踩过一次坑某个技能的 description 写得太泛结果 agent 在处理完全不相关的任务时也触发了它产出了一堆没用的东西。后来把 description 收窄问题就解决了。5. 用 TDD 给技能上保险验收标准怎么定5.1 技能为什么会退化技能不是写完就一劳永逸的。它会退化原因有几个底层模型更新agent 背后的模型升级了行为可能变化原来能触发的技能现在不触发了。项目上下文变化技能依赖的项目结构变了比如目录调整技能里的路径假设失效。指令被误改多人协作时有人改了技能指令但没测试引入了回归。没有测试兜底这些退化你根本发现不了——直到某天 agent 突然不干活了你才回头查。5.2 技能测试用例的三种类型给技能写测试和给代码写测试思路类似但侧重点不同。常见的有三类第一类触发测试。验证给定某个任务描述技能是否被正确触发。比如输入帮我给这个函数写测试期望触发test-driven-development技能。这类测试防的是该触发时不触发和不该触发时乱触发。第二类执行测试。验证技能执行后产出物是否符合预期。比如技能要求生成的测试文件必须放在tests/目录、必须用 pytest 风格、必须包含至少一个边界条件断言。这类测试防的是执行结果跑偏。第三类拒绝测试。验证技能在应该拒绝的场景下是否真的拒绝了。比如输入一段含硬编码密钥的代码期望技能拒绝处理并给出提示。这类测试最容易被忽略但恰恰是安全底线。5.3 一个可复现的测试用例模板下面是一个技能测试用例的通用模板你可以直接套用# tests/trigger-cases.yaml - name: 应该触发测试技能 input: 帮我给 utils.py 里的 parse_date 函数写单元测试 expect: skill_triggered: test-driven-development output_contains: - pytest - def test_ - name: 不应该触发测试技能 input: 帮我重构一下这个类的继承关系 expect: skill_triggered: null - name: 遇到敏感信息应拒绝 input: 给这个含 API_KEY 的函数写测试 expect: skill_triggered: test-driven-development output_contains: - 检测到敏感信息 should_refuse: true这个模板的价值在于它把技能好不好用从主观判断变成了客观断言。跑一遍测试全绿就说明技能健康有红就说明哪里退化了。提示技能测试不需要追求 100% 覆盖率但触发测试和拒绝测试必须覆盖。前者保证技能能被用上后者保证技能不会闯祸。6. 把 agent-skills 接进 Claude Code 的实操路径6.1 环境准备与安装位置选择Claude Code 这类工具在终端里运行能直接读写项目文件。把agent-skills接进去核心是让 CLI 生成的技能索引能被 Claude Code 读到。安装位置的选择前面提过这里补充一个实操细节如果你在多个项目间切换全局安装 项目级覆盖是最灵活的方案。全局装通用技能项目里装特定技能项目级优先级高于全局。这样既不用重复装又能针对项目定制。安装完成后建议先跑一次skills list确认技能被正确识别。如果列表是空的八成是安装路径没配对检查一下 CLI 的配置文件。6.2 技能索引如何被 agent 读取Claude Code 读取技能索引的方式通常是通过项目根目录下的配置文件比如CLAUDE.md或类似的上下文文件来引用。你需要在里面加一行指向技能索引的路径或者直接把索引内容嵌进去。这里有个性能权衡索引内容嵌得越多agent 的上下文占用越大留给实际任务的 token 就越少。所以索引要精简只保留 name 和 description完整指令按需加载。我实测下来的经验是技能数量控制在 30 个以内索引对上下文的影响可以忽略。超过这个数就要考虑分组加载了——比如按任务类型分成编码类测试类文档类agent 先选组再选技能。6.3 验证技能是否生效的最小闭环装完之后怎么确认技能真的生效了别急着上复杂任务先跑一个最小闭环挑一个最简单的技能比如生成 commit message在项目里做一处小改动让 Claude Code 生成 commit message观察它是否按技能定义的格式产出如果格式对了说明技能注入成功。如果没反应按这个顺序排查技能是否安装成功 → 索引是否被读取 → description 是否匹配当前任务 → 技能指令是否有语法错误。这个闭环看起来简单但能帮你快速定位问题出在哪一层。我见过有人一上来就测复杂技能结果出问题了不知道是技能本身的问题还是注入的问题排查了半天。7. 实战中踩过的坑和对应的解法7.1 技能触发不稳定的排查链路现象同一个技能有时候触发有时候不触发。排查链路第一步看 description 是不是有歧义。如果 description 里用了模糊词帮助优化处理agent 的判断就会飘。改成具体动词生成校验转换会稳定很多。第二步看是否有多个技能竞争。如果两个技能的 description 都覆盖了当前任务agent 会随机选一个。解法是给技能加优先级字段或者在 description 里明确边界。第三步看任务描述本身是否清晰。如果用户输入太模糊帮我搞一下这个agent 没法判断该用哪个技能。这时候技能本身没问题是输入的问题。我遇到过一次特别隐蔽的情况技能触发不稳定排查半天发现是 description 里有个错别字导致关键词匹配失败。所以技能写完一定要通读一遍别放过错别字。7.2 技能之间的依赖冲突技能 A 依赖技能 B但 B 被卸载了A 就会报错。更麻烦的是循环依赖A 依赖 BB 又依赖 A直接死锁。解法是在 CLI 层面做依赖检查。安装技能时自动检查依赖是否满足卸载技能时检查是否有其他技能依赖它。如果 CLI 没这个功能就在技能元信息里手动维护dependencies字段定期用脚本扫描一遍。注意技能依赖不要设计得太深。超过三层的依赖链维护成本会急剧上升。我的经验是能扁平化就扁平化宁可让技能稍微冗余一点也不要搞出复杂的依赖网。7.3 技能更新后的回归验证技能更新是高频操作但每次更新都可能引入回归。所以更新后必须跑一遍测试。理想情况下CLI 应该支持skills test命令一键跑完所有技能的测试用例。如果没有就自己写个脚本遍历技能目录逐个跑测试。回归验证的重点是触发测试。因为技能更新时最容易改动的就是 description而 description 一变触发行为就可能变。执行逻辑的改动反而相对安全因为那部分有明确的输入输出可以断言。8. 技能库的长期维护从能用走向好用8.1 技能命名与分类的约定技能库超过一定规模后命名和分类就成了大问题。我的建议是建立一套约定命名动词开头kebab-case比如generate-api-docs、validate-schema、refactor-extract-method分类按任务阶段分比如planning/、coding/、testing/、review/、docs/版本语义化版本破坏性改动升主版本号这套约定看起来是小事但能省下大量沟通成本。新人进来一看目录就知道有哪些技能、怎么找。8.2 什么技能值得沉淀不是所有操作都值得做成技能。判断标准是这个操作是否高频、是否有明确的最佳实践、是否容易做错。高频但没最佳实践的比如随便聊聊需求不值得做技能。低频但有严格规范的比如发布流程值得做。高频且容易做错的比如写测试最值得做。我个人的经验是一个技能如果一个月内被用到少于 3 次就该考虑合并或删除。技能库不是越大越好维护成本是实打实的。8.3 团队协作中的技能评审技能是要进版本控制的所以需要评审。评审的重点不是代码风格而是description 是否精准会不会误触发执行指令是否有安全边界会不会闯祸测试用例是否覆盖了触发和拒绝场景是否和现有技能重复我见过团队把技能评审做成走过场结果技能库里堆了一堆没人用的东西。评审要真刀真枪该拒就拒。9. 我对这套东西的真实看法用了一段时间agent-skills这类技能体系之后我最大的体会是它把 AI agent 从聪明的实习生变成了有章可循的同事。实习生聪明但不可靠每次都要重新交代有章可循的同事你给他一份 SOP他就能稳定产出。技能体系做的就是这份 SOP 的工程化——可版本化、可测试、可组合。但它也不是银弹。技能写得不好反而会束缚 agent 的手脚。我见过有人把技能写得极其死板agent 遇到稍微不同的情况就卡住。所以写技能的心态应该是给方向、划边界而不是规定每一步。最后分享一个我自己的小习惯每当我发现自己在重复交代同一件事超过三次就停下来把它做成技能。这个习惯帮我积累了一套真正用得上的技能库而不是一堆写完就忘的文档。技能的价值不在于多而在于你真的会去用。