☰
Agent技能体系实战:从工单处理看AI Agent如何从会聊到会干活
2026/10/14 8:41:11 网站建设 项目流程

做AI Agent落地这一年多,我最大的体会是:模型本身的聪明程度只是下限,真正拉开体验差距的,是你能不能给Agent配上顺手、可靠、可扩展的“技能体系”。“agent-skills”这件事,在圈子里讨论了很多轮,但真正把它做明白的团队并不多。大多数项目初期都忙着接大模型API、调Prompt,等到Agent开始处理真实业务时才发现——光会聊天远远不够,它得会查数据、调接口、写文件、发消息、做判断,而这些“动手能力”如果没有系统性的设计,很快就会变成一堆堆满if-else的屎山。

这篇文章就围绕“agent-skills”这个核心,把我实际做过的一个工单处理Agent的完整套路拆开讲。包括技能到底是什么、技能库怎么设计、技能描述怎么写才能让模型准确调用、Agent怎么调度一套技能完成业务闭环,以及我在真实运行中踩过的坑和排查方法。适合正在做Agent应用开发、AIGC产品原型,或者想从“会聊天的机器人”升级到“能干活的小助手”的朋友参考。

1. 先捋清楚:Agent技能到底是什么

1.1 从“会聊天”到“会干活”,中间差的是技能

大模型本质上是一个文本生成器。你给它一段输入,它给你一段输出,仅此而已。但业务系统需要的是什么?是“用户说想查一下订单,Agent自动去订单系统查数据,再把结果组织成一段人话回复过去”。这个过程中,真正干活的是“去订单系统查数据”这一步,它不是一个文本生成操作,而是一个系统调用。

所以Agent要落地,必须把“调用外部能力”这件事包装成一个个可以被模型理解和执行的单元,这个单元就叫技能。技能不等同于一个API函数,也不等同于一段Prompt,它是一套完整的“面向任务的操作封装”。一个技能里面可能包含调用多个工具的流程、必要的参数校验、上下文信息的注入、出错时的兜底策略,以及给模型看的触发条件和使用说明。

我见过很多开发者的误区,是觉得“给模型接个function calling就完事了”。function calling只是通了管道,但管道里传输什么、怎么处理业务异常、怎么让模型在多个管道之间选对,这些才是“技能体系”要解决的问题。

1.2 技能、工具、插件,别混为一谈

很多讨论把“工具”“插件”“技能”三个词混着用,但实际设计时它们是三个层次的东西。

工具是最小粒度的一个操作点,比如“发送邮件”“查询订单状态”“计算文件哈希”。它对应一个具体的函数或API,输入输出都很明确。插件是面向宿主环境的一套独立程序,通常包含多个工具、配置文件和界面样式,比如一个“PDF处理插件”可能里面有解析、合并、拆分三个工具。技能则更偏向Agent侧的抽象,它描述的是“在什么业务场景下,用哪些工具,按什么流程做成一件事”。

拿工单Agent来举例。“发送邮件”是一个工具;“自动回复客户邮件”是一个技能,它内部可能要调用“读取邮件内容”“检索知识库”“生成回复文本”“发送邮件”四个工具;“邮件办公套件”可以是一个插件,里面包含邮件读取、日程查询、联系人管理等多个工具。

1.3 一个技能实例长什么样

我给工单Agent设计的技能服务里,每个技能都有固定的结构,包括技能ID、名称、触发场景描述、输入参数定义、输出结果定义、依赖的工具清单、执行超时时间、权限级别,还有一些可选的兜底策略。注册完成后,Agent每次决策时都会读取这个技能列表,就像人看一本操作手册,然后决定调用哪个技能、怎么调用。

举个例子,“创建客诉工单”这个技能,它的输入是客户ID、问题描述、紧急程度,输出是一个工单编号。依赖的工具包括“查询客户信息”“创建工单记录”“通知客服组长”。超时时间设置为5秒,权限级别需要“工单写入”。模型在对话中识别到“用户想要投诉且问题还没有工单号”时,就会调用这个技能而不是其他技能。

这一步看起来简单,但技能设计的质量直接决定Agent后续的表现上限。我后面会详细讲怎么把技能拆好。

2. 技能库怎么设计才不乱

2.1 先盘业务,再定技能清单

很多人上手就急着写技能定义文件,这是错的。技能体系的起点不是代码,而是业务梳理。正确的做法是先把Agent要承担的任务全部列出来,再逐层分解,最后合并收敛成技能清单。

我给工单Agent做规划的时候,先列了一个任务清单:接收用户消息、识别用户意图、查询订单信息、查询物流状态、检索常见问题、起草回复、确认用户满意度、创建投诉工单、通知人工客服、归档对话记录。这些任务单独看都比较细,有的甚至只需要一次工具调用,但将它们分类后可以发现,部分任务是基础能力,部分任务是组合流程。

然后我按“原子技能”和“编排技能”两层整理。原子技能是不能再拆的基础操作,比如“查询订单信息”“查询物流状态”“发送邮件”“创建工单记录”。编排技能则是处理一个完整业务事件的大技能,比如“处理客诉”“处理退货申请”。编排技能内部会引用多个原子技能,并定义调用的先后顺序和判断条件。

2.2 原子技能与编排技能的分工

为什么要刻意区分这两个层次?因为原子技能的复用率高,编排技能的复用率低但价值密度高。查询订单信息这类原子技能,在处理客诉时用得上,在处理退货时用得上,在回答“我的东西发到哪了”时也用得上。而“处理退货申请”这个编排技能,是更多业务的组合,不适合被其他流程随意嵌套。

这种分工在工程上还有一个好处:排障容易。当某个Agent行为异常时,先看编排技能的流程逻辑对不对,再看它依赖的原子技能有没有报错,问题一下子就定位了。如果不分层,所有逻辑平铺开,每个技能里都重复写一大段查询逻辑,后期改一个接口可能要翻遍所有技能。

在实际代码层面,我建议原子技能和编排技能分开注册、分开管理。原子技能倾向于稳定,相反编排技能改动得频繁一些,因为业务流程经常调整。如果混在一个版本里管理,频繁的变更会导致原子技能的版本也反复跳动,这是我在一个项目里踩过的坑。

2.3 命名与注册规范的实践

技能命名容易被人忽略,但它直接影响模型的选择准确性。模型在选择技能时,首先看技能名称和描述文本,名称模糊、描述含糊都会导致调用错误。

我总结了一套命名规则:动词开头加对象名词,避免修辞和歧义。想“query_order”“send_email”“create_complaint_ticket”,这些一眼能看懂。避免使用“handle_stuff”“do_something”这类没有信息量的名字。同类的技能还可以加前缀区分域,比如“order_query”“order_cancel”,这样既便于人识别,也便于模型区分。

注册时每个技能都带一段元数据描述。我最终确定了一个三段式的描述模板:第一段说明技能的用途;第二段说明适合什么场景、不适合什么场景;第三段说明调用时需要注意的细节,比如“如果用户没有提供订单号,不要调用此技能,先向用户询问订单号”。

2.4 技能的版本管理与灰度发布

技能代码是业务代码的一种,它必须有版本管理。我给你一个很实际的教训:技能数量和版本迭代速度上去之后,如果没有版本管理,线上Agent会变成一头无法预测的猛兽。

我用类似Git仓库的方式管理技能定义,在技能的配置文件中加入version字段。同时做了一个简单的灰度策略:新版本技能先在10%流量上生效,观察一段时间再全量发布。比如“起草回复邮件”技能从v2.1升级到v2.2,这次改动调整了回复模板的语气,我先让它在少量会话中生效,确认没有出现异常格式化问题后,再放开到全量。

技能的回滚也同样重要。线上运行时我会保留最近三个版本的技能定义,一旦发现新版本在处理某类请求时效果明显变差,就直接切回稳定版本。这个机制帮我避免过至少三次线上事故,值得优先建设。

3. 技能定义与编写的核心细节

3.1 自然语言描述比想象中更重要

技能定义里有两类描述:一类面向模型,用自然语言;另一类面向系统,用结构化Schema。很多开发者过度关注后者,把JSON Schema写得很复杂,却把模型用的自然语言描述随意填一句。实际上,模型先读的是自然语言描述,它理解力有限,描述的好坏直接决定选择准确性。

面向模型的描述,核心是“让模型知道我什么时候应该用你”。不要堆砌技术术语,不要讲内部实现。我举一个真实例子,“查询订单状态”技能的描述,刚开始写的是“通过订单查询接口获取订单的当前状态信息,包括已创建、已支付、已发货、已完成等枚举值”。后来我改成“当用户想了解自己的订单当前处于什么阶段(下单、付款、发货、收货)时使用。如果用户只问某个商品是否支持退换货,请调用退货政策查询技能”。

对比之后会发现,改版描述多了“什么时候用”和“什么时候别用”的信息。这种边界说明非常管用,极大减少了模型选错技能的频率。我给所有技能描述贯彻这种“输入—处理—边界”的三段式写法。

3.2 输入输出Schema为什么如此重要

技能输入输出Schema是模型参数提取和结果校验的依据。我见过一个很典型的问题:技能定义里输入参数没有写“required”,结果用户说“我要投诉”,模型不知道要不要填客户ID,随便编了一个值传进来,导致下游系统查无此人。这类问题反复出现后,我把所有关键参数都标成了必填,并且增加了一步“参数完整性校验”。

在设计Schema时,要尽量让参数含义单一清晰。复杂嵌套对象不建议让模型直接生成,而是提供ID引用。比如输入参数里需要“商品信息”,尽量传递一个“商品SPU编码”字符串,而不是让模型生成一个包含品牌、型号、价格的对象。原因是模型生成多字段对象时,每一个字段都可能出错,而只生成一个编码,准确率会高得多。

输出Schema同样要克制。技能的输出会被模型用来生成最终回复,所以输出里不要塞冗余字段。我一般只保留状态码、结果主数据、可读的消息文本。

3.3 技能内部工具调用的顺序与容错

一个编排技能内部往往有多个工具调用,调用顺序必须精心安排。我处理“自动答复客户”这个编排技能时,内部顺序是:先读取原始邮件,然后检索知识库,再多轮生成回复草稿,最后发送邮件并登记日志。顺序很重要,因为每一步依赖上一步的输出。

容错设计方面,我给每一步调用都加了“失败降级”逻辑。比如检索知识库失败时,不是让整个技能失败,而是降级为“仅根据订单信息生成回复,并附上咨询人工客服的提示”。这样可以减少因单个工具故障导致技能整体不可用的情况。

另外,工具调用一定要设置超时。一次邮件发送API卡住10秒,整个Agent对话就会卡死,体验极差。我把技能内部单次工具调用的超时时间设置在3到5秒之间,超时后记录状态并进入降级分支。

3.4 技能的上下文管理

技能在执行时需要哪些上下文,这是一个容易被忽略但影响很大的点。上下文传多了,模型容易混淆,也浪费Token;传少了,技能拿不到必要信息,没法完成任务。

我的做法是将上下文分为两层:一层是会话上下文(用户ID、会话ID、历史对话摘要),另一层是业务上下文(当前订单编号、当前客户级别等)。业务上下文是动态变化的,需要在特定的对话节点主动写入。比如用户前一句说了“订单号是12345”,那么业务上下文中就缓存“current_order_id=12345”,后续调用任何订单类技能时,都优先从业务上下文里取这个编号,而不是让模型每次重新提取。

这个设计在实际运行中帮助非常大。没有业务上下文缓存之前,模型经常在一段对话中重复追问订单号,明明用户已经说过了。加了之后,这类追问的频率降低了90%以上,体验提升非常明显。

4. 技能如何被Agent正确调度

4.1 三种常见调度模式

技能注册好之后,关键问题来了:Agent怎么在合适的时机选中合适的技能,并且正确执行?这背后是调度机制的设计。

我实践下来最常见的有三种模式。第一种是意图路由:由上游的意图分类模块先判断用户的意图,再映射到固定的技能。它适合业务流程非常确定的场景,比如“用户说要退款”直接路由到“处理退款”技能。第二种是模型自主选择:把技能列表全部灌给大模型,让模型基于描述自己挑一个技能。它灵活,适合开放域对话,但挑选理由不透明。第三种是混合模式:先做意图粗分类,缩小技能候选范围,再由模型从候选范围里做精准选择。

我在工单Agent上最终采用的是混合模式。先通过一个轻量级意图分类器判断大方向(查询类、投诉类、退换货类、闲聊类),将大方向对应的技能候选集缩小到3到5个,然后由大模型从这3到5个里挑一个。这样可以显著降低技能数量过多时的选错率。实测下来,直接让模型从30多个技能里做选择,准确率大概80%出头;先在10个以内选择,准确率可以到95%以上。

4.2 技能候选中发生冲突怎么办

混合模式引入一个新的问题:候选技能里如果多个技能看起来都沾边,模型就会犹豫。比如“查询订单状态”和“查询物流进度”,在很多对话里确实很接近。我的处理方式是给每个技能增加“优先级”字段和“互斥规则”字段。两个技能同时匹配时,优先选择优先级高且互斥规则更贴合的一个。如果模型犹豫不决,Agent系统还可以主动向用户反问澄清,比如“你是想查看订单目前的状态,还是看快递到哪一步了”。

其实这里有一个更简单且很有效的策略:在技能描述里把边界写清楚。物流进度技能描述里写明“当用户想了解包裹目前到达哪个城市、由哪家快递配送时使用”;订单状态技能写明“当用户想了解订单是否被接受、是否发货、是否完成等生命周期状态时使用。”边界清晰后,冲突率会大幅下降。

4.3 执行链的记录与反馈

Agent每完成一次技能调用,系统都应该把完整链路记录下来,包括选中了哪个技能、填了哪些参数、调用了哪些工具、每次调用耗时多少、结果是否正确。这条链路日志是后续优化技能体系最重要的素材。

我设计和跑数据时,会定期统计几个指标:技能调用成功率、技能平均耗时、技能参数缺失率、模型选错技能的次数。分析日志时发现“处理退款”技能调用成功率只有70%左右,点进去看链路后定位到它依赖的“退款资格校验”工具频繁超时,最终通过优化工具重试策略把成功率拉到了95%。

4.4 一个工单场景的完整调度演示

具体到一次工单处理,调度过程是这样的。用户发来一句“我上周买的东西一直显示未发货,想问问怎么回事”。意图分类器判断这是“订单查询+物流咨询”方向,将候选技能圈定为“查询订单状态”“查询物流进度”“查询当前优惠活动”。

模型阅读三个技能的描述后,发现用户关心的是“一直显示未发货”,于是选择“查询订单状态”技能,并提取参数(订单号在此前对话中已有,从业务上下文直接取)。技能内部调用订单查询工具,返回“订单已支付,仓库备货中”。Agent再结合发货时效规则,组织成一句回答:“您的订单已经完成支付,目前仓库正在备货。正常情况下会在48小时内发货。”

整个过程不需要用户重复提供订单号,也不需要人工介入。从整体来看,一次完整的工单处理最多会串起三到四个技能调用,链路日志清晰记录了每一环。这种可复现、可观测的调度过程,才算是合格的Agent技能体系。

5. 踩坑实录与排查技巧

5.1 技能描述不精确导致Agent选错技能

最近发现的一个问题是:“处理客诉”和“处理一般咨询”两个技能经常被搞混。用户说“我要投诉你们发货太慢”,Agent却选择“一般咨询”技能,给出了一段不痛不痒的自动回复。排查后发现两个技能的前半段描述都写了“用户反馈问题时使用”,模型没有足够的信息做区分。

解决办法是把两个技能的描述改成互斥的写法:“处理客诉”强调触发条件是“用户表达不满、要求赔偿、计划投诉”;“处理一般咨询”强调适用普通咨询、订单查询、预约业务。同时,我在系统层增加规则:如果用户消息中出现投诉关键词,优先选“处理客诉”技能。

5.2 参数Schema太娇气,模型提取不稳定

我曾在一个技能里把“订单编号”的正则写得非常死板,要求纯数字且必须是12位。实际运行中,用户经常输入“订单号是abc123”或者“单号12345678”,模型提取时按着模式去匹配,结果经常少一位或多一位。

后来我调整了Schema:字段改成宽松字符串类型,加上更长范围的长度校验,在系统层做后置清洗,把非数字字符过滤掉再进行查询。这个改动让参数提取成功率提升了不少。核心教训是:面向模型的Schema要宽松,面向业务系统的SQL和API可以严格,中间的清洗逻辑放到技能内部代码里做,而不是要求模型一步到位。

5.3 上下文被技能调用刷爆

有一次我在线观察Agent处理复杂客诉,发现一开始还很正常,聊到后面回复速度越来越慢,Token消耗也越来越大。检查链路日志后发现,每次技能调用都把所有历史对话塞进上下文,调用五六次后文本越来越长,模型处理越来越慢。

解决方案是分离全局上下文与局部上下文。全局上下文只保存一段适量摘要和关键业务信息;局部上下文只保留当前技能执行那几步的中间输出。执行完一个技能后,把技能的关键结果写入业务上下文,把原始细节丢到日志存储中。这样每轮对话的模型输入长度不会随技能调用次数膨胀,速度和成本都恢复了正常。

5.4 技能执行超时与重试风暴

还有一个经典问题:某个查询工具突然响应变慢,技能内部设置的自动重试机制被触发,不仅连续重试三次,还因为重试期间用户又发来新消息,导致并发调用暴涨,直接把后端数据库压垮。这是一次典型的重试风暴。

之后我给所有技能调用加了三层保护:第一层,单次工具调用设置了超时时间,超时后先降级处理,不立即重试;第二层,重试采用指数退避策略,最多重试两次;第三层,一个技能同时只允许一定数量的并发调用,超出部分直接进入待处理队列。加了限流之后,即使下游系统抖动,也不会因为Agent的重试行为引发雪崩。

5.5 几个长期好用的开发小技巧

最后分享几个我自己长期用的小技巧。第一个是在技能库里保留一个测试技能“echo_test”,它不实际调用任何业务服务,只回显输入参数。这个技能用来验证Agent是否能正确提取参数格式,排查问题特别好用。第二个是给每个技能增加一个“维护者备注”字段,哪位修改、改了什么、为什么改,写清楚。几个月后回头查问题时会特别感谢这个字段。第三个是定期做技能裁剪。运行一段时间后,会有一批技能长期没被调用,要么说明业务已经不适用,要么说明它被别的技能抢了。我会区分配置里的last_called_time字段,超过一个月没被调用的技能就下线,避免技能列表越积越臃肿。

我在实际运行中深刻体会到,技能库更像一个活的业务产品,要持续迭代、持续清理、持续观测,不能觉得定义好就丢在那里不管了。

结尾

做完这个工单Agent的技能体系之后,我最大的感受是:Agent真正的竞争力不在模型选得多强,而在于技能库的厚度和工程质量。技能设计得好,一个很小的模型也能把事情办得稳妥;技能设计得乱,再大的模型也会在关键时刻掉链子。如果你正在做类似的事情,我建议先别急着写代码,拿一张纸把业务任务拆清楚,给每个任务赋予清晰的边界和可靠的执行路径。磨刀不误砍柴工,把技能梳理清楚再让Agent上场,你会省掉后面大量的返工时间。

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

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

立即咨询