ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Agent开发新范式:从Prompt到技能抽象,打造高质量Skills库

Agent开发新范式:从Prompt到技能抽象,打造高质量Skills库 1. 为什么技能正在成为Agent开发的底层抽象1.1 从Prompt到Skill一次思维模型的转变我最早接触Agent开发的时候跟大多数人一样习惯把所有指令塞进一个超长的system prompt里。模型确实能听懂不少但问题也随之而来提示词一长模型就开始选择困难经常把步骤A和步骤B混淆甚至在多轮对话里反复调用同一个工具。直到有一次我管理的Agent在生产环境里连续三天误调用了一个只读接口去删除数据幸好有权限拦截我才下定决心换一种思路。那次事故让我意识到问题的根源不是模型不够聪明而是我们把该做什么和该怎么做混在了一起。Prompt描述的是意图而Skill定义的是能力边界。意图可以模糊但能力必须清晰。一个Agent如果没有明确划分好技能单元就像一个人同时拿着二十份说明书做事每一份都看似合理组合起来却不知道在执行哪一份。所谓agent-skills本质上是把Agent可执行的最小能力单元做抽象和封装每个技能包含清晰的名称、职责描述、参数协议和执行逻辑。切换到这个思维模型之后我做的第一件事就是重构现有项目把所有工具、流程和数据处理逻辑全部拆成独立的技能模块。结果非常明显模型的选择准确率上去了单次任务的平均耗时下降了出问题时的排查范围也从整个Agent缩小到了某一个技能。1.2 Skills到底长什么样我之前在社区里见过不少对Skills的误解有人以为Skills就是一段写得很详细的Prompt有人以为它是插件机制的另一个名字。实际用下来我更倾向于把Skill理解成一个带接口定义的最小工作单元它至少包含三个部分第一入口描述。这部分是给Agent也就是大模型看的它决定了模型在什么场景下觉得我应该调用这个技能。入口描述写得好不好直接影响技能被选中的准确率。我之前犯过的错误是描述写得太笼统比如处理订单数据结果模型在用户问昨天的销售额是多少时也调用了这个技能导致流程跑了一大半才报错。第二执行逻辑。这部分是给程序看的它可以是一个Python函数、一段Shell脚本、一组API调用流程甚至是一个调用另一个Agent的嵌套流程。执行逻辑的关键是要足够黑盒也就是说外部只关心输入和输出不关心内部如何实现。这样技能之间才能独立迭代不会牵一发动全身。第三返回协议。这是经常被忽略的部分。Agent调用技能之后拿到的结果必须是结构化、可解析的否则模型面对一堆杂乱文本根本没法继续推理。我见过很多项目在Skills的返回值上偷懒直接return一段字典的str()看着好像能用实际上模型经常解析失败。标准化返回格式这件事值得在项目第一天就定下来。1.3 什么样的任务适合做成Skill不是所有东西都需要变成Skill。如果一项工作只是简单的一问一答或者只需要模型自身的知识就能完成那强行套一个Skill外壳反而增加延迟和复杂度。我总结出三个判断标准基本上可以过滤掉一大半不适合的候选任务。第一个标准是可重复性。同一类操作如果每周都会出现多次就有封装的价值。比如从邮件附件里提取表格并汇总这种事情每次的输入不同但处理流程完全一致非常适合做成技能。反过来如果用户问的都是完全随机的、无法归类的问题那做技能就是白费功夫。第二个标准是确定性操作占比高。Skill适合处理那些有明确步骤、明确输入输出边界的任务。比如调用支付接口查询物流状态转换文件格式这些操作的结果是可预期的技能内部怎么实现都可以稳定交付。而像写一篇有创意的广告文案这种高度依赖模型自由发挥的任务做成Skill反而会限制模型的发挥。第三个标准是需要外部依赖。凡是涉及API调用、数据库读写、文件系统操作、第三方服务交互的任务都应该做成技能。因为这意味着Agent不能只靠想来完成任务它必须借助真实世界的工具。把这类任务做成技能等于给Agent装上手脚让它能真正对世界产生影响。我之前带过一个小团队大家刚开始热情高涨把能想到的功能全做成了技能结果技能数量膨胀到七十多个模型每次做选择都像在逛超市选出最合适那个的成功率直线下降。后来我们忍痛砍掉了将近一半只保留真正稳定、高频、边界清晰的能力效果反而立刻好转。这件事给我的教训是技能不是越多越好而是越内聚越好。2. 拆解一份高质量Skill的标准结构2.1 元信息让Agent看懂技能的关键在agent-skills的设计体系里元信息的重要性往往被新手低估。很多人把注意力放在执行逻辑上花了大量时间优化函数内部实现却对技能的名称、描述、标签毫不在意。但你得搞清楚一个事实大模型Agent的大脑根本不关心你的函数用了什么算法它只通过元信息来理解这个技能是干什么的、何时应该调用它。元信息的核心是描述字段。我建议不要只写一句话而是写一个使用场景说明书包含三个维度技能是做什么的、在什么情况下被调用、什么情况下绝对不应该被调用。听起来有点反直觉但明确写不要做什么往往能让模型的选择准确率大幅提升。比如一个汇率换算技能它的描述里就应该写用于不同货币之间的金额换算当用户询问汇率趋势、外汇市场分析时不要调用此技能。还需要注意标签的维护。标签的作用不是给人看的而是给意图路由用的。我在项目里会把技能标签设计成领域动作的组合格式比如finance.querydata.transformmedia.download。这样在Agent框架层面做初步路由时可以非常快地通过标签缩小候选技能范围减少模型在大量不相关技能中做选择的机会。2.2 执行体可被调用的真实能力执行体是技能的心脏。我这里说的是真正干活的代码逻辑它可以是同步函数、异步任务也可以是把请求转发给外部服务的消息。执行体的设计有几个原则都是我踩过坑之后总结出来的。原则一是入参即协议。定义参数的时候就要想清楚这些参数是不是模型能从用户对话里自然提取出来的。我见过一个技能定义了订单起始日期订单结束日期客户ID三个必填参数看起来很合理但模型经常无从判断客户ID从哪来最后只好硬编一个错误值传进去。更好的设计是给每个参数都写清楚来源说明和默认策略比如如果对话中未提及客户ID则使用当前登录用户的默认客户ID。原则二是内部异常必须吞掉并转换成Agent可理解的信息。执行体内部调用第三方接口超时、限流、参数错误都可能发生。如果你让异常直接抛出来模型往往只会看到一团红色报错它根本不知道用户要怎么处理。应该设计一个统一的异常输出协议把错误转换成结构化的状态码和适宜Text给模型的提示比如本次请求因上游服务超时失败可以稍后重试或检查网络配置。原则三是执行体要可重入。也就是说同一个技能被重复调用多次不能产生副作用叠加问题。我遇到过的情况是技能内部维护了一个全局状态第一次调用正常第二次调用就返回了错误结果排查了半天才发现是静态变量在作祟。改成无状态设计之后这个问题就彻底消失了。2.3 参数设计与校验参数设计是Skill开发中最容易被低估的环节。很多项目里参数被简单定义为Python函数签名然后在描述里随便写两句完全没有做格式约束。这种做法放到Agent场景下会非常痛苦因为LLM生成的参数经常出现类型偏差、格式不匹配、逻辑上合理但实际不存在的数据。我在实践中的做法是给每个Skill配一份JSON Schema这一步建议大家不要省。Schema里不仅定义类型和必填项还定义枚举值、格式模板、约束条件和示例值。比如一个创建数据看板的技能图表类型的参数就应该做成枚举取值限定为折线图、柱状图、饼图、表格而不应该让模型自由发挥。模型一旦自由发挥用户就会收到一个图例混乱、坐标轴错误的看板体验极差。校验逻辑也不能只用现成库去套Schema还必须有业务语义校验。比如一个技能要求输入一个店铺IDSchema只能保证格式是字符串但能不能查到这家店铺、账号有没有权限操作这家店铺必须在执行前做业务校验。我把这类校验统称为前置条件检查前置不通过直接返回不给执行体添乱。2.4 测试与回归Skill开发和普通后端开发有一个明显的区别普通后端你只要测试接口返回正确即可但Skill的正确性包含两层含义一层是执行逻辑本身对不对另一层是模型能不能在正确的时候选中它并传入正确的参数。我目前的做法是对技能做双轨测试。第一轨是单元测试直接调用执行体本身用构造好的数据验证逻辑正确性。第二轨是意图级回归测试模拟一组用户问题跑一遍完整的Agent流程检查技能是否被正确选中、参数是否被正确填充、最终的输出是否符合预期。第二轨非常重要因为很多问题不是出在执行逻辑而是出在模型压根没想起来调用这个技能。回归测试集需要持续积累每次线上出了问题就把复现问题和修复过程沉淀成测试用例。我在项目里会特别维护一个易混淆技能的测试分组专门放那些描述相似、容易让模型选错的技能组合。每改一次元信息就把这个分组跑一遍确保模型的选择准确率没有回退。这个过程很枯燥但非常值得做。3. agent-skills实践从零搭建一套技能库的真实过程3.1 技能目录怎么规划我刚开始做技能库的时候目录结构非常混乱所有技能文件随手堆在一个文件夹里。后来技能数量一多光是找一个技能在哪都要翻半天。经过几次重构我沉淀出一套比较稳定的目录规划方式分享出来供大家参考。首先按业务领域分一级目录而不是按功能类型分。比如一个电商机器人一级目录可以是订单商品用户营销售后五块。每个一级目录下再按交互方式分二级常见的子目录是query查询类、action操作类、subscribe订阅/监听类。这样做的好处是开发者可以一眼定位某个技能所属的业务范围而当Agent需要调用技能时也可以先通过意图判断业务领域再进入对应目录去查找具体技能减少跨领域的混乱调用。目录之上我会维护一份技能索引表用表格记录每个技能的ID、名称、所属目录、参数摘要、调用权限等级、当前版本号。这份索引表相当于技能库的通讯录新增技能时先看索引表能有效避免重复造轮子。我在实际项目里发现很多人做完一个新技能才发现早在三个月前另一个模块就实现了类似功能如果早早维护索引表这种浪费完全可以避免。3.2 命名与模块边界技能命名的好坏直接影响模型理解的准确度。我给技能命名的经验总结起来是十六个字动词开头、名词收尾、避免缩写、不做诗。比如getDailySalesReport获取每日销售报告、createRefundOrder创建退款单、uploadProductImage上传商品图片。这类命名的好处是动词部分告诉模型这个技能能带来什么变化名词部分告诉模型操作的对象是什么。避免缩写这点尤其重要。模型在训练数据里见过各种缩写但它并不知道你项目里OD指的是订单详情还是期权数据。我曾经在项目里用一个缩写技能名getOD结果模型在好几个场景下都错误地理解成获取组织架构调了三次才发现问题是技能名太暗了。改成getOrderDetail之后误调用的现象立刻消失了。关于模块边界我的原则是一个技能只做一件事并把它做到极致。如果一个技能内部的逻辑分支超过三个且有明显的场景差异就应该考虑拆分成多个技能。拆分的边界不是看代码量而是看决策点。如果一个技能在运行到一半的时候需要判断当前是B2C订单还是B2B订单那它就存在两个决策分支就应该拆成两个技能或者用一个内部路由包一层。让技能保持单一职责不仅利于模型理解也让测试和后续迭代都变得轻松。3.3 技能间的组合与上下文传递真实业务里单一技能往往搞不定一个完整任务技能之间需要组合协作。我在项目里通常采用两种组合方式一种是顺序流水线另一种是主从编排。顺序流水线适合那些步骤固定的任务比如用户申请退款Agent先调用核对订单状态技能再调用计算退款金额技能最后调用创建退款单技能一步一步往下走。主从编排则适合那些需要动态决策的任务比如一个客服机器人接到问题后先调用问题分类技能再根据分类结果调用不同的处理技能。组合带来的最核心问题是上下文传递。技能A的输出要作为技能B的输入这中间的转换链路如果处理不好信息就会在传递过程中丢失。我的做法是把每个技能的输出都统一封装成一个执行结果对象包含状态、数据、消息三个字段。状态是给编排器看的数据是给下一个技能用的消息是给模型看的。这样一来编排器能清晰判断该走哪个分支下一个技能也能从数据字段中精确取出自己需要的部分而不是靠模型去解析一坨自由文本。特别提醒一点上下文传递过程中要警惕信息过载。模型能够关注的上下文窗口是有限的如果每个技能的完整输出都无脑丢给下一个技能很快上下文就会被撑爆。我一般会在编排层加一个字段裁剪环节只保留下一个技能真正需要的字段把无关数据丢掉。这个动作看着微小但对长链路任务的稳定执行帮助极大。3.4 版本管理与依赖技能库一旦成长壮大版本管理就会成为绕不开的话题。我最初用的是简单粗暴的方式——直接覆盖代码文件线上是什么就是什么。但当我同时维护好几个Agent分别需要不同版本的技能时这种方式就彻底崩了。后来我引入了分版本技能注册机制每个技能发布时带上语义化版本号MAJOR.MINOR.PATCHAgent实例在启动时声明自己依赖哪些技能的哪个版本技能库则保留历史版本供不同Agent引用。MAJOR版本变更意味着行为可能不兼容比如参数协议变了、返回结构变了MINOR版本变更意味着增加了非破坏性的功能PATCH版本是纯Bug修复。这套机制最直接的好处是我可以放心地发布新版本因为总会有机制保证线上Agent不因为升级而意外挂掉。技能之间的依赖关系也需要显式声明。我在项目里会给每个技能配一份依赖清单写明它依赖了哪些外部服务SDK、哪些公共基础库以及内部的其他技能。这一方面是为了部署时不错过依赖项更重要的是在做回归测试时能快速评估影响面当底层公共库升级时我可以一键过滤出所有依赖它的技能全部跑一遍测试。4. 踩过的那些坑技能开发中常见问题与排查链路4.1 描述模糊导致Agent选错技能这是我在技能开发中遇到的最常见问题没有之一。具体表现是用户提了一个请求模型却调用了完全不相关的技能或者几个描述相近的技能之间反复犹豫浪费时间不说还可能做出错误操作。我记得有一次项目里同时存在导出月度报表和发送月度报表两个技能。本来边界很清楚一个负责生成文件一个负责把文件通过邮件发给指定人群。但因为导出和发送两个动词在某些语境下含义相近模型经常搞混。最无语的一次用户说把上个月的报表发给我模型先调用了导出技能生成了文件然后就没有下文了——它以为发给我这个动作已经被满足了。排查这种问题我的思路是先把技能描述打印出来模拟一次完整的意图匹配过程然后把模型犹豫的几个技能描述并排放到面前看看到底是哪里给了模型歧义信号。修复手段通常是两个方向把容易混淆的动词区分度加大或者在描述里明确写上排除条件。发送月度报表这个技能的描述里加了一句话仅负责邮件/消息发送不负责生成文件生成文件请调用导出月度报表之后误选率立刻下降了一大截。4.2 参数过多导致LLM调用混乱技能的参数设计得越多模型出错的可能性就越大。我在一个创建广告计划的技能里最初定义了二十多个参数包括各种可选项、默认值、嵌套对象。结果就是模型经常漏填参数、填错参数类型或者把一个对象的字段填到另一个对象里。后来我数了一下真正每次调用都必填的参数只有六个剩下十几个都属于低频可选项根本没有理由暴露给模型。解决问题的方法是分层参数策略。必填参数保持在七个以内能合并的分组就分组能设置默认值的就不麻烦模型来填。低频可选项可以进一步封装成配置预设也就是把几十种可选参数组合成几个预设模板模型只需要从预设模板中选一个再覆盖少量字段即可。比如创建广告计划的技能我预置了标准推广快速曝光精准转化三套模板模型选模板时轻松准确需要模型去理解的内容大幅减少。如果你遇到模型频繁传错参数的线上问题建议先不要急着改代码可以翻一下历史调用日志统计出top 5的错误参数看看是不是都是因为参数命名不直观、Schema描述不清晰或者参数本身就不该暴露出来。我个人的经验是80%的参数问题不是模型不够聪明而是我们把接口设计得对模型太不友好了。4.3 技能返回格式不符合预期技能执行成功了但Agent却给出了错误的回答这种问题排查起来比执行报错更让人头疼。它的隐蔽性在于从日志上看技能返回了200没有任何异常但后续的推理步骤全部建立在错误解析上最终输出的答案牛头不对马嘴。我遇到过的一个典型案例是一个查询库存的技能返回了一段JSON其中库存数量字段是stock_qty而另一个查询价格的技能返回的是price。本来这没什么模块内部自己定义字段很正常。问题是技能之间组合调用时库存查询的结果被用于计算订单金额而编排层的字段映射表里没有stock_qty这个字段导致算出来的金额始终是0。排查了很久才发现是返回格式在跨技能传递时没有做映射转换。从那以后我坚持两个习惯。第一个是每个技能返回数据都用统一的包装结构业务数据放在data字段内并在技能文档里用表格列出返回字段的数据字典。第二个是编排层引入显式字段映射技能A的某个字段要传给技能B的哪个字段必须写在流程图旁边作为开发文档的一部分绝对不能靠命名巧合来对接。这两个习惯帮我避免了后续大量类似问题。4.4 调试Agent调用技能的过程调试技能相关的问题最大的困扰是不确定性。同样的用户输入模型不一定会走同一条调用路径有时它会自己脑补出一步操作有时会跳过某个技能。面对这种不确定性我的调试方法可以总结成三步固定输入、打印中间链路、逐个环节打点。固定输入意味着准备一份精心设计的回归测试用例集用例要覆盖正常场景、边界场景和易混淆场景。打印中间链路则是在Agent框架中加入一个观测模块把每一轮的模型推理结果选中的技能、填写的参数、技能输出摘要全部记录下来。现在很多现成的Agent框架都有类似功能如果没有自己写一个也不难——本质上就是给技能调用入口包一层装饰器记录调用前的参数和调用后的结果。逐个环节打点的意思是如果模型选错了技能先检查意图理解环节如果模型选对了技能但参数传错了先检查元信息描述和参数Schema如果参数也对了但最终结果不对才把焦点放到执行体内部。我见过很多开发者一上来就在技能执行函数里打日志忙活半天才发现问题出在模型根本没选择这个技能。这种调试顺序的错位是时间黑洞的根源。5. 从单一技能到技能编排进阶优化方向5.1 技能路由与意图识别技能数量超过几十个之后把所有技能都塞给模型去一个个评估效率和准确率都会越来越差。这时候就需要引入二阶段路由先做粗粒度的意图分类再让模型在候选技能子集里较细粒度选择。我目前的项目里会用一个轻量级分类器做第一层路由。分类器的输入是用户原始问题 最近几轮对话摘要输出是几个可能命中的业务领域标签。模型在后续过程中只能从第一个命中的领域所对应的技能子集里做选择。这种设计的本质是把从一百个技能里选一个的难题拆成先从一百个技能里选十个再从十个里选一个每一步的难度都大大降低整体准确率反而提升。有人可能会担心增加一个路由层的延迟风险实际上只要分类器做得足够快路由层本身的耗时可以控制在几毫秒内。大部分Agent任务中一次技能的调用往往要花费几百毫秒甚至几秒涉及API调用时路由层的成本完全可以忽略。但它带来的准确率收益却能让整个Agent的行为表现产生质变。5.2 多技能协作的工作模式当任务足够复杂单个技能或简单的顺序流水线都不够用。我最近在尝试的模式是规划-拆解-执行-汇总四段式工作流这可以看作是技能编排的一个进阶形态。在规划阶段Agent先接收用户的最终目标不急着调用任何技能。在拆解阶段它把一个大型任务分解成若干子任务并为每个子任务指定一个技能。在执行阶段各技能按编排顺序依次被调用每个步骤的执行结果都被回传给编排器。在汇总阶段编排器收集所有执行结果经过裁剪和整合后交给模型生成最终答案。这种四段式工作流对技能库的要求更高每个技能都需要有清晰的假设条件、输入输出协议组合起来才有可能不出岔子。但它带来的收益也非常明显用户可以让Agent完成分析最近三个月的销售数据找出下滑最严重的产品线生成一份改进建议报告这种跨多个领域、多步骤的复杂任务而不用自己一步步指挥。我建议大家在搭建自己的技能库时不要一开始就追求这种高度自由的编排因为底层技能的质量不够硬编排越自由翻车概率越大。先跑稳顺序流水线再逐步引入规划拆解让每个环节的可靠性得到充分验证再进入下一步。5.3 持续迭代的评估机制技能库不是建好就完事的静态资产它会随着业务的变化、模型能力的变化持续演变。要支撑这种演变的稳定性必须搭一套持续评估机制这也是agent-skills工程化程度的一个重要分水岭。我在实践中维护了一套黄金评估集里面包含几百条用户问题样本每条样本都标注了应该调用哪个技能、期望收到什么格式的结果。每当技能库发生任何变化新增技能、修改描述、调整参数我都会先在本地跑一遍黄金集记录通过率和准确率的变化。如果准确率下降就立刻回滚或者修正绝不让未经评估的变更流到生产环境。这套机制的核心价值归根结底就一句话在充满不确定性的LLM调用场景里用确定性的评估集来对冲不确定性。技能库越大、Agent任务越复杂这套评估机制的收益就越明显。我个人的经验是黄金评估集的维护成本并不高每次线上出问题后补一两条用例即可但它能帮你把技能库的地基越打越牢。最后再分享一个我实践中的体会与其花大量时间调教模型更聪明地理解各种模糊表达不如花同样时间把技能的元信息写清楚、把技能的边界划分明白、把评估机制做扎实。模型的能力会随着底座升级而不断变强但一个设计良好的技能库才是你自己真正积累下来的资产。Agent可以换技能库不会一夜过时——这就是我坚持把agent-skills这件事做扎实的原因。
返回列表