1. 从"装完就吃灰"说起:agent-skills 到底解决了什么问题
如果你最近半年在折腾 AI coding agents,大概率经历过这个循环:兴冲冲装好 Claude Code 或者 Cursor,跑通第一个 demo,觉得"这玩意儿真神",然后一周之后发现——它还是只会帮你补全几行代码、改改报错,真正复杂的活儿一样干不了。问题出在哪?不是模型不行,是你没给它"技能"。
agent-skills这个项目,本质上就是给 AI coding agent 装技能包的一套机制和工具链。你可以把它理解成给一个新员工发《岗位操作手册》:模型本身是那个聪明但啥都不懂的新人,skills 就是告诉他"遇到这类任务,按这个流程、用这些工具、注意这些坑"的标准化文档。没有 skills,你每次都得在对话里从头解释一遍需求背景;有了 skills,agent 能自己识别任务类型、加载对应能力、按既定规范执行。
我最初接触这个概念是在 Claude Code 的技能体系里,后来发现 Cursor、以及一批基于开源模型搭建的 agent 框架都在往这个方向走。核心诉求非常一致:把"提示词工程"沉淀成"可复用、可版本管理、可组合的能力单元"。这跟当年从写 shell 脚本进化到写 Ansible Playbook 是一个道理——从一次性指令,变成声明式的、可维护的能力描述。
这篇文章适合三类人看:一是刚上手 Claude Code 或 Cursor、还在摸索怎么让它干正经活的新手;二是已经在用 agent 但每次都要重复写长提示词、想找更优雅方案的中级用户;三是想给自己团队搭一套内部 agent 能力库的工程负责人。我会把 agent-skills 的目录结构、加载机制、编写要点、和 CLI 工具的配合、以及我在实际项目里踩过的坑,全部摊开讲清楚。看完你至少能做到:给自己常用的三个任务写出可复用的 skill,并且知道怎么调试它为什么不生效。
2. agent-skills 的整体设计与核心思路拆解
2.1 为什么是"技能"而不是"更长的提示词"
很多人第一反应是:我直接把要求写进系统提示词不就行了,为什么要搞一套 skills 机制?这个问题我认真想过,答案在于上下文预算和触发时机。
一个 agent 的系统提示词是常驻的,每次对话都要消耗 token。如果你把十种任务的操作规范全塞进去,光提示词就几千 token,还没开始干活呢,上下文窗口先被吃掉一大块。而且模型面对一大堆"可能用得上"的指令时,注意力会被稀释,真正相关的规则反而容易被忽略——这在长上下文场景下特别明显。
skills 的设计思路是按需加载。平时系统提示词里只放一句极简的索引,比如"你可以使用以下技能:代码审查、数据库迁移、API 文档生成……需要时读取对应文件"。当 agent 判断当前任务匹配某个技能时,才去读取那个技能的完整说明。这就把"常驻成本"变成了"按需成本",上下文利用率高了一个量级。
提示:这个思路和操作系统的虚拟内存很像——不是把所有程序都加载进物理内存,而是用到哪页换哪页。理解这一点,你就能明白为什么 skills 的"描述字段"写得准不准,直接决定了它会不会被触发。
2.2 目录结构与文件组织
一个标准的 skill 通常是一个独立目录,核心是一个 Markdown 文件(一般叫SKILL.md或类似名字),里面包含元信息和正文两部分。元信息用 YAML frontmatter 写,正文就是给 agent 看的操作说明。
典型的目录长这样:
skills/ code-review/ SKILL.md references/ checklist.md scripts/ run_lint.sh db-migration/ SKILL.md templates/ migration.sql.tplSKILL.md的头部大概是这样:
--- name: code-review description: 对指定代码文件或目录进行结构化代码审查,输出问题清单和修改建议。当用户要求 review 代码、检查代码质量、找 bug 时使用。 --- ## 操作步骤 1. 先用 git diff 确认改动范围 2. 按 references/checklist.md 逐项检查 3. 输出格式:文件路径 + 行号 + 问题等级 + 建议 ...这里有个关键点:description 字段是给 agent 做匹配用的,正文是给 agent 执行用的。很多人写 skill 时把这两者混为一谈,description 写得又长又模糊,结果 agent 根本不知道该在什么时候加载它。我的经验是 description 要写成"什么场景下用我"的触发条件,而不是"我是什么"的功能介绍。
2.3 加载机制与触发逻辑
不同 agent 平台的加载机制略有差异,但核心逻辑相通。以 Claude Code 为例,它会在启动时扫描 skills 目录,把所有 skill 的 name 和 description 读进上下文作为索引。当你的请求进来,模型判断需要某个技能,就会主动去读取对应的SKILL.md全文,然后按里面的步骤执行。
Cursor 这边的思路类似,但它更偏向通过 rules 文件和自定义命令来实现类似效果。你在.cursor/rules目录下放的规则文件,本质上就是一种轻量级的 skill。区别在于 Cursor 的规则触发更多依赖文件匹配(比如"编辑.py文件时应用这条规则"),而 Claude Code 的 skills 更依赖语义匹配。
理解这个差异很重要,因为它决定了你写 skill 时的侧重点:语义触发的 skill,description 要写得像"意图识别关键词";文件触发的规则,要写清楚 glob 匹配模式。搞反了就会出现"我明明写了这个技能,它死活不用"的情况。
2.4 组合与复用:skills 的复利效应
单个 skill 的价值有限,真正有意思的是组合。比如你有一个"读需求文档"的 skill、一个"生成接口定义"的 skill、一个"写单元测试"的 skill,理论上 agent 可以串起来完成"从需求到测试"的整条链路。
我实测下来,组合能否成功,取决于两个因素:一是每个 skill 的输入输出格式是否明确(前一个的输出能不能直接喂给后一个),二是 skill 之间有没有职责重叠。如果两个 skill 都声称"负责代码质量",agent 就会犯迷糊,不知道该加载哪个。所以设计 skill 库时,边界清晰比功能强大更重要。
3. 核心细节解析与实操要点
3.1 SKILL.md 的写法:把 agent 当成一个聪明但没背景的新人
写 skill 正文时,最容易犯的错误是"写得太抽象"。比如你写"请仔细检查代码质量",这句话对人类程序员等于没说,对 agent 更是无效指令。有效的写法是把它拆成可执行的动作:
- 检查是否有未处理的异常分支
- 检查是否有硬编码的密钥或路径
- 检查函数是否超过 50 行
- 检查是否有重复代码块
每一条都是 agent 能逐项核对的具体标准。我一般会遵循一个原则:如果这条规则没法用"是/否"来回答,就说明它还不够具体。
另一个要点是给出输出模板。agent 在没有明确格式要求时,输出会非常随意,有时长篇大论有时又过于简略。在 skill 里直接写死输出格式,比如:
输出格式: ## 问题清单 | 文件 | 行号 | 等级 | 问题 | 建议 | |------|------|------|------|------|这样每次执行结果都稳定可预期,方便你后续做自动化处理。
3.2 description 字段的触发优化
description 是 skill 的"广告词",它的唯一任务就是让 agent 在对的时候想起你。我总结了几个写法技巧:
第一,包含用户可能说的原话。用户不会说"执行代码审查流程",他会说"帮我看看这段代码有没有问题"、"review 一下"、"这段逻辑对不对"。把这些口语化表达塞进 description,触发率会明显提升。
第二,明确排除场景。如果有个 skill 只处理 Python,就在 description 里写"仅用于 Python 项目",避免它在 Java 项目里被误触发。
第三,控制长度。description 太长会占用索引空间,一般控制在两三句话内。我见过有人写了 500 字的 description,结果索引本身就超预算了,得不偿失。
3.3 引用文件与脚本的正确姿势
skill 目录里可以放辅助文件,比如检查清单、代码模板、可执行脚本。这里有个坑:agent 不会自动读取这些文件,你必须在 SKILL.md 正文里明确指示它去读。
比如你放了references/checklist.md,就要在正文写"第二步:读取 references/checklist.md 并逐项核对"。否则那个文件就是摆设。
脚本的调用也一样,要写清楚执行命令和参数。我一般会写成:
bash scripts/run_lint.sh <目标目录>并且说明脚本的输出怎么解读。这样 agent 才知道拿到输出后该干什么。
注意:脚本路径建议用相对路径,并且确保脚本有可执行权限。我踩过一次坑,脚本权限不对,agent 执行时报 permission denied,但它不会主动告诉你,而是默默跳过这一步继续往下走,最后结果缺了一块你还不知道。
3.4 版本管理与团队协作
skills 本质上是文本文件,天然适合放进 Git 管理。我建议把 skills 目录作为项目仓库的一部分,或者单独建一个 skills 仓库用 submodule 引入。这样做的好处是:技能可以随项目演进,团队成员共享同一套能力,新人入职直接拉代码就有全套技能可用。
团队协作时要注意命名规范。我见过一个团队里三个人各写了一个"代码审查"skill,名字还不一样,agent 加载时随机挑一个,行为完全不可预测。统一命名前缀能缓解这个问题,比如review-python、review-sql、review-frontend,一看就知道边界在哪。
4. 实操过程与核心环节实现
4.1 环境准备与 skills 目录初始化
先说 Claude Code 这边的操作。安装完成后,skills 一般放在用户级目录或项目级目录。项目级的优先级更高,适合放项目专属技能;用户级的放通用技能,跨项目复用。
初始化步骤大致是:
# 进入项目根目录 cd your-project # 创建项目级 skills 目录 mkdir -p .claude/skills # 创建第一个 skill mkdir -p .claude/skills/code-review touch .claude/skills/code-review/SKILL.md然后编辑SKILL.md,填入 frontmatter 和正文。保存后重启 agent 会话,让它重新扫描目录。
Cursor 这边对应的是.cursor/rules目录,规则文件用.mdc后缀,头部也是 frontmatter,可以指定globs和alwaysApply等字段。如果你想让某条规则在所有对话里都生效,把alwaysApply设为 true;如果只想在特定文件类型下生效,用 globs 匹配。
4.2 写一个能用的代码审查 skill(完整示例)
下面是我实际在用的一个精简版代码审查 skill,你可以直接抄去改:
--- name: code-review description: 对代码进行结构化审查。当用户说"review 代码"、"检查代码质量"、"找 bug"、"看看这段逻辑"时使用。仅用于已提交或已暂存的改动。 --- ## 执行步骤 1. 运行 `git diff HEAD` 获取当前改动,如果无输出则运行 `git diff --staged` 2. 对每个改动文件,按以下维度检查: - 错误处理:是否有未捕获的异常、是否有静默失败 - 边界条件:空值、越界、并发场景是否处理 - 安全性:是否有硬编码密钥、SQL 拼接、路径穿越风险 - 可读性:命名是否清晰、函数是否过长(超过 50 行需提示) 3. 输出格式: ## 审查结果 | 文件 | 行号 | 等级 | 问题描述 | 修改建议 | |------|------|------|----------|----------| 等级说明:P0 必须修复 / P1 建议修复 / P2 可选优化 4. 最后给出总体评价,一句话总结改动质量这个 skill 我用了大概两个月,触发准确率挺高。关键在于 description 里塞了用户常用的几种说法,正文步骤又足够具体,agent 执行起来不会跑偏。
4.3 参数计算与阈值选择
skill 里经常要设一些阈值,比如"函数超过多少行算长"、"圈复杂度超过多少要警告"。这些数字不是拍脑袋定的,我一般参考几个来源:
一是团队既有规范。如果你们代码规范里写了函数不超过 80 行,skill 里就写 80,保持一致。
二是行业惯例。圈复杂度 10 是个比较通用的警戒线,超过 10 的函数测试成本会明显上升。
三是实测调整。我一开始把函数长度阈值设成 30 行,结果误报太多,agent 天天提示"这个函数太长",反而没人看了。后来调到 50 行,信噪比就合理了。
提示:阈值类参数建议写在 SKILL.md 顶部单独列出来,方便调整。别散落在正文各处,改起来容易漏。
4.4 调试 skill 不生效的问题
skill 写完不生效,是最让人抓狂的情况。我的排查顺序是这样的:
第一步,确认目录位置对不对。Claude Code 和 Cursor 的 skills 目录路径不一样,放错了它根本扫不到。
第二步,检查 frontmatter 格式。YAML 对缩进极其敏感,一个 tab 用错就解析失败。我建议用---包裹,字段名和冒号之间不要有空格,冒号后面留一个空格。
第三步,看 description 是否被正确索引。有些平台会打印加载的 skills 列表,如果列表里没有你的 skill,说明是解析问题;如果有但不用,说明是触发问题。
第四步,手动强制触发。在对话里直接说"使用 code-review 技能",看它能不能加载。能加载说明 skill 本身没问题,是自动匹配的锅,回去改 description。
这套流程走下来,九成问题都能定位。
5. 常见问题与排查技巧实录
5.1 高频问题速查表
| 现象 | 可能原因 | 解决办法 |
|---|---|---|
| skill 完全不触发 | description 与用户表达不匹配 | 补充口语化触发词 |
| 触发但执行跑偏 | 正文步骤不够具体 | 拆成可核对的动作项 |
| 引用文件读不到 | 路径写错或未在正文指示 | 用相对路径并在正文明确要求读取 |
| 脚本执行失败 | 权限不足或解释器不对 | chmod +x,明确写 bash/python |
| 多个 skill 冲突 | 职责边界重叠 | 重命名并明确排除场景 |
| 输出格式不稳定 | 未给输出模板 | 在正文写死输出格式 |
| 上下文被撑爆 | skill 正文过长 | 拆分,把细节移到引用文件 |
5.2 我踩过的三个真实坑
第一个坑是description 写成了功能说明书。我最早写的是"本技能用于对代码进行全面的质量审查,涵盖安全性、性能、可维护性等多个维度",结果 agent 很少主动用它。后来改成"当用户说 review、检查代码、找 bug 时使用",触发率立刻上来了。教训就是:description 是给匹配器看的,不是给人看的。
第二个坑是skill 之间互相打架。我同时有一个"代码审查"和一个"重构建议"的 skill,两者都会在用户说"看看这段代码"时被触发,agent 一会儿审查一会儿重构,输出很乱。后来我把重构 skill 的 description 改成"当用户明确要求重构、优化结构时使用",并加了"不用于单纯的问题检查",冲突就消失了。
第三个坑是过度依赖 skill 里的脚本。我写了个 skill 让它调用一个 Python 脚本做静态分析,结果换台机器脚本依赖没装,agent 执行失败后没有报错,而是自己"脑补"了一份分析结果。这个最危险,因为你看不出来它是真跑了还是编的。后来我在 skill 里加了一句"如果脚本执行失败,必须明确报告失败原因,不得自行推断结果",才堵住这个漏洞。
5.3 让 skill 越用越顺的迭代方法
skill 不是写完就完事的,它需要迭代。我的做法是每次用完觉得"这次输出不太对",就顺手改一句 skill 正文。改的时候遵循一个原则:把这次的失败案例变成一条明确的规则。
比如有一次 agent 审查代码时漏掉了配置文件里的密钥,我就在 skill 的检查维度里加了一条"检查配置文件(.env、config.*)中是否有明文密钥"。下次它就不会漏了。
积累下来,一个成熟的 skill 往往迭代过十几版,正文里每一条规则背后都是一次真实的翻车。这也是为什么我建议你把 skills 放进 Git——每次修改都有记录,能看出它是怎么一步步长起来的。
5.4 跨平台迁移的注意事项
如果你同时用 Claude Code 和 Cursor,想把 skill 复用过去,要注意两者的机制差异。Claude Code 的 skill 是语义触发,Cursor 的 rule 更偏文件触发。直接复制过去往往效果打折。
我的做法是维护一份"技能源文档",把核心规则写在一个中立的 Markdown 里,然后针对不同平台各写一层适配。Claude Code 那边包成 SKILL.md,Cursor 那边包成 .mdc 并配好 globs。核心逻辑只维护一份,适配层很薄,改起来不费劲。
另外,不同平台对 frontmatter 字段的支持也不一样。Claude Code 认 name 和 description,Cursor 认 description、globs、alwaysApply。写的时候别把不支持的字段硬塞进去,有些平台遇到未知字段会直接报错。
6. 把 skills 用出复利:一些个人体会
我现在的习惯是,每当我发现自己在对话里第三次解释同一件事,就停下来把它写成一个 skill。这个触发条件很实用,因为重复三次说明它是个高频需求,值得沉淀。
另一个体会是,skill 的价值不在于多,而在于准。我见过有人一口气写了三十个 skill,结果 agent 每次加载索引都要花不少上下文,真正用到的没几个。我现在项目里常驻的 skill 不超过八个,每个都是反复打磨过的,触发准、执行稳。
还有一点,skill 的正文尽量用短句和列表,别写大段散文。agent 读列表的执行准确率明显高于读段落,这一点在长 skill 里尤其明显。我甚至会把关键步骤编号,因为编号能让 agent 按顺序执行,不容易跳步。
最后分享一个小技巧:给 skill 加一个"自检"步骤。在正文最后写一句"执行完成后,确认以上每一步都已执行,如有跳过需说明原因"。这一句话能显著减少 agent 偷懒跳步的情况,实测有效。