最近把一门AI实战方向的公开课第二期反复看了两遍又自己动手试了两天总算把Agent Skills这套东西从听了个概念变成了确实能落地的经验。第一期讲工作流的时候我觉得已经够用了给模型定义好步骤规定好工具大部分自动化任务都能跑起来。但第二期讲到的Agent Skills直接改变了我对模型会调用工具这件事的理解。以前做Agent最头疼的是各种工具定义、操作规则、脚本说明全塞进上下文里prompt越来越长模型的表现却越来越飘。规则写少了模型瞎发挥写多了它反而抓不住重点。Agent Skills就是为了解决这个矛盾出现的不把所有内容塞进对话而是把指令和脚本打包成一个可以按需加载的技能包模型在真正需要的时候才去读取。这篇文章算是我自己的学习笔记加实操记录从核心概念、它能解决什么问题、怎么做、怎么调、常见坑这五个维度展开适合正在做Agent开发、优化自动化流程、或者被超长prompt困扰的同学。不管你是第一次听说这个功能还是已经上手试过都应该能从里面翻出点有用的东西。1. Agent Skills到底是什么一个装好说明书的工具箱1.1 官方定义拆解Agent Skills本质上是一个有固定结构的目录目录里装了两类东西一个SKILL.md文件以及配套的脚本和资源文件。SKILL.md用标准Markdown写成顶部有一段YAML格式的元信息里面最关键的是name和description。元信息下面是正文说明这个技能是干什么的、在什么场景下使用、具体怎么操作。脚本部分可以是Python、Shell、Node等任何你熟悉语言的程序也可以放一些模板、数据文件、配置文件之类的东西。我把这个过程类比成一个场景。工具调用Function Calling相当于一个工具箱你把螺丝刀、扳手、电钻摆在那儿模型需要哪个拿哪个。但Agent Skills更像是工具箱外加一份说明书模型不是一个什么都会的老工程师而是一个刚入职的新人看到说明书上写着当你需要拧开面板上十字螺丝的时候打开第二个抽屉里面有三号螺丝刀和扭矩要求它不会一上来把所有工具平铺在桌上翻找而是按说明一步步操作。这个按需读取的特性是Agent Skills和普通工具调用最本质的区别。普通工具调用的函数定义是放在对话上下文里的每一轮都占用token技能包则放在模型可以访问的文件系统里模型先看到的是技能的目录——也就是名称和描述。它判断当前任务需要某个技能时才会真正打开SKILL.md阅读详细指令再决定运行哪些脚本。这个过程有点像搜索引擎先看结果标题和摘要觉得相关才点进去看全文。1.2 和System Prompt、Tool Calling、MCP有什么不一样很多人一开始会把Agent Skills和已有概念搞混我自己也花了一点时间才分清边界。简单粗暴地理解System Prompt是常驻规则每轮对话都跟着上下文走。你把技能说明写进System Prompt等于让模型一直背着全套说明书在跑。短任务还好长任务下规则密度太高模型很容易在关键步骤上迷路。Tool CallingFunction Calling是定义函数和参数模型在对话中发起调用请求。它适合单次、原子化的操作比如查天气、算数学题。但一旦任务需要多步骤、多脚本协作函数定义会变得很长参数校验和错误处理逻辑也会迅速膨胀。MCP解决的是连接问题它用标准协议把外部数据源、工具的访问能力交给模型。MCP关注的是能不能访问某个资源而Agent Skills关注的是一个完整任务怎么做完。现实里两者经常搭配使用技能里写明需要外部数据时就调用某个MCP工具。我把这四者的对比如下表整理了一下方便对照方案本质上下文占用适用场景典型问题System Prompt所有指令写在对话里每轮全量消耗身份、边界、全局规则指令长时被稀释越写越无效Tool Calling定义函数模型发起调用函数定义在上下文中单次原子操作多步骤任务难编排MCP标准化接入外部数据/工具连接配置数据源访问、工具发现只解决能访问不管怎么做Agent Skills指令脚本打包按需加载只有命中才加载高频、多步骤、可复用任务需要规范目录结构和描述质量这四种方式不是替代关系是配合关系。实战中我的习惯是System Prompt负责说清楚你是谁、目标是什么、绝对不能做什么MCP负责接外部数据Tool Calling负责拆单个动作Agent Skills负责把一套完整的、重复发生的流程沉淀下来。2. 为什么要有Agent Skills它到底解决了什么问题2.1 把说明书塞进上下文还是把说明书放在工具箱里在Agent Skills出现之前实现一个多步骤任务通常有两种做法。做法A是穷举法把所有可能用到的规则、代码示例、约束条件、边缘情况全部写进System Prompt。我早期的项目就是这么干的一个日志分析Agent的System Prompt写了三千多字包括遇到空值怎么办格式不统一怎么处理输出报告要包含哪些板块颜色代码怎么标等等。结果模型在简单场景表现很好一到复杂场景就开始顾此失彼。后来我把每条规则单独拎出来做消融测试去掉哪条规则对应行为就崩坏说明它根本没有真正遵守全部规则只是在靠上下文中的局部模式碰运气。原因就是指令稀释模型上下文窗口是固定的规则一多注意力被摊薄离实际触发点较远的指令权重会明显下降。做法B是工具穷举把一个任务拆成十几个函数全部定义在tools参数里。这样做的缺点是每次对话都要消耗大量token来传输函数签名和参数说明而且函数之间的状态流转需要模型自己记忆一旦涉及上一步清洗完的数据是哪个文件这类问题模型经常记岔。我做过一个数据处理Agent工具数量到了十五个之后模型隔三差五就会把参数传错最后调试变成了一场灾难。Agent Skills的做法是第三种不把说明书放在对话里而是放在技能包里。对话只保留任务描述本身。模型看到请帮我生成一份月度报表会先在技能目录里检索名称和描述发现有个技能叫monthly_report描述写着当用户需要清洗数据、聚合统计、生成Markdown报告时使用于是加载对应的SKILL.md按里面的步骤调用脚本。2.2 按需加载是效率和准确率的双重胜利课程里有一页内容我印象最深一个长文档批量处理的场景原本每个子任务都要把一堆操作规则和脚本调用方式放在上下文中改成技能化打包之后每轮的平均上下文占用肉眼可见地下降模型的输出质量反而更稳定了。虽然具体数字我记得不太精确但当时给我的冲击感是这不仅是优化这是架构升级。为什么会更稳定可以从两个角度理解。第一模型决策时用的注意力预算是有限的上下文里的信息越杂关键指令被真正执行的概率越低。技能化之后指令是用的时候才放进上下文而且是精准放进当前任务的上下文模型相当于在一张干净的桌面上工作而不是在一堆文件里翻找。第二SKILL.md里的步骤可以写得非常详细详细到怎么算输入文件路径输出文件的命名规则失败时退出码的含义这些细则如果全放System Prompt里模型大概率处理不过来但放在技能内部它面对的只是一份面对当前任务的简短说明书遵循率自然高。不过要注意Agent Skills不是让你把System Prompt里的内容全部搬走。我现在的做法是System Prompt仍然保留身份设定、核心目标和安全边界技能只负责怎么做。我是谁和怎么干分离之后两边都清爽很多。3. 动手做一个Skill从目录结构到SKILL.md的完整实操3.1 一个典型的技能目录长什么样我做过不少乱七八糟的实验技能现在给你看一个比较标准的目录结构。以一个月度数据报告技能为例monthly-report-skill/ ├── SKILL.md ├── scripts/ │ ├── clean.py │ ├── stats.py │ └── render.py ├── templates/ │ └── report_template.md ├── requirements.txt └── config/ └── report_fields.yamlSKILL.md是入口也是模型先读的东西scripts目录放所有可执行脚本templates目录放输出模板requirements.txt固定脚本依赖config目录放配置参数避免把参数写死在脚本里。实际使用中模型进入对话后会先做一次技能扫描扫描的是技能目录的元信息也就是SKILL.md顶部的YAML块。只有任务命中某个技能时它才会真正读取SKILL.md全文并根据需要运行脚本。所以整个技能包的骨架就是这个SKILL.md文件。3.2 写SKILL.md时的关键细节SKILL.md顶部是frontmatter用YAML格式写两个字段就行--- name: monthly_report description: 当用户给出数据文件或数据目录要求清洗、聚合统计并生成Markdown格式月度报告时使用。不适用于实时监控、不适用于单表简单查询、不适用于非月度数据。 ---name要短且有辨识度一般用小写字母加下划线或短横线。description是模型判断要不要用这个技能的主要依据。我踩过最大的坑就是description写得太泛比如生成报告结果模型在任何和报告沾边的地方都尝试调用它反而拖慢速度。正确写法是写清触发条件和排除条件前半段描述适用任务后半段用否定句排除相近但不属于本技能的场景。正文部分建议按下面结构组织# 月度数据报告技能 ## 目标 根据输入的数据文件生成月度销售/流量/运营报告。 ## 输入要求 - 数据文件为 CSV 或 Excel 格式 - 至少包含 date、value 两列 - 如果缺少必要字段先向用户确认数据来源不要自行猜测 ## 执行步骤 1. 在技能根目录下运行python scripts/clean.py --input {input_path} --output work/clean_data.csv 2. 检查 clean_data.csv 是否生成如果文件不存在查看 clean.py 的错误输出并修复后重试 3. 运行 python scripts/stats.py --input work/clean_data.csv --output work/stats.json 4. 运行 python scripts/render.py --data work/stats.json --template templates/report_template.md --output {output_path} ## 输出要求 - 生成 Markdown 报告包含核心指标汇总、趋势描述、异常提醒三部分 - 报告第一行必须写清统计周期 ## 失败处理 - clean.py 返回非零退出码时读取 stderr 并检查数据格式 - 网络请求失败时跳过在线补全步骤保留本地统计结果我特意写了如果缺少必要字段先向用户确认不要自行猜测这一条。模型有一个坏习惯输入信息不全时它会编一个默认处理逻辑而不是停下来问人。在SKILL.md里明确不要自行猜测能大幅降低瞎跑的风险。还有一个很容易被忽略的点执行步骤里给出的命令最好带完整可替换的变量标记比如{input_path}。模型不会写代码但它很擅长照着模板填空。你的命令越完整它执行越准确。3.3 放置位置和命名规范技能目录不是随便放的。官方通常支持两类位置一类是用户级全局技能目录所有项目都能用一类是项目技能目录只对当前项目生效。全局目录适合放那些你真的会在很多场景复用的技能比如代码格式化日志初步排查项目目录适合放和当前项目强绑定的私有逻辑比如本项目数据模型生成器。命名规范我也说一句技能目录名建议和skills保持一致短横线分隔避免中文目录名。中文字符在部分脚本环境中处理起来容易出幺蛾子比如系统编码、路径转义、git跨平台换行。不给自己找麻烦。4. 核心细节解析参数传递、环境变量、子技能、串行并行4.1 给脚本传参模型调用脚本本质上是在命令行里执行命令。所以脚本设计要遵循命令行友好原则所有输入输出都通过命令行参数传递不要用交互式输入。我写的脚本一般用argparse而且每个参数都给默认值。给默认值不是为了少写命令而是防止模型漏传某个参数导致脚本崩溃。举个例子python stats.py --input work/clean_data.csv --output work/stats.json如果模型忘传output我就让脚本默认输出到当前目录下的output.json而不是直接报错。对于某些不可能有合理默认值的参数比如输入文件的路径脚本里要明确提示缺失让模型读SKILL.md里的说明再补一次参数。建议每个脚本都加一个--dry-run参数只打印输入输出路径不执行真实计算。调试技能的时候会救命。4.2 环境变量和依赖管理技能脚本在模型调用时是临时启动的不是长驻服务。所以脚本依赖必须可复现。我最开始图省事直接用全局Python环境跑结果某天系统升级了一个依赖库技能脚本突然开始报错排查了小半天才找到原因。现在我的习惯是每个技能自己维护一个requirements.txt并按依赖生成虚拟环境。这样不同技能的依赖互相隔离升级全局环境也不会殃及池鱼。当然技能脚本要有超时控制。模型调用脚本时如果脚本卡死可能长时间没有响应。我一般在命令前加timeout比如timeout 300 python scripts/clean.py ...300秒跑不完的任务基本可以判断是数据或逻辑有问题早点暴露问题比死等要好。4.3 多个脚本和技能之间的配合一个任务可能需要多个技能协作。比如月度报告这个任务先清洗数据再生成统计图。模型在第一次调用时会加载技能A执行完得到中间产物然后在进行下一步时再加载技能B。两个技能之间靠什么传状态我一开始以为靠模型记忆后来发现不可靠。现在我在SKILL.md里约定一个固定的中间产物目录比如work/目录步骤一明确写将清洗后的文件放到work/clean_data.csv步骤二明确写读取work/clean_data.csv。模型只需要记住一个文件路径字符串比记住一整张数据表靠谱得多。串行依赖清晰模型就不容易迷路。有些场景可以并行任务A和任务B互不依赖分别对应不同技能模型可以并行加载多个技能。我实际测试下来模型确实能同时加载两个技能并分别执行。但要注意不要让它贪多技能描述里尽量写明本技能只负责X不处理Y防止并行加载时上下文交错导致混乱。至于子技能我理解它更多是说明引用技能A的SKILL.md里可以写本技能依赖技能B在运行B之前请确保已经完成A步骤。模型读到这个说明后会按顺序加载两个技能。我自己用下来这样的设计比把所有步骤塞进一个技能里更清晰方便在不同任务之间复用公共步骤。4.4 安全与权限技能里的脚本是真实可执行的代码这也就意味着一个从网上下载来的技能包如果带恶意脚本模型一旦调用就等于本地执行了那段代码。所以我对第三方技能的审核策略是宁可多花时间看源码也不直接信任别人的技能包。尤其是那些要处理隐私数据的技能我会进一步把脚本放进单独的隔离环境运行限制网络访问权限能给只读权限就绝不给写权限。如果说Tool Calling的危险边界是模型调用了不该调用的函数那么Agent Skills的危险边界就是模型加载并执行了不该执行的代码。权限模型完全不同。如果你所在团队的Agent应用会被多人共用技能仓库的review流程一定要搭起来。5. 哪些内容适合做成Skill哪些不该做5.1 适合做成技能的场景不是所有任务都适合技能化。我总结下来高价值技能通常具备这几个特征高频你或你的团队每周都会遇到至少一次边界清晰输入是什么、输出是什么、分几步走都能写清楚多步骤不是一步到位的查询而是需要脚本组合、判断、产出的流程结果可预期模型执行完后你知道正常长什么样方便校验重复价值大于构造价值手工做这件事要十分钟以上但把流程固化成技能后可以快速复用。举几个现实例子代码仓库的发布前检查、CSV数据清洗与报表生成、日志批量排查、文档格式转换、会议纪要和周报生成、项目管理里的任务拆解模板这些都是很典型的技能化场景。我自己的第一个正式技能就是发布前检查脚本里列出了需要检查的文件、需要跑的测试命令、需要核对的位置每次发版前让模型跑一遍省了我很多重复劳动。5.2 不适合做成技能的场景反过来这些场景不建议做技能强烈依赖对话上下文任务的输入散落在多轮对话里无法靠单一技能包明确描述时不要硬做需要实时交互和用户确认如果核心流程就是来回沟通确认意图技能的固定步骤反而碍事步骤经常变化今天一步明天三步的技能维护成本高于收益一次性任务做一次就丢的任务技能化是浪费。我做了个简单的判断表方便新手对照判断项适合做技能不适合做技能任务频率每周多次一次性或偶尔输入条件可以明确写在说明里高度依赖人对上下文的理解步骤稳定性基本不变频繁调整结果校验有明确验收标准需要灵活主观判断可复用性跨人或跨项目复用仅当前场景有效如果你拿不准我的建议是先不做技能而是把这次流程记录下来观察。同一个任务出现第三次再开始做技能化效率刚刚好。6. 常见问题与排查技巧实录6.1 模型就是不调用这个技能这个问题我遇到太多次了。最常见原因是description写得太窄或太泛。太窄模型遇到相关任务时看不出这个技能和当前任务的相关性太泛description和很多任务都沾边模型反而没有明确触发感。排查步骤先把自己当成模型用任务的原始描述能不能匹配到技能描述里的关键词比如任务是帮我把这个月的数据整理成报告你的技能描述如果只写生成月度报告文档模型可能觉得当前任务是整理数据不是生成报告文档。这时候把description改成当用户提供本月度数据并要求生成输出文档时使用匹配率明显提高。版本升级或更换模型也算一种不调用场景。不同模型对技能描述语义的理解有差异一个模型下表现很好的描述换一个模型可能完全不灵。遇到这种情况先跑几个最小测试用例看看是哪一层断了。6.2 技能被误调用和不调用相反的问题是乱调用。模型把不相关的任务套到这个技能上通常也是description惹的祸。比如技能里带了report这个词所有和总结、汇报沾边的任务都可能触发。解决方法是把不适用的排除条件写进description。我常用的句式是不适用于……不适用于……。同时在SKILL.md正文里加一道前置检查步骤告诉模型输入如果缺少某个关键字段不要继续执行先向用户确认。这样就算模型误加载了技能也大概率会在前置检查这步停止而不是硬着头皮跑完然后在错误结果上继续操作。6.3 脚本报错排查一圈发现是路径问题技能脚本最容易出的问题有两个一是当前工作路径不对。模型执行命令时的工作目录可能是它自己启动时的路径不是技能目录。脚本里所有相对路径都可能指向错误位置。解决办法有两个方向在SKILL.md里统一写所有命令都在技能根目录下执行并在示例命令里显式加上cd或者更稳妥的在脚本内用Path(__file__).parent定位脚本自身所在目录再以此为基础拼接所有相对路径。这是我最推荐的方案。二是dependency问题。新环境没装依赖脚本直接import报错。SKILL.md里必须写上依赖安装命令同时脚本在启动时先做一次依赖可用性检查缺失时有明确的提示信息比一团traceback强得多。6.4 技能执行完后状态传丢了我前面提到过多技能协作时状态传递不要依赖模型记忆要落成中间文件。但你可能会遇到这个场景模型执行完技能A准备加载技能B时忘了工作目录下的中间文件。这时候问题出在SKILL.md的衔接部分写得不清楚。给技能B的SKILL.md开头加一行本技能读取../技能A工作目录/output_data.json作为输入如文件不存在请先执行技能A。我这样做之后模型基本再没出现过找不到输入文件的情况。6.5 一套顺手的调试技巧调试技能没有捷径但有一套我自己验证过比较顺手的流程先手动跑一遍脚本确保脚本逻辑和参数都对在对话里显式要求使用xxx技能处理这个任务跳过技能自动检索环节直接验证执行路径如果结果不对在公司日志或终端里打开调试模式查看模型到底加载了哪些文件、哪些步骤被执行了在临时副本里改脚本不要在技能原目录上直接改防止改崩了没法回退每次改完SKILL.md或脚本记录一下改动原因。不然过了两周你根本想不起来当初为什么加那句当输出为空时重试一次。我试过给一个数据处理技能加日志输出到技能目录下的debug.log模型跑完我看日志再对照结果基本能定位90%的问题。剩下的10%大多出在环境和权限层面需要回到脚本本身排查。7. 我的几点体会和扩展思考做完第一批技能之后我的Agent开发方式发生了明显变化。以前写Agent是在写一长段对话模板现在更像在写一套操作手册加工具箱。职责分得很清楚System Prompt管身份和边界SKILL.md管流程脚本管执行。这样的好处是团队里不太擅长写代码的同学也可以参与维护SKILL.md的说明部分工程师只需要保证脚本质量分工明确。技能粒度这块我反复调过几次。一开始贪多求全把数据处理做成一个大技能里面塞了清洗、聚合、可视化、报告四个模块结果模型经常在错误的场景跑不必要的步骤。后来拆成四个独立技能每个技能只干一件事准确率明显上去。但拆太细也有问题模型为了完成一个小任务要连续加载三四个技能反而慢。我现在的经验是一个技能对应一个明确任务步骤控制在五到七步脚本数量一到三个是比较舒服的粒度。还有一点值得提技能目录一定要纳入版本管理。技能的本质是可执行的文档它和代码一样需要review、测试、回滚。我把技能库单独建了一个仓库每次修改走Pull Request流程甚至为高频技能写了冒烟测试给脚本喂一份最小测试数据跑完检查输出格式。这样一来技能就不再是某个人的个人经验而是团队的可复用资产。最后再分享一个想法真正值得做成技能的不是那些听起来很酷的功能而是你手工重复了很多遍、终于忍无可忍想交给模型去做的流程。上周我花半天时间把一个客服工单分类与优先级打分流程技能化量产的第一个技能效果比想象中好。做第一个技能时不用追求完美把流程写清楚、脚本能跑通就够了。做完之后你会发现后面该拆什么、该合并什么自然心里就有数了。