Agent Skill:智能体能力封装与复用的关键实践

📅 发布时间:2026/10/21 3:20:05
Agent Skill:智能体能力封装与复用的关键实践
“你天天挂在嘴边的 Agent Skill到底有什么用不就是把 Prompt 写得更长一点吗”上周某位朋友在微信上突然这么问我。他最近刚上手智能体开发已经能让大模型按固定格式写周报、自动总结会议纪要觉得自己玩得挺明白了。这个问题也不怪他——我在聊到智能体架构时总是把“Skill”说得轻描淡写但没有一个清晰的解释告诉大家Skill 解决的到底是什么问题为什么要把它当成一个独立的工程概念而不是一句轻飘飘的“给 Agent 加个工具”。当时我解释了很久然后意识到这件事值得写一篇完整的文章而不是靠一段语音讲清楚。原因很简单Agent Skill 这个名字听起来像“技能”但它既不是单纯的函数调用也不是普通 Prompt 模板它是一套把指令、示例、代码、校验流程打包成独立单元的封装思路。这种封装的收益只有自己动手做过一遍踩过几个坑才能真正体会到。这篇文章写给已经会用智能体、但还没系统想过“如何设计可复用的智能体能力”的人。我会先用大家熟知的场景把概念讲明白然后拆解一个技能的完整构成最后带大家从一个实际技能出发完整走一遍从设计到验证的过程。重点是我自己踩坑后总结出的细节和排查经验这些在官方文档里通常找不到。1. 先把“Agent Skill”这件事说透1.1 它不是“更长的 Prompt”而是“抽屉里的专用工具”先打个比方。一个传统 Prompt 就像把使用说明书整本塞进大模型的脑子里让它在任何对话里都记住全部内容。而 Agent Skill 更像家里的工具箱电钻、螺丝刀、水平尺各占一个抽屉抽屉外贴着一张标签写着“什么时候用、大概能干什么”。当你需要拧螺丝时才打开对应抽屉拿出工具用完再收回去。这个差异在真实场景中体现得非常明显。我见过很多人把系统提示写到一万词恨不得把公司所有业务规则、话术模板、代码规范全部堆进 system prompt。结果就是上下文占用高、大模型选择困难、输出质量反而不稳定。Skill 的核心逻辑恰好相反默认情况下大模型只看到每个技能的“标签”名称和功能描述只有当任务与标签匹配时才把技能内部完整的说明和脚本加载到上下文中。这种“按需加载”让信息使用效率高很多也降低了大模型被无关信息干扰的概率。我一直认为Agent Skill 是对“如何和大模型协作”的一次重新定义不是命令大模型“记住一切”而是给大模型提供一套可检索、可调用、可复用的能力集合。它会变成一个像函数库一样的东西只不过这个“函数库”的使用者是模型而不是程序员。1.2 它和函数调用、RAG 有什么本质区别很多人在第一次接触 Agent Skill 时会把它和函数调用Function Calling或者 RAG 混淆。这里我直接给出一个简单的对照逻辑函数调用解决的是“大模型如何准确地表达意图并触发一段程序”本质是“把自然语言翻译成 API 调用”。RAG 解决的是“大模型如何获取未在训练时见过的知识”本质是“把外部文档转化为可检索的上下文”。Agent Skill 解决的是“大模型如何在不重新写 Prompt 的情况下复用一套完整的行为流程”本质是“把指令、知识、脚本、校验包装成一个可迁移的单元”。举一个例子。如果你想给智能体增加“生成周报”的功能用函数调用你写一个generate_report(data)的接口让模型在合适时机调用但格式、风格、内容结构仍然需要靠 Prompt 约束。用 RAG你可以把历史周报塞进知识库让模型参考风格但它不知道应该调用什么脚本或工具来做字段提取。用 Agent Skill你可以创建一个weekly-report-skill里面既包含“何时生成、生成什么格式”的说明也包含一个 Python 脚本来整理本周数据、渲染 Markdown 表格还包含一个示例输出供模型参考。模型识别到“用户要周报”后会读取整个技能说明按步骤执行、运行脚本、校验结果最后输出合规的周报。三者的边界不是绝对的很多时候会结合使用。但在架构设计上一定要意识到 Skill 承担的职责和其余两个完全不同。1.3 为什么“复用”会成为智能体开发的拐点大模型应用开发初期大家解决一个问题就写一个 Prompt项目里散落着几十个长得差不多的提示词文件。某次改动业务术语要手动同步五六个文件。这种复制粘贴式的开发方式在单 Agent 的小项目里勉强能撑住一旦进入多智能体协作、多个场景复用的阶段就完全不可行了。Agent Skill 之所以这阵子很火正是因为它把长期被忽视的“复用”问题摆到了台面上。一个团队可以维护一个技能仓库不同项目按需接入甚至不同的智能体之间也能共享同一套技能只需要在加载时指定技能目录。这意味着“智能体的能力”真正具备了工程意义上的独立性和版本管理能力。你不需要从一开始就追求复杂的框架但要尽早建立“凡是重复两次以上的能力都应该做成技能”的思维。这个思维转变能让你在后续项目里省掉大量返工时间。2. Agent Skill 的核心组成与设计逻辑2.1 一个技能包里到底该放什么一个标准的 Agent Skill通常以目录为整体单元。我习惯的目录结构长这样my-skill/ ├── SKILL.md ├── scripts/ │ ├── run.py │ └── helpers.py ├── assets/ │ └── templates/report_template.md ├── requirements.txt └── tests/ └── test_skill.pySKILL.md技能的大脑包含了技能名称、描述、使用步骤、注意事项、调用示例。这是大模型真正会“阅读”的核心文件。scripts/实际执行任务的代码。它负责那些大模型不擅长或者不应该靠“想”来解决的事情比如精确计算、字符串解析、文件操作、数据请求。assets/固定资源比如输出模板、参考样例、配置文件。技能内部需要引用但不直接展示给模型的静态资源。requirements.txt技能运行所需的依赖清单方便在正式环境里一键安装。tests/用于验证脚本正确性的测试文件。很多人会省略这一步但我强烈建议保留因为 Agent 开发中最怕的不是模型不聪明而是脚本悄悄出错你却不知道。最好的理解方式是SKILL.md 是说明书scripts 是工具本体assets 是耗材requirements 是工具清单tests 是质检报告。一个技能包就是一个人可以独立完成某类工作的“工作站”。2.2 SKILL.md 的每一部分都有明确目的现在重点拆解SKILL.md因为它是 Agent Skill 的入口和灵魂。一个规范的 SKILL.md 用 YAML 头加正文组成YAML 头主要包含这几个字段--- name: weekly-report-generator description: 生成周报的专用技能。适用于开发者周报、项目进展同步、团队管理汇报等场景。输入原始工作记录输出结构化的 Markdown 周报。 when_to_use: 当用户要求生成周报、汇总本周工作、整理项目进展时使用当用户只是询问某一天的工作内容而明确不需要周报时不要使用本技能。 version: 1.2.0 ---name是技能的唯一标识尽量简短明确。description是模型用来判断“要不要调用这个技能”的关键依据必须写清楚“这个技能能做什么、适合什么场景”。最容易犯的错误是把 description 写成“一个周报工具”这种描述会让模型在犹豫时直接跳过技能。更实操的做法是像上面那样用“动词 对象 典型场景 输入输出格式”来描述。when_to_use是我在其他地方很少看到有人强调、但作用至关重要的字段。它是在给模型划边界告诉模型什么情况下可以用、什么情况下不要用。加了这一步之后“误用技能”的概率能下降一半以上。YAML 头之后是正文我通常按照下面四个小节来组织目标用两三句话说清楚这个技能完成什么任务让模型对最终产出有预期。步骤明确列出执行步骤。注意要写“模型需要做的步骤”和“脚本需要做的步骤”让分工清晰。注意事项列出常见错误、边界条件、输出格式硬性要求。这一部分特别有价值因为模型在推理时会主动参考这些约束。调用示例附一个“用户的输入”和“完整的输出”示例这是模型理解技能的最佳途径。没错一份好的 SKILL.md 本质上是一份“给模型看的标准作业流程”。它不是一个技术架构文档不要用纯技术语言要用模型容易理解的、结构清晰的、步骤化的人类语言来写。2.3 为什么这种封装能显著降低“翻车率”我在前面提到Agent Skill 最大的价值之一是“按需加载”。但从工程实现角度深挖它的设计还从三个维度显著降低了智能体开发的整体风险上下文可控性把大量指令性内容从系统提示中移出交给技能文件被动加载。这样主提示保持精简模型可以集中注意力在当前任务上下文长度也不容易失控。决策确定性技能中的“步骤”将模型可能走的路径收敛到明确流程中。比如让它先调用脚本解析数据再根据解析结果生成正文而不是让它自由发挥。这相当于给模型的“思维过程”加了轨道。可测试性脚本可以被单独测试输出格式可以被程序校验。这样即使模型偶尔理解有偏差最终产物也能通过后置检查来纠错。我自己在实际开发中的体感是加了 Skill 之后同样任务的输出稳定性提升明显尤其是那些流程复杂、格式要求严格、有固定处理步骤的任务。相比“全凭模型悟性”的做法Skill 的确定性优势是压倒性的。3. 实操从零构建一个“客户反馈总结”技能3.1 先想清楚技能的主流程理论讲得再多不如动手做一个。这一次我们来做“客户反馈总结”技能目标是从客户发来的分散消息里提取关键信息、打上问题分类、生成一份结构化的汇总报告。选择这个例子是因为它覆盖了一个技能里几乎所有典型环节文本解析、分类映射、格式化输出、调用外部脚本。难度适中又能暴露大部分常见问题。开始之前我先确定主流程模型读取用户提供的原始反馈文本。模型判断是否属于本技能适用场景有没有客户反馈、投诉、报障。调用scripts/parse_feedback.py对原始文本做关键字分类和情感预判。模型根据脚本输出结果结合上下文补充备注生成 Markdown 汇总报告。校验报告必填字段是否齐全。3.2 实际动手搭建技能目录我先创建目录结构mkdir feedback-summary-skill cd feedback-summary-skill mkdir -p scripts assets tests然后写SKILL.md这是整个技能能否被正确调用的关键文件我直接把我实际用的模板分享出来--- name: feedback-summarizer description: 从客户原始反馈中提取关键信息分类并生成汇总报告。适用于客服团队、产品运营团队处理用户投诉、功能建议、使用疑问等场景。 when_to_use: 当用户提供多段客户反馈并要求整理分析时使用当用户只是询问如何写客服话术时不要使用本技能。 version: 1.0.0 ---正文部分## 目标 将杂乱的客户反馈转成结构化的汇总 Markdown 报告报告包含问题分类、情绪倾向、处理建议、原始引用。 ## 执行步骤 1. 阅读用户提供的反馈原文提取每条反馈的核心诉求。 2. 运行 scripts/parse_feedback.py input.txt传入原文路径获得 JSON 格式的初步分类结果。 3. 将分类结果与用户补充信息合并形成报告内容。 4. 严格按照下方“输出格式”生成 Markdown 报告。 5. 检查报告是否包含所有必填字段分类、标题、情绪、建议、原文引用。 ## 注意事项 - 不要虚构用户没有说过的话。 - 若原始反馈涉及多个问题拆成多条记录不要强行合并。 - 分类只能从: [功能问题, 体验问题, 建议, 咨询, 投诉] 中选择。 - 如果脚本输出为空或报错请停止流程并告知用户“反馈解析失败”。 ## 输出格式 markdown | 序号 | 分类 | 核心问题 | 情绪 | 处理建议 | 原文引用 | |------|------|----------|------|----------|----------| | 1 | 功能问题 | ... | 负面 | ... | 原文摘要 | ### 本轮反馈总结 - 高频问题... - 需要立刻处理...示例用户输入这个应用更新后老是闪退我已经重启三次了。另外希望增加深色模式。模型输出序号分类核心问题情绪处理建议原文引用1功能问题应用更新后闪退负面优先排查崩溃日志 这个应用更新后老是闪退2建议希望增加深色模式中性加入需求池评估 希望增加深色模式本轮反馈总结高频问题稳定性问题。需要立刻处理崩溃问题影响面较大建议第一时间响应。你可能会注意到我把输出格式写得非常具体这是故意的。大模型在看见一个带着表格结构和示例的说明时比看一句“请整理成 Markdown 报告”要容易理解得多。格式约束越清晰输出偏差越小。3.3 写脚本做模型做不好的事parse_feedback.py的作用不是做 NLP 层面的高深分析而是替模型完成“完整读取输入、按预设规则分类”这种精确操作。脚本可以把结果输出成 JSON让模型直接拿来做下一步推理。下面是个简化版本import json import sys import re CATEGORY_KEYWORDS { 功能问题: [闪退, 崩溃, 报错, 无法打开, 卡死, 白屏], 体验问题: [太难用, 不直观, 找不到, 反人类, 不方便], 建议: [希望, 建议, 增加, 能不能, 考虑], 咨询: [怎么用, 在哪, 如何, 请问, 操作], 投诉: [投诉, 赔偿, 垃圾, 差评, 再也不], } NEGATIVE_WORDS [差, 慢, 闪退, 崩溃, 太难, 垃圾, 投诉] POSITIVE_WORDS [好用, 流畅, 喜欢, 满意, 不错, 赞] def classify(text: str) - str: scores {} for category, words in CATEGORY_KEYWORDS.items(): scores[category] sum(1 for w in words if w in text) if not scores or max(scores.values()) 0: return 未分类 return max(scores, keyscores.get) def detect_sentiment(text: str) - str: neg_score sum(1 for w in NEGATIVE_WORDS if w in text) pos_score sum(1 for w in POSITIVE_WORDS if w in text) if neg_score pos_score: return 负面 if pos_score neg_score: return 正面 return 中性 def parse_input(raw: str) - list: lines [l.strip() for l in raw.splitlines() if l.strip()] records [] for idx, line in enumerate(lines[:50], start1): records.append({ id: idx, text: line, category: classify(line), sentiment: detect_sentiment(line), }) return records if __name__ __main__: raw_text sys.stdin.read() print(json.dumps(parse_input(raw_text), ensure_asciiFalse, indent2))脚本接受标准输入逐条扫描反馈文本输出结构化的 JSON。虽然关键词判定的方式比较粗糙但它足够稳定也不会像大模型那样“发挥过度”适合作为技能内部的确定性处理器。实际项目中如果要做更精准的语义分类可以在这个位置换成调用分类 API但整体架构保持不变。写完之后别忘了补上requirements.txt。这个示例里只用了 Python 标准库所以文件里可以留空但我倾向于写一行注释说明用途。真实项目中只要有用到第三方库就一定要固定版本否则技能换一台机器就跑不起来这是很多新手会踩的坑。3.4 把技能接入主智能体每个智能体框架对技能目录的加载方式略有不同但基本思路一致在主配置里声明技能目录位置框架启动时扫描所以 SKILL.md提取 YAML 头生成“技能索引”。模型在运行时看到的是技能名称和 description。当用户的请求与 description 匹配时框架才会把完整的 SKILL.md 正文注入上下文。我通常在接入时做三件事检查“技能索引”里的 name 是否唯一没有与其他技能重名。用一句测试问题验证技能是否能被自动触发。比如问“帮我把这几条客户反馈整理成报告”观察模型是否读取到 SKILL.md 内容。在测试阶段暂时关闭其他技能避免模型因为“选择太多”而调错。等验证通过再逐步放开其他技能。3.5 测试技能时的两段式验证法技能接入之后验证工作不能只靠“跑一次u200b看效果”。我习惯把验证拆成两层调用层验证确认模型在什么场景下会触发该技能。这一步不需要真的执行脚本只需要观察模型是否读取了技能的完整说明。用不同的提问方式反复测比如直接说“整理反馈”、含蓄地说“这些抱怨你帮我汇总一下”、拿来一段与技能无关的技术问题看模型是否正确触发或正确拒绝。如果该触发时没触发大概率是 description 写得不到位。输出层验证确认技能生成的报告结构是否规范、字段是否完整、脚本是否稳定执行。可以先准备好几组标准测试数据跑脚本对比输出再让模型生成报告人工检查格式。输出层的问题通常出在 SKILL.md 里的“注意事项”没有写清楚或者脚本本身存在边界 bug。这个两段式验证法帮我省去了大量“模型跑起来效果不对但分不清是逻辑问题还是框架问题”的排查时间。4. 我踩过的坑Agent Skill 常见问题与排查实录4.1 技能写了但模型就是不调用这是我在最初做技能时遇到最多的问题。你辛辛苦苦写好了 SKILL.md目录也挂载好了模型却像看不见一样自己凭记忆硬答。排查思路其实有几条我按优先级排一下先看 description 是否足够“诱人”。如果描述是“一个用于处理客户反馈的技能”模型很难理解什么情况下该用它。改成“当用户提供客户原始反馈并要求整理分析时使用”触发率会明显上升。再看 when_to_use 是否写了反向条件。我强烈建议每份 SKILL.md 都写明“什么时候不要用”。模型一旦拥有了这个技能的“排除条件”误触率会大幅下降但很多人会忽略这一点。最后确认主提示里没有“技能无用”之类的指令。如果系统提示里写了“不用调用任何工具直接回答”那模型再聪明也不会去读技能。定位问题最快的方法是开启智能体框架的调试模式观察模型在收到用户请求后到底看到了哪些消息、有没有注入技能内容。这一步能看到所有技能触发判断过程。4.2 技能被乱用模型把不相干任务也套进技能里技能不被调用是问题技能被滥用又是另一个极端。有时候用户只是想问一句“你们产品怎么收费”模型却直接调用了反馈总结技能把一句话硬生生整理成报告。这种情况的根源往往是 SKILL.md 里的边界条件写得不够清晰。解决思路是在 when_to_use 中写得更具体同时在正文“注意事项”里增加一句“如果在执行过程中发现输入内容与客户反馈无关立即中止并回复用户这不在我的处理范围内”。给模型一个“退出机制”它就不会一条路走到黑。还有一个我后来养成的习惯技能内部的关键操作比如生成报告、发送邮件、写入数据库要求模型在执行前必须先用一句话向用户说明“即将做什么、为什么这么做”。这个简单的确认步骤能挡住一半以上的误操作。4.3 技能说明太长反而挤爆上下文有些开发者恨不得把项目管理规范、公司制度全部写进 SKILL.md导致技能文件几万字。结果就是每次调用这个技能上下文立刻被灌满模型的推理能力下降还容易“丢三落四”。这里有一个基本的上下文预算意识。技能文件被加载时会占据相当一部分上下文窗口而窗口总量有限。如果一个大技能动不动就占三万字那留给当前对话数据的空间就所剩无几了。解决办法是“分拆”把大技能拆成多个子技能SKILL.md 里只写精简版步骤更长的参考材料和模板放进assets/目录模型需要详细了解模板时可以再让脚本读取对应文件内容而不是全文加载进上下文。比如前面那个反馈总结技能完整的公司话术模板就不该写进 SKILL.md而应放在assets/templates/目录由脚本按需读取。这个做法能很好地控制上下文开销。4.4 技能之间互相打架有段时间我同时挂了十几个技能模型经常出现“左右互搏”用户要一份周报模型先调了周报技能又觉得其中一步需要调用数据分析技能两个技能互相交叉最终输出结构混乱。技能多了之后一定要做“边界梳理”。我会做一张表格把每个技能的 description、适用场景、关键词列出来查看是否有重叠。发现两个技能描述相近时要么调整边界要么给技能增加“前置条件”例如在周报技能里明确说明“本技能不包含数据分析如数据量较大请在报告中标注数据来源字段而不是直接调用其他技能”。另一个方法是在主提示中加入技能选择策略比如“当任务可以被多个技能完成时优先选择包含完整流程的那一个并且不允许在一个回答中组合多个技能的输出”。这句话虽然简单但能避免绝大多数混乱调用。4.5 依赖环境引起的“时好时坏”技能本身写对了但运行时提示缺包、版本不匹配或者模型读不到脚本生成的文件这种环境类的坑往往最耗时间因为它的表现不像逻辑错误那么直观经常是一次能跑、一次不能跑。我现在所有技能都按这三条规则来管理每个技能必须带独立的requirements.txt并锁定版本号不用“”或“latest”。部署时给每个技能准备独立的运行环境或容器避免不同技能依赖冲突。在任何一次重大改动后先跑一遍技能的测试脚本再接入智能体做整体测试而不是“改了就直接上”。另外脚本路径尽量不要用相对路径而是在 SKILL.md 里约定一个固定工作目录脚本内部使用绝对路径定位输入输出文件。否则模型在运行时可能目录搞错导致文件找不到报错信息又不直观。4.6 输出格式不稳定模型偶尔“自由发挥”即使 SKILL.md 里已经写了严格的输出格式模型在极端情况下还是可能输出错误结构。面对这种情况我会做两件事把“输出格式”部分写成可解析的模板并且在“注意事项”里强调“不得更改字段名不得改变表格列数”。模型对“不能动”这种约束的遵守程度比对“建议如何”要高得多。增加后置校验脚本。让智能体在输出报告后调用一段校验代码检查必填字段和格式如果不通过自动要求模型重新生成。这个“人工后置校验换成程序校验”的思路是把最后一道防线交给确定性代码而不是模型自觉。我在实际项目里通过加一个几十行的格式校验脚本就把输出格式的稳定性从“经常出错”提升到“连续跑几百次都不出错”。这笔投入非常值得。最后想说的一点经验Agent Skill 这个概念刚出来的时候我也觉得“这不就是把 Prompt 整理一下吗”直到自己维护的智能体数量变多、业务场景变复杂才意识到它真正的价值不是“让模型多干活”而是“让模型少想”。每次设计新技能时我的思考顺序其实很简单先拆流程把模型擅长的部分理解、归纳、表达和模型不擅长的部分精确计算、稳定解析、格式校验分开然后把后者交脚本前者以结构化说明的方式写进 SKILL.md最后用测试数据把调用边界确定下来。整个过程像是给模型搭了一条有护栏的跑道它跑得快不快另说至少不会动不动冲出跑道。后续如果你们团队的智能体开始跨项目复用可以往“技能仓库”的方向走把技能按领域分类、版本化、加上作者信息和变更记录做成团队内部共享的能力中心。到那个阶段Agent Skill 就真正从一个开发便利工具升级成了组织级的资产沉淀方式。如果你想从零开始试我的建议是先挑一个你自己反复做过的重复性任务哪怕只是“把一段聊天记录整理成待办清单”这种小功能严格按上面提到的结构把它做成技能跑上两周。体验过“一次编写、处处复用”之后你就再也不会回到把所有逻辑塞进一个巨型系统提示的老路上去了。