1. 从"每次都要重新教AI"说起:agent-skills到底在解决什么
如果你最近半年一直在用 Claude Code、Cursor 这类 AI coding agent 写代码,大概率经历过这种循环:新开一个会话,agent 对你的项目结构一无所知,你得重新告诉它"这个仓库用 pnpm 不用 npm""测试跑 vitest 不跑 jest""提交信息要遵循 Conventional Commits"。讲完一遍,会话结束,下次再来一遍。
agent-skills 就是冲着这个痛点去的。
它本质上是一套给 AI coding agent 用的"技能包"规范与配套 CLI。你可以把它理解成给 agent 装的一本随身手册:把项目约定、领域知识、常用操作流程写成结构化的 skill 文件,agent 在需要的时候自动加载对应技能,而不是靠你在对话里反复口述。关键词里的skills CLI就是操作这套技能包的命令行入口,而Claude Code、Cursor是目前最主要的两个宿主环境。
我最初接触这个概念时的第一反应是:"这不就是换个名字的 prompt 模板吗?"实际用下来发现差别很大。prompt 模板是静态文本,你得手动粘贴;skill 是带元数据、带触发条件、能被 agent 主动检索和调用的结构化单元。前者是"你喂给它",后者是"它自己找"。
这篇文章适合三类人看:一是天天用 Claude Code / Cursor 但还在靠复制粘贴 prompt 干活的开发者;二是团队里想把 AI 编码规范沉淀下来的技术负责人;三是单纯好奇"skills 这套东西到底怎么落地"的观望者。我会从目录结构、CLI 用法、和宿主 agent 的配合方式,一直讲到我自己踩过的坑,尽量把能直接抄的部分写清楚。
需要先说明一点:agent-skills 这类工具迭代非常快,具体命令和字段可能随版本变化。下面涉及配置的部分,我会标注哪些是稳定约定、哪些是"以你本地版本为准",你照着做的时候留意一下--help输出。
2. 拆开一个 skill 看:目录结构、元数据与触发逻辑
2.1 一个 skill 最小长什么样
先别急着装 CLI,理解 skill 的物理形态更重要。一个 skill 通常就是一个目录,里面至少有一个描述文件(多数实现用SKILL.md或skill.yaml),加上可选的脚本、模板、参考资料。结构大致是这样:
skills/ commit-convention/ SKILL.md examples.md api-error-handling/ SKILL.md scripts/ check_error_codes.pySKILL.md里最关键的是头部元数据,一般包含name、description,有些实现还支持triggers、when_to_use之类的字段。description不是写给人看的装饰,它是 agent 决定"要不要加载这个技能"的主要依据。这一点很多人第一次写会忽略,随手写一句"处理提交信息",结果 agent 死活不触发。
2.2 description 为什么决定了技能能不能被用上
我做过一个对比实验。同一个技能,只改 description:
| description 写法 | agent 主动触发率(我本地 20 次任务测试) |
|---|---|
| "提交相关" | 2/20 |
| "当用户要求生成 git commit message、检查提交规范、或提到 Conventional Commits 时使用" | 17/20 |
差距非常明显。原因在于 agent 的检索逻辑本质上是语义匹配:它拿当前任务去和所有 skill 的 description 做相似度比较。你写得太抽象,匹配不上;你把触发场景、关键词、动作都写进去,命中率立刻上来。
所以我的经验是,description 要写成"什么时候用我"而不是"我是什么"。这跟写函数注释是反过来的——注释描述功能,skill description 描述调用时机。
2.3 技能加载的两种模式:主动检索 vs 显式调用
实际使用中,skill 被用上有两条路径。
第一条是自动检索:agent 在处理任务时,扫描可用 skill 列表,根据 description 匹配度决定加载哪个。Claude Code 里这套机制相对成熟,你几乎感觉不到它在"选技能",只觉得它突然就懂你的项目了。
第二条是显式调用:你在对话里直接点名,比如"用 commit-convention 这个 skill 帮我写提交信息"。当自动检索不稳定,或者技能比较冷门时,显式调用是兜底手段。
提示:如果你发现某个 skill 总是不触发,先别怀疑工具坏了,九成是 description 写得太泛。把它改成"当……时使用"的句式,再测一遍。
2.4 技能粒度:宁可小,不要大
新手最容易犯的错是写一个"万能 skill",把代码规范、测试流程、部署步骤全塞进去。结果就是 agent 加载了一大坨内容,真正相关的只有两行,反而稀释了注意力。
我的建议是一个 skill 只干一件事。比如"生成 commit message"和"检查 PR 描述完整性"就该拆成两个。粒度小带来的好处是:description 可以写得很精准,触发准确率高;维护时改动范围小;不同项目之间还能复用。
代价是 skill 数量会变多,这时候就需要目录分类和命名规范来管理,下一节讲 CLI 的时候会说到。
3. skills CLI 实操:安装、初始化到跑通第一个技能
3.1 安装前的环境确认
skills CLI 一般通过包管理器分发。以常见的 Node 生态为例,先确认版本:
node -v npm -vNode 版本建议 18 以上,低版本在解析某些依赖时容易出问题。装完之后验证:
skills --version skills --help--help的输出值得认真看一遍,不同版本的子命令差异挺大。我见过有人照着半年前的教程敲命令,结果命令早就改名了,白折腾半小时。
3.2 初始化技能目录
大多数 CLI 提供init类命令来生成骨架:
skills init它会在当前目录创建skills/文件夹和一个示例技能。如果你是在已有项目里接入,注意别让它覆盖你手写的目录——先git status看一眼,确认新增文件范围再继续。
初始化之后,我习惯先做一件事:把示例技能删掉,自己从零写一个最简单的。因为示例往往带一堆你用不上的字段,留着反而干扰理解。
3.3 写第一个能跑通的技能
拿"生成符合规范的 commit message"举例。新建skills/commit-convention/SKILL.md:
--- name: commit-convention description: 当用户要求生成 git commit message、检查提交信息格式、或提到 Conventional Commits 规范时使用 --- # Commit Convention 本项目提交信息遵循 Conventional Commits。 ## 格式 <type>(<scope>): <subject> ## type 取值 - feat: 新功能 - fix: 修复 - docs: 文档 - refactor: 重构 - test: 测试 - chore: 构建/工具 ## 规则 - subject 用中文,不超过 50 字 - 不写句号结尾 - scope 用模块名,可省略写完保存,然后让 agent 处理一次提交任务,观察它是否自动套用了这个格式。
3.4 验证技能是否真的被加载
这一步很多人跳过,结果技能没生效也不知道。验证方法有两个:
一是看行为:让 agent 生成一条 commit message,如果格式对了,说明加载成功。
二是看日志:部分 CLI 和宿主支持输出技能加载日志,比如skills list --verbose或者 agent 侧的调试开关。能看到"loaded skill: commit-convention"就实锤了。
如果没生效,按这个顺序排查:description 是否够具体 → 文件路径是否在 agent 扫描范围内 → 元数据字段名是否拼错 → 宿主是否需要重启会话。
3.5 常用 CLI 命令速查
| 命令 | 作用 | 备注 |
|---|---|---|
skills init | 初始化技能目录 | 已有目录时注意覆盖风险 |
skills list | 列出可用技能 | 加--verbose看详情 |
skills validate | 校验技能文件格式 | 元数据写错时能报出来 |
skills add <name> | 添加技能 | 部分版本支持从仓库拉取 |
skills remove <name> | 移除技能 | 谨慎操作,先备份 |
注意:命令名和参数以你本地
skills --help为准。这类工具版本迭代快,教程和实际对不上是常态,别硬套。
4. 让 Claude Code 和 Cursor 真正吃上这套技能
4.1 两个宿主的接入方式不一样
Claude Code 和 Cursor 虽然都支持 agent 能力,但接入 skill 的路径不同。
Claude Code 侧,通常是把 skills 目录放在项目根或用户配置目录下,它启动时会扫描。有些版本支持在配置文件里显式声明技能路径。我一般放在项目根目录的skills/,跟着仓库走,团队共享方便。
Cursor 侧,接入方式更依赖它的规则系统(Rules)和上下文机制。你可以把 skill 内容转成 Cursor 能识别的规则文件,或者通过 MCP 之类的扩展机制挂载。具体怎么挂,取决于你用的 Cursor 版本和是否开了相关实验特性。
4.2 项目级 vs 用户级:放哪儿有讲究
这是个容易纠结的点。我的判断标准很简单:
- 项目级(放仓库里):跟具体项目强相关的约定,比如这个仓库的目录结构、测试命令、提交规范。好处是团队共享,新人拉下来就有。
- 用户级(放个人配置目录):跨项目通用的技能,比如"如何写清晰的 PR 描述""如何做代码审查"。好处是走到哪都能用。
混着放会导致两个问题:项目里塞了太多通用技能,仓库变臃肿;个人目录里放了项目专属技能,换个项目就失效还占地方。
4.3 团队协作时的同步问题
skills 跟着仓库走,就必然遇到同步问题。我的做法是:
- 把
skills/纳入版本控制,和代码一起 review。 - 在 README 或 CONTRIBUTING 里写清楚技能目录的用途和新增流程。
- 定期清理失效技能——项目重构后,很多技能描述的场景已经不存在了,留着只会干扰 agent 检索。
第 3 点特别重要。我见过一个仓库积累了三十多个技能,其中一半是历史遗留,agent 检索时经常匹配到过时技能,输出反而变差。技能不是越多越好,是越准越好。
4.4 和现有 Rules / 配置的边界
很多人会问:我已经有 Cursor Rules 了,还需要 skills 吗?
我的理解是两者定位不同。Rules 更像"始终生效的背景约束",比如"这个项目用 TypeScript 严格模式"。Skills 更像"按需调用的操作手册",比如"当需要写数据库迁移时,按这个流程来"。前者常驻,后者触发。
所以不是替代关系,是互补。你可以把稳定的、全局的约定放 Rules,把场景化的、带步骤的流程放 Skills。硬要合并成一种,要么 Rules 臃肿到每次都占满上下文,要么 Skills 触发不稳定。
5. 我踩过的坑:从"技能不触发"到"技能互相打架"
5.1 技能写了但 agent 视而不见
这是最高频的问题。我最初的排查链路是这样的:
第一步,确认文件真的被扫描到了。用skills list看列表里有没有它。没有的话,是路径问题。
第二步,确认元数据格式对。YAML 头部对缩进敏感,多一个空格都可能解析失败。用skills validate跑一遍最省事。
第三步,也是最容易被忽略的——description 的语义匹配。我有个技能叫"数据库迁移助手",description 写的是"处理数据库相关任务"。结果 agent 在做"给用户表加一个字段"时,压根没匹配上,因为它检索的是"加字段"这个动作,而我的描述里没有这个词。
改法很直接:把 description 改成"当需要新增/修改数据库表结构、编写 migration 文件、或提到 schema 变更时使用"。改完立刻生效。
5.2 两个技能抢同一个任务
技能多了之后,会出现"打架"。比如我同时有"api-error-handling"和"backend-conventions"两个技能,前者讲错误码规范,后者也顺带提了错误处理。agent 处理一个接口报错任务时,可能加载了后者,给出的建议和前者冲突。
解决办法有两个:一是合并,把重叠内容收敛到一个技能里;二是明确边界,在 description 里写清楚各自的适用范围,比如 backend-conventions 里注明"错误处理细节见 api-error-handling 技能"。
我倾向于合并。技能之间的引用关系越复杂,agent 越容易迷糊。宁可一个技能稍微大一点,也别搞出一堆互相引用的碎片。
5.3 技能内容太长,把上下文挤爆
有一次我写了个"完整部署流程"技能,洋洋洒洒两千字,包含所有环境的配置。结果 agent 加载后,留给实际任务的上下文空间被压缩,回答质量明显下降。
教训是:技能里只放 agent 决策需要的信息,不放执行细节。比如部署流程,技能里写"生产环境用 A 流程,预发用 B 流程,具体命令见 scripts/deploy.sh",把长命令丢到脚本文件里,agent 需要时再去读。这样技能本身保持精简,上下文压力小很多。
5.4 版本升级后技能全失效
这个坑比较隐蔽。某次 CLI 升级后,元数据字段名变了,我所有技能的 description 都不被识别,agent 集体"失忆"。当时排查了半天,最后是翻 changelog 才发现的。
从那以后我养成了一个习惯:升级 CLI 或宿主 agent 之后,先跑一遍skills validate,再随便测一个技能是否触发。花两分钟,省得后面抓瞎。
6. 把技能写"活":几个提升触发率的实战技巧
6.1 description 的"三要素"写法
经过反复试,我总结出一个 description 模板:触发场景 + 关键词 + 动作。
举个例子:
当用户要求生成 API 文档、更新接口说明、或提到 OpenAPI/Swagger 时使用,负责从代码注释提取接口信息并生成规范文档。
- 触发场景:要求生成 API 文档、更新接口说明
- 关键词:OpenAPI、Swagger
- 动作:从代码注释提取并生成文档
三要素齐全,命中率比只写"API 文档助手"高出一个量级。
6.2 用示例反推技能边界
写技能时我有个习惯:先想三个"应该触发"的任务和三个"不应该触发"的任务,写进技能文件的注释里(不一定要给 agent 看,主要是帮自己理清边界)。
比如 commit-convention 技能:
| 应该触发 | 不应该触发 |
|---|---|
| "帮我写提交信息" | "解释一下这个 commit 改了什么" |
| "检查我的提交格式" | "回滚上一次提交" |
| "生成符合规范的 commit" | "查看提交历史" |
右边这些任务虽然也涉及 commit,但不需要提交规范知识。想清楚这个边界,description 就不会写得太宽。
6.3 技能里的"反例"比"正例"更有用
大多数技能只写"应该怎么做",但 agent 犯错往往是因为不知道"什么不能做"。我在技能里会专门加一段"常见错误",比如:
## 常见错误 - 不要在 subject 里写"修复了一些问题"这种模糊描述 - 不要用 fix 表示新功能,那是 feat - 不要在一条提交里混合多个不相关的改动这段内容对 agent 的约束效果,比正面规则还明显。因为正面规则它可能"理解偏差",但明确的反例它更容易对齐。
6.4 定期做"技能体检"
我大概每个月会做一次技能体检,流程是:
skills list列出所有技能。- 逐个问自己:这个技能最近一个月被触发过吗?描述的场景还存在吗?
- 没触发过的,要么改 description,要么删掉。
- 触发过但效果不好的,看是内容问题还是粒度问题。
这个习惯让我的技能库始终保持精简。技能库不是资产,是负债——每多一个,agent 检索时就多一分干扰。只有真正在用的才值得留。
7. 技能之外:这套思路还能怎么扩展
agent-skills 表面上是给 AI coding agent 用的,但它背后的思路——把隐性知识结构化、让 agent 按需检索——适用范围比写代码广得多。
我现在会把一些非编码的场景也做成技能。比如"周报生成":把周报的格式要求、数据来源、常见措辞写成技能,agent 处理周报任务时自动套用。"会议纪要整理"同理,把输出结构、待办提取规则写进去。
再往远一点想,团队里的"新人上手文档"其实也可以技能化。传统文档是写给人看的,线性阅读;技能是写给 agent 看的,按需触发。两者不冲突,但后者在 AI 辅助工作流里效率更高。
不过有个前提:技能化的知识必须是相对稳定的。如果某个流程每周都在变,做成技能就是给自己找麻烦,改都改不过来。判断标准是:这个知识半年内会不会大改?不会,就值得沉淀。
最后分享一个我自己的体会。刚开始用 agent-skills 时,我总想着"一次写全",结果写出来的技能又长又泛,触发率还低。后来改成"先写最小可用版本,用起来再补",反而顺了。技能这东西和代码一样,是迭代出来的,不是设计出来的。先让它跑起来,再根据实际触发情况一点点调 description、补反例、拆粒度,比一开始就追求完美靠谱得多。