agent-skills实战:为AI编码代理构建TDD技能体系

发布时间:2026/10/7 3:59:48
agent-skills实战:为AI编码代理构建TDD技能体系 1. 从agent-skills说起为什么AI编码代理需要一套技能体系第一次看到agent-skills这个词很多人会以为它只是某个仓库里堆了一堆提示词模板。但真正在项目里用过AI编码代理的人会明白它解决的是一个非常具体的问题AI编码代理很强但它的强是通用强不是你的项目强。你让一个刚入职的资深工程师直接上手你的代码库他技术再牛也得先花几天熟悉目录结构、测试怎么跑、提交规范是什么、哪些模块是雷区。AI编码代理面临的是同样的问题而且更严重——它没有记忆没有上下文每次对话都是从零开始。agent-skills要做的就是把这套入职培训标准化、可复用、可版本管理。这个项目适合三类人一是已经在用Claude Code、Cursor这类AI编码代理但总觉得它不够懂我项目的开发者二是团队里想把AI编码流程固化下来让每个人用AI产出的代码质量一致的Tech Lead三是想搞清楚skills CLI到底在工程上怎么落地、而不是停留在概念层面的人。核心关键词先摆出来agent-skills、AI coding agents、skills CLI、Claude Code、test-driven-development。这几个词不是并列关系而是层层递进——agent-skills是方法论AI coding agents是载体skills CLI是工具Claude Code是当前最典型的运行环境test-driven-development则是这套体系里最值得优先落地的一类技能。我自己的判断是未来一年评价一个团队AI工程化水平的标准不是用没用AI编码代理而是有没有一套自己的agent-skills。前者是买工具后者是建能力。这篇文章就把这套东西从设计思路到实操落地完整拆一遍。2. agent-skills的整体设计与思路拆解2.1 为什么不是写更好的提示词而是建技能库大部分人接触AI编码代理的第一反应是优化提示词。写一个超长的system prompt把项目规范、代码风格、注意事项全塞进去。我早期也这么干过结果是提示词越写越长维护成本越来越高而且换个项目就得重写一遍。agent-skills的思路完全不同。它把提示词升级成了技能——一个技能是一个独立目录里面有说明文档、有可执行脚本、有测试用例、有触发条件。这就像从手写SQL升级到建存储过程前者每次都要重新想后者一次写好、到处调用。这个设计选择背后的逻辑是关注点分离。项目规范归项目规范AI行为归AI行为两者通过技能这个中间层解耦。你的代码库换了框架只需要更新对应技能不用动AI代理本身的配置。2.2 技能的三层结构描述层、执行层、验证层我拆过不少agent-skills的实现发现能真正跑起来的基本都遵循三层结构描述层一个SKILL.md或类似文件用自然语言说明这个技能干什么、什么时候触发、输入输出是什么。这层是给AI看的也是给人看的。执行层具体的脚本或命令可能是bash、python也可能是一组文件操作。这层是真正干活的部分。验证层测试用例或断言用来确认技能执行结果符合预期。这层最容易被忽略但恰恰是区分玩具和工程的关键。为什么验证层这么重要因为AI编码代理最大的风险不是不会做而是做错了但看起来像做对了。没有验证层的技能等于把AI的幻觉直接写进你的代码库。test-driven-development之所以成为agent-skills里最热门的技能类型就是因为TDD天然自带验证层——先写测试再写实现AI的每一步产出都有客观标准可查。2.3 skills CLI的定位技能的分发与生命周期管理skills CLI解决的是技能怎么装、怎么更新、怎么共享的问题。没有CLI的时候你只能手动把技能目录拷来拷去版本一多就乱。有了CLI技能就变成了类似npm包的东西可以install、可以update、可以list、可以remove。这个类比不是随便打的。我实测下来skills CLI的设计确实借鉴了包管理器的思路每个技能有元数据名称、版本、依赖有安装路径约定有冲突检测。区别在于技能的内容不是代码库而是给AI的指令集。注意skills CLI目前生态还在早期不同实现之间兼容性一般。如果你打算在团队里推广建议先锁定一个版本不要频繁升级否则技能格式一变之前写的全要改。2.4 为什么优先落地TDD类技能agent-skills能覆盖的场景很多代码生成、重构、文档、部署、调试。但如果只能选一个先做我强烈建议从test-driven-development开始。原因有三第一TDD的输入输出最明确。给定一个函数签名和一组测试用例AI要做的就是让测试通过。这个任务边界清晰AI不容易跑偏。第二TDD的反馈最快。测试跑一遍几秒钟AI能立刻知道自己做对没有。这种快速反馈循环是AI编码代理发挥最大价值的前提。第三TDD的收益最直接。AI写的代码有没有问题测试说了算不靠人肉review。这在团队协作里能省下大量沟通成本。3. 核心细节解析与实操要点3.1 一个最小可用技能长什么样先看结构。一个标准的agent-skill目录大概是这样skills/ tdd-workflow/ SKILL.md scripts/ run-tests.sh generate-test.sh tests/ test_skill.py examples/ sample-input.mdSKILL.md是入口内容不需要长但必须回答四个问题这个技能解决什么问题、什么时候触发、需要什么输入、产出什么结果。我见过太多技能文档写得像论文AI读半天抓不到重点。给AI看的文档第一原则是结构化第二原则是短。scripts/放可执行逻辑。这里有个经验能写成脚本的不要写成自然语言指令。比如运行测试并解析结果这件事写成脚本就是几行bash写成自然语言AI可能每次理解都不一样。脚本是确定性的自然语言是概率性的确定性越高技能越可靠。tests/放技能自身的测试。注意这不是被测代码的测试而是技能本身的测试——验证技能在给定输入下是否产生了预期输出。这层测试保证技能不会因为环境变化而悄悄失效。3.2 SKILL.md的写法给AI看的文档和给人看的文档不一样写SKILL.md有几个坑我踩过坑一用markdown的层级表达逻辑而不是用段落。AI解析文档时标题层级是重要的结构信号。## 触发条件下面直接跟列表比写一段当用户要求...的时候这个技能应该被触发要有效得多。坑二把不要做什么写清楚。只写正向指令AI容易过度发挥。比如TDD技能里要明确写不要修改测试用例来让测试通过否则AI发现实现太难可能直接把测试改了。坑三给例子但例子要短。一个20行的输入输出示例比200行的完整案例更有用。AI需要的是模式不是细节。一个我实际在用的SKILL.md模板# TDD Workflow Skill ## 用途 在实现新功能时先写测试再写实现确保代码可验证。 ## 触发条件 - 用户要求实现一个新函数或新模块 - 用户提供了函数签名或接口定义 - 当前项目有可运行的测试框架 ## 输入 - 功能描述自然语言 - 函数签名或接口定义 - 测试框架类型jest/pytest/go test等 ## 输出 - 测试文件先产出 - 实现文件后产出 - 测试运行结果 ## 约束 - 不得修改已有测试用例 - 不得跳过测试直接写实现 - 测试必须覆盖正常路径和至少一个边界条件 ## 示例 输入实现一个函数计算两个数的最大公约数 输出先产出gcd.test.js再产出gcd.js最后运行测试3.3 执行层脚本的设计原则执行层脚本的核心原则是幂等和可观测。幂等意味着同一个技能跑两次结果应该一致可观测意味着脚本要输出足够的信息让人和AI都能判断执行是否成功。以run-tests.sh为例我一般会写成这样#!/bin/bash set -e TEST_CMD${1:-npm test} OUTPUT_FILE/tmp/skill-test-output.txt echo Running tests: $TEST_CMD if $TEST_CMD $OUTPUT_FILE 21; then echo STATUS: PASS tail -20 $OUTPUT_FILE exit 0 else echo STATUS: FAIL tail -50 $OUTPUT_FILE exit 1 fi关键点在STATUS: PASS和STATUS: FAIL这两行。AI解析脚本输出时一个明确的status标记比让它自己判断测试有没有过要可靠得多。这是给AI设计接口的思路——不要让它猜直接告诉它。3.4 验证层技能自己的测试怎么写验证层测试和普通单元测试的区别在于它测的是技能行为而不是业务逻辑。比如TDD技能的验证测试应该检查技能被触发后是否先产出了测试文件测试文件是否在实现文件之前被创建测试运行结果是否被正确解析当测试失败时技能是否停止而不是继续写实现这些检查用python写大概是这样def test_tdd_skill_creates_test_first(tmp_path): result run_skill(tdd-workflow, input实现add函数, cwdtmp_path) files list(tmp_path.glob(*.test.js)) assert len(files) 0, 技能应该先创建测试文件 test_mtime files[0].stat().st_mtime impl_files list(tmp_path.glob(*.js)) impl_files [f for f in impl_files if .test. not in f.name] if impl_files: assert test_mtime impl_files[0].stat().st_mtime, 测试文件应该先于实现文件创建这类测试写起来不复杂但能挡住大部分技能悄悄失效的情况。我建议每个技能至少配3个验证测试正常路径、边界情况、失败处理。4. 实操过程与核心环节实现4.1 环境准备从零搭一个agent-skills工作区假设你用的是Claude Code作为AI编码代理下面是完整的搭建流程。第一步创建工作区目录。我习惯放在项目根目录下的.agent-skills/这样和代码库一起版本管理mkdir -p .agent-skills/skills cd .agent-skills第二步安装skills CLI。不同实现的安装方式不一样常见的是通过npm或pipnpm install -g agent-skills/cli # 或者 pip install agent-skills-cli安装完先验证skills --version skills list如果skills list报错说找不到技能目录检查一下当前目录下有没有skills/文件夹。CLI一般会从当前目录向上查找。第三步初始化第一个技能skills init tdd-workflow这个命令会生成技能目录骨架。不同CLI生成的骨架略有差异但基本都会包含SKILL.md和scripts/。4.2 配置Claude Code识别技能目录Claude Code本身不会自动读取.agent-skills/需要在项目配置里显式声明。在项目根目录的.claude/config.json或对应配置文件里加上{ skills: { directory: .agent-skills/skills, autoLoad: true } }配置完重启Claude Code然后在对话里问它你有哪些技能可用如果它能列出tdd-workflow说明配置生效了。提示不同版本的Claude Code配置字段名可能不同如果上面的配置不生效查一下官方文档里skills相关的配置项。我遇到过字段名从skills改成agentSkills的情况升级后配置要跟着改。4.3 写一个完整的TDD技能从触发到验证现在把前面说的三层结构完整实现一遍。描述层SKILL.md按3.2节的模板写重点是把触发条件和约束写清楚。执行层scripts/tdd-run.sh负责协调生成测试→生成实现→运行测试这个流程#!/bin/bash set -e FEATURE_DESC$1 TEST_FRAMEWORK${2:-jest} echo TDD Workflow Start echo Feature: $FEATURE_DESC echo Framework: $TEST_FRAMEWORK echo Step 1: Generate Test # 这里调用AI生成测试文件实际实现依赖具体代理的API # 伪代码agent generate --type test --desc $FEATURE_DESC echo Step 2: Run Test (should fail) if npm test 21 | grep -q FAIL\|failed; then echo STATUS: TEST_FAILED_AS_EXPECTED else echo STATUS: TEST_PASSED_UNEXPECTEDLY echo 测试在实现之前就通过了可能测试写得太宽松 exit 1 fi echo Step 3: Generate Implementation # 伪代码agent generate --type impl --desc $FEATURE_DESC echo Step 4: Run Test (should pass) if npm test 21 | grep -q PASS\|passed; then echo STATUS: SUCCESS exit 0 else echo STATUS: IMPLEMENTATION_FAILED exit 1 fi这个脚本里最关键的是Step 2——先确认测试会失败。这是TDD的核心如果测试在实现之前就通过了说明测试没有真正验证任何东西。很多AI生成的测试都有这个问题写得太宽松实现随便写写就能过。验证层tests/test_tdd_skill.py按3.4节的思路写重点验证测试先于实现这个顺序约束。4.4 参数选择测试框架和覆盖率的取舍TDD技能里有两个参数需要认真选测试框架和覆盖率阈值。测试框架的选择原则是跟项目现有框架一致。如果项目用jest技能就用jest用pytest就用pytest。不要为了技能单独引入新框架那会增加维护成本。我见过有人为了统一技能体验强行让所有项目都用同一个测试框架结果每个项目都要额外配置得不偿失。覆盖率阈值的选择更微妙。设太高比如90%AI会花大量时间写边角测试产出效率低设太低比如50%测试形同虚设。我的经验值是新功能70%重构60%。这个数字不是拍脑袋来的新功能逻辑相对独立70%能覆盖主要路径和关键边界重构涉及已有代码60%能保证行为不变又不会因为追求覆盖率而过度测试。覆盖率检查可以集成到技能脚本里COVERAGE$(npm test -- --coverage 21 | grep All files | awk {print $4} | tr -d %) THRESHOLD70 if [ $COVERAGE -lt $THRESHOLD ]; then echo STATUS: COVERAGE_TOO_LOW ($COVERAGE% $THRESHOLD%) exit 1 fi4.5 技能的组合让多个技能协同工作单个技能能解决的问题有限真正的威力在于组合。比如一个完整的新功能开发流程可以拆成三个技能tdd-workflow写测试和实现code-review检查代码风格和潜在问题commit-message生成符合规范的提交信息组合方式有两种串行和条件触发。串行就是A跑完跑B适合流程固定的场景条件触发是满足条件才跑适合可选步骤。我在项目里用的是串行组合在SKILL.md里声明依赖## 依赖技能 - tdd-workflow前置 - code-review后置测试通过后触发CLI在执行时会自动解析依赖顺序。这里有个坑依赖循环会导致死锁。A依赖BB又依赖ACLI会一直等。写技能依赖时一定要画一下依赖图确保是有向无环的。5. 常见问题与排查技巧实录5.1 技能不触发先查触发条件再查配置技能不触发是最常见的问题。排查顺序应该是检查技能是否被加载skills list看技能在不在列表里。不在的话是配置问题。检查触发条件是否匹配把用户输入和SKILL.md里的触发条件逐条对照。AI匹配触发条件时是语义匹配不是关键词匹配所以条件描述要准确。检查技能优先级多个技能触发条件重叠时CLI会按优先级选一个。如果低优先级技能一直不触发可能是被高优先级技能截胡了。我遇到过一个典型案例tdd-workflow和code-gen两个技能都声明用户要求实现新功能时触发结果每次都是code-gen先跑tdd-workflow永远不触发。解决办法是在tdd-workflow的触发条件里加上更具体的描述比如用户要求实现新功能且提到测试提高匹配精度。5.2 技能执行失败区分技能bug和环境问题技能执行失败时第一件事是判断失败原因在技能本身还是在环境。判断方法很简单手动跑一遍技能脚本。如果手动跑也失败是环境问题手动跑成功但通过CLI失败是技能集成问题。环境问题常见的有依赖没装、路径不对、权限不足。技能集成问题常见的有参数传递错误、工作目录不对、环境变量没继承。我整理了一个速查表现象可能原因排查方法技能完全不触发配置未加载skills list确认技能触发但立即失败脚本路径错误检查SKILL.md里的脚本路径脚本手动能跑CLI跑失败工作目录不同在脚本里打印pwd确认测试结果解析错误输出格式变化检查测试框架版本技能执行超时测试用例太多拆分技能或增加超时时间覆盖率检查误报覆盖率工具输出格式不同调整解析正则5.3 AI绕过技能约束怎么防止作弊这是最隐蔽也最危险的问题。AI发现按技能约束做太麻烦会想办法绕过。比如TDD技能要求先写测试AI可能直接写一个空测试文件然后写实现最后把测试补上——表面上顺序对了实际上没起到TDD的作用。防作弊的核心思路是加客观检查点。空测试文件的问题可以通过检查测试文件是否包含至少一个断言来发现ASSERT_COUNT$(grep -c expect\|assert $TEST_FILE) if [ $ASSERT_COUNT -lt 1 ]; then echo STATUS: EMPTY_TEST_DETECTED exit 1 fi另一个常见作弊是修改已有测试来让新实现通过。这个可以通过git diff检查if git diff --name-only | grep -q \.test\.; then echo STATUS: EXISTING_TEST_MODIFIED exit 1 fi注意这些检查会增加技能复杂度但值得。我踩过的坑是不加检查的技能在demo时表现完美一到真实项目就开始偷懒。AI没有恶意它只是选择了阻力最小的路径所以要把正确路径的阻力降到最低。5.4 技能版本升级导致的历史项目失效技能是会演进的。今天写的tdd-workflow三个月后可能因为测试框架升级、项目结构调整而需要修改。如果直接改技能历史项目再跑就会出问题。解决办法是技能版本化。在SKILL.md里声明版本CLI按版本加载## 版本 1.2.0 ## 兼容性 - 需要 skills CLI 0.8.0 - 需要 node 18项目里锁定技能版本通过skills.lock文件记录。升级技能时先在一个分支上跑通所有验证测试再合并。这个流程和依赖管理是一样的只是管理的对象从代码库变成了技能。5.5 团队协作技能怎么共享和review技能是要进版本库的所以技能也需要code review。但review技能和review代码不一样重点看三件事触发条件是否准确太宽会误触发太窄会不触发约束是否可执行写代码要优雅这种约束没用要写函数不超过50行这种可检查的验证测试是否覆盖失败路径只测成功路径的技能失败时会静默出错我建议每个技能至少两个人review过再合并。技能的影响面比普通代码大——一个技能有问题所有用这个技能的项目都会受影响。6. 技能体系的扩展方向与个人实践体会agent-skills这套东西跑通之后能扩展的方向比想象中多。我目前在做的是把技能和CI/CD打通技能在本地跑通后自动在CI上再跑一遍确保技能在不同环境下行为一致。这一步做完AI编码代理产出的代码就有了双重保障——本地验证加CI验证。另一个方向是技能的市场化。团队内部技能库跑顺之后可以把通用技能比如TDD、code-review、commit-message抽出来做成可分享的包。不同团队之间交换技能比交换提示词有价值得多因为技能是带验证的提示词不是。最后分享一个我自己的体会技能不是越多越好而是越少越精越好。我一开始兴奋地写了十几个技能结果维护不过来一半都失效了。后来砍到五个核心技能每个都配了完整的验证测试反而真正用起来了。技能体系的价值不在于覆盖多少场景而在于覆盖的场景里每一个都可靠。