
我一直不太理解为什么很多人到现在还愿意把 Agent 的技能写成一大段提示词模板。我在不少 Agent 项目里见过类似场景prompt 仓库里躺着几百条精心打磨的“万能提示词”可一旦让 Agent 连续执行多步任务不是参数传错就是做到一半忘了自己要干什么最后输出一个格式七零八落的结果。问题不在模型不够聪明而在于你给它的不是“技能”只是一段“话术”。SKILL.md 解决的就是这个问题。它不靠长篇大论的文字“感化”模型而是用一套结构化的文件格式把技能拆成元信息、参数声明、执行步骤、工具绑定、错误处理让 Agent 能真正“照着执行”。本文不是概念科普我会直接带你从零写一个可用的 SKILL.md讲清楚每一项怎么写、为什么这么写、踩过哪些坑。适合正在做 Agent 开发、或想把日常 Prompt 沉淀成可复用技能的从业者。1. 为什么提示词模板撑不起 Agent 技能先说清楚一个容易被忽略的事实提示词模板和 SKILL.md 根本不是一个维度的东西。很多人觉得“技能不就是把提示词写得更长更细吗”这恰恰是问题所在。1.1 提示词模板的本质是“文本”不是“程序”提示词模板最终被消费的方式只有一个——变成一段文本喂给大模型然后模型基于概率生成下一段文本。一次输入对应一次输出模型没有“记住步骤”的压力也不需要对结果负责。它在本质上更像一份给实习生看的“工作说明”你写得再详细实习生看没看懂、做没做到全靠他自己的理解。这在单轮问答场景里效率很高因为模型只需要把“理解指令 生成回答”这件事做对。但 Agent 场景完全不同。Agent 需要连续执行多轮推理期间要选择工具、读取结果、根据中间状态修正下一步最后还要对输出做校验。一旦任务中间状态发生变化模板里写死的流程就失效了。还有一个更实际的问题提示词模板无法被程序解析。你无法让代码判断“这段模板当前执行到哪一步了”“某个必填参数是否已被提供”。所有状态都隐式地藏在一大段自然语言里出了问题只能靠人肉排查。你在模板里写的每一个字都参与了模型的概率计算少写一个词模型可能就转变了行为方式——这种不可控性在工程上是非常难受的。1.2 Agent 技能的真正需求可解析、可编排、可反馈做 Agent 开发的人应该都有同感一个技能要真正被 Agent 使用至少要满足四个条件。第一可被发现。框架要从技能库里匹配到当前任务适合哪个技能靠的是技能描述和任务描述的语义对齐而不是自然语言里模糊的关键词。第二可被解析。技能里的参数、步骤、工具调用要被后端代码拆出来做校验和调度。模型不能凭感觉传参数框架也要在参数缺失时直接报错而不是继续乱跑。第三可被编排。一个复杂任务往往会按顺序或条件调用多个技能。技能之间要有清晰的输入输出边界不然无法被组合。第四可被反馈。执行过程中如果出错了技能内部要有错误处理分支。要么重试要么降级要么明确终止并告诉用户原因。提示词模板给不了这种反馈回路。你可以把提示词模板理解成一张菜谱的文字说明而 Agent 技能需要的是一套“和面 → 醒发 → 烘烤 → 测温 → 判断是否出炉”的可执行流程。前者靠人/模型理解后者靠系统执行。1.3 两者差异到底在哪里我整理了一份对比表方便你快速理解。对比维度提示词模板SKILL.md载体纯文本/变量占位符Markdown 文件 YAML 元信息消费方式直接拼进 prompt 给模型读被框架解析后约束模型行为与工具调用参数处理靠占位符替换缺失难发现声明式参数可校验、可补全执行状态无状态单轮对话有步骤、有分支、可记录进度错误处理无内置机制可写失败分支、重试逻辑复用性复制粘贴改名文件级复用天然支持版本管理调试方式改 prompt 看输出定位到具体步骤/参数逐项验证一句话总结提示词模板是写给模型看的“说明书”SKILL.md 是写给 Agent 编排层执行的“函数定义”。前者提升单次回答质量后者决定多步任务能否闭环。这也是为什么我把标题起成“别再把 Agent 技能写成提示词模板”——不是模板没有价值而是它们根本承担不了技能的责任。2. SKILL.md 的底层设计与文件结构既然 SKILL.md 是一种文件格式我们就得把它的结构聊透。当前主流 Agent 框架对 SKILL.md 的处理方式大同小异基本都是“目录 文件”的形态核心就一个文件SKILL.md里面用 YAML 和 Markdown 混排。2.1 前置元信息让 Agent 认识这个技能SKILL.md 的开头通常是一段 YAML frontmatter它承担“元信息声明”的职责。这段信息不会直接给用户看但模型和框架都会读它用来完成技能匹配、参数校验、版本管理。一个典型的元信息长得像这样--- name: analyze_industry_report description: 面向指定行业生成数据分析报告适用于用户提供行业关键词、时间范围、数据指标的场合。当任务需要提取趋势、对比竞品、生成结论时使用。 version: 1.2.0 author: ops_team parameters: industry: type: string required: true description: 目标行业名称如新能源汽车。 time_range: type: string required: false description: 数据统计时间范围格式为 YYYY-MM-DD 至 YYYY-MM-DD缺省默认最近一年。 metrics: type: array required: false description: 需要重点分析的指标列表缺省自动选择行业核心指标。 dependencies: - scripts/fetch_industry_data.py - config/industry_tags.yaml ---这里面有个字段我要特别强调description。它对模型极其重要因为 Agent 框架在判断“当前任务要不要调用这个技能”时主要靠语义匹配来理解技能描述和任务意图。描述写得太泛比如“用于数据分析”模型看到任何一个分析任务都可能调用它结果就是技能互抢写得太窄又可能永远等不到被调用的那天。实操心得是描述要写成“弹性触发”风格。前半句说明技能能干什么后半句列出适合触发的任务特征。这样模型在意图匹配时能很快命中且不容易误触。参数声明同样关键。required字段决定框架是否强制要求模型先收集参数再执行description则给模型提供了填参依据。我见过一个技能声明了time_range但没写格式模型直接传了个“最近三个月”进去下游脚本解析日期时直接崩了。参数描述必须写清楚格式和枚举值这是很多人会忽略的细节。2.2 执行步骤与工具描述把“怎么做”变成可执行路径YAML 下面就是正文一整段 Markdown。这里的结构与普通文档最大的区别在于每个标题都是被框架当作“状态节点”消费的而不是给人类阅读的排版装饰。通常我会这样组织主流程# 行业数据分析报告 ## 1. 数据拉取 使用 scripts/fetch_industry_data.py 拉取指定行业、时间范围的基础数据。脚本输出结果为 JSON包含总量、环比、同比、头部企业份额等字段。 ## 2. 趋势与竞品分析 基于第 1 步产出的 JSON 数据生成趋势摘要与竞品对比结论。重点判断行业处于上升期、平台期还是下降期。 ## 3. 报告生成 输出 Markdown 格式报告必须包含行业概况、数据趋势、竞品对比、风险提示、结论建议五个章节。报告开头要有执行摘要限制在 200 字以内。 ## 4. 自检 检查报告中所有数字是否与第 1 步 JSON 数据一致缺失指标需要在报告中显式标注“暂无数据”不得编造。有人会问这不就是把步骤写清楚吗和提示词模板里的“请你一步步分析”有什么区别区别在于两点。第一这里的步骤被框架解析后会变成可观测的执行节点。框架可以在每个步骤前后插入日志、校验产出、保存中间状态而提示词模板里的“一步一步”只是模型自己脑补的过程没有外部校验点。第二步骤里可以明确绑定工具。比如“使用 scripts/fetch_industry_data.py 拉取数据”框架会理解这个技能依赖一个外部脚本进而在执行前检查脚本是否存在、可执行权限是否正确。如果脚本缺失框架可以直接报错而不是让模型编一个“虚假的分析结果”。不过这里有一个度的问题步骤句子里既要写清楚做什么又不能写成代码。模型不是执行器工具调用还是要交给框架层。SKILL.md 里的步骤本质上是约束模型“什么时候该调用哪个工具、拿到结果后该做什么判断”而不是教模型用自然语言模拟工具行为。2.3 边界、依赖与错误处理技能不能只写“怎么做”一个工程上成熟的技能文件还必须有边界声明和错误处理。这恰恰是提示词模板完全做不到、也容易被新手 SKILL.md 遗漏的部分。边界声明告诉模型“什么时候不该用这个技能”。比如行业数据分析报告技能就应该写明“当用户未明确指定行业或仅询问通用性问题时不要调用本技能”。有些任务模型会为了凑过程强行套技能边界声明能拦住这种行为。错误处理则要覆盖常见的异常走向。举几个我在实际文件中写过的情况数据拉取结果为空要求模型输出“暂无足够数据支持分析”并停止后续步骤不生成虚假结论。某指标缺失不得编造数值在报告中显式标注“暂无数据”。第三方接口超时可重试一次重试失败后告知用户稍后再试。参数与技能不匹配框架应直接拒绝执行并返回参数错误提示而不是让模型强行“理解”后继续。你可能会觉得这些都是常识但实际跑起来就会发现不写清错误分支的技能等于把异常处理的脑补任务完全交给了模型。模型在告警压力下宁愿编一个看起来合理的解释也不愿承认“我做不了”。所以错误处理不是可选项是技能的血压线。3. 实战从零写一个可用的 SKILL.md 技能为了让你能直接抄作业我把上面的设计理念落到一个完整例子里。我们做一个“行业数据分析报告”技能这是很典型的需求涉及参数输入、外部脚本、多步骤判断、结构化输出能覆盖 SKILL.md 的大部分知识点。3.1 选定场景与目标场景需求用户可能抛出类似“帮我看看新能源汽车行业最近一年的趋势”“对比一下新能源汽车和锂电池行业的增速”这样的请求。目标输出是一份 Markdown 分析报告包含行业概况、数据趋势、竞品对比、风险提示、结论建议。我选这个场景的原因很简单它的步骤有依赖关系先有数据才能分析有分支判断行业上升/下降/平台有外部工具数据脚本这些特性让 SKILL.md 的优势能完整发挥出来。如果只是做个“写周报”技能那本质上和套一个提示词模板真没多大区别体现不出本方案的价值。3.2 完整 SKILL.md 文件拆解先把完整文件摆出来再逐段解释。--- name: analyze_industry_report description: 面向指定行业生成数据分析报告。当用户提到行业名称、时间范围、市场趋势、竞品对比、行业增速等关键词且需要结构化分析结论时使用。若用户仅做简单问答不涉及多维度数据汇总则不使用。 version: 1.2.0 author: ops_team parameters: industry: type: string required: true description: 目标行业名称建议使用规范中文名称如新能源汽车锂电池。 time_range: type: string required: false description: 数据统计时间范围格式为 YYYY-MM-DD 至 YYYY-MM-DD。缺省默认最近一年。 metrics: type: array required: false description: 需要重点分析的指标列表。缺省时由脚本自动选择核心指标。 dependencies: - scripts/fetch_industry_data.py - config/industry_tags.yaml --- # 行业数据分析报告 本技能用于生成指定行业的数据分析报告。适用场景为行业研究、投资分析、竞品跟踪。报告要求数据可溯源、结论有依据、不编造数值。 ## 1. 数据拉取 1. 解析参数确认 industry 和 time_range。若 time_range 缺失默认取最近一年。 2. 调用脚本 scripts/fetch_industry_data.py传参格式为 bash python3 scripts/fetch_industry_data.py --industry 新能源汽车 --time-range 2024-01-01 至 2024-12-31 --metrics 总量 增速 份额脚本输出 JSON包含字段total_market_size、yoy_growth、mom_growth、top_companies、industry_stage。将结果暂存为下一步的输入。2. 趋势与竞品分析基于第 1 步的 JSON 数据完成如下判断根据 yoy_growth 数值判断行业阶段大于 15% 判断为上升期介于 0% 到 15% 判断为平台期小于 0% 判断为下降期。对比 top_companies 中头部企业的份额变化找出份额增长最快的企业并说明可能原因。如果 mom_growth 连续三个月为负需要在报告中增加“短期回落风险”标注。3. 报告生成输出 Markdown 格式报告必须包含以下章节顺序不可调整执行摘要200 字以内概括行业阶段、核心结论。行业概况市场规模、增速、所处生命周期。数据趋势时间范围内的总量变化与增速变化。竞品对比头部企业份额及变化。风险提示至少写一条未来可能影响行业的外部风险因素。结论建议基于分析的下一步建议。报告内所有数字必须与第 1 步 JSON 数据一致。凡是没有数据支撑的结论一律不得出现在报告中。4. 自检与输出对照 JSON 数据核对报告中所有数值如有偏差需重新生成。若某指标无数据在对应位置标注“暂无数据”不得编造。确认报告章节完整、顺序正确后将最终报告输出给用户。如果数据拉取结果为空输出“当前时间范围内暂无足够数据支持分析”并终止流程。请你仔细看几个细节。 description 字段与正文的第一句话是配合设计的。模型读取时先用 description 判定要不要调用技能进入技能后正文第一句话交代边界提示模型“这里不是自由发挥的场合”。这两者缺一不可description 管入口正文第一句管执行基调。 第 1 步里我写了“缺省默认最近一年”这与 YAML 参数声明里的描述保持了一致。很多技能文件的问题就在这里YAML 里说 time_range 缺省可选正文步骤里却没有给出“缺省后该怎么办”模型就卡在两个约束中间不知道该听谁的。技能文件内部必须自洽。 第 2 步里的阈值判断我用很直白的条件句写了出来。这是刻意为之。模型在生成分析结论时需要有可验证的判定依据而不是让它“根据增速情况进行大致判断”。你给了阈值模型的输出稳定性会高非常多。 第 4 步的自检段落同样重要。它把“检查”这件事做成了流程中的最后一个节点而不是留给模型自觉。凡是数据型技能我都强烈建议加上这一步——它可以极大减少报告里数字对不上的问题。 ### 3.3 挂载与触发如何让 Agent 真正用起来 文件写完之后得挂到 Agent 框架上才会生效。虽然不同框架的具体操作有差异但大体思路是一致的。 第一步把技能放到固定的技能目录比如 skills/analyze_industry_report/SKILL.md。框架一般会扫描该目录下的所有子目录识别每个子目录里的 SKILL.md 并注册为可调用技能。 第二步确认 dependencies 字段声明的脚本或配置文件都存在。比如我上面的示例里依赖了 scripts/fetch_industry_data.py那就需要确保该脚本在技能文件对应的相对路径下可以找到。依赖缺失时很多框架会直接跳过注册导致技能静默失效。 第三步重启 Agent 服务或者执行框架的“重新加载技能”命令。这一步经常被忘掉你满腔热情改完了文件结果 Agent 用的还是旧版本容易误判为“技能没用”。 验证阶段建议用一个“最小召回”测试直接抛给 Agent 一句任务请求比如“帮我分析一下新能源汽车行业最近一年的趋势”。然后重点观察几点 - 模型有没有选择到这个技能如果没选多半是 description 写得不够贴合。 - 传入的参数是否正确industry 有没有被识别、time_range 有没有填对格式 - 执行到第几步中断了中断时的报错信息是什么 还有一个我常用的调试技巧让模型在调用技能后、执行步骤前先用一句话解释它为什么认为这个技能适合当前任务。这会逼模型把技能选择依据说出来你一眼就能看出它是“真懂”还是“碰巧命中”。虽然这会增加一次模型调用成本但在调试期非常值得。 ## 4. 调试与排查技能写好了却老是不生效怎么办 写 SKILL.md 的过程本质上是一个持续调试的过程。我把自己在实际项目中遇到过的高频问题整理成了一张速查表希望对你有直接用。 ### 4.1 高频问题速查表 | 常见问题 | 可能原因 | 解决建议 | | --- | --- | --- | | Agent 始终不调用该技能 | description 与真实任务场景匹配度不够 | 把描述改成“任务特征 触发词”结构避免过于抽象 | | 调用了技能但参数总传错 | 参数 description 不够明确或缺少格式声明 | 在 YAML 参数描述里写清楚类型、格式、示例值 | | 技能执行到一半停止 | 步骤之间缺少明确的输入输出衔接 | 每个步骤开头明确“上一步产出了什么这一步要消费什么” | | 输出格式不稳定 | 步骤里没有规定输出的章节与顺序 | 在报告生成步骤写死章节结构和顺序 | | 数字与真实数据对不上 | 缺少自检节点 | 在流程末尾加入自检步骤强制逐项核对 | | 改完文件后行为无变化 | 框架没有重新加载技能 | 重启服务或执行 reload 命令确认新文件被注册 | | 多个技能功能相近导致误调 | 技能边界描述不够清晰 | 在 description 末尾显式写明“不适合用于什么场景” | ### 4.2 三个我踩过的坑与解决思路 第一个坑description 写得太“高级”模型不认识。早期我给一个技能写描述时用了“基于指标体系输出深度洞察”这种偏 marketing 的说法。结果模型每次看到数据分析请求都犹豫半天最后选择调用另一个描述里有“表格、汇总、图表”关键词的技能。后来我改成“当用户提供行业名称并需要市场分析、趋势判断、竞品对比时使用”召回率立刻上来了。这个细节让我彻底明白技能描述是写给检索系统看的不是写给论文评审看的。 第二个坑参数校验太宽松导致下游崩溃。之前声明 time_range 时只写了“时间范围”没有指定格式。模型非常自然地把用户的“最近一年”直接传进去下游日期解析脚本直接抛异常。后来我把描述改成“格式为 YYYY-MM-DD 至 YYYY-MM-DD缺省默认最近一年”并在步骤里显式写了缺省处理逻辑问题就消失了。参数描述里少写一个格式说明执行层就要多背一个 bug。 第三个坑让模型“自由发挥”导致输出千奇百怪。有一版技能里我写“根据数据生成趋势分析”没有规定每段的结构。结果模型有时先写结论再列数据有时先列一堆表格再给结论用户反馈很难读。后来我在报告生成步骤里硬性规定了六段章节结构和顺序输出立刻稳定下来。做 Agent 技能自由发挥是毒药约束清晰才是上策。 ### 4.3 检查技能调用效果的几条经验 关于验证我的习惯是建一个小型回归样本集里面放 20 到 30 条典型任务请求覆盖正常请求、缺参请求、边界场景、不该调用技能的负例。每次改动 SKILL.md就把这个样本集跑一遍统计技能的正确召回率和参数正确率。这个习惯帮我省下了大量“上生产才发现坏了”的尴尬时间。 还有一个容易疏忽的点SKILL.md 也是一种代码一样应该走版本管理。我见过不少团队直接在生产服务器上改文件改完没有备份出问题后连回滚都难。建议把技能文件纳入 Git 仓库每次改动留下 diff。技能文件的可测试性是提示词模板给不了的。 最后提一个更进阶的玩法。当你的技能库变多之后可以在 SKILL.md 的 description 里互相引用关联技能比如“本技能生成的报告可作为 competitor_analysis 技能的输入”。这样 Agent 在编排复杂任务时就能按技能依赖链自动串联多个文件而不是每次都在一个巨型 prompt 里塞下所有逻辑。这也是 SKILL.md 做得越久越有价值的原因它不是替代你的提示词而是把可复用的经验沉淀成了工程资产。 我在实际使用中最深的体会是SKILL.md 的引入让我第一次能把“调模型”变成“写配置”。以前调提示词模板每次改动都在 reset 模型行为改一个词都可能影响全局现在我把步骤、参数、工具都拆开放进技能文件里每个字段都可以单独测试、迭代、回滚。这个思维转变比某个具体语法重要得多。你手头如果有一条被反复改过 n 版的提示词模板不妨把它拆成一个 SKILL.md跑通一个流程你就能理解为什么我这么说。