1. 从“agent-skills”说起为什么这个项目值得你花时间第一次看到agent-skills这个标题很多人会以为它又是一个“提示词合集”或者“工具清单”。但真正在 AI coding agent 这条线上折腾过的人会立刻意识到它解决的是一个更底层的问题如何让 AI 编程代理具备可复用、可组合、可测试的工程能力。简单来说agent-skills是一套围绕 AI coding agents 构建的技能封装体系。它把“让 AI 写代码”这件事从“每次都要重新描述需求”变成“调用一个已经定义好输入输出、有测试用例、有边界约束的技能模块”。你可以把它理解成给 AI 代理准备的“标准件库”——不是告诉它“帮我写个排序”而是给它一个名为sort-array的技能里面包含了函数签名、边界条件、测试用例和失败处理逻辑。这个项目适合三类人第一类是在日常开发中已经用上 Claude Code、Cursor、Copilot 等工具但总觉得“每次都要重新调教”的工程师第二类是想把 AI 代理接入自己团队工作流却苦于没有统一规范的技术负责人第三类是对 test-driven-development 和 skills CLI 感兴趣想看看别人怎么把 TDD 思路搬到 AI 代理身上的实践者。我最初接触这个方向是因为在一个内部工具项目里反复让 AI 改同一个模块每次都要重新解释上下文、重新定义接口、重新跑测试。后来我把这些重复劳动抽象成几个“技能文件”发现 AI 代理的产出稳定性明显提升。agent-skills这个项目本质上就是把这种个人经验系统化、标准化了。2. 核心设计思路拆解为什么是“技能”而不是“提示词”2.1 从提示词工程到技能工程的范式转移过去两年大家聊 AI 编程提得最多的是“提示词工程”。你写一段精心设计的 prompt告诉 AI 你的角色、任务、约束、输出格式。但实际用下来会发现两个问题一是提示词越写越长维护成本越来越高二是同一个提示词在不同项目、不同模型上的表现差异很大很难沉淀。agent-skills的思路是把“提示词”升级成“技能”。一个技能不是一段自然语言描述而是一个结构化的包通常包含技能元数据名称、版本、适用场景、依赖项输入输出契约参数类型、返回值结构、错误码定义实现逻辑可以是提示词模板、代码片段、工具调用链测试用例验证技能是否按预期工作的最小测试集使用示例给 AI 代理看的 few-shot 示例这样做的好处是技能可以被版本控制、可以被测试、可以被组合。你不再需要每次从零开始描述需求而是像调用函数一样调用技能。2.2 为什么 test-driven-development 在这里是关键项目热词里出现了test-driven-development这不是偶然。在 AI 代理场景下TDD 的意义比传统开发更大。原因很简单AI 代理的输出具有不确定性你需要一个自动化的“验收标准”来判断它是否做对了。传统 TDD 是“先写测试再写实现”。在agent-skills里这个顺序被进一步强化先定义技能的测试用例再定义技能的实现逻辑。测试用例不仅是验证手段更是给 AI 代理的“行为规范”。当 AI 代理看到一个技能附带 5 个测试用例时它会更倾向于生成能通过这 5 个用例的代码而不是自由发挥。我自己的做法是每个技能至少包含三类测试正常路径测试、边界条件测试、错误处理测试。比如一个parse-date技能正常路径是解析2024-01-15边界条件是解析2024-02-29闰年错误处理是解析not-a-date时返回明确错误。这三类测试写清楚AI 代理的实现基本不会跑偏。2.3 skills CLI 的定位与选型考量项目里提到的skills CLI我理解它是一个命令行工具用来管理技能的创建、测试、发布和调用。为什么是 CLI 而不是 GUI 或 Web 服务因为 AI coding agents 的工作环境通常是终端CLI 是最自然的交互方式。一个典型的 skills CLI 应该支持这些命令# 创建一个新技能 skills init my-skill # 运行技能测试 skills test my-skill # 列出所有可用技能 skills list # 调用某个技能 skills run my-skill --input {key: value} # 发布技能到本地或远程仓库 skills publish my-skill选 CLI 的另一个原因是可组合性。你可以把 skills CLI 嵌入到 CI/CD 流程里每次提交代码时自动跑一遍技能测试确保 AI 代理生成的代码没有破坏已有技能。3. 核心细节解析与实操要点3.1 技能目录结构怎么设计一个规范的技能目录我建议至少包含以下文件my-skill/ ├── skill.yaml # 技能元数据与输入输出契约 ├── prompt.md # 给 AI 代理的提示词模板 ├── implementation/ # 参考实现可选 │ └── index.js ├── tests/ # 测试用例 │ ├── normal.test.js │ ├── edge.test.js │ └── error.test.js └── examples/ # 使用示例 └── basic.mdskill.yaml是整个技能的核心它定义了技能的“接口”。我通常会这样写name: parse-date version: 1.0.0 description: 将常见日期字符串解析为 ISO 8601 格式 inputs: - name: dateString type: string required: true description: 待解析的日期字符串 outputs: - name: isoDate type: string description: ISO 8601 格式的日期 errors: - code: INVALID_FORMAT message: 无法识别的日期格式 dependencies: []这个文件的作用是让 AI 代理在调用技能前就知道“我需要传什么、会得到什么、可能出什么错”。实测下来有了这个契约文件AI 代理生成调用代码的准确率能提升不少。3.2 提示词模板的写法与避坑prompt.md是给 AI 代理看的“实现指南”。很多人写提示词喜欢堆砌形容词比如“请仔细、认真、严谨地...”。但在技能场景下这种写法效果很差。更有效的方式是用测试用例代替形容词。我通常会把prompt.md写成这样# 技能parse-date ## 目标 将输入的日期字符串转换为 ISO 8601 格式YYYY-MM-DD。 ## 输入 - dateString: 字符串可能格式包括 - 2024-01-15 - 01/15/2024 - Jan 15, 2024 - 2024年1月15日 ## 输出 - isoDate: 字符串格式为 YYYY-MM-DD ## 错误处理 - 如果无法识别格式抛出错误code 为 INVALID_FORMAT ## 测试用例 1. 输入 2024-01-15 → 输出 2024-01-15 2. 输入 01/15/2024 → 输出 2024-01-15 3. 输入 2024年1月15日 → 输出 2024-01-15 4. 输入 not-a-date → 抛出 INVALID_FORMAT 错误注意不要在提示词里写“请尽量”“最好”这类模糊词汇。AI 代理需要的是确定性指令不是建议。3.3 测试用例的编写原则测试用例是技能的“验收标准”。我写测试用例时遵循三个原则第一每个测试只验证一个行为。不要在一个测试里既验证正常解析又验证错误处理这样失败时很难定位问题。第二测试用例要覆盖 AI 容易犯错的场景。比如日期解析里AI 经常忘记处理闰年、时区、月份缩写。这些都要写成独立测试。第三测试用例要能独立运行。不要依赖外部网络或数据库否则 AI 代理在本地跑测试时会失败影响体验。一个典型的测试文件长这样// tests/normal.test.js const { parseDate } require(../implementation); test(解析 ISO 格式日期, () { expect(parseDate(2024-01-15)).toBe(2024-01-15); }); test(解析美式日期, () { expect(parseDate(01/15/2024)).toBe(2024-01-15); }); test(解析中文日期, () { expect(parseDate(2024年1月15日)).toBe(2024-01-15); });// tests/error.test.js const { parseDate } require(../implementation); test(无效日期抛出 INVALID_FORMAT, () { expect(() parseDate(not-a-date)).toThrow(INVALID_FORMAT); });3.4 技能组合与依赖管理单个技能的价值有限真正强大的是技能组合。比如你有一个fetch-user技能和一个format-address技能就可以组合成get-user-address技能。在skill.yaml里声明依赖name: get-user-address version: 1.0.0 dependencies: - fetch-user^1.0.0 - format-address^1.0.0skills CLI 在运行时会自动解析依赖确保所有依赖技能都已安装。这里有个坑依赖版本要写清楚。我见过有人写fetch-userlatest结果某天依赖更新后技能行为变了整个流程崩掉。建议用语义化版本范围比如^1.0.0。4. 实操过程与核心环节实现4.1 环境准备与 skills CLI 安装假设你已经在本地装好了 Node.js 和 npm安装 skills CLI 通常是一行命令npm install -g agent-skills/cli安装完成后验证skills --version如果输出类似1.0.0的版本号说明安装成功。这里有个常见问题全局安装时权限不足。在 Linux 或 macOS 上你可能需要加sudo但更好的做法是配置 npm 的全局目录到用户目录下避免权限问题。mkdir -p ~/.npm-global npm config set prefix ~/.npm-global export PATH~/.npm-global/bin:$PATH把最后一行加到你的.bashrc或.zshrc里以后就不需要sudo了。4.2 创建第一个技能从零到跑通测试我们用parse-date作为例子走一遍完整流程。第一步初始化技能skills init parse-date cd parse-date这会生成前面提到的目录结构。你会看到skill.yaml、prompt.md、tests/等文件已经就位。第二步编辑skill.yaml填入输入输出契约。这一步很关键因为 AI 代理后续生成调用代码时会读取这个文件。第三步编辑prompt.md把测试用例写进去。记住测试用例就是最好的提示词。第四步编写测试文件。我建议先写测试再写实现。这样你能确保测试本身是合理的。第五步运行测试skills test parse-date如果实现还没写测试会失败。这时候你可以让 AI 代理根据prompt.md和测试文件生成实现。我通常会把prompt.md和测试文件一起丢给 Claude Code让它生成implementation/index.js。第六步再次运行测试直到全部通过。skills test parse-date # 输出5 passed, 0 failed4.3 把技能接入 Claude Code 工作流Claude Code 是目前比较流行的 AI coding agent 之一。把agent-skills接入 Claude Code 的方式通常是在项目根目录放一个.claude/skills/文件夹里面存放所有技能。然后在 Claude Code 的配置里指定技能目录{ skills: { directory: .claude/skills, autoLoad: true } }这样当你在 Claude Code 里描述需求时它会自动扫描可用技能并在合适的时候调用。比如你说“帮我把用户输入的日期转成标准格式”Claude Code 会识别到parse-date技能并按照技能契约生成调用代码。提示技能目录不要放太多技能否则 AI 代理扫描和选择的时间会变长。我一般控制在 20 个以内超过就按领域分文件夹。4.4 技能版本管理与团队协作当团队多人维护技能时版本管理就很重要。我建议每个技能独立一个 Git 仓库或者放在 monorepo 的skills/目录下。发布新版本时更新skill.yaml里的version字段然后打 taggit tag parse-date1.1.0 git push origin parse-date1.1.0skills CLI 支持从 Git 仓库安装技能skills install githttps://your-repo.git#parse-date1.1.0这样团队成员就能用同一版本的技能避免“我本地能跑你本地报错”的问题。5. 常见问题与排查技巧实录5.1 技能测试通过但 AI 代理调用失败这是最常见的问题。测试通过说明实现逻辑没问题但 AI 代理调用失败通常是契约不清晰导致的。排查步骤第一检查skill.yaml里的inputs和outputs是否和实际实现一致。我见过有人改了实现但忘了改契约AI 代理按旧契约调用就失败了。第二检查prompt.md里的示例是否和测试用例一致。如果示例里的输入格式和测试用例不同AI 代理会困惑。第三用skills run手动调用一次看看实际输出是什么skills run parse-date --input {dateString: 2024-01-15}如果手动调用成功但 AI 代理失败那问题多半在提示词或契约描述上。5.2 技能依赖冲突怎么处理当两个技能依赖同一个库的不同版本时会出现冲突。比如skill-a依赖lodash4.17.0skill-b依赖lodash3.10.0。解决方案有两种一是升级技能让它们依赖同一版本二是用 skills CLI 的隔离模式每个技能在独立的node_modules里运行。skills run skill-a --isolated隔离模式会为每个技能创建独立的依赖环境避免冲突。代价是启动速度稍慢但稳定性更好。5.3 AI 代理不调用技能怎么办有时候你明明定义了技能但 AI 代理还是自己写代码不调用技能。原因通常是技能描述不够明确。在skill.yaml的description字段里要写清楚“什么时候用这个技能”。比如不要写“解析日期”而要写“当需要将用户输入的日期字符串转换为 ISO 8601 格式时使用”。另外可以在项目的CLAUDE.md或类似配置文件里显式提醒 AI 代理优先使用技能## 技能使用规范 - 优先使用 .claude/skills/ 下的技能而不是自己实现 - 调用技能前先读取对应的 skill.yaml 了解契约 - 如果技能不存在再考虑自己实现5.4 常见问题速查表问题现象可能原因排查方法解决方案测试通过但调用失败契约与实现不一致对比skill.yaml和实现代码更新契约或实现依赖冲突多技能依赖不同版本查看skills list --deps使用隔离模式或统一版本AI 不调用技能技能描述模糊检查description字段写清楚使用场景测试运行慢测试依赖外部资源检查测试文件改为纯本地测试技能版本混乱未打 tag 或版本号未更新查看skill.yaml版本每次发布更新版本号注意不要为了省事跳过测试。我见过有人直接改实现不跑测试结果 AI 代理调用时行为变了排查了半天才发现是技能本身的问题。6. 技能设计的进阶思路与个人体会6.1 从“单技能”到“技能链”单个技能解决单个问题但实际开发中往往是多个问题串联。比如“用户注册”这个流程可能涉及validate-email、check-password-strength、create-user、send-welcome-email四个技能。agent-skills支持技能链的方式是在skill.yaml里定义pipelinename: user-registration version: 1.0.0 pipeline: - skill: validate-email input: ${email} - skill: check-password-strength input: ${password} - skill: create-user input: email: ${email} password: ${password} - skill: send-welcome-email input: ${email}这样 AI 代理只需要调用user-registration一个技能内部会自动按顺序执行。实测下来技能链能显著减少 AI 代理的决策负担提升整体流程的稳定性。6.2 技能的可观测性怎么做技能跑在后台出问题了怎么排查我建议每个技能都输出结构化日志。在skill.yaml里加一个logging配置logging: level: info format: json fields: - skillName - input - output - duration - error这样每次技能调用都会生成一条 JSON 日志方便后续分析。如果某个技能频繁失败你可以快速定位是输入问题还是实现问题。6.3 我个人在实际操作中的体会折腾agent-skills这段时间最大的体会是技能的质量取决于测试的质量而不是提示词的质量。我早期花了很多时间优化提示词效果一般。后来把精力转到写测试用例上发现 AI 代理的产出稳定性提升非常明显。另一个体会是技能不要贪多。我一开始想把所有常用操作都封装成技能结果技能目录膨胀到 50 多个AI 代理反而不知道该用哪个。后来精简到 15 个核心技能每个技能都有明确的边界和使用场景整体效率反而更高。最后分享一个小技巧定期用skills test --all跑一遍全量测试。我设置了一个每周一的定时任务自动跑所有技能测试。这样能及时发现依赖更新或环境变化导致的技能失效避免在关键时刻掉链子。这个方向后续还可以扩展的地方很多比如技能的市场化分发、技能之间的自动组合推荐、基于使用数据的技能优化等。如果你也在折腾 AI coding agents不妨从写第一个技能开始慢慢积累自己的技能库。
阅读完成 · 觉得有帮助?