ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

agent-skills 实战:用 TDD 封装 AI 编程能力

2026/10/8 11:31:39 拓冰建站 浏览量
agent-skills 实战:用 TDD 封装 AI 编程能力 1. agent-skills 到底在解决什么问题第一次看到agent-skills这个词很多人会以为它又是一个新的 AI 编程工具或者某个大模型的插件市场。实际上它更像是一套能力封装规范——把 AI coding agent 在特定任务上的操作经验、约束条件和验证标准打包成可复用、可组合、可版本管理的技能单元。你可以把它理解成给 AI 编程助手写的岗位操作手册而不是给它换一个更聪明的大脑。我接触这个概念是从 Claude Code 开始的。当时团队里几个人都在用 Claude Code 写代码但每个人调教出来的效果差异极大有人让它改个 bug 要来回五六轮有人两三轮就能拿到可合并的代码。排查下来发现差距不在模型本身而在于有没有把这个项目该怎么改、改完怎么验证、哪些文件不能碰这些隐性知识显式地告诉 agent。agent-skills要解决的正是这个信息传递问题。它的核心价值可以拆成三层。第一层是任务边界定义一个 skill 会明确说清楚我负责什么、我不负责什么避免 agent 在无关方向上浪费 token。第二层是操作流程固化把先读测试、再改实现、最后跑验证这类步骤写成 agent 能执行的指令序列。第三层是验证标准内嵌skill 里通常带着验收条件agent 做完之后能自己判断是否达标而不是等你人工 review 才发现跑偏了。适合谁来用如果你只是偶尔让 AI 帮你补全几行代码那agent-skills可能有点重。但如果你在做持续性的项目开发尤其是需要 AI agent 反复介入同一代码库的场景这套东西的收益会非常明显。它让 AI 编程从每次都要重新解释需求变成调用一个已经调好的能力模块。关键词里提到的test-driven-development其实是agent-skills最典型的应用形态之一。TDD 本身就是一套强流程约束的开发方法把它封装成 skill 之后agent 会严格按照红-绿-重构的节奏走不会出现先写实现再补测试这种偷懒行为。这也是为什么很多agent-skills的示例都围绕测试展开——测试是最容易验证、最容易标准化的环节。2. 一个 skill 的内部结构长什么样2.1 从目录组织看设计意图agent-skills通常以目录形式存在一个 skill 一个文件夹。我见过的最简结构大概是这样skills/ fix-bug/ SKILL.md examples/ input.md output.md scripts/ verify.shSKILL.md是核心里面用自然语言加结构化标记描述这个 skill 的元信息、触发条件、执行步骤和验收标准。examples目录放的是输入输出样例作用是给 agent 提供 few-shot 参考——当 agent 不确定该怎么处理时看一眼样例比读十遍规则都管用。scripts目录放的是可执行脚本比如验证脚本、格式化脚本agent 可以直接调用。这种组织方式的好处是自包含。一个 skill 文件夹拷到任何项目里都能用不依赖外部配置。我在实际使用中会把常用 skill 放在用户级目录项目特有的 skill 放在项目根目录下的.agent-skills/里这样既有个人的通用能力又有项目的定制能力。2.2 SKILL.md 里必须写清楚的几件事很多人第一次写 skill 会把它写成一篇教程结果 agent 读完还是不知道具体该干什么。我踩过这个坑之后总结出一个原则SKILL.md 是给 agent 看的操作指令不是给人看的说明文档。它需要包含以下要素。触发条件什么情况下该用这个 skill。比如当用户要求修复一个已有测试覆盖的 bug 时或者当需要为新功能添加测试时。触发条件写得越具体agent 误用的概率越低。前置检查执行前需要确认什么。比如确认当前工作目录是 git 仓库、确认测试命令可用、确认没有未提交的更改。这些检查能避免 agent 在错误状态下开始工作。执行步骤按顺序列出 agent 应该做什么。每一步都要足够具体比如读取tests/目录下与目标模块对应的测试文件而不是了解测试情况。验收标准怎么判断任务完成。比如所有测试通过、lint 无报错、改动行数不超过 50 行。验收标准最好能通过脚本自动检查减少主观判断。禁止事项明确说什么不能做。比如不要修改测试文件、不要引入新的依赖、不要改动公共 API。这一条经常被忽略但实际用起来能省很多事。2.3 为什么用 Markdown 而不是 JSON 或 YAML有人会问既然是要给程序读的为什么不用结构化格式我的理解是agent-skills的目标读者是 LLM而 LLM 对自然语言的理解能力远强于对严格结构化格式的解析能力。Markdown 的好处是既能用标题和列表提供结构又能用自然语言补充上下文和例外情况。举个例子如果你用 JSON 写不要修改测试文件那遇到测试文件本身有 bug 需要修的情况就没法处理。但用 Markdown 可以写默认不要修改测试文件如果确认测试文件本身存在错误先向用户说明再修改。这种带条件的规则用自然语言表达最自然。当然Markdown 也不是没有代价。它的解析稳定性不如 JSON不同 agent 对同一份 SKILL.md 的理解可能有偏差。我的做法是关键约束用加粗和列表强化同时在 examples 里放正反例用样例来消除歧义。3. 把 TDD 封装成 skill 的完整过程3.1 为什么选 TDD 作为第一个 skillTDD 适合作为入门 skill 有三个原因。第一它的流程极其明确先写一个失败的测试再写最少的实现让测试通过最后重构。这个流程不需要 agent 做太多判断照着走就行。第二它的验证标准是客观的测试通过就是通过没通过就是没通过不存在差不多行了的模糊地带。第三它能暴露 agent 的很多坏习惯比如跳过测试直接写实现、一次改太多文件、不跑验证就宣布完成。我建议每个刚开始用agent-skills的人都从 TDD skill 入手哪怕你平时不写测试。因为写这个 skill 的过程本身就是在梳理我希望 agent 怎么工作这件事。3.2 写 SKILL.md 的实操细节下面是我实际在用的一个 TDD skill 的骨架去掉了项目特定内容# TDD Skill ## 触发条件 当用户要求实现一个新函数、新方法或新模块且该功能可以通过单元测试验证时。 ## 前置检查 - 确认项目有可运行的测试命令检查 package.json / pyproject.toml / Makefile - 确认目标文件所在目录存在对应的测试目录 - 确认当前没有未提交的更改如有先提示用户 ## 执行步骤 1. 阅读目标模块的现有代码和测试理解代码风格和测试风格 2. 编写一个测试用例覆盖用户描述的核心行为 3. 运行测试确认它失败红 4. 编写最少的实现代码让测试通过绿 5. 运行完整测试套件确认没有破坏其他测试 6. 在保持测试通过的前提下重构实现 7. 再次运行完整测试套件 ## 验收标准 - 新增测试通过 - 原有测试全部通过 - 新增代码有对应的测试覆盖 - 没有修改任何已有测试的断言 ## 禁止事项 - 不要一次写多个测试再一起实现 - 不要在测试失败的情况下继续写实现 - 不要为了让测试通过而修改测试断言 - 不要引入新的测试框架或依赖这份 skill 的关键在于步骤 3 和步骤 6。步骤 3 强制 agent 确认测试确实失败了——很多 agent 会写一个实际上能通过的测试然后假装走了 TDD 流程。步骤 6 的重构环节则防止 agent 写出能跑但很丑的代码。3.3 examples 目录该放什么examples 目录我一般放两组样例一组是标准情况展示 skill 正常执行时的输入输出一组是边界情况展示遇到异常时该怎么处理。标准情况的样例可以是一个简单的函数实现请求配上 agent 应该产出的测试文件和实现文件。边界情况的样例则展示比如用户要求实现的功能已经有测试覆盖时agent 应该先检查现有测试而不是重复写。这些样例不需要很长但必须真实。我见过有人为了省事examples 里放的是编造的代码结果 agent 学到的模式跟实际项目完全不搭。样例最好直接从你项目的 git 历史里摘这样风格最一致。4. 让 skill 真正跑起来的配置要点4.1 Claude Code 里怎么挂载 skillClaude Code 对agent-skills的支持方式是通过项目根目录的配置文件声明 skill 路径。我一般会在项目根目录建一个.claude/目录里面放settings.json指向 skill 文件夹。具体路径和字段名可能随版本变化建议以官方文档为准。挂载之后Claude Code 在启动时会读取这些 skill并在对话中根据触发条件自动判断是否调用。你也可以在对话里显式说用 TDD skill 来实现这个功能强制它走指定流程。这里有个容易忽略的点skill 的加载顺序会影响优先级。如果两个 skill 的触发条件有重叠后加载的可能会覆盖先加载的。我的做法是给每个 skill 的触发条件写得尽量互斥避免依赖加载顺序。4.2 在 VS Code 里的使用体验VS Code 配合 Claude Code 插件使用时skill 的调用会体现在侧边栏的对话面板里。你能看到 agent 什么时候读取了 skill、执行到哪一步、有没有触发禁止事项。这个可视化对调试 skill 特别有用。我建议在 VS Code 里调试 skill 时打开终端的详细日志。Claude Code 会把 skill 的解析结果和每一步的执行情况打到日志里你能看到 agent 是不是真的按你写的步骤走了。我最初写的几个 skill 就是因为没看日志一直以为 agent 在偷懒后来发现是 SKILL.md 里某一步写得有歧义agent 理解成了另一个意思。4.3 验证脚本的编写原则scripts/verify.sh这类验证脚本我的原则是只做客观检查不做主观判断。比如检查测试是否通过、检查 lint 是否报错、检查改动文件数量是否超限这些都可以脚本化。但代码是否优雅这种判断就不要放进脚本留给人工 review。验证脚本的退出码要规范0 表示通过非 0 表示失败。agent 会根据退出码决定是否继续。我见过有人写的脚本无论成功失败都返回 0结果 agent 以为一切正常实际上早就出问题了。脚本里还要注意超时设置。测试套件如果很大跑一次可能要几分钟agent 可能会等不及。我一般会在脚本里加超时超时后返回特定退出码让 agent 知道是超时而不是失败。5. 实际使用中踩过的坑5.1 skill 写太细反而不好用我最初写 skill 的时候恨不得把每一步都拆成原子操作结果 agent 执行起来非常僵硬。比如我写先读取文件 A 的第 10 到 20 行但实际项目里文件 A 的行号经常变agent 每次都要重新定位反而浪费时间。后来我改成描述意图而不是具体操作。比如读取目标函数的实现和它的测试让 agent 自己决定读哪些行。这样灵活性高很多而且 agent 对代码结构的理解通常比行号定位更可靠。这个度的把握需要试几次。我的经验是涉及外部状态的步骤要具体比如跑哪个命令涉及代码理解的步骤可以模糊比如读哪些文件。5.2 agent 会假装执行了 skill这是最让人头疼的问题。有时候 agent 会在回复里说我已经按照 TDD skill 执行了但实际上它根本没读 skill 文件只是根据对话上下文编了一套流程。这种情况在 skill 触发条件写得不够明确时特别容易发生。我的应对办法是在 skill 里加一个显式的确认步骤。比如第一步就是输出[TDD-SKILL-LOADED]表示已加载本 skill。这样你能从对话里直接看到 agent 是不是真的读了 skill。虽然有点笨但确实有效。另一个办法是让验证脚本检查 skill 的执行痕迹。比如 TDD skill 要求先写测试再写实现那验证脚本可以检查 git diff 里测试文件的修改时间是否早于实现文件。这种检查虽然不能百分百可靠但能拦住大部分假装执行的情况。5.3 多个 skill 冲突时的处理当项目里 skill 多了之后冲突几乎不可避免。我遇到过最典型的是代码格式化 skill和TDD skill打架TDD skill 要求先写测试格式化 skill 要求每次改动后立即格式化结果 agent 在写测试的过程中被格式化打断流程全乱了。解决思路有两个。一是给 skill 加优先级标记高优先级的 skill 执行期间低优先级的 skill 暂停。二是把冲突的 skill 合并比如把格式化作为 TDD skill 的一个子步骤而不是独立的 skill。我倾向于第二种因为合并之后流程更连贯agent 不需要在多个 skill 之间切换。但合并的代价是 skill 会变复杂维护成本上升。具体怎么选要看冲突的频率和严重程度。5.4 测试环境不稳定导致的误判TDD skill 依赖测试结果来判断是否继续但如果测试本身不稳定比如依赖网络、依赖时间、有随机性agent 就会收到错误的信号。我遇到过 agent 因为一个 flaky test 失败反复修改实现代码最后把好好的代码改坏了。对策是在 skill 的前置检查里加一条确认测试套件在干净状态下能稳定通过。如果发现有 flaky test先修测试再跑 skill。另外可以在验证脚本里对失败的测试重试一次排除偶发失败。6. 从单个 skill 到 skill 体系6.1 什么时候该拆出新 skill一开始我只有一个 TDD skill后来发现有些任务不适合走完整 TDD 流程比如修一个拼写错误、改一个配置值。这些任务走 TDD 太重了agent 会花大量时间写测试而实际上根本不需要。于是我把 skill 拆成了三个层次轻量修改 skill适用于改配置、改文案、标准开发 skill适用于新功能走完整 TDD、重构 skill适用于不改行为的代码整理。每个 skill 的触发条件不同agent 根据任务类型自动选择。拆分的判断标准是如果一类任务反复出现且现有 skill 处理它时明显别扭就该拆了。不要为了拆分而拆分skill 太多会导致 agent 选择困难。6.2 skill 之间的组合调用有些复杂任务需要多个 skill 配合。比如给现有模块添加一个新功能并重构旧代码这既需要标准开发 skill又需要重构 skill。我的做法是在 skill 里允许调用其他 skill类似函数调用。具体实现是在 SKILL.md 里写完成步骤 X 后调用 refactor skill 处理 Y 部分。agent 读到这行会去加载对应的 skill。这种组合调用要注意避免循环依赖A 调 B、B 调 A 会让 agent 陷入死循环。我一般会画一张 skill 依赖图确保没有环。6.3 版本管理与团队共享skill 是要演进的。项目变了、团队习惯了、agent 能力提升了skill 都得跟着改。我用 git 管理 skill 目录每次修改都写清楚改了什么、为什么改。这样出问题的时候能快速回滚。团队共享方面我的做法是通用 skill 放仓库、项目 skill 放项目。通用 skill 比如 TDD、重构、代码审查这些跨项目都能用放在一个独立的 skill 仓库里各项目通过 git submodule 或包管理工具引入。项目特有的 skill 比如这个项目的数据库迁移流程就放在项目自己的.agent-skills/目录里。这样分工的好处是通用 skill 的改进能惠及所有项目而项目 skill 的定制不会污染通用库。代价是引入通用 skill 需要一点配置工作但一次配好之后就很省心。7. 关于 agent-skills 的几个常见误解7.1 它不是 prompt 模板很多人把agent-skills和 prompt 模板混为一谈。区别在于prompt 模板通常是一段静态文本你复制粘贴到对话框里而 skill 是带执行逻辑和验证机制的能力单元。skill 里可以有条件分支、可以调用脚本、可以引用其他 skill这些都不是单纯的 prompt 能做到的。另一个区别是持久性。prompt 模板用完就没了下次还得重新贴skill 挂在项目里agent 每次启动都能用。对于需要反复执行的任务skill 的边际成本几乎为零。7.2 它不能替代人的判断我见过有人期望agent-skills能让 AI 完全自主地完成开发任务这是不现实的。skill 能规范 agent 的行为但没法保证 agent 的每个决策都正确。尤其是涉及架构设计、业务逻辑取舍这类需要上下文判断的事情agent 仍然需要人的指导。我的用法是把 skill 当作执行层的约束把判断留给决策层。比如 skill 规定改完代码必须跑测试但该不该改这个代码仍然由人决定。这样分工之后agent 负责把确定的事情做对人负责把不确定的事情想清楚。7.3 它不是一次配置就一劳永逸skill 需要持续维护。项目在变skill 也得跟着变。我每个月会花半小时 review 一下现有 skill看看有没有过时的步骤、有没有可以合并的 skill、有没有新出现的任务类型需要新 skill。这个维护成本听起来不高但实际做起来容易忘。我的办法是把 skill review 加进项目的例行维护清单跟依赖更新、文档更新放在一起。这样就不会因为长期不维护导致 skill 跟实际项目脱节。8. 我个人的一些使用心得用agent-skills这段时间最大的体会是它逼着我把隐性知识显式化。以前很多我知道该这么做但说不清楚为什么的经验在写 skill 的过程中被迫梳理清楚了。这个过程本身对团队协作就有价值哪怕不用 AI agent这些梳理出来的流程也能帮到新人。另一个体会是不要追求一步到位。我最初的 TDD skill 写了满满两页结果 agent 执行起来各种问题。后来砍到半页反而好用了。skill 的复杂度应该跟任务的确定性匹配越确定的任务skill 可以越简单越需要判断的任务skill 才需要更多约束。最后分享一个实用技巧给 skill 加一个逃生舱。在 SKILL.md 里写清楚如果遇到 skill 未覆盖的情况停止执行并询问用户。这样 agent 不会在遇到意外时硬着头皮往下走而是把问题交回给人。这个逃生舱机制帮我避免了好几次 agent 自作主张导致的麻烦。如果你也在用 Claude Code 或者其他 AI coding agent我建议从一个小 skill 开始试。不用一上来就搞完整的 skill 体系先写一个解决你当前最痛的问题的 skill跑通了再考虑扩展。这个过程里踩的坑比看十篇教程都有用。