☰
agent-skills 实战:用 TDD 封装 AI 编程能力
2026/10/8 11:31:37 网站建设 项目流程

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.sh

SKILL.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 作为第一个 skill

TDD 适合作为入门 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 里怎么挂载 skill

Claude 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,跑通了再考虑扩展。这个过程里踩的坑,比看十篇教程都有用。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询