ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

Agent Skills实战:从技能封装到调度机制,打造稳定可靠的AI Agent

Agent Skills实战:从技能封装到调度机制,打造稳定可靠的AI Agent 1. Agent为什需要“Skills”而不是一堆零散的工具函数这两年“Agent”这个词快被说烂了但真正跑过生产环境的人心里都清楚一个Agent能不能干活很多时候不取决于模型有多聪明而取决于它手里有没有一套沉淀好的方法。我在agent-skills项目上折腾了差不多半年最大的体会是——把Agent变强的最短路径不是给它更多API权限而是把“老手做这件事的完整过程”封装成它能直接照做的技能库。1.1 没有Skills的Agent为什么总在低级错误上反复打转先看一个我们几乎都经历过的场景你让Agent帮你调研某个行业你煞费苦心地在提示词里写了“先找权威来源”“再交叉验证”“最后输出结论”第一次它做得不错。第二次换了个话题你又得重新写一遍。第三次你忘了写“输出中要标注信息来源”它就真的开始满嘴跑火车。这不是模型变笨了而是你每次都在把工作经验临时灌输给它。Agent本身没有任何记忆你的Prompt写得再细关掉会话就归零了。更麻烦的是一次对话里塞入的规则一旦超过某个量模型会开始忽略细节只挑它觉得重要的部分执行。Skills解决的就是这个问题。它本质上是在Agent的工作目录里放一套“操作手册”一个技能对应一类任务手册里写清楚什么场景使用、按什么步骤执行、需要调用哪些脚本、输出长什么样。上次调好的流程这次直接复用不需要再教一遍。我当时在agent-skills仓库里写的第一个技能是report_generator作用很简单把一堆杂乱的项目记录整理成结构化周报。这个技能放在很多Agent框架里看就是个Prompt模板但我故意把它做得更厚——里面包含了数据清洗规则、周报格式模板、以及异常数据怎么处理的示例。跑了一个月之后它生成的周报几乎不需要人改。1.2 Skill、Function Calling、Plugin到底怎么区分不少朋友问我Skills和Function Calling有什么区别和Plugin又有什么关系。我一般用这张表来回答维度SkillFunction CallingPlugin核心载体流程化的过程知识单个函数接口外部系统集成包解决的问题让Agent知道“怎么做”让Agent知道“能调什么”让Agent具备对接“外部服务”的能力是否包含逻辑包含多步推理和执行规则通常只有一个原子操作包含API对接、鉴权、数据转换典型表现一份SKILL.md加若干脚本函数名加参数schema独立模块接入后成为Agent的能力扩展它们不是替代关系。一个成熟的技能库里一个Skill通常会调用好几个Function也可能依赖某个Plugin去拉外部数据。区别在于粒度Function是“手”Skill是“操作说明书”Plugin是“工具箱里的专用设备”。1.3 agent-skills项目的核心思路我做agent-skills的时候给自己定了一个调与其做一个全能的Agent不如做一批足够好用的Skills。项目的目录大概是这样的agent-skills/ ├── README.md ├── skills/ │ ├── report_generator/ │ │ ├── SKILL.md │ │ ├── scripts/ │ │ └── templates/ │ ├── research_task/ │ │ ├── SKILL.md │ │ ├── scripts/ │ │ └── examples/ │ └── ...核心思路只有一句话把个人经验转成团队可复用的资产。每个技能目录都自包含可以单独测试、单独发布、单独回滚。别人拿过去不需要理解你的原始想法只需要读一遍SKILL.md就能用起来。2. Skills的目录结构与内部组织把一项能力拆成最小可复用单元很多开源项目里的Skill就是一片Markdown看起来方便真跑起来会发现缺东西。我建议一个可用的Skill至少包含三部分能力声明SKILL.md、执行工具scripts、参考素材templates/examples。2.1 SKILL.md是给Agent看的说明书不是给人看的文档我踩过最大的坑就是按照“写给人看的技术文档”标准去写SKILL.md结果Agent读起来效率极低。后来我总结了一套更适合Agent解析的结构大概是这样的--- name: research_task description: 当用户需要调研某个主题、行业、竞品或技术方向时使用此技能。 - 输入: topic主题、depth深度、lang输出语言 - 输出: 结构化调研报告Markdown格式 --- ## When to Use 用户提出“调研、研究、分析、了解一下、对比一下”等关键词时通常需要本技能。 ## Process 1. 信息收集利用search_fetch工具获取至少10个不同来源的页面。 2. 信息筛选按权威性、时效性、相关性三档打分保留分数不低于7分的资料。 3. 交叉验证同一事实必须在至少两个独立来源中同时出现否则标记为“单一来源信息”。 4. 结果输出按模板生成报告包含摘要、核心发现、数据表格、来源列表。 ## Dependencies - search_fetch: 必需 - fetch_webpage: 必需 - extract_pdf: 可选 ## Constraints - 不得将任何单一来源信息表述为“事实”。 - 输出语言必须与lang参数一致。这种写法Agent读起来效率很高因为每个段落的边界非常清晰。关键是description字段要写“触发场景”而不是“功能定义”。举个例子“当用户需要调研某个主题时使用”比“执行调研任务”更容易被Agent命中。2.2 配属脚本与模板让技能不止是“嘴上说说”纯文本的Skill能教会Agent流程但教不了它“动手”。我会给大部分技能配上至少一个脚本哪怕脚本很小。比如research_task技能里放了一个filter_sources.py作用是对收集到的来源列表做初筛剔除明显不相关的URL。这样Agent就不用每跑一次都让模型自己判断一遍来源质量。脚本放在技能目录里有几个实在的好处依赖可以声明在技能内部避免全局安装一堆库。单个技能可以被单独单元测试不污染其他技能。给脚本加注释就是最低成本的技能文档。templates目录我一般放输出模板。report_generator技能里有一个weekly_report_template.md里面预置了表格和段落的骨架Agent只需要往里面填内容。比起让模型自由发挥模板能显著提升输出的一致性。2.3 命名与描述的艺术技能命名要面向“能力”不要面向“接口”。我见过有人把技能命名为github_api_wrapper这就是典型的面向接口命名。换成release_packager或者project_syncerAgent在应对“帮我整理发布包”这类请求时命中率会明显更高。描述里的触发词要覆盖自然语言的多种表达方式。比如调研技能我会把“调研、研究、分析、了解一下、对比一下、查一查”全部写进描述里。Agent做行动决策时很多时候就是靠description和用户请求的语义匹配来选技能。这段描述写不好再好的技能也会被晾在一边。3. 我如何从零封装一个可用Skill以调研型任务为例空谈理论没意思我直接拿research_task这个技能来走一遍完整封装流程。这也是agent-skills里被复用次数最多的一个技能。3.1 选定场景先定义输入输出没有明确输入输出的技能就是耍流氓。我当时是先写了一份内部约定把技能边界画清楚了输入字段类型必填说明topicstring是调研主题一句话说清楚depthstring否可选值summary/detail/deep默认detaillangstring否输出语言默认zhmax_sourcesint否最大来源数量默认10contexttext否附加背景信息例如调研目的输出则固定为Markdown报告分五个段落执行摘要、核心发现、数据与证据、风险与局限性、参考来源列表。每段都有明确要求比如“风险与局限性”必须有哪怕内容是“本次调研未发现重大风险”。把输入输出定成这样最大的价值是Agent知道自己“干完活”的标准是什么。很多Agent跑偏就是因为任务完成的标准没定义清楚。3.2 把“老手怎么做”写进过程框架定义完输入输出接下来是最难的一步把老手调研时脑子里走的流程显式地写进技能里。我拆解了自己做调研的动作总结出四步信息收集。这里有个关键规则先用尽量宽的搜索词拿回大量候选宁多勿缺。很多Agent调研质量差是因为第一步就只搜索了用户给的那个关键词漏掉了同义词、相关概念和上下游信息。信息筛选。不是所有搜索结果的得分都一样。我给每条来源做三档评分权威性是否来自机构官网、学术数据库、行业头部媒体、时效性是否近三年内、相关性是否直接命中主题。三项评分相乘低于阈值进不去待用池。交叉验证。同一个数据点至少要找到两个独立来源才能写进“核心发现”。只出现一次的信息单独放进“单一来源信息”一节。结果组织。按模板输出不要自己发明结构。这套流程看起来平平无奇但它解决了Agent最让人头疼的“一本正经编数据”问题。交叉验证那一步直接砍掉了大部分幻觉输出。3.3 本地验证的三板斧技能写完之后不能直接上生产我在本地会做三轮验证第一轮最小样例。用一个很小的topic跑一遍完整流程比如“什么是Agent Skills”。重点看过程是否卡住、输出是否完整、有没有越界动作。第二轮边界输入。把topic设成一个极其模糊的词比如“效率”看看Agent会不会不知所措再把topic设成一个极其具体的词比如“React 19 服务器组件在Next.js 15中的水合错误率”看看技能能否兜住深度需求。第三轮资源消耗记录。每次跑完统计token消耗和耗时。我给自己定了一条线单个调研任务全流程的token消耗不能超过一定范围如果超了说明检索步骤可能循环太多次需要给脚本加阈值。这个验证流程完全可以照抄不管你是用现成框架还是自己写调度器跑一遍花不了多少时间但能省下后面调试的无数个小时。4. 装载与调度Agent运行时如何发现并调用Skill技能封装好了下一个问题是怎么让Agent“知道”有这些技能并在合适的时机把它们调用起来。4.1 显式调用和自动调度怎么选我在项目里同时支持两种调用模式。显式调用适合任务边界清晰的场景。用户输入“运行周报技能”Agent直接加载report_generator不需要做任何决策。这种模式可靠性最高适合企业内部的固定流程。自动调度适合开放式场景。Agent收到“帮我分析一下上周的数据情况”这种模糊请求后自己从技能库里匹配最合适的技能然后加载执行。这种模式灵活但决策错误的风险也高。两手准备的原因是完全依赖自动调度在技能数量多了之后一定会出现误选完全依赖显式调用又等于让用户背技能清单。最终我采取的策略是关键任务优先显式调用辅助任务允许自动调度。4.2 技能描述、优先级与冲突消解当技能库里的技能超过十个自动调度一定会碰到“多个技能看起来都合适”的情况。我靠三个机制解决触发分数。我给每个技能的描述里隐式标定了触发场景。agent-skills里的调度器会计算技能描述和用户请求的语义相似度超过阈值才进入候选池并且会输出一个置信度分数。技能优先级。候选池里的技能按优先级排序。比如report_generator和data_analyzer都可能处理周报任务但report_generator优先级更高因为它是专门做格式化的。仲裁规则。两个技能置信度都很高时采用“更具体的技能获胜”原则。data_analyzer是泛指report_generator是特指那么特指的胜出。这套机制不复杂但确实把误选率降了不少。4.3 多技能协同时的上下文管理实际业务里很少有一个技能从头干到尾的情况。更多时候是调研技能先产出资料分析师技能再对资料做解读最后周报技能把解读结构化成报告。技能一旦串联上下文管理就成了大问题。每个技能都会向对话里注入自己的指令文本、脚本输出和中间结论累积起来会撑爆上下文窗口。我的做法是三个原则技能输出必须带摘要。每个技能结束时生成一个不超过300字的“结果摘要”后续技能只读摘要和必要数据文件不读完整日志。技能上下文尽量自包含。技能A如果依赖技能B的输出那么技能B应该把结果落成一个中间文件技能A去读文件而不是从对话历史里找。中间结果隔离。Agent的工作目录里按任务ID建子目录每个技能产生的临时文件都存在对应子目录里避免互相覆盖。这三条原则让多技能协同从“一团乱麻”变成了“流水线作业”。5. 实测中最容易翻车的几个问题即使结构和调度都搭建好了真正跑起来还是会遇到各种奇怪问题。我把最典型的三个放在这里这些坑在官方文档里基本找不到。5.1 上下文污染技能“好心办坏事”我在2.1节提到SKILL.md要写得边界清晰是因为我吃过一次很大的亏。一开始我的skills包内容写得特别丰富把各种背景知识、最佳实践、示例都写了进去一个SKILL.md能有一两千行。结果就是Agent每次加载这个技能都要把这一两千行塞进上下文。看起来是懂了很多实际上模型被大量指令干扰反而执行不好主任务。有一回调研技能加载后模型居然花了不少精力去遵循文档里“保持好奇心”这种泛泛的要求却把“交叉验证信息”这个核心步骤给省略了。解决方案是“分层压缩”SKILL.md只保留必须的规则和流程背景知识挪到docs/目录只有在Agent需要时才会读取。主说明文件控制在150行以内让模型在一屏之内能把握全貌。5.2 技能之间互相打架Agent陷入死循环技能多了之后会出现一种诡异的失败模式Agent在调研技能里发现需要整理信息来源于是调用了格式化技能格式化技能又认为自己需要先分析数据于是调用了分析技能分析技能又觉得应该先搜索更多材料于是又调回了调研技能。整个Agent陷入了一个循环白白消耗大量token最后输出一个没有结论的报告。我给的解法是给每个技能声明“允许调用边界”。SKILL.md里的Constraints段落不仅写“不能做什么”还要写“不能调用哪些技能”。同时我在调度器里加了一个硬性规定技能嵌套深度不能超过一层。也就是说技能A可以调用技能B但技能B内部不允许再调用技能C需要更复杂的能力时必须回到主循环里重新规划。这个限制一开始觉得很死板但实际跑下来反而稳定很多。Agent又不是分布式系统没必要让技能无限递归下去。5.3 过度设计技能库从助力变成负担做agent-skills的第三个月我的技能数量膨胀到了30多个。看起来是好事实际上每个技能都要维护、测试、更新描述。更糟的是技能一多自动调度器开始频繁误选。有一次用户说“帮我整理一下PDF里的合同条款”调度器居然匹配到了表单提取技能因为那个技能描述里写了“抽取文本”。这种错误说实话有点蠢但责任在我——我不该给还没用熟的能力建立那么多入口。后来我定了一条铁律一个技能如果在四周时间内没有被任何Agent成功调用过就摘掉它的自动调度入口降级为普通文档存档。技能数量从30多个压回15个左右误选率立刻下降了一大截。6. 面向团队与项目落地的进阶思考如果你只是自己玩前面五章的内容已经够用了。但如果要把技能库放进团队项目里还有几件容易被忽略的事。6.1 技能版本的治理像管理代码一样管理技能技能也是代码的一种只不过它的“运行环境”是Agent。我在agent-skills项目里把每个技能都纳入Git管理并且规定任何改动必须走MR流程同时更新CHANGELOG.md。每个技能目录下有一个轻量的metadata.json{ name: research_task, version: 1.4.2, last_updated: 2025-06-18, changes: [ 增加单一来源信息标记规则, 修复了深度调研时搜索循环次数过多的问题 ] }靠这个文件我能快速定位“上周还能用这周突然表现变差”是不是某个技能版本更新导致的。6.2 从使用日志中反推技能改进技能好不好用不能只靠感觉。我给调度器加了一个很基础的日志模块每回Agent调用技能都会记录一组信息调用时间、触发的用户请求、命中的技能名、是否完成、是否中途失败、如果失败是哪一步失败。字段示例timestamp2025-06-18 14:32:11request帮我分析最近30天的销售数据异常skilldata_analyzerstatuscompletedfail_stepnull每两周我会拉一遍日志把失败率最高的技能捞出来看原因。绝大多数失败都集中在三个原因技能描述与用户请求不匹配、技能流程中某一步需要的外部资源不可用、输出模板和实际场景对不上。这些信息比任何抽象讨论都有用。6.3 让技能库保持“脏乱但可用”的实践经验最后一个建议可能跟很多人的直觉相反不要太早追求技能库的规范化。我见过有人花了三周时间设计技能Schema、写单元测试、做自动化校验结果一个实际能跑的技能都没做出来。对我来说先让技能库里有一批“能用但丑”的技能比有一套漂亮但空转的框架重要得多。前期的脏乱是可以接受的因为只有在真实使用中你才知道哪些字段是必要的哪些规则是多余的。等到技能数量超过15个、开始影响调度准确率时再花时间做规范和演进也不迟。我个人现在维护agent-skills的方式其实挺朴素保持每个技能能独立工作保证SKILL.md是最新的然后让真实的调用数据告诉我下一步该优化什么。这个方向并不性感但它就是让Agent真正“好用”的那条路。
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进