尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
Agent技能体系:从堆工具函数到技能总线的工程实践
最近排查一个内部客服机器人项目时我发现了一个很有意思的现象系统里明明接了十几个外部工具但智能体在真实对话里翻来覆去只会用其中两三个其余大部分函数从来没被触发过。负责的同学也很委屈——每个工具的文档都写清楚了描述也改了模型就是“不听话”。后来我们把这个问题的根因挖到底才发现核心不在于某个模型能力不够而在于整个系统缺少一整套把“能力”组织起来的机制。我后来把整套思路整理成了一个叫agent-skills的模块这篇文章就把这套设计从头到尾摊开讲一遍什么是Agent技能体系、为什么它和单纯堆工具函数完全两码事、具体怎么搭、踩过哪些坑。这篇文章适合两类人看一类是正在做智能体应用发现Agent在真实场景下总是不稳定、不可控的开发者另一类是刚入门LLM应用开发想知道除了“写Prompt调函数”之外还能怎么提升系统上限的人。全文不依赖特定框架思路和代码都是通用方案可以直接搬到你自己的项目里。1. 为什么“给Agent多塞几个函数”解决不了问题先聊聊最基础的问题Skills和Tools到底差在哪。很多人第一次听说“Agent技能”这个概念时第一反应是“这不就是给模型多定义几个函数吗”。这个理解不能说错但确实只看到了最表层的东西。1.1 单体函数方案在真实场景下的失控过程我拿当时客服机器人的“退款”功能举例。最初实现很直接给系统定义了一个refund_order(order_id, reason)函数告诉模型“当用户要求退款时调用它”。Demo阶段一切完美一问一答函数乖乖被执行。但一上线就出了问题用户说“我买的东西不合适想退了”模型确实调了退款函数但选错了退款原因参数用户说“帮我看看物流不对我是想退款”模型在同一个上下文里连续调用了物流查询和退款两个函数流程直接乱套更麻烦的是用户只说“我不想要了”没有给订单号模型居然凭空生成了一个订单号传进去返回错误后它又假装退款成功回复用户“已经处理好了”。这些问题单看都是模型调用不准但往深了想其实是设计层面的缺位没有校验参数真实性的机制、没有定义技能触发边界、没有处理“技能被调用了但条件不满足”的情况。你在Demo里看到的成功是模型在理想上下文里的超常发挥而生产环境考验的是系统能不能在信息不全、意图模糊、多轮穿插的情况下兜住底。1.2 Skills的本质带条件、带状态、带编排的执行单元所以agent-skills里对Skill的定义和普通工具函数最大的区别在于一个Skill不仅仅是一个“输入-输出”的函数它是一组完整的能力单元包含四块触发条件明确什么情况下这个技能可以被激活什么情况下绝对不能激活入参校验参数不是模型说什么就信什么要经过规则和上下文的双重校验执行逻辑核心业务动作本身后置状态执行完之后对会话上下文、用户状态、其他技能可见性的影响整个设计借鉴了微服务里的“Bounded Context”思路。每个技能内部可以自由实现但对外通过契约交互互不干涉内部状态。这样一来模型需要做的决策从“我要调哪个函数”变成了“当前情境下该激活哪个技能”后者明显更适合LLM的推理方式。1.3 Skills该拆多细粒度问题是我最先趟出来的经验。一开始我把“退款”做成了一个超大技能里面处理了全部退款子场景部分退款、整单退款、黑户拦截、风控复审。结果就是技能描述长得像一篇小作文模型反而经常误判因为触发条件和各类异常在文本上互相干扰。后来我遵循一个“一行描述原则”如果这个技能没法用一句话说清楚它在什么情况下触发那就拆。拆出来的每个技能保持功能单一、边界清晰。比如退款被拆成了下面几个技能名一句话触发描述备注检查订单可退状态用户发起退款请求且订单号已知时先检查该订单是否满足退款条件不执行退款只做状态确认提交整单退款确认订单可退后执行整单退款需要双重确认参数提交部分退款用户明确只想退其中某个商品时触发需校验商品属于该订单退款进度查询用户询问“退款到哪了/多久到账”时触发只读技能不产生副作用拆细之后模型的选择压力小了很多准确率也明显上来了。2. 技能总线的整体架构与核心数据结构确定要把技能体系做成一等公民之后我就开始搭一套轻量级的“技能总线”所有技能注册到总线上由总线负责路由、执行和审计。后续所有技能都挂在总线上不再直接面向模型裸奔。2.1 三条核心设计原则整个模块的实现围绕三个原则展开这也是和“给模型一堆函数”方案的本质区别所在第一模型只负责表达意图不负责直接触发副作用。模型在对话中输出一个结构化的“技能调用请求”总线先经过校验、权限检查、去重、冲突检测之后才真正执行。这样做是为了避免模型乱来的时候连后悔的机会都没有。第二技能之间不直接通信。技能A执行完之后想把结果交给技能B只能通过总线传递不允许在技能内部直接引用另一个技能的返回值。这样就彻底规避了Agent调用链的循环依赖和无序交织。第三每个技能调用都留审计痕迹。入参、出参、耗时、来源上下文都在总线上记录方便后面做回放和评估。我当时觉得这个“不过就是加日志”结果发现它成了后续排查问题的最强武器。2.2 技能的注册信息到底长什么样先看最核心的数据结构。每个技能注册时都包含这样一份Schemaskill_schema { name: check_order_refundable, description: 当用户表达退款意向且订单号已确认存在时检查该订单是否满足退款条件, trigger_rules: { must_include: [退款, 退货, 不想要了], must_not_include: [已收到退款, 退款到账], requires_order_id: True, intent_confidence: 0.75 }, parameters: { order_id: { type: string, required: True, validation: ^ORD\\d{10}$, source: context_or_user } }, execution: { timeout_ms: 3000, retry_policy: no_retry, idempotent: True }, side_effects: [read_only] }这份Schema最关键的几个点trigger_rules是硬性门槛先把明显不合适的调用挡在外面。比如用户已经明确表示“退款到账了”这时候就不该再触发退款类技能。这不是靠模型自觉而是系统层面的硬规则。parameters.source允许参数来自用户消息或者系统上下文。比如订单号可能用户已经在前几轮给出过模型在这一轮根本不需要再问直接从上下文里取就行省去一轮无意义的追问。idempotent: True标记只读技能可以安全重试有副作用的写操作技能标记为False总线会自动加防重护栏。2.3 技能注册与动态路由总线上维护了一个内存注册表启动时加载所有技能定义运行时可以动态注册新技能。路由决策依赖一个“两段式仲裁机制”先用确定性规则过滤再用模型打分兜底def route_request(request_text, session_context, available_skills): candidates [] for skill in available_skills: # 第一阶段规则过滤 if not pass_trigger_rules(skill, request_text, session_context): continue # 第二阶段语义相关性打分 score compute_semantic_relevance(request_text, skill) if score skill.min_score: candidates.append((score, skill)) # 若唯一候选且评分足够高直接命中 if len(candidates) 1 and candidates[0][0] 0.8: return candidates[0][1] # 多个候选时交给LLM做精细决策 return llm_disambiguate(request_text, candidates)第一阶段的规则匹配是零成本且完全可预测的能把命中范围迅速压缩到两三个真正相关的技能只有候选结果模糊时才会消耗一次模型调用来做最终判断。这样既保证确定性又保留语义理解的灵活性。实测下来约六成请求可以走规则直达完全不需要额外的模型推理成本和延迟都降了。3. 技能编排机制从“单次调用”走向“组合执行”单个技能能跑通只是第一步。真实业务场景几乎都是多个动作的编排先检查订单状态再执行退款然后通知用户结果。这条链路如果让模型在单轮里自己决定编排逻辑很容易出中途断链的问题。所以agent-skills把编排逻辑抽到了代码层用确定的流程来处理多技能组合。3.1 一个典型的三步编排链路以“处理用户退款请求”为例完整编排链路是这样的用户表达退款意向 - Step 1: 解析订单号从当前消息或会话上下文中提取 - Step 2: 调用 check_order_refundable 技能检查订单状态 - 若可退继续 Step 3 - 若不可退终止并生成解释话术 - Step 3: 调用 submit_full_refund 技能执行退款 - Step 4: 调用 notify_user 技能发送结果通知这段逻辑如果用自然语言让模型来做等于让它即兴表演一个可能出错的多步操作但落到代码里就是有序、可控、可回滚的固定流程。我在工程里的做法是给编排器定义了三种节点类型——普通执行、条件分支、并行分发然后用一个progress对象记录每个节点的状态。3.2 并行技能串行依赖问题这里有个很容易忽视的坑多个技能看起来相互独立但内部可能依赖同一个外部资源。比如“检查退款进度”和“查询订单物流”可以并行执行但如果它们都打到同一个老系统接口而那个老系统的连接池上限只有5个并行一多直接Timeout。我在最初版本里贪图效率把所有无依赖技能全部并行结果刚压测就暴露了这个问题。后来做了一个“共享资源标签”机制每个技能在注册时声明自己用到的外部资源组比如[order_service]。编排器在调度时如果发现两个技能存在交集即使逻辑上无依赖也会串行执行避免把底层资源打爆。3.3 编排中的回滚与补偿编排链路变长以后失败处理必须提前设计。尤其是退款这种有副作用的操作“订单已经退款成功但通知用户的那个技能挂了”属于最常见也最容易被忽略的情况。我在总线上实现了两步补偿逻辑每个技能执行前总线记录一个“前置状态快照”。对退款来说前置状态就是“订单处于可退状态”。如果后续技能失败且当前技能标记为requires_rollback总线自动触发对应补偿技能比如cancel_refund指令把订单状态回滚到快照时刻。对于下游通知类技能失败不再回滚上游成功的操作而是放入“待重试队列”在恢复后补发通知。核心原则是读操作不回滚写操作尽量幂等不可幂等时回滚到快照。4. 从单Agent到多Agent场景下的技能分派技能体系在我这边验证得差不多之后我开始往协作场景扩展。团队里同时跑着客服、质检、数据分析三个不同角色的Agent彼此之间如果直接互调会产生大量纠缠不清的级联调用。技能总线在这里正好变成了一个天然的调度平台。4.1 技能改为远程服务注册在多Agent场景下每个技能不再以本地函数形式存在而是包装成远程服务通过总线注册。每个Agent在运行时维护自己的“可见技能列表”列表之外的技能对它是完全不可见的。客服Agent看不到数据分析Agent的内部统计技能需要统计结论时只能调用总线暴露出来“提交分析请求”这一个技能。通过这样的“门面”方式技能边界被硬性隔开。这种模式下技能总线承担了两层路由层级作用示例第一层请求方Agent确定自己需要什么能力的技能客服Agent需要“数据分析类技能”第二层总线在数据分析Agent的候选技能中做仲裁选择“订单量趋势统计”而非“用户画像分析”4.2 防止多Agent互相打断多Agent协作最糟糕的体验是一个Agent正在执行技能另一个Agent带着更高优先级的技能请求冲进来直接把前者的状态清掉了。我在总线里引入了“技能锁”机制每个Agent在开始一个不可中断的写技能前向总线申请对应资源组的排他锁其他请求要么等待要么被拒绝并返回“稍后重试”的提示。有段时间客服Agent执行退款技能时质检Agent同时调用了订单状态查询两边明明都是只读和写操作按理说不冲突。后来发现质检Agent的查询技能内部有“同步订单缓存”的副作用它会去刷新缓存导致客服侧读到的订单状态短暂不一致。这个问题让我对“只读”定义多了个心眼技能标记不能只看功能名得看它到底改了什么。4.3 技能的执行痕迹转化为长期记忆多Agent跑一段时间之后总线里积累了海量的技能调用日志。后来我做了个升级把每次技能调用的特征向量化之后放进记忆池当某个Agent遇到类似请求时能检索到历史上这类技能调用的成功率、常见失败原因然后附带在Prompt后面供参考。举个例子每当用户申请退款时总线会拉取该用户历史订单的退款调用记录如果发现他之前发生过“填写错误退货原因”的情况下一次调用提交退款的技能时系统会在Prompt里追加一条提示“该用户上次退款失败原因为原因填写错误本次请在参数中引导用户选择正确原因”。这个机制上线后退款类技能的首次成功率提升了一个台阶。这也是技能总线附带产生的一个正外部性。5. 环境准备、落地步骤与调试经验最后这部分写给想直接落地的同学。按照下面这套流程花两三天时间就能把最小可行版本跑起来之后再根据业务需要逐步扩展。5.1 最小可行版本的核心模块我的实现里没有引入重型框架核心模块只有三个文件skill_registry.py # 技能注册表负责注册、反注册、动态加载 skill_bus.py # 技能总线路由、校验、调用、审计入口 skill_orchestrator.py # 编排器处理技能组合、顺序、回滚逻辑底层的LLM调用接的是一个统一的llm_client接口方便切换不同模型。skill_bus.py对外暴露的核心接口只有两个def invoke_skill(skill_name, params, session_context): Agent请求执行某个技能这是唯一入口 pass def register_skill(skill_definition): 动态注册一个技能运行时调用不影响线上其他技能 pass所有技能实现都收到统一的(params, session_context)返回统一的SkillResult。不需要在代码里到处实现自己的调用函数这让后续新增技能的改动范围变得很小。5.2 环境配置时的两个细节环境配置阶段有两个容易被忽略的细节一个是技能调用超时和重试策略必须单独设置。有些技能是查询型、天然幂等可以放心重试有些技能是提交型一旦超时不确定是否已生效盲目重试可能造成重复提交。所以我在每个技能的注册Schema里强制要求写明retry_policy不写不允许注册。另一个是所有外部依赖都要做成可Mock的接口。我前期调试时经常遇到这种尴尬订单系统联调环境不稳定导致技能链路反复报错我已经分不清到底是技能逻辑问题还是环境问题了。后来我安排了一个“影子模式”所有外部调用走Mock数据只有彻底验完技能逻辑后才切到真实环境。这一步帮我省了大量无效排障时间。5.3 冷启动调试技巧技能总线上线后最有效的调试方式是把落起来的技能跑一遍“全量调用测试”针对每个技能写一组黄金输入包括正常场景、临界场景、错误场景全部通过后再交给模型在对话里自由触发。我发现很多人跳过了这部分直接让Agent在对话中试结果出问题后很难判断是模型选择错误还是技能实现错误。把这两层分开验证排障效率会完全不一样。再配合总线上的全链路审计日志基本可以做到每个问题都能回放到具体技能和参数。5.4 最常踩的坑技能描述里全是实现细节前前后后一个多月我见过最多的技能质量问题出现在描述写法上。有些同学写技能描述的时候喜欢把内部实现细节写进去比如“本技能调用订单系统的V3接口如果返回500则重试一次”这种内容对模型理解技能触发场景没有任何帮助反而稀释了核心信息。我后来总结了一份技能描述模板大致是这样一个结构第一句什么场景下触发用用户视角描述第二句这个技能做什么一次完整动作第三句绝对不能触发的情况必要时的否定清单其余内容一概不写把描述控制到这个信息密度之后路由命中准确率一直在稳步提升。这也是我建议每个准备做Agent技能体系的人先想清楚的一点技能描述是给模型看的最重要的元数据写作优先级应该排在实际功能实现之上。agent-skills这套体系目前已经在我参与的好几个项目里稳定运行了一段时间整体收益确实明显技能调用的可观测性和确定性大幅提高新增一个技能的平均耗时压缩到半小时以内Agent在真实场景下的表现也从“时好时坏”变成了“行为可预期”。如果你正在被Agent不听使唤的问题折磨不妨先别急着换模型试着在模型和业务动作之间加一层技能总线它的价值会随着技能数量变多而越来越明显。
RELATED

相关推荐

PS5折腾指南:从M.2扩容到Remote Play串流与备份全覆盖

PS5折腾指南:从M.2扩容到Remote Play串流与备份全覆盖

说句实话,PS5买回来头三个月,我也和大多数人一样:接电视、插光盘、下载游戏、玩完就关机。直到有一天我看着主机里只剩下30多GB的剩余空间,才意识到不对劲——这台机器根本不是“装不了游戏”,是我压根没把它往能充分发…

📅 2026/10/13 6:12:24
把 3 小时脑暴变成一张能发群里的导图:SimpleMindMap 本地实操

把 3 小时脑暴变成一张能发群里的导图:SimpleMindMap 本地实操

把 3 小时脑暴变成一张能发群里的导图:SimpleMindMap 本地实操 【免费下载链接】mind-map SimpleMindMap(思绪思维导图):一个强大的思维导图。A powerful mind map. 项目地址: https://gitcode.com/GitHub_Trending/mi/mind-map…

📅 2026/10/13 6:07:23
UI Grid 框架投影运行时(Framework Projection Runtime)规范详解:统一 Angular / React / Solid 适配器的插槽投影生命周期

UI Grid 框架投影运行时(Framework Projection Runtime)规范详解:统一 Angular / React / Solid 适配器的插槽投影生命周期

前端UI组件 【免费下载链接】ui-grid UI Grid: a Data Grid 项目地址: https://gitcode.com/gh_mirrors/ui/ui-grid 点击查看 免费下载 本文深入解读 ui-grid 开源仓库中 openspec/changes/consolidate-framework-adapter-runtime 变更提案所定义的 framework-proj…

📅 2026/10/13 6:07:23
MORE NEWS

更多资讯

📰

Java博客作业

1.JAVA语言基础的总结Java语言基础的核心可以概括为:Java是一门具有语法简单、面向对象、平台无关性、动态特性的语言,程序由类和对象组成,是目前软件设计中优秀的编程语言。通过JVM(Java Virtual Machine , 简称“Java虚拟机”),…

📰

MMAction2 版本演进全解析:从 v0.5.0 到 v1.2.0 的能力版图与迁移脉络

人工智能计算机视觉深度学习 【免费下载链接】mmaction2 OpenMMLabs Next Generation Video Understanding Toolbox and Benchmark 项目地址: https://gitcode.com/gh_mirrors/mm/mmaction2 点击查看 免费下载 MMAction2 是 OpenMMLab 面向视频理解(动作…

📰

电力系统调峰成本量化与分摊的Matlab实现

1. 项目背景与核心挑战电力系统调峰一直是行业内的关键难题,特别是在可再生能源占比不断提升的背景下。去年参与某省级电网调度项目时,我们遇到一个典型案例:某风电场在凌晨2-4点出力突然飙升,导致系统不得不紧急下调3台燃煤机组出…

📰

FFmpeg纯音频HLS推流时间同步问题解决方案

1. 问题现象与背景分析最近在开发一个纯音频直播系统时,遇到了一个典型的流媒体播放问题:使用FFmpeg将纯音频数据推流成HLS格式后,VLC播放器持续报错"ES_OUT_SET_(GROUP_)PCR is called too late (pts_delay increased to 1000 ms)"…

📰

Codex CLI第三方API接入:config.toml配置排错指南

最近帮团队把 Codex CLI 切到公司内部的 OpenAI 兼容网关时,我踩了一整天的 401 和 404,最后发现根因都藏在config.toml的细节里:一个env_key名字写错、多拼了一层/v1、甚至旧登录 token 没清干净,都会让终端看起来像在“玄学报错…

📰

CRM系统前端源码丢失后如何二次开发?AI分析API + Vue3/Vite重建前端实战

最近接手了一个比较典型的企业CRM二次开发项目。客户是一家200多人规模的外贸服务企业,内部销售人员较多,长期使用一套CRM系统管理客户、销售跟进、公海、客户归属等业务。这次客户原本只提出了几个业务调整:取消部门公海;将相关客…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬