ARTICLE DETAIL

资讯详情

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

Agent技能体系实战:让大模型从会说到会做

Agent技能体系实战:让大模型从会说到会做 圈子里聊“agent-skills”的人越来越多了。去年大家还在纠结怎么调Prompt、怎么让大模型输出稳定的JSON今年话题已经变成了“怎么让模型真正把活干成”。所谓agent-skills说白了就是给智能体配置一套可调用的技能体系让模型不再只是“会说话”而是“会做事”——查数据、调接口、写文件、发消息、操作浏览器这些动作都封装成技能由模型在对话过程中自主决定什么时候调用、调用哪个。这篇东西我从设计思路、核心实现到踩坑经验完整梳理一遍适合正在做智能体应用、想搞清楚技能体系怎么落地的人看。1. agent-skills解决什么问题——先从大模型的边界说起1.1 模型的“知识”和“行动”之间隔着一条河很多人第一次做大模型应用都会产生一种错觉既然GPT级别的大模型什么都懂那让它帮我操作软件、处理业务、完成复杂任务是不是直接对话就行实际跑通一个端到端的任务之后你会发现差距非常大。大模型本质上是一个概率性的文本生成器它的强项是“理解”和“生成”弱项恰恰是“确定性执行”。你问它“帮我查一下上海明天天气”它能给你描述一段“上海明天多云转阴、气温22到28度”的合理文本但这段文本完全可能是编的。它没有真的去气象接口拉数据也不会因为你问了一句就去触发任何外部动作。这就是模型“知识”和“行动”之间的河知识来自参数记忆行动需要真实世界的交互。而且模型的知识有截止日期内部参数里存的是训练阶段的“记忆”不是实时的、结构化的业务数据。你让它处理你企业内部的订单、库存、客户信息它连你的数据库在哪都不知道。更别说写文件、发邮件、调用内部API这些具体操作没有一套机制把“意图”翻译成“可执行的调用”大模型永远只能停留在“嘴上说说”。1.2 技能体系让模型从“会说”到“会做”agent-skills解决的就是这条河上“架桥”的问题。它的核心思路是把外部世界的能力抽象成一个个带描述、带参数规范、带执行逻辑的“技能”大模型在推理时先看到“我有哪些技能可以用”当用户的请求命中某个技能时模型输出一个结构化的调用指令比如“调用查询天气技能参数city上海, date2025-07-15”然后由系统层去执行这个调用把结果拿回来交给模型继续加工。这样一来模型负责的部分是“判断用哪个技能、传什么参数、怎么解读结果”外部系统负责的是“真正把这个动作做了”。模型还是那个模型但它的能力边界被技能体系大大扩展了——你能封装多少技能它就能干多少种活。我在实际项目中体会很深的一点是技能体系带来的不只是“能调用工具”而是把大模型从“单轮闲聊”推向“多步骤任务执行”。当技能足够多、编排逻辑足够清晰时一个复杂的业务需求可以被拆解成多个技能的序列调用。比如“帮我把这批客户的订单状态导出成报表并发给运营组”需要检索客户数据、获取订单状态、生成报表文件、搜索收件人、发送邮件五个动作没有技能体系这五个动作一个都落不了地。1.3 谁需要关心agent-skills如果你正在构建任何形式的智能助手、自动化工作流、客服机器人、企业内部知识问答系统或者想在LLM之上做业务自动化那么技能体系是你绕不开的环节。哪怕你只是用现成的Agent框架底层跑的还是同一套技能注册与调用的逻辑。搞清楚它的设计原理你才能知道为什么有些场景跑得好、有些场景天天出错。反过来如果你只是做文本摘要、翻译、情感分析这一类“模型自身能力范围之内”的小工具技能体系暂时不是必需品但提前理解它对你后续做更复杂的应用也是有帮助的。2. 技能体系的核心设计——不先想清楚架构写代码就是白费2.1 技能的四层分类感知、操作、认知、记忆很多初学者以为技能就是把API包一层就完事。实际上一个成熟的agent-skills体系技能至少要覆盖四个层次。感知类技能让模型获得“眼睛”比如联网搜索、读取网页内容、调取摄像头/传感器数据、接收文件上传内容。这类技能的特点是把外部信息拉入模型上下文让模型基于真实数据而不是记忆做判断。操作类技能让模型拥有“手”比如写文件、改数据库、发邮件、调用业务API、点击界面控件。这类技能是真正对外部世界产生影响的动作也是风险最需要控制的类型。认知类技能让模型拥有“思维能力的外挂”比如调用一个专门的知识图谱查询模块、跑一个数学计算引擎、调用一个向量检索库做语义搜索。这些能力模型本身有但不擅长封装成技能后能极大提升准确率。记忆类技能让模型拥有“长期记忆”比如往向量数据库写入对话摘要、读取用户的历史偏好、跨会话保持业务状态。没有这类技能你会发现模型像个“金鱼”每次对话都从头开始。分清楚层次最大的好处是你做权限设计、资源控制、异常处理时思路会非常清晰。比如操作类技能必须加审批流感知类技能要控制调用频率记忆类技能要做数据隔离。混在一起设计后期管理难度会成倍上升。2.2 一个技能的标准结构描述、参数、执行、回传我在项目里把每个技能抽象成四个组成部分缺一不可。描述用一两句话说明这个技能是干什么的什么场景下用。这是模型判断“要不要调用”的依据描述写得含糊模型就会选错技能。参数规范定义技能需要哪些入参每个参数的类型、取值范围、是否必填。最常用的格式是JSON Schema模型在推理时严格按照这个规范输出参数。执行逻辑真正运行技能的程序代码。它接收模型传来的参数做校验、调用外部系统、处理返回结果。回传格式技能执行完后返回给模型的结果结构。返回的内容要结构化、简洁、信息密度高避免把一堆无关数据丢给模型。这四个部分缺一个技能链路的可靠性就会断一截。实操中我见过太多人只定义了“描述函数体”参数规范用一句话带过回传结果扔一堆原始JSON结果模型要么传错参数要么被冗余数据干扰判断。2.3 设计技能时的三个原则原则一技能粒度要“原子化”。每个技能只做一件具体的事不要搞“万能技能”。比如“处理订单”是一个危险的反模式应该拆成“查询订单状态”“修改订单备注”“取消订单”“导出订单列表”。“处理订单”这四个字模型无法可靠地决定参数和动作边界。原则二技能描述要面向“模型”写而不是面向“人类”写。人类的技能清单可以写“本函数用于查询订单信息”但给模型看的好描述是“当用户询问订单的当前状态、物流进度、预计送达时间时使用本工具输入参数order_id为用户提供的订单编号”。要写清楚触发条件、典型场景和参数来源。原则三回传结果要“为下一步推理铺路”。技能返回给模型的内容不只是给用户看的更重要的是让模型决定下一步怎么办。所以尽量返回结构化数据比如JSON并且附带必要的状态标识如成功/失败、数据条数方便模型做分支判断。2.4 技能注册表与全局编排当技能数量超过十几个之后每一轮对话都把所有技能描述塞给模型是不现实的Token消耗和意图识别准确率都会出问题。这时候需要一个技能注册表集中管理所有技能的元信息并支持按需加载。我的做法是设计一个双层调度第一层是“预筛选”根据用户当前请求的关键词、会话历史、业务上下文从注册表里筛出可能与本次请求相关的5到10个技能第二层是“全量推理”把筛选出的技能描述、参数Schema传给模型让模型做最终选择。这个策略能在技能总量很多时保持响应速度和准确率。后面的实操部分我会给出具体的筛选逻辑参考。3. 从零搭建技能系统——定义、注册、调用的完整实操3.1 第一步定义技能描述一个好的技能描述长什么样我直接用一个实际案例演示。假设我们要做一个内部订单助手第一个技能是查询订单状态。这个技能面向模型的描述我建议写成下面这样技能名称: 查询订单状态 技能描述: 当用户查询订单的当前状态、物流进度、签收情况、发货时间时调用该技能。 用户可能用口语化表达例如我的订单到哪了发货了没什么时候送到。 该技能根据订单号查询业务系统返回订单状态和物流轨迹。 如果用户提供了多个订单号只查询第一个。 如果用户没有提供订单号请先向用户询问订单号。为什么要写得这么啰嗦因为模型的意图识别完全依赖这段文字。你写“查询订单”模型会在用户问“我买的东西什么时候到”时不敢确认该不该调用。而你写了“什么场景用、口语怎么表达、拿不到参数怎么办”模型的判断成功率会从70%左右直接提升到95%以上。这一段是我测试了大量项目后总结出的经验值。3.2 第二步用JSON Schema约束参数参数规范我强烈建议直接上JSON Schema因为主流大模型OpenAI、Anthropic、通义、文心等的function calling接口都原生支持这个格式。还以查询订单为例{ name: query_order_status, description: 当用户查询订单的当前状态、物流进度、签收情况、发货时间时调用该技能。如果用户没有提供订单号请先向用户询问订单号。, parameters: { type: object, properties: { order_id: { type: string, description: 用户的订单编号通常是字母和数字的组合例如 ORD20250715001 }, customer_name: { type: string, description: 下单时留的收货人姓名可用来辅助校验订单归属非必填 } }, required: [order_id] } }参数定义有几个关键点。每个字段的description要写清楚“值从哪里来”比如“order_id通常是ORD开头的一串字符”模型抽取实体时更准确。required不要贪多只把真正必须的字段设为必填减少模型因为缺参数而放弃调用的情况。其实customer_name非必填就是一个实用设计用户可能只记得订单号不记得收货人姓名必填的话调用直接失败。3.3 第三步注册技能到模型注册这一步取决于你用什么入口。以OpenAI的Chat Completions接口为例把上面的JSON Schema放进tools数组即可tools [ { type: function, function: { name: query_order_status, description: 查询订单/物流状态的技能当用户询问订单到哪了、发货没、什么时候送到时使用, parameters: { type: object, properties: { order_id: { type: string, description: 订单号如 ORD20250715001 } }, required: [order_id] } } } ] response openai.chat.completions.create( modelgpt-4o, messagesuser_messages, toolstools, tool_choiceauto )注意当tools数组里传入了多个技能时模型可能会选择不调用任何工具也可能连续调用多个工具。这是正常行为你的代码要能处理这两种情况。tool_choiceauto是告诉模型“你自己判断”如果某个场景你希望模型必须调工具可以设成tool_choice{type: function, function: {name: query_order_status}}强制指定。3.4 第四步技能执行与结果回传当模型返回一个tool_calls请求时你的代码需要解析出技能名称和参数然后去执行对应的函数最后把结果作为一条roletool的消息回传给模型。这里有一个循环直到模型不再要求调用工具为止。伪代码如下while True: response client.chat.completions.create( modelgpt-4o, messagesmessages, toolstools ) message response.choices[0].message if not message.tool_calls: # 模型没有要求调用技能说明任务完成或者信息不足直接返回给用户 final_reply message.content break # 模型要求调用技能执行并回传 for tool_call in message.tool_calls: function_name tool_call.function.name function_args json.loads(tool_call.function.arguments) if function_name query_order_status: result execute_query_order_status(function_args) else: result {error: f未知技能: {function_name}} messages.append({ role: tool, tool_call_id: tool_call.id, content: json.dumps(result, ensure_asciiFalse) })这个循环是整个agent-skill的核心骨架你把它弄通了剩下的事情就是不断往tools列表里增加技能而已。需要注意每次模型返回tool_calls后你必须把那个assistant消息也append进messages再回传不然接口会报错因为OpenAI要求tool消息必须关联到某个tool_call_id。3.5 多技能协作的编排逻辑当一次任务需要多个技能配合比如“查询订单→如果已签收则发送感谢邮件→把结果写入跟进记录”这个“编排”工作谁来做最佳实践是不要用硬编码的流程去编排让模型基于技能描述自主编排。你需要做的只是在用户消息里把目标说清楚模型自然会在多轮tool调用中按顺序调用多个技能。前提是你的每个技能描述里写清楚了前置条件和后续建议。比如“发送邮件”这个技能描述里可以写“如果需要发送邮件但还没有收件人邮箱先调用查询客户信息技能获取邮箱”。自主编排虽然灵活但有失控风险。如果业务链路是强约束的比如必须先审批才能付款你对关键环节要用强制校验最好在技能执行逻辑里做状态校验而不是完全依赖模型自觉。混合模式是日常简单任务靠模型自主编排核心敏感操作靠系统层强校验和人工审批兜底。4. 工具选型——自己写还是用框架取决于你的场景4.1 自研轻量级技能系统的适用场景如果你的需求是“给大模型接三五个内部API”我建议直接自研。理由很简单主流框架的抽象层级高调试链路长一个小功能要理解它好几层概念。而自研只要维护一个tools列表和一个执行循环代码量不到两百行所有的逻辑都在你掌控之中出了问题一眼就能定位。我第一版agent-skill系统就是自研的把公司内部的订单、库存、客户三个系统的API封装成技能总共维护了十二个函数跑了一个季度没出过大问题。这个阶段根本不需要引入重型框架。自研时注意点技能执行函数要写单元测试尤其是参数校验部分以及技能的JSON Schema要集中管理不要散落在各个函数文件里方便后续做自动生成和校验。4.2 主流Agent框架什么时候值得换当技能数量上到几十个需要处理复杂多轮任务、记忆机制、子Agent调度时自研就有点吃力了。这时候可以考虑现成的Agent框架。我自己调研了几个主流方案LangChain的tool装饰器配合AgentExecutor能比较快地搭建原型生态成熟但抽象层次多很多隐性行为需要读源码才能搞明白。AutoGen的多Agent对话模式适合做需要多角色协同的场景但它的消息流转机制有一定学习成本。字节的Coze和Dify这类平台化产品则适合不想写代码的团队它们在界面上配置技能和工作流但灵活性受平台能力上限约束。这里给一个基于经验的选型参考表场景特征推荐方案理由技能少于10个、内部API为主自研几百行代码可控性强、无框架学习成本技能多、需要记忆与检索LangChain / LlamaIndex生态完善、内置向量存储需要多角色协同推理AutoGen多Agent对话模式适合复杂决策业务团队自己配置Coze / Dify可视化编排、低代码上手快强一致性校验、合规敏感场景自研人工审批节点框架的自主性反而不利于强管控说实话框架不是银弹。我接触过不少项目用了LangChain最后发现大量时间花在理解框架内部的callback机制和模块之间的隐式协作上反而是在自研的简易循环上迭代更快。除非你的团队已经很熟悉某个框架不然“够用就好”是更务实的原则。4.3 技能数量与上下文消耗的计算这是很多人忽略的一个硬指标。每个技能的描述加参数Schema平均消耗约200到400个Token。如果你一口气把50个技能全塞给模型仅技能描述就消耗掉1到2万Token这还没算对话历史和工具返回结果。以4K上下文窗口为例保守起见当3K可用50个技能光注册表就爆了。所以必须做技能筛选。我常用的一个策略是用一个轻量级模型比如更小更快的那种先对用户意图做分类从技能注册表里捞Top K个候选技能再把这K个技能的完整描述传给主力模型来决策。K通常取5到8个兼顾覆盖率和Token成本。另外要注意工具返回结果也是一笔大的Token开销。查询一个订单如果返回几十条物流轨迹可能就吃掉一两千Token。我的做法是回传给模型前做截断只保留最近N条轨迹并加一条汇总字段既省Token又减少干扰。4.4 MCP与技能标准化趋势最近一个值得关注的动向是MCPModel Context Protocol模型上下文协议它本质上是为了解决“技能定义格式不统一、不同平台之间难以互通”的问题。之前你在OpenAI上写技能用OpenAI的格式迁移到Anthropic又得重写一套每个平台各搞各的。MCP的出现让技能可以按统一协议暴露出来Agent只要支持MCP客户端就能发现并调用远程技能类似“技能的USB-C接口”。我的建议是新项目可以关注MCP的发展如果已经有成熟的MCP Server可以复用直接接入比自己封装一遍省太多事。今年我看到不少主流Agent框架都已经原生支持MCP协议接入未来技能互相复用会越来越便利。不过对于内部核心技能还是建议保留自研方案毕竟关键业务逻辑和协议解耦才能保证稳定可控。5. 常见问题与排查技巧实录5.1 问题一模型选错了技能这是最让人头疼的问题明明技能A更适合模型偏偏调了技能B。排查步骤从粗到细先看技能描述的质量。描述里有没有覆盖用户的典型口语化表达如果没有模型会倾向选择一个名字看着顺眼的技能。其次看技能之间是否存在边界重叠。比如“查询订单”和“查询物流”如果描述都没写清楚适用场景模型很难分辨。我的做法是写技能描述时明确写“如果用户关注的是签收时间和物流轨迹使用技能A如果用户关注的是商品信息和金额使用技能B”。这是最笨但最有效的办法。5.2 问题二参数幻觉模型经常会编造一个看起来合理的参数值尤其是用户消息里没有直接给出参数时。用户说“帮我查订单”模型可能直接生成order_idORD20250715001这种假值而你真实系统里根本没有这个订单。处理办法参数校验是第一道防线执行函数里必须有严格校验查无此单就返回明确错误。更重要的是技能描述里要写清楚“如果用户没有提供订单号请先向用户询问订单号不要猜测”。如果你发现模型屡教不改可以在系统提示词里加一条硬性规则“禁止猜测任何用户未提供的业务参数参数缺失时必须主动询问并在对话中说明缺失项。”5.3 问题三技能执行失败之后的恢复策略技能调用失败了比如数据库超时、第三方接口报错模型该怎么应对最朴素的错误处理是让执行函数返回一个带error字段的JSON模型看到后会自己想办法重试一次换个技能还是直接告诉用户“系统暂时不可用”实测下来如果你把错误信息交给模型让它自主决策大部分情况是好的。但有个坑模型可能陷入无意义的连续重试白白浪费时间。解决方案是在错误返回里明确说明“该错误可能为暂时性故障最多重试一次如果再次失败请告知用户稍后再试”。执行函数里同时要设置超时机制我这里通常设5秒超时避免一次调用卡住整个循环。5.4 问题四上下文越来越长、Token失控多技能长任务跑起来对话历史里既有工具定义、模型调用记录又有每次技能返回的长结果Token以肉眼可见的速度飙升。我的习惯是给每次技能返回设定一个上限比如超过500字的返回结果先截断再回传。另外定期压缩历史把前面的多轮对话摘要成一条精简记录。技能执行过程本身不需要用户感知的可以直接不回传给前端展示。5.5 问题五死循环与重试风暴有一种经典故障模型反复调用某个技能每次的结果都表明操作失败但模型不死心一直重试。比如调用发送邮件的技能第一次超时失败第二次返回失败第三次又失败……直到把调用额度烧光。针对这类问题我这里设置了三个保护机制。第一个是调用次数熔断单个会话内每个技能最多调用3次超过直接返回“技能调用次数已达上限”。第二个是时间护栏每个技能执行队列限制总时长跑超了就停止。第三个是规则兜底在系统提示词里写“如果某个技能连续两次执行失败请务必停止调用该技能直接向用户报告错误并提供替代方案。”这三层保护加下来重试风暴基本能挡死。我整理了一个速查表平时排查时对照着看症状可能原因排查与解法模型不调用技能技能描述不明确模型没识别到意图重写描述覆盖更多口语表达与场景关键词调用了错误技能技能边界重叠、描述缺乏区分在描述中显式说明“A管什么、B管什么”参数乱填参数描述未说明“值从哪里来”每个参数描述里写清楚来源询问缺失参数而不是猜技能返回后模型答不上来返回内容被截断或信息密度低增加汇总字段结构化精简返回反复调用同一技能缺少执行失败后的止损机制加次数熔断、连续失败强制停止策略上下文爆了技能描述全量塞入返回结果过长做技能预筛选、返回截断、历史摘要5.6 一个真实的排查案例说一个我上个月处理的实际问题。一个客服问答Agent技能体系里有查询订单、查询退换货进度、查询售后记录三个技能用户问“我的退款到哪一步了”模型却调用了查询订单导致返回结果里根本没有退款字段模型就开始编答案。排查时我先看了三个技能的描述。“查询订单”写的是“查询订单状态”“查询退款进度”写的是“查询退款状态”看起来没问题但问题出在“退款”这个关键词在用户的表达里既可能关联订单也可能关联售后模型在两个技能里都看到了“退款”相关的描述被搞混了。修复方式是给每个技能增加明确的“负面触发条件”在“查询订单”的描述里加上“如果用户询问退款、售后退货进度不要使用本技能”在“查询退款进度”里写清楚“退款进度包括买家申请的退款、卖家同意的退款、平台介入的处理。用户说退款到哪了钱什么时候退时使用本技能”。改完描述后一天之内误调用率从12%降到了1%以下。这个案例给我的教训是技能描述不只是“写清楚”更是“写清楚界限”。正面描述覆盖触发场景负面描述排除易混淆场景才是一个完整合格的技能文案。6. 落地的两条小经验和一条建议技能体系上线不是终点跑完两周就会发现用户的实际表达总比你想的更刁钻。我自己常备一个“误触发记录表”每个技能都记录它被误调用的场景每两周围绕这些记录去补描述里的边界词。这个方式一开始费点功夫但持续做下来技能调用的准确率会越来越稳定属于投入产出比很高的维护手段。另外一个实用的小技巧是给技能加上版本号改描述时不要原地覆盖而是新版本、旧版本并存一段时间用线上日志对比新旧两版在技能选择准确率上的差别。这个办法温和稳定不会出现“改完反而变差”退不回去的局面。如果你正准备开始做agent-skills我个人的建议是先选三个最有业务价值的动作封装成技能跑通“调用—执行—回传—再推理”的完整闭环而不是一上来就追求技能数量的齐全。技能体系的价值靠“准确性”而不是“数量”来体现十条不准确的技能还不如三条精准的技能。先把链路跑顺、把边界摸清后续再扩张你会顺手很多。
返回列表