ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

Agent Skills实战:从SKILL.md到技能包,让大模型稳定干活

Agent Skills实战:从SKILL.md到技能包,让大模型稳定干活 最近圈子里讨论最猛的已经不是哪个模型又涨了多少分而是怎么让模型把手头的活真正干完。agent-skills 这个关键词几乎每个 AI Agent 框架的文档里都会出现从大模型厂商官方发布的技能包到开源社区里各种 skill 仓库都在讲同一件事——把任务的 know-how 从人的脑子里搬到模型能读取的地方。说直白一点就是给 Agent 准备一系列可插拔的能力模块每个模块自带一份说明书和配套脚本遇到对应场景就自动加载按部就班把活干完。它解决的是最实际的问题模型每次面对同类任务都像第一次上班没有沉淀、没有肌肉记忆而 Agent Skills 恰好提供了一层可复用、可共享、可迭代的任务经验层。这篇内容适合三类人第一类是正在做 AI 原生应用或者智能体产品的开发者第二类是每天依赖 LLM 处理大量重复文档工作的运营和产品同学第三类是想真正理解 Agent 内部机制的技术爱好者。我会从概念拆到结构再手把手写一个技能包最后把实际踩过的坑全部摊开。前面这些铺垫不重要下面这些可以直接照抄的实操才是重点。1. agent-skills 到底在解决什么问题1.1 从“会聊天”到“会干活”的那道坎我们早就习惯了大模型会聊天但让它稳定干活是另一回事。拿写周报来说你直接发一句“帮我把这周的工作整理成周报”模型大概率能写但格式今天一套明天一套该写风险不写风险该列下周计划也不列甚至会把用户随口说的话当成完成项。这不是模型笨而是任务本身缺少一种“执行规范”。Agent Skills 要补上的正是这条规范。它把一类重复性很高的任务——比如整理周报、清洗数据、生成复盘、批量处理文件——固化成一个“技能包”。技能包里有一份 SKILL.md像操作手册一样告诉模型先做什么后做什么还可以带一个 scripts 目录放一些需要精确计算的辅助脚本如果技能依赖第三方库还能带上依赖清单。Agent 一旦命中这个技能就不靠临场发挥而是靠一套可复用的流程来执行。我经常把这件事类比成开餐厅。你请了一个非常聪明但没干过活的新厨师他做菜全靠临场发挥出品极不稳定Agent Skills 就是给这位厨师一本标准作业手册和一袋提前处理好的半成品食材。厨师仍然有自己的判断但流程、配方、标准都摆在那至少出品是稳定的。对项目而言稳定比聪明重要得多。1.2 和普通 Prompt、Function Call 到底差在哪很多人会问我用 Prompt 不也能把步骤写清楚吗为什么还要搞一个技能包关键区别在于持久性和组合性。普通 Prompt 是一次性的这条消息用完就失效下个会话、下一个 Agent 完全不知道你上次总结过什么方法。Agent Skills 则是沉淀在文件系统里的资产可以被多个 Agent 挂载、被团队共享、被版本管理工具追踪。今天新建一个技能明天同事的 Agent 也能用这才是它跨过普通 Prompt 的地方。再和 Function Call 区分一下。Function Call 暴露的是一个可执行动作比如send_email、query_database模型可以决定“要不要调用”但怎么把一件事按步骤做完Function Call 管不着。Agent Skills 是组合拳它内部可能会调用多个函数、多个命令甚至调用其他技能。如果说 Prompt 是口头的“你看着办”Function 是手里的“一把螺丝刀”那 Agent Skills 就是一套完整的“维修流程手册加工具箱”。说白了Agent Skills 的价值不在“更聪明”而在“更靠谱”。它让大模型从一个什么都懂一点的通才变成一个在特定岗位上有 SOP 的熟练工。2. 标准形态SKILL.md、技能包、调用约定2.1 SKILL.md 是技能的灵魂一个 Agent Skill 可以有脚本、有数据、有依赖但最核心的一定是 SKILL.md。这个文件名几乎成了事实标准它用 Markdown 写既能给人读也能被运行时按约定解析。技能加载器第一步就是读这个文件从文件头的 YAML 元数据里拿技能名、描述、版本号再从正文里拿具体的执行步骤。YAML 元数据里最重要的字段是 description 和 allowed_triggers。description 决定了模型的“选择器”要不要加载这个技能allowed_triggers 则进一步限定触发词避免技能被乱用。比如一个技能名字叫weekly_report_fixer如果 description 写得太宽泛模型可能在用户问“今天天气怎么样”时也把技能加载出来白白浪费上下文长度。这块写不好整个技能就废了一半。正文部分则要像一份好菜谱步骤清晰、顺序明确、有输入输出说明、有异常处理提示。注意正文是给模型看的不是给用户看的所以语气可以用祈使句直接写“读取输入”“按章节分类”“输出以下模板”。模型不是人但它在执行文字指令时比人更忠实也更死板所以每条指令必须无歧义。2.2 技能包目录该放什么文件一个标准技能包的目录结构通常长这样skills/ weekly-report/ SKILL.md scripts/ merge.py classify.py assets/ template.md requirements.txt README.mdSKILL.md 是入口一定放在技能包的根目录scripts 目录放辅助脚本assets 放模板、静态文件requirements.txt 声明 Python 依赖README.md 则是给人看的说明。注意不是每个技能都必须有 scripts有些纯流程型技能可以只靠 SKILL.md 完成比如“给代码写 commit message”这种任务模型自己就能完成不需要脚本。但反过来说凡是涉及精确计算、文件批量处理、正则匹配、数据去重的活尽量写脚本。模型不擅长精确处理但它很擅长“决定什么时候调用脚本”。脚本越简单越好能一行做完的事不要写十行模型不需要看懂脚本的每一行它只关心脚本的输入输出是什么。目录命名也建议统一小写加中划线不要用中文不要带空格否则在跨平台加载时很容易出路径问题。这个细节我后面会再提但提前记住能少踩很多坑。2.3 最小技能示例读起来像说明书跑起来像 API下面是一个最小可用的 SKILL.md 示例我按“整理周报”这个场景写--- name: weekly_report_fixer description: 将零散的工作日志整理为结构化周报适用于周报汇总、项目总结。 version: 1.0.0 allowed_triggers: - 整理周报 - 汇总周报 - 把本周工作整理一下 dependencies: - python3 --- ## 目标 根据用户提供的零散工作记录生成一份包含本周完成、下周计划、风险项的结构化周报。 ## 输入 用户可以直接粘贴工作日志也可以传入日志文件路径。 ## 执行步骤 1. 读取所有输入文本或文件。 2. 将文本按“完成”“推进中”“风险”“下周计划”进行粗分类。 3. 将重复内容合并保留时间最新的表述。 4. 调用 scripts/merge.py 对结果做去重和排序。 5. 按下方模板输出 Markdown。 ## 输出模板 ### 本周完成 - ... ### 下周计划 - ... ### 风险与阻塞 - ... ## 注意 - 如果用户没有提供任何信息先向用户询问不要编造内容。 - 如果某条记录既像完成又像风险优先标记为风险。 - 必须按步骤顺序执行不得跳过去重。这份 SKILL.md 的价值在于它把“整理周报”这个模糊任务变成了五步可见的流水线。模型执行时第一步读输入第二步做分类第三步去重第四步调脚本第五步套模板。每一步都有限度不会自由发挥。你甚至可以在团队里把它当成一段产品需求文档来 review每个环节是否合理、模板是否满足业务要求一眼就能看出来。3. 从 0 到 1 手写一个 Agent Skill我拿“批量整理周报”练手3.1 把工作流拆成模型能照做的步骤第一次写技能时最容易犯的错是“凭感觉把需求写进 SKILL.md”结果模型看着一大堆形容词完全不知道从哪下手。正确做法是先关掉编辑器拿纸笔把人工处理一份周报的流程画出来越机械越好。我来演示一下。假设我每个周五要收一堆群里零散的周报素材有人发“这周把登录页改完了”有人发“明天用户增长会上线但数据延迟还没解决”还有人直接甩一个文件路径。人工整理时我会先扫一眼把“完成”“进行中”“风险”分开然后合并重复项最后按公司模板填进去。这套动作看着简单但里面有分类标准、合并规则、优先级判断不写清楚模型就会自作主张。所以我把它拆成五个确定性的步骤读取所有输入、粗分类、去重、排序、套模板。粗分类这件事我会在 SKILL.md 里给出明确的判断规则比如“带有‘完成’‘上线’‘结束’等词的归为本周完成”“带有‘阻塞’‘延迟’‘风险’‘等待’的归为风险与阻塞”“带有‘下周’‘计划’‘预计’的归为下周计划”。这些规则可能不完美但比让模型空想强得多。拆完之后你会发现真正需要脚本的地方只有去重和排序。分类可以由模型基于语义完成但去重这种精确操作交给模型会不稳定写个 Python 脚本更靠谱。这就是技能设计里的分工模型负责理解脚本负责精确。3.2 约定输入输出比写实现更重要技能能不能被 Agent 顺利调用很大程度取决于输入输出契约。我见过不少技能写着写着就变成一团乱麻模型不知道该怎么把用户的消息变成脚本可读取的形式脚本跑完也不知道该把结果返回给模型还是直接给用户。我的建议是所有脚本都统一走标准输入输出脚本从 stdin 读文本往 stdout 写结果不要擅自改文件不要打印多余日志。这样模型调用脚本时只需要把当前已处理的文本交给 stdin再读回 stdout逻辑链路非常干净。#!/usr/bin/env python3 import sys import re def normalize_lines(lines): items [] seen set() for line in lines: line line.strip() if not line or len(line) 4: continue # 标记分类 category other if re.search(r完成|上线|结束|交付, line): category done elif re.search(r阻塞|延迟|风险|等待|问题, line): category risk elif re.search(r下周|计划|预计|准备, line): category next # 去重 key re.sub(r[\s。:;], , line) if key in seen: continue seen.add(key) items.append(f[{category}] {line}) return items if __name__ __main__: raw sys.stdin.read().splitlines() for item in normalize_lines(raw): print(item)这个脚本只做三件事读取 stdin、过滤空行、按关键词打标并去重。模型完全不需要理解脚本内部逻辑它只需要知道“把原始文本喂进去拿回来的是带分类标签、去重后的文本”。输入输出一旦固定下来技能调试就变成了单纯的管道测试给一段输入看输出是否符合预期不符合就改 SKILL.md 或改脚本。这里我也想强调一个类比技能包就像外卖平台的统一配送单。标注了菜品、数量、地址后厨才能按单出餐。如果输入输出契约不稳定Agent 就是那个接单的人拿到一张乱写的单子再好的厨师也没法出餐。3.3 本地验证中的三次翻车把 SKILL.md 和脚本都写完后别急着接到 Agent 里用先在本地模拟一遍完整调用。我第一次做这个技能时连续翻了三次车正好都是典型问题。第一次模型没有按 SKILL.md 里的步骤走跳过了去重步骤直接把用户发来的几条重复内容原样输出。我查了查发现 SKILL.md 里写的是“将重复内容合并”但没写“必须按步骤执行”模型就把它当成了可选项。修复方式是在步骤列表前加一句硬性规则“执行时必须严格按步骤顺序不得跳过任何一步。”别小看这句话模型对明确禁令的遵守程度远比软性建议高。第二次脚本在本地跑得好好的接到 Agent 里直接报 UnicodeDecodeError。原因是 Windows 下默认编码和 Linux 不一致而我的脚本没显式声明编码。修复很简单在脚本开头把输入编码统一成 UTF-8sys.stdin.reconfigure(encodingutf-8)。跨平台问题在 Agent 技能里特别常见因为你永远不知道运行时在什么系统上执行。第三次更隐蔽技能被加载了但模型居然没调用。我查日志发现allowed_triggers 里写了“汇总周报”用户说的是“把这几天的记录整理成报告”模型认为没有命中触发词所以根本没加载技能。后来我把 description 写得更宽并且把 allowed_triggers 改成了“周报、报告、工作日志、summary”等多个近义词才解决了这个问题。触发词的覆盖度一定要按用户真实说法设计而不是按你的书面说法设计。3.4 先写 SKILL.md 再写脚本顺序别反这个建议听起来简单但实际很容易反着来。很多人一上来就开始写脚本脚本做得很复杂回头再去补 SKILL.md这时你已经把技能当成了一个“程序”而不是一份“任务指南”。Agent Skills 的执行主体仍然是模型脚本只是辅助SKILL.md 才是主脑。正确的顺序是先写 SKILL.md 的正文把它当成一份给新员工的培训材料来写。写完看一遍如果每一步都不需要脚本也能被模型理解那说明拆分到位如果某一步涉及精确计算、文件操作、数据处理再加脚本。脚本也应该后写因为你已经知道了输入输出契约写起来会非常快。我甚至会在 SKILL.md 里先写“调用 scripts/merge.py 完成去重与排序”然后才去写 merge.py。这样保证文档驱动代码而不是代码绑架文档。一个技能如果文档写不清楚脚本再漂亮模型也不会用因为模型只看得懂 SKILL.md。4. 部署与调用让多个 Agent 共享同一套技能4.1 怎么把技能挂到 Agent 运行时技能包写完以后要让它真正跑起来需要把它放到 Agent 运行时能扫到的地方。不同框架的配置方式不一样但大体思路只有两种一种是自动发现一种是手动注册。自动发现模式下运行时有一个技能根目录比如/data/skills它启动时扫描这个目录下所有子目录只要子目录里有合法的 SKILL.md就把它注册为可用技能。你可以通过环境变量指定多个目录类似于追加路径export SKILLS_PATH/data/skills:/data/shared_skills手动注册模式则是在代码里显式加技能agent.add_skill(skills_dir./skills/weekly-report)我个人比较推荐使用共享技能目录。因为团队里一旦沉淀出好的技能应该是所有人都能直接用的而不是每个人各自拷一份。把SKILLS_PATH指向一个共享目录再配合 Git 仓库做版本管理就能形成团队自己的技能库。技能库越大Agent 能解决的问题就越多而每个技能本身的维护成本是可控的。实际部署时我还会给技能目录加一个manifest.json或至少保留目录名和版本号的可读性。虽然 SKILL.md 里有 version 字段但目录名一眼能看到版本是最省事的。比如weekly-report-v2和weekly-report分开存放切换版本时只改配置不动旧文件回滚也方便。4.2 技能之间互相调用怎么办技能不会一直孤立存在。整理周报这个技能很可能需要调用另一个“读取 Excel 并转 Markdown”的技能。这时就出现了技能间依赖。我踩过的坑是在 SKILL.md 里直接写“调用 xxx 技能”但运行时根本不知道该怎么找 xxx。因为很多 Agent 框架默认不允许技能内部随意调用其他技能这会造成加载顺序不确定甚至循环依赖。更稳妥的做法是在一个技能内部通过脚本去调用另一个技能的脚本而不是让模型在 SKILL.md 里“想象”去调用。如果你发现两个技能频繁一起出现那更应该考虑把它们合成一个更大的技能。技能间依赖越少调试成本越低Agent 运行时也越稳定。另外每个技能应该保持自包含。比如scripts/下不要引用技能包之外的文件除非你有绝对路径并在依赖里声明。否则换一台机器、换一个 Agent路径一不一样技能就静默失效了。4.3 版本迭代破坏性变更怎么通知 AgentAgent Skills 本质上是一段会被模型反复读取的文本和脚本所以版本管理格外重要。SKILL.md 里的 version 字段不只是给人看的模型在加载时也可以根据它判断是否和自己记忆中的版本一致但前提是运行时支持版本识别。我的习惯是兼容性变更比如加一个输出字段、优化步骤描述原地更新 version 号破坏性变更比如改了输入格式、改了目录名直接建新目录让旧技能保留一段时间。因为模型对技能名称的关联比我们想象中更敏感目录名都换了它才会把新技能当成一个新对象去学习和调用如果只改内容不改名字某些缓存机制会让模型继续沿用旧记忆导致“改了个寂寞”。还有一个很实用的做法每个技能包维护一个 CHANGELOG.md。不用写得很长几句就够。比如“v1.1增加风险项排序”“v2.0输入从纯文本改为 Markdown 文件”。当你要排查某个技能为什么表现变化时看一眼 CHANGELOG 就能定位问题。技能只有像软件一样被管理才敢真正交给 Agent 长期使用。5. Agent Skills 什么时候该用什么时候别用5.1 和 MCP、Tools、Workflow 到底什么关系很多初学者会把 Agent Skills 和 MCP、Tools、Workflow 混为一谈其实它们解决的是不同维度的问题。形态回答的问题典型场景Tools / Function Call我能调用什么动作发邮件、查天气、调用 APIMCP我如何统一连接外部数据和工具读数据库、访问文件系统、接第三方服务Workflow这件事在确定条件下下一步走哪审批流、状态机、固定业务编排Agent Skills这类任务该用什么方法做完整理周报、写复盘、处理批量文档、做分析Tools 是单个动作MCP 是连接通道Workflow 是确定性流程编排Agent Skills 是一整套“做事方法论”。它们不是替代关系而是配合关系。一个 Agent 可以先用 MCP 读数据库再调用技能里的分析脚本做数据处理最后按技能里的模板输出报告。你在设计系统时不要纠结“用 Skills 还是 MCP”而是问自己这里是缺一个连接器还是缺一套做事流程我习惯看一句话如果任务是“我怎么拿到数据”那是 MCP/工具层的事如果任务是“我拿到数据之后怎么把分析报告做出来”那是 Agent Skills 的事。两者边界很清楚一旦混在一起系统就会变得很难维护。5.2 哪些场景用了反而难受Agent Skills 不是银弹有些场景用它反而添乱。第一种是流程完全确定、没有任何分支的任务。比如每天早上拉取数据、做清洗、写入数仓每一步都固定不需要模型判断。这种直接写 Workflow 就行更快、更便宜也更可控。技能的意义在于灵活性固定流程不需要灵活性硬套技能只会增加一层不确定性。第二种是一次性临时任务。比如你临时让模型帮你改一串文字今天用完就不再复用何必做成技能直接写 Prompt 最划算。技能是需要维护的资产如果只跑一次就没有沉淀的价值。第三种是对权限和安全要求极高的场景。技能一旦被模型调用意味着执行一系列操作你很难像 Workflow 那样精确控制每一个动作的边界。如果业务要求每次执行前都有人工审批我更建议把审批环节放在工具层而不是依赖 Agent Skills。技能适合处理“方法上如何做事”而不是替代“权限上的硬约束”。那什么时候才值得做技能我的判断标准是这个任务一个月至少出现十次以上并且每次都要用相同的经验逻辑。符合这个条件果断做技能不符合就先忍住。6. 实战中踩过的坑排查技巧速查表6.1 模型不按 SKILL.md 走怎么办这是被问得最多的问题。模型不按 SKILL.md 走大多数时候不是模型不听话而是 SKILL.md 写得不够“死”。模型对“建议”“尽量”这类词会当空气但对“必须”“不得”“禁止”敏感得多。所以我的第一条修复策略是在 SKILL.md 开头明确写“执行时必须严格按步骤顺序不得跳过任何一步不得编造输入内容。如果缺少输入先向用户确认。”这些硬性规则会显著降低模型乱来的概率。第二步是压缩步骤数量。如果步骤超过八步模型很容易迷失这时候要么合并同类项要么把一个大技能拆成几个子技能。第三步是给每一步加“完成标志”比如步骤结束时要输出什么模型更容易检查自己有没有漏。还有一种情况是模型根本没加载技能而不是加载后不遵守。判断方法很简单在 SKILL.md 的输出模板里加一个固定标记比如“ ”看看模型输出里有没有这个标记。没有说明技能压根没加载问题出在触发词或运行时配置上。6.2 技能加载失败或静默失效技能“完全没反应”比“执行错”更让人头疼因为没有任何报错。最常见的元凶有三个。第一个是路径问题。技能目录名带空格或中文运行时在某些环境下解析失败但不是所有环境都会报错有时只是静默跳过。强制规范目录命名小写英文、中划线分隔、不空格。第二个是 SKILL.md 格式问题。文件最开头必须是---不能有 BOM不能有空行YAML 字段的冒号后面必须有空格。这些错误很隐蔽因为你去读这个文件时肉眼看着很正常但解析器就是不认。第三个是环境变量覆盖。如果你的SKILLS_PATH配置了多个目录某个目录排在前面却缺少子目录启动器可能只扫第一个有效目录。我排查这类问题时一定会先运行一遍框架自带的技能列表命令确认目标技能是否已经出现在已加载列表里。如果框架没有提供这个命令就写一个最简单的脚本去解析 SKILL.md保证文件本身合法再说。6.3 技能互相污染多个技能同时加载时脚本之间的隔离问题特别容易翻车。我踩过的一个典型坑是技能 A 的 Python 脚本里定义了一个全局配置技能 B 的日志输出模块读取同名全局配置结果日志全部写到技能 A 的目录下B 的所有记录都消失了。这种问题在单独测试技能 A 和 B 时绝不会暴露只有一起加载时才出现。解决方案是给每个脚本入口都封装成函数尽量不用未命名的全局变量脚本内部不要cd改变工作目录而是用绝对路径或由 SKILL.md 传入相对路径如果运行时支持子进程隔离优先让每个技能跑在独立进程里。还有一个更简单的方法每个脚本统一在开头加if __name__ __main__:把主逻辑都关到这个入口后面避免 import 时副作用被触发。技能包的依赖也要隔离。两个技能依赖同一个库的不同版本是脚本环境的噩梦。我现在的做法是每个技能目录自带requirements.txt并固定版本号升级依赖时要重新跑一遍技能测试不能直接latest了事。6.4 我最后想分享的一个小技巧给每个技能补一个最小的测试目录哪怕只有一个smoke_test.sh也好。比如周报技能可以这样写cat tests/case1.md | python scripts/merge.py /tmp/out.md diff /tmp/out.md tests/expected.md这个测试不追求覆盖多少业务场景只保证“输入输出管道没断”。每次修改 SKILL.md 或者脚本后先跑一遍测试再挂到 Agent 上。你可能觉得这有点小题大做但 Agent 项目最怕的就是“改一处、崩三处”。技能一旦沉淀下来维护频率会越来越高没有测试用例护航你根本不敢放它上线。我自己现在养成的习惯是新建技能时先写测试输入和期望输出再写 SKILL.md最后写脚本。测试反而变成了定义需求的锚点。Agent Skills 本质上是在帮模型沉淀经验而经验必须被验证过才算真正的技能。
返回列表