如果你最近也在折腾AI Agent一定遇到过这种让人血压升高的场景模型写出来的代码挑不出毛病但你让它自己把活儿干完它却总是在某个不起眼的环节掉链子——要么忘了查中间结果要么把参数传错要么做到一半开始一本正经地编造结论。这类问题我花了很长时间才想明白缺的不是更聪明的模型而是一套能被稳定调用、组合、验证的能力单元。这就是agent-skills体系想解决的问题。我想借这篇文章把围绕agent-skills做技能化改造的完整思路梳理一遍它本质是什么、一个技能包的结构长什么样、怎么从零给Agent新增技能、运行中会踩哪些坑以及最终怎么把它变成团队化的能力资产。无论你是在做Chatbot、自动化流程还是内部工具只要你的Agent还停留在“能聊天但干不成事”的阶段这套思路应该都能帮上忙。1. 先想明白Agent为什么需要“技能”而不是更长的提示词1.1 模型能聊天不等于模型会干活聊过天的人都知道大模型在对话场景下表现极好你问它“帮我写一段Python读取CSV”它能给你写得很漂亮。但一旦进入任务场景比如“读取我桌面上这个文件做数据清洗统计分布画三张图最后输出一份结论报告”模型就经常半路崩盘。原因在于对话是单轮或几轮的语言交互任务是带有目标、约束、中间产物和验收标准的连续过程。模型本质上是一个概率系统它擅长的是“生成最可能的下一段文本”而不擅长“记住当前项目的硬约束”。比如它可能忘了你的文件是GBK编码可能不知道目标服务器上没有某个Python包也可能把上一步产出的文件路径记错。这些都不是“智能”问题而是“工程状态管理”问题。agent-skills思路的核心就是把“干活”这件事拆成可管理、可复用、可验证的能力单元。一个技能不只是让模型“知道怎么做”而是把做事的上下文、边界、脚本、校验逻辑全部打包进去让模型一旦选中这个技能就相当于进入一条相对固定的轨道而不是每次都在开放空间里自由发挥。1.2 “提示词越写越长”是走向失控的下坡路很多人的本能反应是模型不稳那就把规矩写在system prompt里。我一开始也这么干——把环境信息、输出格式、使用工具列表、注意事项全塞进一个12000字的系统提示词里。结果是灾难性的上下文被无关信息占满模型开始“选择性地遗忘”关键规则而且每次修改提示词都可能影响其他能力。你把CSV处理规则写进去结果模型写邮件总结时也开始用表格输出整个行为都变了形。为什么因为提示词是全局广播它没有触发条件也没有边界。模型在每一个输出位置都在考虑这段规则要不要生效决策空间越大行为越不稳定。技能体系的做法完全不同它是按需加载。模型先判断“当前用户意图匹配哪个技能”匹配到之后才把该技能对应的指令、脚本、示例载入上下文。没被选中的技能完全不占空间也不会干扰其他行为。这就好比一个公司给每个岗位写SOP而不是把全公司所有人的职责写在同一张纸上。1.3 技能、工具、工作流三种粒度别混用在动手设计技能库之前有必要把三个概念分开。很多人把它们混为一谈导致架构混乱。工具Tool/Function是最小单位一次原子操作比如“读取文件”“调用搜索API”“执行SQL”。模型只需要决定用不用、参数填什么。技能Skill是中层单位一段可复用的多步流程内部可能调用多个工具、执行脚本、做分支判断。技能有自己的输入输出约定和校验逻辑。比如“月度经营分析”技能可能包含拉数、清洗、统计、画图、写报告五个步骤。工作流Workflow是全局编排通常由系统或人来预定义执行时每个阶段可能唤起不同Agent和技能。它更像流水线不依赖模型临场决策。三者的对比我整理成了表格维度工具/Function技能/Skill工作流/Workflow粒度单次调用多步骤编排端到端流程决策方模型决定用哪个模型确定性脚本混合系统/人主导灵活性高中高低可复用性单点复用能力级复用流程级复用典型结果返回查询结果产出一份完整分析完成一个业务目标我的建议是把稳定的多步操作沉淀为技能把跨技能、跨角色、需要审批的流程做成工作流而工具则作为技能内部的“零件”存在。不要试图用一个超长提示词覆盖三个层级那等于让模型在每一个输出位置都重新发明一次工作方法。2. 技能包的结构目录、元信息与执行机制2.1 一个技能文件夹里到底装了什么一个标准技能包不应该是一堆杂乱说明而是一个有固定结构的目录。我个人经过几轮迭代最终固定下来的结构大致长这样my-skill/ ├─ SKILL.md ├─ scripts/ │ ├─ run.py │ └─ requirements.txt ├─ assets/ │ └─ 模板、参考图等静态资源 ├─ references/ │ └─ 领域规则、样例文档 ├─ examples/ │ └─ 输入输出示例 └─ tests/ └─ 回放测试用例其中SKILL.md是整个技能包的入口所有加载器先读这个文件。scripts目录放确定性逻辑脚本因为纯靠模型“想”出来的中间值不可靠凡是计算、转换、校验类工作都应该下沉到脚本里。references和examples是给模型参考的让它在执行时知道“长什么样的输出是合格的”。tests目录则用于自动化回归——技能升级后需要确认旧场景没有被破坏。我见过很多人只写一个几百字的SKILL.md就宣称“技能化”了这其实只是把提示词挪了个位置。真正的技能化必须伴随可执行脚本和可验证的测试否则你得到的还是随机行为。2.2 description是技能被“看见”的唯一入口技能库里的技能多了以后模型怎么知道该调用哪个靠语义匹配。而匹配的质量几乎完全取决于SKILL.md里description字段写得怎么样。这是我在整个agent-skills实践中体会最深的一点模型不是靠目录路径找技能的它是靠用户意图和技能描述之间的语义相似度来找的。写了一版“处理CSV文件”的描述之后模型在用户提到数据分析时经常不去调它。后来我改成“对CSV/Excel等表格文件执行探索性数据分析输出字段统计、缺失值清单和可视化图表。当用户提供文件路径并要求了解数据概况、字段分布或数据质量时使用”命中率立刻上来。写description的核心原则是动词开头、包含触发场景、包含明确的使用边界甚至要写“什么时候不要用”。边界写清楚了误调用的概率才会下降。这一点很像搜索引擎里的标题和摘要——技能库就是模型的信息检索系统索引建得差后面一切执行层面的努力都会被浪费。2.3 声明式执行与命令式执行两种形态各有用途技能执行有两种典型形态。声明式技能技能给出步骤说明模型负责逐步解释并执行每一步模型都有判断空间。这种技能灵活但同时也继承了模型不稳定的缺点。命令式技能技能主体是一个脚本或程序模型只负责解析用户输入、填充参数然后调用脚本拿结果最后把结果转述给用户。这里模型几乎没有中间决策空间稳定性和可测试性都大幅提高。我的经验是能用命令式解决的部分坚决不把决策权交给模型。比如数据统计、格式转换、文件遍历、调用外部API这些步骤一旦做成“模型临场发挥”每次输出的格式都可能不一样下游根本没法处理。而像“根据结果组织用户沟通语言”“决定下一步追问什么”这类需要对话智能的环节才应该保留给模型。声明式和命令式不是二选一而是同一个技能内部的分工。大部分高质量技能都是混合体脚本完成确定性动作模型夹在中间做语义翻译和用户交互。2.4 技能如何被发现、加载和与MCP协作技能不是写好了就自动生效它需要被一个运行环境发现并加载。常见的做法是在Agent配置里声明技能目录的路径启动时扫描所有技能包的SKILL.md把元信息尤其是description和version汇总成一个索引。当用户新输入到来时Agent先在这个索引上做检索选出最相关的几个技能再把它们的完整内容注入当前上下文。这里有个关键技术点不要把整个技能库一次性放进上下文。技能数量多了以后上下文会爆掉而且无关技能还会干扰推理。正确的做法是像pipeline一样先粗筛再精排只把top-k个技能的详情注入。k通常取2到5就够了。技能和MCPModel Context Protocol的关系我习惯理解为MCP解决“Agent怎么连上外部工具和数据源”的协议问题技能解决“Agent如何组合这些能力完成一个目标”的编排问题。技能内部可以通过MCP调用外部服务比如你说“分析本月营收”技能里可能就包含一个MCP工具调用来拉取数据库数据。一个管连接一个管编排二者并不冲突。3. 面向模型的写作《SKILL.md》的核心设计心法3.1 你的读者是LLM不是人给技能写文档最容易犯的错误是把它当成面向人的内部手册来写——背景讲一大堆逻辑很完整但模型读完不知道怎么落地。原因在于人阅读时会自动补充常识而模型不会它的每一步行为都直接受文本字面影响。我现在的写作原则是SKILL.md是一份只给模型看的“操作说明书”不是给人看的“设计文档”。开头不要写“本技能旨在提升”直接写当用户表达什么需求时使用本技能正文不要写“合理评估数据质量”要写“调用scripts/quality.py从stdout中读取missing_rate字段若大于0.3则输出告警”。每一条指令都必须有一个模型可以判定的动作和可观察的结果。你可以把SKILL.md想象成给一个“聪明但容易忘事、且刚刚入职的实习生”写的交接文档。你需要把工作环境、操作步骤、可用的工具和验收标准都交代清楚并且每一步都能让他自查。3.2 用离散步骤替代笼统指令模型处理长指令的能力有限尤其当指令本身充满含糊动词例如“综合评估”“合理处理”。我踩过最典型的坑是技能里写“处理数据后生成分析结论”模型真的就只写了一句话“数据质量良好分析完成”完全没有输出任何中间统计信息。这不是模型偷懒而是指令里没有限定“处理”到底包含哪些子动作。正确的写法是拆分步骤并且明确每一步的输入与输出调scripts/preprocess.py处理输入文件输出到临时目录。读preprocess.py输出的manifest.json确认字段完整。调scripts/analyze.py生成stats.json与图表。依据stats.json组织用户可读的结论逐项列出关键指标。每一步的产出都是下一个步骤能够消费的明确对象这样模型没有机会“跳过”它应该完成的动作。步骤数量建议控制在5到10个之间太少了模型容易跳步太多了上下文太长、决策负担重。3.3 输入输出Schema防呆的关键模型填写参数时如果全靠自由文本很容易出现五花八门的格式。比如同一个技能有人传“file_path: /data/a.csv”有人传“路径是/data/a.csv哦”下游脚本一旦做了严格解析就崩。我的办法是在SKILL.md里直接写输入参数表包含参数名、类型、必填性、默认值和允许值范围。模型比人更擅长读结构化定义你给它一个枚举列表它基本上不会跑偏你给它一个开放描述它就能给你整出七八种风格。输出也是一样的道理。如果技能最终结果会被下一个环节消费必须要求输出JSON——且JSON的schema要预先定义好。我会在SKILL.md里写{ summary: string, 一句话结论, table_name: string, 数据表名, row_count: number, issues: [string] }并在规则里明确写着“禁止在JSON外层添加markdown代码块”。这句话看着多余但它能省掉无数下游解析Bug。模型输出自然语言时太爱加修饰了你不把边界画死它就会把边界踩碎。3.4 高质量的示例与显式错误回退示例的作用往往被低估。模型在上下文里的few-shot能力极强你放一个高质量输入输出示例它模仿的成功率会显著高于你写十句抽象规则。所以我在每个技能里至少放两个示例一个是正常场景最好贴近真实业务另一个是边界场景比如空文件、超大数据量或权限缺失的情况。错误回退也要写清楚。比如技能脚本失败时模型应该做什么不应该做什么。我常写的几条规则包括脚本返回非零退出码时先读取stderr原样反馈给用户不要自行臆测原因文件不存在时调用list_files扫描目录不要假设路径主技能无法满足需求时明确告知用户能力边界而不是编造一个结果。错误处理写清楚了模型面对异常时才会有“有原则”的反应否则它会在失败时用一段流畅的话制造一个完美假象。4. 完整复现给Agent加一个可复用的CSV分析技能4.1 先看没有技能时Agent为什么翻车我用一个实际场景做例子。假设你希望Agent完成这样一个任务给你一个CSV文件路径它自动读取、检测字段类型、统计缺失率、输出几个关键分布图然后用几句话总结数据质量。没有技能时模型通常会先写一段pandas代码然后假设自己已经运行过直接输出“数据共10列无缺失值”。更麻烦的是它可能不知道目标环境的Python版本、不知道需要安装matplotlib。就算你把这个坑给它讲了一遍换一个文件、换一台机器它又会踩一遍。因为每一轮对话都是新的概率生成上一轮的教训根本没有变成可复用的资产。技能化的目标就是把这个场景里的全部环境知识、脚本逻辑、输出规范固化下来。让同一个任务不管是今天跑还是三个月后跑不管用户表述得清不清楚Agent都自动进入同一套稳定的执行路径。4.2 创建技能目录与编写SKILL.md我动手新建一个技能包时会先在代码库的skills目录下建一个子目录名字就叫csv_eda。然后写SKILL.md。核心内容如下--- name: csv_eda description: 对CSV/表格文件执行探索性数据分析输出字段统计、缺失值、数据类型与分布图。当用户提供本地文件路径并要求了解数据概况、数据质量或字段分布时使用。不适用于超大数据集建模。 version: 1.2.0 language: zh --- ## Input - file_path: string, required, CSV文件绝对路径 - target_column: string, optional, 需要重点分析的字段 - max_rows: number, default 50000, 超出则自动采样 ## Steps 1. 检查file_path是否存在不存在则调用list_files技能定位文件。 2. 运行: python3 scripts/summarize.py --input file_path --max-rows max_rows 3. 读取stdout中的JSON结果若返回码非0读取stderr并向用户反馈。 4. 依据JSON组织结论包含行数、列数、缺失率TOP3、数值字段均值中和位数。 5. 如果用户关注分布展示脚本生成的分布图文件路径。 ## Output { row_count: number, col_count: number, columns: [{name: string, dtype: string, missing_rate: number}], top_issue: string, chart_path: string } ## Rules - 禁止修改原始文件 - 不输出“可能没问题”这类无数据依据的结论 - 所有统计结果必须来自脚本禁止模型自行估算写的时候我特别盯住两个点description的触发条件够不够具体Rules里有没有把模型最常犯的错提前堵死。“所有统计结果必须来自脚本禁止模型自行估算”这条规则直接杜绝了模型编数字的问题。4.3 让脚本承担“确定性计算”让模型负责“解释与决策”技能包里的scripts/summarize.py才是一切数字的真正来源。脚本负责读文件、自动识别编码依次尝试utf-8-sig、gb18030、推断列类型、计算缺失率、标记异常值然后输出一个结构化的JSON。如果数据量超过max_rows脚本会做分块采样避免内存溢出。如果文件太大脚本会先算一个文件大小阈值超过就只读元信息。选择让脚本来做这些是因为这类工作有三个特点确定性要求高数字错了用户直接不信任、环境依赖强编码问题、内存问题都是环境相关的、重复性强每个文件都要做一遍。这些恰恰是模型最不擅长的。模型适合的是判断用户意图、把脚本结果翻译成人话、决定什么时候追问用户。我经常跟人说模型是外交官脚本是实验室外交官不应该亲自做化学实验。写完脚本后我把requirements.txt也放进scripts目录说明依赖numpy和pandas并锁版本。很多技能跑不起来不是因为逻辑错而是依赖不完整。4.4 注册技能、测试回放与改造前后对比技能写好后我在Agent配置里加上一行指向skills目录。启动时会看到Agent成功加载了csv_eda的元信息。然后用三个测试文件做回归第一个是正常的中文列名CSV第二个是GBK编码带缺失值的CSV第三个是超过1GB的超大文件。每个测试都有一个预期的输出JSON和用户可读结论。改造前同样的三个测试跑下来成功率其实不低——那是恰好模型蒙对的次数。把同样的请求反复跑五遍模型每次都给出不同的输出格式统计口径也飘忽不定。改造后我连跑五遍三个文件全部通过且每次输出结构完全一致只有自然语言结论部分略有措辞差异。这个“结构稳定性”正是技能化最大的收益它把不确定的模型行为变成了确定的产品行为。5. 技能包运行中的六个边界问题踩坑实录5.1 技能隐身description写不好再强也没人用技能最冤的死法不是逻辑错误而是“该出场时没出场”。我早期写过一个小工具技能功能是批量压缩图片逻辑完全正确。但用户真的说“帮我压缩图片”时Agent经常直接自己动手写Python代码压根不去调这个技能。原因就是description里只写了“批量压缩图片”缺少触发场景“当用户提到图片体积大、需要缩小尺寸或转换格式时使用”。语义检索这东西偏偏就吃这个细节。教训是每次写新技能先别急着写逻辑把description当成“招聘启事”来写你要吸引模型在合适的时机选中你。写完测试时多造几个说法不同的指令看模型能不能每次都在合适的时机调用对应技能。如果三次测试里有两次没触发优先改description而不是改执行逻辑。5.2 指令打架全局提示词与技能规则冲突技能运行时的上下文里除了技能指令还有系统的全局提示词。二者一旦冲突模型就会陷入左右摇摆。我遇到过的一个典型案例全局提示词要求“所有回复保持简洁”而报告生成技能要求“输出完整分析结论”结果模型把报告硬缩成了三行核心图表都没提。解决思路是给技能内部规则更高的执行优先级并在SKILL.md里明确写上“本技能执行期间以本文档规则为准忽略与步骤无关的简洁性要求”。这里本质上是给模型划清“上下文区域”让它知道当前处于技能执行模式而非闲聊模式。如果你的运行框架支持也可以直接把技能注入到独立上下文块里减少与全局指令的纠缠。5.3 输出不规范自由文本毁掉下游解析只要技能结果需要被另一个脚本或另一个Agent消费输出格式就绝不能靠模型自由发挥。我吃过一次亏技能要求“输出JSON”模型正常输出JSON后还贴心地加了一圈markdown代码块下游解析直接报错。你以为问题解决了它又在下一次把JSON里的布尔值写成了字符串true。彻底解决的办法是双重保险一是在SKILL.md里写死输出schema并禁止markdown包装二是脚本接收模型输出时先做一次严格校验解析失败就要求模型重新生成。校验失败重试这个机制很重要它是你容忍模型不确定性的最后一道防线。技能架构再完善也要假设模型偶尔会犯低级错代码层面兜底才稳。5.4 权限与依赖脚本默认没有敬畏心技能脚本运行在本地环境时它的权限边界必须被显式约束否则很容易读取不该读的文件。技能包里的脚本一开始没有做路径白名单导致它顺带扫描了Agent工作目录里的所有文件把用户没有授权的配置信息也拉进来分析了一轮。这个问题很隐蔽因为输出结果看着正常实际已经越权。我的处理是在脚本入口加校验只允许访问用户显式提供的file_path参数其他路径一律拒绝。如果技能需要访问网络也单独配置网络白名单和超时限制。敏感操作比如删除文件、改写原文件、上传数据都要设置一个人工确认标志。权限这件事宁可一开始就严格也不要等出了问题再打补丁。5.5 版本漂移技能改了Agent却还在用老版技能库上线之后一定会有迭代。改完SKILL.md里的某个步骤或者更新了脚本逻辑结果运行环境的索引缓存没刷新Agent加载的仍然是老版技能描述。这种问题最坑的地方在于错误提示不明显你会误以为“改了没效果”其实是加载机制的问题。解决方案是规范版本管理每个技能包都带version字段修改时必须更新并写一行变更说明。技能目录启动时强制扫描并比对哈希值哈希变化就重建索引。这听起来是工程常识但我在实际项目里看过太多人忽略它最后在“为什么更新不生效”上浪费半天。6. 分层、复用与自举把技能体系变成Agent团队的岗位说明书6.1 技能分层基础、领域与编排各司其职技能数量多了以后一定要做分层设计。我把自己的技能库分成三层基础技能对应文件、网络、编码、格式转换等通用能力领域技能对应某一类业务场景比如财务对账、内容审核、指标分析编排技能负责跨层组合比如“周报生成”技能会调用“数据拉取”“文本摘要”“图表渲染”等多个下层技能。这个分层的好处是控制每个技能包的复杂度。基础技能保持稳定封装细节领域技能偏向业务规则迭代频繁编排技能只描述组合逻辑不重复造轮子。跟团队里岗位分工是一个道理你不可能让所有人都懂全部细节但你要让每个人都知道找谁、做什么、产出什么。6.2 把技能库当内部开源项目来维护技能一旦超过五个就不能一个人随手写了。我现在的做法是用Git管理技能库每个技能包独立成目录提交信息统一格式feat新增技能、fix修复行为、refactor重构合入前有人专门review SKILL.md的description和Rules。review的重点不是代码风格而是“如果你是模型看完这个技能描述能不能在正确时机调用它”。技能库本质上是一个团队资产不是你个人写给自己用的脚本集。明确技能所有者和维护边界之后别人才能放心依赖你的技能。另一个容易被忽视的点是文档与示例的更新。你修改了脚本逻辑examples里的示例也必须跟着改否则模型会照着过期的示例输出错误结果。6.3 自举让Agent帮你生产新技能技能体系建立起来之后会产生一个很有意思的现象Agent自己也开始具备“总结能力”。在实际项目中我会先让Agent在某个任务上跑几次用它的观察来起草一份初期技能文件。比如它发现一个文件夹里反复出现同名文件就可以让它把这个操作固化成脚本生成一个“文件去重”技能草稿。然后我再把description和边界规则补完整跑一遍测试用例确认无误后合并入库。这个过程很像带新人的逻辑先让新人试错和总结沉淀出SOP再由专家把SOP变成可配置的正式资产。自举不是让Agent替你做决策而是让它把重复劳动结构化让人的精力集中在描述边界、定义质量标准这些真正需要判断力的事上。6.4 控制技能库规模检索比收藏更重要最后一句经验技能库不是越多越好。技能包的规模超过一定量级描述检索的准确率会明显下降。模型不是无限上下文它精排的注意力资源就那么多技能太多反而互相干扰。我现在的原则是只把高频、稳定、价值高的场景沉淀为正式技能低频场景保留为普通文档让模型凭通用能力临场处理。打标签和分组也很重要。技能命名时带上领域前缀比如data_、file_、report_检索时就多一个有效信号。随着数量继续膨胀考虑给技能描述做向量索引用embedding做第一轮召回再用关键词和规则精排。这些都是运维层面的优化前提是前面几章讲的设计基本功都做到位。在我自己的项目里最大的改变不是某个具体任务的成功率升了多少而是我从“跟模型对话”变成“给模型搭体系”。agent-skills这个概念听起来像一套工程规范本质上却是一种思维方式先把人的经验拆解成能力边界清晰的模块再把模块用稳定机制组合起来。沿着这个方向走你能给Agent的能力就不只是“聪明”而是“可靠”。