“Skills”这个词,这两年我见得太多了。初代AI产品经理叫它能力地图,做企业培训的叫它岗位胜任力模型,做协作工具的管它叫人员标签。但站在2025年的开发语境里,它已经变成了一类极其具体的技术资产:Agent Skills,也就是给AI Agent装配的可复用技能包。你在GitHub上搜这个关键词,能看到大量SKILL.md文件,每个文件背后都是一套可以被大模型动态调用的能力模块——一份领域知识、一套决策流程、一段可执行的脚本。这篇文章我想从自己实际落地的角度,把Skill从概念到工程实现拆开讲一遍,包括目录结构怎么写、指令如何组织、依赖怎么管理、上线之后怎么排错。适合正在做Agent应用、准备把零散Prompt沉淀成标准化技能的团队,也适合对Agent工程化感兴趣的个人开发者。
1. “Skills”到底是什么:从词面到Agent能力的转变
1.1 业务语境里的“技能”和工程语境里的“Skill”
在互联网行业,“技能”这个词被不同岗位用出了截然不同的含义。HR看的是员工胜任力模型,销售看的是客户行业Know-how,产品经理看的是功能模块的多样性。这些定义都有一个共同点:技能是一种“能做某件事”的能力描述,但它没有办法被机器直接执行,只能存在于人的经验和文档里。
工程领域的Agent Skills不一样。它把“会做什么”固化成了“能调用什么”——一段可以被大模型读取的指令文本、一组可复用的脚本、一套明确的输入输出约定。我刚接触这个概念时有个很直观的体会:以前调一个Agent干活,Prompt写得再好,换一个对话重新来一遍,效果又得碰运气;但把流程封装成Skill之后,行为就稳定下来了,因为模型的每次调用都会重新加载同一份标准化指令,不会因为聊天轮次变化而“遗忘”。
1.2 它和Prompt模板、插件、Function Calling的边界
很多人问过我:Skill不就是个升级版Prompt模板吗?把话说清楚的话,两者确实是同一个思想源头的产物,但工程价值完全不同。Prompt模板是给人看的,顶多复制粘贴到多个对话里;Skill是给系统和团队看的,它有固定目录结构、元信息、依赖声明,可以被代码加载、被版本管理、被多个Agent共享。你可以把Skill理解成“自带说明书和工具箱的Prompt”。
它跟Plugin(插件)的区别也得拎清楚。插件是外部系统的能力适配层,解决的是“Agent怎么调用外部API”;Skill解决的是“Agent在特定任务上应该遵循什么流程、应用什么知识”。前者偏系统集成,后者偏认知编排。Function Calling则更底层,它定义的是函数签名和数据往返格式,Skill可以把一组相关Function包进来并编写更高层的执行策略。实际项目里它们经常配合使用:Skill编排逻辑,Plugin打通数据源,Function负责具体的函数调用。三层各管一摊,边界清晰,团队协作时才不会互相踩脚。
从我个人的项目经验看,Agent Skills最大的价值不在“单个Skill多聪明”,而在“把好用的能力沉淀成组织资产”。以前我们团队里每个工程师都在自己的Prompt里写了半套私有经验,换个人就丢了。现在大家统一把高频任务固化成Skill包,谁写的都能给所有人复用,新人上手成本直接降了一截。这就是Skill能从概念变成趋势的根本原因——它把口口相传的“魔法”变成了可管理的工程构件。
2. 核心机制拆解:Skill的结构、指令与依赖
2.1 Skill的目录结构到底长什么样
先上一份我从实际项目中整理出来的标准目录结构,麻雀虽小但五脏俱全:
my-skill/ ├── SKILL.md ├── scripts/ │ └── analyze_log.py ├── assets/ │ └── sample_log.txt ├── requirements.txt └── README.mdSKILL.md是这个包的核心,大模型实际加载的就是这个文件。scripts目录放可执行的辅助脚本,模型在指令指引下可以调用它们处理结构化数据。assets放示例文件、模板、参考文档等静态资源,用于给模型提供few-shot示例。requirements.txt声明运行脚本所需的Python依赖,装载时由运行环境统一解析。README.md面向人类开发者,解释这个Skill的适用场景和维护方式。
这个结构的设计逻辑很清晰:文档告诉模型“怎么做”,脚本帮模型“算出来”,资源让模型“看到例子”,依赖让环境“跑得起来”。四件事用文件目录天然分开了,比把所有内容塞进一个Prompt要可维护得多。
2.2 指令文件的“心理学”:怎么让模型稳定执行
SKILL.md的核心是一个规则文件,通过自然语言写清楚完成任务所需的决策逻辑。实际编写时,我会把它分成三个层次来组织:
第一层是“身份与边界”,明确这个Skill在什么场景下被激活、在什么场景下必须拒绝执行。第二层是“流程步骤”,把任务拆成可验证的小步骤,每步写清楚输入是什么、怎么做、产出什么。第三层是“输出约定”,规定结果的结构和格式,比如要求JSON字段、限定表格列名、指定报告模板。三个层次合起来,本质上是在给大模型建一套“操作SOP”。
这里有个容易忽略的细节:指令不是写给人看的规范,而是写给模型看的“认知脚手架”。模型不擅长从零开始推理一套复杂流程,但如果你把流程拆成“先看字段、再过滤异常、最后汇总”,它的准确率会明显提升。这个现象其实跟人类新手入职很像:给一份细化到步骤的SOP,上手效率远高于给一句“好好干”的鼓励。所以别嫌指令啰嗦,清晰的步骤本身就是生产力。
2.3 依赖、元数据与包管理带来的工程化能力
SKILL.md的头部通常有一段YAML格式的frontmatter,用来声明元信息,类似这样:
--- name: log-anomaly-analyzer description: 用于分析应用日志中的异常模式并生成诊断报告。 when_to_use: 当用户提供日志文本或日志文件路径,并希望定位异常原因时使用。 ---name是Skill的唯一标识,便于统一管理和索引。description要给模型一个“什么时候该用这个技能”的简洁提示,因为Agent面对多个Skill时,需要靠这段描述做路由匹配。when_to_use则进一步补充触发条件,防止误用。
到这里,Skill就已经不是一段Promt了,它具备了一个软件包的基本特征:有元数据、有依赖、有版本、有目录规范。多个人协作时,可以用Git管理整套技能包,把版本控制在发布记录里写清楚;多个Agent共享时,可以通过统一的技能仓库装载。代码能做的工程化管理,Skill都能做,这才是它可以规模化的前提条件。
3. 从零编写一个可用的Skill:完整实操记录
3.1 选题:什么样的任务值得做成Skill
我见过不少团队一上来就把所有Prompt都改成Skill,结果维护成本比收益还大。踩过坑之后,我给自己定了几条选择标准:任务必须是高频的,每周至少遇到三五次;任务必须有相对稳定的判断标准,输入输出都比较好定义;任务最好能跨越多个对话独立复用,而不是只跟某个特定项目绑定。
老老实实说,不是所有任务都适合封装。比如“帮我写一封邮件”这种话,看起来很常用,但用户期望千差万别,Skill里写死了反而限制灵活性。更适合做Skill的是“把这份Nginx访问日志按状态码聚类并找出P99延迟拐点”这类任务——流程固定、判断标准明确、输出形态可预期。刚开始练手时,从“日志分析”“代码审查清单”“标准周报生成”这类场景切入,成功率会高很多。
3.2 编写SKILL.md:从模糊需求到可执行步骤
我用日志异常分析Skill当例子,完整演示一遍。一开始的需求描述只有一句:“帮我分析日志,看看有什么异常。”这种描述拿给任何人做都会被反问:什么日志?什么异常?按什么维度分析?所以Skill编写的第一要务,就是把模糊需求翻译成明确的步骤。
我最终写出的核心指令是这样的(简化为可直接参考的长度):
--- name: log-anomaly-analyzer description: 分析应用日志中的异常模式,输出分级诊断报告。 when_to_use: 用户提供日志内容、日志文件路径或日志目录时使用。 --- # 日志异常分析 你是一名资深的SRE工程师。请严格按以下步骤分析日志,不要跳过任何环节。 ## 输入 - 日志文件路径或直接粘贴的日志内容 - 可选:分析重点关注的时间范围 ## 分析步骤 1. 先读取完整日志,标注时间戳格式、日志级别字段、服务名称字段。 2. 按时间窗口切分数据,默认每5分钟一个窗口,统计每个窗口的日志量。 3. 在窗口内检测以下异常信号: - ERROR / FATAL 级别日志占比超过窗口总量的 5% - 同一错误信息在10分钟内重复出现20次以上 - 日志量出现3倍以上的突增或突降 4. 对每个异常信号,结合上下文日志内容推测可能原因。 5. 若存在多个异常信号,按时间先后顺序排列,而不是按日志级别排列。 ## 输出格式 返回一份Markdown报告,包含: - 概览表格:每个时间窗口的状态(正常/可疑/异常) - 异常详情:按时间序列列出检测到的异常信号、对应日志片段、推测原因 - 建议动作:针对每个异常给出下一步排查建议 ## 注意 - 不要输出原始日志全文,提取关键片段即可。 - 如果信息不足,明确列出还需要哪些数据,不要猜测。这份指令有几个关键设计。步骤拆得足够细,每一步都有明确产出,模型不容易跑偏;异常信号的定义用了量化标准(5%、20次、3倍),而不是“明显异常”这种模糊词,模型判断起来有依据;“按时间排序”这种反直觉的规定,是为了贴合日志排查的真实操作习惯,避免模型按严重级别重新排序打乱时序线索。
3.3 本地验证与迭代:用三份日志样本跑通闭环
写完SKILL.md只是第一步,关键在验证。我的做法是准备三份典型日志:一份完全正常,用来测试“误报率”;一份有明显ERROR堆积,用来测试“检出率”;一份日志量突增但级别正常,用来测试“对隐性异常的识别能力”。把这三份样本分别丢进加载了Skill的Agent里,观察输出结果是否符合指令约束。
第一次测试结果通常是哭笑不得的——模型很可能把正常日志里某个INFO信息也当成异常,或者输出格式跟指令要求的Markdown结构对不上。这都是正常现象,因为Skill和模型之间的“磨合”需要迭代。我会照着两份原始输出逐条比对:格式不对就加强输出约束段的表述,误报过多就提高异常信号的触发阈值,上下文引用不够就补充“结合上下文日志内容”的明确指令。通常两三轮迭代之后,输出就会稳定在可用水平。本地验证这一步千万不要省,一套没有经过样本测试的Skill,上线之后就是一台随机抽奖机。
3.4 发布、版本管理与团队共享
验证通过之后,Skill就算进入了发布阶段。我习惯给每个Skill包维护一个CHANGELOG,记录每次指令改动的原因和影响范围。版本号用语义化规则:改动输出格式算minor版本,改动分析逻辑算major版本,修复错别字或微调阈值算patch版本。
团队共享时,用统一的Git仓库管理所有Skill,仓库根目录下建一个索引文件,登记每个Skill的name、路径、当前版本、维护人。Agent侧支持按需拉取,而不是一次性全量加载——加载了太多Skill反而会增加模型路由负担,影响响应速度。每新增一个Skill,都要在索引里更新团队成员可见的说明,让其他人知道“有这个能力、该在什么场景用、找谁问”。这个习惯帮我避免了很多“重复造轮子”的尴尬场面。
4. 上线之后的坑:问题排查与体验优化
4.1 模型不听话:指令级问题排查
Skill上线之后最常见的问题,就是模型“没有按指令来”。第一反应别去怪模型,先回头检查指令本身。根据我的经验,90%的“不听话”本质上是指令写得不够明确。看一下有没有量化标准、有没有明确输出格式、有没有定义边界条件。比如“分析一下日志”这种表述,模型按自己的理解自由发挥是很正常的,如果你希望它只关注ERROR级别,就得明说“只统计ERROR及以上级别,忽略INFO和DEBUG”。
另一种情况,指令写得没问题,但Agent在长对话中加载了太多上下文,导致Skill的指令权重被稀释。解决办法是配置层面把指令放在Prompt靠前的位置,或者确认当前对话没有无关上下文干扰。实在不行就开一个新会话再试一次——看起来像取巧,但确实能有效排除上下文污染。
4.2 输出质量不稳定:校验脚本与回归测试
输出质量是另一个大坑。大模型本质上是概率生成器,哪怕指令完全一样,它两次输出的格式和内容也可能有细微差异。对内部工具来说,这点差异还能忍;但对需要对接下游流程的场景,格式不稳定就完全没法接受。
我的方案是加一道程序化校验:用scripts/validate_output.py这类脚本,对Agent输出做规则校验,比如关键字段是否存在、数字是否为合法浮点数、时间戳格式是否正确。校验不通过就自动触发一次修复动作,让模型根据校验错误信息重新生成。这道防线能救回相当多“看起来还行但实际没法用”的输出。另一个更稳妥的做法是沉淀一组回归测试用例,每次修改Skill之后都跑一遍,防止“修好一个问题、引出三个新问题”的连锁反应。
字段级别的稳定性还可以靠few-shot示例兜底。在assets目录下放两个完整的输入输出样例,让模型照着样例的格式来写,输出的波动性会明显下降。这招跟带新人很像:给一堆抽象规范不如直接甩两个优秀案例来得快。
4.3 成本与性能:Token开销怎么控制
Skill虽然好用,但Token不是大风刮来的。尤其当一个Skill写了三四千字指令、附带多份示例文件时,每一次调用都要把整份Skill作为上下文传给模型。对高频小任务来说,这个开销可能占单次调用的很大比例,跑一个简单查询却背着一整套沉重指令,成本结构很不健康。
控制手段有几个层面:指令高度精炼,只保留必要步骤,已有的示例文件能用一两行说清楚的就不放完整样本;按任务粒度拆分Skill,一个“代码审查”Skill拆成“安全审查”和“风格审查”两个变小变轻的子Skill,路由也更精准;高频调用场景优先用上下文缓存能力,避免重复计费。实际操作时我会给每个Skill记录一次典型调用的Token消耗数,定期排查“谁的Token越吃越多”,一旦发现用量异常增长,就回去看是不是指令或示例被某个成员改胖了。成本优化不是一次性的,得长期盯。
4.4 问题排查速查表
| 现象 | 常见原因 | 处理办法 |
|---|---|---|
| 模型不按Skill的步骤走 | 指令步骤不够明确、顺序不清晰 | 拆细步骤,每步标注输入输出 |
| 该用Skill时没触发 | description和when_to_use写得泛泛 | 明确触发条件,给出一正一反两个使用场景 |
| 输出格式时好时坏 | 指令约束不足或缺少样例 | 加输出格式定义,加few-shot示例 |
| 响应速度突然变慢 | Skill加载了过多上下文 | 精简指令,拆分大Skill为多个小Skill |
| 成本显著上涨 | 指令过长或示例过多 | 压缩内容,启用缓存,按调用量定期审计 |
| 多个Skill被同时误触发 | 各Skill的描述边界不清 | 统一加场景限定词,避免能力描述重叠 |
我把这张表贴在团队Wiki上,每次有人报“Agent又不听话了”,先自己对着排查一轮,实在解决不了再找我。实测下来至少省掉了一半的“陪调试”时间。
写在最后:一个小技巧
从最早把Prompt复制来复制去,到现在团队里维护着几十个版本化的Skill包,我最大的感受是:Skill不是越复杂越好,而是越“恰好”越好。写得像百科全书一样面面俱到,模型反而不知道该重点执行哪条;砍到只剩关键路径,哪怕少了很多修饰词,模型反而执行得更准。
最后分享一个我一直在用的技巧:每次写完一个新Skill,我会回到用户视角,拿最初那段模糊需求原文去测试一次——不带任何背景提示,看看Agent光凭Skill能不能给出及格的回答。如果连这种“最差输入”都能兜住,这个Skill才算真正能交付。这套方法帮我避开了很多自我感觉良好、上线就翻车的尴尬场景。如果你也在做Agent方向,不妨从这周挑一个自己最常干的高频任务,照上面的步骤把它做成第一个Skill,跑一轮样本测试,你会回来感谢自己的。