最近很多做 AI 应用和 Agent 开发的朋友跑来问我同一个问题:Skill 到底是个什么东西?怎么越看越像 Prompt?为什么官方文档里把它吹得天花乱坠,我用起来却总是不痛不痒?
有这个困惑的不在少数。过去大半年,Codex、Claude Code、Trae 这些终端型 Agent 陆续把 Skill 推到了台前,GitHub 上各种 skill 仓库如雨后春笋,连“skill 推荐”“skill creator”这类热词都跟着起来了。但风口越热,大家就越容易走偏。我见过有人把几十个 Skill 一股脑塞进 Agent,结果模型在选 Skill 的时候比干活还慢;也有人把一条简单的 Prompt 套上 Skill 的壳就发出来宣称“工程化落地”;更常见的是,Skill 写了一大堆,测试一跑就翻车,翻完车也不知道是该改提示词、改脚本还是改目录结构。
这篇文章我不打算做名词科普,而是想从工程实践的角度,把一个 Skill 从设计、编写、测试到迭代的完整链路拆开讲透,顺便泼几盆冷水:哪些场景根本不需要 Skill,哪些做法是在给 Agent 系统埋雷。看完之后,你至少能回答三个问题——Skill 的本质是什么?一个及格的 Skill 是怎么落地实现的?以及,怎么判断自己是不是正在滥用它。
1. Skill 概念的来龙去脉,以及它到底在解决什么问题
1.1 从“会聊天”到“会干活”,Agent 缺的是可复用的能力单元
要理解 Skill,得先看大模型产品形态的演进。最早我们接触的是对话机器人,模型的任务是“回答”而不是“完成”。后来有人把多轮对话、工具调用、记忆管理拼在一起,做出了 Agent 的雏形——模型开始能调 API、读文件、写代码。到了这个阶段,问题就变了:模型虽然什么都会一点,但它在面对某个特定领域任务时,没有任何“职业习惯”。
什么概念?你让一个刚毕业的高材生去写周报,他能写出来,但写出来的东西要么格式不对,要么啰嗦得像个流水账。你让他去分析日志,他会用 grep 把报错抓出来,但他不会先按时间窗口切分数据,再逐层聚合错误码,最后给你一份带着趋势判断的报告——因为这些是你在公司里带了他三个月之后,他才学会的“岗位技能”。
Skill 就是给大模型做“岗前培训”的那个载体。它不是模型参数里的能力,也不是一次性的对话指令,而是一套结构化的、可复用、可版本化、可被多个场景装载的“能力单元”。可以说,Agent 负责“临场发挥”,Skill 负责“有章法”。
我在实际项目里的体感是:没有 Skill 的 Agent 像一个什么都懂但没有任何工作经验的实习生,有了 Skill 的 Agent 才像一个带过一段时间的正式员工。这句话听起来很简单,但它决定了 Skill 在设计上必须同时包含“说明”和“执行”两个维度——光告诉模型要怎么做不够,还得给它提供可执行的具体工具脚本和参考样例。
1.2 Skill、Prompt、Plugin、Agent 的边界在哪里
这四者的关系,几乎是每次分享必被问的问题。我先说结论:Skill 是一种介于 Prompt 和完整 Agent 之间的工程形态,它不是单纯用来“提示”模型的文本,也不是一个独立自主的智能体,它是可以被 Agent 随时调用的一组流程化能力。
如果画一条轴,最左边是 Prompt——一段文本,只在你输入的那一次会话里生效,没有固定的存储结构,没有可复用的执行逻辑。最右边是 Agent——一个具备目标拆解、多步规划、工具调度、自我纠偏能力的完整系统。Skill 落在中间偏右的位置:它有固定的格式,有一定的执行脚本,但自身不做复杂规划,它的“决策权”受限,本质上是供一个更大的智能体使用的模块。
那 Plugin 呢?Plugin 在多数框架里指的是系统层的扩展点,比如给某个应用插一个鉴权中间件,或者给 IDE 插一个语言服务器。它关注的是“软件系统的集成”,Skill 关注的是“模型能力的复用”。一个 Skill 内部可能会调用一个或多个工具脚本,这些脚本可以长得像 Plugin,但 Skill 的边界更偏向“提示词 + 脚本 + 元数据”的组合。
我用一句话给团队讲清这件事:Prompt 是你说的一句话,Skill 是你沉淀下来的一份 S.O.P.,Plugin 是你工具箱里的一把螺丝刀,Agent 是那个拿着螺丝刀按 S.O.P. 干活的人。
1.3 为什么 Skill 在最近半年突然成了香饽饽
热词不会凭空出现。Skill 之所以爆发,是因为几个条件恰好凑齐了。
第一,模型底座的能力到了临界点。代码型 Agent 已经能稳定完成多步文件操作和命令行调用,模型需要在“调用之前”知道该遵循什么流程——Skill 完美补上了这个接口位。第二,终端型 Agent 产品(Codex、Claude Code、Trae 这类)开始流行,它们天然允许用户在自己的工作目录里塞一个.claude/skills或类似的结构化目录,这让 Skill 的“可分发”属性大大增强。第三,社区生态起来了,越来越多的开发者开始公开自己的 SKILL.md,大家发现同一个 Skill 可以跨项目复用,于是“skill 推荐”“skill creator”这类新热词就跟着长了出来。
但繁荣背后有个隐患:门槛太低了。写一个 Skill 的真实门槛并不高,谁都能把一段话包装成 Skill,但一段话包装出来的东西根本撑不起“工程化”三个字。所以接下来我重点拆解,真正合格的 Skill 到底该由哪些部分构成。
2. Skill 的本质拆解:一份“可执行的说明书”背后有什么
2.1 Skill 的真实形态,是“结构化知识 + 可执行逻辑”的复合体
很多人把 SKILL.md 看成“更长的 System Prompt”,这是目前最大的误解。Prompt 的目标是影响模型的输出风格和内容,而 Skill 的目标是让模型在特定任务上“按流程办事、按标准产出”。
我拆过很多个公开的 Skill,发现凡是好用的,内部都藏着一个共性结构:先告诉模型“这个技能什么时候该用、边界在哪里”,再给它“完整可执行的操作步骤”,最后给它“参考工具脚本和验收标准”。这有点像传统软件开发里的“接口文档 + 单元测试”——前者定义行为契约,后者验证行为正确。
举个例子,一个“日志分析 Skill”如果只写“你需要分析日志并找出错误”,那它连 Prompt 都不如。合格的版本应该写清楚:日志文件可能多大、什么格式、常见的错误码含义、按什么维度聚合、最终输出什么结构,同时附一个统计脚本,自动把高频错误和异常趋势算出来。这样才能保证无论谁来调用,产出的质量和格式是稳定一致的。
2.2 Skill 的核心组成:声明、说明、参考实现、测试
我把一个工程化的 Skill 拆成四个模块来看。
第一是元信息声明。包括 Skill 的名字、描述、触发条件、依赖环境。这部分不是给人看的,是给 Agent 的“路由系统”看的。描述写得好不好,直接决定了模型在遇到任务时会不会选到这个 Skill。很多 Skill 不好用的首要原因就是描述太差,模型根本判断不出什么时候该调用它。
第二是行为说明。这是 SKILL.md 的主体,告诉模型“按什么顺序、用什么方式、输出成什么样子”。行为说明要足够细——步骤要编号,边界要写死,异常分支要讲清楚。
第三是参考实现。也就是配套的脚本或模板代码。它们的作用是把“确定的部分”从模型的推理压力里解放出来。比如解析 JSON、计算聚合指标这类操作,交给脚本做又快又稳,不需要模型自己发挥。
第四是测试用例。一套能验证 Skill 是否正常工作的输入输出样例。没有测试,Skill 就是个盲盒,没人知道它在下一次调用中会不会打出离谱的结果。
2.3 一个好 Skill 应该满足的三个标准
我衡量一个 Skill 是否合格,只看三个标准。
第一,可触发。模型读到对应任务时,能够稳定地把这个 Skill 选出来。换句话说,它的描述和业务场景之间要有足够明确的映射,不能是那种模棱两可的泛泛而谈。
第二,可执行。按照 SKILL.md 里的流程,配合参考脚本,确实能跑通,产出符合预期。
第三,可验收。你有一套明确的检查办法,能判断 Skill 的输出到底对不对,而不是“看起来差不多”。不可验收的 Skill 写得再漂亮也无法迭代。
这三个标准听着简单,但当我拿它去检视自己手头的 Skill 时,会发现一大批项目连第一条都过不了——因为描述写得不清不楚,Agent 在关键时刻根本不调用它。
3. 工程实现:手把手教你写一个能用的 Skill
3.1 目录结构与命名规范,先搭好一个不会乱的架子
我建议每个 Skill 在项目里单独占一个目录,目录名就是 Skill 的名字,内部再按“说明、脚本、资源、测试”拆分子目录。以一个通用的格式为例:
skill-name/ ├── SKILL.md ├── scripts/ │ ├── run.py │ └── utils.py ├── assets/ │ ├── templates/ │ └── examples/ └── tests/ ├── case1.md └── case2.md目录命名必须短、具体、一眼能看懂用途。比如log-analyzer就比analysis-tool好得多——前者明确了对象和动作,后者是个谁都能往里塞东西的筐。
scripts目录放 Skill 运行时需要调用的脚本。assets放模板文件、参考输出样例。tests放测试输入和预期的输出结果。这个结构不是硬性标准,不同框架的约定略有差异,但“说明、脚本、测试三者分离”的思路是通用的。
3.2 SKILL.md 该怎么写才不会被模型当作空气
这是全场最关键的一步。SKILL.md 写不好,后续的工作白搭。我根据自己的踩坑经验,把它拆成几个区块来写。
第一块,是身份与能力边界。开头就要直接告诉模型这个 Skill 叫什么、在什么范围内有效。比如“本 Skill 用于服务端日志文件的错误模式分析,不处理前端埋点数据”。能力边界写得越清楚,模型越不会乱用。
第二块,是触发条件。要明确列出什么情况应该使用、什么情况坚决不要用。触发条件不能太窄,太窄了模型想用的时候找不到它;也不能太宽,太宽了模型会在无关场景下硬套。我的经验是,至少列出三个肯定触发的场景作为正例,再列两个禁止触发的场景作为负例。
第三块,是执行步骤。用有序列表逐步描述工作流。每一步都要具体到“做什么、怎么做、产出什么”。不要只写“分析日志”,要写“先用脚本提取时间窗口内的错误码分布,再按错误码聚合相关上下文,最后输出 JSON 格式的汇总报告”。
第四块,是输出规范。直接用模板定义一个输出格式,可以是 JSON、Markdown 表格或自定义文本格式。最好是给一个完整的输出样例,让模型照着填。这一步能显著提升结果的可控性。
第五块,是失败兜底。告诉模型在遇到什么情况时应该放弃、向上报告,或者尝试降级处理。没有兜底的 Skill 一旦出错,Agent 就可能陷入死循环。
用我自己的一个精简版为例:
# Log Analyzer 本 Skill 用于分析服务端日志文件,定位错误模式并输出量化摘要。 不对前端埋点、业务订单数据做分析。 ## 适用场景 - 用户反馈接口报错率上升,需要快速定位错误码分布。 - 上线后需要检验日志中是否出现新的异常类型。 - 定时巡检多个服务的错误日志。 ## 不适用场景 - 实时流式日志的监控(请交给告警系统)。 - 需要逐条阅读的深度业务日志。 ## 执行步骤 1. 确认日志路径与格式,必要时先预览前 50 行。 2. 调用 scripts/run.py 统计错误码分布和时间趋势。 3. 对 TOP5 错误码,提取各自的典型上下文。 4. 按输出规范生成摘要。 ## 输出规范 { "time_window": "...", "total_errors": 0, "top_errors": [], "trend": "increasing/decreasing/stable" } ## 失败兜底 - 日志文件不存在或为空,直接返回提示信息。 - 脚本运行出错,附上原始报错并停止流程。这里最容易被忽略的是“失败兜底”这一段。没有它,模型在脚本报错时往往会凭想象力编一个结果——这错得很隐蔽,很致命。
3.3 参考脚本的设计原则:不要什么都指望模型推理
Skill 里要不要带代码,是很多人的纠结点。我的原则很简单:凡是确定性逻辑,全部下沉到脚本;凡是开放判断,才留给模型。
打个比方,统计错误码出现次数,这种逻辑是确定的,人类看一眼代码就能验证,让模型去数反而可能数错,就应该写成脚本。而“整体报错趋势是上升还是下降”这种需要结合业务上下文判断的结论,则适合让模型综合输出。脚本的价值是把模型从“计算器”这个不称职的角色里解放出来,让它专注做“分析师”。
设计脚本时还要注意容错。日志格式千奇百怪,脚本至少要做到:文件不存在时给出明确报错、解析失败的行跳过而不是中断、输入超大文件时有合理的采样策略。这些细节看着琐碎,但真到了 Agent 自动运行的时候,每个没有兜底的分支都可能变成一个白痴行为。
另外,脚本的运行方式要写清楚,最好在 SKILL.md 里给出标准命令。不要让模型猜是python3 run.py还是bash run.sh。能显式规定的,就不要留给模型自由发挥。
3.4 测试与调优:怎么确认 Skill 真的在生效
很多 Skill 发布了就没人管,这是工程化的大忌。我建议每个 Skill 至少要配三组测试:一组典型正常场景,一组边界场景,一组不该触发场景。
典型正常场景就是技能最核心的任务,验证“走正常流程能不能跑通”。边界场景测试的是“输入有变化时,Skill 是否能优雅处理”。不该触发场景测试的是“模型在无关任务上会不会误用这个 Skill”。三组测试都过了,Skill 才敢说基本可用。
做完测试还要做“可观测性验证”。一个常见困惑是:怎么知道模型到底有没有按我的 SKILL.md 走?我的做法是让 Skill 在流程中留下“执行痕迹”——比如规定模型在输出摘要的末尾附加一个元信息块,或者固定调用一个能写日志的脚本。这样当你审查结果时,能一眼看出它到底走了没有。
调优时,优先检查触发条件和执行步骤,不建议一上来就改提示词的口吻。大部分 Skill 失灵,问题都出在“模型压根没选到它”或者“选到之后不知道下一步具体该做什么”上。
4. 你大概率正在滥用 Skill 的 5 个信号
4.1 信号一:把 Skill 当成了 Prompt 收藏夹
这是我见到的最高频错误。很多人写的 Skill 其实是“换个格式的精美 Prompt”:“你是一位资深的产品经理,请用结构化思维分析这个问题……”——Stop。
Skill 必须有可执行性,至少要包含操作步骤、工具调用或输出规范这些“带骨架”的内容。如果一段话去掉“Skill”的外壳之后,本质上还是“请帮我做好 X”,那它就是 Prompt 披了个马甲。把这种内容挂到 Agent 上,不会让模型变强,只会让你的配置文件变长、路由开销变高。
我自己的判断标准是:如果写完的 Skill 没有任何脚本、没有任何能机器校验的输出规范,也不包含特定领域的分步方法论,那我就不该叫它 Skill。
4.2 信号二:一个 Agent 挂了十几个互不相关的 Skill
有人为了追求功能齐全,把市场调研、周报生成、代码审查、PPT 排版全挂到同一个 Agent 上,结果每次任务触发时,模型都要在十几个 Skill 描述里做一次“路由决策”。决策空间越大,选错的概率越高;选错了,后续输出就跑偏,而且跑偏得很隐蔽——因为模型常常会表现得“看起来很像那么回事”。
我在项目里的经验是,单个 Agent 上同时挂载的 Skill 最好不超过四五个,且这些 Skill 应该在领域上有某种相关性。如果业务场景差异太大,拆成多个不同的 Agent,分别挂各自的 Skill 组合,效果会比“一个大而全的 Agent”好得多。
4.3 信号三:重复发明轮子,把模型原生能力包成了 Skill
模型的上下文理解、通用推理、基础文本改写,这些能力本身就是内置的,你不需要为它们写 Skill。比如“将一段英文翻译成中文并润色”这种任务,直接对话就能完成,包一层 Skill 除了增加截断风险毫无帮助。
优雅的边界是什么样的?判断标准是“是否需要领域知识或固定流程”。如果任务每一步都依赖模型临场发挥,没有外部依赖、没有固定步骤、没有独特产出规范,那它就是原生能力,不该包装成 Skill。Skill 应该用在“一个外行无法通过简单对话直接完成”的领域任务上,比如法律文书一致性审查、行业数据源汇总、复杂报表生成。这些任务里有固定的业务规则和产出标准,才需要沉淀成 Skill。
4.4 信号四:只写不给测,Skill 成了盲盒
Skill 本质上是代码资产,不测试就是负资产。我见过很多团队兴致勃勃地写完一个日志分析 Skill,结果真实日志里只要出现一种 SKILL.md 没覆盖的格式,脚本就崩了,模型就在崩溃边缘疯狂补丁,越补越离谱。
正确的做法是把测试当成 Skill 开发的一部分,每次改动 SKILL.md 或者脚本,都至少要跑一遍回归。我不是说一定要上 CI/CD,但至少你心里要对“这个 Skill 现在能不能用”有一个明确答案。连这个答案都给不出,就不要把它放进任何会自动运行的系统里。
我一直强调,不可验收的 Skill 是无法迭代的。因为你在出问题的时候根本不知道是脚本的问题、SKILL.md 指导的问题,还是模型执行的问题,最后只能靠猜。
4.5 信号五:版本混乱、没有维护责任人
Skill 这种形态很容易让人低估它的维护成本。Skill 会依赖模型版本、依赖脚本环境、依赖所在项目的具体情况,任何一个变量变了,它都可能开始“发神经”。
我在实际的工作中有个体会:Skill 库和代码库一样,需要版本管理、变更记录、一个明确的责任人。否则三个月后模型版本升了,某个 Skill 突然开始产生奇怪结果,你翻遍记录都找不出它是什么时候变的、为什么变,只能从头排查。
5. 常见问题与排查技巧实录
5.1 Skill 加载了但不生效,先分清是“没看到”还是“做不到”
Skill 不生效,需要先确认故障在哪一层。把排查分成三层:加载层、选择层、执行层。
加载层,是 Skill 文件有没有被正确放到 Agent 约定读取的目录,命名、格式是否符合规范。很多框架对目录名和 SKILL.md 文件名有严格约定,一个字符错了就静默失败。
选择层,是模型面对当前任务时,到底有没有把这个 Skill 纳入候选。这一步最容易出问题。如果你想测试,我建议直接在一个新会话里问模型“当前环境下有哪些 Skill 可用”,先看它能不能报出你的 Skill 名字;再给一个明确触发场景,看它会不会主动选择。选不到的话,问题一般出在 SKILL.md 的描述语句上——太泛、太偏、和目标任务同名性太弱,都会让路由失败。
执行层,是模型选择并读取 SKILL.md 之后,有没有按步骤执行、脚本有没有跑起来。这一层需要看工具调用日志。如果脚本根本没被调用,说明 SKILL.md 里的步骤写得不够显式,模型选择性地跳过了;如果脚本调用了但结果不对,再回到脚本本身的容错上排查。
5.2 模型总是不按 SKILL.md 的步骤走,怎么办
这一步的根因通常是步骤粒度太粗。多数模型执行长流程时,倾向于按自己的“常识”来理解一步操作,而不会脑补你没有写的细节。比如你写“提取错误码”,模型不知道是要用 Python 脚本还是用 grep,这时它的选择就不可控了。
解决方法是把每一步写成像机器指令一样明确。建议给每个步骤配上“用什么工具、执行什么命令、读取哪个文件、产出什么中间结果”。宁可多写几行,也不要留给模型发挥空间。我测试过同一个 Skill,把步骤从“2. 统计错误码”改成“2. 运行 scripts/run.py --input <日志路径> --output /tmp/error_summary.json”,执行准确率提升非常明显。
另外一个容易踩的点是:SKILL.md 太长。如果一份 SKILL.md 超过了上下文窗口的舒适区,模型在后续生成过程中可能会“遗忘”后半段的规范。好东西要学会精炼,步骤能合并就合并,说明能去冗余就去冗余。
5.3 多个 Skill 冲突了怎么办
冲突场景最典型的是:同一个任务既能由 Skill A 处理,也能由 Skill B 处理,结果模型二选一的时候选错了。我的处理办法是给 Skill 设置显式的边界声明,在描述里直接写明“本 Skill 优先处理 X 场景,Y 场景请交给 Z 处理”。
如果冲突仍然存在,就要回到触发条件设计的正反例上。把常见的易混淆场景,分别放进两个 Skill 的“不适用场景”段里。这样等于在路由层面做了人工消歧,能显著降低选错概率。
5.4 Skill 写太多之后 Agent 变笨了,怎么处理
这是系统性问题的症状,不是单个 Skill 的问题。Skill 多了以后,每个 Skill 的描述都要占用模型的注意力,路由决策变慢,错误率变高。
我会定期做归档操作:把低频使用的 Skill 移出默认加载列表,改成按需加载;把高频使用的 Skill 合并同类项;把已经证明无效的 Skill 直接删除。不要有不舍得删的心理——Skill 是资产,但它首先应该是“可用资产”,不可用的东西躺在那里,只会继续消耗上下文和注意力。
判断一个 Skill 留不留,我只有一个问题:如果这个 Skill 被删了,这些任务你会不会真的觉得不方便?如果答案是不会,或者不确定,那它就该被归档或重写。
写在最后的几句实在话
Skill 是好东西,但它是一种工程产物,不是玄学道具。我个人的经验是,在决定要不要造一个 Skill 之前,先回答三个问题:这个任务是否会反复出现三周以上?它是不是有固定的多步操作流程?它是否需要结合外部脚本或模板才能稳定完成?如果三个答案里至少有两个是肯定的,再考虑把 Skill 提上日程;否则,写 Prompt 就够了。
代码写多了之后,人会对“封装”产生本能好感,但在 Agent 领域,每一次封装都在给系统的路由和决策增加成本。克制,本身就是一种工程能力。希望这篇拆解能帮你在下一次动手前,多想一步:你到底是在打造一件趁手的兵器,还是仅仅在往工具箱里囤积一件精美的摆设。