
单看“skills”这个词如果不限定上下文其实是个特别容易飘的定义。你在搜索引擎里敲进去可能出来一堆培训课程、招聘要求、个人简历模板但在AI应用开发这个圈子里最近大半年只要有人提“skills”大概率说的不是你的个人能力而是Agent Skills——也就是智能体技能包。我最早接触这个概念是在折腾AI智能体的时候。那时候想让AI帮我查天气、读PDF、操作某个内部系统最原始的办法是写一堆System Prompt把所有规则和调用逻辑灌进去。结果Prompt越长AI越容易犯迷糊上下文稍微一挤前面的指令直接被“遗忘”。后来我把这些能力从Prompt里拆出来做成了独立的、按需加载的skills包效果立竿见影。这篇就好好聊聊这个叫“skills”的项目到底在做什么、解决什么问题、怎么落地以及我在实操过程中踩过的坑和总结的经验。开篇先把结论抛出来skills本质上是一种让AI按需获取能力的机制它跟传统的Prompt工程不一样不是把“怎么做”写进对话里而是把“能做什么、什么场景用、怎么调用”定义清楚让模型在需要的时候自己决定加载哪份能力。这套思路解决的核心痛点是长Prompt失效、能力冗余、维护成本高这三件事。适合正在做AI产品、智能体开发、或者想把手头重复性AI任务模板化的朋友阅读。1. “skills”到底在解决什么问题1.1 从手工写Prompt到技能封装以前我们让AI干活最土的办法就是堆Prompt。比如我要做一个写周报的助手通常会在System Prompt里写“你是一个周报助手请根据用户提供的本周工作内容按照以下格式输出周报本周完成事项、数据指标、下周计划、风险提示”。这段描述看着不复杂但实际用起来会有很多隐性问题。比如用户给的原始内容是聊天记录、会议纪要、邮件AI经常分不清哪些是“完成事项”还是“计划事项”。于是你又得补充一句“如果是将来时态归入下周计划”。补着补着Prompt越来越长模型行为却越来越不稳定。我当时试了好几个方案最长的一版System Prompt写了接近3000字涵盖各种边角场景。结果实测下来AI在长对话中段经常出现“失忆”明明前面定义好的格式到后面越写越随意。后来我意识到问题不在模型而在设计模式你让模型同时做太多事——理解任务、分析内容、套用格式、判断时态这些全挤在一个线性指令里等于让一个人既当翻译又当编辑还要当排版员。skills的解法是反过来的。它把一套能力做成了一个独立的“技能包”让模型在需要时主动去加载。技能包里有任务拆解、输入参数定义、输出格式约定甚至有专门给模型看的“使用说明”。模型接到任务后会先判断“这个请求适合调用周报生成技能吗”适合就加载技能不适合就走通用聊天逻辑。这样每个技能包都可以做得很专、很深而不会互相干扰。我实际用下来最大的感受是不再需要为每个场景维护一个不断膨胀的System Prompt了。1.2 skills与MCP、Function Calling的分工很多朋友第一次接触skills时容易跟MCP和Function Calling搞混。这里我用自己的理解梳理一下这三者的区别。Function Calling本质上是给模型开了一扇门让它可以调用外部函数。它解决的是“模型怎么触达外部世界”的问题——比如模型要查天气你给它一个get_weather的函数声明模型把参数填好你的代码去请求天气API再拿结果返回给模型。这套机制很成熟但它关注的是“每一步调用”而不是“整件事怎么做”。MCP(Model Context Protocol)解决的是“工具怎么被标准化接入”的问题。它能统一管理多个数据源和工具服务把文件、数据库、API一次性暴露给模型。但MCP本身不带业务逻辑它更像是给模型开了一个超市模型能看见货架上的所有商品可“怎么做一顿饭”——也就是完整的任务流程——依然要靠Prompt去描述。skills站在更上层。它描述的不是“能调什么函数”而是“这个能力是什么、适用于什么场景、输入输出是什么样、整个流程怎么走”。举个例子MCP负责让你能读Excel文件Function Calling负责让你能调用Excel解析函数但“把Excel里的数据整理成一份带同比环比的分析报告”这件事是skills的活。它是把工具组合、业务规则、产出要求打包成了一个独立可复用的单元。这个分工对我最大的启发是不要试图让一个方案解决所有问题。实际架构里底层通信用Function Calling或MCP对外的“能力封装”用skills两者是配合关系而不是替代关系。2. 怎么把一个想法变成一个skills2.1 目录结构与配置文件先说结论一个完整的skills目录结构我这边常见两种形态。一种是单体技能包适合单个能力独立使用另一种是复合技能包把多个关联能力串成一个流程。这两种结构在一线实践中都有对应的场景。拿单体技能包来说我目前的推荐结构是这样的skills/ ├── SKILL.md # 技能主文件模型最优先读取 ├── scripts/ # 存放可执行脚本或数据处理代码 ├── assets/ # 静态资源、模板文件比如输出Excel的模板 ├── references/ # 参考资料一般放文档、示例、FAQ └── requirements.txt # Python依赖或系统依赖说明SKILL.md是这个技能包的大脑。它的作用不是写给用户看的而是写给模型看的“说明书”。模型在判断是否加载这个技能时会先读这个文件里的name和description字段如果命中场景再继续往下读具体执行步骤。所以SKILL.md写得清不清楚直接决定了技能会不会被正确触发。我在做skills时一般会先在本地建一个目录确定好名称和用途再拆解这个技能需要哪些子能力。比如我之前做的一个“会议纪要整理”技能拆解后包含“语音转文字结果清洗”“发言人分离”“任务项抽取”“纪要格式生成”四件事。其中前两件靠脚本处理后两件靠模型逻辑所以skills目录里既有代码文件也有给模型的推理指令。这个拆解过程非常重要但很多人会跳过它直接开写后面维护时就会后悔。2.2 SKILL.md里该写什么不该写什么SKILL.md的写法我觉得是这门手艺里最值得花时间打磨的地方。它不同于给人类看的技术文档因为你的读者是一个语言模型。模型读文档的时候不会像人一样有耐心慢慢琢磨字里行间的隐含意思它对结构化信息更敏感。我先给你一个示例这是我写的一个“周报整理技能”的SKILL.md精简版--- name: weekly_report description: 根据用户提供的工作记录、聊天记录或会议纪要生成结构化周报。 适用于用户需要周报、周总结、工作汇报时的场景。 --- # 目标 将零散的原始工作记录整理为一份结构清晰、数据可追溯的周报。 # 输入 - 原始记录: 用户提供的文本可能是聊天记录、待办清单、散装笔记 - 时间范围: 周报覆盖的起止时间 # 输出格式 1. 本周主要完成事项按优先级排序不超过5条 2. 关键数据与指标如果原始记录里有具体数字必须呈现没有则注明“本周无关键数据” 3. 下周工作计划只提取原始记录中明确属于未来的事项 4. 风险与阻塞项判断是否存在依赖他人、资源不足、进度延误等情况 # 执行规则 - 不要推断原始记录中没有提及的数据 - 如果原始记录过短少于3条内容主动告知用户输入不足而不是强行生成 - 在周报末尾加上“本报告由AI辅助生成原始数据请以实际工作为准”的提示你不难发现这份文档不是在教模型“写周报要专业”而是明确告诉它“输入长什么样、输出长什么样、遇到边界情况怎么处理”。尤其是执行规则里的三条可以说是整个技能包最关键的防腐剂它们防止模型在数据不足时瞎编也防止它越权处理模糊信息。写SKILL.md时有一个反复出现的坑把技能描述写得像是给用户的产品说明PPT而不是给模型的执行手册。比如我看到有人会在description里写“生成专业、高质量、有价值的周报”之类的主观形容词这对模型触发几乎没有任何帮助。模型的判断依据应该是可被客观识别的场景特征。比如你提到“周报”这个词提到“本周做了什么”或者用户直接要求按周报格式输出这些才是能被模型理解的触发条件。2.3 依赖管理与运行环境别小看依赖管理这一块它决定了你的skills换个环境还能不能跑起来。我的做法是任何需要运行脚本的skills都必须在包内自带requirements.txt并且尽可能把Python版本、第三方库版本固定下来。因为模型加载skills后实际执行代码时如果发现缺包或者版本冲突整个任务就卡住了前期体验会非常糟糕。我自己的一个习惯是把运行环境分为三层第一层是模型本身它负责逻辑推理第二层是脚本执行环境也就是你本地或服务器上的Python/Node环境第三层是外部依赖比如API密钥、数据库连接。skills包主要管好第一层和第二层的衔接而外部依赖一般通过环境变量注入不要写死在skills文件里。这样做的好处是同一个skills可以复用在不同的项目里不会因为API密钥不同就互不兼容。3. 核心参数与输入输出设计一次说透3.1 输入定义中的类型、必填与默认值输入定义是skills设计中最容易被低估、但影响最大的部分。很多最初版的skills只写了一句“input: users text”模型收到请求后就开始自由发挥结果输出的质量完全取决于这个请求撞在了模型的哪个随机采样上。要是把输入定义得够细、够可预测模型的表现就能明显稳定下来。我一般在输入定义里至少会做三件事说明类型、标注必填项、给出默认值。举个例子“周报整理技能”的输入参数可以定义成这样参数: - 名称: raw_text 类型: string 必填: true 说明: 用户提供的原始工作记录 - 名称: time_range 类型: string 必填: false 默认值: 本周 说明: 周报覆盖的起止时间可以填上周、上个月等 - 名称: include_metrics 类型: boolean 必填: false 默认值: true 说明: 是否需要单独呈现关键数据指标别看这只是几行参数说明它对模型行为的影响是实打实的。默认值的存在特别重要它告诉模型即使用户没提这个信息你也可以按照某个标准继续执行。如果没有默认值模型就会试图在用户输入里猜测猜不准的时候就开始编这是很多AI输出“看起来合理但全是假的”的直接原因。类型标注的另一个好处是当用户给了一个时间范围却写成“前几天”这种模糊表达时模型至少会意识到类型不匹配从而追问“您说的具体是从哪天到哪天”。这在人看来很笨但至少它不会直接把错误信息写进周报里。3.2 输出约定的表达技巧输出约定这部分我觉得核心思路是把“目标风格”翻译成“可检查清单”。比如你说“输出一份专业的周报”模型没法判断什么叫专业但如果你列一条“每个完成事项后面必须跟一个动作动词”模型就能按规则执行。我自己常用的做法是定义一个固定模板然后在模板里用{{变量}}标注可变化的部分。这样的好处是模型只需要把内容填充进去格式完全不用发挥。遇到特定的业务场景我还会在输出约定里加上“二选一”规则当满足某个条件时输出A版本否则输出B版本。这比直接让AI自由创作稳得多。另外强烈建议在输出约定里加入一条“假设用户不懂本领域术语”的默认说明。这是我从一次实测中悟出来的。一开始我做的竞品分析技能默认输出全是行业黑话虽然专业但对转述给业务方看极其不友好。后来我在输出规则里加了“每个专业术语首次出现时必须附一句话解释”生成结果的可用度立刻提升了一个档次。这个习惯后来被我带入了所有skills中。3.3 异常输入的处理机制异常输入处理是最能体现skills完整度的地方。一个只有“正确路径”的skills本质上是个半成品因为真实用户输入大部分都带噪音。比如用户本来想让你整理周报结果贴了一段完全不相关的生活记录或者用户在周报数据里只写了三句话信息量不够却希望生成完整报告。我的处理原则是宁可让模型停下来问问题也不要让它硬着头皮接着编。在这个原则下我通常会在SKILL.md里明确规定“什么情况下必须中止生成”和“什么情况下必须向用户追问”。比如前文示例里的执行规则就是这种设计。你可以在规则里写“如果信息量不足以生成5条完成事项且无法从原文推断则停止生成说明缺失内容”。这一条看起来简单但真正能拦住模型胡编乱造特别是当它处于一种严格按照格式推进任务的状态时这条规则就像一道刹车片。排查问题时有一个定位方法非常实用就是故意给技能一个残缺输入观察模型是否触发追问。如果模型在信息明显不足的情况下还能自圆其说地输出报告那说明异常处理规则没有生效你要么把规则写得更冲突更明确要么检查模型是否真的读取了技能文件。大多数情况下问题出在规则写得不够“指令化”比如写的是“建议遵循”而不是“必须遵守”。4. 完整实操从零写一个“竞品分析服务”技能4.1 从需求梳理到目录设计这里分享一下我打磨过的“竞品分析服务”技能它很能说明一个完整skills的开发流程。需求来自我们组里一个做产品运营的同事她每周要花两个多小时手动去搜集竞品的动态、整理成报告。当时这活儿没有数据接口也没有现成的数据库唯一的信息来源是一堆网站和行业新闻。于是我决定做一个能自动汇总、分析并输出报告的技能。第一步是跟同事聊清楚需求边界。这里有个经验多花十分钟问清楚“报告给谁看”“决策什么用途”比闷头做功能强得多。我们的结论是报告是给产品经理看的目的是辅助评估竞品功能改版方向所以报告要包含“竞品功能变化速览”“针对性点评”“对我们的启示”三个模块。你看这个需求梳理直接决定了后面所有的模板设计和参数定义。我把这个技能的目录设计成了这样skills/ ├── SKILL.md ├── scripts/ │ ├── fetch_products.py # 获取竞品信息 │ ├── fetch_updates.py # 获取竞品动态/新闻 │ └── summarize_diff.py # 对比归纳 └── references/ └── report_template.md # 报告模板目录设计的核心是把“获取数据”和“生成内容”分离。脚本负责抓取原始材料模型负责做判断和写作双方通过中间文件对接。这样做的好处是将来如果数据源换了只需要改脚本不需要动SKILL.md里的推理逻辑。4.2 前期准备与脚本实现搭建脚本那一步我踩过一个比较深的坑一开始把抓取竞品新闻的时间跨度设置成了近24小时。后来发现很多时候竞品并没有那么活跃报告里经常只有一条动态显得很单薄。后来我改成“近7天动态 近90天重点变化”两层结构抓取脚本返回原始数据时做一次粗分类模型拿到的是已经按时间维度整理好的素材。我简单放一下fetch_updates.py的核心逻辑它不是完整的生产代码但可以展示脚本和skills之间的接口设计import requests from datetime import datetime, timedelta def fetch_updates(product, days7): 获取竞品近days天的动态返回结构化列表 params { product: product, start: (datetime.now() - timedelta(daysdays)).isoformat(), limit: 20, } resp requests.get(/api/competitor/updates, paramsparams, timeout10) resp.raise_for_status() updates resp.json()[items] # 按时间排序并过滤掉明显与产品无关的内容 updates [u for u in updates if u.get(is_product_related, True)] updates.sort(keylambda x: x[published_at]) return updates这份脚本的思想是把脏活累活请求网络、过滤噪音、排序在数据进入模型之前就做掉。模型本质上是一个推理引擎你给它一堆没有清洗过的原始网页标题它会浪费大量“智力”在判断相不相关上很容易分析着分析着就跑偏。让脚本先把食材洗好切好模型才能专心炒菜。4.3 模型调用与输出模板设计报告模板的设计我花了最长的时间。最开始我参考了同事过往手写的报告格式把模板一开始就定得很细包括标题、段首引导语、结论句式等。真正试运行后发现问题模板太满模型没有空间写自己的判断生硬得像填空。后来我调整策略把模板拆成“硬结构”和“软表达”两层——硬结构是必须遵守的章节顺序和标题层级软表达是各章节内的行文方式模型可以自由发挥。我最终采用了类似这样的报告模板# {{product_name}} 竞品分析报告{{date_range}} ## 1. 功能变化速览 以表格形式列出本周观察到的功能更新字段为 | 时间 | 模块 | 变化描述 | 来源 | ## 2. 针对性点评 从“增强体验”“扩大覆盖”“商业化探索”“风险信号”四个维度中 选择本次变化最相关的2-3个维度展开点评。 每个维度控制在3-5句话避免主观臆断。 ## 3. 对我们的启示 针对每个功能变化给出“我们是否需要跟进”的判断 判断必须在“建议跟进、建议观察、暂不跟进”三选一。 若选择建议观察或建议跟进需附上一句理由。这个模板的妙处在于它在关键决策点是否跟进上强制模型做出“三选一”而不是让它自由发散但同时给足了表达空间。实际效果是报告从一堆记叙文变成了有结构、有判断、可直接讨论的经营材料。模板设计之后你可以先在测试数据上跑一遍观察模型的输出是否符合预期再对照检查报告结构是否完整。4.4 自测与调优迭代比一步到位靠谱调优环节我基本形成了固定套路准备三份不同难度的人工标注数据——简单样本、中等样本、边界样本。简单样本指竞品信息丰富、模板按部就班输出即可中等样本指信息有缺失或存在模糊表述边界样本指竞品没动态或用户给的竞品清单不明确。这三类样本能覆盖大多数真实使用场景。以边界样本为例最初我的模板没有考虑“竞品没有任何动态”的情况模型硬着头皮输出了一张只有一行“暂无动态”的表格显得很敷衍。后来我在执行规则里加了“如果只有一条动态至少在点评里展开说明和上个版本的关联并指出信息不足以判断是否要跟进”模型输出质量立刻提升。每次调优我通常只改一处规则跑一遍测试集比较前后输出确认没有引入新问题再改下一处。一次改三四处的话出了问题你根本分不清是哪处导致的。这个习惯推荐你们也试着养成虽然慢但特别稳。5. 常见问题与排查技巧实录5.1 模型不触发技能怎么办这是我在skills实践中收到最多的疑问。“我写好了SKILL.md但对话时模型根本不鸟它还是走通用聊天逻辑。”排查的第一步是确认技能文件确实被加载了。很多框架提供debug模式或日志输出你可以在初始消息里要求模型“先确认自己具备哪些可用技能”看看回复里是否包含你定义的那个名称。如果模型确认技能在但对话中没有触发问题大概率出在description字段。我的经验是模型的场景匹配大部分靠对description的语义理解。一个常见的错误是description写得太抽象比如“此技能用于帮助用户高效完成信息整理与分析”几乎毫无特征模型无法把它和某个具体请求联系起来。更有效的写法是“当用户提到竞品、竞品动态、竞品分析、竞品监测、竞品报告时使用”写满你能想到的触发词和场景变体。第二种情况更隐蔽你的技能确实被加载了但模型在下一次对话后“忘了”技能内容里的细节。很多框架对长技能内容有限制或者模型上下文被其他指令挤占导致SKILL.md被部分截断。遇到这种情况我会在生成任务前加一句“请先仔细阅读SKILL.md的全部内容再开始执行”实测下来能明显减少忘记细节的情况。5.2 多个技能互相覆盖或冲突怎么处理当项目里的skills数量多起来后另一个高频问题出现了多个技能都觉得自己能处理当前任务。比如你同时装了“竞品分析”和“行业趋势分析”两个技能用户说“帮我看看现在的AI应用行业有什么动向”两个技能都可能被触发。解决这个问题的思路有两个一个是在description里明确技能的专属边界比如“竞品分析技能仅适用于用户明确指定了竞品名称或竞品列表的场景”把模糊场景排除掉另一个是在SKILL.md的“执行优先级”里写清楚——如果同时存在更匹配的技能让给那个技能执行。虽然模型不一定总能严格遵守优先级但写明后冲突率确实下降很多。我还碰到过一种挺抓狂的情况两个技能的脚本都要写同一个临时文件导致运行到一半互相覆盖。后来我的统一方案是所有skills的脚本写入路径按技能名加时间戳命名并且默认建在一个独立的临时目录下互不干扰。这个改动极小但避免了很多诡异故障。5.3 输出格式偶发混乱如何根治偶发格式错乱的排查我会先判断是规则缺失还是模型问题。规则缺失指的是SKILL.md里没写清楚某个边界情况模型只能自己发挥模型问题则是指即使规则完整模型在长输出或复杂输入下也偶尔不遵守结构。针对前者我会反复测试各种输入组合找出“哪种输入下格式最容易乱”。找到之后直接把这个情况与对应的格式要求写进执行规则并在描述时用非常肯定的语气“在任何情况下都必须先输出表格再输出点评”。针对后者我有一个笨但有效的方法在输出约定里增加“模型应在最终输出末尾检查一次章节编号是否连续、表格是否完整”的自我检查规则让模型在生成完内容后进行一次质量核对。这个自我检查规则一开始我是不信的觉得增加了模型负担实际跑下来发现对格式稳定性提升很明显代价是生成时间稍微变长了一点点完全值得。如果你对格式的稳定要求极高可以在外部再加一层规则校验比如用脚本检测输出里是否存在指定章节标题没有就触发重新生成。5.4 速度慢与资源占用问题skills加载是有成本的。每轮对话如果模型都要读一遍所有技能的SKILL.md响应时间会明显变长。我实测过一个框架配置了12个技能后首响应时间比不配技能时慢了将近一倍原因就是每个技能文件都参与了输入的拼接。对应的优化思路有三个按需加载、精简文本、内容外置。按需加载是让系统先根据用户的初始请求做一个粗略的关键词匹配只把可能命中的技能注入模型精简文本是尽量把SKILL.md压到600字以内把详细说明拆到references里让模型按需读取内容外置则是把大量静态内容放进外部文件SKILL.md里只留一句“完整细则见references/xxx.md”引导模型按需查阅。这三个办法可以叠加使用。我目前的项目里默认情况下一轮请求只加载1到2个技能其余全部通过references懒加载速度问题基本不在是瓶颈。6. skills的边界与扩展它还能怎么用6.1 安全边界别让技能越权技能包一旦能被模型按需加载就存在“被诱导执行不该执行的操作”的风险。想象一下你做了一个“财务数据分析”技能里面有读取内部预算表的脚本如果用户在对话中诱导模型把数据导出发送到某个外部邮箱模型可能会因为“被限制在技能的可执行范围内”而不加区分地照做。这是我在做内部技能时重点防范的场景。我的防护做法有三层一是脚本层面做白名单校验凡是要写入或导出数据的操作必须校验目标路径是否在白名单内二是SKILL.md里毫不含糊地写明“禁止将财务数据发送到任何内部系统之外的目的地”三是在更敏感的操作里强制增加一步人工确认要求模型在导出前输出确认提示等用户明确确认后再执行。这三层叠加虽然不是万无一失但在实际项目中已经能挡住绝大部分风险。6.2 skills的组合进阶主技能拉子技能当前skill体系还可以往组合方向进阶。我的做法是把一个复杂任务拆成多个子技能再用一个“编排型技能”把它们串起来。比如“月度经营分析”这个主技能内部实际会依次调用“财务数据提取”“竞品动态汇总”“业务核心指标计算”“经营报告生成”四个子技能。主技能的SKILL.md不直接写具体怎么分析而是写清楚调度逻辑判断需要哪些子技能、用哪个结果喂给下一个环节。这种组合方式的好处是每个子技能都可以被单独复用主技能更像是一份菜谱。缺点是调试起来复杂一些一旦某个环节出错需要顺着整条链路排查。我自己目前的取舍是当子技能的复用率超过50%才值得拆成独立技能如果一次性场景直接写成一个大的技能反而更好维护。6.3 用版本管理给技能做迁移最后提醒一点skills作为代码一样的存在一定要纳入版本管理。我自己吃过大亏某个技能调了格式模板之后上线没有及时备份两周后想回滚直接没得回。现在我把每个技能都看成一等公民代码目录严格按照标准来管理——变更记录、版本号、依赖锁定一个都不少。如果是多人协作我还会在技能的SKILL.md顶部加一个元信息块记录当前版本号、修改人、修改原因、兼容性说明。这样当别人接手技能时不需要从聊天记录里考古“这个配置是谁在什么时候加的、为什么这么写”。这看着像小事但在项目跑起来后能省掉大量沟通成本。关于skills目前我自己最深的一个体会是技能的设计到了一定程度拼的就不再是让模型变聪明而是你如何把一个复杂任务拆得足够清晰、定义得足够严谨、容错做得足够好。模型的推理能力是底座而skill体系是让这个底座稳定输出价值的脚手架。如果你想从“会写Prompt”迈向“能搭建一个可靠AI助手”从手写第一个自有技能目录开始是最恰当的选择。