
Skills不是插件的替身是Agent的肌肉记忆过去半年我一直在折腾各种Agent框架越来越发现一个尴尬的现状大部分人把Skills理解成给模型多塞几个工具或者干脆当成插件的另一个叫法。结果做出来的Agent要么上下文爆掉要么任务执行得五花八门——同一个指令今天能用明天就崩换个输入格式就完全跑偏。直到我把Skills当作一套独立的、可复用、可测试的行为协议来对待之前那些模型不听话的问题才真正松动下来。这篇东西不聊概念玄学就把我这段时间沉淀的Skills设计思路、目录结构、调参心得以及踩过的真实坑一条条摊开讲。适合正在做Agent应用、卡在工具调用不稳或者想沉淀自己的技能库的开发者看。1. 为什么这一轮AI浪潮里Skills会被单独拎出来讲1.1 模型会知道但不一定会做先举个我经常拿来开场的例子。你让一个模型写一篇项目周报它写得出来格式也漂亮但如果你让它根据这个目录下的日志文件统计最近一周线上报错TOP10按模块归类再用周报格式输出——它往往会卡住甚至直接编一份报错统计出来。区别在哪里前者靠的是模型在预训练阶段积累的写作能力属于陈述性知识后者需要一套操作性流程先遍历日志再聚合归类再格式化输出。模型知道该怎么写周报但它不知道日志文件在哪、用什么方式遍历、聚合时要剔除哪些噪声。这就是Skills要补的位把一段可重复执行的操作流程连同触发条件、边界约束、输出规范打包成模型可以直接拿起就用的行为模块。我在一次内部交流里听到一个更直白的类比Skills是给模型做的肌肉记忆。模型本身是大脑知道很多道理Skills是肌肉让它真正把手头的事办利索。这个说法我很认同因为它点出了一个核心——Skills不是为了教模型新知识而是为了让模型在特定场景下稳定地做对动作。1.2 Skills、Function Calling、Tools、Plugins到底差在哪这是新手最容易混的地方我把几个概念摊开对比一下免得后面聊代码时绕晕。Function Calling是模型生成结构化函数调用的机制它只是把手——模型决定要调什么函数、传什么参数真正执行的是你写的后端函数。Tool是Function Calling的具体载体一个Tool往往就是一个函数加上它的描述。Plugin通常指一组Tool的集合更偏打包好的集成比如Jira插件表示能对接Jira的一组能力。那Skills呢它既可以包含多个Tool也可以完全不包含Tool只包含一段提示词和一套处理流程。更关键的区别是Skills强调模型如何正确完成一个任务的完整协议它把思考路径、工具使用、输出格式、异常处理都写清楚而Plugin更多是把API包一层给模型调用。用一个表格来对比可能更直观维度Function CallingToolPluginSkill最小单元一个可调用函数函数的描述实现一组工具的打包一段完整的行为协议是否一定有代码是是通常是不一定可以是纯指令流程关注点怎么把参数传对提供什么能力集成哪些系统任务如何被稳定完成复用粒度函数级能力级系统级任务级可测试性单测即可单测即可集成测试需要场景化回归测试我见过有人把Skills做成一堆描述写得很长的Tools集合结果模型的调用准确率反而下降——因为工具描述越长占的上下文越多模型做路由决策时越容易混乱。Skills的正确做法恰恰相反一个Skill只解决一类任务内部可以有多个步骤但对外只暴露一个清晰的入口。1.3 Skills对普通开发者的真正价值对普通开发者来说Skills最大的红利不是让模型变聪明而是让经验可以沉淀。以前你调一个Agent流程所有调校都散落在提示词的只言片语里换个项目一切重来。有了Skills你把如何做代码审查、如何汇总技术周报、如何分析用户反馈这些流程固化下来下次直接挂载就能用还能在团队里共享。说白了Skills把你的Agent开发从每次从零写提示词推进到搭积木的层次。这个转变和价值我后面几章会结合具体文件结构和代码细节展开。2. SKILL.md一份可复用的技能说明书长什么样2.1 元信息区让Agent在几百个Skill里认出你一个Skill绝不只是放一段提示词那么简单。我把Skill做成一个目录目录里最核心的是SKILL.md——一份结构化描述文件。它的元信息区决定了Agent在庞大的技能库里能不能第一时间认出你、选对你。我习惯在SKILL.md最顶部用YAML格式写三样东西name、description、trigger。name不用多说是唯一标识。description是一句话描述措辞非常重要因为它直接进入Agent的路由选择器。我踩过一个大坑在某版Skill里我把description写成了帮助用户处理各种与日志相关的事情结果这个Skill被Agent选中去回答日志轮转是什么——它根本不该管这种纯知识问答。后来我把description改成基于日志文件做聚合统计、异常检测、趋势分析并输出报告路由准确率立刻上来了。trigger是可选的但强烈建议写。它告诉Agent什么情况下非我不可。比如财务数据统计类Skilltrigger建议写当用户需要处理财务相关Excel、CSV文件时这样Agent就不会把一个计算器Skill当成万能统计工具。2.2 触发条件与依赖声明别让Agent望文生义元信息下面是两个我后期才加上的区块prerequisites和dependencies。前者描述调用这个Skill所需的前置条件比如需要存在raw_data/目录并且其中包含至少一个CSV文件后者声明运行环境依赖比如需要Python3.10需要安装pandas、openpyxl。为什么要单独划出这两个区块因为Agent在规划任务时不会老老实实地按你的思维走。在早期版本中我把依赖信息藏在了正文最后一段结果模型在解析时经常忽略最后直接生成了一段import pandas的代码但环境里压根没装。现在我把依赖放在文件最前端显眼位置Agent在执行前的准备阶段就会读到编译环境的遗漏基本绝迹。还有一个细节依赖声明不要用可选这类模糊词。SAE在推理时并不擅长做可能需要的判断它只会二选一要么盲目地跳过要么固执地准备一堆不需要的东西。要写就写成明确的版本区间比如pandas2.0, 3.0越精确越不容易出幺蛾子。2.3 指令正文和示例差异收敛的关键元信息之下是正文这是Skill的核心。我把它进一步拆成三段instruction指令、workflow流程、examples示例。instruction描述任务目标要写得像一份验收标准比如输入一份原始会议记录输出一份包含背景、决议、待办、风险四部分的会议纪要。注意这里不要写帮助用户整理会议纪要这种模糊表述模型对模糊目标的执行方差非常大。workflow是给模型的行动地图建议用编号列表写明每一步做什么。我见过很多失败的Skill失败原因不是写得太少而是写得太多太细把模型在每一步之间的推理空间都塞死了。我的经验是workflow只需要写到关键决策点级别——比如第一步识别说话人并标记轮次第二步判断每条发言属于议题讨论、决议拍板还是待办承诺第三步按四段式归并输出。剩下的归并和润色让模型自己发挥反而效果更好。examples的作用是收敛输出格式。给1到2个完整的输入输出示例尤其要展示糟糕输入和标准输出的映射关系。我发现给一个负面示例什么不该做往往比给三个正面示例更能降低模型的犯错率。比如在会议纪要Skill的example里我可以明确展示仅提到讨论了一下方案这类无结论记录应该被丢弃而不是被写进决议这种对比让模型对任务边界的理解立刻清晰。3. 实战把会议纪要整理做成一个可复用的Skill3.1 需求拆解与边界确认纸上谈兵没意思我直接拿会议纪要整理这个真实场景走一遍完整流程你能看到我每一步的取舍逻辑。先明确需求输入是带Speaker标签的会议转写文本输出是结构化的会议纪要。听起来简单但需求方其实是另一个组的同事后来才告诉我他们真正想要的不只是纪要而是能从纪要里直接抽出待办和风险好同步到项目管理工具里。这就是典型的用户不知道自己要什么如果一开始不把边界问清Skill做到一半必然返工。我的需求拆解分四步输入什么——确认是带时间戳或speaker标签的转录文本纯文本即可不需要解析PDF或音频。输出什么——确认包含背景概述、核心决议、待办事项含负责人、待跟进风险四部分。不做什么——不做任务下发不做日程同步。边界画清楚模型才不会越权去做计划外的操作。容错要求——遇到缺失信息时宁可留空也不能编造这是必须写进验收标准的。3.2 编写标准化的SKILL.md基于上面的拆解我写了一份简化但完整可用的SKILL.md结构如下--- name: meeting-minutes description: 将带说话人标记的会议转写文本整理为结构化会议纪要包含背景、决议、待办、风险四部分。 trigger: 当用户输入会议转写文本、带有Speaker标签的对话记录或要求整理会议纪要/输出待办时使用。 prerequisites: - 输入文本中包含至少两条发言记录 - 能识别出发言人姓名可用name或Speaker:前缀表示 dependencies: - 无外部依赖纯文本处理能力 --- ## Instruction 将输入文本转换为如下结构的Markdown纪要 ... ## Workflow 1. 识别每轮发言的说话人身份 2. 将每条发言归类为背景陈述、讨论观点、决议结论、待办承诺或风险提示 3. 对待办事项提取负责人与截止时间若无明确负责人则标记为待确认 4. 按Summary/Decisions/Todos/Risks四段式输出 ## Examples ...这份文件里最关键的设计是四段式输出和待确认兜底。四段式输出是最终验收的锚点而待确认兜底则是对模型幻觉的一道闸门。如果我不写这条兜底规则模型在面对没有人明确负责的待办时会倾向于根据说话内容猜一个负责人——这种幻觉一旦混进纪要工伤比漏写还大。3.3 挂载到Agent并跑通第一个案例SKILL.md写好后把它放进skills/meeting-minutes/目录在Agent的配置里声明技能根目录即可。我测试时用了一段模拟的会议转录Speaker A: 这个版本的功能冻结定在下周五测试这边要提前安排回归。 Speaker B: 回归用例我来整理周三给到大家。 Speaker C: 线上有个支付超时的问题还没根治风险等级我觉得应该标高。 Speaker A: 那就把支付超时列入高风险B你来跟进带上QA一起排查。第一次跑输出就合格了四分之三——背景、决议、待办都对但风险部分模型把支付超时还没根治写成了支付超时可能导致资金损失多了一句它自己脑补的影响推测。这不是事实层面的严重幻觉但在银行类客户的纪要里这种推测句是不能出现的。我为此在Instruction里加了一条硬约束风险描述只允许转述原文中出现的事实禁止进行后果推测。再测一次这个问题就消失了。我特别想强调这个迭代闭环跑一个案例、定位一处偏差、修改SKILL.md、重新验证。Skills的质量不是设计出来的是这么一点点转出来的。4. 调试经验Skills踩过的坑和它们的根因4.1 路径与上下文最常见的第一坑第一个坑和目录结构有关。我把所有Skills放在一个仓库的skills/目录下但有些Skill需要读取历史数据文件我在SKILL.md里用了一个相对路径raw_data/report.csv。问题来了Agent执行时的工作目录是仓库根目录不是Skill目录所以相对路径直接失效文件找不到。这个坑的根因在于——我默认Skill是运行时的工作目录但大多数Agent框架的工作目录取决于启动位置。解决方案有两种一是把路径写成占位符运行时注入绝对路径或通过专门的文件读取Tool定位二是在SKILL.md的prerequisites里明确声明执行本Skill时需挂载数据目录SKILL_DIR/../shared_data/。我更推荐第二种因为占位符注入在调试时很麻烦你很难确认注入是否成功。把数据挂载作为前置条件写清楚Agent在执行前会做目录检查一旦缺失就报错而不是跑到一半才发现读不到文件。4.2 LLM对描述的自由发挥问题第二个坑是模型对description的过度解读。我有个仪表盘数据生成Skilldescription写的是根据数据库中的订单表生成销售趋势图结果用户只是问了一句你们数据库里订单表长什么样Agent就自动调起了这个Skill去查询数据库结构。这个问题的根因是Agent的路由决策完全基于description的语义匹配它不理解生成趋势图和查询表结构之间的边界。我的修正方案是在description里加大量的负面引导除非用户明确要求生成趋势图或报表否则不要调用本Skill。这个除非...否则不要句式是实测下来收敛效果最好的比在正文里写一段禁止滥用管用得多。还有一次我把一个处理日志聚合的Skill描述得太丰富把能做的事全列了一遍。结果Agent在用户提了个只需要简单统计的需求时也把这个Skill拉出来跑全流程浪费了不少token。后来我给它加了触发条件仅当需要按模块维度做跨文件聚合统计时使用单文件单行查询不用本Skill。触发场景收紧后误用率下降了一个量级。4.3 输出格式不稳定如何用约束驯服幻觉第三个坑最隐蔽同一个Skill同样的输入连续跑几次偶尔一次输出格式完全乱套——该用的标题没用待办事项掉进风险里。最开始我以为是模型抽风重试几次会好但这是治标不治本。后来我把问题定位到示例的缺失。模型在不稳定时缺少的是参照锚点它不知道该把内容精确地放到哪个槽位。解决方法是给足一元二元的对照示例。比如我在会议纪要Skill里给了一个带歧义文本的输入示例文本里有句话是这个问题的风险不大我们先继续开发模型第一次会把继续开发记成待办但实际上这是一句风险判断。示例里我直接展示了正确归类——话术包含风险等级评价应归入Risks而非Todos。给完这个对比例子输出格式就稳定了很多。我建议给每个Skill至少配两个示例一个常规输入一个挑战性输入。挑战性输入专门针对那些模型容易混淆的边界案例它比十条规则都好用。5. 测试、版本与团队协作让Skill像代码一样被管理5.1 测试用例与回归验证Skill写到可用程度后下一步就是把它纳入测试体系。我参照代码测试的思路在Skill目录里放了一个test_cases.md维护一组输入输出对。每次修改SKILL.md后我都会跑一遍全部测试用例观察哪些case通过、哪些case退化。## Test Cases for meeting-minutes.md ### TC-01 标准输入 Input: 带speaker标记的标准会议转写约10轮发言 Expected: 四段式输出Todos含3项每项必须有负责人 ### TC-02 无负责人输入 Input: 围绕技术方案讨论的转写无人认领任务 Expected: Todos项负责人标记为待确认不得编造姓名 ### TC-03 含风险讨论输入 Input: 含支付超时尚未根治等风险表述 Expected: Risks仅转述事实无后果推测回归测试的价值在我改第四版时体现得很明显——我为了优化待办识别调整了workflow的步骤顺序结果TC-02无负责人输入就退化了模型开始胡乱猜测负责人。如果没有测试用例我根本不会注意到这个副作用。5.2 版本号与更新日志Skills不是写一次就定型的东西它们会随着使用不断演化。我习惯在SKILL.md里加一个version字段并维护一个CHANGELOG.md记录每次改动的动机。比如会议纪要Skill目前是v1.4.0CHANGELOG里能看到v1.1.0 加入待确认兜底规则修复幻觉负责人问题v1.2.0 增加风险描述约束v1.3.0 优化路由触发条件降低误用率。版本号的价值不光是为了追溯更是为了团队协作时能明确哪个版本是稳定的、哪个是试验中的。我不会一上来就更新shared库里的版本而是先在自己的分支做实验跑过全部TC后才合入共享技能库。5.3 共享与审查流程当Skills开始在一个小团队里共享时审查流程比想象中更重要。团队里每个成员对什么是好的描述、什么算是边界的理解都不一样结果就是共享库里的Skill水平参差。我目前跑通的流程是提案-试用-审查-合入提交流程成员提交一个新Skill或修改版本附上TC和CHANGELOG摘要。试用期其他人用测试数据跑一遍不以个人偏好为准只看TC通过率和实际使用中的误用频率。审查点重点看description是否语义清晰、prerequisites是否完备、有没有过度依赖特定模型。合入标准必须通过所有历史TC且新TC通过率不低于90%。这个流程看着重但对3个以上使用者的共享库来说非常必要——没有流程Skill库迟早会变成垃圾场。6. 从单技能到技能编排一些进阶思考6.1 Skill之间如何互相调用做到后期我开始遇到一个更复杂的问题一个任务可能需要多个Skill协作。比如根据群聊记录生成项目周报就需要先调用会议纪要整理Skill处理关键讨论再调用数据汇总Skill统计任务进度最后才做周报输出。Skill编排我目前试过两种路子。一种是让Agent在workflow里显式引用其他Skill的名字靠路由机制自动切换另一种是写一个编排型Skill它本身不做事只负责把任务拆解后分发给子Skill。我实测下来第二种更稳因为编排型Skill可以把子Skill的调用顺序和执行条件写成确定性的流程而不是完全交给模型自己发挥。举个例子我的周报生成Skill的workflow可能是这样的1. 调用meeting-minutes处理群聊中包含决策讨论的片段 2. 调用task-aggregator统计项目管理系统中的任务状态 3. 合并两部分结果按周报模板输出这种写法的好处是每个子Skill都可以独立测试编排层只是个调度器。坏处是编排型Skill本身也需要维护而且一旦子Skill接口变化编排也要跟着改。6.2 权限与安全边界Skill能做的事情越多权限边界就越要小心。我吃过一次亏一个包含文件重命名能力的Skill在一次执行中把一批日志文件的名字改错了因为模型对重命名的理解是规范化文件名但实际文件是给下游系统用的名字不能随意动。从那以后我在所有涉及写操作或删除操作的Skill的prerequisites里都加了一条变更影响声明要求模型在执行前先输出一份变更摘要等用户确认后才动手。对纯读取类Skill则不加这个限制免得交互太重。权限边界这件事在Skill设计阶段就必须考虑而不是等出了问题再补。6.3 往后看Skills会怎么演进就我目前的使用感受来说Skills还处在一个手工编写、靠调试迭代的阶段。我预估下一步会出现更成熟的做法一是Skill的自动生成——通过分析历史成功执行记录自动归纳出可复用的流程模板二是Skill的标准化评测——类似模型的benchmark用一套固定的任务集来横向对比不同版本Skill的优劣三是跨Agent的Skill互通——同一个Skill设计能跑在不同厂商的Agent平台上。不管怎么演进核心那条线不变Skills的本质是把如何稳定地完成一类任务沉淀成可复用的资产。在这个方向上越早建立自己的技能库和迭代流程后面就越不吃亏。我现在的习惯是每完成一个Agent功能都会多花半小时思考这里面的流程能不能抽成一个Skill如果能我就直接把它抽出来哪怕当前场景只用一次。因为我知道这种积累一到临界点价值是复利式的。