ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

Agent技能体系设计实战:从工具调用到技能编排

2026/9/26 0:20:16 拓冰建站 浏览量
Agent技能体系设计实战:从工具调用到技能编排 1. agent-skills到底在解决什么问题最近大半年我一直在折腾基于LLM的Agent项目陆陆续续接手过几套不同团队留下的代码基座发现一个非常普遍的现象大家在初期Demo阶段跑得飞快各种花哨的Agent能力都能演示出来但只要一进入真实业务场景立刻被一堆细节问题卡死。比如同样的一个查询天气的能力A项目写死在Prompt里B项目做成了函数调用C项目又封装成了独立的微服务换个人接手根本不知道该怎么复用。更麻烦的是只要你敢让Agent多干几件事它的行为就开始飘有时候调用错工具有时候干脆自己编一个结果出来。这些问题归根到底都指向同一个痛点——Agent缺少一套结构化的技能体系。光有模型能力不够你得把模型的推理能力跟外部工具、内部流程、业务约束系统地组合起来让Agent知道自己有什么技能、什么时候该用、怎么用、用完之后怎么收尾。这就是我理解的agent-skills一个关于如何设计、实现、维护Agent技能集合的完整方法论。这篇文章不是讲某个具体框架的API怎么调用而是想把我自己在实际项目里踩过的坑、验证过的方案、沉淀下来的设计套路整理出来。适合谁看适合那些已经跑通了一个简单的Agent Demo但正在发愁怎么把它做成一个真正能扛住业务压力的人。也适合刚接触Agent开发、想避开一些常识性坑的初学者。我会尽量用大白话讲清楚也会给出可以直接复制的代码结构和配置方案。2. 先把技能这件事想清楚2.1 技能不只是调一个API很多人一提到给Agent加技能第一反应就是多接几个API。这种思路不能说错但很容易把一个本该稳健的系统做成四处漏风的脚手架。我见过一个团队接入了十几个第三方接口结果Agent经常搞混参数把给CRM系统的数据发到了监控系统里排查了半天才发现是技能描述写得含糊模型压根分不清两个工具的边界。真正的技能设计应该把工具调用这个概念再往上抽象一层。一个技能应该是一个完整的、自包含的能力单元它要回答清楚四件事这个技能在什么场景下触发它需要哪些输入信息它内部如何执行它返回什么结果以及结果如何被上层使用。你甚至可以把它理解成一个微服务——有明确的接口契约、有内部实现、有错误处理、有版本管理。唯一的不同是它的调用方不是另一个程序而是一个语言模型。我在设计技能时最早犯的错误就是把技能定义得太功能化。比如我封装了一个查订单的技能但实际上业务方要的是用户问订单到哪了这个意图的完整处理链路。这两者的差别在于功能化的技能只拿到订单号去查物流状态意图化的技能要先判断用户是想查物流、想改地址还是想催发货然后才决定调用哪个底层函数。如果只做前者你会发现Agent在面对稍微复杂一点的用户问题时依然会手足无措。2.2 技能拆分的粒度怎么把握技能的粒度是个典型的看着简单、做起来要命的问题。拆得太细Agent要在几十个技能里做选择决策成本高还容易选错拆得太粗每个技能内部塞了一堆逻辑难以复用也不好维护。我自己的经验是以一个完整的用户意图闭环为粒度基准而不是以一个底层操作为基准。拿电商客服场景举例。查询订单状态和修改收货地址是两个独立的技能因为它们的触发意图明确、边界清晰、内部逻辑互不交叉。但如果把查订单拆成查订单基本信息查物流轨迹查售后进度三个技能就明显过度了因为用户通常不会刻意区分这三者的边界Agent判断起来也费劲。这时候不如合并成一个查询订单全貌的技能内部透出结构化结果让模型自己决定怎么回答用户。另一个维度是考虑复用性。如果一段逻辑将来大概率会被其他Agent场景复用那就值得单独拆成技能如果只是当前场景一次性用掉那放在工作流里就好。比如从用户历史消息中提取手机号这种技能大概率所有客服场景都会用到值得独立而根据商品ID生成推荐话术这种强业务绑定的逻辑就不必急着抽出来。2.3 技能的描述是给模型看的说明书一个特别容易被忽略的点是技能的description字段某种意义上比技能本身的实现还要重要。因为模型就是靠这段描述来判断我现在该不该调用这个技能的。描述写得太泛模型会在不该用的时候乱用描述写得太细模型抓不住重点描述里用了跟其他技能重复的关键词模型就会懵。我在一个项目里吃过亏给两个技能分别写了查询用户会员等级和查询用户积分余额description都很简短结果模型经常把查积分的请求打到会员等级接口上返回的数据驴唇不对马嘴。后来我把两个描述改成了带触发条件、输入要求和业务场景的完整版本错误率立刻降了很多。现在我对description的要求是包含触发条件、输入字段说明、输出结果说明、使用限制或禁忌。写完之后反复读几遍想象自己是一个啥都不知道的模型看到这段描述能不能准确判断该不该调用。3. 技能体系的核心设计从数据结构到调度逻辑3.1 技能注册表给每个技能建立户口当你手里的技能超过五个之后就必须引入一个统一的注册管理机制。我这里推荐一个很实用的做法——技能注册表Skill Registry。每个技能在注册表里都有唯一标识、名称、版本号、依赖关系、描述信息、执行入口和参数Schema。这样一来Agent的调度层只需要跟注册表打交道而不是跟几十个散落的函数打交道。下面是我常用的技能注册表的Schema示例参考价值比较高{ skill_id: order_query_v3, name: 查询订单全貌, version: 3.2.0, description: 当用户查询订单状态、物流进展、售后进度时使用。输入需要order_id或用户手机号。返回订单基本信息、物流轨迹及售后状态。注意本技能不处理改地址、退款等操作。, input_schema: { type: object, properties: { order_id: {type: string, description: 订单号长度通常为18位}, phone: {type: string, description: 下单手机号与order_id二选一} }, required: [] }, output_schema: { type: object, properties: { order_status: {type: string}, logistics: {type: array}, after_sale: {type: object} } }, dependencies: [user_auth, order_api_adapter], tags: [order, customer_service], timeout_ms: 3000 }这段JSON看起来简单但几个字段的取舍值得展开说。version字段不只是给自己看的更要紧的是模型在理解技能变更历史时能拿它做参照。tags字段用来做粗粒度的初始筛选避免模型每次都把所有技能描述塞进上下文里省token也省决策时间。timeout_ms则是对技能执行的最长等待时间超时了必须有兜底逻辑否则Agent会一直傻等。3.2 技能调用协议让模型和技能之间讲规矩技能注册表解决的是有什么技能的问题但模型跟技能之间怎么协作还需要一套明确的调用协议。我的做法是定义一个统一的函数调用规范把所有技能都包装成同一个模式的函数对外暴露统一的入口。这样做的好处非常明显调度层不需要针对每个技能单独写适配逻辑新增技能的成本降到最低。我推荐一个三段式协议意图识别、参数提取、执行确认。意图识别由模型完成它根据用户输入和技能描述判断该调用哪个技能参数提取要求模型从对话上下文中抽取出调用技能所需的参数抽取失败时要主动向用户追问补齐执行确认则是在调用前把解析出来的参数展示给用户或上层系统确认一遍避免误操作。这个协议看起来多了一步确认但它能拦下大量因为模型参数幻觉导致的错误调用。我自己在实现参数提取时会额外要求模型输出一个confidence字段表示它对参数匹配的把握程度。当这个值低于阈值时系统自动转人工或发起澄清式追问而不是闭眼执行。这个设计救过我很多次尤其是处理那种用户语焉不详的请求时效果立竿见影。3.3 技能编排多个技能如何协同作战单技能调用只是基本功真正的复杂度来自多个技能的编排组合。比如用户问我昨天买的东西怎么还没到能不能帮我把收货地址改成公司这个请求同时涉及查订单、查物流、改地址三个技能而且它们之间有明确的先后依赖关系先查订单拿到订单号再查物流确认状态最后才能改地址。我对多技能编排的处理思路是优先让模型自己生成编排计划Plan但必须用结构化的方式表达。具体来说就是要求模型输出一个包含多个步骤的执行计划每个步骤绑定一个技能和预期输出系统按计划逐步执行每步结果回填给模型再决定下一步怎么走。这种做法比让模型一口气调完所有技能稳妥得多因为中间任何一步出错了都能及时纠偏。def execute_plan(plan: list[dict], registry: SkillRegistry): context {} for step in plan: skill registry.get(step[skill_id]) result skill.execute( inputsresolve(step[params], context) ) context[step[name]] result if not result.is_success: # 中间步骤失败触发兜底逻辑 return fallback_handler(step, result) return context这段伪代码展示的是一个最简执行引擎实际项目里你还会加上日志、审计、超时控制、重试策略等。但核心思想是一致的让模型当指挥官让技能当执行者中间加一层程序化的调度保障。4. 实操记录从零到一搭建一套Agent技能框架4.1 第一步先盘点业务场景再决定技能清单很多人的第一步是先去选模型、搭框架、写代码但我强烈建议反过来——先做业务场景盘点。找业务方聊一聊把用户最常问的50个问题拉出来分类整理看看到底需要哪些技能支撑。这个过程看起来很笨但能把后面的返工成本降到最低。我最近帮一个本地生活类项目梳理技能清单时就是从客服聊天记录里筛出一批高频问题然后逐个标注这个问题需要哪些数据才能回答需要调用什么系统回答的正确形式是什么。整理完发现真正核心的技能其实只有七个门店查询、菜单查询、排队取号、优惠券核销、投诉建议、会员查询、订单查询。其他一堆听着高大上的需求全是低频场景根本不需要初期就做成技能。4.2 第二步搭一个最小可用的技能执行器技能清单确定后下一步就是搭执行器。我用的架构很轻一个FastAPI服务做技能执行入口一个SQLite表存技能注册信息一个Redis做会话状态缓存再加上一个LLM调度层。整个架子两百行代码就能跑起来。执行器的核心是一个路由函数它接收模型输出的结构化调用请求然后根据skill_id匹配到对应的处理函数执行完把结果包装成统一格式返回给模型。这个过程中我最看重的是错误边界。每个技能执行都必须包一层try-except返回的错误信息要带上错误码、可读的错误描述和可恢复的提示。为什么要这么做因为模型在拿到错误信息后会尝试自己修复——比如换个参数重新调用或者向用户解释情况。如果你返回的是赤裸裸的堆栈信息模型大概率会用一段废话来掩盖它的无能。def execute_skill(skill_id: str, params: dict, session: Session): try: skill registry.get(skill_id) if not skill: return SkillResult(err_codeSKILL_NOT_FOUND, err_msg技能不存在) result skill.run(params, session) return SkillResult(dataresult) except TimeoutError: return SkillResult(err_codeTIMEOUT, err_msg技能执行超时请稍后重试) except BizException as e: return SkillResult(err_codee.code, err_msge.message) except Exception: logger.exception(unexpected error in skill execution) return SkillResult(err_codeUNKNOWN, err_msg系统开小差了请稍后再试)4.3 第三步让技能执行效果可观测技能上线后最忌讳的就是黑盒运行——光知道它在跑不知道它跑得好不好。我的做法是给每个技能加三件套全链路日志、执行结果评估、调用频次统计。全链路日志记录的是一次完整请求从用户输入到最终响应的全过程包括模型调用了哪些技能、每步花了多少时间、中途有没有出错。执行结果评估更复杂一点我会抽出一部分线上请求人工标注这次调用是否合理、结果是否正确然后把标注结果作为评测集定期跑回归测试防止模型升级或技能改动后出现行为退化。调用频次统计则用来判断哪些技能是高频刚需、哪些技能基本没人用指导后续的优化投入。这个环节还有一个务实的用途它是你跟业务方之间最好的沟通语言。当业务方质疑Agent能力时你甩出一份本周技能调用分布报表比空口解释一百倍都有效。4.4 第四步技能测试与上线流程技能的测试不能只靠联调冒烟要建立一套针对性的评测用例。我自己的做法是给每个技能准备三类测试数据正常输入、边界输入、恶意输入。正常输入好理解边界输入包括参数缺失、参数类型错误、参数值超范围等恶意输入则是故意构造的、容易诱导模型误判的请求比如用户说我没下单但你要给我查订单或者帮我查一下别人的订单。跑完测试不代表就完事还要做灰度。我常用的策略是切一小部分流量到新技能版本观察错误率和用户反馈确认没问题再全量放开。灰度期间的监控指标至少要覆盖技能调用成功率、平均响应时长、参数解析失败率、用户投诉率。任何一个指标出现异常立刻回滚。5. 典型问题与排障实录5.1 模型选错了技能怎么办这是所有Agent项目里最常遇到的问题。模型明明有查物流的技能它偏偏去调了查订单返回了一堆无关信息。排查的第一步不是去改Prompt而是去看日志——看看模型在决策时到底看到了什么。很多时候问题是出在技能描述上两个技能的description有重叠关键词或者其中一个写得太泛模型分不清。我整理过一个技能描述对照表把容易混淆的技能放在一起改。比如查订单和查物流是重灾区我会刻意在描述里强调边界查订单用于获取订单的基本状态、商品明细、金额信息物流轨迹查询请使用另一个技能本技能不返回物流过程信息。这种我不管什么你去找别人的写法能很有效地帮助模型做区分。5.2 模型参数解析经常出错参数提取是另一个高发问题。用户的表达千奇百怪模型很容易提取出错别字、缺字段、张冠李戴。我最常用的一道防线是声明式校验给每个参数都配上类型、格式、可选值和校验规则在技能执行前先跑一遍校验校验不过就返回需要补充信息。这套机制能拦住大约60%的参数错误。剩下的40%要靠追问机制兜底。校验失败时系统返回一条缺少必要参数order_id之类的信息模型会据此向用户发起追问而不是硬着头皮瞎猜。这里有一个细节追问信息里最好给出示例或者期望的格式比如订单号一般是18位数字可以在订单列表页找到用户配合度会高很多。5.3 技能内部报错怎么回退技能内部依赖的第三方接口不可能永远稳定。我的兜底策略分三层超时重试、备选路径、优雅降级。超时重试最好理解对时效性要求不高的技能可以试着再调一次。备选路径是指同一个技能内部可以有多条实现路线比如主接口挂了就切备用接口甚至切到人工处理。优雅降级则是直接给用户一个说得过去的替代结果比如查不到实时物流时回复目前物流信息更新有延迟建议1小时后再查询。设计兜底策略时有一条铁律宁可给用户一个明确的不完美答复也不能让Agent假装自己完成了操作。我见过一些项目技能内部抛了异常模型为了表现得有用硬是编造了一个成功的假象那才是真正的事故现场。5.4 上下文超长导致的技能调用失败当对话轮次变多上下文塞满了历史消息模型的理解能力会明显下降技能调用的准确率跟着掉。我的解决办法是引入上下文治理机制每次模型决策前先对上下文做一轮精简把已经完成的信息摘要化只保留当前决策真正需要的字段。这个操作能让长对话场景下的技能调用成功率提高不少。另一个补充手段是给关键参数设立会话级缓存。比如用户在前面已经确认过手机号后续的技能调用就不需要模型再去上下文里找直接走缓存。这既降低了参数解析的错误率也省了模型的推理时间。6. 关于agent-skills的一些个人体会这些内容写下来其实就是一句话Agent的能力上限很大程度上取决于你给它配了哪些技能以及这些技能被设计得有多靠谱。模型本身决定了Agent的天花板但技能体系决定了Agent离这个天花板有多近。我踩过那么多坑之后最大的体会是不要迷恋花哨的模型调优技巧踏踏实实把技能的定义、注册、执行、观测、兜底这一套基本功做扎实你的Agent才能真正在业务里站住脚。分享一个小技巧作为收尾每个技能上线后记得定期翻看真实的调用日志尤其是那些模型犹豫不决乱调技能的case。每一次这类case都是你优化技能描述和调用策略的绝佳素材。你不需要一口气做得完美只要保持每个迭代都让技能的决策准确率往上涨一点三个月后再回头看你自己的Agent和最初那版Demo已经是完全不同的两个东西了。