首页
/
行业洞察
/
正文
INDUSTRY INSIGHT · 深度
Agent技能体系设计:从Function Calling到可靠执行
📅 2026/10/28 14:00:14
✍️ 爱科研究院
👁 阅读 3,247
做 agent 的人应该都有同一种体会模型很聪明但把模型放进业务里它就会变得像一个“什么都懂、但什么都不敢保证”的新人。你让它查个库存、下个订单、发条通知它嘴上答应得好好的真到了执行环节不是参数给错就是绕开流程自己瞎编。我在做 agent-skills 这套东西之前已经看过太多 agent 项目死在同一个地方——模型把所有事都揽下来结果所有事都做得不干净。agent-skills 这个项目本质上就是给大模型配上一套“职业技能体系”。它不是让模型自由发挥而是先把业务动作拆成一个个可描述、可调用、可验证的技能再让模型像一个调度员一样根据用户意图去选择和编排这些技能。这篇文章我会把我在设计、落地、踩坑过程中积累的东西完整讲一遍包括技能怎么定义、分层怎么切、执行引擎怎么取舍、上下文怎么隔离、评估怎么做还有那些文档里不会写的坑。1. agent-skills 到底在解决什么问题1.1 为什么单靠大模型的“聪明”撑不起 agent 落地你可能会想现在的大模型已经能理解复杂指令、能写代码、能调用工具为什么还要单独搞一套技能层我举个例子。假设你让一个 agent 帮你处理售后工单用户说“我上周买的耳机右耳不响了想换货”。模型确实能理解这句话也知道“换货”意味着要校验订单、检查售后政策、生成退货单、通知物流。但问题在于这一步一步的动作模型是“即兴发挥”还是“按标准执行”如果按标准执行谁来定义这个标准如果即兴发挥出错了谁负责实际测试里让模型自由发挥的下场通常是三种第一种模型跳过了关键步骤比如没查订单是否在退换期内就直接生成退货单第二种模型把步骤顺序搞反了先通知物流再生成退货单导致物流系统查不到单第三种模型对同一个操作每次都生成不同的参数格式下游接口根本没法稳定对接。这些问题不是模型笨而是它缺少一套“动作边界”。所以 agent-skills 的核心思路是把业务里那些确定性高的动作从模型的“自由意志”里剥离出来变成预定义好的技能。模型只负责两件事理解用户意图以及从技能清单里选对技能、填对参数。真正执行时走的是代码写好的逻辑不是模型现场发挥。这样既保留了大模型理解自然语言的灵活性又保住了业务执行层面的确定性。1.2 技能层和 function calling 的区别在哪里很多人会问这不就是 function calling 吗OpenAI 的 function calling、Anthropic 的 tool use不都是让模型调用外部函数吗对也不对。function calling 是一种协议它解决了“模型怎么把意图转换成一个函数调用请求”的问题。但 agent-skills 解决的是“这个函数调用请求怎么被安全、可靠、可追溯地执行”的问题。两者的层次不一样。打个比方function calling 相当于你给新人一张电话列表告诉他“你想联系谁就拨谁的号码”。agent-skills 则相当于一套完整的培训手册和作业标准什么时候能拨这个号码、通话时第一句说什么、遇到占线怎么办、通话结束后要不要做记录、记录写到哪里、写错了怎么改。这些都是 function calling 本身不会管的事。具体到实现上我在 agent-skills 里做了几件 function calling 一般不做的事技能描述里声明了前置条件。比如“退货单生成前必须校验订单状态为已签收”这个条件会先被代码检查不满足就直接返回错误不等模型反应。技能声明了副作用。比如“发送站内信会产生一条消息记录”这样编排引擎可以评估技能叠加后的影响而不是盲目串联。技能可以组合。单个技能是原子能力但 agent 实际干活经常要串多个技能agent-skills 里用编排规则把“先查订单、再校验政策、再生成单据、再通知用户”这类流程固定下来而不是每次让模型现场想先做哪步。说白了function calling 是“给了模型一双手”agent-skills 是“教会这双手按规程操作”。两者可以配合使用但不能互相替代。2. 核心设计技能如何定义、分层与组合2.1 技能描述让模型和人都能看懂的一页纸技能定义这件事听起来简单做起来全是细节。我在 agent-skills 里把每个技能定义为一份 YAML 描述里面包含名称、描述、参数、返回、前置条件、副作用、成功标准、超时策略和版本号。这份描述有双重用途给模型看让它知道什么场景下该选这个技能给执行引擎看让它知道怎么校验入参、怎么判定成功。一个我实际在用的技能定义长这样name: send_order_notification description: 在订单创建成功后向用户发送站内信通知适合在需要主动告知用户订单状态时调用 version: 1.2.0 enabled: true parameters: order_id: type: string required: true description: 订单号例如 ORD20250101001 channel: type: enum values: [in_app, sms, email] default: in_app description: 通知渠道默认站内信 returns: - delivery_id - channel preconditions: - order_id 对应的订单状态必须为 created side_effects: - 会在 message_center 表中创建一条送达记录 success_criteria: - 返回 delivery_id 非空 - 消息状态为 delivered 或 pending timeout_ms: 3000这份定义里description不是给人看的是给模型看的。你描述得越好模型选错技能的概率就越低。我一开始写的是“发送通知”结果模型动不动就把所有通知需求都导向这个技能连“退款成功的通知”也往这里塞。后来我把 description 改成“仅在订单创建成功后发送站内信且用户主动询问订单进度时也可使用”选择准确率明显提升。preconditions是给执行引擎看的。每次执行前引擎会先跑一遍前置检查不满足就直接拒绝调用。这个设计非常关键它等于在模型和真实业务之间加了一层“安检”。2.2 原子技能与组合技能粒度怎么切不后悔技能粒度切多细是 agent-skills 里最让人头疼的决策。粒度太粗技能变成一个大杂烩模型选不准、复用率低粒度太细技能数量爆炸编排时模型要在一堆相似选项里做选择准确率照样下降。我最终采用了一种两层的结构原子技能和组合技能。原子技能对应一个不可再拆的业务动作比如“查询订单状态”“生成退货单”“发送站内信”。组合技能则是把多个原子技能按照固定流程串起来比如“处理售后换货申请”就是一个组合技能它内部依次调用了查询订单、校验售后政策、生成退货单、发送通知四个原子技能。切粒度的原则我总结成三条如果两个操作必然一起出现那它们大概率应该合并成一个原子技能或者做成一个组合技能而不是让模型做两次选择。如果某个操作在多个业务场景里都会被用到那它就应该被拆成独立技能方便复用。如果一个技能的入参超过 5 个就要考虑是不是切细了。参数多意味着模型填错参数的概率高也意味着这个技能的边界不清晰。我踩过的最明显的一个坑是把“创建售后工单”做成了原子技能。表面上看它确实是一个操作但它的入参有订单号、用户ID、售后类型、问题描述、图片列表、期望解决方案六个字段。模型经常把“问题描述”填成“用户说耳机坏了”这种口语文本或者漏掉图片列表。后来我把它拆成“创建售后工单基础信息”和“补充售后凭证”两个技能准确率提高了不少。2.3 仲裁与调度多个技能时由谁说了算当系统里有上百个技能时模型每次要面对的就是一道选择题给定用户请求选哪个技能或者哪几个技能的组合。我在 agent-skills 里做了一个调度器它不直接让模型输出“技能名”而是先把候选技能列表按相关性排好序再把排序后的列表和用户请求一起交给模型做最终选择。这里有一个很微妙的地方如果你把全部一百多个技能描述都塞给模型模型的选择准确率会明显下降因为相似技能之间的区分度被拉平了。我们的做法是先做一轮检索召回用关键词和语义相似度把候选技能缩小到 5 到 8 个再由模型精确判断。检索这层我用的是比较轻量的方案每个技能打上业务标签比如“订单”“售后”“物流”“支付”用户请求进来先做一次标签过滤再做一次向量召回。向量召回我直接用现成的 embedding 接口把技能描述和用户请求都转成向量算个余弦相似度。纯向量召回的问题是它容易把描述相近但实际功能不同的技能排在前面比如“发送站内信”和“发送短信验证码”的向量距离可能非常近但业务语义完全不同。所以标签过滤必须先做不能省。仲裁规则这块我设计了三级优先级第一级是业务规则比如“售后单状态为待处理时禁止调用重新发货技能”这种规则直接写在代码里不经过模型第二级是组合技能内置的流程节点流程里每一步该调什么技能由编排脚本决定也不经过模型第三级才是模型自由选择用来兜底那些没法枚举的长尾场景。绝大多数请求走到前两级就能结束需要模型真正自由发挥的比例其实很低这也是 agent 稳定性的一个重要来源。3. 执行引擎的实现细节与配置3.1 请求协议与注册表给每个技能一个门牌号执行引擎是 agent-skills 的心脏。它接收调度器给出的技能选择和参数负责校验、执行、记录、返回。我给技能执行定义了一套统一的请求协议所有技能无论内部逻辑多复杂对外都暴露同一个接口格式。一个典型的执行请求长这样{ skill: send_order_notification, input: { order_id: ORD20250101001, channel: in_app }, trace_id: trc_8f3a2c79e1, timeout_ms: 3000, idempotency_key: ord_20250101001_inapp_01 }这里面有几个字段值得单独说。trace_id用于全链路追踪任何一个技能执行出问题能通过这个 ID 把日志、参数、返回值和上游请求串起来。idempotency_key是幂等键用来防止重复执行。agent 在编排流程时经常因为超时重试导致同一个技能被调用两次比如生成退货单发两次如果没有幂等键下游系统就会产生两条一模一样的退货单。加了幂等键之后引擎在写操作前先查一下这个键是否已经处理过处理过就直接返回上一次的结果。技能注册表是一个内存中的字典key 是技能名value 是技能定义和执行器。服务启动时从 YAML 描述文件加载动态注册。这里有一个容易被忽略的点YAML 里的参数声明必须跟代码里的参数校验逻辑保持一致。我出现过一次很尴尬的情况调整了 YAML 里channel的枚举值把email加进去了但代码里还是只接受sms和in_app结果模型规规矩矩按 YAML 传了email直接被代码拒绝。现在我把参数声明和校验逻辑做成了同源生成避免两处维护导致的漂移。3.2 上下文管理与隔离防止一次对话拖垮所有任务agent 执行多技能任务时上下文管理是绕不开的问题。最常见的场景是一个组合技能要按顺序调用四个原子技能前后两个技能之间可能有数据依赖比如“生成退货单”需要用到“查询订单”返回的订单号。如果把这些中间数据全都塞进模型上下文让它自己看很快就会把上下文撑爆而且模型还会被无关信息干扰。我在 agent-skills 里做了一套轻量的上下文隔离机制每个原子技能执行时只能看到自己入参里显式传入的数据以及一个只读的“共享数据区”。共享数据区由编排引擎统一管理组合技能每完成一步就把关键产出写入共享数据区供后面的技能读取。模型不直接读共享数据区它只负责在需要时指出“下一步要用哪个字段”具体取值由引擎从共享数据区里填。举个实际流程处理售后换货申请的组合技能调用query_order查询订单把返回的order_id、order_status、user_id写入共享数据区。调用check_return_policy入参从共享数据区取order_id和user_id校验售后政策把policy_allowed和return_deadline写入共享数据区。如果policy_allowed为真调用create_return_order入参取order_id和user_id生成退货单写入共享数据区。调用send_order_notification入参取order_id和用户渠道偏好发送通知。整个过程里模型只在最开始看到用户的原始诉求然后按编排脚本逐级执行数据依赖由引擎解析不让模型自己记忆或传递中间结果。这样做的好处是单个技能的输入输出模式是固定的错误率低组合流程是显式定义的可读性高而且模型上下文只保留用户原始诉求和最终结果不会越滚越臃肿。3.3 失败回滚与重试策略不允许半套动作留在系统里多技能编排还有一个很现实的痛如果流程执行到第三步失败了前两步已经产生的副作用怎么处理我见过很多 agent 项目在这块偷懒结果就是用户收到一条“您的退货单已生成”的通知但后台根本没有生成退货单或者反过来退货单生成了但用户没收到任何消息。agent-skills 里我给组合技能配了两个保护机制补偿回滚和幂等重试。补偿回滚的思路是为每个写操作技能预先定义对应的“逆向技能”。比如create_return_order的逆向操作是cancel_return_ordersend_order_notification的逆向操作是withdraw_notification实践中撤回通知很难做到更常见的做法是记录一条通知纠错日志。流程下一步失败时引擎按逆序依次调用已经执行过的技能的逆向操作把系统状态尽量恢复到此前。补偿不是万无一失的比如短信已经发出去了你没办法真正撤回。所以对这类外部副作用我采用的是“先记录后发送”策略技能先把通知记录写入自己的消息表状态为pending等所有前置步骤都成功之后再由一个独立的异步发送器去真正发送。这样即使流程中途失败也只需要把状态改成failed用户不会收到“幽灵通知”。重试策略则遵循一个简单原则读操作无脑重试写操作看幂等键。读操作超时或返回网络错误可以自动重试两到三次间隔逐渐拉大。写操作如果失败先检查幂等键是否已经处理过没处理过才允许重试处理过就直接返回原结果避免重复写入。这个原则看着简单实际研发里经常被忽略真出问题时都是线上事故级别的代价。4. 技能评估与迭代让你的 agent 越用越稳4.1 自动化评估不量化就谈不上优化没有评估体系的 agent 项目本质上就是靠感觉开发。我在 agent-skills 里建立了一套自动化的技能质量评估流水线核心指标分成三个维度选择准确率、执行成功率、业务完成率。选择准确率衡量的是给定一组测试请求模型选中的技能是否跟人工标注的期望技能一致。这一项反映的是技能描述够不够清晰、检索召回是否靠谱。执行成功率衡量的是技能被调用后能不能在规定的超时时间内正常返回且返回值通过 schema 校验。业务完成率则是端到端的指标模拟一个完整业务场景看组合技能走完全流程的比例有多高比如“完成一次售后换货申请”的成功率。我每周跑一次评估把结果跟上一周对比。选择准确率下降优先怀疑最近改了哪个技能描述或者加了新技能导致模型产生了混淆执行成功率下降优先查最近变更过的技能代码和依赖系统看是不是接口参数改了没同步。这套评估流程看着简单但持久跑下来agent 的稳定性提升非常明显。测试集的建设也有讲究。我维护了一个业务场景库每类场景至少 20 条真实用户请求是从线上日志里抽样脱敏得到的另加 5 条边缘 case比如“订单号带了空格”“用户重复提交换货申请”这类。边缘 case 的价值在于它专门用来检验技能的前置条件和参数校验是否够严。4.2 回归测试技能也需要“体检报告”自动化评估跑完之后我会生成本周的技能体检报告格式是一张表维度指标上周本周变化说明选择准确率售后类技能93.2%91.8%-1.4%疑似受新增技能干扰执行成功率发送通知98.5%98.7%0.2%稳定执行成功率生成退货单96.1%93.4%-2.7%等待下游接口超时业务完成率售后换货流程88.7%86.2%-2.5%受退货单技能拖累平均时延全部技能420ms458ms38ms网络波动待观察这张表最大的作用不是好看而是能让所有变更都有据可查。比如因为我调整了某个技能的参数描述导致选择准确率掉了 1.4 个百分点通过对比版本就能很快定位到变量而不是摸黑猜。回归测试的执行方式我最初用的是离线批量跑把所有测试请求喂给调度器看选技能和执行结果。后来发现一个问题离线跑的时候 mock 了下游接口但 mock 数据跟真实数据存在差异导致测试结果看起来正常真实环境却状况百出。现在我改成线上线下两套并行线上先灰度接入一个小比例的流量用真实请求验证技能选择的正确性离线保留一套 mock 环境用于快速迭代两者互相补充。4.3 技能迭代节奏小步快跑别一次大改技能系统的迭代节奏我的经验是“小步快跑每次只改一点点”。最忌讳的事是突然给系统加上一批新技能或者一次性重写十几个技能的描述然后出了问题根本没法定位是谁的锅。每次迭代我遵守几个原则一次只改一个技能改完立刻评估看到指标稳定了再动下一个。技能描述的小改动可能比新增一个技能对整体准确率的影响更大。因为模型在候选技能列表里做选择时每个技能 description 的用词会互相影响你改了一个可能连带影响另一个的选中率。新增技能先以灰度状态注册enabled: false只参与检索召回不参与最终调用。跑几天观察模型选择情况确认描述没造成混淆再把它启用。这是一个非常安全的开关强烈建议保留。废弃技能不要立即物理删除先禁用让历史日志和评估数据继续留存。确认没有流量依赖之后再考虑清理。我见过不少团队把 agent 当成一次性的“智能功能”来开发上线后就不管了。实际上技能体系更像一套运营系统要持续调参、持续评估、持续治理谁把它当成静态系统谁早晚被线上事故教育。5. 常见问题与排查实录5.1 模型就是不按技能走怎么办这是 agent-skills 上线前最常遇到的问题。模型明明看到了技能列表却选择在回复正文里直接给出答案而不是调用技能。排查思路有三个方向第一个方向检查技能描述和用户请求之间的语义距离。如果模型觉得“我直接回答也行不需要调工具”它就倾向不调用。对策是把技能描述写得跟业务流程强相关比如“当用户请求涉及订单状态变更时必须调用本技能”而不是 “用于查询订单状态返回订单信息”。第二个方向检查技能是否被正确纳入候选列表。调度器做召回过滤时可能因为标签匹配问题把正确技能漏掉了。我在排查时会在日志里打出来“候选技能列表”和“最终选择”一眼就能看出是召回阶段丢了还是选择阶段错了。第三个方向检查模型在少样本示例中的行为。模型对示例的模仿倾向非常强如果你给的示例里类似请求都是直接回答不调技能的那模型大概率会模仿。我会在每个组合技能的 prompt 里固定放一个“必须调技能”的示例并在最后用显式指令强调遇到此类请求调用技能是强制步骤。5.2 参数幻觉模型填出了不存在的值参数幻觉会发生在模型以为某个业务字段存在但系统里根本没有这个字段。比如模型给订单查询技能传了一个order_type参数而实际上这个系统没有订单类型的概念。半年前我面对这个问题直接改参数校验加校验规则。后来发现治标不治本因为模型的幻觉参数千奇百怪你永远堵不完。更有效的办法是在给出技能定义时参数只有description和required不给示例值一旦给了示例值模型会倾向于照着填哪怕示例跟用户场景不匹配。还有一招是从 prompt 层面限制在调度器的系统提示里明确写“参数必须严格来自用户请求原文或者来自共享数据区不得自行推断业务字段”。这两个来源之外的值一律拒绝。我自己用了之后参数幻觉频率降低了六成左右剩下的靠参数校验兜底。最严重的一种情况是模型把用户请求里的口语表述直接当参数填比如把“用户说尽快处理”填进priority字段。这种情况给priority做成枚举类型只接受low / normal / high不满足就报校验错误并让模型重新阅读用户意图。宁可不处理也不要让脏数据流进下游。5.3 技能冲突和上下文爆炸技能冲突最典型的场景是两个技能描述高度相似模型无法区分。解决方式通常是调整其中一个技能的名称前缀和描述措辞让它们在语义空间里的距离拉开。比如“发送站内信”和“发送短信验证码”我把前者改名为notify_user_inapp_message描述里强调“站内信、App 内通知、非短信”后者改名为send_sms_code描述里强调“验证码、短信渠道、六位数字”。改完之后选择准确率从 78% 升到了 95% 以上。命名和描述里明确写出“不是什么”往往比写出“是什么”更有效。上下文爆炸问题主要出在长流程组合技能上。如果组合技能有十几个步骤每步都往共享数据区写数据数据区会越来越大而且写操作日志也非常冗长。我做的优化是共享数据区只保留“后续步骤还会用到的字段”每步执行完根据编排脚本标记淘汰无效字段。流程结束后整个共享数据区可以序列化后存入 trace 日志但不再参与任何模型上下文。6. 一些踩坑后的心得如果你也想做一套 agent 技能体系我建议你先别急着上复杂框架。我自己最大的体会是技能的边界和质量比技能的数量重要得多。一百个定义模糊的技能不如二十个边界清晰的技能好用。每次新增技能前先问自己三个问题这个技能能不能被复用它跟已有技能的边界是否清晰它的参数是否干净、可校验如果有一个问题答不上来先停下。还有一点想特别提醒agent 的稳定性不是靠模型而是靠工程约束。让模型理解意图让代码保证执行让日志记录一切这三件事各司其职agent 才会变得可靠。别迷信某个大模型的能力提升也别轻视确定性的流程控制把两者结合起来才是 agent 能真正进入业务场景的关键。我现在每次迭代 agent-skills最优先看的仍然是指标变化和线上日志而不是某个模型又变聪明了多少。
📌 标签:
工业官网
设计趋势
AI 建站
SEO
获取完整报告 →
RELATED ARTICLES
推荐阅读
2026/10/28 14:00:14
ThunderAgent:Dynamo脚本加速与任务代理实战详解
2026/10/28 14:00:14
Codex 修完 Bug 就算完?用失败样例、命令与回归记录搭一个最小评估环
2026/10/28 14:00:14
ECC的三重含义:椭圆曲线加密、SAP ERP与内存纠错
2026/10/28 15:16:25
GRFBUNet与低光增强模块融合方案解析
2026/10/28 15:16:25
把“无可挑剔”变成一套可执行的标准:从完美主义到高效交付
2026/10/28 15:16:25
大模型安全红队评估:框架设计与实战技术
2026/10/28 15:16:25
易语言WebSocket通讯源码解析:握手、帧封装与断线重连
2026/10/28 15:16:25
t3code:命令行代码生成器,用模板引擎终结重复劳动
2026/10/28 15:11:22
DCReg:面向退化场景的可诊断式LiDAR配准方法
2026/10/28 0:00:12
Stable Diffusion文生图全解析:从CLIP到UNet的扩散模型原理与实战
2026/10/28 0:00:12
从 Notebook 到 Python 项目:如何重构量化行情获取代码?
2026/10/28 0:00:12
2026版人形机器人标准:具身智能与模块化设计解析
2026/10/28 12:27:10
全国计算机二级C语言真题PDF高效刷题指南
2026/10/27 9:24:40
requests+BeautifulSoup:爬虫抓取h1标题的入门实战指南
2026/10/28 12:37:46
递推最小二乘参数辨识MATLAB编程:二阶系统与渐消记忆实现
2026/10/25 22:15:16
我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频
2026/10/26 11:29:48
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证
2026/10/25 12:42:56
2026 大模型集体涨价:用 Python 做企业 Token 成本测算与选型避坑(附配置)