SKILL.md实战:告别提示词模板,构建结构化Agent技能包 📅 发布时间:2026/9/13 14:00:18 👁 浏览次数: SKILL.md 实战别再把 Agent 技能写成提示词模板我这两年看了太多团队的 Agent 项目发现一个很普遍的问题大家一说“给 Agent 加个技能”第一反应就是写一段漂亮的提示词模板塞进 system prompt完事。但真正上线跑几个复杂任务就露馅了——模型根本不按套路执行换个场景就失效维护起来更是灾难。今天我想认真聊一聊 SKILL.md 这套东西它到底和提示词模板差在哪以及怎么用最顺手的姿势把一个技能落地成可复用、可组合的“技能包”。先说清楚这篇文章适合谁看你正在做 Agent 开发或者只是好奇“Agent 技能到底怎么设计”都合适。我会从一个真实项目的角度把 SKILL.md 的结构、编写方法、设计权衡、踩坑记录全部摊开讲你可以直接照着抄作业。1. SKILL.md 真不是换个格式的提示词很多人第一次接触 SKILL.md 的时候看完样例都会冒出同一个念头这不就是 Markdown 里写了一段指令吗和提示词模板有什么区别说实话我一开始也是这么想的但真正跑通几个技能之后才明白它们根本不是一个物种。1.1 提示词模板和技能包的差别在哪先说提示词模板。它的本质是一段“一次性”的文本注入到对话上下文里指导模型完成某个任务。它的优点是简单、灵活但要命的问题也很明显没有结构、没有校验、没有版本概念换模型、换场景就得重新调。而且同一个模板一旦写长模型容易抓不住重点甚至把指令当成普通文本一起“理解”掉。SKILL.md 则是把技能当成一个“文件夹级”的工程单元。除了主文件之外技能目录下可以放脚本、配置、参考文献、示例数据甚至带上一个小型 API 封装。Agent 在运行时不是把整个技能文档丢进对话而是按需读取结构化的元数据和分块内容再决定怎么执行。我习惯用一个类比来解释提示词模板像一张便签写了“记得带伞”SKILL.md 像一个出行工具箱里面有天气预报脚本、备选路线、应急联系方式还有一张写着“如果下雨就打车如果晴天才骑车”的决策卡。便签可以帮你提醒一次工具箱能帮你把整个流程跑起来。1.2 SKILL.md 为什么能站稳脚跟SKILL.md 能成为现在 Agent 技能开发的主流写法靠的不是 Markdown 本身而是它背后的三个设计理念。第一是结构化。技能的前置条件、执行步骤、输出格式、边界约束都被拆成了独立段落Agent 不需要从一大段散文里“猜”意图。你可以把它理解为给模型写了一份带章节和编号的说明书而不是一段含糊的建议。第二是组合性。提示词模板是扁平的写十个模板就是十份孤岛而 SKILL.md 可以声明依赖其他技能可以在自己的步骤里调用别的技能文件甚至可以编写子目录下的 Python 脚本完成模型不擅长的事情。这样技能与技能之间能拼出更大的工作流。第三是版本与复用。技能以目录形式存在天然适合放进 Git 仓库改动可 diff、可 review、可回滚。团队协作的时候A 同学改了技能描述B 同学能清楚看到变更记录。这比在 system prompt 里改两行字然后复制给同事要靠谱一个量级。1.3 什么时候该用技能什么时候不该用不是所有任务都要上升到技能层。我自己有一个判断标准如果任务只需要一两句话就能说清楚而且不涉及多步流程、不依赖外部工具、不需要严格的输出校验那写提示词就够了。比如“帮我把这段文字改成更口语化的风格”一条 prompt 完事没必要建技能目录。但如果任务满足下面任意两条就应该考虑技能化执行步骤超过三步且步骤顺序会影响结果需要引入外部数据、脚本或工具才能完成输出格式有硬性要求必须符合某种 schema同一个任务会在不同对话、不同项目里反复出现需要多人协作维护对一致性和版本有要求。你甚至可以反过来想提示词模板解决的是“本次对话怎么说”SKILL.md 解决的是“这个能力怎么沉淀”。沉淀下来的东西才有复用的价值。这也是我把标题起成“别再把 Agent 技能写成提示词模板”的原因——技能就应该有技能的样子别用便签的思维去做工具箱。2. 动手前先想清楚技能封装的设计思路写 SKILL.md 之前最忌讳的就是直接打开编辑器开始敲字。一个技能的本质是对一类问题的解法建模建模做不好后面写得多精细都是白搭。所以这一章我想先聊设计思路这是我做了几个技能之后才悟出来的。2.1 一个技能应该包含哪几层一个完整的技能我习惯把它切成四层入口层、流程层、工具层、知识层。入口层解决的是“什么时候该用这个技能”。它由 frontmatter 里的 name 和 description 决定模型看到用户的请求后靠这段描述判断自己要不要激活技能。写描述的时候别写太抽象什么“处理数据”“进行分析”都太泛了要写清楚触发场景和典型输入。流程层解决的是“具体怎么做”。这是 SKILL.md 正文的核心包含前置检查、执行步骤、分支处理、质量校验。流程层要写得像菜谱不能有歧义但也别写成死板的代码要给模型根据实际情况微调的空间。工具层解决的是“模型不擅长的事”。比如正则替换、API 调用、文件批量处理这类事情让模型硬写容易出错。技能目录下可以放一个 scripts 文件夹里面是写好的 Python 或 Shell 脚本SKILL.md 里只需要说明“什么情况下运行哪个脚本、传什么参数”。这能大幅提升技能稳定性。知识层解决的是“任务需要哪些背景”。像是行业规范、代码库约定、历史决策记录这些内容可以放在 references 文件夹里。与正文不同的是知识层不要求模型每次全读按需检索就行避免白白占用上下文窗口。2.2 划分技能边界的三条经验技能边界划分错了后面会很难受。我分享三条在实际项目里摸出来的经验。第一一个技能只解决一个问题。我见过有人写一个“全能助手技能”里面涵盖了写代码、写文档、做计划、查资料。看起来很强大用起来全是坑。因为描述越长模型越难判断当前任务该不该触发这个技能触发之后该执行哪些步骤。不如拆成独立的 skill单技能单职责。第二技能粒度要匹配调用频率。如果一个技能每天要被调用几十次可以拆得细一点让响应路径更短如果一个技能很重、需要大量工具配合那么粒度大一点没关系但内部模块要清晰。第三允许技能之间互相引用。技能不是孤岛。你在 SKILL.md 里可以直接写明“先调用 xxx 技能确认格式再继续本流程”这种组合是提示词模板完全做不到的。把常见组合固化下来Agent 的执行路径会越来越稳。2.3 技能和工具、工作流、记忆的关系很多刚开始接触 Agent 开发的朋友会对“技能”“工具”“工作流”“记忆”这几个概念之间的关系感到混乱。这里我给出自己的理解框架。工具是最小颗粒度的原子能力比如“发送 HTTP 请求”“执行 shell 命令”“读写文件”。技能是围绕某个场景组织的工具加指令组合比如“部署检查技能”内部会用到执行命令、读取配置、比对版本号这三个工具。工作流是更高层的东西它描述的是多个技能如何按顺序被编排比如“发布流程”就是“代码检查技能 - 构建技能 - 部署技能 - 健康检查技能”。记忆则是跨对话的持久化信息技能可以在运行时读写记忆但本身不负责长期存储。把这几层想清楚之后你再回头设计技能就会发现思路清晰很多先想着场景需要哪些原子能力再设计技能内部怎么组装最后考虑与其他技能的编排关系。不要一上来就把技能当成一个什么都装的大杂烩。3. 手把手写一份 SKILL.md 的硬核手艺设计思路定了就得落到文件上。这部分我讲具体写法从目录结构到 frontmatter再到正文的结构安排全部用实际可抄的例子讲。3.1 标准目录结构与文件职责一个标准的技能目录长这样my-skill/ ├── SKILL.md # 技能的主文件唯一必须存在的文件 ├── scripts/ # 辅助脚本目录可选 │ ├── preprocess.py │ └── validate_result.py ├── references/ # 参考资料目录可选 │ ├── style_guide.md │ └── examples.md └── assets/ # 静态资源比如模板文件、图片可选 └── output_template.mdSKILL.md 是入口和指挥中心。scripts 放模型不擅长、不适合靠“思考”完成的任务references 放背景知识assets 放固定的模板资源。这个结构不是强制的但它是目前我看到的所有 Agent 框架里最通用的一种按这个来技能迁移到别的框架也方便。需要特别提醒的是技能目录里的所有文件都应该自带说明。所有文件都应该有。每个辅助脚本至少要有一行注释告诉模型这个脚本是干嘛的、输入输出是什么这样才能确保 Agent 在运行时会去读取并正确使用它。3.2 如何写好 frontmatter名称、描述、元数据SKILL.md 的头部是 YAML 格式的 frontmatter这部分决定了技能能不能被 Agent 正确识别和触发。--- name: log-triage description: 用于对服务器应用日志进行初步排查与分类。当用户提供一段原始日志、日志文件路径或描述线上异常现象时使用本技能确定问题类型、定位关键错误线索、给出可执行的排查建议。触发关键词包括日志报错、线上异常、error log、triage、排障。 ---写 description 的时候可以从三个角度来约束模型场景触发条件用户在什么情况下该用输入形式通常接受哪些类型的输入任务目标技能最终想达到的产出。描述要写得具体但不用把步骤写在 description 里。description 只负责“什么时候调用”步骤是正文的事情。大多数框架还支持在 frontmatter 里写其他字段比如 version、author、dependencies这些按你自己的项目管理习惯来就行。但最少最少name 和 description 一定要有否则技能等于没穿衣服。3.3 正文的写法目标、步骤、约束、边界frontmatter 下面是正文。正文不要写成“你是一个什么都懂的专家”式的废话要直接、结构化、可执行。我常用的骨架是这样的# 日志初步排查技能 ## 目标 在收到异常日志或线上报错时快速定位错误类型输出格式化的排查摘要。 ## 前置条件 - 确认已取得日志内容或日志文件路径可访问 - 如果日志内容超过 2000 行优先使用 scripts/truncate_log.py 压缩到关键片段 ## 执行步骤 1. 提取日志中的时间戳、日志级别、进程 ID、关键字错误摘要。 2. 对错误进行初步分类网络错误、超时、语法错误、依赖缺失、资源耗尽、业务异常。 3. 查找错误首次出现的上下文向前取 20 行、向后取 10 行。 4. 调用 scripts/check_error_code.py 核对常见错误码含义脚本返回可读解释。 5. 结合上下文信息输出包含「问题分类、可疑原因、影响范围、建议动作、需要补充的信息」的排查摘要。 ## 输出格式 \\\markdown ### 问题分类 一句话 ### 可疑原因 条目列表 ### 影响范围 描述 ### 建议动作 有序列表 ### 待确认信息 如果信息不足列出还需要哪些日志或指标 \\\ ## 约束与边界 - 本技能只负责“定位和分析”不执行任何修改或重启类操作。 - 如果日志中未发现明确错误线索应直接输出“未定位到明确错误”不要编造原因。 - 单次分析只针对一个错误现象多报错场景需要拆分为多次调用。看明白了吗步骤里全是动词和决策点没有“请仔细分析”“从多个角度思考”这种正确的废话。约束与边界是很多人忽略的部分但它特别重要。明确写清楚“不做什么”能防止模型在任务失败时强行编造结果也能防止它把技能行为扩展到不可控的范围。3.4 把技能参数化用变量降低重复写 SKILL.md 的时候最怕的就是把具体案例写死。比如你在步骤里写“检查 /var/log/app.log”换个项目这个技能就废了。正确的做法是把这类变量抽象成参数在正文里给出占位符和说明。我现在习惯在 SKILL.md 开始的部分加一个“输入参数”区块## 输入参数 | 参数名 | 必填 | 说明 | | --- | --- | --- | | log_source | 是 | 日志文件路径、日志文本粘贴或日志流地址三者之一 | | error_keyword | 否 | 用户已知的错误关键字用于加速定位 | | since | 否 | 只分析该时间点之后的日志格式为 YYYY-MM-DD HH:MM:SS |然后步骤里只写参数名不写具体值。这样技能就具备泛化能力了同一个技能既能帮 A 项目查日志也能帮 B 项目查日志不需要复制两份。参数化还带来一个额外的好处模型在调用技能之前会先尝试从用户的输入里“抽出”这些参数。这种抽取动作本身就是一个结构化过程相当于强制模型把自然语言请求映射到固定 schema 上比直接丢一段文本让它自由发挥要稳定得多。所有需要根据实际情况变化的内容包括文件路径、城市名称、日期范围、关键词列表、目标平台都请参数化处理。我发现这是新手写 SKILL.md 和熟手写 SKILL.md 的明显分界线。4. 一个完整实战做一个“日志初步排查技能”光说不练假把式。这一章我从零开始带你完整走一遍日志排查技能的制作过程。这个技能既实用又能完美展示 SKILL.md 的设计要点。4.1 技能需求与前置准备背景是这样的我的一个监控告警群里每天会产生大量日志报错人工排查效率很低。我希望做一个技能让 Agent 拿到日志后自动完成错误分类、上下文提取、常见错误码解释、排查建议输出。第一步确定技能边界它只做分析和建议不做修复。第二步确定需要哪些辅助脚本一个日志截断脚本用于处理超长日志一个错误码解析脚本用于把常见错误码翻译成可读的原因与建议。第三步确定输出格式方便后续接入告警通知系统。4.2 目录结构搭建与 SKILL.md 完整示例我创建了这样一个目录log-triage/ ├── SKILL.md ├── scripts/ │ ├── truncate_log.py │ └── check_error_code.py └── references/ └── common_cases.md完整版的 SKILL.md 如下可以直接参考--- name: log-triage description: 对服务器应用日志进行初步排查与分类。当用户提供日志文本、日志文件路径或描述线上异常现象时使用本技能定位错误类型、提取关键线索并给出排查建议。触发关键词日志报错、线上异常、error log、triage、排障、log analysis。 --- # 日志初步排查技能 ## 目标 在收到异常日志或线上报错时快速定位错误类型输出格式化的排查摘要。 ## 输入参数 | 参数名 | 必填 | 说明 | | --- | --- | --- | | log_source | 是 | 日志文件路径、日志文本粘贴或日志流地址三者之一 | | error_keyword | 否 | 用户已知的错误关键字 | | since | 否 | 只分析该时间点之后的日志格式为 YYYY-MM-DD HH:MM:SS | ## 前置条件 - 如果 log_source 是文件路径确认路径可读。 - 如果日志内容超过 2000 行先运行 scripts/truncate_log.py 进行截断避免上下文过长。 ## 执行步骤 1. 读取日志按时间排序提取时间戳、日志级别、进程 ID。 2. 结合 error_keyword如果有定位第一条错误日志。 3. 向前取 20 行、向后取 10 行作为错误上下文。 4. 对错误进行分类网络错误、超时、语法错误、依赖缺失、资源耗尽、业务异常、未知类型。 5. 如果错误消息中出现常见错误码如 ENOENT、ECONNREFUSED、ETIMEDOUT调用 scripts/check_error_code.py 获取解释。 6. 综合以上信息输出排查摘要。 ## 输出格式 \\\markdown ### 问题分类 一句话 ### 可疑原因 条目列表 ### 影响范围 描述 ### 建议动作 有序列表 ### 待确认信息 如果信息不足列出还需要哪些日志或指标 \\\ ## 约束与边界 - 只负责定位和分析不执行任何修改、删除、重启类操作。 - 如果未发现明确错误线索必须输出“未定位到明确错误”不得编造原因。 - 一次只分析一个错误现象多报错场景拆分为多次调用。 - 如果日志包含的是纯业务数据而不是报错应拒绝执行并说明原因。 ## 辅助脚本说明 - scripts/truncate_log.py输入为日志文件路径与最大行数输出为截断后的日志摘要。脚本会保留错误级别最高的若干行并在开头标记截断位置。 - scripts/check_error_code.py输入为错误码字符串输出为该错误码的通用含义、常见触发场景、排查建议。这个 SKILL.md 写成后整个技能的“大脑”就就位了。4.3 辅助脚本怎么写才能让 Agent 真正愿意用SKILL.md 里提到了两个辅助脚本如果它们不好用模型大概率会绕过它们直接硬猜。所以脚本要写得足够傻瓜。truncate_log.py 的核心逻辑很简单读取指定日志文件统计行数如果超限通过“判断日志级别”的方式挑选出 ERROR/WARN 行保留这些行及其前后各几行上下文最后在输出文件头部加一段说明告诉 Agent 这是截断后的内容。check_error_code.py 也简单内置一个小型错误码字典把常见的 ENOENT、EACCES、ECONNREFUSED、ETIMEDOUT、EADDRINUSE 之类的错误码映射到含义与建议。脚本接收参数比如--code ECONNREFUSED输出一段结构化的解释。写辅助脚本的时候有一条铁律要记住每个脚本都要做到“接受命令行参数输入输出标准文本结果”不要做成交互式不要依赖 GUI不要有额外的网络请求。因为 Agent 是通过命令行方式调用脚本的任何交互式设计都会卡住执行流程。4.4 在 Agent 里加载并验证调优迭代技能文件写好之后把它放到 Agent 框架默认的技能扫描目录下然后在对话里测试。我第一次测试时输入的是项目真实报错日志中的一段。Agent 按步骤执行了但输出有一个问题问题分类写了“综合类异常”这个分类太模糊了对定位问题没有帮助。排查原因后发现是我在步骤 4 里给了“未知类型”这个选项模型偷懒的时候就会选它。于是我把步骤 4 改成了强制规则“如果分类为未知类型必须明确列出你观察到的关键日志特征不允许只写未知类型”。这样一改模型就会被迫思考输出质量明显提升。这类调优会反复多次。我的经验是每一个细节都可以通过迭代磨到更稳。就像打磨一件工具你觉得它已经能用了但用个三五天总会发现新的边角需要修。技能开发本身就是一个持续迭代的过程。5. 常见问题与排查技巧实录任何技能开发都会遇到模型不听话、技能失效、上下文爆炸这一类问题。这一章我把自己踩过最多的几个坑集中写成速查希望能帮你少走弯路。5.1 模型根本不读这个技能怎么办很多人会疑惑我的 SKILL.md 写得清清楚楚为什么模型视而不见这个问题通常由三个原因造成。第一是 description 写得不够清楚。模型靠 description 决定是否调用技能如果你的描述里只有“日志分析”三个字那模型确实很难判断何时该触发它。解决办法是重写 description明确触发场景、输入形式和任务目标。第二是技能被其他指令压住了。如果你的 system prompt 里写了“你是一个日志专家直接根据经验回答问题”那模型就会觉得自己不需要技能也能完成自然不读 SKILL.md。处理办法是检查 prompt 里有没有和技能职责重叠的内容把这类角色设定清掉或弱化。第三是框架配置问题。有些框架默认关闭技能自动调用或者需要显式指定技能列表。遇到这种情况去翻文档确认技能的开关和加载路径这一步是纯工程问题不用怀疑自己的技能写法。5.2 技能之间互相打架优先级怎么定技能多了以后会出现多个技能描述相似、模型不知道该调哪个的情况。比如你有一个“日志分析”技能又有一个“错误排查”技能描述都覆盖了“线上报错”场景模型就懵了。我的解决思路是错开触发域在 description 里强调各自的独特输入类型。比如“日志分析”技能只处理日志文件路径和文本“错误排查”技能只处理监控告警通知和指标数据。用户需求不同触发域就分开了。如果两个技能确实必须覆盖同一场景可以在 frontmatter 里增加一个自定义字段比如priority: high并且在 SKILL.md 正文里直接写清楚“如果用户需求同时适用于 A 技能和 B 技能优先使用本技能”。大多数框架都会尊重这种显式声明。5.3 上下文被技能塞爆怎么瘦身SKILL.md 写得越全阅读理解成本越高。尤其当技能里塞了大量参考资料模型每次都会试图读取全部内容上下文一会就满了。我现在的做法是“主文件瘦身参考资料按需读取”。SKILL.md 里只保留最核心的执行步骤和约束参考资料全部放 references 目录。然后在主文件的相应步骤里写“如果需要判断错误类型的常见模式请先阅读 references/common_cases.md 后再输出结论”。这样模型只有在需要时才去读知识层上下文压力会小很多。另外scripts 目录里的脚本说明也要精简。你不需要在 SKILL.md 里贴脚本全文只要写清楚用途和参数。模型要跑脚本的时候自然会用命令行去读脚本文件的头部注释。5.4 技能退化成“会念经的提示词”最后一个问题很隐蔽技能文件写得像一篇优美的散文模型读了记了但执行的时候又回到自由发挥的老路。原因通常是内容全是“别忘了”“务必”“一定要”这种原则性指令缺少可校验的下一步动作。真正的技能要能通则过要能失败则停。具体来说每个步骤都要有明确的产出物和检查点。比如“提取时间戳并检查格式是否为 YYYY-MM-DD如果不符合则停止并提示用户格式问题”。这种硬性校验可以强行约束模型的输出路径。没有校验点的步骤就是变相的提示词内容删掉也不影响。我自己在写技能时会把步骤里所有形容词都替换成动词和名词的组合。比如不说“全面分析”说“提取三个关键线索”不说“合理判断”说“按规则匹配并输出匹配结果”。语言的精确性会直接影响模型执行时的精确度。5.5 调试与日志技巧技能开发本身就是个工程活调试方法也很重要。我常用的手法是在步骤里临时加一行“思考过程输出”的强制指令让模型把自己的判断依据写出来。虽然这类输出在生产环境的技能里要删掉但在调试阶段非常有用能帮你看到模型为什么走偏。另一个好用的技巧是准备一套“黄金测试集”。把典型输入和预期输出整理成一个目录每次改动技能之后跑一遍对比输出差异。没有测试集的技能迭代等于闭着眼睛开车看着能走一上高速就翻。还有个容易被忽略的点技能文件的修改不会每次都热加载。如果你改了 SKILL.md 但测试时发现模型行为没变先确认框架是不是还在用旧的技能缓存。很多问题不是你的写法有问题而是根本没加载上新版本。我个人真心体会到Agent 技能开发里最值钱的能力不是你多会写提示词而是你多会把一个实际场景抽象成结构化的、可校验的、可组合的执行单元。SKILL.md 恰好给了我们一个统一的载体把这套思路落地下来。以后再看到有人把 Agent 技能写得跟作文似的我第一反应就是送他一句去看看 SKILL.md 吧别再把技能写成提示词模板了。