首页 / 资讯中心 / 文章详情

AI Skills技能包实战:如何用SKILL.md打造可复用Agent技能

AI Skills技能包实战:如何用SKILL.md打造可复用Agent技能 ★ FEATURED ARTICLE
大概半年前“skills”这个词在AI应用开发圈子里开始频繁刷屏。我最初以为又是某个新框架的噱头直到自己上手把一堆散落的提示词、工具脚本整理成标准技能包格式、接入Agent跑通之后才发现这东西确实在改变我写AI应用的姿势。SkillsClaude系、OpenAI系生态里都越来越常见本质上是一套“教模型干活”的标准封装一份带元数据的任务说明书外加若干可执行的脚本和参考资源。它解决的问题很具体——大模型通用能力再强遇到特定领域任务比如把任意HTML整理成干净Markdown、批量处理某一类业务数据时依然会“手忙脚乱”要么输出格式不统一要么反复试错。与其靠堆长提示词硬撑不如把这些任务沉淀成可复用、可组合的技能包。这篇文章适合正在做Agent应用、被提示词工程折磨过、或者想把手头重复性任务模板化的开发者。1. 先搞明白Skills到底是什么东西1.1 核心思路把“操作方法”从上下文里抽出来以前我做AI应用遇到一个复杂任务时第一反应是往系统提示词里堆步骤。比如让模型做“网页内容整理”就在prompt里写“先提取标题、再去除导航、再转Markdown、最后检查链接”……一套下来少说几百字。问题是这个prompt只对这一个任务有效换个任务又要重新写而且这些操作说明会一直占着上下文既费tokens又干扰模型处理主对话。Skills的思路是把“操作方法”从上下文里彻底抽出来以独立目录的形式放在Agent环境中。每个技能是一个包含说明文档和可选脚本的文件夹。模型在对话中会根据技能描述自行判断“现在这个任务需要用到哪个技能”需要时才把对应的SKILL.md加载进上下文。这样一来上下文里平时只有一句技能列表只有真正执行时才读到详细步骤效率高得多。打个比方以前是每来一个新任务就临时给员工做一次培训把培训内容全写在工位上现在是每个岗位准备一份标准作业指导书SOP加一套工具箱平时放在柜子里来了对应任务再拿出来照着做。模型的任务从“记住所有步骤”变成了“判断该翻哪本书”。1.2 一个标准技能包的文件结构我见过不少新手搞不清技能包该装什么其实结构很简单核心就三个部分。路径作用是否必须SKILL.md技能的元数据名称、描述与操作指南模型执行时主要靠它必须scripts/存放可被模型调用的脚本比如解析HTML、压缩图片、格式化JSON通常有assets/存放参考样例、模板、词表、配置文件等辅助资料可选SKILL.md是所有技能的心脏。顶部是YAML格式的frontmatter至少要写name和description。name是技能的唯一标识description决定了模型在什么场景下会想到调用它。正文部分就是给模型看的说明书语言可以比普通文档更口语化因为读它的是模型不是人。scripts目录里的脚本不是传统意义上“给用户用”的工具而是“给模型用”的辅助工具。模型会自己决定要不要运行脚本、怎么传参数然后把脚本结果整合进最终回答。assets目录则更自由放模板、样例数据、错误对照表都行模型可以根据需要翻阅。关键是要让模型知道“什么时候读哪个文件”而不是让它把整个assets读一遍。1.3 它和function calling、插件有什么本质区别很多人第一次接触Skills时都会问这和function calling、AI插件有什么区别function calling的本质是给模型一个函数清单模型负责决定“调用哪个函数、传什么参数”函数内部逻辑完全由外部代码执行模型看不到也不关心函数内部怎么工作。Skills则不同SKILL.md本身就是给模型读的操作说明模型需要理解任务步骤自己决定执行路径脚本只是辅助。换句话说function calling给模型的是“一扇门”Skills给模型的是“一张地图加一本操作手册”。AI插件通常站在用户侧是给用户扩展功能入口的比如浏览器的翻译插件、广告过滤插件。Skills站在模型侧扩展的是模型“会做的事”。用户甚至不需要知道技能是何时被加载的整个过程更像是一个更懂行的助手在后台翻工具书。理解了这层区别就不会在设计时走偏不要指望Skills替你做产品功能它是用来让模型更可靠地完成任务的。2. 设计一个高质量Skill的核心要点2.1 名字与描述决定模型能不能找到它实操时最容易被忽视、也最容易翻车的环节就是description。模型判断是否加载技能主要靠description是否与当前用户请求匹配。描述写得太抽象、太宽泛模型要么不敢用要么乱用。反例description: 处理HTML内容这个描述等于没说模型不知道什么时候该用它可能只在用户明确说“请使用html转换技能”时才勉强触发。正例description: 将HTML内容转换为干净、结构清晰的Markdown。当用户粘贴HTML源码、要求从网页抓取内容整理成Markdown、或需要把富文本/邮件正文转成便于阅读的格式时使用。这个描述给了具体的触发场景模型对“什么时候用”的判断会准很多。我自己实测的经验是描述里不要堆形容词要用“当...时”的句式列出三到五个真实场景再给一个简短示例。如果你已经有一个现成技能库写完描述后最好能自己站在模型角度读一遍——我现在看到这条描述会不会知道该在什么时候用这个技能不知道就重写。2.2 渐进式披露说明书别一次全塞给模型Skill内容组织有一个核心原则叫渐进式披露Progressive Disclosure。意思很直白先让模型读最核心的摘要信息细节按需展开而不是把完整说明书一次性灌进上下文。一份合格的SKILL.md前面是“何时用、输入是什么、输出是什么”中间是“核心操作步骤”最后才是“边界条件、注意事项、示例”。模型加载技能后先读头部就能判断是否继续如果确认使用再往下读详细步骤。这样即使技能内容长达几百行每次真正消耗的上下文也只是它需要的那部分。这个原则的落地方式frontmatter的description负责“第一层判断”正文开头一段一页纸摘要负责“第二层判断”后面分节的详细步骤才是“最终执行手册”。我在实际写技能时会把超过50行的SKILL.md拆成“快速开始”和“详细规范”两节让模型优先走快速开始只有遇到边界情况才翻详细规范。效果立竿见影输出稳定性和响应速度都有提升。2.3 技能粒度一个技能只做一件事设计时最需要克制的是“把流程整合成大技能”。我见过有人把“数据分析”做成一个技能里面塞了取数、清洗、统计、出报告全流程看起来省事用起来噩梦模型加载它时上下文消耗大而且某个环节一旦描述不清就会连环翻车。相比之下把“取数”“清洗”“统计”拆成三个独立技能模型可以按需组合哪个环节出错就单独调整哪个技能维护成本低得多。技能粒度可以参考函数设计的原则单一职责、可组合。判断标准很简单——这个技能的名字能不能用“动词名词”清晰概括如果概括不出来说明它太大了。我自己的习惯是能拆就先拆等用了一段时间发现两个技能总是被一起调用再合并也不迟。3. 从零搭建一个Skills技能包的完整实操3.1 我选的场景HTML转Markdown为了不空谈我拿一个自己实际用过的技能来走一遍完整流程。这个技能叫“html-to-markdown”功能是把HTML内容转换成干净、可读的Markdown。选它是因为场景足够典型网页抓取、富文本复制、邮件正文转存几乎每个做Agent的人都会碰到而且结果可以直接验证好坏。目标定义输入一段HTML文本可能包含完整文档结构也可能只是div套span的碎片输出保留标题层级、列表、链接、代码块、表格的Markdown不做什么不保留内联样式、不处理JavaScript渲染后的动态内容、不猜测无法解析的图片只保留alt属性和src路径明确“不做什么”其实比明确“做什么”更重要。模型在执行时有很强的主观能动性如果不告诉它边界它可能自行脑补“把HTML里的图片下载并保存到本地”这在很多Agent环境里是高风险操作。3.2 搭建技能目录mkdir -p html-to-markdown/scripts mkdir -p html-to-markdown/assets目录建好后放一张最小的示例HTML进assets目录方便模型自测。我从一篇博客复制了一段正文带标题、列表、代码块和链接大约30行。给模型一个真实样例比写一百句“输出要干净”都管用——模型看完样例输出后对“干净”的理解会一下子落到地上。3.3 写SKILL.md--- name: html-to-markdown description: 将HTML内容转换为干净、结构清晰的Markdown格式。当用户粘贴HTML源码、需要从网页抓取内容整理成Markdown、或要求把富文本/邮件正文转为可读格式时使用。 --- # HTML转Markdown技能 将任意HTML转换为Markdown保留标题层级、列表、链接、代码块等结构移除内联样式、script和style标签。 ## 操作步骤 1. 检查输入是否为完整HTML文档。如果包含html/body标签先提取body内部内容。 2. 调用 python3 scripts/convert.py将HTML内容通过标准输入传给脚本。 3. 如果脚本执行成功把输出的Markdown交给用户并用代码块包裹。 4. 如果脚本执行失败不要猜测结果请改用手动转换并提醒用户内容可能不完整。 ## 转换规则 - h1到h6转为对应数量#的Markdown标题 - ul/ol分别转为-或1. 开头的列表 - a标签转为[链接文字](url)格式 - pre/code转为代码块 - 删除script、style标签及其内容 - 删除所有行内style属性 - 图片转![alt](src) ## 边界条件 - 不下载图片只保留属性 - 不执行JavaScript - 不做任何中文翻译或内容改写这份SKILL.md干活的部分其实很短因为核心转换逻辑在脚本里。模型需要做的事情是判断输入、调用脚本、检查结果然后把结果转交给用户。这是很典型的“模型负责判断与沟通、脚本负责确定性操作”的分工。3.4 写辅助脚本为什么需要脚本HTML标签嵌套千奇百怪让模型凭空手写一个可靠的HTML解析器不现实。脚本负责确定性高的解析和转换模型负责边界判断和结果润色两者配合可以实现“模型不擅长的事交给代码代码不擅长的事交给模型”。#!/usr/bin/env python3 import sys from bs4 import BeautifulSoup def convert(html: str) - str: soup BeautifulSoup(html, html.parser) # 去掉 script 和 style for tag in soup([script, style]): tag.decompose() # 去掉行内样式 for tag in soup.find_all(styleTrue): del tag[style] # 先处理链接保证标题里的链接也能保留 for a in soup.find_all(a): text a.get_text(stripTrue) href a.get(href, ) a.replace_with(f[{text}]({href})) # 处理图片 for img in soup.find_all(img): alt img.get(alt, ) src img.get(src, ) img.replace_with(f![{alt}]({src})) # 处理标题h1 - # 标题 for level in range(1, 7): for h in soup.find_all(fh{level}): heading h.get_text(stripTrue) hashes # * level h.replace_with(f{hashes} {heading}\n\n) # 简单处理列表项 for li in soup.find_all(li): text li.get_text(stripTrue) li.replace_with(f- {text}\n) # 剩余正文文本 text soup.get_text(\n) lines [line.strip() for line in text.split(\n) if line.strip()] return \n.join(lines) if __name__ __main__: html sys.stdin.read() print(convert(html))脚本不追求极致健壮覆盖日常80%场景就够用。完整的方案会复杂得多比如要处理嵌套列表、表格、引用块但这个版本已经能让模型完成大部分任务。脚本写得清清爽爽模型才愿意用脚本太长太绕模型反而会因为不知道参数怎么传而选择绕过脚本。3.5 挂载到Agent并测试不同框架挂载方式不同但思路一致把技能目录放进Agent的skills根目录然后在系统提示词或配置里声明有这些技能可用。以我用的工具为例配置里指定skills_root指向存放技能的目录Agent启动时会自动扫描每个目录的SKILL.md把技能列表注入上下文。启动后我做的测试是这样第一轮直接粘贴一段带内联样式的网页正文说“把这段转成Markdown”。模型识别到任务加载技能调用脚本返回了干净的Markdown。标题层级、链接、列表都对了。第二轮给一个只有HTML片段、没有任何说明的输入说“整理一下”。模型依然触发技能因为description里的场景覆盖了“粘贴HTML源码”。这里我验证的其实是description是否写到位。如果这轮模型没有触发问题大概率出在描述上。第三轮给一段明显是JSON的数据说“整理一下”。模型没有触发技能而是直接做了格式化。技能没有误触发说明边界判断正常。三轮测试下来一个技能的基本行为就清楚了。整个过程不需要写任何外部代码全在对话层面验证效率很高。3.6 实测中的两处调优调优主要是两处。第一处是description。最初我写的是“将HTML转换为Markdown”测试时发现模型在“直接粘贴HTML源码”的场景下触发率不高因为模型不确定这个场景属于“转换HTML”还是“用户随便贴一段代码”。改成“当用户粘贴HTML源码、需要从网页抓取内容整理成Markdown...”之后触发率明显提高。这个细节也印证了2.1里说的description写场景不要写抽象能力。第二处是SKILL.md里增加了“失败不要猜测”的指令。有一次模型遇到一个带严重损坏标签的片段脚本解析失败模型竟然自己脑补了一段转换结果看起来像模像样实际上漏了很多内容。我加了对失败行为的约束后模型会如实报告失败而不是假装成功。这对Agent应用至关重要宁可让用户知道“我没处理好”也不能悄悄输出错误结果。4. 常见问题与排查技巧实录4.1 技能一直不被触发怎么办先检查description。最可能的原因是描述没有覆盖用户请求的真实场景或者描述得太抽象。另一个原因是技能太多description互相重叠模型判断时产生了混淆。排查顺序先单独创建一个只含该技能的临时目录看模型是否能触发能触发说明是技能冲突不能触发说明是描述问题。这个二分定位法非常实用能帮你快速把问题缩小到一个方向。4.2 技能触发了但输出格式总是不稳定原因通常在SKILL.md正文没有给出明确的输出格式要求。模型执行任务时有自己的偏好如果你不写清楚“用什么结构、保持什么顺序、禁止出现什么”它就会每次发挥一点点。建议在SKILL.md里给一段真实样例输入和样例输出并在规则里写明“必须输出Markdown、禁止输出HTML标签、禁止添加额外解释”。模型看到样例后输出稳定性会明显改善。4.3 多个技能互相干扰被同时加载技能之间相互干扰的典型表现是明明只想要A技能的效果模型却把B技能的内容也掺了进来。这通常是因为两个技能的description都覆盖了同一批场景或者模型觉得组合使用更像“聪明助手”。处理办法有两个一是给description明确划分边界比如一个写“仅处理HTML转Markdown”另一个写“仅处理纯文本内容提取”让场景不重叠二是如果技能确实在业务上相邻可以考虑合并成一个技能用内部小节区分而不是让模型在多个技能之间自由组合。4.4 安全边界别让模型执行不可信的脚本Skills的威力来自脚本执行风险也来自脚本执行。一个恶意构造的SKILL.md可以诱导模型运行任意命令读取敏感文件。实际部署时我至少做了这几件事技能目录按来源隔离不直接加载陌生人分享的完整技能包尤其是其中的脚本脚本以Python和shell为主运行在沙箱环境中网络访问默认禁止SKILL.md中明确写入“禁止读取用户主目录以外的文件”之类的边界说明让模型自己也有安全意识。养成“信任但验证”的习惯——加载新技能前先读一遍SKILL.md和脚本确认没有危险操作再启用。4.5 技能加载太慢、上下文常被占满如果技能本身没有做渐进式披露模型每次加载都要读完整个说明书速度肯定慢。另一个常见原因是assets里放了过大的模板文件模型会试图把整个模板读进上下文。最直接的解决方案是把大文件路径写进SKILL.md让模型按需读取指定文件和指定片段而不是一次性全量加载。这个优化做完后我那个内容比较重的技能加载速度几乎提升了一倍上下文占用也降下来了。我自己做了小半年技能包之后最大的体会是提示词工程被解构了。以前调一个复杂任务的prompt动不动就上千字改一处就要全局测试现在拆成技能包每个文件都很短单独维护、单独测试、单独上线出现问题时定位也快。如果你还没试过我建议从自己重复次数最多的那个任务开始——比如把日常抓取的网页统一成Markdown或者把日报生成模板化成技能。把一个技能做到顺手你就摸到了这套模式的门道。技能包生态现在还很像早期的浏览器插件市场格式慢慢在走向统一等共享和复用真正跑起来AI应用的生产力大概率还会再上一个台阶。
阅读完成 · 觉得有帮助?
咨询建站