最近在开发者社区逛了一圈,发现一个词出现的频率高得吓人:Skills。GitHub上带Skills标签的仓库越来越多,各家AI Agent产品也陆续把"技能市场"做成了标配。我实际动手做下来之后,最大的感受是——这不只是一个新概念,而是把以往"靠堆Prompt调教AI"的玩法,彻底变成了"按需让Agent自己读说明书、自己调工具"的工程化模式。这篇文章我不打算谈虚的,直接从我自己的踩坑和实操出发,把这个东西到底是什么、底层怎么跑、怎么开发一个能用的Skill、以及哪些坑不能踩,讲清楚。
1. Skills到底是个什么东西:把"教一次"变成"复用一万次"
先说一个最容易混淆的地方:Skills不是一个具体产品,也不是某个模型独有的能力,它更像一套约定俗成的组织方式。各个Agent产品把它包装成不同名字,但底层逻辑趋于一致:把一份技能打包成标准目录,里面既有给Agent看的操作说明,也有实际执行的脚本和资源,Agent在接受任务时根据描述自主决定要不要加载这个技能包。
这个设计的第一性原理,我琢磨了很久,归根结底就是——上下文资源有限。大模型处理文本的上限虽然越来越大,但你不可能每次启动对话都塞几千字的步骤指南,更不可能指望用户每次都把写好的方法论复述一遍。与其让用户重复描述"我想要什么、应该怎么做",不如把"怎么做"固化成一份机器可读的技能包。Agent拿到Skill,相当于同时拿到了说明书和工具箱,剩下的就是按图索骥。
1.1 从长Prompt到Skills,本质是注意力利用方式的升级
早期调教Agent,主流手段是写长Prompt。角色、任务、格式、示例一股脑写进去,模型按着提示走。这个方法有效,但有两个硬伤:第一,提示词一旦超过一定长度,模型对关键信息的注意力会被稀释,开头强调的重点到后面可能就模糊了;第二,Prompt是"一次性"的,换个场景或者换个项目,之前沉淀的思路很难直接复用。
Skills解决的正好是这两个问题。一个Skill自带独立的描述字段,Agent在接到任务时会先扫描所有可用的Skill描述,如果匹配,才把完整的SKILL.md加载进上下文。这种"按需加载"机制,本质上把注意力资源做了精准分配:平时技能包是沉睡的,只有被调用时才苏醒。用户也不用费心维护超长Prompt,把方法写进Skill之后,下次再遇到同类问题,Agent会自动找到它。我自己在反复试验中的体会是,同一个分析任务,用一套封装好的Skill来处理,比直接贴一段很长的Prompt要求模型"扮演专家"要稳定得多。
1.2 与插件、MCP、工作流的区别与协同
现在市面上的概念实在太多,插件、MCP、Workflow、Action,再加上Skills,初学者很容易懵。以我个人的理解,它们的定位其实可以做个简单区分:
| 概念 | 定位 | 解决什么问题 |
|---|---|---|
| Prompt | 一次性指令 | 告诉模型当前任务怎么做 |
| Skill | 可复用的技能包 | 让Agent自主识别并调用通用流程 |
| MCP | 工具连接协议 | 把外部数据源、操作系统接口标准化地接到Agent上 |
| 插件 / Workflow | 上层编排 | 绑定特定应用环境,做自动化流程组合 |
举个例子你就明白了。MCP解决的是"怎么让Agent打开数据库、怎么调用某个API"这类连接问题,它很像给Agent装上的各种插头;而Skills解决的是"当你需要做代码审查、写周报、画分镜时,该按什么步骤来"这类方法问题,它更像一个装着流程和脚本的工具包。两者还能协同:Skill的脚本内部完全可以去调用MCP暴露的工具,一边管方法,一边管通道。搞清这层关系,后面设计自己的Skill时,你就知道什么东西该放进Skill,什么东西该交给MCP或插件去管了。
2. 核心机制拆解:一个Skill从被调用到产出的完整链路
要真正掌握Skills,不能只停留在概念层面。我建议从"一个任务进来之后到底发生了什么"来理解它的内部链路。整条链路拆开看,大约是:任务输入 -> Agent扫描所有Skill描述 -> 命中某个Skill -> 读取SKILL.md -> 按指令执行脚本/引用资源 -> 产出结果并返回。这个过程中,最容易被忽视但影响最大的,往往是SKILL.md和命名描述的设计。
2.1 元信息是开关,SKILL.md是操作手册
大多数Skill规范里,SKILL.md的开头都要求一段YAML格式的元信息,里面最关键的就是name和description。很多人以为这只是装饰,实际不然。name基本只有人类管理时用得上,而description是Agent判断"这个技能适不适用于当前任务"的核心依据。
description写得好不好,直接决定Agent会不会在正确时机调用这个Skill。比如你写"用于代码审查",太笼统了,Agent遇到和代码沾边的任务可能都会尝试调用,很混乱;如果写成"对前端JS/TS代码做静态审查,检测潜在错误、风格问题和安全风险,适用于PR评审前的本地检查",命中率和场景匹配度会明显提高。SKILL.md的正文部分则是给Agent看的操作手册,它会指导Agent按步骤执行:先看哪些文件、运行什么命令、如何解读脚本输出、最终按什么格式汇总。你可以把元信息理解为快递单上的收件地址,正文就是包裹里的安装说明书。
2.2 scripts、resources、assets:目录里的分工逻辑
打开一个设计良好的Skill仓库,目录通常长这样:
my-skill/ ├── SKILL.md ├── scripts/ │ ├── analyze.py │ └── report.js └── assets/ ├── example-input.txt └── reference.mdscripts目录存放的是实际要执行的可执行文件或脚本,Agent在需要时会调用命令行工具去运行它们;assets目录存的是辅助资料,比如模板、示例、参考文档,Agent可以把它们作为输入或读取对象。有些实现还支持references目录,用来装比较长的背景知识文档,平时不占上下文,只有相关时才按需读取。
这套目录结构并不复杂,但它是整个Skill能被Agent正确调用的基础。命名上保持清晰,目录不要套太深,脚本尽量做到自包含,这些都是我在维护多个Skill之后得出的经验。依赖外部包越多,Skill的可用性越差,因为这个技能包一旦移到别的环境,很可能因为缺少依赖直接跑不起来。
2.3 命名、描述与触发:影响调用效果的三个隐性因素
在实际测试中,有三个容易被忽略但会显著影响调用效果的因素。一是Skill的名字,尽量用动作加对象的结构,比如"analyze-frontend-code"明显比"fe-helper"更直白;二是description中要写清楚"什么时候用",最好点名场景和排除项,避免误触发;三是脚本入口的说明,Agent是通过自然语言去决定调用命令的,如果你在SKILL.md中没有写清楚"运行python scripts/analyze.py -i ",它可能就会用错误的参数组合。
这就像你给一个实习生交代任务,光说"去处理数据"远远不够,要说清楚"先跑这个脚本、传什么参数、看哪个输出文件、什么情况算异常"。这里我不厌其烦地推荐一个做法:在SKILL.md正文中,明确写一段"执行步骤",按顺序列出命令和结果判断标准,并附带一个简单示例,这个Skill的成功率会瞬间提高不少。
3. 手把手开发一个"前端代码审查Skill"
理论说得再多,不如自己动手做一个。我拿自己经常用的一个场景来演示:开发一个前端代码审查Skill。这个Skill的定位是,在PR合并前或代码提交前,对指定目录下的JS/TS代码做一遍自动化检查,输出问题清单和修复建议。整个开发过程我拆成了五步,你照着做基本都能复现。
3.1 需求拆解:先定义清楚边界和产出
做任何Skill之前,第一件事是定边界。我当时对前端审查Skill的要求是:只针对前端项目,只检查JS/TS文件,不跑完整测试套件,输出格式统一为"严重程度 + 文件位置 + 问题描述 + 修复建议"四要素。边界定得越清楚,后续写description就越轻松。
同时还要回答一个关键问题:脚本到底要做什么?我的方案是用Node脚本配合ESLint来做静态规则检查,再额外跑一段正则风格的自定义扫描,用来发现API Key硬编码、console.log遗漏这类ESLint默认规则不太管的问题。产出物是一份JSON中间结果,后续交给Agent来解读并写成人类可读的审查报告。这一步明确了,脚本开发和SKILL.md编写就有依据了,不会出现Agent拿到Skill却不知道怎么用的尴尬。
3.2 编写SKILL.md:元信息、指令、示例三件套
SKILL.md是技能包的大脑,写法直接影响Agent执行质量。我常用的模板是YAML头部加正文,正文分为概述、使用场景、执行步骤、注意事项、示例五个部分。以我的前端代码审查Skill为例:
--- name: frontend-code-review description: 对 JavaScript/TypeScript 前端项目进行静态审查,检测代码规范、 常见错误、安全问题,输出问题清单和修复建议。适用于 PR 评审、提交前自检, 不适用于后端或全项目大范围重构。 ---正文部分我会写清楚操作流程:第一步,让Agent先读取指定的配置文件或目录结构;第二步,运行审查脚本并把输出定位到一个临时文件;第三步,要求Agent根据JSON结果输出Markdown报告,按严重程度排序;第四步,明确禁止Agent擅自修改源码,只能输出建议。这些"边界提示"很关键,不然Agent很多时候会自作主张,直接帮你改代码,反而埋雷。
示例部分我一般放一段简短的"输入命令示例",方便Agent参照。比如:
node scripts/audit.js --dir ./src --format json --output ./review-result.json顺便说一句,SKILL.md别写得太啰嗦。我第一次写的时候把各种边缘情况全都写了进去,结果Agent执行时总是在判断该走哪条分支,反而效率变差。后来我精简成"主流程 + 关键注意事项"两级结构,效果好很多。
3.3 脚本实现:Node还是Python,怎么选
写Skill的脚本时,你首先会面临技术栈选型。我的经验是,优先考虑目标Agent运行环境里最"原生"的语言。如果这个Skill主要跑在Node生态的前端项目里,用Node.js写,依赖安装方便,调用也自然;如果是数据类的处理任务,Python更合适,因为文件处理、数据加工、各类算法库都更顺手。
以我的审查脚本为例,我选了Node.js,因为它能直接复用项目里的node_modules,比如ESLint。脚本结构很简单:入口读取命令行参数,解析目录,调用ESLint的Node API做lint,再跑一段自定义的正则扫描,最后汇总成JSON。伪代码风格如下:
const { ESLint } = require("eslint"); async function runAudit(directory) { const eslint = new ESLint({ fix: false }); const results = await eslint.lintFiles([`${directory}/**/*.{js,ts}`]); // 遍历 results,提取 messages,加上自定义扫描结果 const formatted = results.flatMap(r => r.messages.map(m => ({ severity: m.severity === 2 ? "error" : "warning", file: r.filePath, line: m.line, message: m.message, ruleId: m.ruleId })) ); console.log(JSON.stringify(formatted, null, 2)); } runAudit(process.argv[2]);这里有个细节:不要让脚本输出大段彩色日志或无关信息,尽量输出纯净的格式化结果,因为Agent需要依赖输出来生成报告。输出越结构化,Agent解读越准确。
3.4 本地验证与调试:让Agent真的去用它
开发完Skill,不要直接丢进Agent环境就完事。我通常会在本地先做一轮"手动验证",确认脚本单独跑没有任何问题。然后才把Skill目录放到Agent的skills目录下,给它一个真实的审查任务,观察它是否会自动调用。
这里我遇到过一个典型问题:Agent确实识别出了这个Skill,但执行脚本的时候参数传错了。原因是我在SKILL.md里没有写清楚参数的含义。后来我把参数说明加到了正文里,比如"--dir 指定要审查的目录、--output 指定临时输出文件路径",再试就稳定多了。调试时建议开Agent的详细日志,能清楚看到它读取了哪些文件、运行了什么命令、输出了什么,方便定位问题出在哪个环节。
3.5 安装、发布与多人协作
做完本地验证之后,就是安装与分发的问题了。不同产品对Skill的安装路径有各自约定,但大体思路是一致的:把整个技能包目录放进Agent能扫描的固定目录即可。我自己的习惯是,把每个Skill维护成独立的Git仓库,结构固定,然后在本机做一个统一的skills目录,里面通过git clone拉取或通过符号链接指向各个仓库。
关于Skill的分享和下载,现在已有不少社区平台做这件事,包括各家的官方市场,也有很多开发者把自己整理的Skill合集直接公开在GitHub仓库里。下载平台怎么选,我的建议是优先选择那些有版本记录、有描述文档、更新活跃的仓库,避免下到结构不完整或脚本有问题的技能包。下载之后先看一眼目录结构,确认包含SKILL.md再使用,这是最基本的辨别办法。
多人协作时,文档就变得更重要了。SKILL.md打包的既有代码也有方法论,最好在仓库README里写明适用场景、依赖要求、已知限制,减少团队里其他同事的上手成本。我在团队内部实践过一版,把公共的审查、检查类流程都用Skills沉淀下来之后,新人熟悉Agent的使用习惯就快了很多,因为这些"习以为常的检查流程"已经变成了可复用的标准动作。
4. 容易踩的坑与现场排查实录
Skills这东西,乍一看简单,用起来却有不少暗坑。我把自己在开发和测试过程中真实遇到过的问题整理成了一份速查表,附上排查思路。有些坑属于设计层面的,有些是环境层面的,都值得记一下。
4.1 SKILL.md写得太抽象或太啰嗦
这是我看到最多的问题。写得太抽象,Agent不知道从哪里下手;写得太啰嗦,Agent在判断分支时反而混乱。我的标准是:正文控制在300到600行以内,执行步骤部分尽量用命令和明确的行为描述,不要写"分析文件"这种空话,要写"运行node scripts/audit.js --dir ./src,读取输出结果"。同时,把"禁止做的事"单独列一节,比如"禁止自动修改源码""禁止直接删除测试文件",这类负面约束对Agent的约束力往往比正面指令更强。
4.2 脚本权限、依赖与路径问题
环境相关的坑主要集中在这几个方向:第一,脚本没加执行权限,Agent调用时直接Permission denied;第二,脚本依赖的npm包或Python包没有安装;第三,脚本里使用了绝对路径,导致换台机器完全跑不了。这些问题在发布的时候特别容易坑到别人。
我的排查思路很固定:先在Shell里手动执行一遍脚本命令,确认能跑通,再检查依赖是否声明在package.json或requirements.txt里,最后把路径改成相对当前Skill目录的写法,这样移植性会好很多。还有一个很容易忽略的点:Agent执行命令时的当前工作目录不一定是Skill所在目录,所以脚本内部如果有文件读取,要注意路径计算,建议基于脚本自身位置来定位资源文件。
4.3 多个Skill之间的互相干扰
当你装了十几个Skill之后,新的问题出现了:Agent面对多个描述近似的Skill时会选择困难,甚至选错。比如我同时装了"前端代码审查"和"全栈代码规范检查"两个Skill,某个任务里Agent选错了,输出风格完全不对。排查下来,问题出在description写得有重叠。
解药是给每个Skill的description增加明确的"排除条件"。比如"前端代码审查"里就写明"不适用于Python或后端Java代码","全栈代码规范检查"则写明"侧重配置文件与跨语言通用规范"。这样Agent做匹配时就能把无关项过滤掉。这个调整看着不起眼,但对整体调用准确率提升非常明显。
4.4 常见问题速查表
| 现象 | 可能原因 | 排查思路 |
|---|---|---|
| Agent从未调用Skill | description太笼统或场景不匹配 | 检查描述是否写明适用场景,试着明确排除项 |
| 调用了但结果不对 | SKILL.md步骤不清晰或参数未说明 | 补全执行步骤,明确命令参数含义 |
| 脚本报Permission denied | 缺少执行权限 | 执行chmod +x,或改用"node xxx.js"调用 |
| 输出结果很长且混乱 | 脚本输出包含无关日志 | 让脚本只输出结构化结果,重定向日志到临时文件 |
| Agent同时启用多个Skill | description边界重叠 | 为每个Skill添加排除使用场景的说明 |
| Skill换了环境跑不通 | 依赖未声明或路径写死 | 检查依赖文件,路径改为基于Skill目录的相对路径 |
还有一个我个人的经验,比上面这些都重要:每一个Skill都要配一个"最小冒烟测试样例"。比如前端审查Skill配一段有明显问题的代码片段,分镜Skill配一段待拆解的剧本。每次改完Skill,跑一遍冒烟测试确认基本链路仍通,再放心的用。这个习惯帮我挡下了好几次回归问题。
5. 实践心得:Skills该怎么积累,才不变成一次性玩具
如果你只是把网上现成的Marketplace里别人做好的Skill下载来用,那体验确实很省事,但要真正发挥Skills的价值,我还是建议亲自动手封装几个属于自己工作流的技能包。原因是,真正贴合你工作流的方法论,网上往往找不到完整的现成版本,只有你自己最清楚"一份合格报告长什么样""一次代码审查该查哪些重点"。
我的一个体会是,Skills的价值其实是慢慢浮现出来的。刚做好第一个Skill时,可能会觉得"这也没比我自己复制一段Prompt快多少啊"。但当你把工作流程里那些重复发生、有明确判断规则、需要沉淀经验的任务一个一个包成技能,后面再接到类似任务时,就不需要从头组织思路,直接交给Agent处理,再上手检查结果就行。这种复利效果,是临时写Prompt比不了的。
最后分享一个小技巧:Skill不是做一次就完事,它是可以迭代的。每次运行之后,如果发现输出格式不好、步骤不够完整、脚本误判率偏高,就把修改记下来并更新到SKILL.md里。我自己实践中,大多数Skill在前两三次使用后都会有一次明显的"打磨期",度过之后,才算真正沉淀成了稳定可靠的能力。这也提醒我,别指望一个Skill一步到位,边用边改,是这类技能包工程里最正常的节奏。