跟大模型 Agent 打交道的时间越长,我越觉得"临场写 Prompt"是一种靠不住的交付方式。上周让同一个模型整理一个中大型代码仓库,连续跑了三次,三次生成的目录结构和模块说明风格都不一样,第四次才勉强对味。与其每次靠碰运气,不如把"这件事应该怎么做"的完整流程、检查项、产出模板,甚至配套脚本,全部打包成一个结构化的技能文件,放进 Agent 的技能库里,用到时自动加载、按固定流程执行。这就是 Skills 的出发点。
这篇文章不打算复述官方文档。我会以一个常年搭 AI Agent 工作流、踩过各种坑的开发者身份,把 Skills 从概念到落地讲透:它到底解决什么问题、和 Tools 差在哪、一份 SKILL.md 该怎么组织、怎么从零做一个真能用的技能包,以及我在技能设计、调试、维护过程中积累的真实经验教训。适合那些已经让大模型做过自动化任务,但总觉得输出不可控、流程不可复现,想把能力真正沉淀下来的同学。
1. 从 Prompt 到 Skills:Agent 能力扩展的范式转变
1.1 为什么临场写 Prompt 成不了"能力"
动笔之前先聊一个扎心的问题:你不是已经把流程写进系统提示词了吗?为什么模型还是不稳定?
我自己做过实验:一段包含 8 步操作流程、3 条禁止事项、1 个输出模板的系统提示词,单次执行时效果不错,但放在对话轮次多、上下文长了以后,模型对流程的遵循率明显下降。原因不复杂:大模型对长指令的注意力不是均匀分布的,越靠后的步骤越容易被忽略;而且当上下文里塞了用户消息、工具返回、中间输出之后,最初那几行流程指令的权重会被持续稀释。
换句话说,Prompt 适合表达"当下这一次怎么做",不适合表达"这一类任务永远怎么做"。后者需要一种更结构化的承载方式。你可以把 Prompt 理解成口头交代,Skills 更像是把口头交代变成了带图纸、带模具、带质检标准的流水线。同样一句话,口头说三遍和三份规范文件跑三遍,稳定性完全不在一个量级。
1.2 Skills 是什么:一次把能力变成"文件"
Skills 的直观形态是:一个按约定组织的目录,里面放一份描述文件(约定俗成叫 SKILL.md)、若干配套脚本、模板和参考资料。模型在对话中会根据用户任务和每个 Skill 的元信息,决定是否需要加载这个技能,加载后按部就班执行。
它和系统提示词、SOP 的本质区别在于:提示词是文本,脚本是逻辑。纯文本靠模型的注意力去理解,脚本靠解释器去执行。真正确定性强的环节(比如"列出目录树""过滤忽略文件""统计函数数量")交给脚本,模型把精力集中在理解需求、编排步骤、组织输出上。
这个组合解决了两件事:一是把重复劳动变成可复用资产,二是把模型的"自由发挥"空间压缩到合理范围。你不需要也不可能让模型每一步都按你的想法走,但你可以通过技能文件把关键路径固定下来,剩下的细节才让它发挥。
1.3 Skills 和 Tools 的关系:零件 vs 图纸
很多人刚接触时会混淆 Skills 和 Tools(在不少平台里叫 Function Calling、MCP、插件)。我的理解是:Tools 是原子操作,Skills 是流程编排;实际落地时两者经常嵌套。
举个例子:一个编辑文档的 Tool 可以"读取文件""写入文件",但"把仓库里的所有 TODO 标记汇总成一份风险报告"这件事,需要 读文件 -> 穿行仓库 -> 识别 TODO -> 分类汇总 -> 按模板输出 五六个动作,单靠一个 Tool 做不了,得把它编排成 Skill。
如果说 Tools 是乐高零件,Skills 就是拼装图纸。图纸的价值不在零件本身,而在它把经验固化成了次序。这也是为什么 Skills 往往比单个 Tool 更能代表"某个人的做事方法"——你可以在不同 Agent 平台间迁移自己的技能库,但不一定能同样轻松迁移底层的 Tool 实现。
2. 剖析一份 Skill 的标准结构:SKILL.md 的语义与分工
2.1 一个技能包的目录布局
下面是我比较习惯的技能包布局,不一定需要严格遵守,但建议保持统一,方便自己维护,也方便模型识别:
repo-overview/ ├── SKILL.md # 技能说明书:模型主要读取这份文件 ├── scripts/ │ └── scan_repo.py # 确定性脚本:数据收集、格式转换 ├── templates/ │ └── overview_template.md # 输出模板:限定最终产出形态 └── examples/ └── output_sample.md # 示例输出:给模型一个"正确长什么样"的参照templates 和 examples 的区别要注意:模板是可以被填写的空结构,示例是已经填好的结果。前者约束格式,后者校准语感。对输出风格要求高的任务,两者都需要;如果只是内部处理不需要生成报告,可以只留模板,示例都可以省掉。
2.2 SKILL.md 的四个核心段落
我见过的 SKILL.md 结构五花八门,但最终沉淀下来,信息基本围绕四件事:元信息、触发条件、执行步骤、硬性规则。
元信息里最关键的是 name 和 description。name 是技能包的标识,description 是模型判断"该不该加载这个技能"的依据。我的经验是:description 要写得像一段"任务宣言",而不是关键词标签。尽量描述任务场景和用户意图,而不是堆名词。
执行步骤要写到"模型不会做错"的程度。不能只写"分析仓库",要写"先读取根目录文件清单;再运行 scripts/scan_repo.py 并传入仓库路径;根据脚本输出中的 top_level 字段判断项目入口;最后使用 templates/overview_template.md 渲染结果"。模型需要的不是方向,而是检查点。
一个简化版的 SKILL.md 长这样,你可以感受下密度:
--- name: repo-overview description: 当用户请求分析一个本地代码仓库并生成结构化概览文档时使用。也适用于"这个项目是做什么的""帮我看看仓库结构"等表述。 --- # Repo Overview ## 使用时机 - 用户想了解本地仓库的整体结构和职责。 - 用户请求生成 README 风格的项目概览。 - 不用于代码内容问答、不用于在线仓库操作。 ## 执行流程 1. 先列出仓库根目录的文件,查找 README、依赖清单等入口信息。 2. 运行 `python scripts/scan_repo.py <repo_path>`,读取 JSON 输出。 3. 结合 README 和脚本输出,识别技术栈、入口文件、核心模块。 4. 使用 `templates/overview_template.md` 渲染最终结果。 ## 硬性规则 - 禁止输出绝对路径,一律使用仓库相对路径。 - 禁止将 .git、node_modules 等目录纳入结构和统计。 - 脚本异常时,必须如实报告错误信息,不得自行猜测结果。2.3 硬性规则:把最容易错的写死在前面
很多 Skill 最终翻车,往往不是因为不知道怎么做,而是踩了零散的坑。硬性规则段就是专门用来收纳这些坑的。
我会把每一类已知错误浓缩成一句"禁止"或"必须"写进去。比如:禁止输出绝对路径;禁止将 .git 目录加入统计;当脚本异常时不要猜测,必须如实报告输出内容。模型对规则的遵循,跟规则条目数量和措辞直接相关,一条一条列清楚,别用一大段散文混在一起。
提示:SKILL.md 是给模型看的代码。它应当严整、简短、可穷举,而不是一篇洋洋洒洒的散文。每一条规则都要能通过自动化检查来验证,写进去前先问问自己:这句话,能不能用脚本判断模型是否遵守?
3. 手把手把"代码仓库梳理"做成一个可用 Skill
3.1 先把"要做的事"拆成确定动作
搭建任何技能之前,我习惯先写伪流程,再写代码。仓库梳理的伪流程是:
- 检查根目录,找到 README、配置文件、包管理文件。
- 运行扫描脚本,拿到过滤后的目录树和文件统计。
- 识别入口文件(main、cli、init.py、index.js 等)。
- 按模板输出:项目简介、技术栈、目录结构、入口说明、扩展建议。
第 2 步是确定性工作,必须用脚本;第 1、3、4 步留给模型判断,但用规则和模板约束。这步拆解非常关键,它决定了技能里哪些部分依赖模型智能,哪些部分依赖代码逻辑。
3.2 配套脚本为什么是"兜底"
扫描脚本用 Python 写,核心逻辑其实不复杂:遍历目录、过滤常规噪声目录、返回相对路径列表。我在实现中有一个细节:过滤逻辑不只判断路径后缀,还要判断中间任意层级是否命中忽略目录,否则.git子目录下的文件仍然会漏进来。
核心实现大约是下面这样:
def scan_repo(root: Path) -> dict: ignore_dirs = {".git", "node_modules", "__pycache__", ".venv", "dist", "build"} ignore_files = {".DS_Store", ".pyc"} tree = [] for path in sorted(root.rglob("*")): if any(part in ignore_dirs for part in path.parts): continue if path.name in ignore_files: continue tree.append(str(path.relative_to(root))) return { "top_level": [item for item in tree if "/" not in item], "file_count": len(tree), "tree": tree[:300], }脚本的返回值是 JSON,模型只需要读取 JSON 再组织语言,不需要自己去翻文件系统。这就把最容易出错的部分变成了确定性操作。我见过不少人试图让模型自己遍历目录,最后无一例外会出现路径拼接错误、忽略目录漏过滤、输出顺序随机的问题。脚本兜底的第一原则就是:凡是能编程实现的,就别让模型自由发挥。
3.3 用模板约束输出形态
模板我写成一段带占位符的 Markdown,模型只需要填空:
# {项目名称} 概览 - 技术栈:{从依赖文件推断} - 入口文件:{main_entry} ## 目录结构 {扫描树的关键目录部分} ## 主要模块 - module: {路径} — {职责说明}模板的意义在于:模型不用每次发明输出格式,它只需要填充内容。这能大幅降低不同会话之间的风格漂移。很多人觉得模板会限制模型的发挥,但对于"仓库梳理"这种任务型输出,稳定远比惊艳重要。如果哪天想让输出带更多洞察,可以在模板里加一节"风险和扩展建议",而不是放开格式控制。
3.4 一次真实调用后的效果观察
搭建完成之后,我拿一个实际项目试跑:Agent 根据用户一句"帮我看看这个仓库是干嘛的",先加载 skill,运行脚本,再结合 README 生成概览。整体流程顺利,但我也注意到一个有意思的现象:模型对top_level字段的使用比我想象得更好,它会自动把根目录下的package.json读进来做技术栈判断;而对"目录结构"的展示,它更倾向挑有业务含义的模块,而不是平铺所有路径。
这说明脚本负责数据、模型负责解释的分工,方向是对的。只要你把数据喂干净,模型的解释质量会明显改善。反过来,如果扫描结果里混着一堆.git内部文件,模型也会一本正经地把它们写进概览——脏数据进,脏决策出,这个规律在 Agent 场景下尤其明显。
4. 设计 Skill 时的四个关键决策:边界、依赖、权限与参数
4.1 这件事值不值得做成 Skill
不是所有任务都值得打进技能包。我判断的三条标准:是否高频?是否多步骤?是否容易因为自由发挥而产出不一致?
如果一个任务一条 Prompt 就能说清楚,或者它只做一次,那做成 Skill 反而是负资产。等到某类任务开始频繁出现,并且每次都要重新教模型同样的流程时,才值得固化成技能。我见过一些人第一天接触 Skills 就雄心勃勃做了十几个技能,两周后全废在维护上,因为大部分技能根本轮不到用,反而成了模型误触发的来源。
4.2 技能包粒度:按"任务"切,不按"模块"切
一个容易犯的错误是拿业务模块划分技能,比如"用户模块技能""订单模块技能"。模块的技能往往夹杂了查询、分析、格式化等多个任务,模型难以判断该激活哪一种,技能内部也可以用,等于变相写了一个超长 Prompt。
我的建议是按任务类型切:summarize_user_profile、summarize_order_details、export_order_report。技能之间以任务为唯一维度,职责单一。名称一眼能看出"什么时候用",副作用是数量会膨胀,但维护成本反而低于大而全的技能。
| 划分方式 | 优点 | 典型问题 |
|---|---|---|
| 按业务模块 | 符合人的直觉 | 一个技能里塞了多个任务,模型容易选错动作 |
| 按任务类型 | 职责单一,触发精准 | 技能数量偏多,需要规范命名和索引 |
| 按输出格式 | 格式统一 | 忽略任务差异,容易陷入模板套模板的死循环 |
4.3 控制运行依赖,别让你的 Skill 变成"环境杀手"
技能包里的脚本是要在用户环境跑的,依赖越重越容易出问题。我踩过的典型场景:一个数据整理技能依赖 pandas 和 openpyxl,但用户的 Python 环境里没装,模型直接报错卡在那里。后来我把脚本改成纯标准库实现,几十行就搞定,再也没有环境问题。
编写技能脚本的原则:能不用第三方库就不用;必须用时,在 SKILL.md 里标注运行前提,并在脚本入口做依赖检测,给出清晰的安装引导。依赖控制的本质是降低运行门槛,你永远不知道用户会在什么样的 Python 版本、什么样的系统环境下加载你的技能。
4.4 权限边界与危险操作
这是很多人忽视的一点:技能脚本能执行系统命令,权限边界必须在设计阶段想清楚。我的做法是在 SKILL.md 里显式声明"本技能不使用删除指令、不写入系统目录、不访问用户主目录之外的敏感区域",同时脚本内部再做一层检查,比如拒绝在脚本参数里出现系统目录之外的路径。
安全设计不是摆设。一个 Agent 的技能越多,越需要有可解释的权限模型。把权限声明写进技能文档,既是给模型的约束,也是给人看的审计信息。尤其在多人共享技能库的场景里,每份技能都应该让人一眼看清它到底能碰哪些东西。
5. 踩坑实录:Skill 落地中我遇到的五类典型问题
5.1 description 写得太宽泛,模型疯狂误触发
第一次做文档整理技能时,我把 description 写成"帮助用户分析和整理各种文档",结果用户一句"帮我把这两个文件合并了"也触发了它,模型加载了完全不合用的技能,输出反而更差。
改正思路:把触发描述换成具体的场景和用户意图,并补充"仅当""不用于"的信号。我发现描述里加一句"此为文档整理技能,不用于内容生成和问答"能显著降低误触发率。触发描述是你的技能第一道门禁,写宽了模型乱闯,写窄了该用时不用,需要反复调到一个平衡点。
5.2 脚本路径地狱:模型永远找不到脚本
早期版本里,我在 SKILL.md 写的是"运行 scripts/scan.py",但 Agent 的工作目录往往不在技能包所在目录,于是脚本就找不到。花了很久才定位到问题根源:相对路径是以 Agent 当前工作目录为基准,而不是以技能文件所在目录为基准。
解决方案很朴素:在脚本开头用自己的文件路径推导技能根目录,再拼接数据路径。模型只需要调用绝对路径,或者 SKILL.md 里明确"先定位本文件所在目录,再运行同目录下 scripts/scan.py"。这个问题在个人项目里最多算摩擦,在多人协作里就是严重的可用性缺陷——因为每个人的工作目录习惯完全不同。
5.3 输出的"示例"污染了新一轮输入
技能包里放了example_output.md之后,出现了一个奇怪的问题:模型有时会把示例内容当成真实数据直接输出。原因在于模型分不清"这是给你看的范例"和"这是你要处理的数据"。
我的解决方式:把示例文件放到examples/子目录,并在 SKILL.md 里用一句话注明"examples 目录下的文件仅供格式参考,不得作为真实执行数据";另外,真实的输出统一写到输出目录,和技能包的静态资产隔离。隔离原则能避免非常多隐含的上下文污染,尤其是当你开始用技能生成内容并反复迭代时。
5.4 步骤超过一定数量后,遵循率明显下降
我最早的一个技能写了 17 个步骤,执行得很糟糕,模型在步骤 6 之后就边界模糊。后来我把流程重构为"主流程 + 子流程":主流程只放核心检查点,细节放进可选择的子步骤,并用"如发现 X 则转入 Y 子流程"来组织。
这和大模型处理长指令的原理是一致的:保留主线,分叉条件化。技能描述越扁平,模型越容易迷失。如果你发现一个技能需要超过十个步骤,几乎可以断定它的粒度出了问题,需要拆成多个技能或引入子流程结构。
5.5 失败案例没沉淀,同样的错误反复出现
我以为技能一旦写好就能一劳永逸,直到发现同一个错误在这个星期出现了三次:模型在读取 scan_repo.py 输出时,忽略了 file_count 字段,导致报告里没有总文件数。其实我第一次就发现了,但我没把这条写进 SKILL.md 的规则里。
从那以后我养成一个习惯:每次技能执行质量不达标,就把失败原因转译为一条新规则,加入技能文档。技能的进化不该靠重写,而应该靠失败驱动的增量修改。每一次跑偏都是免费的调试数据,问题只在你想不想花三十秒把它变成一条对未来的约束。
6. 从单技能到技能库:版本管理、测试与演进
6.1 目录组织的工程化
技能数量超过十个之后,无组织的散放会变成灾难。我会采用单一 skills 仓库,每个技能一个子目录,子目录内统一元信息格式。每个 Skills 目录本身也带一个索引文件,写明技能名、职责、触发场景、维护者,方便人找,也方便模型快速浏览可选能力。
命名规范我建议用 kebab-case,描述用一句动词短语,避免形容词堆砌。比如copy-move-file、extract-table-from-pdf。名称不仅要给人看,它也常出现在模型触发的描述里。技能库的目录结构本质上是给人和模型共享的地图,地图越清晰,导航越省心。
6.2 回归测试:用样板任务验证技能质量
不要相信"模型这次跑对了"就等于稳定。给每个技能配一个samples目录,里面存放黄金用例:输入 + 期望输出。每当技能改动后,就拿这些样本跑一轮,观察是否退化。
更精确的做法是用脚本做快照对比:把 Agent 输出转为规范化文本,和上一版本对比,差异过大就人工检查。这本质上把模型行为纳入了回归测试体系,虽然没法做到 100% 确定性,但它能帮你尽早发现由描述改动引起的输出漂移。我见过最难受的排障就是"上周还能用,这周怎么就不行了",而回归测试几乎能直接定位到是哪次描述改动惹的祸。
6.3 什么时候该重构一个技能
重构信号有三个:触发率低但命中后质量好,说明大多数人不知道它什么时候用,描述有问题;触发率高但每次返回结果都需人工修,说明流程没覆盖真实场景,伪步骤太多;技能之间频繁"串门",说明任务边界切错了。
我的经验是,技能库更像花圃而不是粮仓:定期修剪比大干一场更重要。每季度把使用日志拉出来,看看哪些技能被遗忘,哪些技能被滥用,再动手调整。别在大家用得好好的时候为了"规范"去重构,重构的驱动力永远是使用数据,而不是审美洁癖。
最后说一点我自己这几轮做下来的体会。Skills 真正改变我的地方,不是让 AI 变得更聪明,而是让 AI 的工作结果变得可盘点了。以前我问模型"你怎么得出这个结论的",它只能给我编一段推理;现在我会直接说"把技能里第 2 步运行一下,把脚本输出给我看",逻辑链条一下打回确定性的地面上来。如果你刚开始接触这个概念,别急着搭一个大而全的技能库,先挑一个自己每周都会重复两三次的任务,固化成技能,跑一个月,再回头看值不值。我相信你会感受到那种"同一个问题,再也不用解释第二遍"的愉快。