ARTICLE DETAIL

资讯详情

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

Agent技能管理实战:从提示词硬编码到注册表协议

Agent技能管理实战:从提示词硬编码到注册表协议 做Agent开发这半年多我踩过最大的一个坑就是把所有能力都硬编码在系统提示词里。最开始只调两三个工具还好等技能一多提示词越堆越长模型反而开始选择困难——该调天气的时候它跟你聊哲学该查数据库的时候它给你编数据。后来我把这一整套逻辑抽出来做成了独立的技能管理层也就是我现在在用的这套agent-skills方案。这篇文章就是把这套方案的设计思路、核心协议、以及我在实际接入过程中踩过的坑完整捋一遍给同样在做Agent技能封装的朋友做个参考。这套东西适合谁如果你正在做基于大模型的Agent应用开始遇到工具太多模型不会选、技能调用出错后不知道怎么恢复、新加一个技能要改一大圈代码这类问题那这篇内容应该能帮你省下不少走弯路的时间。我会把技能注册、参数校验、错误恢复、权限控制这几个关键环节的代码结构和设计取舍都讲清楚顺带附上一些实测下来的参数配置和避坑经验。1. 从Agent开发痛点说起为什么需要一套技能规范1.1 当Agent还停留在聊天阶段时一切都很简单早期我做对话机器人模型只需要理解用户意图然后用自然语言回复整个链路就是用户输入 - 模型生成 - 返回文本。这个阶段其实不太需要技能这个概念模型本身的知识和推理能力就是全部。但一旦Agent开始被要求做事——比如查订单、发邮件、操作数据库、调用第三方API——事情就变了。模型不能只输出文字了它得输出一个可以被系统执行的动作。这时候你需要回答三个问题模型怎么知道系统里有哪些能力可用模型怎么正确地把参数填进去系统执行完怎么把结果告诉模型这三个问题如果靠拼提示词解决初期还能撑技能超过十个之后基本就崩了。我见过最典型的情况提示词里写了十几个工具的JSON Schema模型开始混淆两个相似工具的参数或者在多个工具之间犹豫不决甚至自己发明一个不存在的工具名。这不是模型笨是信息架构出了问题——你把所有工具平铺在提示词里等于让模型在一个没有目录的仓库里找东西。1.2 一旦Agent开始调用工具混乱就出现了再说说工具调用的混乱阶段。有个项目里我一开始直接给模型暴露了二十多个Python函数每个函数装饰一下就算技能了。表面上看起来挺方便实际上有几个致命问题。第一个问题是技能描述不可控。函数的docstring写得长模型就倾向调用它写得短模型就无视它。你很难通过调描述来影响模型的行为因为描述和函数本身耦合在一起改描述就是在改代码。第二个问题是参数验证极度单薄。模型填参数经常出错尤其是有多个可选参数的时候。而Python这种动态语言参数类型不对往往要到运行时才炸一旦炸了整个对话状态就断了。第三个问题是错误处理完全没设计。工具抛出异常之后模型拿到的是一段堆栈信息它根本不知道怎么从堆栈里恢复。结果就是同一个调用反复失败模型在同一个坑里摔十次。我把这个阶段叫作能用但脆弱的阶段也是我决定重新设计技能体系的原因。我当时列了一张表把碰到过的问题跟理想方案做了映射这里可以给你看一下。遇到的问题根因理想的解决方案工具一多模型就不选平铺在提示词里无索引技能注册表 分组发现参数填错导致运行时崩溃参数Schema缺失或太弱强约束JSON Schema 预校验调用失败后无法自愈堆栈信息对模型不可读结构化错误返回 恢复指令新加技能要改主流程路由逻辑硬编码声明式注册动态挂载1.3 agent-skills的定位一套可复用的技能封装协议基于上面这张表我最后设计了一套以技能即数据为核心理念的方案也就是agent-skills。核心思路很简单每个技能不再是一段直接调用的代码函数而是一个包含名称、描述、参数Schema、校验函数、执行函数、错误处理策略的完整描述单元。模型看到的是一份技能清单系统执行的是技能卡片背后的实现。这个设计的好处有三个。解耦技能实现与模型提示完全分离改实现不需要动提示词。可控技能对模型的暴露程度可以精细调节从仅展示名称到完整展示参数都有对应配置。可观测所有技能的调用都有统一的生命周期记录排查问题的时候能完整回溯请求响应链路。我把这套方案跑通之后最直观的感受是加一个新技能不需要动主流程代码只需要遵循协议添加一个技能定义文件注册后打个日志确认加载成功就完事了。接下来我会从设计到实现把每个环节详细拆开讲。2. 技能设计先想清楚技能的边界2.1 技能的粒度过大过小都会难受在设计技能体系时第一个需要拍板的问题是一个技能应该多大我见过两种极端情况。一种是把整个业务操作塞进一个技能里比如create_order这个技能内部实现了查库存、算价格、扣减库存、生成订单、发通知五个步骤另一种是把一个动作拆成十几个技能比如get_user_name、get_user_email、get_user_phone各自独立一共用了十多个技能才能凑齐一个用户的信息。两种情况都很难受。技能粒度过大模型没法精细控制执行过程一旦中间某一步想变通它没有这个选项而且排查问题时你看到的是一大坨不可拆分的日志。技能粒度过小模型需要多次调用才能完成一个目标每一轮调用都有推理开销和延迟还增加了调用失败的概率。我的经验是技能的粒度应该以一次性可观测的原子操作为基准。也就是说一个技能内部可以有多个动作但这些动作对模型来说应该是一个整体要么一起成功要么一起失败。如果你发现某个技能内部的分支逻辑越来越多那说明应该拆了如果你发现模型为了完成一个目标需要连调七八次技能那说明应该合了。以我做的订单Agent为例submit_order和cancel_order各自独立合理但get_order_status和get_order_detail我会合并成一个query_order因为模型通常需要详情来判断状态分开反而让模型多猜一步。这个决策没有绝对标准但有个经验法则如果一个技能描述超过80个字才能说清楚它做什么那多半粒度不对考虑拆开。2.2 一个技能的标准组成描述、参数、实现、校验接下来是技能本身的数据结构设计。我把一个技能定义成了四大部分每部分各司其职。首先是基本信息包括技能名称和一句话描述。名称要用小写下划线格式容易辨识描述要写清楚什么时候该用这个技能而不是这个技能怎么实现。比如查询订单信息适用于用户询问订单状态、物流进度、商品明细等场景这就是合格描述而通过订单编号从数据库获取订单记录就是不合格描述因为模型在看到用户问题的时候它是按照场景来匹配的不是按照实现来匹配的。其次是参数Schema。这一块我用JSON Schema标准来定义。每个参数必须有明确的类型、是否必填、取值范围和描述。尤其需要注意的是描述字段模型填参的时候就是靠这个描述来决定传什么的所以描述要写订单ID来自用户提供或系统上下文中的订单编号而不是简单写订单ID。第三是校验与执行逻辑。我分成两步校验函数在参数进入执行前先跑负责检查业务规则比如金额必须大于零、日期不能早于今天执行函数才是真正的业务实现接收已经校验过的参数并返回结构化结果。这样拆分的好处是参数错误能在执行前就被拦截不会浪费一次外部调用。最后是错误处理策略。每个技能都要声明自己在什么情况下会抛什么错误以及这些错误能否被重试。我把错误分为三类参数错误、状态冲突和外部服务故障对应不同的恢复策略后面我会详细展开。2.3 技能的意图-能力映射关系还有一个在设计期就要想清楚的问题模型是怎么从用户的意图找到对应技能的如果你只靠模型自由发挥那技能的命中率会很飘。我实际做的时候给每个技能打上了触发场景的标签比如订单查询、售后退款、库存查看、物流追踪这些场景标签建了一个索引。模型在选技能前我会先让系统以场景标签为维度做一次粗筛把候选技能缩小到三到五个再交给模型精挑。这个做法一开始我觉得有点多余后来发现特别管用。因为用户的表述经常有歧义比如帮我看看我那个蓝色的东西到哪了如果不加场景粗筛模型可能会在订单查询和商品搜索之间犹豫。有了场景标签系统先把物流追踪和订单查询划进候选模型的选择准确率明显上升。这个意图-能力映射表我会维护在一个独立的配置文件里跟技能实现分开。每次新增技能的时候第一件事不是写代码而是更新这张映射表确保新技能能跟已有技能在场景上有区分不重叠不冲突。实际测试下来这个映射表的维护质量直接决定了Agent能不能做到用得准。3. 核心实现注册、发现与调用的完整链路3.1 技能注册表所有技能的可信入口技能注册表是整个体系的大脑。我实现的方式是启动时扫描指定目录下的所有技能定义文件做一次统一的合法性校验校验通过后注册到一个字典里。这个字典就是运行时唯一的技能来源模型能看到什么、系统能调什么都以这个注册表为准。注册表里的每个技能条目我设计成下面这种结构dataclass class SkillEntry: name: str description: str parameter_schema: dict validate_func: Callable execute_func: Callable error_policy: dict tags: list[str] enabled: bool True这里有个容易被忽略的点技能注册要分级。我分了可见和可调用两个级别。有些技能允许模型知道它的存在但只有在特定条件下才允许被真正调用。比如删除用户账号这个技能可以让模型知道系统有这个能力但在开调用权限之前必须经过二次确认。这个设计后面在权限章节再展开。注册表还需要暴露个简单的查询接口我在实现里用了标准库的dispatcher模式没有引入太重的框架。核心就两个方法list_skills()返回所有已注册技能或者按tag过滤后的结果get_skill(name)根据名称获取单个技能。就这么简单但足够满足绝大部分Agent场景。3.2 技能发现给大模型一份菜单模型怎么知道有哪些技能可用这个环节我管它叫技能发现本质上是给模型生成一份动态的菜单。我做了两层发现机制。第一层是静态清单。在每次对话请求进入时系统把所有已注册且可见的技能名和一句话描述拼成一份精简清单注入到系统提示词里。注意这里不展开参数Schema只给名称和一句话描述避免提示词过长干扰模型注意力。我实际测试过一份30个技能的清单用名称 - 描述的格式压缩整体也就几百字模型完全能消化。第二层是动态详请。当模型初步选定一个技能后系统再把这个技能的完整参数Schema加载进上下文中引导模型按结构填参。这一步我用了function calling的原生机制来实现模型在生成时会输出一个结构化的函数调用请求我再据此从注册表取出技能并处理。第二层是整个链路里最关键的因为参数Schema一次性全给的话模型根本记不住效果远不如先选技能再看Schema。这个两段式发现设计是我对比了好几种方案后觉得最稳定的一种推荐你直接抄作业。3.3 调用协议一次技能执行的生命周期技能的执行并不是简单的调用函数拿返回值。我把它设计成了六个阶段的完整生命周期每个阶段都有日志和超时控制。六个阶段是这样的解析阶段从模型输出中抽取结构化调用请求这个阶段失败往往是模型生成了非法JSON需要做一次容错解析后面排查章节细说。校验阶段用技能注册表里配的validate_func跑参数校验不合规就直接短路返回不进入业务逻辑。授权阶段检查这个技能在当前对话上下文里是否有调用权限敏感技能需要用户确认确认没通过也是短路返回。执行阶段真正跑业务逻辑我会在这里设置超时阈值避免技能调外部API时卡死整个对话。结果封装阶段把执行结果转成统一的响应结构区分为成功、业务失败、系统异常三类。最后是上下文记忆阶段把本次调用的入参、出参、耗时、错误信息写入本次会话的上下文摘要供模型后续参考。我用一个伪代码概括这个生命周期def invoke_skill(skill_name: str, raw_args: dict, context: dict): entry registry.get_skill(skill_name) # 阶段1: 解析(此处已由LLM层完成结构化输出) # 阶段2: 校验 validation_error entry.validate_func(raw_args) if validation_error: return skill_response(successFalse, error_typevalidation_error, error_messagevalidation_error) # 阶段3: 授权 if not verify_permission(skill_name, context): return skill_response(successFalse, error_typeauthorization_required, error_message需要用户授权后才能执行) # 阶段4: 执行(带超时) try: result with_timeout(entry.execute_func, timeout10, **raw_args) return skill_response(successTrue, dataresult) except SkillBusinessError as e: return skill_response(successFalse, error_typebusiness_error, error_messagestr(e)) except Exception as e: return skill_response(successFalse, error_typesystem_error, error_messagef系统异常: {e})想特别强调一下上下文记忆这个阶段这个是最容易被忽略但实际超有用的设计。技能调用的成功结果要精简为一段摘要放回上下文中让模型不用记住全部细节也能继续对话。比如技能返回了一大段订单详情我会压缩成订单状态已发货预计今日到达物流单号SF123456这段摘要对模型理解状态足够了。如果后续用户问问题需要更多细节模型可以再发起一次精确调用幂等性做好就行。4. 实操中的关键细节参数校验与错误恢复4.1 参数校验大模型给的参数永远需要怀疑参数校验这块我吃过最大的亏就是轻信模型填的参数。模型不是数据库应用程序它没有类型概念它会为了满足必填项而编造看起来合理的值。比如用户说帮我查一下订单模型不知道订单编号就会填一个12345然后在数据库里查不到返回空结果。更麻烦的是如果接口侧没校验这种假参数可能造成脏数据入库。所以我强烈建议所有技能在执行前必须过三道校验关卡。第一道是类型与格式校验用JSON Schema跑一遍确保参数是字符串就是字符串、是数字就是数字、日期格式合法。第二道是业务规则校验比如状态枚举值是否在允许范围内、金额是否为正数、时间范围是否合理。第三道是上下文一致性校验意思是模型传的参数要和当前对话上下文能对上号比如用户在问自己的订单那订单ID应该属于当前用户这个校验能拦住数据越权访问。三道关卡落地的代码结构我大致是这样组织的每个技能注册时附带一个validator列表每个validator就是一个函数输入参数字典输出错误信息或None。校验失败时返回给模型的信息要具体到哪个字段、为什么失败、应该怎么修正一旦错误信息模糊模型就会用新的错误参数去重试陷入一个低效循环。4.2 错误分类让Agent知道为什么失败错误是怎么返回给模型的这是决定Agent能否自我修复的关键。我一开始直接把异常堆栈丢给模型结果模型看到一堆Python报错完全不知道该怎么办。后来我把错误整理成了三种类型每种类型有不同的恢复策略。第一类叫参数性错误特征是模型或用户给的输入不满足要求比如日期格式错、订单号不存在。这类错误属于不可重试的模型应该做的不是闭眼重试而是向用户澄清信息或修正参数。第二类叫状态冲突错误特征是请求本身合法但当前状态不允许执行比如订单已经发货了不能取消、商品已下架不能加购。这类错误同样不可重试但需要模型向用户解释当前状态以及可替代方案。第三类叫外部依赖错误特征是技能本身没问题是外部服务暂时不可用比如数据库超时、第三方API返回500。这类错误是可重试的但需要做指数退避不能短时间无限重试。结构化的错误返回在代码里就是标准化的字典包含error_type、error_code、user_message、retry_hint这几个字段。我在实际测试里发现当错误信息里带上你可以尝试取消订单或联系人工客服这种可操作建议时模型的表现会明显更好因为它不需要自己猜下一步了。这个经验后来我应用到所有技能的错误提示上整体体验提升很明显。4.3 组合技能把简单技能编排成复杂流程单个技能能解决的问题有限Agent真正强大的地方在于把多个技能串起来形成一个流程。比如退货退款这个操作至少涉及查订单、校验退货条件、生成退货单、通知仓库、退款五个步骤如果每个步骤都要模型自己一步步调不仅慢而且容易出错。我的方案是把常用的流程封装成组合技能。组合技能对外看起来就是一个普通技能但它内部分为多个子技能按照预定义的编排逻辑依次执行。我用一个轻量的编排定义来描述流程大体上是这样{ name: return_refund_flow, description: 处理用户退货退款请求包含订单校验、退货单创建、退款处理, steps: [ {skill: query_order, outputs: [order_info]}, {skill: check_returnable, inputs: [order_info], outputs: [check_result]}, {skill: create_return_order, inputs: [order_info], outputs: [return_order_id]}, {skill: process_refund, inputs: [return_order_id]} ], failure_policy: stop_on_error }组合技能里最关键的设计决定是什么时候让模型思考、什么时候让流程自动走。我的原则是确定性逻辑用编排不确定性判断用模型。比如查询订单到检查是否可退这一步是确定性的不需要模型参与直接按编排走但如果查询结果有多个订单需要用户选择那这个过程必须停下来让模型跟用户交互确认之后再做下一步。这样做的好处是整个流程既高效又不会出现模型胡走流程的情况。5. 权限与安全技能放权前必须想清楚的几件事5.1 最小权限原则技能不该有万能钥匙技能系统如果在本地项目里自用权限问题可能还不明显。但一旦Agent要面向用户开放或者技能会操作敏感数据权限设计就必须前置思考。我见过一个反例一个Agent项目里模型可以调用export_all_user_data这种技能结果用户一句话就触发全量数据导出。这种权限粒度显然是有问题的。我建议每个技能一开始就声明自己需要的权限级别。我把权限划分为只读、受限写、敏感写三个等级。只读技能查订单、查库存在用户授权后可自动执行受限写技能修改备注、更新某个字段要求执行前向用户展示将做的改动敏感写技能删除账号、转账、批量修改必须二次确认且需要在系统里留下审计日志。权限检查放在执行前的授权阶段也就是我在第3.3节生命周期里提到的阶段3。检查不通过时返回的authorization_required信息里应该明确告诉模型当前用户未授权执行此操作需要用户确认这时候模型应该向用户发确认请求而不是绕过权限直接调底层代码。5.2 敏感操作的二次确认机制关于二次确认我踩了一个设计上的坑一开始我把确认做成一个单独的confirm_action技能让模型调它。结果模型有时候忘调有时候确认了但没传递用户的明确意图。后来我改成了确认是系统级拦截不是技能级调用。具体做法是当模型选中的技能属于敏感写等级时系统自动中断正常返回路径先把本次操作卡片操作内容、影响范围、涉及数据返回给前端前端展示给用户用户点了确认后系统才会继续执行。整个确认流程对模型来说是透明的不需要模型参与省掉了模型自由发挥的空间。这个改动有个额外的好处系统日志里能明确记录用户于XX时间确认了某操作审计链路完整。如果出纠纷可以还原真实流程。评论区如果有做金融、医疗类Agent的朋友应该能理解这条有多重要。5.3 技能沙箱与超时控制技能执行的沙箱和超时控制是保证整个系统稳定性的最后防线。沙箱的意思是把技能的执行环境隔离开避免一个技能崩溃导致整个Agent进程挂掉。我目前用的是进程级隔离每个技能在一个子进程/工作线程里执行用队列传递参数和结果设置明确的超时时间。超时时间根据技能类型差别很大。查缓存类技能我给1秒查数据库给5秒调用外部API给10秒涉及人工审核的长流程我单独用异步任务管理。我给每个技能设了一个超时阈值超过阈值直接终止本次执行并返回外部依赖超时错误模型可以据此判断是否重试。曾经有个技能调第三方物流接口对方延迟到30秒才返回那段时间整个Agent体验非常卡加上超时控制后立刻改善。沙箱这一块还有一个细节技能不应该有直接访问全局文件系统或环境变量的默认权限。如果一个技能确实需要读写文件我建议它通过统一的文件服务接口操作这样能记录每一次文件访问行为。我知道前期这样设计会增加一些工作量但等技能数量多了、调用频率上来了你一定会感谢自己提前做了这层隔离。6. 测试、观测与迭代技能系统上线后才是开始6.1 技能的单元测试不依赖LLM的确定性验证很多做Agent的朋友会忽略技能系统的测试理由是反正模型行为不可控测了也没用。这个观点我不同意。技能系统本身是确定性的代码完全可以做单元测试和回归测试。模型的选择行为确实有随机性但技能的执行结果不应该有随机性。我用的测试策略是预置一个fake_llm模块它不调用真实模型而是根据预设的调用请求返回固定输出。这样我就能模拟模型选择了技能A并传了参数X的场景然后断言技能A的校验、执行、错误返回是否符合预期。这个测试跑一遍只要几秒钟但能拦住大部分上线后才能发现的低级错误。测试用例要以边界值为主。比如参数校验里的空字符串、极长字符串、特殊字符、负数、浮点精度问题这些都值得测一遍。我之前有个技能参数Schema里写了字符串最长100字符但校验函数里忘了做长度限制结果模型传了个超长文本直接把后端数据库字段撑爆了。加了这个测试之后类似问题再没出现过。6.2 可观测性每次调用的完整轨迹技能系统的可观测性我管它叫轨迹日志意思是每一次对话请求背后能完整还原所有技能调用的时间线、入参出参、耗时、错误。排查线上问题时这个轨迹的价值无论如何强调都不过分。每个技能调用我会记录以下字段调用ID、技能名称、参数脱敏后、校验结果、权限检查结果、执行耗时、返回数据摘要、错误类型。这些日志统一写到结构化存储里按调用ID聚合。用户报一个问题我先查调用ID对应的时间线往往几分钟就能定位到是哪个技能出了什么错。日志有一个要注意的坑入参出参在写日志前要做脱敏。技能可能处理身份证号、手机号、地址等敏感信息直接明文写日志是安全事故。我做了一个脱敏函数把关键字段替换成掩码形式既保证排查可用又不泄露隐私。这一点在合规上特别重要。6.3 版本演进技能也会腐化最后想聊一下技能的长期维护。我见过很多Agent项目技能开发完上线后就没人管了结果两个月后模型开始表现怪异一查才发现是某个技能的底层依赖API升级了返回结构变了但技能代码没更新。我把技能系统做了一个版本化演进流程每个技能定义文件头部带版本号和变更记录外部API变更时技能需要升版本并重跑回归测试废弃的技能不能直接删除先标记为deprecated给模型仍能看到但调用时返回明确的弃用提示以过渡时间窗口再下线。这套流程简单但有效能避免很多查了一晚上发现是接口变更的情况。还有一个维护技巧定期分析技能调用日志看哪些技能调用频次极低或者频繁报错。调用频次极低的技能要么是描述写得模型用不上要么是场景定位模糊值得重新设计频繁报错的技能往往是业务规则已经变了但代码没跟上需要优先排查。这个分析我大概是每两周做一次对保持Agent整体体验很有帮助。7. 常见问题与排查技巧实录7.1 模型反复调用同一个失败技能我遇到过的最典型问题模型在一个技能上连续失败四五次每次都是同样的参数错误但它不换方案。后来我发现根因在错误返回信息上——错误提示里没有给出不要重试的提示模型会认为换个参数再试就能成功。解决方法是参数性错误的返回信息里固定带上该参数不符合要求请修改参数或向用户澄清不应重试同一参数这样的策略提示。加上之后模型基本会停止无效重试转向跟用户确认。另一个原因是缓存未传上下文。模型第一次调用失败后后续它的即时记忆中已经没有之前失败的细节了只能靠当前错误信息推理。所以错误信息里需要携带足够的上下文线索让它能独立判断。7.2 技能描述写得花哨但模型就是不用有一种情况很让气技能明明在线且实现正常但模型在应该用它的场景里始终不用。我排查后发现通常不是模型的问题而是技能描述和应用场景之间没有建立清晰的关联。举个例子我有个generate_sales_report技能描述写的是根据指定日期范围生成销售报表但用户实际表达是帮我看看这个月的卖得怎么样模型匹配生成销售报表和看看卖得怎么样之间缺乏中间的语义桥。后来我把描述改成适用于用户询问销售情况、业绩表现、本月/本周/上月销量等场景生成结构化销售报表加了场景触发词之后命中率直接翻倍。描述里不要只写做什么要把用户可能会用什么话提出这个需求也嵌入进去。7.3 参数幻觉问题参数幻觉是指模型在没有信息的情况下编造了一个看起来合理但实际不存在的参数。最常出现在用户没有提供订单号/用户ID等关键标识但参数Schema里标了必填的情况。模型为了完成函数调用会硬编一个ID出来。这个问题的根治办法将关键标识参数的必填改为条件必填并允许空值。当参数缺失时校验阶段返回专门错误提示缺少必要参数应向用户询问XX信息。这样模型就会停下来问用户而不是猜一个效果立竿见影。还有一类参数幻觉是枚举值幻觉比如模型传了一个并不在枚举列表里的状态值。我在JSON Schema的枚举描述里把取值范围写得明明白白校验函数里再做一次白名单匹配基本能拦截大部分。7.4 死循环与尾调用失控最后分享一个比较进阶的问题技能编排时可能引发死循环。比如技能A执行完后返回了一个状态模型看到这个状态后认为需要再调用技能A就形成了一个循环。日志里表现为同一个技能的调用记录连续出现十几次偶尔几十次。我在系统里加了一个最大技能调用次数限制默认单轮对话连续调用上限设为10次超过后强制要求模型进入向用户总结当前进度并询问下一步的模式。这个设定也是从一次真实事故里学来的当时一个Agent在退款流程里卡住了循环调用了20多次查询接口既浪费钱又影响体验。限制调用次数还有一个附带效果迫使技能设计更合理。如果你的Agent经常触发这个限制那大概率不是模型的问题而是技能粒度和编排逻辑还需要优化。写到这里我最后再分享一个我自己觉得特别好用的小技巧给技能注册表加一个dry_run调试模式。这个模式下所有技能调用都走完整生命周期但执行阶段用模拟数据代替真实业务逻辑。调试技能描述、测试模型会选择哪个技能、验证参数Schema都不需要碰真实系统也不会产生脏数据。我每次调整完技能配置先跑一轮dry_run确认模型的选择和填参都正确再切成正式模式。这个技巧帮我省掉的测试时间保守估计每周有半天到一天。如果你也想把Agent的技能体系做扎实强烈建议从这个调试模式开始搭起。
返回列表