1. 从“skills”这个热词说起:它到底是什么
最近几个月,不管是在技术社区还是开发者群聊里,“skills”这个词出现的频率高得离谱。有人把它当成一个工具,有人把它当成一套规范,还有人把它当成一种新的开发范式。我一开始也懵,直到自己动手把整套东西跑通、拆开看了一遍,才明白它到底在解决什么问题。
简单来说,skills 是一套让 AI 智能体(Agent)具备可复用、可组合、可分发能力的结构化技能包。你可以把它理解成给 AI 装的“插件”或者“技能卡”——每一张卡定义了一件事怎么做、需要什么输入、产出什么结果、依赖哪些工具。Agent 拿到这些技能卡之后,就能在特定场景下自动调用,而不需要每次从零写提示词。
它解决的问题非常实际:过去我们让 AI 干活,靠的是一大段一大段的提示词,写起来累、维护起来更累,换一个模型或者换一个场景就得重写。skills 把这种“一次性提示词”变成了“可版本管理的技能模块”,谁写的、什么版本、依赖什么、怎么测试,全都清清楚楚。
这篇文章适合谁看?如果你是前端开发者、AI 应用开发者、或者正在折腾 Agent 工作流的人,那这篇内容基本就是给你写的。我会从设计思路、核心结构、实操步骤、常见坑四个维度,把 skills 这套东西彻底讲透。哪怕你之前只听说过这个词,看完也能自己动手写一个能跑的 skill。
2. skills 的整体设计与核心思路拆解
2.1 为什么需要 skills:从“提示词堆砌”到“技能模块化”
我先说说自己踩过的坑。早期做 Agent 项目的时候,所有的能力都塞在一个巨大的系统提示词里,今天加一个“查天气”,明天加一个“生成周报”,提示词越写越长,最后长到模型自己都抓不住重点。更麻烦的是,团队里三个人维护同一份提示词,改一处冲突三处,根本没法协作。
skills 的出现本质上是为了解决三个问题:复用、组合、分发。
复用是指同一个技能可以在不同项目、不同 Agent 之间直接拿来用,不用重写。组合是指多个技能可以像搭积木一样拼起来,完成复杂任务。分发是指技能可以打包、发布、安装,就像 npm 包一样。
这三个问题一旦解决,Agent 的开发方式就变了——从“写提示词”变成“选技能、配技能、测技能”。这是一个范式级别的转变,也是为什么 skills 这个词能火起来的原因。
2.2 skills 的核心结构:一个技能包里到底装了什么
我拆过好几个不同来源的 skill 包,结构大同小异,核心就三样东西:
- 元数据(metadata):技能的名字、版本、描述、作者、依赖项。这部分决定了技能能不能被正确索引和加载。
- 指令(instructions):告诉 Agent 这个技能是干什么的、什么时候用、怎么用。通常是一段结构化的自然语言描述,但比普通提示词更规范。
- 工具声明(tool declarations):这个技能需要调用哪些外部工具或 API,参数是什么,返回什么。这部分是可选的,但如果技能需要跟外部系统交互,就必须写清楚。
有些实现还会加一个测试用例(test cases)目录,用来验证技能在给定输入下能否产出预期输出。这个设计非常关键,后面讲实操的时候我会详细说。
用一句话概括:skill = 元数据 + 指令 + 工具声明 + 测试用例。这四样东西凑齐了,就是一个完整、可分发、可验证的技能单元。
2.3 跟传统提示词工程的区别在哪
很多人会问:这不就是提示词模板吗?有什么区别?
区别大了。提示词模板是“一段文本”,skill 是“一个有生命周期的软件制品”。具体来说:
| 维度 | 传统提示词 | skills |
|---|---|---|
| 复用方式 | 复制粘贴 | 安装引用 |
| 版本管理 | 靠文件名 | 语义化版本 |
| 依赖管理 | 无 | 显式声明 |
| 测试 | 靠人肉试 | 自动化用例 |
| 分发 | 发文档 | 包管理器 |
| 组合 | 手动拼接 | 自动编排 |
这个对比表是我自己在项目里总结的,不一定全面,但能说明核心差异。skills 把提示词从“文本”升级成了“制品”,这是本质区别。
3. 核心细节解析与实操要点
3.1 一个最小可用 skill 的目录结构
我拿自己写的一个“生成周报”skill 举例,目录结构是这样的:
weekly-report-skill/ ├── skill.json ├── instructions.md ├── tools.json └── tests/ ├── case-01.json └── case-02.jsonskill.json是元数据,长这样:
{ "name": "weekly-report", "version": "1.0.0", "description": "根据本周的提交记录和任务列表生成结构化周报", "author": "your-name", "dependencies": [], "entry": "instructions.md" }instructions.md是指令,核心是告诉 Agent 什么时候触发、怎么执行:
# 周报生成技能 ## 触发条件 当用户提到"周报""本周总结""weekly report"时激活。 ## 执行步骤 1. 收集本周的 git commit 记录 2. 收集任务管理系统中的已完成任务 3. 按"完成事项/进行中/下周计划"三段式组织 4. 输出 Markdown 格式 ## 输出格式 - 完成事项:列表,每条不超过 50 字 - 进行中:列表,标注进度百分比 - 下周计划:列表,按优先级排序tools.json声明依赖:
{ "tools": [ { "name": "git-log", "type": "shell", "command": "git log --since='7 days ago' --oneline" }, { "name": "task-query", "type": "http", "endpoint": "/api/tasks/completed" } ] }这个结构看起来简单,但每一部分都有讲究。下面我逐个拆。
3.2 元数据怎么写才不容易出问题
元数据里最容易踩坑的是版本号和依赖声明。
版本号我建议严格遵循语义化版本(semver),也就是主版本.次版本.修订号。为什么?因为 skill 被别的项目引用之后,你改一个指令可能就会影响下游。主版本变了说明有破坏性变更,下游需要主动升级;次版本变了说明加了新功能但兼容;修订号变了说明只是修了 bug。
依赖声明这块,我踩过一个坑:早期我写了一个 skill 依赖另一个 skill,但没写清楚版本范围,结果上游 skill 升级之后我的 skill 直接挂了。后来我学乖了,依赖一定要写清楚范围,比如">=1.0.0 <2.0.0",这样上游发 1.x 的时候我能自动拿到,发 2.0 的时候我会被拦住,逼着我先测试再升级。
提示:元数据里的 description 不要写得太泛,比如“一个有用的技能”这种等于没写。要写清楚“在什么场景下解决什么问题”,这样 Agent 在检索技能的时候才能准确匹配。
3.3 指令设计的三个关键原则
指令是 skill 的灵魂,写得好不好直接决定技能能不能用。我总结了三个原则:
第一,触发条件要具体。不要写“当用户需要帮助时”,要写“当用户提到 X、Y、Z 关键词,或者当前上下文包含 A 条件时”。触发条件越具体,误触发的概率越低。
第二,执行步骤要可执行。每一步都应该是 Agent 能直接做的动作,而不是模糊的描述。比如“分析一下数据”就不如“读取 data.csv,按日期分组,计算每日均值”来得明确。
第三,输出格式要固定。输出格式固定了,下游才能稳定消费。我一般会用 JSON Schema 或者 Markdown 模板来约束输出,这样即使模型换了,输出结构也不会乱。
这三条听起来简单,但实际写的时候很容易忘。我的做法是写完指令之后,自己扮演 Agent 走一遍,看看每一步是不是真的能执行。走不通的地方就是需要改的地方。
3.4 工具声明的参数设计
工具声明这块,核心是把“技能需要什么能力”和“能力怎么实现”解耦。
举个例子,一个“发送通知”的技能,它需要的能力是“发消息”,但具体是发邮件、发 Slack、还是发短信,不应该写死在技能里。技能只声明“我需要一个 send-message 工具,参数是 recipient 和 content”,具体用哪个实现,由运行环境决定。
这样做的好处是技能可以跨环境复用。同一个技能,在 A 公司用邮件发,在 B 公司用内部 IM 发,技能本身不用改。
参数设计要注意几点:
- 参数名要语义化,
recipient比to好,content比body好 - 必填参数和可选参数要分清
- 参数类型要明确,字符串、数字、布尔、数组、对象,别含糊
- 如果有默认值,写清楚
我见过有人把工具声明写成一大坨 JSON,几百行,根本没法维护。我的建议是能拆就拆,一个工具只做一件事,参数不超过五个。超过五个就说明这个工具职责太重了,该拆。
4. 实操过程与核心环节实现
4.1 环境准备:从零搭建 skill 开发环境
先说环境。我目前用的是 Node.js 生态,因为大部分 skill 工具链都是 npm 包,装起来方便。
第一步,确认 Node 版本。我实测下来 Node 18 以上比较稳,16 也能跑但有些新特性不支持。
node -v # 建议 v18.x 或 v20.x第二步,初始化项目:
mkdir my-first-skill && cd my-first-skill npm init -y第三步,安装 skill 开发工具链。不同平台的工具名不一样,我用的是一个通用的 CLI:
npm install -D @skill-cli/core第四步,初始化 skill 模板:
npx skill init这个命令会生成前面说的目录结构,你只需要往里填内容就行。
注意:如果你在公司内网环境,npm 源可能需要配置。我一般会先
npm config get registry看一下当前源,如果是内网源,确认一下有没有对应的包。没有的话找运维同步一下,别硬装。
4.2 写第一个 skill:从需求到可运行
我拿一个真实需求来演示:自动整理会议纪要。
需求是这样的:用户丢进来一段会议录音转写文本,skill 需要提取出“决议事项”“待办任务”“负责人”“截止时间”四个字段,输出成结构化 JSON。
第一步,写元数据:
{ "name": "meeting-minutes", "version": "1.0.0", "description": "从会议转写文本中提取决议、待办、负责人和截止时间", "author": "your-name", "dependencies": [], "entry": "instructions.md" }第二步,写指令:
# 会议纪要提取技能 ## 触发条件 当输入包含会议转写文本,或用户提到"整理纪要""提取待办"时激活。 ## 执行步骤 1. 通读全文,识别会议中的决议性语句 2. 提取所有待办任务,每条任务包含:任务描述、负责人、截止时间 3. 如果某条待办缺少负责人或截止时间,标记为"待确认" 4. 按以下 JSON 格式输出 ## 输出格式 { "decisions": ["决议1", "决议2"], "todos": [ { "task": "任务描述", "owner": "负责人或待确认", "deadline": "截止时间或待确认" } ] }第三步,写测试用例:
{ "input": "本次会议决定采用方案A。张三负责在下周五前完成接口联调。李四跟进用户反馈,时间待定。", "expected": { "decisions": ["采用方案A"], "todos": [ {"task": "完成接口联调", "owner": "张三", "deadline": "下周五"}, {"task": "跟进用户反馈", "owner": "李四", "deadline": "待确认"} ] } }第四步,跑测试:
npx skill test如果输出跟 expected 一致,说明技能基本可用。不一致的话,看差异在哪,回去改指令。
4.3 参数计算与选择:怎么定技能的粒度
技能粒度是个很微妙的问题。太粗,一个技能干十件事,复用性差;太细,一个技能只干一件事,组合起来又太碎。
我的经验是:一个技能对应一个“用户可感知的完整任务”。
什么叫用户可感知的完整任务?比如“生成周报”是一个完整任务,“收集 git 记录”就不是,它只是周报的一个子步骤。子步骤应该放在技能内部的执行步骤里,而不是单独拆成一个技能。
判断标准很简单:如果这个技能单独拿出来给用户用,用户会觉得“这是个有用的功能”,那粒度就对了。如果用户觉得“这只是个中间步骤”,那就该合并到上层技能里。
我一般会把技能控制在3 到 7 个执行步骤之间。少于 3 步,说明太简单,可能不值得单独成技能;多于 7 步,说明太复杂,该拆了。
4.4 技能组合:多个 skill 怎么串起来
单个技能能做的事有限,真正有价值的是组合。我拿一个实际场景演示:自动生成项目周报并发送。
这个场景需要三个技能:
git-summary:收集本周提交记录,生成摘要weekly-report:把摘要和任务列表组织成周报send-notification:把周报发出去
组合方式有两种:
串行组合:前一个技能的输出作为后一个技能的输入。
{ "pipeline": [ {"skill": "git-summary", "output": "summary"}, {"skill": "weekly-report", "input": "summary", "output": "report"}, {"skill": "send-notification", "input": "report"} ] }条件组合:根据条件选择不同的技能分支。
{ "router": { "condition": "input.type", "branches": { "weekly": "weekly-report", "daily": "daily-report" } } }串行组合适合流程固定的场景,条件组合适合需要分支的场景。实际项目里两种经常混用。
提示:组合的时候一定要注意数据格式的兼容性。前一个技能输出 JSON,后一个技能却期望 Markdown,这种不匹配是组合失败的最常见原因。我的做法是在每个技能的元数据里明确声明输入输出格式,组合之前先做一次格式校验。
5. 常见问题与排查技巧实录
5.1 技能不触发怎么办
这是最高频的问题。技能写好了,但 Agent 就是不用。排查思路按顺序来:
第一,检查触发条件。把触发条件里的关键词拿出来,看看用户输入里有没有。没有的话,要么改触发条件,要么确认用户输入是否真的该触发这个技能。
第二,检查技能是否被正确加载。有些平台需要显式注册技能,光有文件不够。看一下加载日志,确认技能在列表里。
第三,检查优先级。如果多个技能都匹配当前输入,Agent 可能选了别的。这时候需要调整技能的优先级或者触发条件的特异性。
我整理了一个速查表:
| 现象 | 可能原因 | 解决方法 |
|---|---|---|
| 完全不触发 | 技能未加载 | 检查注册配置 |
| 偶尔触发 | 触发条件太泛 | 收窄关键词 |
| 触发但走错分支 | 优先级冲突 | 调整优先级 |
| 触发后报错 | 依赖缺失 | 检查 dependencies |
5.2 输出格式不稳定的处理
模型输出格式飘,是另一个高频问题。明明指令里写了输出 JSON,结果模型给你输出一段带解释的文字。
我的处理办法有三层:
第一层,指令里强化格式约束。不要只说“输出 JSON”,要说“只输出 JSON,不要有任何其他文字,不要用代码块包裹”。越明确越好。
第二层,加格式校验。技能执行完之后,跑一个校验步骤,不符合格式就重试。重试的时候把错误信息带上,模型通常第二次就能改对。
第三层,用结构化输出能力。如果模型支持 JSON mode 或者 function calling,优先用这些能力,比纯提示词约束靠谱得多。
这三层下来,格式稳定性基本能到 99% 以上。剩下 1% 是模型本身的随机性,接受就好。
5.3 依赖冲突与版本管理
技能多了之后,依赖冲突几乎不可避免。A 技能依赖工具 X 的 1.0,B 技能依赖 X 的 2.0,两个技能一起用就炸了。
我的做法是:
- 技能依赖尽量声明宽范围,比如
>=1.0.0 <3.0.0,给运行环境留出选择空间 - 运行环境统一管理工具版本,技能不直接锁定版本
- 如果实在冲突,把冲突的技能隔离到不同的执行环境里
版本管理这块,我强烈建议用 lock 文件。每次安装技能的时候生成一份 lock,记录所有技能和依赖的确切版本。这样换机器、换环境的时候,装出来的东西完全一致,不会出现“我这儿能跑你那儿跑不了”的情况。
5.4 性能优化:技能执行太慢怎么办
技能执行慢,通常有三个原因:
一是技能粒度太细,组合层数太多。十个技能串起来,每个都要加载、解析、执行,开销叠加起来很可观。这时候该合并的合并。
二是工具调用太频繁。一个技能里调了十次外部 API,每次都等网络往返。能批量就批量,能缓存就缓存。
三是指令太长,模型处理慢。指令不是越长越好,冗余的描述会拖慢推理。我一般会把指令控制在 500 字以内,超过就拆。
实测下来,一个设计良好的技能,从触发到输出,延迟应该控制在 2 秒以内。超过 5 秒就要查原因了。
5.5 调试技巧:怎么快速定位问题
调试技能的时候,我一般会开三个东西:
- 详细日志:记录技能加载、触发、执行、输出的每一步
- 中间产物:每个步骤的输出都存下来,方便对比
- 回放能力:把一次失败的执行录下来,改完技能之后回放,看是否修复
这三个东西搭起来之后,调试效率能提升好几倍。尤其是回放能力,改一次跑一次,比手动复现快多了。
注意:日志里不要记录敏感数据。技能执行过程中可能会碰到用户隐私、密钥之类的信息,记录之前先脱敏。这个坑我踩过,后来加了脱敏层才解决。
6. 技能分发与团队协作的实操经验
6.1 技能打包与发布
技能写好了,怎么分享给团队?最土的办法是发文件夹,但这样没法管理版本,也没法追踪谁用了哪个版本。
正规做法是打包发布。打包就是把技能目录打成一个压缩包,附带元数据和校验和。发布就是把这个包推到技能仓库里。
npx skill pack # 生成 my-skill-1.0.0.tar.gz npx skill publish # 推送到配置的仓库发布之后,别人就可以通过名字和版本号安装:
npx skill install my-skill@1.0.0这套流程跟 npm 几乎一样,用过 npm 的人上手很快。
6.2 团队协作中的技能管理规范
团队用技能,最容易乱的是命名和版本。我建议定几条规矩:
- 技能名用
领域-功能格式,比如report-weekly、notify-email - 版本严格 semver,破坏性变更必须升主版本
- 每个技能必须有 owner,出了问题找得到人
- 技能变更要走 review,不能直接推
这几条看起来是流程问题,但实际执行下来,能避免 80% 的协作事故。我见过太多因为技能改名导致下游全挂的案例,都是因为没规范。
6.3 技能市场与生态现状
目前技能的分发渠道主要有几类:官方市场、社区仓库、私有仓库。
官方市场的技能质量相对有保障,但数量有限。社区仓库数量多,但质量参差不齐,用之前一定要看测试用例和更新记录。私有仓库适合公司内部,安全可控。
我选技能的时候会看几个指标:最近更新时间、测试覆盖率、issue 响应速度、依赖数量。依赖越少越好,更新越勤越好,测试越全越好。
6.4 从个人使用到团队落地的路径
最后说说落地路径。我的建议是分三步:
第一步,个人先用起来。自己写几个技能,解决自己的重复劳动。这一步的目的是熟悉技能的设计和调试。
第二步,小范围共享。找两三个同事,把技能分享给他们用,收集反馈。这一步的目的是验证技能的通用性。
第三步,团队推广。建立技能仓库,定规范,做培训。这一步的目的是规模化。
每一步之间不要跳,跳了容易翻车。我见过有人第一步还没走稳就推全团队,结果技能质量不过关,大家用了一次就不用了,反而打击了积极性。
7. 我个人的一些实操体会
写了这么多,最后分享几个我自己踩坑踩出来的经验。
技能不是越多越好。我一开始特别兴奋,什么都要写成技能,结果技能列表里几十个,自己都记不住哪个是哪个。后来砍到十几个核心技能,反而用得更顺。技能的价值在于被用,不在于被写。
测试用例比指令更重要。指令是给模型看的,测试用例是给你自己看的。指令写得再漂亮,测试不过就是白搭。我现在写技能,先写测试用例,再写指令,这样目标更明确。
版本管理要趁早。我第一个技能没管版本,改了十几次之后自己都不知道哪个版本是哪个。后来全部重来,加上版本管理,才理清楚。这个教训挺深刻的。
别追求完美,先跑起来。技能这东西,跑起来比设计完美重要。我见过有人花两周设计一个技能架构,结果一行代码没写。先写个能跑的,再迭代,比什么都强。
关注生态,但别追新。技能生态变化很快,新工具新规范层出不穷。我的做法是关注,但不急着追。等一个东西稳定了、有社区验证了,再上手。追新追得太紧,容易变成小白鼠。
这套东西我用了几个月,最大的感受是:它把 AI 应用开发从“手工作坊”推向了“流水线”。以前每个项目都要重新写提示词,现在技能可以复用、可以组合、可以测试。这个转变带来的效率提升是实打实的。
如果你还没开始用,建议从一个小技能入手,比如“整理会议纪要”或者“生成周报”。跑通一个,后面的就顺了。