
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 串联。比如“提交前检查”可以拆成三个 skilllint-check、test-run、commit-guard。agent 可以按需组合也可以单独调用。这样每个 skill 的 SKILL.md 都很短agent 理解成本低出错也容易定位。而且拆分后你可以在不同项目里复用其中一部分不用整包搬运。3.4 版本管理skill 也要打 tagskill 是代码资产必须进版本控制。我建议每个 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/binUbuntu 上可能是/usr/bin。我见过不少人在这一步卡住其实加一行 export 就解决了。接下来初始化一个 skill 工作区mkdir -p ~/agent-skills-workspace cd ~/agent-skills-workspace skills initskills 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-runneragent 自动调用则依赖 triggers 匹配。我建议先手动跑通再交给 agent。手动跑的时候可以显式传环境变量TARGET_DIR/path/to/project TEST_COMMANDnpm 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 -- --watchfalse只跑一次。在 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.shJSON 解析失败用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 加指标采集统计每个技能的调用次数和失败率用数据驱动优化。我目前还在折腾前者等跑顺了再单独写一篇。