去年年底开始,我把自己工作流里一大半的重复性任务都交给了AI Agent,但真正让效率上了一个台阶的,不是提示词写得有多花哨,而是把那些反复用到的能力沉淀成了一个个独立的skills模块。这个东西说白了就是给Agent配备的“技能包”,对我来说,它比单纯调prompt稳定太多,也比硬堆tool文档灵活太多。如果你正在折腾AI工作流、Agent开发,或者团队里想统一AI的使用方式,这篇文章可能正是你需要的——我会从概念、设计,到一次完整的实操,再到我踩过的坑,全部摊开讲。
1. 先把 Skills 这件事说透:它到底是什么
1.1 用一个生活类比理解技能模块
我在给团队分享的时候最喜欢用一个类比:prompt是一张菜谱,tool是一把菜刀,而skill是一个会做菜的人。菜谱告诉你步骤,菜刀给你工具,但真正知道什么时候颠勺、什么时候关火、火候不对怎么救场的,是人脑子里那套经验。Skills就是试图把“人脑里的经验”以文件、脚本、校验规则的形式打包给Agent,让它拿到这个技能包之后,能像“一个会做这件事的人”一样工作。
在技术实现上,一个skill通常是放在特定目录下的一组文件,包含一个描述性文档(比如SKILL.md)、若干参考文档、一些可执行脚本,有时候还有参数配置文件。当Agent在对话中意识到当前任务匹配到某个skill的描述时,就会把这个技能包里的内容加载进上下文,并按里面定义的步骤去执行。注意,它是“按需加载”的,不是一开始全塞给模型,所以对上下文窗口的占用非常克制,这也是它和“写一个超长system prompt”最本质的区别。
1.2 为什么现在 Skills 被单独拎出来讲
以前我们处理复杂任务,最常见的两个办法:一是靠一个巨长的prompt把流程写死,二是把每个功能做成API让Agent调用。这两个方案都有痛点。prompt写太长了,模型执行到后半段经常“忘记”前面的格式要求;API做细了,维护成本高,而且Agent只知道“调接口”,并不知道什么时候该调、调完结果怎么判断。
Skills是在这两个方案之间取了一个平衡点。它让Agent在正确的场景下自动触发正确的处理流程,并且这个流程是可以迭代、测试、复用的。很多Agent平台之所以把Skills做成官方推荐机制,核心原因是它解决了“LLM应用难落地”的一个关键瓶颈:把一次性的对话能力,升级成可积累的自动化作业能力。社区里已经有人在彼此交换skill了——你写一个好的摘要技能,我写一个数据处理技能,互相复制就能用,这种生产资料层面的共享,和前几年“交换prompt”的性质完全不一样。
对我个人而言,看得见的好处有三个:第一,我的Agent不再每次从零“摸索”任务,成功率高了很多;第二,新功能上线不用改主程序,加一个skill目录就行;第三,同一个skill可以在不同项目里复用,时间越久越值钱。
2. 一个合格 Skill 的构成与设计要点
2.1 Skill 的标准文件骨架
虽然各家平台对skill的封装格式有细微差别,但核心结构其实高度一致。我以一个比较通用的技能包为例,通常是这样组织的:
skills/ └── meeting-minutes/ ├── SKILL.md ├── scripts/ │ └── generate_minutes.py ├── references/ │ └── output_template.md └── assets/ └── sample_input.txtSKILL.md是入口文件,也是最重要的文件,里面通常有一个YAML格式的frontmatter,声明这个技能的名称、描述、适用场景、允许使用的工具等;正文部分则详细描述执行思路、步骤约束和输出格式要求。scripts目录放实际的代码逻辑,references目录放参考资料和模板,assets目录放静态资源。
为什么要用这种“入口描述+附伴文件”的结构,而不是全部写在一个文件里?这就好比一个技术方案的说明书和实现代码应该分开,入口描述要保持精简,确保Agent在加载时能快速理解“这是什么”“什么时候用”,而具体的执行逻辑、数据集、模板可以放到子文件里按需读取,避免一次性占用太多上下文窗口。我见过有人把整个技能的细节全部堆在SKILL.md里,结果Agent一加载就刷掉几千token,后面的任务还没开始,上下文就已经费了一半,得不偿失。
2.2 设计 Skill 的四个核心原则
第一个原则叫“单一职责”。一个skill只解决一类任务,不要想着做一个“万能整理助手”把摘要、翻译、分类全塞进去。单一职责的skill描述写起来简单,模型匹配的准确率高,调试时也容易定位问题。我手上一开始有个“内容处理”skill,后来拆成了“会议纪要”“文章摘要”“要点提取”三个,使用成功率明显上升。
第二个原则叫“描述精准”。skill的description字段怎么强调都不过分,因为它决定了Agent什么时候触发这个技能。描述写得模糊,该触发的时候不触发,不该触发的时候乱触发。好的描述应该包含触发场景、输入格式、输出要求。比如:“当用户提供会议转录文本或对话记录,并要求整理为结构化会议纪要时使用。输入为纯文本或txt文件路径,输出为Markdown格式。”这种写法比“会议整理”四个字好太多。
第三个原则叫“步骤可验证”。SKILL.md指导里的每一个步骤,尽量让Agent在中间节点可以自检。比如“读取文件”“提取发言人”“归纳行动项”,每一步之后可以要求Agent检查中间产物是否存在、格式是否正确。这能有效降低幻觉出现的概率,因为Agent在长流程里特别容易“跳步”。
第四个原则叫“约定优于配置”。凡是能在SKILL.md里写清楚默认值的,就不要让Agent在执行时去猜。比如输出语言默认中文、时间格式默认ISO 8601、字段缺失时默认填“待确认”。这些约定写在文档里,模型执行时就不会反复犹豫或者擅自创造规则。
2.3 该调的参数一个都不能省
Skill不仅仅有文本描述,它还应该携带执行参数。很多时候Agent表现不稳定,不是模型不行,而是参数没有跟skill绑定。常见需要声明的参数包括:temperature、max_tokens、top_p,以及是否允许调用外部工具、是否需要额外的上下文窗口预留。
举个例子,写会议纪要这类事实抽取任务,temperature设0.2以下比较好,让输出尽量确定;如果是头脑风暴类的创意技能,temperature可以放到0.7以上,让模型有发挥空间。这些参数写在skill的配置文件里,Agent加载这个技能时自动应用,不需要用户每次手动指定。
我通常在SKILL.md的metadata区域声明这些参数,类似于:
--- name: meeting-minutes description: 当用户提供会议转录文本并希望生成结构化纪要时使用。 allowed-tools: - read_file - run_python parameters: temperature: 0.2 max_tokens: 3000 ---有一个细节要提醒:参数不是越多越好。有些平台支持非常多的高级参数,但普通任务根本用不到,写多了反而增加解析出错的风险。我的一般原则是:temperature、max_tokens必须有,其他参数按需补。参数一旦配置好,就不要频繁改动,否则你很难判断一次失败到底是技能逻辑的问题还是参数漂移导致的。
3. 从零搭建一个可复用的 Skill:完整实操
3.1 准备目录与应用注册流程
在动手写之前,建议先确认你使用的Agent平台或框架支持哪种skill规范。主流的Agent平台基本都有类似机制,有的是把skills目录放在项目根目录,有的是通过配置文件注册路径。我以最常见的“目录即技能”的约定来示范:在项目根目录下建一个skills文件夹,里面每个子文件夹就是一个独立技能,平台启动时会自动扫描加载。
我实际执行时的第一步是这个:
mkdir -p skills/meeting-minutes/{scripts,references,assets}然后初始化SKILL.md。这里有一个容易忽略的小细节:目录名最好和技能名保持一致,并且用中划线分词,不要用空格或下划线混用。因为很多平台的技能加载器会拿目录名做标识,如果目录名和SKILL.md里声明的name不一致,可能出现奇怪缓存问题。我一开始吃过这个亏,把目录起名“meeting_minutes”,skill name写成“meeting-minutes”,结果平台把它当成两个技能,浪费了半小时排查。
3.2 写一个“会议纪要整理”Skill 的完整过程
我拿一个最常用的技能来演示:给出一段会议转录文本,输出结构化会议纪要。这是所有团队都会遇到的需求,也是skill的最佳应用场景。
先写SKILL.md,内容不要贪多,突出触发条件和流程约束:
--- name: meeting-minutes description: 当用户提供会议转录文本、语音转写结果或对话记录,并要求生成会议纪要、行动项、决策清单时使用。输入可为纯文本内容或txt文件路径。 allowed-tools: - read_file - run_python parameters: temperature: 0.2 max_tokens: 3000 --- # 会议纪要生成技能 ## 任务目标 将输入转录内容转换为结构化会议纪要,包含:会议主题、时间、参会人、讨论要点、决策、行动项。 ## 执行步骤 1. 读取输入内容。如果输入是文件路径,先调用工具读取文件内容。 2. 用 scripts/generate_minutes.py 对文本做分段预处理,提取发言人和段落结构。 3. 基于预处理结果,生成 Markdown 格式纪要。 4. 将结果写入输出文件 meeting_notes.md,并在回复中给出文件路径。 ## 输出格式 参考 references/output_template.md 中的模板,字段缺失时填“待确认”。 ## 注意 - 行动项必须标注负责人和截止时间,无法推断时写“待指定”。 - 不修改用户原文中的事实性数据。然后写一个配套的Python脚本,负责分段和初步清洗。真正的结构化抽取交给模型,但脚本地步先把脏数据整理好,Agent后面生成纪要就精准很多:
import re import sys def preprocess_transcript(text): lines = text.splitlines() cleaned = [] for line in lines: line = line.strip() if not line: continue # 合并时间戳行,保留发言内容 if re.match(r"\d{2}:\d{2}", line): line = re.sub(r"^\d{2}:\d{2}\s*", "", line) cleaned.append(line) return "\n".join(cleaned) if __name__ == "__main__": input_path = sys.argv[1] with open(input_path, "r", encoding="utf-8") as f: text = f.read() result = preprocess_transcript(text) print(result)这个脚本看起来很基础,但作用很大:把语音转写里常见的时间戳、空行、重复片段过滤掉,让Agent的输入干净,输出质量直接上一个台阶。脚本不需要很复杂,能用就行,因为真正的智能部分在Agent那里。
接着写references/output_template.md,内容如下:
# 会议纪要 - 会议主题:{} - 会议时间:{} - 参会人:{} ## 讨论要点 {} ## 决策 {} ## 行动项 | 事项 | 负责人 | 截止时间 | |------|--------|----------| | {} | {} | {} |模板的作用是给Agent一个稳定的输出锚点,避免它每次生成的结构都长得不一样。如果需要批量处理,还可以加一段调用脚本的测试命令,确保环境能跑通。
3.3 多 Skill 协作与复杂任务拆解
单个skill解决一个环节,但真实场景往往是多个skill协作。比如我每周的周报流程,涉及三个skill:会议纪要skill处理周会,数据汇总skill拉取项目进度,格式化skill把零散信息整合成周报。关键点在于:不要让一个skill去“调用”另一个skill,而是让Agent在任务层面做路由判断,依次匹配并触发相关技能。
业务上怎么拆?我的经验是看“领域”和“动作”。领域是数据方向还是文本方向,动作是提取、转换还是生成。每个skill对应一个“领域+动作”的组合,这样职责边界清晰,Agent路由时不容易混淆。如果发现两个skill的描述经常同时触发,说明边界没切好,需要重新定义触发场景。
还有一个小技巧:在SKILL.md里可以显式写上“此技能不处理什么”,比如会议纪要skill里写“本技能不负责翻译、不负责生成待办应用”。负向描述能明显减少误触发,算是性价比极高的一行字。
4. 真实项目踩过的坑与排查实录
4.1 最常遇到的几个问题速查表
| 现象 | 可能原因 | 排查与修复 |
|---|---|---|
| Skill 从未被触发 | description 与用户表达匹配度太低 | 重写 description,多列举触发场景和同义词 |
| 技能被过度触发 | 描述边界模糊,负向条件缺失 | 增加“不处理/不适用”场景说明 |
| 加载后上下文不够 | SKILL.md 写得过长 | 将细节移入 references,只保留流程性描述 |
| 执行结果不稳定 | 缺少参数配置或 temperature 过高 | 在 metadata 中固定 temperature、max_tokens |
| 输出格式混乱 | 缺少模板约束 | 在 references 中提供完整示例,并在步骤中要求“严格按模板输出” |
| 更新技能后行为没变 | 平台缓存了旧版本 | 清缓存或重启会话,查看版本号是否生效 |
4.2 三个最该注意的细节
第一个细节是“权限声明别贪多”。SKILL.md里allowed-tools最多列三四个,如果一个技能声明了太多工具权限,平台会有更严格的安全审核,Agent也容易在执行时跑偏。尤其是网络请求类工具,非必要不声明。很多人一开始图省事,给技能开了脚本执行、文件写入、网络请求三件套,结果Agent在拿不准的时候调了网络工具去搜索,输出的东西又慢又不可控。
第二个细节是“相对路径与绝对路径的统一”。如果技能脚本里要读取references下的文件,最好统一使用相对于skill目录的路径,而不是写死绝对路径。因为技能目录复制到别的项目时,绝对路径必然失效。我一开始没注意,所有脚本都写“/Users/xxx/projects/...”,换个机器全部报错,一个个改路径快改到崩溃。建议在skill目录下放一个config.json,路径字段统一维护:
{ "template_path": "references/output_template.md", "output_dir": "output" }第三个细节是“输出自查机制”。在SKILL.md里要求Agent在返回结果前做一次自检:“检查输出是否包含行动项、是否缺少负责人”。这看起来是在提示模型,实际上是在用最后一道闸门拦截幻觉。实测下来,加了这句自检之后,缺失字段的概率降低了大概一半。
4.3 版本管理与回滚
很多人在写prompt和工具脚本时没有版本管理意识,但skill作为工程资产,一定要纳入版本跟踪。我的做法是每一个skill目录单独建git仓库,或者至少放在一个带git的monorepo里。每次修改SKILL.md时更新frontmatter里的version字段,并保持语义化版本号。遇到一次改坏的情况,直接把整个目录也合并到主项目的历史里,一键还原,不用靠脑补“上次正确的版本是哪天写的”。
还有一个建议:重大变更不要直接覆盖原目录,而是先复制一份“-beta”目录,测试通过后再替换正式版。这跟发布一个应用是一个道理,给线上环境留一条退路。尤其当这个skill是团队共享时,一个不稳定的版本可能连累所有人的自动化流程。
5. Skills 的复用价值:从个人资产到团队能力
5.1 建立团队级 Skills 库的流程
当你的skill已经稳定运行了一个月以上,下一步自然是把它共享给团队。我在团队里推过一个很简单的流程:先定标准,再选试点。定标准是指统一SKILL.md的编写规范、参数命名、模板存放位置;选试点是指先找一两个需求最强烈的场景做样板技能,跑通之后再扩大范围。
团队共享时最常遇到的问题不是技术,而是“没人维护”。所以我建议每个技能指定一个owner,至少要有一个onwer。owner负责收集反馈、定期测试、发布版本。没有owner的技能上线三个月后基本就会腐化。skill库建起来之后要与文档、示例、最佳实践配套推广,不然团队成员只会用你演示过的那一两个技能,库的价值大打折扣。
5.2 把隐性经验变成可复用资产
往深了说,skills本质上是把我们脑子里那些“只可意会不可言传”的经验,一点点固化成了机器能执行的东西。一个老员工知道怎么高效开周会、怎么整理客户反馈、怎么排查数据异常,这些以前只能靠带教传承的“手艺”,现在可以变成一个技能包,让AI替所有人执行标准版本。
我自己的体会是,写skill的过程比用skill收获更大。为了把流程写成指令,我需要重新审视自己平时做事的步骤,去掉冗余动作,理清先后顺序,定义输入输出。这个“自动化反思”的过程,本身就是一次极其深入的个人工作流梳理。每次一个技能稳定工作,我都有一种“把一部分自我外置”的踏实感。
最后分享一个我在实操中的小技巧:不要一开始就追求“完美技能”。先写一个能跑通60分场景的版本,然后在真实使用中观察卡点,迭代到80分,比你闭门造车想三周再上线要快得多。我也曾经为一个表单提取技能反复设计了五天,最后发现用户最需要的只是一个bug修复和一个字段映射调整,而这些只有跑了真实数据才能发现。动手写第一个skill,比读十篇教程都管用。