"agent-skills"这个词,我第一次看到的时候,第一反应是又一个把Function Calling包装成新概念的东西。但真正在一个中型项目里把它落地之后,我才意识到自己之前的理解有多浅。这个项目表面上是给Agent加一层"技能库",实际上它解决的是大模型应用从"能跑通"到"稳定可用"之间那个最让人头疼的鸿沟——怎么让模型在复杂任务里不跑偏、不偷懒、不发挥"创造力"。
如果你正在做AI Agent相关的开发,或者被工具调用不稳定、提示词越写越长但效果越来越差这些问题折磨过,这篇内容应该能帮上忙。我会从原理拆解到三种技能形态、完整搭建步骤、还有我在真实环境里踩过的几个坑,一次性讲清楚。
1. agent-skills到底是什么,以及它解决的核心矛盾
1.1 从"一堆API"到"一套技能"的思路转变
传统的工具调用方式,是给模型暴露一堆函数,然后让模型自己选。这听起来很灵活,实际用起来问题特别多。比如你给Agent接了十个工具,模型经常选错;你给工具写了详细描述,模型又会把description里的示例当成唯一标准答案;更离谱的是,当任务步骤一多,模型经常漏掉某些环节。
agent-skills的核心思路,是把"工具选择"这件事从模型手里部分收回来。它不再是一个个孤立的函数,而是把某个领域中完成一件事的完整能力包装成"技能"。技能内部可能包含多个步骤、多个工具调用、甚至包含特定的提示词策略和输出格式约束。对模型来说,它面对的不是一个扁平的工具列表,而是一组用户可以理解和选择的"能力卡片"。
这个转变本质上是在解决一个大模型应用里的核心矛盾:模型的泛化能力是优势,但也是不确定性的来源。你希望它能自主决策,又不希望它在关键流程上瞎决策。技能库就是那个中间层——把确定性锁定在技能内部,把灵活性保留在技能选择层面。
1.2 技能系统的三个核心组成
我在落地agent-skills时,把整个系统拆成了三个部分,缺一不可:
技能注册表(Skill Registry):这是所有技能的元信息中心,包括技能名称、描述、参数Schema、版本号、依赖关系、所属领域等。注册表的核心价值是让Agent在运行前就能"看到"有哪些技能可用,而不是把所有技能一股脑塞进上下文。
技能执行引擎(Skill Runtime):负责解析模型选中的技能、校验参数、调用底层工具或API、处理中间结果、汇总结论。执行引擎是技能内部逻辑的载体,它把"如何完成一件事"的步骤固化下来。
技能匹配器(Skill Matcher):这个组件负责把用户请求映射到最合适的技能上。它可以是纯LLM调用,也可以是嵌入向量检索加LLM排序的混合方案。匹配器的质量直接决定了整个系统的效果上限。
这三个部分之间通过一套标准化的协议通信。我后面会详细讲这套协议的Schema设计。
1.3 和传统插件的区别
很多人会问,这不就是插件系统吗?早期我也这么认为,但实际使用后发现区别还是很大的。
传统插件系统是"宿主程序调用插件",比如IDE、浏览器扩展,宿主是明确的核心,插件是边缘扩展。但agent-skills里,Agent本身没有固定的"核心流程",每个技能都可能是核心。另外传统插件通常是确定性代码,输入输出和内部逻辑相对固定;而技能内部可以混合使用提示词、规则、代码、外部API多种执行策略,甚至还可以在运行时动态决定内部步骤。
打个比方,传统插件是一整套预装好的厨房设备,而技能更像是给厨师提供的菜谱加备菜服务——菜谱告诉你怎么做,备菜帮你把材料处理好,但炒菜的火候和节奏,还是由当前这顿饭的需求决定。
2. 我在实际项目里把技能拆成了三种形态
2.1 工具型技能:把确定性操作封装到底
第一种是工具型技能,也是最容易理解的一类。它的特点是内部逻辑几乎是确定性的,输入参数固定,输出结构也固定。比如"查天气"、"发送邮件"、"创建工单"这类操作。
工具型技能的关键设计点在于,不仅仅是把API封装一层,而是要把错误处理、参数校验、幂等性这些细节都做进去。比如我之前封装了一个"查询订单状态"的技能,如果直接让模型调订单API,模型经常会传错订单号格式,或者查完也不知道结果意味着什么。封装成技能后,内部会先做参数归一化,查询后还会附带一个"结果解读"字段,告诉Agent这单处于什么阶段、下一步可以做什么。
这类技能不需要太多AI能力,但它是最考验工程功底的。因为模型对确定性的操作容忍度非常低——一旦出错,它就会开始"编"别的方案,这是最让人头疼的。
2.2 提示词型技能:把经验沉淀成模板
第二种是提示词型技能,核心是"在正确的时机给模型喂正确的上下文"。这个我一开始是拒绝的,因为这听起来太像把提示词工程包装成新概念了。但实际做下来,我发现它确实值得被当作独立技能来管理。
比如我在一个客服Agent项目里,沉淀了一个"安抚情绪"技能。它不是一个具体的API调用,而是一套话术模板加上几条情感判断规则。当识别到用户情绪激烈时,Agent会触发这个技能,先执行"情感确认"动作,再进入"结构化道歉",最后才处理具体问题。这套东西如果全部写在系统提示词里,会让上下文越来越臃肿,而且影响模型在其他任务上的表现。作为独立技能按需加载,效果差别很大。
这类技能的价值在于把"知道什么时候该说什么"这个经验从系统提示词里解放出来,让它变成一种可以被单独测试、优化和复用的能力。
2.3 编排型技能:把多步骤流程管起来
第三种是最复杂的编排型技能,也是我认为agent-skills最核心的形态。它内部包含多个子步骤,每个子步骤可能是工具调用,也可能是另一个子技能。它在做的事,就是传统软件工程里的工作流编排。
举一个我实际做的例子:一个"竞品分析"技能,内部包含:抓取指定网页内容、提取产品关键信息、对比参数表、生成分析报告四个子步骤。模型只需要选择"竞品分析"这个技能,剩下的事情由技能内部自己编排——包括每一步用什么工具、拿到中间结果后怎么判断是否继续或重试。
编排型技能的重点是把"决策点"设计清楚。不是每一步都硬编码,而是定义好在哪些节点允许模型介入做判断,哪些节点必须严格执行。我见过很多人把编排技能做得特别死板,结果失去了灵活性;也见过做得太开放,结果跑着跑着就偏了。这个平衡要靠反复调。
2.4 三种形态的对比
| 维度 | 工具型技能 | 提示词型技能 | 编排型技能 |
|---|---|---|---|
| 内部逻辑 | 确定性代码 | 模板加规则 | 多步骤流程 |
| 主要复杂度 | 参数校验、错误处理 | 触发时机、上下文控制 | 状态管理、分支决策 |
| 依赖AI程度 | 低 | 中 | 高 |
| 典型场景 | 查天气、发邮件、建工单 | 情绪安抚、话术引导 | 竞品分析、报告生成 |
| 测试难度 | 容易 | 中等 | 最难 |
这个表格是我自己在做技术选型时反复看的。三种形态没有好坏之分,核心是要匹配你的场景。如果一个操作是纯确定的,非要用编排型技能就是过度设计;如果一个复杂任务非要拆成无数个工具型技能,Agent根本编排不过来。
3. 从零搭建一个Agent技能库的完整流程
3.1 第一步:盘点你的Agent到底要做哪些事
很多人上来就开始写技能代码,这是最大的误区。我建议先花一两天做需求盘点,把Agent所有可能要承担的任务列出来,然后逐个分类:哪些是高频确定操作、哪些需要经验性话术、哪些是多步骤复杂流程。
我用的方法是写"任务卡片",每张卡片包含:任务名称、触发场景、输入信息、期望输出、可用的底层工具、失败时的兜底方案。这一步做完,你基本就能确定需要哪些技能,以及它们应该属于哪种形态。
举个例子,同样是"查快递"这个需求,如果只是查询物流轨迹,那就是工具型技能;但如果是客服场景下受理快递投诉,那就得是编排型技能,因为内部还涉及安抚情绪、判断责任方、登记工单多个环节。
3.2 第二步:定义技能的Schema
接下来是最关键的Schema设计。我经过几个版本的迭代,最终稳定下来的技能Schema如下:
{ "skill_name": "customer_order_refund", "version": "1.4.0", "description": "处理用户订单退款申请,包含资格校验、金额计算、退款执行全流程。适合用户明确表达退款意图时使用。", "domain": "customer_service", "skill_type": "orchestration", "parameters": { "order_id": { "type": "string", "pattern": "^[A-Z0-9]{12}$", "description": "订单号,通常由数字0-9和大写字母组成,共12位", "required": true }, "reason": { "type": "string", "enum": ["quality_issue", "delivery_delay", "user_change_mind", "other"], "description": "退款原因分类", "required": false } }, "steps": [ {"id": "check_eligibility", "type": "tool", "tool_name": "refund_eligibility_api"}, {"id": "calc_amount", "type": "code", "logic": "check_eligibility.total * refund_ratio"}, {"id": "confirm_with_user", "type": "llm", "prompt_template": "refund_confirm_v2"}, {"id": "execute_refund", "type": "tool", "tool_name": "refund_api"} ], "fallback": { "on_failure": "order_refund_manual_review" } }这个Schema里有几个设计要点。skill_name和description是模型识别技能的关键,描述要写清楚"何时使用"和"何时不用";parameters要严格定义类型、格式、枚举值,否则模型自由发挥的空间太大;steps是技能内部的执行逻辑;fallback则是失败时的兜底方案。
3.3 第三步:编写技能描述的艺术
技能描述是块硬骨头。我前后写过很多版本,最后总结出几个经验:
描述不要太长。模型加上所有技能的描述后,上下文已经很长了,你给每个技能写500字,模型根本读不完,匹配效果反而差。
描述要写"边界"而不是"教条"。比如"本技能用于查天气"这种描述基本没用,模型根本分不清查天气和查温度有什么不同。更好的写法是"本技能用于查询未来15天内的天气情况,包含温度、降水概率、风力等级。不适合查询历史天气、空气质量、日出日落时间,这类需求请使用climatic_data_analysis技能"。
描述要包含"关键词锚点"。我测试发现,在描述里加入常见的同义表达,匹配准确率会明显提升。比如退款技能的描述里加上"退货"、"不想要了"、"申请退款"这些词,模型更容易选中正确技能。
3.4 第四步:设计技能加载与路由
有了技能定义,下一步就是让Agent"知道"这些技能存在。这部分我建议采用分层加载思路,而不是把全量技能一次性塞给模型。
我实践下来的方案是:先做一次粗粒度检索,根据用户请求的嵌入向量从技能注册表里检索Top-K个候选技能,然后把这些候选技能的描述和参数Schema一起交给模型做最终筛选。粗粒度检索用传统BM25加向量检索的混合方案就行,不需要太复杂的算法;最终筛选才是LLM的主场,因为它能理解用户请求的语义上下文。
这个方案的好处是技能数量可以扩展到几百个而不会严重影响响应质量和速度。如果技能只有十几个,全量塞给模型是可以接受的;但一旦超过这个量级,必须做检索路由。
3.5 第五步:技能回归测试
技能库会不断迭代,所以回归测试机制非常关键。我建了一套"技能评测集",包含三类测试样本:
第一类是单技能正样本:明确属于某个技能范畴的请求,覆盖各种措辞变体。第二类是易混淆样本:两个技能边界模糊的请求,比如"这个订单为什么还没到"既可能触发物流查询也可能触发投诉受理。第三类是越界样本:不在任何技能范围内的请求,用来测试系统是否会乱触发。
每次新增或修改技能,我都会拿这套评测集跑一遍,观察匹配准确率和执行成功率的变化。这个习惯帮我避免了好几次技能更新引发的"连锁反应"——你以为只改了一个技能,结果其他技能的匹配率被带崩了。
4. 技能库在真实环境中踩过的坑
4.1 坑一:技能描述里的冗余信息造成匹配偏差
第一个踩的坑是技能描述里包含了太多"无关紧要但模型很在意"的信息。我给某个技能写了很详细的背景说明,本意是帮模型理解,结果模型把这些背景信息当成了技能适用条件——用户说得不够"正式",模型就不选这个技能。
后来我把所有跟"什么情况下触发"无关的信息全部从描述中移除,效果才恢复正常。技能描述要像API文档的summary字段一样克制,只保留边界信息,背景知识放到技能内部逻辑里,不要让模型在匹配阶段就做太多推理。
4.2 坑二:参数Schema太宽松,导致运行时错误
我还踩过一个很深的坑:参数校验做得太宽松。当时图省事,很多参数只定义类型不定义格式,结果模型把各种奇奇怪怪的格式都传进来了。比如订单号,有的传带横杠的、有的传不带横杠的、有的甚至传了订单号和商品名的拼接。
后来我在Schema里加了pattern字段,并且在技能执行前做严格的参数校验,不合法就返回错误信息让模型重新提供,这才解决了问题。这个经验是:你在Schema上偷的懒,最后都会变成执行时的故障。
4.3 坑三:技能内部状态管理容易失控
编排型技能一旦步骤变多,状态管理就容易失控。我遇到过的情况是:技能执行到第三步时发现第二步的结果异常,想回退重试,但中间状态数据已经部分更新了,搞得整个流程数据不一致。
解决思路是引入"步骤级状态隔离"——每个子步骤的输入输出都放到独立的命名空间,不直接修改上游数据;需要更新时通过显式提交实现。另外还要设计好"补偿动作",当某个步骤失败时,哪些已生效的操作需要撤销。这其实是分布式系统里的Saga模式,但在技能编排里同样适用。
4.4 坑四:模型在技能内外的"行为切换"不彻底
这是一个很微妙的问题。同一个模型,在技能内部执行时和不使用技能时,行为模式应该是不一样——技能内部应该更严谨、更结构化。但我发现,如果技能内部的提示词设计得不够强,模型经常会"跳出角色",用泛化的对话能力来"表演"技能执行,而不是真正按流程走。
比如某个技能要求从网页里提取商品价格,模型有时会直接根据上下文"猜"一个价格,而不是真的去调用提取工具。这个问题很难通过提示词完全消灭,我的经验是:在技能内部节点时,系统提示里加入"你现在处于xxx技能的yyy步骤,必须使用给定的工具获取数据,禁止根据常识推断"这样的强约束,并在执行日志里监控这类违规行为,持续调整提示词。
4.5 坑五:评测集过拟合,上线后泛化表现差
回归测试做多了,另一个问题会出现——评测集过拟合。我有一段时间疯狂根据评测集调整技能描述,把评测集准确率刷到99%,结果一到真实用户请求就掉到70%多。
原因是评测集样本太集中,技能描述里的"关键词锚点"都是从评测集里归纳的,等于变相背题。后来我重写了评测集,加入了大量从真实对话日志里抽出来的长尾表达,并且刻意控制"锚点词"和"评测集样本"的重复度,情况才好转。技能优化要把重心放在边界区分上,而不是背评测集的答案。
5. 从工具集合到能力系统的几条进阶思路
5.1 技能组合与动态编排
单个技能能力总是有限的。在技能库稳定运行一段时间后,我开始尝试让Agent在多个技能之间做动态组合。比如"新客户背景调查"这个需求,实际上需要组合"企业信息查询"、"工商数据显示"、"舆情搜索"三个技能。
实现方式不是简单的顺序调用,而是定义技能间的"输入输出契约"——前一个技能的输出结构要和后一个技能的输入参数兼容。这让我意识到,技能Schema设计时就要考虑可组合性,否则后期做编排会寸步难行。
5.2 技能自省与自我修正
另一个方向是给技能加"自省"能力。具体来说,就是在技能执行完成后,追加一个反思节点,让技能自己判断结果是否合理,如果觉得不合理,可以触发一次自动重跑或者切换子策略。
我在"报告生成"型技能里试过这个机制。模型生成完报告后,系统会让它自己检查:结论是否有数据支撑、数据是否跟原始来源一致、报告结构是否完整。如果自查发现缺失,就自动补全。实测下来,报告完整度指标提升了不少,但代价是整体耗时增加了约30%。所以自省机制最好只在低频率、高质量要求的技能里开启。
5.3 技能版本管理与灰度发布
技能也是代码,要按软件工程的规范来管理。我现在每个技能都在Git仓库里独立版本管理,Schema变更要走Pull Request评审。发布时采用灰度策略——先切5%流量观察指标,表现稳定再把比例逐步提高。
这套流程一开始被认为"太重了",但经历过一次事故后所有人都闭嘴了。那次是更新了一个高频工具的调用参数格式,没有走灰度,直接全量上线,结果所有依赖这个工具的技能都出现了执行失败,花了两个小时才回滚。技能之间的隐式依赖远比你想的多,没有灰度机制就是裸奔。
5.4 从平台化到赋能化
最后聊一个方向性的思考。技能库做到一定程度,瓶颈往往不再是技术,而是技能的生产效率。新场景出现时,你不可能每次都手动写技能。所以我在探索两条路:一是让业务人员通过"示例教学"的方式创建简单技能,系统根据几条示例自动生成技能草稿;二是让Agent在遇到未知需求时,自动生成一个新技能的原型,由人工审核后再纳入技能库。
这两条路都还没有完全跑通,但我觉得这是agent-skills这个概念最有想象力的方向——从"人给Agent造技能"进化到"Agent给自己造技能"。到那时,技能库不再是一个静态的仓库,而是一个不断自我生长的能力生态。
6. 一些写在最后的个人经验
我在使用agent-skills这套理念的过程中,最大的收获不是某个具体的技术方案,而是思维方式的转变:与其让模型在无限自由里摸索正确路径,不如给它划定合理的约束空间,然后在空间内让它充分发挥。技能库本质上就是在干这件事——把经验、流程、边界都固化下来,让模型把精力花在真正需要判断力的地方。
有一个建议特别想分享给刚开始接触这个方向的朋友:不要一上来就追求技能的通用性,也不要一上来就设计一个"万能技能"。从你业务里最高频、最确定的那件事开始,把它做成一个工具型技能,跑稳了,再慢慢扩展形态和边界。技能库里每一个技能的质量,都比你拥有的技能数量重要得多。