☰
agent-skills 实战:为 AI 编码代理注入 TDD 技能包
2026/10/8 11:31:36 网站建设 项目流程

1. 从“agent-skills”说起:为什么它值得单独拿出来聊

“agent-skills”这个词最近在 AI coding agents 圈子里出现的频率越来越高,尤其是搭配 Claude Code、skills CLI、test-driven-development 这些关键词一起看的时候,你会发现它其实指向一个很具体的东西:给 AI 编码代理装上一套可复用、可组合、可版本管理的技能包。说白了,就是让 AI 不只是“会写代码”,而是“知道在什么场景下该按什么流程写代码”。

我最早接触这个概念是在折腾 Claude Code 的时候。当时我的诉求很简单:让 AI 帮我改一个老项目的 bug,结果它上来就改代码,改完也不跑测试,最后提交上去 CI 直接红了一片。后来我才意识到,问题不在于模型能力不够,而在于我没有给它一套明确的“工作技能”——比如“改代码前先读测试”“改完必须跑 test-driven-development 流程”“提交前检查 lint”。agent-skills 要解决的,就是这类问题。

它适合谁?三类人最该关注:一是已经在用 Claude Code、Cursor、Windsurf 这类 AI coding agents 的开发者;二是想把自己的开发流程沉淀成可复用资产的技术负责人;三是刚入门 AI 辅助编程、还在“AI 写啥我用啥”阶段的新手。因为 agent-skills 本质上不是某个工具的功能,而是一种把工程经验结构化注入 AI 工作流的方法论。

我下面会从设计思路、核心细节、实操落地、问题排查四个层面,把 agent-skills 这套东西拆开讲清楚。你不需要先成为 Claude Code 专家,只要你会用命令行、写过一点代码,就能跟着复现。

2. agent-skills 的整体设计与思路拆解

2.1 为什么不是“写个 prompt”就完事

很多人第一次听到 agent-skills,第一反应是:“这不就是给 AI 写一段系统提示词吗?”我一开始也这么想,但实际用下来发现差别很大。普通 prompt 是一次性、上下文绑定的,你在这个对话里写了“改代码要跑测试”,换个会话就失效了。而 agent-skills 是持久化、可发现、可组合的。

它的设计思路有点像 Linux 的man手册加PATH环境变量:每个 skill 是一个独立目录,里面有说明文档、执行脚本、依赖声明;agent 在需要的时候通过 skills CLI 去“发现”这些技能,然后按需加载。这样做的好处是,你的开发规范不再散落在各个聊天记录里,而是变成项目仓库的一部分,可以提交、可以 review、可以继承。

另一个关键设计是技能与模型解耦。Claude Code 只是其中一个宿主,理论上任何支持工具调用的 AI coding agent 都可以消费同一套 skills。这就避免了“我换了个模型,之前调教好的流程全废了”的尴尬。我实测下来,同一套 test-driven-development skill,在 Claude Code 和另一个支持 skills CLI 的 agent 里行为基本一致,只是触发时机略有差异。

2.2 核心架构:skills CLI 扮演了什么角色

skills CLI 是整个 agent-skills 体系的“入口层”。你可以把它理解成一个包管理器加运行时:它负责扫描本地 skill 目录、解析元数据、把 skill 暴露给 agent 调用。没有它,agent 就得硬编码每个技能的路径和参数,维护成本极高。

我画不出图(也不建议用图),但你可以这样理解数据流:agent 收到用户任务 → 判断需要某个技能 → 调用 skills CLI 查询可用技能 → CLI 返回技能描述和执行入口 → agent 按描述调用 → 执行结果回传给 agent → agent 继续推理。这个链条里,skills CLI 是唯一需要稳定运行的组件,所以它的安装和版本管理要格外注意。

注意:skills CLI 的版本最好和 agent 宿主版本对齐。我踩过一次坑,CLI 升级到新大版本后,旧 skill 的元数据格式不兼容,导致 agent 一直报“skill not found”,排查了半天才发现是版本错位。

2.3 和 test-driven-development 的天然契合

test-driven-development 被列为热搜词不是偶然。AI coding agent 最大的风险是“自信地写出错误代码”,而 TDD 恰好提供了一套可验证的反馈闭环:先写失败测试 → 让 agent 实现 → 跑测试 → 红了就修 → 绿了才提交。把 TDD 做成一个 skill,等于给 agent 装了一个“质量闸门”。

我自己的做法是,在 skill 里明确写死三条规则:第一,任何代码修改前必须先运行现有测试套件并记录基线;第二,新增功能必须先写测试再写实现;第三,测试未通过时禁止调用提交类工具。这三条看起来简单,但实际能挡掉八成以上的低级错误。而且因为它是 skill 而不是 prompt,团队里每个人拉下仓库就自动生效,不需要口头强调。

3. 核心细节解析与实操要点

3.1 skill 目录结构:别小看这几个文件

一个标准的 agent-skill 目录通常长这样:

my-skill/ SKILL.md # 技能说明,给 agent 读的 skill.json # 元数据,给 skills CLI 读的 scripts/ run.sh # 实际执行入口 tests/ smoke.test.js # 技能自身的冒烟测试

SKILL.md是核心,它要用自然语言写清楚“这个技能什么时候用、输入是什么、输出是什么、有什么禁忌”。我见过很多人把 SKILL.md 写成 README,全是安装步骤,结果 agent 读完不知道啥时候该调用。正确的写法应该像给新同事写操作手册:第一段说触发条件,第二段说执行步骤,第三段说失败处理。

skill.json里最关键的是name、version、entrypoint和triggers。triggers我建议用关键词数组而不是正则,因为 agent 的意图识别对自然语言关键词更友好。比如 TDD skill 的 triggers 可以写["写测试", "tdd", "test first", "红绿重构"]。

3.2 参数传递:环境变量比命令行参数更稳

skills CLI 调用 skill 时,参数传递方式有两种:命令行参数和环境变量。我强烈建议用环境变量。原因是 agent 生成的命令行参数经常带空格、引号、特殊字符,shell 转义一不留神就出错。环境变量则稳定得多。

具体做法是在skill.json里声明env字段,skills CLI 会把 agent 传入的上下文注入为环境变量。比如:

{ "name": "tdd-runner", "version": "1.2.0", "entrypoint": "scripts/run.sh", "env": ["TARGET_DIR", "TEST_COMMAND", "MAX_RETRY"] }

然后在run.sh里直接读$TARGET_DIR。这样即使路径里有空格,也不会被拆成多个参数。我实测下来,这个改动让 skill 调用失败率从大概三成降到了不到半成。

3.3 技能粒度:一个技能只做一件事

新手最容易犯的错是做一个“万能 skill”,里面塞了格式化、测试、提交、部署全套流程。结果 agent 调用时要么全跑一遍浪费时间,要么因为某一步失败导致整个技能不可用。我的经验是:一个 skill 只做一件事,复杂流程用多个 skill 串联。

比如“提交前检查”可以拆成三个 skill:lint-check、test-run、commit-guard。agent 可以按需组合,也可以单独调用。这样每个 skill 的 SKILL.md 都很短,agent 理解成本低,出错也容易定位。而且拆分后,你可以在不同项目里复用其中一部分,不用整包搬运。

3.4 版本管理:skill 也要打 tag

skill 是代码资产,必须进版本控制。我建议每个 skill 独立仓库或者独立目录,用语义化版本打 tag。skills CLI 支持从 git 仓库拉取指定版本的 skill,这样团队里有人改了 skill,其他人不会被动升级到不稳定版本。

实操心得:在 CI 里加一步“skill 冒烟测试”,每次 skill 变更都跑一遍tests/smoke.test.js。这个测试不需要覆盖业务逻辑,只要确认 skill 能被 CLI 发现、入口脚本能执行、退出码正确即可。这一步能挡掉大部分“改完 skill 结果 agent 完全不认识”的问题。

4. 实操过程与核心环节实现

4.1 环境准备:从零把 skills CLI 跑起来

假设你在 Ubuntu 或者 macOS 上,已经装好了 Node.js 18+ 和 git。第一步是安装 skills CLI。官方推荐用 npm 全局安装:

npm install -g @agent-skills/cli

装完后验证:

skills --version

如果提示命令找不到,检查 npm 全局 bin 目录是否在 PATH 里。macOS 上通常是/usr/local/bin或~/.npm-global/bin,Ubuntu 上可能是/usr/bin。我见过不少人在这一步卡住,其实加一行 export 就解决了。

接下来初始化一个 skill 工作区:

mkdir -p ~/agent-skills-workspace cd ~/agent-skills-workspace skills init

skills init会生成一个skills.config.json,里面配置了 skill 扫描路径。默认是./skills,你可以改成绝对路径,方便多个项目共享。

4.2 写第一个 skill:以 TDD 为例

我在skills/下新建tdd-runner/,然后写SKILL.md:

# TDD Runner ## 何时使用 当用户要求新增功能、修复 bug,且项目包含测试框架时使用。 ## 输入 - TARGET_DIR: 项目根目录 - TEST_COMMAND: 运行测试的命令,如 `npm test` - MAX_RETRY: 最大重试次数,默认 3 ## 执行步骤 1. 在 TARGET_DIR 下运行 TEST_COMMAND,记录基线结果。 2. 如果基线就是红的,停止并报告,不要继续改代码。 3. 提示 agent 先写失败测试。 4. 运行测试,确认新测试失败。 5. 提示 agent 实现功能。 6. 运行测试,若失败则重试,最多 MAX_RETRY 次。 7. 全部通过后输出成功信号。 ## 禁忌 - 禁止在测试未通过时调用 git commit。 - 禁止跳过基线检查。

然后写skill.json:

{ "name": "tdd-runner", "version": "1.0.0", "entrypoint": "scripts/run.sh", "triggers": ["tdd", "写测试", "test first", "红绿重构"], "env": ["TARGET_DIR", "TEST_COMMAND", "MAX_RETRY"] }

scripts/run.sh的核心逻辑:

#!/usr/bin/env bash set -euo pipefail TARGET_DIR="${TARGET_DIR:-.}" TEST_COMMAND="${TEST_COMMAND:-npm test}" MAX_RETRY="${MAX_RETRY:-3}" cd "$TARGET_DIR" echo "[tdd] 运行基线测试..." if ! eval "$TEST_COMMAND"; then echo "[tdd] 基线测试失败,停止。" exit 1 fi echo "[tdd] 基线通过,等待 agent 写失败测试..."

这里set -euo pipefail很重要,能避免脚本在中间步骤失败后继续跑。eval用在这里是因为 TEST_COMMAND 可能是带参数的复合命令,但要注意只接受可信输入。

4.3 注册与调用:让 agent 真正用起来

写完 skill 后,在skills.config.json里确认扫描路径包含./skills,然后运行:

skills list

应该能看到tdd-runner。如果看不到,检查skill.json的 JSON 格式是否合法,我经常因为多一个逗号导致解析失败。

调用方式有两种:手动测试用skills run tdd-runner,agent 自动调用则依赖 triggers 匹配。我建议先手动跑通,再交给 agent。手动跑的时候可以显式传环境变量:

TARGET_DIR=/path/to/project TEST_COMMAND="npm test" skills run tdd-runner

确认输出符合预期后,再在 Claude Code 里用自然语言触发,比如“用 TDD 方式给这个模块加一个校验函数”。agent 应该会自动发现并调用 tdd-runner。

4.4 参数计算与选择:MAX_RETRY 到底设多少

MAX_RETRY 这个参数看似随意,其实有讲究。设太小,agent 还没修完就放弃;设太大,一个死循环能烧掉大量 token。我的经验值是 3 到 5 之间。对于测试反馈明确的场景(比如单元测试),3 次足够;对于集成测试或端到端测试,反馈慢且噪声大,可以设 5 次但加上超时。

超时怎么算?假设单次测试运行平均 30 秒,5 次就是 150 秒,加上 agent 推理时间,整个 skill 执行可能超过 5 分钟。这时候要在skill.json里加timeout字段,skills CLI 会在超时后强制终止并返回失败。我一般设timeout: 600,也就是 10 分钟,给足余量但不至于无限等待。

注意:不同项目的测试命令差异很大。有的项目npm test会跑全量测试,耗时很长;有的项目支持npm test -- --watch=false只跑一次。在 SKILL.md 里要明确写出推荐命令,避免 agent 自己猜。

5. 常见问题与排查技巧实录

5.1 skill 不被发现:从三个层面排查

这是最高频的问题。我整理了一个排查顺序:

现象可能原因排查方法
skills list为空扫描路径不对检查skills.config.json的paths字段
列表有但 agent 不调用triggers 不匹配在 SKILL.md 里补充同义关键词
调用报 entrypoint 不存在路径大小写或权限问题ls -l确认脚本可执行
调用后立即退出脚本缺少 shebang 或 set -e 误伤手动bash scripts/run.sh看报错

我遇到最多的是 triggers 不匹配。agent 的意图识别对中文关键词支持不错,但如果你只写英文 triggers,用户用中文提问就可能匹配不上。解决办法是双语都写,并且把用户可能说的口语化表达也加进去,比如“先写测试”“测试驱动”“别直接改代码”。

5.2 环境变量丢失:agent 调用和手动调用的差异

手动调用时你可以在 shell 里 export 环境变量,但 agent 调用时环境变量由 skills CLI 注入。如果skill.json里没声明某个变量,agent 传了也读不到。我踩过一次坑:在 SKILL.md 里写了PROJECT_ROOT,但skill.json的env数组里漏了,结果脚本里$PROJECT_ROOT为空,cd 到了根目录,差点把系统文件当项目文件处理。

实操心得:在脚本开头加一段防御性检查,比如: "${TARGET_DIR:?TARGET_DIR is required}"。这样变量为空时脚本会立即报错退出,而不是带着空值继续跑。这个技巧帮我挡掉了至少两次潜在事故。

5.3 测试命令注入风险:别让 agent 随便 eval

前面run.sh里用了eval "$TEST_COMMAND",这其实有风险。如果 agent 被诱导传入恶意命令,eval 会直接执行。更安全的做法是限制 TEST_COMMAND 只能是白名单里的命令,或者用数组方式传参而不是 eval。

我的改进方案是在 SKILL.md 里明确列出允许的测试命令,然后在脚本里做前缀校验:

case "$TEST_COMMAND" in "npm test"*|"yarn test"*|"pytest"*|"go test"*) ;; *) echo "不允许的测试命令"; exit 1 ;; esac

这样即使 agent 传了奇怪的东西,也会被挡在门外。安全无小事,尤其是 skill 会被自动调用,人工 review 的机会很少。

5.4 技能冲突:两个 skill 同时被触发怎么办

当项目里 skill 多了以后,可能出现一个任务同时匹配多个 skill 的情况。比如“修复 bug 并提交”可能同时触发tdd-runner和commit-guard。skills CLI 默认按注册顺序执行,但顺序不一定符合你的预期。

解决办法是在skill.json里加priority字段,数字小的先执行。或者更彻底一点,用dependsOn声明依赖关系,让 CLI 做拓扑排序。我一般给质量类 skill 设高优先级(数字小),提交类设低优先级,确保测试先跑完再提交。

5.5 常见问题速查表

问题快速解决
skill 改了不生效运行skills reload或重启 agent
日志太少难排查在脚本里加set -x临时开启调试
跨平台路径问题用path.resolve或realpath统一
权限被拒chmod +x scripts/run.sh
JSON 解析失败用jq . skill.json验证格式
agent 反复调用同一 skill在 SKILL.md 里加“完成后输出 DONE 标记”

这些坑我基本都踩过一遍,最耗时的往往是“改了不生效”,因为 skills CLI 有缓存机制。后来我养成了一个习惯:每次改完 skill 先skills reload,再skills list确认版本号变了,才去 agent 里测试。

6. 把 agent-skills 用出复利:一些个人体会

我现在的做法是,每解决一个重复出现的开发问题,就把它沉淀成一个 skill。比如“新项目初始化检查清单”“依赖升级前的兼容性扫描”“日志脱敏规则”这些,以前靠人记,现在靠 skill 自动执行。时间一长,这套 skill 库就成了团队的实际工程规范,而且比文档更可靠,因为它是可执行的。

另外一个小技巧是给 skill 写“反例”。在 SKILL.md 里专门用一段写“什么情况下不要用这个技能”,这能显著降低 agent 误调用率。比如 tdd-runner 里我写了“纯文档修改、配置文件调整不要用本技能”,agent 就不会在改 README 的时候还去跑测试。

这个方向后续还能扩展的地方很多,比如把 skill 和 CI 流水线打通,让本地 agent 和远端 CI 用同一套技能定义;或者给 skill 加指标采集,统计每个技能的调用次数和失败率,用数据驱动优化。我目前还在折腾前者,等跑顺了再单独写一篇。

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

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

立即咨询