
1. 从一次项目失控说起为什么要重新审视 skills大概两个月前我接手了一个内部知识库整理项目。需求本身不复杂把散落在几十个文档、聊天记录和邮件里的零散信息整理成结构化的产品手册。最开始我直接让模型用大段 prompt 去处理但效果一直不稳定——同一个任务换一种表述方式输出格式就变了换一个文件字段名就乱套了。更要命的是每次调整 prompt 都要牵动全局改一处丢一处我一度怀疑自己是不是在用模型做一件它根本不擅长的事。后来我接触到了 Agent Skills 这个概念。它的核心思路其实特别朴素与其每次都把怎么完成任务的完整说明塞进对话里不如把这个能力打包成一个结构化的、可复用的技能包让模型按需加载调用。当时我并没有意识到这个朴素的思路会彻底改变我处理这类任务的方式。这篇文章想做的就是把我在实际项目里使用、调试、甚至推翻重做 skills 的过程完整记录下来包括那些官方文档里不会写、只有自己踩过坑才知道的细节。适合读这篇文章的人我觉得有三类。第一类是已经在用 AI 辅助工作但觉得 prompt 越来越长、越来越难维护的人第二类是做过 Function Calling 或 RAG但觉得这两种方案都有点别扭、想看看有没有新抽象的人第三类是纯粹对模型怎么能稳定复用一个技能这个机制感兴趣的人。我会从为什么需要这个东西讲起然后拆解它的标准形态接着用一个可以照着做的完整案例演示全过程最后把我踩过的坑和什么时候不该用的思考一并倒出来。2. skills 解决的核心问题把会和能做分开在深入细节之前我想先把一个概念层面的东西讲清楚。很多人第一次看到 Agent Skills 时会觉得这不就是把 prompt 存起来吗或者是这和 function calling 有什么区别。这两种理解都有点道理但都没说到根子上。2.1 模型的通用能力和专项能力是两回事大模型的基础能力很强它能理解自然语言、能推理、能写代码这些东西是它天生或者说预训练就会的。但真实工作里需要的往往是专项能力比如把一段会议录音转成按项目、按负责人归类的待办清单比如从一份工单里提取出客户情绪、紧急程度、处理建议三个字段并格式化成 JSON。这些专项能力的共性是有固定的处理流程、有特定的输出格式、对细节要求高、而且重复出现。你可以用 prompt 告诉模型该怎么做但 prompt 有几个天然问题。第一长了以后会稀释注意力模型抓不住重点第二一旦多个任务混在一个对话里指令会互相干扰第三不同的任务需要不同的 prompt全都堆在系统提示词里既浪费上下文窗口又难以维护。我用一个生活化的类比来解释。假设你手下有个很聪明的实习生他学习能力强、悟性高但他刚来公司什么都不懂。你不可能每天早上把如何写周报如何做会议纪要如何整理客户信息全重新讲一遍。你会做的是把这些工作流程写成标准作业手册他需要做哪件事就把哪本手册递给他。Skills 就是那本手册模型就是那个实习生。2.2 skills 与 function calling、RAG、微调的本质差异这部分我用一张表先说结论然后再展开讲。方案解决的痛点核心机制最怕遇到的事Function Calling让模型能调用工具、获取实时数据模型输出结构化参数由外部代码执行参数设计一变全链路都要改RAG让模型知道不知道的知识检索库把相关内容塞进上下文检索质量不稳定答非所问微调让模型学会新语言、新风格、新领域改权重动底层成本高数据要求高易过拟合Agent Skills让模型稳定执行一套复杂流程按需加载的标准化技能包技能边界不清触发逻辑混乱讲得再细一点。Function Calling 本质上是模型出参数代码执行动作那个动作本身是你在外部代码里写死的模型并没有参与执行。Skills 不一样的地方在于技能内部的处理步骤可以完全由模型自主完成它更像是一个带了操作手册的自主agent。RAG 解决的是信息缺失它负责把相关材料找出来塞进上下文但找到了材料之后该怎么处理它是不管的。而一个 skill 可以做得更完整检索、清洗、转换、输出一整条链路都在技能包内部定义好。微调则是另一个量级的事它适合长期改变模型的某种能力或风格投入大、周期长而一个写好的 skill 几分钟就能加进去不满意改几行描述就行。用一句话概括就是Function Calling 是给模型一只手RAG 是给模型一双眼睛微调是改造模型的大脑而 Skills 是给模型一本标准作业流程手册——它既不改变模型本身也不需要实时检索外部知识但它让模型在某个特定场景下表现得像一个熟手。2.3 为什么按需加载是关键中的关键Skills 最让我觉得巧妙的设计其实是按需加载这四个字。一个模型可以装几十个甚至上百个技能包但它不会在每次对话时都把所有的技能说明读一遍——那样上下文早就爆了。模型会先看每个技能的描述信息就是 metadata判断当前任务匹配哪个技能然后只加载那个技能的完整说明。这个机制带来的直接好处是单次任务的上下文占用小指令清晰度高不同任务之间也不会互相污染。我在实际使用中发现同一个模型用 skills 处理任务时的输出稳定性明显好于把一堆指令全塞在系统提示词里的做法。原因很简单上下文越长注意力越分散上下文越聚焦模型发挥越稳定。一次只干一件事而且按手册干干好的概率自然大得多。3. 一个标准 skills 的目录结构与 metadata 设计逻辑聊清楚了它解决什么问题接下来该看它长什么样了。这部分我会以一个相对规范的结构为例先讲框架再讲为什么每个字段要那么写。3.1 SKILL.md、scripts、assets三个角色的分工一个规范的 Agent Skill通常长这样skills/ ├── meeting-minutes/ # 技能目录名建议用 kebab-case │ ├── SKILL.md # 技能说明书核心必须有 │ ├── scripts/ # 可选放处理脚本 │ │ └── parse_todos.py │ └── assets/ # 可选放模板、参考文件 │ └── output_template.mdSKILL.md 是整个技能包的灵魂。它是一份 Markdown 文档前半部分是 YAML 格式的元信息后半部分是给模型看的具体操作说明。模型在决定要不要调用这个技能时读的是元信息在真正执行这个技能时读的是操作说明。scripts 和 assets 则是给技能提供外部武器的scripts 里放 Python 或 Shell 脚本当模型需要做一些精确计算、文本处理时直接调用assets 里放一些不适合写进说明的长模板、样例数据。这个设计最妙的地方在于分层。元信息解决什么时候用说明文档解决怎么用脚本解决光靠模型做不精确的事。三个角色各司其职互不干扰。如果全塞在一个文件里模型读完描述还要在一堆代码中间找指令效率会大打折扣。3.2 metadata 里 name 和 description 的写法直接决定成败这可能是整个 skills 体系里最容易被低估的部分。很多人在写技能时把时间和精力全花在正文说明上metadata 随便写两句。我在实测中发现模型是否能在正确的时机触发正确的技能90% 取决于 metadata 写得好不好尤其是 description。一个合格的 description 需要同时具备三个特征。第一是场景触发词明确要告诉模型当用户提到什么词、什么任务时使用这个技能。第二是适用范围清晰要写清楚这个技能能处理什么、不能处理什么避免模型在边界情况误触发。第三是输入要求明确要让模型知道哪些输入是必要的、哪些可以在输入缺失时主动向用户询问。我举一个反例和一个正例。反例是name: meeting-minutes description: 将会议记录整理成格式化的会议纪要提取待办事项并按负责人分类。这个描述看起来没毛病但实际使用中会出现各种误触发用户只是想问会议纪要怎么生成模型就会调用这个技能用户提供的是培训课程的逐字稿模型也试图往里套。因为它把触发器描述得太宽泛了。正例是name: meeting-minutes description: 当用户提供会议录音转写文本 / 会议笔记 / 聊天讨论记录并希望生成格式化会议纪要、提炼行动项、按负责人归档时使用此技能。仅处理与会议讨论内容相关的文本不适用于新闻稿、技术文档、课程讲义等其他类型的内容整理。这个描述把触发条件和排他条件都写清楚了。模型判断起来就容易得多遇到符合触发条件的任务就调用遇到不符合的就不调。这个差别在实际运行中的影响很大后面我讲踩坑部分时还会再提到。3.3 正文部分该怎么组织模型才愿意照做SKILL.md 的正文部分是给模型看的说明书它的组织方式直接决定了模型执行的准确度。我的经验是正文至少需要包含这几块任务目标、输入要求、处理步骤、输出格式、质量检查清单。任务目标要一句话说清楚这个技能做完之后交付什么。输入要求要明确接受什么格式的数据、哪些字段是必填的。处理步骤是核心最好用编号列表每一步说清楚做什么怎么做为什么这么做。输出格式要给出明确的模板甚至可以直接放到 assets 里作为参考文件。质量检查清单是很多人忽略的但它很有用——让模型在输出之前按清单自查一遍能显著减少低级错误。这里有一个非常重要的写作原则不要试图在说明里教会模型为什么要重视效率这类抽象道理直接告诉它具体怎么操作。模型不需要被说服只需要被指引。你写得越具体它执行得越准确。4. 手写一个可复用的 skills从零到稳定运行的完整过程理论说再多都不如动手做一遍。这一节我会用一个我实际做过的技能作为例子完整演示从需求分析到测试调优的全过程。这个技能的任务是把零散的会议记录整理成格式化的会议纪要并提取待办清单。4.1 先分析需求再写代码别急着动手我见过很多人写技能时上来就写文档、写脚本结果做出来的东西根本不好用。正确的顺序应该是先回答三个问题这个任务是不是高频重复的这个任务的判断标准是不是明确可描述的这个任务是不是模型单靠 prompt 容易翻车的对会议纪要这个任务来说三个问题的答案都是肯定的。会议记录整理是高频需求输出结构可以定义得很清晰背景、讨论要点、结论、待办而且模型单靠临时 prompt 很容易出现遗漏要点、待办归类混乱的问题。确认了这三个问题之后我才开始搭目录。4.2 SKILL.md 的完整写法与逐行解释我的技能目录设计如下meeting-notes/ ├── SKILL.md └── scripts/ └── to_markdown.pySKILL.md 的元信息部分我这样写--- name: meeting-notes description: 当用户提供会议录音转写文本、会议笔记或多人讨论记录并希望生成结构化会议纪要、提炼讨论要点、汇总待办事项时使用此技能。适用于团队会议、项目同步、客户沟通等场景。不适用于简历撰写、代码生成、翻译等非会议整理类任务。 ---这里我把触发场景和排他场景都写清楚了后面测试时发现模型很少误触发。正文部分我按这个结构写# 会议纪要整理 ## 任务目标 将输入的非结构化会议原始记录整理为一份结构化会议纪要包含会议背景、讨论要点、结论与待办事项四个部分输出 Markdown 格式。 ## 输入要求 - 接收用户直接粘贴的文本或用户指定的包含会议记录的文件内容 - 输入内容允许存在口语化表达、重复语句、无关寒暄需在整理时自动清洗 - 如果输入内容明显不是会议记录如技术教程、新闻文章不要使用本技能直接告知用户 ## 处理步骤 1. 通读全文识别参会者、讨论主题、时间信息填入纪要头部无法识别时用待补充占位 2. 将讨论过程按主题归纳为 3~6 个要点块用无序列表列出每个要点下的关键论据和数据 3. 提取所有明确结论用编号列表列出每条结论一句话说清因果 4. 提取所有待办事项按负责人分组未明确负责人的待办归入待指派 5. 对照质量检查清单逐项自查后输出 ## 输出格式 严格使用 Markdown结构如下 - 一级标题会议纪要 - 二级标题基本信息参会人、时间、主题 - 二级标题讨论要点 - 二级标题会议结论 - 二级标题待办事项 ## 质量检查清单 - 是否所有原始记录中的明确结论都已覆盖 - 是否所有待办都有负责人与截止时间缺失时是否标注待确定 - 是否将口语化冗余内容清理干净 - 输出是否严格符合 Markdown 结构无多余解释这份说明写完后我还有意识地做了一件事在处理步骤里加入通读全文这个要求。这不是废话它是在避免模型拿到文本后只看第一段就开始整理。很多模型在处理长文本时容易注意力偏置明确要求先通读一遍再动手能显著提高覆盖率。4.3 可选的 scripts什么时候需要给技能配一个脚本在会议纪要这个技能里我配了一个轻量级的 Python 脚本。它的作用不是做理解而是做格式清洗把原始文本中多余的空行、无意义的符号、重复的标点清理掉输出一个干净的纯文本传给模型处理。import re import sys def clean_text(text: str) - str: text re.sub(r\s, , text) # 合并多余空白 text re.sub(r[。]\s*, 。\n, text) # 句号后换行便于分段 text re.sub(r([!?]), r\1\n, text) return text.strip() if __name__ __main__: raw sys.stdin.read() print(clean_text(raw))脚本的使用逻辑是模型在 skill 的操作说明里被引导为先调用 scripts/clean.py 对输入做清洗再开始整理。这样模型就不需要自己在上下文里做精细的文本去重——这类机械性工作本来就是代码的强项。我在这个环节最大的体会是脚本不要试图替模型做理解方面的事。你让脚本去判断某个句子是不是结论它做不好模型做更好但你让脚本把文本清洗成更规整的输入它做得又快又好。把机械的事交给代码把判断的事交给模型各用所长。4.4 测试阶段用真实素材跑三轮记录每一次失败技能写完不是终点测试才是真正开始。我的测试方式是准备了几份形态差异很大的真实素材一份是杂乱无章的会议录音转写一份是带有人名和表情符号的聊天记录一份是结构相对规整的项目周会纪要。第一轮测试暴露的问题很典型输出格式不一致。我在说明里写了二级标题待办事项但第一次跑出来的结果是## 待办、第二次跑出来是### 待办清单。原因在于我用文字描述格式要求模型理解得不够精确。我的解决办法是把完整输出样例直接写进 SKILL.md 的输出格式部分让模型模仿样例而非理解规则。修改后再跑格式稳定多了。第二轮测试暴露的问题是待办归类的准确率。有些原始记录里的待办表达得很隐晦比如这块回头跟财务对一下模型一开始漏掉了。我在处理步骤里补充了一条留意含跟进确认同步对一下等动作词的表述它们通常是隐性待办。加上这一条之后召回率明显提升。第三轮测试暴露的问题是边界误触发。我拿了一份技术教程文本去测模型仍然试图调用这个技能。虽然我在元信息里写了排他条件但模型在执行阶段还是可能跑偏。我在正文开头加了一句警告如果用户输入内容不涉及任何会议、讨论、多人沟通场景立即停止并输出提示信息不要套用本格式。加了这一句之后误处理基本绝迹。5. 那些让技能失灵的坑我的完整排查链路与修复方案这一节是本篇最想分享经验的部分。技能用的时间长了各种奇奇怪怪的问题都会冒出来这里我把踩过的坑按问题表现—根因分析—修复过程的链路完整记录下来希望能帮你少走弯路。5.1 元信息写太宽模型逮谁调谁这是所有坑里最常见的一个。我最早写的一个技能是从客户工单中提取结构化信息元信息的 description 是这么写的description: 提取工单中的关键信息如客户问题、紧急程度、处理建议。看起来简短清晰但在实际运行里用户只要输入任何一点像工单的内容模型就会调用这个技能甚至用户直接问工单系统怎么用模型也会调用。原因就是这个描述只说了提取工单信息没说明触发场景和排他条件。修复思路是这样的我先分析了实际触发它的所有请求归纳出哪些请求属于合理触发、哪些属于误触发然后把合理触发的特征全部写进 description把误触发的特征明确列为不适用情况。改完之后误触发率从大约 40% 降到了 5% 以下。这个坑的本质原因是模型对技能触发条件的判断完全依赖 description 里的文字。你在描述里写得越宽泛模型的触发范围就越宽泛。所以永远不要在描述里写处理各种文本这类话它等于告诉模型什么都可以调用。5.2 正文指令写得像文档而不是手册模型不照做第二个坑比较隐蔽。我早期写技能时正文部分喜欢铺陈背景、解释原因比如会议纪要对团队协作非常重要请认真完成又或者写一堆原则性的要求比如请确保准确性请使用专业的语言。这看起来没毛病但模型执行时会更倾向于参考具体的、可操作的内容而忽略那些抽象的要求。有一次调试时我观察到同一个会议纪要技能我写请确保输出质量的那一版输出的质量明显不如后来改写的整理完成后逐项对照以下检查清单1. 结论是否全部覆盖2. 待办是否有负责人3. 口语化内容是否已清理那一版。我后来总结经验正文写的是操作手册不是价值观宣导。让模型做一件事最好的方式是给它步骤清单、判断标准和输出模板而不是给它一堆形容词。形容词不能约束行为清单能。5.3 脚本输出传不回模型数据链断裂问题第三个坑就不是 prompt 层面了是工程层面的。我之前给一个技能配了一个处理脚本逻辑是让模型把原始输入写到一个临时文件然后调用脚本对这个文件做处理最后从输出文件读回结果。脚本本身没问题但实际跑起来经常出现模型假装调用脚本但没有真正读取输出的情况——它在回答里直接假设脚本处理完了开始编后续内容。这个问题排查了很久最后发现根因在于我对 SKILL.md 中工作流程的表述是调用脚本对内容进行清洗但没有明确要求调用后必须读取输出文件的内容并以输出文件的内容作为后续处理的基础。模型对于调用了工具但没读结果没有天然的强制校验意识。修复方式有两个思路。第一个思路是把指令改得更严格调用脚本后必须先读取输出文件内容并粘贴到对话中确认无误后再继续后续步骤禁止在未读取输出文件的情况下进行整理。第二个思路更彻底调整设计让脚本不需要模型中间传递——比如把输入直接通过 stdin 传进脚本、脚本处理后用 stdout 返回这样数据链就不依赖文件中转。我用的是第二个思路把脚本改成标准输入输出流的方式后这个坑基本没有再现。5.4 一个技能想干太多事边界混乱的根源最后一个坑是我自己在设计策略上犯的错。早期我图省事把总结邮件并生成回复草稿写成了一个技能原以为可以让模型一步到位。实际使用中发现这个技能经常出现两种情况要么总结做得很好但回复内容质量很差要么回复写得不错但总结部分太潦草。根本原因是总结邮件和写回复其实需要的能力侧重点不同硬把它们塞进同一个技能流程里模型很难两头都兼顾。后来我把它们拆成了两个技能一个负责总结邮件输出固定格式的摘要另一个负责根据摘要写回复草稿依赖前者的输出作为输入。分开之后两个技能各自的表现都稳定多了。这个经历让我得到一个设计原则一个技能只做一件事每个技能的输出要足够清晰最好能被其他技能当输入使用。技能的拆分粒度应该是独立交付物而不是完整业务流。粒度太粗会导致模型顾此失彼粒度太细则会导致技能数量爆炸、触发逻辑混乱。6. 什么时候真的不该用 skills把 skills 吹了这么多也该泼泼冷水了。在我试过的场景里有几种情况用 skills 反而不划算甚至会让事情变更糟。6.1 一次性的任务不值得做成技能如果某个任务你只是随便做一次比如帮我把这份 pdf 的第一页转成图片这种一次性任务做成技能纯粹是浪费时间。做技能本身有成本要写说明、要测试、要维护这些成本只有在任务会重复发生时才能摊薄。我在实际项目里给自己定的判断标准是同一个任务至少重复出现三次才值得考虑做成技能只有一两次的直接用临时 prompt 处理。6.2 任务过于开放时技能反而会捆住模型的手脚有一些任务天然就是开放的、需要发散思维的比如帮我头脑风暴五个产品名给我一些创意灵感。这类任务没有固定的处理流程和输出格式硬套技能模板会把模型的创造性压得很死。我试过一个创意生成技能效果是生成的内容确实格式规范了但也变得套路化、无趣。后来我把这个技能删了回归直接对话。6.3 全局性的、跨场景的要求用系统提示词更合适还有一种情况也不该用技能要求在所有对话中持续生效的。比如回答问题时使用中文不要输出无关内容回答要简洁这类约束是全局性的它们适用于每一个对话而不是某个特定场景。这种情况下把它们写进一个技能模型只有调用那个技能时才遵守、不调用就忘了效果反而更差。这类全局约束应该放在系统提示词或对话指令里而不是技能里。6.4 与工具调用混淆的场景最后一种我需要特别提醒如果你的技能本质上只是调一个 API、查询一次数据库那它应该被实现为 function calling而不是 Agent Skill。两者最核心的区别在于Function Calling 是模型决定参数外部代码执行结果返回给模型它适合短平快的动作Agent Skill 是模型自己主导整个流程它适合多步骤、需要模型自主判断的任务。如果硬把 API 调用包装成技能你不仅增加了触发链路的长度还失去了 function calling 本身的结构化验证机制。6.5 我的取舍框架总结下来我现在做决定时用的是这个框架。如果任务是重复的、流程清晰的、输出格式可定义的用 skills。如果任务是开放的、一次性的、需要发散的直接对话。如果任务本质是调一个接口拿一个数据用 function calling。如果任务要求长期改变模型的风格或知识储备那考虑微调——尽管成本高但它是唯一能改变模型底层行为的方案。7. 最后分享一个我在实际调试中的小习惯文章写到最后我不打算再做宏观总结了就分享一个实际调试中的小习惯。我在开发与调试技能时专门建了一个技能自检对话用一个干净的会话往里面扔各种当天真实遇到的输入样本然后观察模型是否在正确时机触发正确技能、输出是否符合预期。这个会话不参与实际工作只做测试用。这个习惯的价值在于技能的退化往往是慢慢发生的。你今天改了技能 A 的描述可能影响到了技能 B 的触发判断你往技能 C 里加了一段新逻辑可能让模型在边界场景下的表现变了。定期用一批固定的样本做回归测试能在问题变得严重之前就发现它。我一般每周花半小时做这件事比起临时出了问题再排查成本低太多了。如果你要开始尝试写 skills我的建议是先挑一个你工作中最痛、最重复的任务按文中的方法做出来然后坚持跑一周。一周之后你回头看那些输出样本大概率会感受到结构化技能包和临时 prompt之间的差别那是稳定性和可维护性的差别也是我从这个项目里获得的最大收益。