这两年做大模型应用,我最大的体会是:Prompt 写得再漂亮,顶多算「会用 AI」;真正拉开差距的,是把 AI 能力像代码一样拆成可安装、可复用、可迭代的组件。这个组件,现在行业里管它叫 Skill。Skill 的封装与复用,恰恰是很多人从「玩 AI」跨到「用 AI 做事」的那道门槛。
这篇文章不聊概念对不对、谁家定义更标准,我只想把自己在 Agent 工程里做 Skill 设计、封装、调试和复用的一整套经验摊开讲。适合正在做 AI Agent、自动化工作流,或者已经在用各类 Skill 框架却被调用率低、效果飘忽不定的问题困扰的开发者和产品同学。读完之后,你应该能独立设计出一个真正会被模型主动调用的 Skill,并且知道怎么让它跨项目、跨平台持续生效。
1. 先说清楚:Skill 到底封装的是什么
1.1 从一段 Prompt 到一份可复用资产
最早我们做 AI 提效,最常见的形式是收藏一堆 Prompt。写周报一个 Prompt,做代码审查一个 Prompt,提炼会议纪要又一个 Prompt。时间一长问题就来了:Prompt 散落在各个文档、聊天记录、笔记软件里,真正要用的时候根本想不起来哪个版本最新。更尴尬的是,同一个 Prompt 在不同模型、不同上下文窗口下表现完全不一样,今天好用明天就抽风。
Skill 解决的正是这件事。它不是简单把你的 Prompt 存起来,而是把「触发条件 + 执行指令 + 配套脚本/资源 + 边界约束」打包成一个结构化的目录或文件,让 Agent 能在合适的时候自动发现、加载并执行。换句话说,Prompt 是写给模型看的即兴台词,Skill 是给 AI 装上的标准化技能模块,二者最大的差别在于有没有「可管理性」。
我做过的第一个 Skill 是自动生成项目周报。起初就是一段很长的 Prompt,每次都要复制粘贴、替换项目名。后来我把这段 Prompt 拆成三部分:一段描述何时应该调用这个 Skill 的说明,一段要求模型遵循的输出格式与思考流程,一个读取 Git 提交记录并生成结构化摘要的 Python 脚本。从那时起,模型会在我提到「周报」「weekly report」时主动去读仓库历史,然后按照固定模板输出。同样的功能,从「复制粘贴 Prompt」变成了「安装一个能力」,这个转变是我理解 Skill 价值的起点。
1.2 Skill、Prompt、Tool、Agent 四者的边界
很多人分不清 Skill、Prompt、Tool、Agent 这几个概念,这也是热搜词里「skill和agent的区别」常年被搜的原因。我用一个做饭的类比来说明。
Prompt 是菜谱,它告诉模型「按这个步骤做」;Tool 是厨具,模型自己没法切菜,必须调用外部工具才能完成特定动作(比如查数据库、发请求);Skill 是一道菜的完整做法加配套工具,它可能包含多个步骤、多个工具调用,还有模型需要遵循的判断规则;Agent 则是那个根据客人需求决定今天做什么菜、怎么做、做完怎么上桌的角色,它负责调度 Skill 和 Tool。
| 层次 | 类比 | 可复用性 | 典型载体 |
|---|---|---|---|
| Prompt | 菜谱 | 低,依赖复制粘贴 | 文本 |
| Tool | 厨具 | 中,单点能力 | API、函数 |
| Skill | 标准菜品 | 高,整体打包 | SKILL.md + 脚本 + 资源 |
| Agent | 厨师 | 取决于内部设计 | 编排逻辑 + 多 Skill 调用 |
理解这个边界有个实际好处:你知道该在哪个层面解决问题。如果模型回答质量差但动作正确,问题多半出在 Prompt/指令部分,改 Skill 里的说明就行;如果模型说想做某事却执行不了,那是缺 Tool,得补工具;如果你发现一个 Skill 要在多个场景里被反复调度,才需要考虑是不是升级成 Agent 或工作流的一部分。我见过不少团队一上来就搭 Agent,结果里面只有一堆 Prompt 和两个工具,调度逻辑全写在代码里,最后复用性极差——这就是层次没分清。
1.3 为什么「封装」比「提示词工程」更高一层
单纯做提示词工程,经验是沉淀在个人脑子里的。我做提示词调优那阵子,每换一个项目就要重新适应上下文,每次都从零开始试。封装成 Skill 之后,整个能力变成了一个可交付的实体,它有文件名、有版本、有配套脚本,甚至在团队里可以走代码评审流程。这种从「经验」到「资产」的跃迁,本质上和软件工程里从「复制粘贴代码」到「抽象成库」是同一件事。
所以封装的核心目的不是让 Prompt 更好写,而是让能力可以被安装、被卸载、被测试、被改进。你的 Skill 目录就是 AI 时代的方法论仓库,沉淀下来的不是零散经验,而是经过验证的、可随时调用的能力包。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. Skill 怎么设计才不算白封装
2.1 拆需求:什么样的事情值得做成 Skill
不是所有 AI 用法都值得封装。我在实践中总结了一个判断标准:一件事是否满足「高频」「稳定」「有边界」三个条件。高频,指的是你每个星期都会让 AI 干这件事,比如写周报、做代码审查、格式化日志;稳定,指的是这件事的输入输出比较固定,不会每回都换一套完全不同的玩法;有边界,指的是可以清楚描述「什么情况算这件事」,模型能够判断该不该出手。
反例我也踩过。有一段时间我想把「帮忙想点子」做成 Skill,后来发现这个需求太开放,模型要么不知道该不该调用,要么调用了但输出天马行空,封装之后效果反而比直接对话差。这类创造性任务更适合靠上下文记忆和模型能力直接完成,不需要 Skill 硬性约束。
我的建议是:先拿一周时间记录你让 AI 做的事情,然后把出现三次以上的任务列为 Skill 候选。再从中挑选输入输出相对明确的开始封装,不要一上来就挑战高难度场景。
2.2 SKILL.md 是灵魂,不是摆设
绝大多数 Skill 框架的核心是一个 Markdown 文件,通常叫 SKILL.md。很多人写这个文件时把它当成普通 Prompt 写,这是最大的误区。SKILL.md 承担的是双重职责:对模型来说它是指令集,对框架来说它是元数据索引。因此它的结构必须同时满足两种读者的需求。
我常用的 SKILL.md 结构包含四块:第一块是 YAML Frontmatter,写明 name、description,description 要写得极其克制,只描述「能干什么」和「什么时候用」,不要写「这是一个强大的工具」这类空洞修饰;第二块是 Instructions,写模型拿到这个 Skill 后要遵守的步骤和判断规则;第三块是 Execution,说明是否需要运行脚本、脚本路径是什么、参数怎么传;第四块是 Examples,给一两个输入输出示例,帮助模型理解调用时机。
注意,Instructions 里不要堆砌「你要仔细」「你必须认真」这种没有信息量的词。模型对这类指令不敏感,真正有用的是具体步骤和判断条件。比如「提取 Git 提交记录前 50 条,按日期分组,对每条提交判断是否涉及需求变更」,这比「你要全面分析提交记录」有效得多。
2.3 命名与检索:让小模型也能准确命中
Skill 的触发通常由模型自行判断,而判断依据主要是 name 和 description。我一开始不重视这个,给 Skill 起了个挺文艺的名字,结果调用率低得可怜。后来我把名字改成《weekly-report-generator》,description 写成「Generate weekly work reports from git commit history and todo list」,调用率立刻上来了。
原因很简单:模型在做意图识别时,靠的是关键词和语义相似度。你的 Skill 描述越贴近用户自然表达,越容易被命中。日常对话里用户不会说「请你调用 git 提交分析模块」,他们会说「帮我写个周报」。所以 description 里要包含这类常见口语变体,甚至可以把同义词写进去,比如「周报」「weekly report」「本周总结」。
我还建议在 description 里标注 Skill 的「不适合使用」场景,减少误触发。比如某个 Skill 是针对 Java 代码审查的,就写清楚「仅适用于 Java 项目,不用于 Python」。这个信息看似多余,实际上能显著降低模型在其他场景乱调用的概率。
3. 实操:把一个高频场景封装成 Skill
3.1 场景定义与目录结构
选一个我最拿手的例子:日志异常分析。运维团队每次排查线上问题,都要从一堆日志里找异常堆栈、统计错误码、关联时间线。这事高频、稳定、边界清晰,非常适合做成 Skill。
我先定义一个标准目录结构,这也是多数 Skill 框架约定的格式:
code复制log-anomaly-analyzer/
├── SKILL.md
├── scripts/
│ ├── parse_errors.py
│ └── summarize.py
└── assets/
└── error_patterns.yaml
SKILL.md 放最外层,scripts 放可执行脚本,assets 放辅助参考数据。目录名用连字符小写命名,不要用空格和中文。这个习惯源自 npm 的包名规范,好处是跨平台、无转义问题、方便做版本管理。
3.2 编写 SKILL.md 的技巧
这个 Skill 的 SKILL.md 我是这样写的,YAML Frontmatter 是:
code复制---
name: log-anomaly-analyzer
description: 分析日志文件中的异常与错误堆栈,统计错误码频率,输出时间线摘要。适用于排查线上故障、查看 error log、分析 exception 场景。不适用于数据报表生成。
---
Instructions 部分我写得很具体,包含三件事:先让模型判断日志文件路径是否存在、文件编码是否为 UTF-8;再要求模型运行 scripts/parse_errors.py 提取结构化异常信息;最后规定输出格式必须包含错误码 Top5、首次出现时间、影响范围分析。
这里有个经验:Scripts 要刻意写成「不会抛异常」的稳健版本。模型执行脚本时如果遇到报错,通常不会自己去修复代码,而是直接把这个 Skill 标记为失败。所以我在脚本里加了大量容错处理——文件不存在时返回空列表、编码错误时尝试 gbk 读取、超时强制退出。虽然脚本可读性下降了一点,但实测模型对这个 Skill 的信任度明显提高。
3.3 脚本与资源的落位
scripts/parse_errors.py 是我用 Python 写的一个解析器,核心逻辑是正则匹配常见异常关键字。这里我建议不要追求一个脚本解决所有问题,脚本越简单,模型越容易理解和调用。我把解析逻辑和汇总逻辑拆成两个脚本,就是为了让模型在只需要统计时少跑一步。
assets/error_patterns.yaml 存放业务自定义的错误码映射表。这样做的好处是,后续要扩展新的错误码,不需要改 SKILL.md 和脚本,只要编辑 YAML 文件。这个设计思路和软件工程里的配置与代码分离一脉相承,当 Skill 面对不同项目时,只要替换 assets 资源就能复用。
3.4 加载与验证:不要相信一次就成功
封装完成后,验证阶段同样重要。我习惯用一套自测用例:
- 输入一个包含 1000 条正常日志、10 条异常堆栈的文件,看模型是否准确识别异常比例。
- 输入一个空文件,看模型是否误报。
- 输入一个 GBK 编码文件,看模型是否正确转换。
- 输入不带「日志」「异常」关键词的模糊请求,看模型是否误触发。
前两类多数 Skill 都能过,第三类能暴露脚本容错问题,第四类最能检验 description 写得好不好。我亲眼见过一个团队把 Skill 描述写得太泛,结果用户问「帮我看一眼这个文件」时,模型直接调用了日志分析 Skill,对着一份合同文件输出「未发现异常堆栈」。这类问题靠改 Prompt 修不好,必须回头拆 description。
4. 复用这件事,藏着很多细节
4.1 版本管理与跨项目搬运
Skill 本质上是代码加文档,所以版本管理我没想过用别的方案,直接走 Git。每个 Skill 一个仓库,或者放在 monorepo 的 skills/ 目录下,打 tag 对应版本号。我见过有人把 Skill 文件塞进网盘里传着用,短期内也许省事,但一旦多人协作,版本冲突会把你折磨到怀疑人生。
跨项目搬运时要注意资源和指令的绑定关系。直接拷贝目录是最粗的方式,更好的方式是把可复用部分和项目特定部分拆开。还是用日志分析那个例子:脚本和 SKILL.md 是通用部分,error_patterns.yaml 则要随项目换。我会在 SKILL.md 里用变量占位,比如 {{ERROR_PATTERNS_PATH}},加载时由外层环境注入路径。这样同一个 Skill 就能适配多个项目,而不用复制多份。
4.2 Skill 的组合与依赖
Skill 和 Skill 之间也能组合。我做过一个「发布检查」的 Skill,内部依赖三个子 Skill:代码审查 Skill、测试覆盖分析 Skill、日志异常预检 Skill。组合方式不是在一个 Skill 里把另一个的脚本调用一遍,而是让主 Skill 的 Instructions 里明确写出「先调用 code-review-skill 获取审查意见,再调用 test-coverage-skill 获取覆盖率,最后汇总」。
这里需要注意:组合会让上下文变长,如果没有好的上下文预算管理,模型容易漏步骤。我的做法是在 Instructions 里用列表强制顺序,并且要求每完成一步就输出一个简短标记。这样即使中间某一步失败,模型也能继续后面的流程,而不是整个 Skill 断掉。
4.3 团队内怎么沉淀
Skill 真正发挥价值是在团队层面。我自己维护了一个 skills 目录,新成员入职第一件事就是翻一遍可用 Skill 清单。但这需要配套机制,不然很快会变成一堆没人维护的死代码。
我建议团队里至少做三件事:第一,每个 Skill 必须有明确的 owner,负责响应改进需求和修改描述;第二,Skill 变更要记录 changelog,至少说明改了哪部分、为什么改、影响哪些场景;第三,定期抽样测试 Skill 调用情况,发现调用率低的就先审查 description 再审查质量。这一套跑下来,Skill 才会从个人工具变成团队资产。
5. 常见问题与避坑清单
5.1 描述写得太抽象,模型根本不会调用
这是最高频的问题,没有之一。现象是 Skill 文件写得很认真,但实际对话中模型几乎不触发它。排查思路很简单:把 description 拿给一个不了解你项目的同事看,问他能不能说出这个 Skill 什么时候能用。如果对方答不上来,模型大概率也判断不准。
修法也很直接,把描述改成场景化表达。不要写「提供全面的项目分析」,要写「当用户提到代码评审、pull request 检查、commit 质量时使用」。描述里可以带上高频触发词,但注意不要为了命中率堆砌不相关的词,否则会出现前面提到的误调用。
5.2 Skill 之间职责重叠
团队里人一多,Skill 就容易重叠。比如一个叫 weekly-report 的 Skill 和一个叫 git-summary 的 Skill,功能高度重合。模型面对两个相似的候选时,行为可能不稳定,有时调用这个,有时调用那个,输出格式自然对不上。
我的处理原则是:同类能力只保留一个入口,差异通过参数或配置表达。如果两个 Skill 确实各有侧重,那就必须在 description 里明确区分适用边界,比如一个写「适用于敏捷项目周报」,另一个写「适用于 Git 仓库整体概览分析」。你要是发现边界很难说清楚,大概率说明这两个 Skill 该合并了。
5.3 安全边界:别把敏感逻辑塞进 Skill
Skill 里如果包含可执行脚本,一定要做最小权限设计。模型可能会在一个错误的时间调用你的 Skill,也可能被用户诱导去操作某些危险动作。我在内部实践时有一条硬性规则:凡是脚本里有写操作、删除操作、网络请求的,必须在 SKILL.md 里加醒目警告,并且在脚本里做双重确认。
比如日志分析脚本如果提供「自动清理历史日志」功能,就必须在删除前检查目录名、文件年龄、文件数量,并且默认输出待删除清单而不是直接删。很多 Skill 框架现在还支持权限声明,尽量把权限最小化,只给完成既定任务必需的权限。
5.4 平台差异:换基座模型之后要重新校准
同一个 Skill 在 GPT 系和 Claude 系模型上的表现可能差很多。这不是玄学,是因为不同模型对 Markdown 指令的理解粒度不一样。我的经验是:Claude 系对长指令和结构化步骤反应更好,GPT 系更吃 description 的关键词匹配。所以你在一个平台上调好的 Skill,搬到另一个平台上最好重新跑一遍验证用例。
如果你用 Spring AI Skill 这类 Java 生态的封装方案,还要注意框架层的差异。框架可能自动注入额外的 context,或者对脚本执行方式有限制。我的建议是先用最简单的方式跑通端到端,再去调整指令细节,不要一上来就追求全面。
5.5 什么时候不应该用 Skill
最后说点反共识的东西:Skill 不是越多越好。我见过有人给聊天机器人装了五十多个 Skill,结果模型每次决策都负担极重,响应又慢又容易选错。 Skill 的触发是一种注意力开销,数量越多,单个 Skill 被准确命中的概率越低。
我的经验是:个人项目控制在 5 个以内,团队级项目控制在 15 个以内。超出这个量级,优先考虑做 Skill 分组或构建上层 Agent 来分流。宁可少而精,不要多而杂。真正优秀的 Skill 设计,不是把所有事情都封装起来,而是知道哪些事情不值得封装。
回到开头那句话,Skill 的封装与复用,本质上是把 AI 能力变成软件工程的一部分。我在实际落地中最大的感受是:它不像写 Prompt 那样可以靠灵感取胜,更像设计 API 接口——需要克制、需要边界、需要维护。每次我把一个新 Skill 打磨到可以被团队里任何人稳定复用时,那种感觉和当年把一个模块重构到可以直接发布 npm 包是一样的。如果你正在做 Agent 开发,我建议从这周开始,挑一个你重复过三次以上的任务,试着把它封装成第一个 Skill,然后跑一遍自测用例。这个过程本身,就是你对 AI 能力理解的一次升级。
