1. 从"每次都要重新交代一遍"说起:我为什么会研究 Skills
如果你也试过每次让 AI 助手干活前,都要把同一套流程说明、输出格式、注意事项从头到尾复制粘贴一遍,那你一定懂我的烦躁。连续帮同一个项目做代码审查到第三天,我决定认真把 Agent Skills 这套思路捡起来研究,结果不仅省掉了反复粘贴的体力活,还顺带把整个团队的 AI 协作方式重新梳理了一遍。
1.1 一个让我崩溃的日常场景
我的日常有一大块工作是给项目做代码审查。最初我用 AI 助手的方式很原始:每次打开新会话,先贴一段"你是资深工程师,请按 XX 规范审查,重点关注安全性和可维护性,输出格式为问题列表",再贴上 diff,等结果。一天最多的时候我要复制这段前置指令五六次,而且每次措辞还有细微出入——今天多写了一句"别忘了检查 SQL 注入",明天忘了写"按严重程度排序"。
更麻烦的是,这类"长 prompt"根本没法维护。今天加一条规则,明天删一个步骤,分散在十几个聊天记录里,谁也说不清当前版本是什么。等团队里其他人想复用我这套审查方式时,只能靠我把文字一段段发过去,复制错了也没人发现。
后来我开始认真研究 Agent Skills。说白了,它就是把"给 AI 的指令 + 配套的脚本 + 参考资料"打包成一个标准目录,放进约定的文件夹,AI 助手就能在需要的时候自动发现并使用。这个思路解决的不是"能不能生成代码"的问题,而是"如何稳定地让 AI 按我的流程办事"的问题。
1.2 Skills 到底解决了什么问题
一个 Skill(技能包)在我看来就是一个自包含的文件夹,里面至少有一个SKILL.md文件,这个文件用 YAML frontmatter 写元信息,用 Markdown 正文写具体执行步骤;旁边可以放脚本、模板、代码片段、参考文档等附属资源。
以我日常使用的 Claude Code 生态为例,官方把这套机制叫做 Agent Skills,典型的目录结构长这样:
~/.claude/skills/ └── code-review/ ├── SKILL.md ├── checklist.md └── scripts/ └── run_review.pyAI 助手在启动时会先"看到"所有技能的目录清单,知道每个技能叫什么、是干什么的(这部分只有 name 和 description,体积很小);等到某个任务和某个技能描述匹配上了,才会去读取那个技能的完整SKILL.md;正文里提到需要某个脚本或模板时,再按需加载那些附属文件。
这个机制有点像家里的工具箱。你不需要把电钻、水平仪、螺丝刀全部握在手上才能干活,你只需要知道柜子里有什么工具、每把工具标签上写了什么用途;等真需要拧螺丝的时候,再打开抽屉取出那把螺丝刀。整套文件都在柜子里,但你手里始终只拿着当前需要的那件。
值得一提的是,这套抽象在多个厂商的生态里正在快速收敛。除了 Anthropic 的 Agent Skills,OpenAI 的 AgentKit 里也有名为 Skills 的能力封装;社区里还出现了各种开源的多 Agent 技能格式。各家在命名、目录约定、字段细节上还有差异,但骨架是一致的:可发现、按需加载、自带方法论的可复用指令包。这也是我敢投入精力去研究它的原因——就算底层模型换成别家,这套思维方式也大概率能平移过去。
1.3 适合谁读这篇文章
我想把这篇写成一份实战手记,而不是官方文档的转述。如果你属于下面任何一类,应该会有收获:
- 重度使用 AI 编程助手,但一直在靠复制粘贴长指令干活;
- 团队里希望大家用同一套标准和流程调用 AI,而不是每人一套 prompt;
- 遇到"AI 每次给的答案风格差别很大"、"换个人问结果完全不一样"这类问题,希望把不确定性压下来。
接下来我会按这样的顺序讲:先拆解 Skill 包的文件结构和工作原理,再对比它和 MCP、Subagent 的分工边界,然后手把手带你把一个"代码审查"技能包从零写出来,最后分享我迭代过程中踩过的坑和几条个人体会。
2. 拆开一个 Skill 看看:SKILL.md 与目录结构
2.1 目录结构长什么样
技能包本质上就是一个文件夹,里面必须有一个SKILL.md。以 Claude Code 生态的约定为例,技能通常放在三个层级:
| 放置位置 | 作用范围 | 典型用途 |
|---|---|---|
~/.claude/skills/ | 个人所有项目 | 放自己最常用的通用技能,比如会议纪要、提交信息生成 |
<项目>/.claude/skills/ | 当前项目团队 | 放项目特有的规范,比如该项目的数据库迁移审查流程 |
| 插件(plugin)里 | 可以随项目分发 | 把整套技能打包进团队脚手架,新成员 clone 即用 |
放在这三处,本质上都只是目录约定,不用安装什么依赖。你把一个带SKILL.md的文件夹放进去,AI 助手重启会话后就能发现它。
举个例子,你可以直接这样创建一个个人级技能:
mkdir -p ~/.claude/skills/code-review/{scripts,templates}就这么简单,剩下的工作是把SKILL.md写出来。哪怕你现在用的是其他 AI 编码工具,只要它支持"技能"或"自定义指令"这类机制,思路都可以按同一套来:一个文件夹、一份带元信息的说明文档、若干配套资源。
2.2 SKILL.md 的 frontmatter 字段
SKILL.md的头部是一段 YAML frontmatter,常见字段长这样:
--- name: code-review description: 对代码变更进行系统化审查,适用于 PR/MR 评审、提交前自查,以及"帮我看看这段代码有什么问题"之类的请求。当用户提到 review、code review、代码审查、CR 时优先使用本技能。 allowed-tools: - Read - Grep - Bash - Write version: 1.2.0 license: MIT ---每个字段的作用不一样,我的经验是:
- name:技能的内部标识,一般用小写字母、数字、短横线。它主要用来被
@skill-name这种方式显式引用,也方便你在日志里看到底是哪个技能被触发了。 - description:这是整个技能包里最重要的字段。AI 助手判断"当前任务要不要用这个技能"时,主要就是靠它。写得好,模型在合适的时机自然想起它;写得敷衍,技能根本不会被触发。
- allowed-tools:可选字段,限定这个技能执行时可以使用哪些工具白名单。如果你不想让技能通过 Bash 随意改文件,就把
Edit、Write排除掉。 - version / license / metadata:可选字段,主要用于版本管理和团队分发。数量多了之后,你一定会感谢自己当初顺手写了 version。
2.3 渐进式披露(Progressive Disclosure)
理解"渐进式披露"是掌握 Skills 的关键。在 Claude Code 这类实现里,AI 助手的上下文窗口里平时只保留一份技能清单,每一条包括技能名和 description,可能还有版本号。这部分信息很轻,几十个技能也不会占用太多 token。只有判断当前任务与某个技能匹配时,它才会去读取该技能的SKILL.md全文;而SKILL.md里引用的附属脚本、模板,则要等真正派上用场时才加载。
这个设计非常聪明,因为我早年写过那种"把所有规则一口气塞进 system prompt"的做法,很快就撞到几个问题:指令太长后模型会选择性遗忘中间内容;无关任务的场景也要白白承担这些 token 的开销;想改一条规则还得在那段几千字的 prompt 里找半天。
渐进式披露等于把"常驻内存"和"磁盘中的文档"做了分层。清单常驻,全文按需加载。就像图书馆里你只需要随身带着检索卡片,真要读某本书时才去书库取,而不是把所有书都扛在背上。
提示:这也反过来提醒你,
SKILL.md的开篇和 description 一定要把"什么时候用、怎么用"说清楚,因为模型做触发判断时只看这几行字。具体操作步骤写得再完美,触发条件写得模糊,技能也只会躺在文件夹里吃灰。
3. Skills、MCP、Subagent:这三兄弟到底怎么分工?
3.1 各自解决的问题
第一次接触 Skills 的人最容易混淆的是:它和 MCP(Model Context Protocol)工具、Subagent(子代理)到底什么关系?我的理解是,它们解决的是三个不同维度的问题。
- Skill解决的是"怎么干":它给模型提供了一套做事的流程、规范和领域知识。比如代码审查应该先看什么再看什么、输出格式是什么、哪些红线必须检查。
- MCP Tool / Function Call解决的是"能干什么":它给模型提供了操作外部世界的能力,比如读文件、查数据库、调外部 API、在某个系统里创建工单。
- Subagent解决的是"让谁去干":它把一整块任务交给一个独立上下文的子代理区处理,主代理不掺和中间的每一步,只接收最终结果。
可以用一张表把它们的差异列开:
| 对比维度 | Skill | MCP Tool | Subagent |
|---|---|---|---|
| 抽象层次 | 方法论/流程 | 操作能力 | 独立执行者 |
| 主要开销 | 按需加载指令文本 | 工具定义与调用 | 独立上下文窗口 |
| 是否可复用 | 跨项目、跨会话直接复用 | 配置好后通用 | 通常按任务现场创建 |
| 典型场景 | 代码审查、会议纪要、发布检查 | 读仓库、跑测试、调接口 | 深度专项分析、长链路调研 |
打个比方:Skill 是操作手册,MCP 是工具柜里的电钻,Subagent 是你临时请来的老师傅。老师傅需要看操作手册(Skill)来了解你们团队的标准,也需要用电钻(MCP)来施工,但你不必每一步都盯着他。
3.2 什么时候应该写成一个 Skill
我在实战里总结了一个很简单的判断标准:只要某个任务满足"固定方法论 + 多步流程 + 需要领域规则",而且你希望换个项目、换个会话之后还能用同样的方式完成,就应该封装成 Skill。反之,如果只是一次性让模型帮你改个正则表达式,写成技能反而是过度设计。
举个例子,下面这几种都适合做成 Skill:
- 代码审查、依赖升级检查、安全扫描;
- 生成符合规范的 Git 提交信息、变更日志;
- 整理会议纪要并将行动项导出到指定格式;
- 按公司模板写技术方案、写复盘文档。
一旦你开始把高频使用的 prompt 模板逐个"技能化",就会发现它们的共性:有明确的输入、有稳定的步骤、有统一的输出格式。这正是适合固化下来的东西。
3.3 一个典型的协作场景
三者不是互斥关系,实际项目里经常配合使用。我说一个自己最近在用的场景:
每次要审查一个 PR 时,AI 助手会通过 MCP 提供的仓库读取能力拿到 diff 文件;接着它发现任务和code-review这个技能描述匹配,于是加载SKILL.md,按照里面定义的流程开始逐层分析:先跑一个 Python 脚本来做静态扫描(脚本是技能包自带的),再对照checklist.md逐项检查,最后按模板输出审查报告。
如果某个文件特别复杂,主代理还可以派一个 Subagent 去专门深挖那段逻辑,拿到结论后再汇总进报告。整个过程里,技能负责"按什么节奏做",MCP 负责"每一步怎么拿数据",Subagent 负责"把难啃的骨头丢给独立上下文去啃"。
4. 手把手:把"代码审查"这个技能包从零写好
4.1 先定义边界,别贪多
我第一次写技能就犯了个典型错误:想一个技能包解决所有问题,把风格审查、性能审查、安全审查、架构审查全塞进一个SKILL.md里。结果文档写了快两千行,模型根本记不住,触发之后表现还不如不触发。
后来的经验是:一个技能只做一件事,把边界划清楚。所以我这里拿"代码审查"举例,但刻意把它限定为一个具体场景——针对一个 PR/MR 的改动做正确性和安全性审查,不做大架构评审。
先想清楚三件事:
- 输入:一个 PR 的 diff、相关文件路径、可参考的近期改动背景;
- 输出:问题列表(含严重程度、文件位置、代码引用、修改建议),外加一个总结段落;
- 红线:安全类问题(注入、硬编码密钥、危险的默认参数)必须强制标记为 High。
边界一旦明确,写SKILL.md就不会东拉西扯。
4.2 写一份合格的 SKILL.md
下面是一个可以直接抄的示例,你完全可以把名字和细节换成自己的场景:
--- name: code-review description: 对代码变更进行系统化审查,适用于 PR/MR 评审、提交前自查,以及"帮我看看这段代码有什么问题"之类的请求。当用户提到 review、code review、代码审查、CR 时优先使用本技能。 allowed-tools: - Read - Grep - Bash - Write --- # Code Review 对一次代码变更进行系统性审查,重点关注正确性和安全性,兼顾可维护性。 ## 什么时候使用 - 用户请求审查一个 PR/MR 的 diff; - 用户说"帮我 review 一下这段代码"、"看看这次改动有什么问题"; - 提交前自查。 ## 执行步骤 1. 获取变更范围和 diff。优先使用 `scripts/run_review.py` 做初筛,该脚本会输出一个候选问题清单。 2. 阅读 `checklist.md` 中的逐项检查清单,结合 diff 逐条核对,不要遗漏安全类检查项。 3. 对每一个发现,定位到具体文件和代码行,给出严重程度评级: - HIGH:会导致数据泄露、崩溃、明显错误行为; - MEDIUM:潜在问题或不符合项目规范; - LOW:风格、注释、可读性建议。 4. 按 `templates/review_report.md` 的格式输出报告。报告必须包含"问题列表"和"总结"两个部分。 ## 注意事项 - 不要把工具脚本发现的全部问题都直接丢进报告,先人工判断是否为误报。 - 涉及安全红线(注入、硬编码密钥、命令拼接)时,至少标 HIGH。 - 没有发现任何问题时也要明确写"未发现高风险问题",避免留白。这份文档的妙处在于:它把"触发条件"写在了 description 里;把"执行步骤"写成可核对的序列;把"红线"写成显式规则;把细化的对象(checklist、模板、脚本)都指向附属文件,而不是全都堆在主文档里。
4.3 配套资源文件怎么放
技能包的威力很大一部分来自配套资源。还是以代码审查为例,我建议至少准备三个附属文件。
第一个是checklist.md,它是审查的逐项清单,比SKILL.md正文更细,适合经常更新:
# 代码审查检查清单 ## 安全 - [ ] 是否存在 SQL 拼接、命令拼接、不安全的反序列化? - [ ] 是否有硬编码密钥、Token、连接串? - [ ] 是否有默认密码或可预测的鉴权逻辑? ## 正确性 - [ ] 边界条件是否处理(空列表、None、超长输入)? - [ ] 异常路径是否兜底?失败后是否可能静默吞掉错误? - [ ] 并发场景下是否有竞态问题? ## 可维护性 - [ ] 命名是否清晰?是否有大段重复代码? - [ ] 是否引入了不必要的复杂度?第二个是scripts/run_review.py,它做初筛,用处是让模型不必每次从头读一遍整个仓库。脚本不用很高级,能抓出常见的危险模式就够:
#!/usr/bin/env python3 import re, sys patterns = { "sql_concat": r"(SELECT|INSERT|UPDATE|DELETE).*['\"].*[+%]", "hardcoded_secret": r"(password|api_key|token)\s*=\s*['\"][^'\"]{6,}['\"]", "eval_usage": r"\b(eval|exec)\s*\(", } def scan_file(path): findings = [] try: with open(path, "r", encoding="utf-8", errors="ignore") as f: for lineno, line in enumerate(f, 1): for kind, pat in patterns.items(): if re.search(pat, line, re.IGNORECASE): findings.append((path, lineno, kind, line.strip()[:80])) except Exception as e: findings.append((path, 0, "read_error", str(e))) return findings if __name__ == "__main__": for path in sys.argv[1:]: for f in scan_file(path): print(f"{f[0]}:{f[1]} [{f[2]}] {f[3]}")第三个是templates/review_report.md,限定输出格式:
# 变更审查报告 - 审查范围: ... - 审查时间: ... ## 问题列表 | 严重程度 | 文件 | 行号 | 问题描述 | 建议 | | --- | --- | --- | --- | --- | ## 总结 ...这些资源文件用相对路径在SKILL.md里引用(比如上面示例里的scripts/run_review.py、checklist.md),整个技能包就能作为一个整体被拷贝、共享,不会因为路径散落而失效。
4.4 验证和迭代闭环
写完先别急着到处用,我的验证流程是这样的:
- 把技能包放进
.claude/skills/或~/.claude/skills/; - 开一个全新会话,直接对 AI 说"帮我 review 一下最近这次提交",不要自己补充任何额外规则;
- 观察它是否加载了技能。如果它没按
SKILL.md的步骤走,第一条要怀疑的就是 description 写得不够明确; - 让它跑一次真实 diff,检查报告是否包含 HIGH/MEDIUM/LOW 分级、是否有误报;
- 把发现的问题带回
checklist.md和SKILL.md修改,重复第 2 步。
提示:测试时一定要开全新会话,不要在同一个会话里既写技能又让它执行。同一个会话里模型已经知道你的意图,即使没加载技能也可能"表现正确",这会严重干扰判断。
5. 我踩过的几个坑,写出来给你避雷
5.1 description 写得太"佛系",模型根本想不起来用它
我最早给"代码审查"技能写的 description 是"用于代码审查"。结果开了新会话后,模型依然按照自己的习惯去回答,技能完全没被触发。原因很简单:模型需要在对话里做触发判断,而"代码审查"这个信号太弱了。
后来我改成写清楚"触发条件 + 同义词 + 示例意图",效果立刻不一样:
description: 对代码变更进行系统化审查,适用于 PR/MR 评审、提交前自查,以及"帮我看看这段代码有什么问题"之类的请求。当用户提到 review、code review、代码审查、CR 时优先使用本技能。要记住,这个字段不是给人看的,是给模型做检索用的。把你平时会说的每一种说法都写进去,模型才更容易在正确时机把它捞出来。
5.2 把 SKILL.md 写成了百科全书
我见过有人把项目背景、API 文档、历史决策、团队组织架构全写进SKILL.md正文,几百行起步。这样做有两个问题:一是按需加载时全文会占用大量上下文,挤压真正执行任务的空间;二是内容太长后,模型对文档中部的规则记忆会明显衰减。
正确做法是让SKILL.md保持精炼,只写"步骤 + 规则 + 引用指向",把细节拆到附属文件里。我现在的经验是,主文档尽量控制在 300 行以内,超过的部分问一句"这个细节属于哪类资源",然后拆出去。前端代码审查的规则放在checklist-frontend.md,后端安全规则放在checklist-backend.md,按任务类型分别引用,而不是一股脑塞进主文档。
5.3 资源文件引用与路径问题
技能包被复制到不同项目后,附属文件的相对路径一定会变。如果你的SKILL.md里写了scripts/run_review.py,但某个项目里技能放在更深层目录,触发后模型可能找不到文件。
我的做法是:在SKILL.md正文里明确写上"脚本位于本技能目录下的scripts/run_review.py,请先定位技能所在目录再执行"。很多实现里,模型可以用类似pwd或读取文件列表的方式来确认位置,你只需要在指令里提示它"先确认目录,再运行命令",就能避免大部分路径问题。
5.4 忘了限制工具边界
技能一旦被触发,AI 助手在执行过程中是有工具调用权限的。如果你写了一个"数据脱敏审查"技能,本来只想让它读文件、找敏感信息,结果它顺手用Edit帮你改了文件,那就危险了。
allowed-tools字段就是干这个的。比如代码审查技能我通常只放Read、Grep、Bash、Write,并且刻意不开放Edit——因为它要输出报告,写报告是允许的,但我不希望它直接改我的源代码。如果你的实现不支持这类白名单字段,就在SKILL.md的注意事项里用"禁止修改任何源代码文件""只允许输出报告"这样的强指令兜底。
5.5 版本管理与团队共享
技能一多,版本混乱就来了。团队里有个人改了checklist.md,另一个人还在用老版本,审查结果自然对不上。我的经验是把技能包直接放进 Git 仓库管理,团队共用一份,改动走 MR 流程。
推荐在仓库里按这样的结构组织:
skills-repo/ ├── code-review/ │ ├── SKILL.md │ ├── checklist.md │ └── scripts/ ├── meeting-notes/ │ ├── SKILL.md │ └── templates/ └── README.md # 写明每个技能的适用范围和维护人每个技能目录里放一个版本号,改动时更新version和变更说明。刚开始不用搞复杂的治理机制,两三个人协作时,一条简单的约定就够了:改技能必须连版本号一起改,且要在 README 里留一行变更记录。
6. 写给也想入坑的人:几条个人体会
最后分享几个我自己摸索出来的实操习惯,不一定适合所有人,但至少能帮你少走弯路。
第一,从你最长最常用的那条 prompt 模板开始改造成技能。我最先技能化的就是"代码审查"和"会议纪要",因为它们是我每周都要用十几次的流程,投入产出比最高。不要一开始就想着建设一套庞大体系,先做两个真正高频的,跑通了再扩展。
第二,坚持"一个技能只负责一件事"。我拆过最夸张的一个技能,原本涵盖审查、修 bug、写测试、生成提交信息四个功能,后来拆成四个独立技能包,每个都更稳定,触发也更准确。技能之间的组合可以靠模型自然调度,不需要硬塞进一个包里。
第三,每迭代一版,都要在全新会话里验证一次。我很多次觉得"改好了",结果开新会话一测,description 还是没触发,或者某个路径写错导致脚本跑不起来。真正的验证标准只有一个:在一个完全不知道你意图的新会话里,它能不能靠 description 主动找到这个技能,然后严格按流程执行。
第四,注意观察上下文开销。技能包越大、被触发的次数越多,token 消耗越明显。我一般会对频率最高的技能定期"瘦身":把正文里的示例代码挪到附属文件,把冗长的解释压缩成指令。毕竟技能是为了省事,不是为了给模型加负担。
第五,团队共享之前,先把个人版本跑稳。自己都没用顺手的技能,别急着同步给同事。我在团队里推广的经验是:先在个人环境里用一周,确认输出稳定、误报率低,再提交到共享仓库,并在 README 里写明适用场景,避免有人误用。
这套东西不需要等谁发布新版本,你今天就可以打开终端,创建一个目录,把你最常用那条 prompt 改写成第一份SKILL.md。我打包完第一批技能之后最直接的感受是:和 AI 协作这件事,终于从"每次碰运气"变成了"按标准作业"。希望你也能早点体会到这种感觉。