先说个结论:只要你有过一次“让AI按固定流程干活,结果它每次都发挥不稳定”的经历,就应该认真了解一下Skills的写法。它跟普通提示词最大的区别在于——提示词是“说一次”,Skills是“训练出来的岗位说明书”,让对话助手在遇到对应场景时,能自己翻出标准流程、调用脚本、按格式输出,而不是每次从零开始自由发挥。
这篇文章我按自己的实践经验,把Skills的本质、目录怎么写、description怎么调、脚本怎么配合、以及最常见的坑,全部拆开讲一遍。不搞那种“先看概念再看案例”的学院派结构,直接上手,边写边解释为什么这么做。
1. 先搞清楚Skills解决的是哪个痛点
1.1 一个类比让你快速理解Skills
你可以把现在的大语言模型对话助手,想象成一个新来的实习生。人很聪明,知识面广,但你每次交代任务,都得从头说一遍背景、要求、格式、禁忌。今天说“你帮我整理会议纪要,要分三部分”,明天忘了说“分三部分”,它就给你输出一大段流水账。
Skills要做的事情,相当于给这个实习生一本《岗位操作手册》。以后只要它判断“当前任务属于周报生成”,就自动翻到对应章节,按里面写的步骤走、按里面规定的格式输出、需要时执行配套脚本。你不需要每次重复要求,它也不会因为状态不好而漏掉关键步骤。
这个概念不是我编的,是现在各家助手平台都在推的能力形态,只是有的叫Skills,有的叫Actions或者能力包。原理同根同源:把重复性任务流程固化下来,让模型在恰当的时候主动调用。
1.2 为什么这比普通提示词更值钱
我见过很多人把长提示词存在一个文档里,每次用时复制粘贴。这种方式勉强可行,但有几个明显的天花板:
第一是上下文浪费。3000字的提示词每次都要占用上下文窗口,留给真正业务数据的空间就被挤掉了。Skills是按需加载的,模型只在判断“这个任务匹配”时才读完整说明,平时根本占用不到。
第二是能力边界。纯文字提示词无法执行代码、无法访问本地文件、无法提取压缩包。而Skills可以携带scripts目录,把“需要计算的环节”交给Python脚本来做,模型负责理解任务、拆解步骤、拼装结果。人机各干各擅长的事。
第三是复用与迭代。写好的Skills可以导出、共享、在多场景复用。我团队里一个“销售线索清洗”的Skill,从最早自己用,到后来分发给三个人同时用,中间只改过两版,每次改动所有人都能立刻生效。这种维护成本,比每个人维护各自提示词低一个量级。
1.3 什么时候值得写一个Skill
不是所有场景都值得写Skills。我自己的判断标准很简单——同一件事,如果一周内重复做了三次以上,而且每次步骤基本一致,就可以写。典型的有:
- 周期性的文案产出:周报、月报、项目复盘、会议纪要。
- 固定格式的数据处理:Excel清洗、JSON转CSV、日志分析。
- 需要调用工具的重复操作:批量改图片尺寸、批量重命名、文件格式转换。
- 知识领域受限的问答:比如只回答某一产品线的问题,不越界。
反过来,一次性任务、探索性问题、还没想清楚流程的事情,别急着写Skill,先用普通对话跑通再说。我见过有人第一天接触Skills就写了个“全能助手”,结果那个目录里躺了五六个半成品,全都指向同一个模糊的description,一个都触发不了。
2. 动笔之前先看清结构:一份Skills的标准骨架
2.1 必须知道的目录与文件约定
一个Skill本质上是一个文件夹,里面至少包含一个SKILL.md文件,可选包含scripts、assets、references等目录。我第一次看规范时觉得太简陋了,后来才发现这种极简设计有它的道理。
my-skill/ ├── SKILL.md # 技能的主说明,所有核心逻辑都在这里 ├── scripts/ # 可选的辅助脚本,执行具体计算任务 ├── assets/ # 可选的静态资源,如图表模板、配置文件 └── references/ # 可选的参考资料,供模型按需查阅SKILL.md是整个技能的入口。模型判断“这个任务是否匹配当前对话”时,会先读这个文件的开头部分。所以你不能再按写博客的习惯把背景知识堆在最前面——模型和人都一样,开头被废话占据,耐心就没了。
scripts目录里的脚本,是本地的或者沙箱环境里的可执行程序。它们接收参数、返回结果,模型根据结果继续推理。这里有个关键设计:模型不直接改代码,脚本是独立运行的。你写脚本时要把输入输出都想清楚,尽可能让模型只需要拼字符串就能调用,不要指望它能理解你的Python代码内部逻辑。
2.2 frontmatter里的description是整个文件的灵魂
SKILL.md的开头有一段YAML格式的frontmatter,最少需要两个字段:name和description。很多新手在这里翻车,因为description写得不好,模型就不触发你的Skill,或者动不动就误触发。
--- name: meeting_minutes description: 当用户要求整理会议纪要、生成会议总结、提取待办事项时使用本技能。不适用于普通问答、翻译、代码调试。 ---description要回答三个小问题:这个技能什么时候用、具体做什么、什么时候不要用。第三点尤其重要,因为模型在判断触发时会做语义匹配,你不明确写出排除项,它就能把“帮我总结一下这个网页讲了什么”也匹配到会议纪要上。
我的习惯是写完description后自问一句:如果把“会议纪要”换成“小说、朋友圈文案、菜谱”等相近词,用户请求还能匹配到这个Skill吗?如果还能,就得加排除项。
2.3 渐进式披露:正文怎么排版才不会吓跑模型
SKILL.md正文不追求把所有细节一次性铺开。模型第一次读取时只掌握“这个技能的基本流程和关键指令”,遇到具体分支时再往references里查细节。
我常用的结构:
- 第一段:用一段话说明这个技能统一干什么事,达到什么标准。
- 第二段:给出固定的操作流程,步骤编号清晰,每步说明输入、处理、输出。
- 第三段:定义输出格式,尽可能给一个模板或者示例。
- 最后:如果需要,注明“详细规则见references/xxx.md”,以及执行helper脚本的方法。
为什么这么做?你可以把SKILL.md理解成菜谱的做法页,参考文献相当于食材百科。你炒菜时看做法,不认识某种食材才去翻百科,而不是把百科全书放在灶台上从头读。模型也是这个道理,上下文窗口有限,把不常用的细节放到references里,既不影响理解主流程,又能按需加载。
2.4 多个实例与脚本怎么协同
脚本的作用往往是承接“确定性计算”。举个例子,你在做数据清洗时,模型能理解“把日期列统一成YYYY-MM-DD格式”,但真让它逐行改几百行,它要么改错几行,要么编造数据。这时候就应该把原文件路径给它,让它调用scripts/normalize_dates.py,脚本跑完返回一个干净的文件路径,模型只需要检查结果并继续下一步。
我写脚本时遵循三条原则:输入要么是命令行参数,要么是标准的stdin;输出必须是stdout或者生成独立文件,不要来回对话式交互;脚本本身要稳,最好带异常处理,跑挂时能把错误信息吐出来,不然模型只能干瞪眼。
3. 手把手写一个能跑的Skill:周报生成器全流程
3.1 先定边界:这个Skill的输入输出
我在实战中最早写的Skill就是周报生成器,因为这是每个人最重复、最不想做、又最容易“按时出活”的任务。流程很简单:用户粘贴本周的工作流水记录,Skill输出一份结构化周报,包含本周重点、数据变化、问题与风险、下周计划。
但光这个描述远远不够,还要想清楚“工作流水记录”长什么样。我定义成“不限格式,可以是会议记录、代码提交、聊天片段”,但要求模型先自己整理成时间线,再归档分类。为了演示脚本联动,这个Skill我还配了一个Python脚本,用来从Git提交记录里提取“文件名+提交时间+提交说明”,帮助用户在没整理流水时也能快速生成草稿。
3.2 目录初始化与SKILL.md编写
在本地建立一个目录,命名为weekly_report:
weekly_report/ ├── SKILL.md └── scripts/ └── extract_git_log.pySKILL.md写成这样(我给的是可用的含frontmatter版本,实际字段按你自己的平台规范调整):
--- name: weekly_report description: 当用户需要生成周报、周总结、本周工作回顾,并提供本周零散工作记录、git提交信息或会议纪要时使用。不适用于写日报、项目复盘、个人年度总结。 --- # 周报生成器 将用户提供的零散工作信息转化为结构化周报。周报需包含以下四部分:本周重点工作、关键数据与结果、问题与风险、下周计划。 ## 操作步骤 1. 如果用户未提供任何素材,先执行 `python3 scripts/extract_git_log.py --days 7`,获取最近7天git提交记录。 2. 将用户提供的素材(包括Git记录)整理为按时间排序的工作时间线,去掉无效信息。 3. 从时间线里提炼出不超过3项“本周重点工作”,每项用一句结论性的话概括,并附1至2条支撑事实。 4. 识别涉及量化结果的内容(如用户数、收入、性能数据),归纳成“关键数据”表格。 5. 将阻塞性事件、待协调事项归入“问题与风险”,并给出建议。 6. 依据本周内容推导出下周计划,不超过4条,要求具体可执行。 ## 输出格式 用Markdown输出,结构如下: ## 本周重点工作 - 重点一:... - 重点二:... ## 关键数据与结果 | 指标 | 数值 | 说明 | ## 问题与风险 - 问题描述 / 建议 ## 下周计划 - ... ## 注意事项 - 不要虚构数据。如果素材里没有数据,明确写“本周无可用数据”,不要填0。 - 如果素材过少,主动询问用户是否需要补充。注意我在“操作步骤”里刻意写了“先执行”,这是为了让模型遇到“空手来写周报”的场景时,不是干等用户喂素材,而是自己动手去拿Git记录。这一招能让Skill的实用性瞬间上一个台阶。
3.3 scripts里的Python脚本怎么配合
extract_git_log.py这个小脚本,作用就是读git log然后输出整理好的文本。我写的时候特意让它输出成“时间——提交说明——涉及文件”的纯文本,方便模型直接处理,而不需要去解析JSON。
#!/usr/bin/env python3 import subprocess import sys import argparse from datetime import datetime, timedelta def main(): parser = argparse.ArgumentParser() parser.add_argument("--days", type=int, default=7) args = parser.parse_args() since = (datetime.now() - timedelta(days=args.days)).strftime("%Y-%m-%d") cmd = ["git", "log", f"--since={since}", "--pretty=format:%ad|%s", "--date=format:%m-%d %H:%M", "--name-only"] try: result = subprocess.run(cmd, capture_output=True, text=True, check=True) except subprocess.CalledProcessError as e: print(f"[错误] 无法获取git日志: {e}", file=sys.stderr) sys.exit(1) lines = result.stdout.strip().split("\n") output = [] current_time = "" for line in lines: if not line: continue if "|" in line and not line.endswith((".py", ".md", ".json", ".txt", ".css", ".html")): current_time = line output.append(current_time) else: if current_time: output.append(f" 文件: {line}") print("\n".join(output)) if __name__ == "__main__": main()这个脚本不复杂,关键在两点:一是用--pretty=format把每次提交压缩成一行,二是将文件名单独列在提交行下面,让模型一眼看清“这个提交改了哪些文件”。之所以这么设计,是因为我在测试中发现模型直接解析原始git log效果不好,时间格式太杂,它容易看漏。提前处理成规整文本,它就能聚焦在“归类”和“总结”这两件事上。
3.4 本地联调与对话验证
文件写好之后,不要急着拿去生产环境。我习惯先在本地做三轮测试:
第一轮,直接问“帮我写周报”,观察模型是否触发Skill。如果触发,它会自己去执行脚本获取git记录;如果没有触发,大概率是description没写好,先去改description。
第二轮,给一段模拟素材,比如“周二改了登录接口,周三跟某客户开会确认了需求,周五修复了线上偶现白屏问题”,看它是否按要求生成四段式周报,有没有漏掉部分。
第三轮,刻意给一个超出边界的问题,比如“帮我把这段Python代码改成C++”,确保它不会因为内容里有“代码”二字就错误触发周报Skill。
三轮都过,再放到真实工作流里跑。真实环境跟测试环境最大的区别是素材杂乱程度高,用户可能直接粘一段聊天记录、一张表格截图转的文字、甚至语音转写。好的Skill不要求素材格式统一,模型应该能主动做清洗和结构化。
3.5 把Skill接到工作流里跑真实数据
我现在实际用的是这样的自动化路径:每天结束时,把当天重要的IM消息、会议纪要、代码提交,随手丢进去一个项目笔记文档里,字段只有日期、来源、内容摘要。到了周五下午,执行周报Skill,它读笔记文档、拉git log、合并重排、按模板输出,我只需要调整语气和拼写,全程不到五分钟。
有人可能会问:直接用脚本生成不就行了,为什么还需要Skills?因为脚本只能处理结构化数据,像“某客户在会议上说他们对新模块很满意,希望下个版本能再加两个字段”这种信息,脚本无从下手,但模型能理解并把它归为“下周计划”的素材。Skills的价值恰恰在于把“人的理解”和“机器的计算”缝合在一起,各自发挥长板。
4. 常见问题与排查技巧实录
4.1 description太宽导致误触发
这几乎是所有新手遇到的第一个问题。我见过有人给图像处理Skill写的description是“处理所有与图片相关的任务”,结果用户说“帮我看看这个表情包什么意思”,模型也触发了图像技能,然后一本正经地分析图片构图,完全无视用户只是想问梗。
解法:description里的排除项要多写。我通常会在最后加一句“不适用于:表情包解读、图片审美评价、生成图片”。还要注意description不要写得太像百科词条,要让模型觉得“这个活儿就该找它”。你可以用触发词列表的方式来写,比如“当用户提到周报、weekly report、本周总结、工作回顾时使用”,这样匹配准确率会高很多。
4.2 脚本输出“看起来对但用不了”
有个比较隐蔽的坑:脚本输出的内容表面上正确,但模型不知道接下来该拿它干嘛,最终输出质量依然差。比如上面的git log脚本,如果它输出的是一堆带颜色控制符的文本,模型还得花心思做肉眼清洗。
遇到这类问题,我的排查顺序是:
- 先在终端里直接运行脚本,确认输出是干净文本,没有多余解释、没有报错。
- 再看脚本输出的格式是否足够扁平,能不能两行以内看懂一条记录。
- 最后看是否有一条明确指令告诉模型“拿到脚本输出后做哪一步”。SKILL.md里如果只写了“执行脚本”没说“将结果整理成时间线”,模型很可能会把输出直接贴进周报,那效果就糟糕了。
4.3 模型不按流程走
你会发现即使SKILL.md里写了编号步骤,模型有时候还是会跳步。比如第2步要求“先整理时间线”,它直接跳到第4步总结了。原因是模型把它当成了参考而非约束。
我的对策有两个:一是把必要的步骤写成“必须”而非“可以”,语气更强制;二是在SKILL.md输出格式中同时把中间产物也规定出来,比如先输出“## 工作时间线”,再输出最终周报。这样模型为了完成格式要求,只能老老实实走完流程。另外,步骤数不要超过7条,超出之后模型的遵循度会显著下降。
4.4 Skill越写越胖怎么办
Skill用久了,你会忍不住往里加各种需求,今天要支持英文周报,明天要多一个图表字段,后天又希望它自动翻译。结果就是SKILL.md越来越长,模型越来越不给力。
我的经验是:当一个SKILL.md内容超过600行或者流程步骤超过10步,就该拆分。把“生成周报”拆成“汇总本周数据”和“输出周报文案”两个Skill,中间通过共享references衔接。也可以把不太常用的细节全部移入references目录,主文件只留核心流程。保持主文件瘦身,是对模型遵循度的最大尊重。
4.5 排查Skill时的三个心法
排错的时候不要上来就改代码,也不要随手改description。先做“对照测试”——同一个用户请求,在你修改前后分别让模型跑一次,看差异在哪一步。如果差异出现在“没触发”,问题在description;如果触发但结果格式乱,问题在输出格式定义;如果输出格式对但内容空,问题在素材处理指令;如果内容也丰富但逻辑不通,问题多在脚本返回的数据结构。
第二,多用“最小用例”验证。不要拿真实工作记录去测,那段素材太乱,出问题你分不清是哪一步出的。构造一个只有三行素材的最小场景,跑通了再上真实数据。
第三,测试时强制加一句“请先说明你准备执行哪些步骤”。这句话能让模型把内部规划说出来,你就可以在它真正动手前判断流程对不对。这比等它输出完再懊恼高效太多。
我现在写新Skill的标准流程也基本固定了:先在对话里把流程跑通一次,再把对话内容和结果整理成SKILL.md的骨架,然后补上脚本、references和边界说明,最后做三轮最小测试,上真实场景。大概一个Skill从想法到落地,半天时间。
我实际操作中最大的体会是,Skills不是一个让你“把提示词写得更长”的方案,而是逼着你“把流程和边界想得更清楚”。你写得越具体,模型发挥越稳定;你留的口子越多,它返工的概率越大。如果你手头正好有一个每周都要做的重复任务,不妨就拿它练手,写一个最简单的版本跑起来,再逐步迭代。这比我在这里列一百条要点都管用。