☰
agent-skills实战:为AI编码代理构建TDD技能包
2026/10/7 1:44:03 网站建设 项目流程

1. 从“agent-skills”说起:为什么我们需要给AI编码代理装上技能包

第一次看到agent-skills这个项目名,我脑子里蹦出来的不是某个具体工具,而是一类正在快速成型的东西——给AI编码代理(AI coding agents)定义、管理和分发“技能”的基础设施。你可以把它理解成给一个刚入职的实习生配一本《岗位操作手册》,手册里写清楚:遇到什么场景、该调用什么工具、按什么步骤执行、验收标准是什么。agent-skills干的就是这件事,只不过服务对象是 Claude Code 这类能直接读写文件、执行终端命令的编码代理。

为什么这个东西现在变得重要?因为过去一年我用 Claude Code 写代码、跑测试、改配置,踩过太多“它明明有能力,但就是不知道该怎么干”的坑。比如让它写一个 Python 脚本,它会写;但让它按照团队既定的测试驱动开发(TDD)流程——先写失败测试、再写最小实现、最后重构——它经常跳步。不是模型不行,是它缺少一份明确的、可复用的“技能定义”。agent-skills这类项目要解决的,就是把这个“技能定义”标准化、CLI 化、可版本化。

这篇文章适合三类人看:第一类是把 Claude Code 当日常主力工具、想让它更听话的开发者;第二类是团队里负责搭建 AI 编码工作流、想让多个代理行为一致的技术负责人;第三类是对 skills CLI、TDD 与代理结合感兴趣、想自己造轮子的折腾党。我会从设计思路、核心机制、实操落地、问题排查四个层面,把agent-skills这个方向讲透,所有步骤都可以直接抄作业。

2. 核心设计思路:技能为什么要独立于代理存在

2.1 代理能力与技能知识的分离

Claude Code 这类工具的核心能力是“通用”的:读文件、写文件、跑命令、调 API。但“通用能力”不等于“专业产出”。一个能跑pytest的代理,不代表它知道你们团队的测试命名规范、fixture 组织方式、mock 边界在哪里。这些知识如果每次都塞进 prompt,会带来三个问题:token 浪费、上下文漂移、无法复用。

agent-skills的设计哲学就是把**能力(capability)和技能(skill)**拆开。能力由代理本身提供,技能由外部文件定义。技能文件通常包含:触发条件(什么时候用这个技能)、执行步骤(按什么顺序做什么)、工具约束(允许调用哪些命令)、验收标准(怎么算完成)。这种分离带来的直接好处是,同一个 Claude Code 实例,加载不同的技能包,就能在“写前端组件”和“写数据库迁移脚本”两种模式间切换,行为差异由技能文件控制,而不是靠临时 prompt 祈祷。

我实测下来,这种分离对 TDD 场景尤其关键。TDD 的纪律性很强:红-绿-重构,顺序不能乱。如果只靠 prompt 说“请用 TDD”,代理大概率会先写实现再补测试。但如果你有一个tdd-workflow技能文件,里面明确写了“第一步必须创建测试文件并运行至失败,第二步才能创建实现文件”,代理的执行路径就会被约束住。

2.2 为什么选择 CLI 作为分发形态

skills CLI这个关键词出现得很频繁,说明社区里很多人关心“怎么安装、怎么管理技能”。选择 CLI 而不是纯配置文件,我认为有几个务实考量。第一,技能需要版本管理,CLI 天然适合做install、update、list、remove这类操作。第二,技能可能来自不同来源(官方、团队内部、社区),CLI 可以统一拉取和校验。第三,CLI 能跟现有的开发工作流集成,比如在 CI 里检查技能版本是否最新。

对比一下几种方案:纯 prompt 模板库,优点是零依赖,缺点是没法版本化、没法校验、容易和代码脱节;IDE 插件内置技能,优点是集成度高,缺点是绑定特定编辑器,换工具就废;CLI 管理技能,优点是工具无关、可脚本化、可审计,缺点是多了一层安装步骤。对于认真把 AI 编码代理当生产力工具的团队,CLI 方案的长期收益明显更高。

2.3 技能文件的粒度控制

技能粒度是个容易被忽视但很致命的设计点。粒度太粗,比如一个“后端开发”技能包,里面塞了几百条规则,代理加载后上下文爆炸,执行时反而抓不住重点。粒度太细,比如“创建一个 Flask 路由”单独一个技能,那技能数量会失控,管理成本超过收益。

我的经验是,一个技能对应一个可独立验收的工作流。比如“TDD 工作流”是一个技能,“数据库迁移”是一个技能,“API 契约测试”是一个技能。每个技能控制在 50 到 200 行描述,包含 3 到 7 个明确步骤。这样代理加载时上下文可控,人类审查时也容易判断对错。agent-skills如果要做得好,必须在文档里把粒度建议写清楚,否则用户会陷入“技能爆炸”或“技能臃肿”两个极端。

3. 核心细节解析:一个技能包里到底该写什么

3.1 触发条件与作用域声明

技能文件的第一部分必须是触发条件。我见过太多技能包失败在“代理不知道什么时候该用它”。触发条件要写清楚三件事:任务类型(比如“当用户要求新增功能时”)、文件范围(比如“当操作目录包含tests/时”)、前置状态(比如“当项目已配置 pytest 时”)。

举个例子,一个 TDD 技能的触发条件可以这样写:

trigger: task_type: feature_implementation file_scope: - "src/**" - "tests/**" preconditions: - "pytest.ini exists OR pyproject.toml contains [tool.pytest]" exclude: - "hotfix/*"

这种声明式写法的好处是,代理在决定是否加载技能时,有明确的判断依据,而不是靠语义模糊的“相关时使用”。agent-skills如果支持这种结构化触发条件,就能大幅降低误触发率。

3.2 执行步骤的原子化拆解

执行步骤是技能的核心。这里的关键词是原子化:每一步都应该是可独立验证的。不要写“实现功能并测试”,要写“1. 创建测试文件,包含至少一个失败用例;2. 运行测试命令,确认失败;3. 创建实现文件,写最小可通过代码;4. 再次运行测试,确认通过;5. 重构实现,保持测试通过”。

为什么强调原子化?因为代理在执行长步骤时容易“偷懒”或“跳步”。原子化步骤配合明确的验证命令,能让代理在每一步后都得到反馈,及时纠偏。我在实际项目里把 TDD 技能拆成 7 个原子步骤后,代理跳步的概率从大概三成降到了不到一成。

每个步骤还要标注允许的工具。比如“运行测试”步骤允许bash: pytest,“创建文件”步骤允许write_file。这样能防止代理在测试步骤里顺手改了实现代码,破坏 TDD 纪律。

3.3 验收标准与失败回退

验收标准是很多技能包缺失的部分。代理执行完技能后,怎么判断“做对了”?不能只靠“测试通过”,因为测试可能被代理改过。验收标准应该包含:测试未被修改(通过 git diff 检查)、覆盖率未下降、无新增 lint 错误。

失败回退同样重要。如果代理在第三步发现测试无法通过,它应该回退到哪?是重新写测试,还是报告阻塞?技能文件里要写清楚回退策略。我的做法是定义三个回退级别:retry_step(重试当前步骤)、rollback_to(回退到指定步骤)、abort_with_report(终止并生成报告)。没有回退策略的技能,代理遇到错误时容易陷入死循环或胡乱修改。

注意:验收标准里的检查命令要尽量用只读操作,避免代理在验收阶段意外修改文件。比如用git diff --stat而不是git checkout。

4. 实操落地:从零搭建一个 TDD 技能并接入 Claude Code

4.1 环境准备与 skills CLI 安装

假设你在 Ubuntu 或 macOS 上,已经装好了 Node.js 18+ 和 Claude Code。第一步是安装 skills CLI。根据社区常见实践,这类 CLI 通常通过 npm 分发:

npm install -g @agent-skills/cli

安装后验证:

skills --version skills list

如果skills list报错说找不到配置目录,手动创建一下:

mkdir -p ~/.agent-skills/skills

这里有个坑:不同版本的 CLI 可能默认目录不同,有的用~/.config/agent-skills,有的用~/.agent-skills。装完后先跑skills config看看实际路径,别急着往下走。

4.2 编写第一个 TDD 技能文件

在~/.agent-skills/skills/tdd-workflow/下创建skill.yaml:

name: tdd-workflow version: 1.0.0 description: 强制红-绿-重构循环的功能实现技能 trigger: task_type: feature_implementation file_scope: ["src/**", "tests/**"] preconditions: ["pytest available"] steps: - id: create_test action: write_file target: "tests/test_{{feature}}.py" content_template: | def test_{{feature}}_basic(): assert False, "not implemented" verify: "pytest tests/test_{{feature}}.py -x" expect: failure - id: run_failing_test action: bash command: "pytest tests/test_{{feature}}.py -x" expect: failure - id: create_impl action: write_file target: "src/{{feature}}.py" content_template: | def {{feature}}(): pass verify: "pytest tests/test_{{feature}}.py -x" expect: success - id: refactor action: bash command: "pytest tests/ -x --cov=src" expect: success acceptance: - "git diff --name-only tests/ | wc -l == 1" - "coverage >= previous_coverage" fallback: on_step_failure: retry_step max_retries: 2 on_max_retries: abort_with_report

这个文件里,{{feature}}是占位符,实际执行时由代理根据任务填充。expect: failure和expect: success是验收断言,代理必须检查命令退出码是否符合预期。

4.3 在 Claude Code 中加载技能

Claude Code 加载外部技能的方式,根据社区讨论,通常有两种:一种是通过配置文件声明技能目录,另一种是在会话中显式调用。以配置文件方式为例,在项目根目录创建.claude/skills.yaml:

skill_dirs: - ~/.agent-skills/skills - ./.claude/skills active_skills: - tdd-workflow

然后在 Claude Code 会话里,当你提出“实现一个用户注册功能”时,代理会先匹配触发条件,发现task_type是feature_implementation,file_scope匹配src/和tests/,于是加载tdd-workflow技能,按步骤执行。

我实测时发现一个细节:Claude Code 对技能文件的读取有缓存,修改技能后需要重启会话或执行/reload-skills(如果支持)。别改完技能发现没生效就以为写错了,先试试重载。

4.4 验证技能是否真正生效

验证方法很直接:让代理做一个简单功能,然后检查 git 历史。如果技能生效,你应该看到提交顺序是“先测试文件,后实现文件”,而不是反过来。还可以检查代理的终端输出,看它是否执行了pytest并检查了失败。

git log --oneline --name-only -5

如果看到tests/test_xxx.py出现在src/xxx.py之前,说明 TDD 纪律被遵守了。如果顺序反了,回去检查触发条件里的file_scope是否写对,以及代理是否真的加载了技能。

提示:第一次跑建议用一个玩具项目,别直接在主力仓库上试。技能文件里的write_file如果路径模板写错,可能覆盖已有文件。

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

5.1 技能不触发或误触发

最常见的问题是技能该触发时不触发。排查顺序:先看task_type是否匹配,代理对任务类型的判断可能和你的预期不同;再看file_scope,如果任务涉及的文件不在声明范围内,技能会被跳过;最后看preconditions,比如pytest available这个条件,如果代理检测不到 pytest,技能就不会加载。

误触发则相反,技能在不该用的时候被加载了。比如你在改一个紧急 bug,代理却启动了完整 TDD 流程。解决办法是在触发条件里加exclude,把hotfix/*或bugfix/*分支排除掉。

现象可能原因排查动作
技能完全不触发task_type 不匹配打印代理的任务分类结果
技能偶尔触发file_scope 太窄扩大 glob 范围
技能误触发缺少 exclude添加分支或路径排除
技能加载但跳步步骤 verify 缺失给每步加验证命令

5.2 代理在技能执行中“偷懒”

代理偷懒的表现包括:跳过失败测试直接写实现、重构步骤直接省略、验收检查不执行。根因通常是步骤的verify字段不够强制。我的经验是,每个步骤的verify必须是一个会返回非零退出码的命令,代理看到非零退出码才会认为步骤失败。

另一个技巧是在技能文件里加strict_mode: true,强制代理在每步后输出验证结果。如果 CLI 不支持这个字段,可以在步骤描述里写“必须输出命令退出码”。

5.3 技能版本冲突与依赖管理

当你有多个技能包,且它们依赖不同版本的公共库时,会出现冲突。比如tdd-workflow依赖pytest>=7.0,而另一个技能依赖pytest<7.0。agent-skills如果要做依赖管理,应该在技能文件里声明dependencies,CLI 安装时做版本求解。

目前社区实践里,比较稳妥的做法是每个技能包自带虚拟环境配置,或者用容器隔离。我在团队里是用uv管理 Python 依赖,技能文件里写uv run pytest,这样每个技能的执行环境相对独立。

5.4 与 Claude Code 版本升级的兼容性

Claude Code 更新频繁,技能文件的 schema 可能随版本变化。我踩过的坑是:升级 Claude Code 后,旧的skill.yaml里某个字段被重命名了,导致技能静默失效。建议在技能文件里加min_agent_version字段,CLI 加载时检查版本,不匹配就报错而不是静默跳过。

min_agent_version: "1.2.0"

另外,每次升级 Claude Code 后,跑一遍skills validate(如果 CLI 提供)来检查所有技能文件的兼容性。没有这个命令的话,就手动跑一个最小任务验证。

6. 技能生态的扩展方向与个人实践体会

agent-skills这个方向真正有意思的地方,是它可能催生一个可组合的技能生态。想象一下:你有一个tdd-workflow技能负责测试纪律,一个api-contract技能负责接口契约,一个migration-safety技能负责数据库变更安全。代理在执行一个“新增用户接口”任务时,可以同时加载这三个技能,按优先级协调执行。这种组合能力,比单个大而全的技能包灵活得多。

我在实际项目里尝试过把技能按“纪律型”和“知识型”分类。纪律型技能(如 TDD、代码审查)约束代理的行为顺序,知识型技能(如“本项目的错误码规范”)提供领域知识。两类技能分开管理,纪律型技能全团队统一,知识型技能按项目覆盖。这样既保证了流程一致,又允许项目差异。

最后分享一个我踩过的小坑:技能文件里的示例代码不要写得太具体。我一开始在 TDD 技能里写了一个完整的 Flask 路由示例,结果代理在所有项目里都模仿那个示例,连 Django 项目也写 Flask 风格。后来把示例改成伪代码占位符,代理的适应性明显好了。技能是给代理的“操作手册”,不是“代码模板”,这个边界要分清。

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

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

立即咨询