☰
Agent开发新范式:从Prompt到技能抽象,打造高质量Skills库
2026/10/8 13:40:47 网站建设 项目流程

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.query""data.transform""media.download"。这样在Agent框架层面做初步路由时,可以非常快地通过标签缩小候选技能范围,减少模型在大量不相关技能中做选择的机会。

2.2 执行体:可被调用的真实能力

执行体是技能的"心脏"。我这里说的是真正干活的代码逻辑,它可以是同步函数、异步任务,也可以是把请求转发给外部服务的消息。执行体的设计有几个原则,都是我踩过坑之后总结出来的。

原则一是"入参即协议"。定义参数的时候就要想清楚,这些参数是不是模型能从用户对话里自然提取出来的。我见过一个技能定义了"订单起始日期""订单结束日期""客户ID"三个必填参数,看起来很合理,但模型经常无从判断"客户ID"从哪来,最后只好硬编一个错误值传进去。更好的设计,是给每个参数都写清楚来源说明和默认策略,比如"如果对话中未提及客户ID,则使用当前登录用户的默认客户ID"。

原则二是"内部异常必须吞掉并转换成Agent可理解的信息"。执行体内部调用第三方接口,超时、限流、参数错误都可能发生。如果你让异常直接抛出来,模型往往只会看到一团红色报错,它根本不知道用户要怎么处理。应该设计一个统一的异常输出协议,把错误转换成结构化的状态码和适宜Text给模型的提示,比如"本次请求因上游服务超时失败,可以稍后重试,或检查网络配置"。

原则三是"执行体要可重入"。也就是说,同一个技能被重复调用多次,不能产生副作用叠加问题。我遇到过的情况是,技能内部维护了一个全局状态,第一次调用正常,第二次调用就返回了错误结果,排查了半天才发现是静态变量在作祟。改成无状态设计之后,这个问题就彻底消失了。

2.3 参数设计与校验

参数设计是Skill开发中最容易被低估的环节。很多项目里,参数被简单定义为Python函数签名,然后在描述里随便写两句,完全没有做格式约束。这种做法放到Agent场景下会非常痛苦,因为LLM生成的参数经常出现类型偏差、格式不匹配、逻辑上合理但实际不存在的数据。

我在实践中的做法是,给每个Skill配一份JSON Schema,这一步建议大家不要省。Schema里不仅定义类型和必填项,还定义枚举值、格式模板、约束条件和示例值。比如一个"创建数据看板"的技能,图表类型的参数就应该做成枚举,取值限定为"折线图、柱状图、饼图、表格",而不应该让模型自由发挥。模型一旦自由发挥,用户就会收到一个图例混乱、坐标轴错误的看板,体验极差。

校验逻辑也不能只用现成库去套Schema,还必须有"业务语义校验"。比如一个技能要求输入一个店铺ID,Schema只能保证格式是字符串,但能不能查到这家店铺、账号有没有权限操作这家店铺,必须在执行前做业务校验。我把这类校验统称为"前置条件检查",前置不通过直接返回,不给执行体添乱。

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.PATCH),Agent实例在启动时声明自己依赖哪些技能的哪个版本,技能库则保留历史版本供不同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这件事做扎实的原因。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询