最近折腾Agent技能包(skills)的人越来越多了,无论是Claude生态还是Codex生态,你都会发现同一个趋势——大模型本身的“聪明程度”已经不是瓶颈,怎么让它稳定地执行一套标准流程、调用正确的工具、输出符合预期的结果,才是真正拉开体验差距的地方。Skills就是用来解决这个问题的:把一段可复用的能力打包成标准化的技能文件,让Agent在需要的时候自动加载、按步骤执行。这篇文章我打算从第一性原理出发,把skills的底层逻辑、开发方法、安装生态和实战经验一次讲透,给正在研究Agent开发、想让自己的AI工作流更稳定的朋友一份可以直接落地的参考。
1. 先说清楚skills到底是什么
1.1 你其实每天都在用“伪skills”
很多人在接触skills之前,其实已经在用类似的思路了——比如在系统提示词里塞一大段“你是一个擅长XXX的助手”,或者在每个任务开始时把一段长长的背景说明粘贴给模型。这些都是“伪skills”,它们看起来能让模型表现得专业一点,但本质上只是临时性的指令注入,没有办法被复用、不能被检索、也不会被模型主动“意识到自己需要调用”。
真正的Agent skills是一套结构化的、可持久化的能力封装。它的核心不是“告诉模型怎么做”,而是“给模型一套可以按需调用的标准化操作手册”。模型在拿到任务时,会先理解任务内容,然后从自己的技能库里判断:这个任务是否匹配某个技能?匹配的话就加载这个技能对应的说明文件和脚本,按照里面的步骤去执行。
我用一个生活化的类比来解释:把模型想象成一个新入职的工程师,他脑子很聪明、学习能力很强,但他不知道你们公司的代码规范、不知道部署流程、不知道线上出现问题该找谁。Skills就是把“新人入职手册”写成标准文档放到他桌上,他遇到对应场景时会自己去翻手册,而不是每天都靠你口头嘱咐一遍。
这里的关键差异在于“主动检索”和“被动等待”。伪skills是模型被动接收的上下文,skills是模型主动调用的工具集。这个差异在实际体验中非常明显:伪skills在对话长度增加后会被逐渐遗忘,skills则不会,因为模型每次执行前都会重新读取技能文件。
1.2 一个skill组件的真实构成
从实现层面看,一个标准的Agent skill由三大部分组成:描述文件(通常是SKILL.md)、可执行脚本(Python/Shell/JavaScript等)、以及辅助资源(模板、配置文件、数据文件等)。
描述文件是整个技能的“门面”,它决定了模型什么时候会调用这个技能。我见过太多人写不好这个文件,导致技能明明写得很完善,Agent却从来不触发。描述文件里最重要的是frontmatter区的name和description字段,description尤其关键,它需要精准描述“这个技能在什么场景下使用”,而不是简单写一句“用于处理图片”这种模糊描述。我在实战中发现,description的措辞会直接影响触发率,写得越具体、越带场景化,触发越稳定。
脚本部分则是技能的执行体。模型读取描述文件后,会按照里面的指引生成调用脚本的命令,然后通过Agent的沙箱环境执行脚本,把结果反馈给它。这个过程对模型的推理能力要求并不高,真正考验的是脚本本身的健壮性——脚本需要处理好参数校验、异常捕获、输出格式统一这些基础问题,否则Agent再聪明也会被不靠谱的脚本拖垮。
辅助资源往往是被忽视的一环。一个写好的技能,最好能把模板文件、参考样例、依赖清单都放到技能目录下,让模型在需要时可以自行查看。这样一来,技能就从一个“会执行动作的工具”升级成了“自带知识库的完整工作流”,在复杂任务中的表现会好非常多。
2. 动手开发一个自己的skill
2.1 目录结构与SKILL.md的标准写法
先给大家一个最基础、最标准的目录结构,我建议所有技能都从这个骨架开始:
my-skill/ ├── SKILL.md ├── scripts/ │ ├── compress.py │ └── requirements.txt └── assets/ └── template.mdSKILL.md是模型的入口文件,它的frontmatter格式在不同平台上略有区别,但基本都遵循YAML规范。以我目前用的方案为例,下面是SKILL.md的完整示例:
--- name: image-compress description: 批量压缩指定目录下的图片,适用于需要优化网页资源体积、减小图片大小、提升加载速度的场景。不建议用于需要保留100%无损质量的图片处理场景。 --- # 图片批量压缩 ## 何时使用 当用户提供图片目录路径,并表达压缩、优化、减小体积等意图时使用。 ## 执行步骤 1. 确认目录存在且包含支持的图片格式(jpg/jpeg/png/webp) 2. 检查scripts目录下脚本的运行环境,必要时先安装依赖:pip install -r requirements.txt 3. 执行命令:python scripts/compress.py --input-dir [目录路径] --quality 80 4. 将压缩前后的文件大小对比反馈给用户 ## 注意事项 - 压缩png时建议保留原图备份 - 输出格式统一使用表格,不要用markdown外的其他格式这里有几个写作要点非常关键。description不能使用否定式描述空泛带过,要给出正向触发条件和反向不适用场景,帮助模型快速判断。正文部分的结构要清晰,让模型看到“何时使用”四个字就能建立条件反射式的触发逻辑。执行步骤要写成“直接可执行的指令”,而不是“应该做什么”的意图描述,不要出现“分析一下”这种模糊动词。
我实际写了几十个skill之后发现,SKILL.md的篇幅也需要控制。太短会让模型缺少上下文,太长则会消耗大量token、影响模型对关键信息的提取。根据我自己的体感,大部分成功的SKILL.md在500到1000字之间,核心指令尽早在前面出现,细节尽量放后面。
2.2 推理与执行的分离原则
这是skill开发里最重要、也最反直觉的一条原则:描述文件管推理,脚本管执行,两者不要混在一起。很多新手写skill时,喜欢把所有逻辑都塞进SKILL.md,让模型“根据规则自己想怎么做”,这其实走回了伪skills的老路。
Skills设计的本意是:人类把确定性逻辑写进脚本,模型只负责判断“要不要用”和“怎么调”。这么做有非常实际的好处。第一,确定性逻辑交给脚本后,执行效率高、错误率低,不会因为模型幻觉导致步骤错乱。第二,脚本可以用Python的完整生态处理文件、调用API、解析数据,这些能力模型在沙箱里很难稳定复现。第三,调试方便——脚本出问题可以直接跑脚本看报错,不用再审一遍对话记录。
我开发时遵循一个简单法则:凡是能用代码实现的内容,绝不让模型“思考”完成。比如“批量压缩图片”这件事,模型不需要知道Pillow库内部怎么处理像素,它只需要知道运行哪个脚本、传什么参数、输出什么格式。脚本代码是确定性的,模型只要按说明调用,结果就可预期。
有一点需要提醒:脚本参数的设计要尽量简单直观,不要设计太多难以理解的flag。模型生成的命令不一定完全按你预期来,参数越少越不容易出错。我在设计脚本时,一般只保留两到三个必要参数,全用--param value格式,并在SKILL.md里给一两个默认值示例,模型照抄就能跑通。
2.3 写一个可用的命令行脚本
上面示例里的compress.py,我贴一个具体可跑的实现:
#!/usr/bin/env python3 """批量压缩图片脚本,支持jpg/png/webp格式。""" import argparse import os from pathlib import Path from PIL import Image SUPPORTED_FORMATS = {'.jpg', '.jpeg', '.png', '.webp'} def compress_image(input_path: Path, output_dir: Path, quality: int) -> str: """压缩单张图片,返回压缩后文件大小。""" try: img = Image.open(input_path) output_path = output_dir / input_path.name if input_path.suffix.lower() in {'.jpg', '.jpeg'}: img.save(output_path, quality=quality, optimize=True) elif input_path.suffix.lower() == '.png': img.save(output_path, optimize=True) else: img.save(output_path, quality=quality) original_size = input_path.stat().st_size compressed_size = output_path.stat().st_size return f"{input_path.name}: {original_size} -> {compressed_size} bytes" except Exception as e: return f"{input_path.name}: ERROR {e}" def main(): parser = argparse.ArgumentParser(description='批量压缩图片') parser.add_argument('--input-dir', required=True, help='图片目录路径') parser.add_argument('--output-dir', default='compressed', help='输出目录,默认compressed') parser.add_argument('--quality', type=int, default=80, help='压缩质量,默认80') args = parser.parse_args() input_dir = Path(args.input_dir) if not input_dir.is_dir(): print(f"目录不存在: {input_dir}") return output_dir = Path(args.output_dir) output_dir.mkdir(parents=True, exist_ok=True) results = [] for file_path in sorted(input_dir.iterdir()): if file_path.suffix.lower() in SUPPORTED_FORMATS: results.append(compress_image(file_path, output_dir, args.quality)) for result in results: print(result) print(f"处理完成,共{len(results)}张图片") if __name__ == '__main__': main()这个脚本代码本身很简单,强调几个容易被忽略的细节。第一,输出信息一定要结构化、包含文件名和前后大小对比,模型需要这些信息给用户做反馈。第二,异常不能静默吞掉,要用ERROR标记,这样模型读取输出时能识别哪张图片出了问题。第三,输出目录要自动创建,减少模型额外操作的步骤。
脚本写完后,我会先直接在终端跑一遍,确认输入输出都符合预期,再放入skill目录。这一步非常必要——如果你自己手动调用都报错,模型调用时只会更乱。
3. 安装、分发与生态盘点
3.1 官方市场与社区平台去哪儿找
现阶段,skills的安装方式主要有三大类:手动复制目录、通过市场/平台安装、从代码仓库克隆。
手动复制是最原始也最通用的方式,适用于所有支持skills的Agent。以Claude生态为例,个人级技能放在用户目录下的.claude/skills/,项目级技能放在工程目录下的.claude/skills/。Codex CLI则对应.codex/skills/。直接把技能文件夹放进去,重新启动会话就能生效。这种方式对单机用户很友好,但没法解决多设备同步和版本管理的问题。
市场与平台是更系统的分发方式。目前社区里已经出现了不少收集和分发skills的平台,形式类似npm或者Hugging Face的模型库,用户可以浏览、搜索、一键安装。我个人的经验是,选平台时重点看三个指标:更新频率、审核机制、评论质量。更新频率决定技能会不会跟着模型能力演进及时优化,审核机制决定技能质量的下限,评论质量则能帮你避开明显有坑的技能。
代码仓库(尤其是GitHub)依然是最硬核的获取渠道。很多开发者会把自己的skills仓库开源,仓库里包含多个技能目录、使用说明和版本历史。这种方式的好处是可以直接git clone下来,还能通过PR跟踪别人的改进,对想深入研究的开发者特别友好。
3.2 Claude与Codex的差异点
市面上主流的Agent对skills的支持并不完全一样,我目前深度使用过的两个代表是Claude系和Codex。
描述格式上总体一致,都是SKILL.md加脚本的结构,但细节有差异。Claude的skills更强调对长文档的处理,SKILL.md里允许写较多的上下文和规范;Codex的skills相对更轻量,description往往起到决定性作用,正文太多反而容易被忽略。
触发机制也有区别。Claude Agent在执行任务时,会把技能库中的description都读一遍做一个匹配判断,之后再选择加载哪些技能文件。Codex CLI则采用更轻量的模式,依赖模型在操作过程中的自主判断。这个差异导致了调参思路的不同:在Claude生态里,description像“目录索引”,要让匹配算法容易命中;在Codex生态里,description像“广告文案”,要让模型在开放式推理中更容易联想到。
沙箱环境方面,Claude提供了更完整的沙箱隔离机制,脚本执行的资源控制较严格;Codex则更像本地执行,与系统环境的交互更直接,写文件、跑命令都更自由。这带来一个实用建议:涉及大量文件操作、需要调用系统本地方令的技能,在Codex环境里更容易开发;需要与外部API稳健交互的技能,Claude的沙箱隔离能提供额外的安全层。
对普通用户来说,不用纠结选哪个生态,建议以你日常使用的Agent为准,技能结构两边共用,只需调整描述文件和安装目录的细节就行。
3.3 测试skill的完整流程
测试一个skill,我按照从浅到深的顺序走四个阶段,能避免后期反复返工。
第一步是静态检查。检查SKILL.md的frontmatter格式是否合法,路径是否与脚本实际位置一致,脚本依赖是否声明。这一步可以自动化,我习惯写一个小脚本扫描目录,看每个技能的frontmatter是否完整。
第二步是单次调用测试。直接给Agent一个明确指令,看它是否能触发技能、输出的中间信息是否正确、最终结果是否符合预期。注意观察Agent的“思考过程”——如果它没有读取SKILL.md而是用自己的理解乱操作,那问题多半出在description,需要重写。
第三步是边界测试。把输入条件改到极致,比如空目录、超大文件、损坏图片,看脚本会不会崩、Agent会不会被误导。这一步非常考验脚本的健壮性,我发现很多技能在正常路径下表现完美,一到边界情况就原形毕露。
第四步是回归测试。连续跑多个任务,确认技能不会因为上下文积累而“忘了自己”,也不会与另一个技能的功能重叠产生混乱。理论上,每次Agent都需要重新读取技能文件,状态是独立的,但实际中我遇到过触发优先级冲突的情况——两个技能都对同一个任务“感兴趣”,这时候就需要调整其中一个技能的description,明确“不适用于”的场景。
4. 实战中踩过的坑与避坑技巧
4.1 触发不稳定的根源在描述文件
如果你发现技能写好了、安装对了,但模型就是不用它,十有八九问题出在description上。我拆解过大量这种案例,归纳出三个高频根源。
第一个是description写得太抽象。光说“处理图片”四个字,模型无法判断什么任务算“需要处理图片”,它可能就默默忽略了你。正确写法是带上具体场景词,比如“当用户要求压缩图片尺寸、优化页面加载速度、减小图片文件体积时使用”。
第二个是description缺少反向条件。只告诉模型什么时候用,不告诉它什么时候不用,会导致过度触发。比如一个“图片压缩”技能,用户只是想让图片变滤镜效果,模型也可能误触发。在description里加一句“不要用于添加滤镜、修改颜色、裁剪等非压缩操作”,触发准确率立刻提升。
第三个是正文结构混乱。模型读取SKILL.md后,需要在几秒内快速建立行动方案。如果正文一堆大段论述、步骤藏在一大段话里,模型的提取效率会大幅下降。把正文改成“何时使用-执行步骤-注意事项”三段式,是最稳妥的写法。
我还发现一个细节:部分Agent对SKILL.md的换行和缩进非常敏感。YAML里如果缩进不一致、列表符号混用,解析时会静默失败。写完后,把文件丢到一个YAML校验器里过一遍,能节约很多排查时间。
4.2 环境依赖和路径陷阱
脚本运行时,依赖和路径这两个问题出现的频率最高。
依赖问题主要是“脚本里import的库在Agent沙箱里没装”。我就遇到过技能在本地跑得很顺、放进Agent环境后疯狂报ModuleNotFoundError。解决方案有两个:一是SKILL.md里明确写清楚需要先执行pip install -r requirements.txt;二是尽量只用标准库解决,或者把依赖声明成必须的可选项。我目前更倾向标准库优先——Pillow虽然好用,但原生标准库做不了图片处理,那就把依赖声明写清楚,让模型先装再跑。
路径问题则更隐蔽。Agent沙箱的工作目录往往和你的本地目录不同,如果你的脚本用相对路径,可能在用户机器上就是另一套目录结构。最佳实践是:所有路径相关参数必须通过命令行参数传入,脚本内部一律用绝对路径形式处理;SKILL.md里的示例命令也要写明“用用户提供的实际路径替换示例路径”,避免模型死板照抄导致路径错误。
另一个我经常踩的坑是权限问题。脚本写文件时,如果Agent沙箱对某些目录只读,就会出PermissionError。解决方法是:输出目录统一放在用户指定的可写目录,或者默认放/tmp之类Agent明确可写的位置,并且脚本要提前mkdir并带上exist_ok=True,避免目录冲突。
4.3 什么时候不该用skill
说了这么多,最后聊聊skills的边界。并不是所有能力都适合封装成skill,强行封装反而增加维护成本。
低价值、一次性任务不值得。如果一个操作你三个月才做一次、每次场景变化都很大,写成技能后的维护成本比临时提示词高得多。技能的真正价值来自“高频、稳定、可复用”的三重属性,缺一都不划算。
高度依赖实时判断的任务要慎重。比如客服对话、心理咨询这类需要深度理解语境的任务,硬编码成固定步骤会显得僵硬,反而拉低输出质量。模型的临场发挥能力应该在推理阶段体现,而不是被脚本五花大绑。
需要外部密钥或私有账号的能力要额外小心。技能脚本如果涉及API调用,我不建议把密钥直接写在脚本里,至少要用环境变量注入,避免技能分发后密钥泄露。
最后还有个体验层面的坑:技能加载过多反而拖慢速度。每次任务开始时,Agent都要过一遍技能描述来做匹配,如果你的技能库里有几十个描述文件,匹配判断的消耗会明显影响响应速度。定期清理不再使用的技能,保持技能库精简,长期看很有必要。
我在日常使用中逐渐形成的习惯是:技能库永远控制在10个以内,每个都保持单一职责,宁可多装一个轻量技能,也不做一个臃肿的全能技能。这个原则帮我在Agent的响应速度和任务准确率之间找到了很好的平衡点,也推荐你尝试。