1. 从"skills"这个热词说起:它到底在解决什么问题
最近一段时间,"skills"这个词在技术社区里的出现频率高得有点反常。如果你只是偶尔刷一刷动态,可能会以为这又是哪个新出的前端框架或者某个编辑器插件。但真正翻过几篇讨论、动手跑过几个示例之后就会发现,大家嘴里的"skills"其实指向的是一类更底层的东西——给智能体(Agent)装配可复用的能力模块。
我最初接触这个概念的时候也走了弯路。当时看到"Agent Skills"这个词,第一反应是"这不就是函数调用(Function Calling)换个说法吗"。结果真正上手之后才明白,两者虽然表面上有重叠,但设计哲学完全不同。函数调用更像是你临时告诉模型"有这么个接口,你按格式调一下";而 skills 强调的是把一套完整的、带上下文的能力封装成一个可发现、可加载、可组合的单元,让智能体在需要的时候自己去"找"和"用"。
这个区别听起来很抽象,但落到实际项目里影响巨大。举个我自己的例子:之前做一个文档处理类的智能体,需要它能够读取表格、做数据清洗、再生成图表。如果走函数调用的路子,我得把这三个能力全部塞进系统提示词里,模型每次都要在庞大的工具列表里挑。而换成 skills 的思路之后,我可以把"表格解析"做成一个 skill,"数据清洗"做成一个 skill,"图表生成"做成一个 skill,每个 skill 自带说明文档、参数定义和示例。智能体接到任务后,先判断需要哪些能力,再按需加载对应的 skill。上下文更干净,命中率更高,维护起来也更容易。
所以这篇内容我想聊的,不是某个具体平台的安装教程,而是把 skills 这套东西从"是什么"到"怎么用"再到"怎么自己写"完整地拆一遍。关键词里出现的 Agent Skills、Google Cloud、GKE、Genkit 这些,本质上都是这套思路在不同场景下的落地形态。无论你是做前端、做后端,还是做 AI 应用开发,只要你在跟智能体打交道,skills 这套机制都值得花时间搞明白。
适合读这篇的人大概有三类:一是刚听说 skills 但还没动手的开发者;二是已经在用但总觉得"没用到点子上"的实践者;三是想自己设计一套 skill 体系的技术负责人。下面我会尽量用大白话把原理讲透,同时给出可以直接抄的操作步骤和踩坑记录。
2. 拆开一个 skill 看内部:它凭什么比提示词堆砌更靠谱
2.1 skill 的最小构成:描述、指令、资源三件套
要理解 skills 为什么好用,得先知道一个 skill 里面到底装了什么。我拆过不少现成的 skill,也自己写过十几个,总结下来核心就三块:元数据描述、执行指令、附带资源。
元数据描述解决的是"发现"问题。智能体面对几十上百个 skill 的时候,不可能把每个的完整内容都读一遍,那样上下文直接爆掉。所以每个 skill 都有一个简短的描述,告诉智能体"我是干什么的、什么时候该用我"。这部分通常包括名称、一句话简介、适用场景、以及一些触发关键词。你可以把它理解成图书馆的索引卡片——不翻开书,光看卡片就知道这本书大概讲什么。
执行指令解决的是"怎么做"问题。当智能体决定要用某个 skill 之后,它才会加载这个 skill 的完整指令。这部分是真正的操作手册,包括步骤、注意事项、输入输出格式、边界条件等等。我见过写得好的 skill,指令部分详细到"如果输入是空数组,返回什么错误码"这种程度;也见过写得烂的,就一句话"处理数据",模型拿到之后完全不知道从哪下手。
附带资源解决的是"能力扩展"问题。有些 skill 不只是文字指令,还会带脚本、模板、配置文件、参考文档。比如一个"生成周报"的 skill,可能附带一个 Markdown 模板和一个 Python 脚本用来统计本周的提交记录。智能体加载 skill 的时候,这些资源也一并可用。这是 skills 相比纯提示词最大的优势之一——它能把代码和文档打包在一起,而不是让模型凭空去猜。
2.2 渐进式加载:为什么它能让上下文不被撑爆
skills 机制里我觉得最巧妙的设计,是渐进式加载(Progressive Disclosure)。这个词听起来很学术,其实逻辑特别朴素:不要一次性把所有东西都塞给模型,而是分层次、按需给。
具体来说分三层。第一层是元数据,所有 skill 的描述都会常驻在上下文里,但每个只有几十个 token,几十个 skill 加起来也就一两千 token,完全扛得住。第二层是完整指令,只有当智能体判断"这个任务需要用到某个 skill"时,才把这个 skill 的详细内容加载进来。第三层是附带资源,只有当指令执行到某一步真的需要某个脚本或文档时,才去读取那个具体文件。
我做过一个对比测试,同样一个包含二十个能力的智能体,用传统"全塞进系统提示词"的方式,光工具定义就占了将近八千 token,模型经常在长对话里"忘记"某些工具的存在。换成 skills 的渐进式加载之后,常驻部分压到了不到一千 token,模型的选择准确率反而提升了。原因很简单:上下文越干净,模型的注意力越集中。这跟人开会是一个道理,桌上摆二十份文件你反而不知道看哪份,只摆一份摘要你立刻就能抓住重点。
2.3 和函数调用、插件系统的本质区别
很多人会把 skills 和函数调用混为一谈,我一开始也是。但用久了会发现三个关键差异。
第一是粒度不同。函数调用通常对应一个具体的 API 接口,比如"查询天气"就是一个函数。而一个 skill 可以包含多个步骤、多个函数、甚至多个决策分支。比如"出差安排"这个 skill,内部可能先查航班、再比价、再订酒店、最后生成行程单,这一整套流程封装成一个 skill。
第二是自描述程度不同。函数调用的参数定义是给机器看的 JSON Schema,而 skill 的指令是给模型看的自然语言加示例。这意味着 skill 更容易表达"什么情况下不该用我"这种模糊但重要的判断逻辑。
第三是组合方式不同。函数调用一般是平级的,模型一次挑一个调。而 skills 可以嵌套、可以组合,一个 skill 内部可以调用另一个 skill。这让复杂任务的拆解变得自然很多。
理解这三点差异之后,你在设计自己的 skill 体系时就能少走很多弯路。我见过有人把每个 API 都包装成一个 skill,结果 skill 列表长得吓人,模型反而挑花眼。正确的做法是按任务场景而不是按接口来划分 skill 边界。
3. 从零跑通第一个 skill:环境、目录与加载逻辑
3.1 目录结构怎么摆:一个被低估的关键细节
动手写第一个 skill 之前,先把目录结构定下来。这件事看起来不起眼,但我踩过的坑里有一半跟目录有关。不同平台对 skill 的存放位置要求不一样,但通用的逻辑是:每个 skill 一个独立文件夹,文件夹名就是 skill 名,内部放一个主描述文件加若干资源文件。
一个典型的 skill 目录长这样:
skills/ weekly-report/ SKILL.md template.md scripts/ collect_commits.py table-parser/ SKILL.md examples/ sample.csv主描述文件通常叫SKILL.md或者skill.yaml,里面用特定格式写元数据和指令。我建议用 Markdown 格式,因为可读性最好,模型解析起来也稳。文件夹名用短横线连接的小写英文,别用中文、别用空格、别用大写,这些细节在跨平台的时候能省掉一堆麻烦。
提示:如果你的 skill 需要读取外部文件,路径一定要用相对路径,并且相对于 skill 自己的文件夹。用绝对路径的话,换一台机器或者换个部署环境就直接崩了。
3.2 主描述文件的字段设计:哪些必填、哪些容易写错
主描述文件是整个 skill 的灵魂,字段设计直接决定模型能不能用对。我总结了一份最小可用字段清单,按重要性排序。
| 字段 | 是否必填 | 作用 | 常见错误 |
|---|---|---|---|
| name | 必填 | skill 唯一标识 | 用了中文或空格 |
| description | 必填 | 一句话说明用途 | 写得太泛,如"处理数据" |
| when_to_use | 强烈建议 | 触发场景说明 | 漏写,导致模型不知道何时调用 |
| instructions | 必填 | 详细执行步骤 | 步骤太粗,缺少边界处理 |
| examples | 建议 | 输入输出示例 | 示例和实际格式不一致 |
| resources | 可选 | 附带文件列表 | 路径写错 |
description这个字段我要单独强调一下。很多人写描述的时候喜欢写"这是一个用于处理表格的 skill",这种写法等于没写。好的描述应该包含动作、对象、产出三个要素,比如"读取 CSV 表格,清洗空值和重复行,输出标准化后的 JSON 数据"。这样模型一看就知道什么时候该用它。
when_to_use是另一个容易被忽略但极其重要的字段。它的作用是告诉模型"什么情况下该考虑我,什么情况下不该"。比如一个"生成正式报告"的 skill,可以写"当用户需要输出正式文档时使用;如果只是快速查看数据,不要使用本 skill"。这种负向说明能大幅降低误触发率。
3.3 加载与触发的完整链路:模型是怎么"选中"一个 skill 的
搞清楚了文件结构,接下来要理解模型是怎么在运行时选中某个 skill 的。这条链路我画不出来(也不打算用图表),但可以用文字走一遍。
第一步,系统启动时扫描 skills 目录,把所有 skill 的元数据读进内存,形成一份"能力清单"。这份清单会以精简形式常驻在模型的上下文里。
第二步,用户发来一个请求。模型先做意图判断:这个请求需要用到哪些能力?如果判断不需要任何 skill,直接回答;如果需要,就从清单里匹配最合适的 skill。
第三步,匹配到 skill 之后,系统把该 skill 的完整指令加载进上下文。模型按照指令里的步骤执行,需要用到附带资源时再去读取对应文件。
第四步,执行完成后,skill 的详细内容可以从上下文里释放掉,只保留结果。这样多轮对话下来,上下文不会被历史 skill 撑爆。
这条链路里最容易出问题的是第二步的匹配。如果 skill 描述写得模糊,模型可能匹配错;如果多个 skill 描述重叠,模型可能反复横跳。我的经验是每个 skill 的适用场景要尽量互斥,实在有重叠就在when_to_use里写清楚优先级。
4. 让 skill 真正好用的四个设计原则
4.1 单一职责:一个 skill 只干一件事
这条原则听起来简单,做起来最难。我刚开始写 skill 的时候,总想着"既然都封装了,不如把相关的功能都放进去",结果写出来一个"万能 skill",什么都能干,但什么都干不好。
后来我强迫自己遵守单一职责:一个 skill 只解决一类问题。比如"读取表格"和"清洗表格"就是两个 skill,不要合并。这样做的好处有三个。一是描述可以写得很精准,模型匹配准确率高。二是维护的时候改动范围小,改一个不影响另一个。三是可以自由组合,需要读取加清洗就依次调用两个 skill,需要只读取就调一个。
判断粒度是否合适的标准是:如果你能用一句话说清楚这个 skill 的输入和输出,粒度就对了。如果一句话说不清,说明它承担了太多职责,该拆了。
4.2 指令要写成"给新人的操作手册",而不是"给专家的备忘录"
写 skill 指令的时候,最大的误区是假设模型"应该懂"。我见过不少 skill 的指令写得极其简略,比如"解析输入,提取关键字段,返回结果"。写的人觉得已经说清楚了,但模型拿到之后完全不知道"关键字段"是哪些、"结果"是什么格式。
正确的写法是把它当成给一个聪明但完全不了解你业务的新人写的操作手册。每一步都要明确:输入是什么格式、输出是什么格式、遇到异常怎么处理、有没有需要特别注意的边界情况。
举个例子,一个"提取合同关键信息"的 skill,指令应该写成这样:
- 接收一段合同文本,可能是纯文本或 Markdown 格式
- 依次提取以下字段:甲方名称、乙方名称、合同金额、签署日期、有效期
- 金额统一转换为数字,单位元,保留两位小数
- 日期统一转换为 YYYY-MM-DD 格式
- 如果某个字段在文本中找不到,对应值填 null,不要编造
- 输出为 JSON 格式,字段名用英文小写下划线
这样写出来,模型执行起来几乎不会出错。多花十分钟写清楚指令,能省掉后面几个小时的调试。
4.3 示例要覆盖"正常情况"和"边界情况"
示例是 skill 里性价比最高的部分。一个好的示例能让模型瞬间理解你要什么,比一大段文字描述都管用。但很多人只写正常情况的示例,忽略了边界情况,结果模型一遇到异常输入就抓瞎。
我的做法是每个 skill 至少配三组示例:一组标准输入输出、一组空输入或异常输入、一组格式不规范但常见的输入。比如表格解析 skill,标准示例是一个规整的 CSV;异常示例是一个空文件;不规范示例是一个列数不齐、有合并单元格的表格。三组示例摆出来,模型对"什么算正常、什么算异常、异常怎么处理"就有了完整认知。
4.4 版本管理:skill 也会"过期"
这一点很少有人提,但实际项目中特别重要。skill 不是写完就一劳永逸的,业务逻辑会变、数据格式会变、依赖的接口会变。如果不做版本管理,某天你会发现模型突然开始出错,排查半天才发现是某个 skill 依赖的东西变了。
我的做法是在 skill 目录里加一个CHANGELOG.md,每次修改都记一笔:改了什么、为什么改、影响范围。同时在主描述文件里加一个version字段。这样出问题的时候能快速定位是哪次改动引入的。
注意:修改 skill 指令之后,一定要重新跑一遍回归测试。我吃过这个亏,改了一个看似无关紧要的措辞,结果模型对某个边界情况的处理完全变了。
5. 实战中踩过的坑:从误触发到上下文溢出
5.1 误触发:模型总在不需要的时候调用 skill
这是我最常遇到的问题。表现是:用户只是随口问一句,模型却兴师动众地调用了一个 skill,结果答非所问。
根本原因通常是when_to_use写得太宽泛。比如一个"数据分析"skill,如果只写"当需要分析数据时使用",那模型看到任何带数字的问题都可能触发它。解决办法是在when_to_use里加负向条件:明确写出"以下情况不要使用本 skill",比如"如果用户只是询问数据含义而非要求分析,不要使用"。
另一个原因是多个 skill 的描述高度重叠。我有一次同时装了两个都涉及"文本总结"的 skill,结果模型在两个之间反复横跳,输出质量极不稳定。后来把其中一个的适用范围收窄到"仅处理英文长文档",冲突就消失了。
5.2 上下文溢出:skill 加载太多导致对话崩掉
渐进式加载虽然能缓解上下文压力,但如果你在一个对话里连续调用了十几个 skill,累积的指令和资源还是会撑爆上下文。我遇到过一次,一个复杂任务触发了八个 skill,到第七个的时候模型开始"失忆",前面几步的结果全忘了。
解决办法有两个。一是在 skill 指令里明确写出"执行完成后释放",让系统知道这个 skill 的内容可以清理了。二是把复杂任务拆成多个对话轮次,每轮只处理一个子任务,中间结果落盘保存,下一轮再读进来。这样虽然多几次交互,但稳定性高得多。
5.3 资源路径错误:本地能跑、部署就崩
这个坑我在前面提过,但值得再强调一次。本地开发的时候,skill 目录和运行目录的相对关系是固定的,用相对路径没问题。但部署到容器或者云环境之后,工作目录变了,相对路径的基准也变了,脚本就找不到文件了。
我的解决方案是在 skill 指令里显式声明资源路径的基准,比如"所有资源文件路径均相对于本 skill 所在目录"。同时在加载逻辑里做一次路径解析,把 skill 目录的绝对路径注入进去。这样无论部署到哪里,路径都能正确解析。
5.4 指令歧义:模型"理解"了但理解错了
有一种失败特别隐蔽:模型没有报错,输出了结果,但结果跟你的预期完全不是一回事。这通常是指令有歧义导致的。
我遇到过一个典型案例。一个"提取日期"的 skill,指令写的是"提取文本中的日期"。测试的时候输入"会议定在三月十五号",模型输出了"三月十五号",没问题。但输入"合同有效期三年,从签署日起算"的时候,模型把"三年"也当成了日期提取出来。问题出在"日期"这个词太模糊,模型不知道你要的是具体日期还是时间段。
修复方法是在指令里明确排除项:"只提取具体的年月日,不要提取时间段、期限、频率等表达"。加上这一句之后,准确率立刻上来了。指令里的每一个模糊词,都是未来的一次事故。
6. 把 skills 用出复利:组合、复用与团队协作
6.1 skill 之间的组合调用:1+1 大于 2
单个 skill 的价值有限,真正强大的是组合。我现在的做法是维护一个"基础 skill 库",里面都是原子能力,比如读取文件、格式转换、字段提取、结果校验。然后针对具体业务场景,写"组合 skill",内部按顺序调用基础 skill。
举个例子,一个"合同审查"的组合 skill,内部依次调用:读取文档 skill、提取关键字段 skill、比对标准条款 skill、生成审查报告 skill。每个基础 skill 都可以被其他组合 skill 复用,写一次用十次。这种分层设计让整个体系的维护成本大幅下降。
组合的时候要注意数据格式的统一。基础 skill 之间的输入输出最好用同一种格式,比如都用 JSON。如果 A 输出 CSV、B 要求 JSON,中间就得加转换步骤,既慢又容易出错。
6.2 跨项目复用:怎么让 skill 不绑定具体业务
skill 写多了之后,你会发现有些能力是通用的,比如"读取 Excel""发送邮件""生成图表"。这些 skill 不应该绑定具体业务,而应该做成参数化的通用组件。
我的做法是把业务相关的部分抽成参数。比如"生成图表"这个 skill,不写死"生成销售趋势图",而是接收"数据、图表类型、标题、坐标轴标签"这些参数,由调用方决定具体内容。这样同一个 skill 可以在销售、运营、财务各个场景复用。
参数化的代价是 skill 的指令会复杂一些,需要处理各种参数组合。但相比每个场景重写一遍,这个代价完全值得。
6.3 团队协作:skill 的命名规范与文档约定
一个人写 skill 和一群人写 skill 是两回事。团队协作最大的挑战是命名冲突和风格不一致。我见过一个团队,三个人分别写了三个功能类似的 skill,名字还都叫"data-process",加载的时候直接乱套。
解决办法是建立命名规范。我们团队现在的约定是:skill 名用"领域-动作-对象"三段式,比如sales-extract-fields、hr-generate-report。领域段区分业务线,动作段说明干什么,对象段说明对什么干。这样一眼就能看出 skill 的归属和用途。
文档约定也很重要。我们要求每个 skill 必须包含:用途说明、输入输出格式、依赖项、维护人、最后更新日期。维护人这一项特别关键,skill 出问题的时候能直接找到人,不用在群里挨个问。
6.4 测试与回归:怎么保证改了 A 不影响 B
skill 体系一旦上了规模,改动的影响面就很难预估。改一个基础 skill,可能影响十几个组合 skill。所以回归测试是必须的。
我的做法是给每个 skill 配一组测试用例,包含标准输入和预期输出。每次修改 skill 之后,自动跑一遍所有依赖它的 skill 的测试用例。如果某个用例失败,说明这次改动有副作用,需要重新评估。
这套机制搭起来有点麻烦,但一旦跑通,改 skill 的时候心里就有底了。没有测试的 skill 体系,规模越大越不敢动。
7. 关于 skills 的一些个人体会
写了这么多,最后说几句掏心窝的话。skills 这套机制刚出来的时候,我也觉得"不就是把提示词拆开吗,能有多大差别"。但真正用起来之后,我发现它改变的不只是技术实现,更是思考问题的方式。
以前做智能体,我习惯性地想"怎么把提示词写得更全",恨不得把所有情况都塞进去。现在我会先想"这个任务需要哪些能力,这些能力怎么拆成独立的模块"。这种从"写提示词"到"设计能力"的转变,让整个系统的可维护性上了一个台阶。
另一个体会是,skill 的质量比数量重要得多。我见过有人一口气装了五十个 skill,结果模型挑花眼,准确率还不如只装五个精心设计的。与其追求"什么都能干",不如把几个核心场景做到极致。
如果你刚开始接触 skills,我的建议是从一个最小场景入手,写一个 skill,跑通,然后慢慢加。别一上来就设计大而全的体系,那样大概率会烂尾。等你有三五个 skill 跑顺了,自然就知道该怎么扩展了。
这个领域变化很快,今天好用的方法明天可能就过时了。但底层的思路——把能力模块化、让模型按需加载、用清晰的指令降低歧义——这些是不太会变的。抓住这些,具体工具怎么换都不慌。