拓冰建站拓冰建站
首页 / 资讯中心 / 正文

从函数调用到技能网络:AI Agent技能管理实战解析

SkillNet 这个方向解决的是 AI Agent 在技能变多之后不知道怎么管的问题。很多刚接触 Agent 开发的人会以为只要把功能函数写出来再放进系统提示词里告诉模型“你可以调用这些”就够了。但等技能数量超过十个甚至几十个模型会开始选错工具、参数频繁传错、多个技能之间配合不起来。SkillNet 的思路就是把技能当成一套可注册、可检索、可编排、可调用的网络资产让 Agent 在面对任务时不是靠运气选工具而是有一条确定的链路注册、检索、组合、调用。这里先说明SkillNet 这个名字来自 EvoAgentX 系列讨论里的技术方向它不是某一家独有的封闭系统下面很多内容是通用设计方法可以直接套到自己项目里。我会按实际落地顺序先讲它解决什么问题再拆注册和检索然后讲组合编排最后给参数和排查经验。1. 先理解 SkillNet解决的是“技能怎么管”的问题1.1 Agent 技能管理的三个问题发现、选择、组合如果手上只有三个技能查天气、查日历、发邮件那不需要什么网络。直接在代码里写分支判断就行。但当技能变成三十个、三百个Agent 会面临三个绕不开的问题。第一个是发现。Agent 根本不知道系统里有哪些技能或者知道存在但不知道这个技能适不适合当前任务。比如用户说“帮我整理一下这个季度的销售数据”如果系统里有一个数据分析技能但它的描述写的是“执行 SQL 查询并返回结果集”模型很可能没有意识到“整理销售数据”应该调用它。技能写好了Agent 发现不了等于没写。第二个是选择。即便技能被发现模型还要在多个相似技能之间做选择。比如系统同时有“通用网页搜索”“垂直新闻搜索”“本地文档搜索”用户问“最近有什么新闻”模型该调哪个如果候选不区分场景结果经常会选错。第三个是组合。真实任务很少是一个技能能完成的。用户说“帮我查几个开源项目并看看它们的 star 趋势”这就涉及搜索、读取仓库信息、数据处理、生成总结多个环节。如果这些动作要程序员在代码里手动串联那系统就不是 Agent而是一个固定业务流。SkillNet 的核心就是把这三个问题统一处理。注册是为了解决发现检索和路由是为了解决选择编排是为了解决组合。三件事一起设计才能叫“技能网络”否则只是多了一个工具管理表。1.2 和传统 Function Calling 的差异传统函数调用大家已经很熟了。把函数定义传给模型模型根据对话内容决定调哪个函数然后返回结构化参数。这个思路本身没问题也是很多 Agent 框架的底座。但它有两个短板。第一函数列表是平铺的。技能少的时候没问题技能多了之后把所有函数定义塞进上下文不仅 token 消耗高模型还容易在相似函数之间混淆。第二它只解决单次调用。函数调用回答的是“这次该调用哪个”但回答不了“这三个技能之间应该按什么顺序执行”“一个技能失败之后要不要换另一个”。真实多步任务需要编排而编排需要先把技能组织起来。SkillNet 的思路不是抛弃函数调用而是在它上面加两层东西一层是技能检索先把候选范围缩小一层是技能编排把多个技能按照任务需求串成图。函数调用仍然存在只是不再直接暴露给模型而是被上层网络调度。这也是为什么它适合 EvoAgentX 这类多智能体场景。在多 Agent 环境里不同 Agent 需要不同技能集合如果所有技能都全局平铺管理成本会非常高。按需要给每个 Agent 挂载部分技能再加上统一检索才更可控。1.3 什么阶段该引入 SkillNet 思路不是所有项目都需要技能网络。判断标准很简单技能少于 10 个用户请求也比较固定直接维护函数列表甚至写一个简单的工具枚举效率更高。不要为了架构而架构。出现下面这些特征时再考虑引入技能数量持续增长每周都有新的工具函数要接入。同一个用户问题经常需要多个技能配合完成。不同角色、不同 Agent 使用不同的技能集合。产品或运营希望在不改代码的情况下调整 Agent 能力。如果你的项目命中两到三个特征说明问题已经不只是“模型选哪个函数”而是“技能资产怎么组织”。这时候再开始做注册表、检索和编排才有实际收益。过早引入技能网络会变成额外负担。技能很少时维护一套注册表、检索索引和编排配置本身就是成本。技术选型要看场景阶段而不是看概念热不热。2. 技能注册与检索让 Agent 先知道“你有什么能力”2.1 技能注册表是第一步很多人一上来就想做向量检索、做语义匹配但真正动手后才发现最基础的问题是你的技能根本没有结构化登记。技能分散在代码里、文档里甚至只存在于某个同事的脑子里。这种情况做检索没有意义因为检索的是索引索引的前提是数据。技能注册表简单说就是一张清单一个技能一条记录。每条记录至少要包含这些字段skill_id技能唯一标识。name技能名称。description技能描述说明这个技能能干什么、适合什么任务。tags标签方便静态过滤。input_schema输入参数定义。output_schema输出结构定义。permission权限要求。timeout超时时间。enabled是否启用。这张表放在哪里不重要。本地 JSON 文件、SQLite、PostgreSQL、配置中心都可以。初期从 JSON 或数据库表开始就行。注册表的核心价值不是存储而是让后续的检索、路由、编排有唯一数据源。AI Agent 每一次决定“要不要调用技能”“调用哪个技能”都应该基于注册表而不是基于散落在代码里的函数注释。2.2 检索链路静态过滤、向量召回、精排注册表建好后接下来是检索。技能检索不能只是“把用户的话和技能描述做相似度匹配”要分几层。第一层是静态过滤。先根据当前 Agent 的职责、用户权限、技能标签排除一批明显不该出现的技能。比如当前 Agent 是只读分析型角色那些写操作技能直接过滤掉。这一步能大幅缩小后续检索范围而且几乎不出错。第二层是语义召回。把用户当前请求转成向量和所有技能的描述向量做相似度计算取 Top N。相似度阈值不能一开始就定死要看你用的 embedding 模型和文本表达。一般可以先取 Top 10 到 Top 20后面再精排。第三层是精排。候选已经缩小到十几个可以让 LLM 从里面挑出最合适的 1 到 5 个也可以写规则做最终排序。如果用户需求明显是搜索类就优先搜索类技能如果是信息抽取类就优先文本处理类。整个检索链路最终输出的是“候选技能列表”这个列表才是给 Agent 看到的东西。不要一上来就把全部技能列表暴露给模型。有一个容易被忽视的细节静态过滤要在向量召回之前。过滤是精确判断召回是模糊判断先把确定的排除掉再对剩余部分做模糊匹配准确率和性能都会更好。反过来做的话向量召回阶段会浪费很多计算在无关技能上。2.3 技能描述怎么写直接影响检索命中率技能描述是检索系统最敏感的因素。两个技能功能接近描述写得好检索命中率可以差很多。我一般会按这个格式写 description该技能用于什么场景输入是什么输出是什么适合处理哪些任务不适合处理什么。举个例子{ skill_id: order_query, name: 订单查询, description: 根据订单编号或用户 ID 查询订单状态、金额、物流信息。适合在用户询问订单进度、售后状态时调用。不适合查询历史销售统计报表。, tags: [order, query, ecommerce], input_schema: { type: object, properties: { order_id: {type: string}, user_id: {type: string} }, required: [order_id] }, output_schema: { type: object, properties: { order_id: {type: string}, status: {type: string}, amount: {type: number}, logistics: {type: string} } }, timeout_seconds: 10, permission: read }description 里写了“适合什么时候调用”和“不适合做什么”。这样检索和 LLM 精排时能更好判断当前用户请求是否匹配。每个技能还要写清楚输入输出参数尤其是参数类型。如果 schema 只写{type: object}模型和程序都没法组装参数。至少要标识 required 字段和每个字段的语义。注意技能描述不是给程序员看的注释是给检索模型看的“商品标题”。它最重要的目的是让正确的技能在召回和精排时排到前面。3. 技能编排从单技能到多技能协作3.1 先把单个技能调稳这是我踩过最多坑的地方。很多人做技能网络一上来就设计复杂编排图结果连单个技能都没跑稳。最后排错的时候根本分不清是技能坏了还是编排坏了。所以我的建议非常直接先不做编排先把每个技能当成独立单元跑通。怎么判断单技能跑稳了看三点。第一参数能正确传入。不同调用方传参方式可能不一样有的传 JSON有的传表单你的技能要能用统一方式接住。第二输出可解析。如果技能返回的是自由文本后面对接会很难。尽量返回 JSON 结构成功失败要有明确标识。第三失败要可读。技能异常时不能只是抛一个 “Error”最好返回错误码和简要说明。这样编排层才能决定是重试、换技能还是直接中断。把单技能这条基础打牢再讨论组合编排。这就好比你要先确认每一块积木是完好的搭积木才谈得上有意义。3.2 顺序、分支、并行三种编排方式技能编排的常用方式有三种实际项目里经常混用。顺序编排最直观。技能 A 执行完后把结果作为技能 B 的输入一条线走到底。比如调用搜索技能 → 调用网页读取技能 → 调用总结技能。这种模式最容易实现也最稳定。第一版编排建议只做顺序编排。分支编排支持根据条件走不同技能。比如查询订单状态如果订单已经发货调用物流查询技能如果订单未付款调用支付提醒技能。实现分支时要注意条件判断来源于上一个技能的结构化输出。如果上一个技能输出是非结构化文本分支判断会很不可靠。并行编排是多个技能互不依赖同时执行最后合并结果。比如同时调用多个搜索源等所有结果返回后做聚合。并行调用能显著提升速度但也带来了三个新问题并发控制、部分失败处理、结果合并顺序。第一次做编排时不要急着开并行先把顺序和分支跑熟。很多框架会把编排定义成图结构节点是技能边是数据流。SkillNet 这个方向也一样“可编排”说的就是这个图能灵活配置而不是写死在代码里。3.3 输入输出衔接组合任务最容易踩的坑组合任务里大部分故障不是技能本身出问题而是输入输出结构衔接不上。常见的坑有三个。第一个类型不匹配。上一个技能输出是数组下一个技能输入要求是字符串。没有任何转换程序直接报错或者模型编了一个奇怪的转换逻辑。第二个数据量过大。搜索技能返回了 50 条结果全部传给下一个总结技能token 一下子就爆了。有些项目就在这里频繁触发上下文限制。第三个字段命名不一致。上游输出result下游 schema 期望data匹配不上模型开始猜字段猜错了就出幻觉。怎么解决在编排节点之间增加一层“数据转换”。不要直接把一个技能的 output 原样塞给下一个技能。先做提炼只保留必要字段做类型转换控制长度。这样才能保证链路稳定。如果实在要做复杂场景我建议给每个技能配一个轻量的“输出处理器”。例如搜索技能的输出处理器负责截断、排序、只保留标题和链接总结技能的输入处理器负责接收这些精简字段。有人会觉得这点工作不值得做但在密集组合调用时这个设计能省掉很多无意义的 debug。4. 实际调用过程自动路由和显式编排怎么平衡4.1 什么时候让 Agent 自己选技能技能编排做到后面会面临一个选择流程是写死好还是让 Agent 动态决定好我自己的经验是分任务类型。对于高重复、结果确定性强的任务用显式编排。比如“音频转写 上传音频 → 转写 → 分段 → 整理”。你不需要模型思考要不要先调用天气技能直接按预定流程执行速度更快结果稳定。对于开放式任务让 Agent 动态选择。比如“帮我做一份行业调研报告”模型需要根据当前进度决定下一步是搜索、是读文章还是总结。这个时候如果写死固定流程Agent 就没有智能可言了。SkillNet 的思路里两种模式不是对立的。可以是固定编排图和动态生成的编排图共存。系统里有预定义模板也有运行时让 LLM 根据任务生成的流程。判断标准就一条如果这个任务每次路径都一样就固化下来如果路径经常变化就交给 Agent 动态编排。4.2 参数组装与结果回传调用技能时最容易忽略的是参数组装。技能的输入不只是用户说的话。例如用户说“帮我查一下我在平台的订单”调用订单查询技能时你需要自动带上 user_id。user_id 来自哪里来自登录状态、对话上下文或者用户画像组件。如果忽略这一步技能就只能拿到用户原话无法完成查询。所以调用前要有一个参数组装环节把用户显式参数、Agent 运行状态、前置技能输出结果合并成一个完整的调用参数。结果回传同样需要处理。回传前先做摘要或过滤。假设订单查询技能返回了 1000 行表格不能全塞回给 LLM。要么用程序提取关键字段要么用另一个技能做摘要再把压缩后的结果交给主对话模型。技能网络里的节点并不只是“调用一个函数”它还包括数据的进入和离开。把输入输出控制好整条链路的稳定性会明显提升。4.3 调用失败和成功率低的排查方向技能调用成功率不理想时不要第一个就怀疑大模型选错了技能。按出现概率排序我一般会看这么几个方向。优先看技能描述与用户意图是否匹配。用户问“我的东西送到哪了”你的技能描述如果只写“根据订单编号查询订单金额”模型匹配不上很正常。这种问题改代码没用改 description 和 tags 才有效。其次看参数组装。很多调用失败不是技能不行是参数没传对。比如 user_id 字段存在但进程里没取到或者类型是字符串但 schema 定义成数字。到日志里看实际 payload基本能确认。再看候选技能数量。有些项目在提示词里塞了 20 个工具模型经常把相似工具搞混。减少候选到 3 到 5 个准确率通常会上升。最后才是模型能力问题。如果前面几项都正常还是频繁选错可以换更大的模型或者加规则兜底。但大多数情况下问题出在前三项尤其是描述质量。5. 落地环境、参数阈值与排查链路5.1 需要哪些模块和环境准备SkillNet 不是一个软件包是一个架构方向。真要在项目里落地需要准备这些模块。技能注册模块负责保存技能定义。最简单的实现就是一个数据库表和读写接口。数据量不大用 SQLite 足够技能数量很多再做迁移。检索模块负责找候选技能。如果技能少可以用关键词和标签过滤技能多、表达多样再引入向量检索。向量检索需要 embedding 模型和向量存储。embedding 可以调用在线 API也可以用本地模型。执行模块真正去调用技能对应代码或远程接口。这一步建议和主 Agent 进程解耦。比如技能执行是独立服务Agent 通过 HTTP 或消息队列调用。好处是单个技能超时或异常不会拖垮整个 Agent。编排模块维护技能的调用顺序、分支条件和状态。可以自己写一个简单流程引擎也可以复用现有工作流引擎。第一版不需要图编排顺序执行加条件判断就够。日志和缓存模块容易被忽略。运行日志要记录每个技能从检索、选到执行、返回的完整链路。状态缓存用来保存多技能协作过程中的中间状态。环境方面单机开发学习用 8G 内存的机器就够跑注册表、检索和编排逻辑。本地部署开源模型时建议 16G 以上内存显存看模型体积。生产环境建议把技能执行做成独立服务避免单个技能拖垮主进程。具体资源需求以你选型的模型和框架为准。5.2 常用参数建议进入调参阶段后我会重点关注这些参数。参数建议初始值调参方向候选技能数 top_k3 到 5候选越多LLM 选错概率越高相似度阈值先不设观察召回结果召回结果不相关再逐步提高外部技能超时10 到 20 秒外呼接口偏长内部服务偏短内部技能超时3 到 5 秒快速失败优于傻等重试次数1 到 2 次只对网络类和外部接口重试最大编排步数5 到 10防止动态编排进入死循环单次输出长度上限2000 字符左右按下游节点承受能力调整这些参数没有绝对标准。实际值取决于你的模型、技能复杂度和数据规模。我的习惯是先用默认值跑通再基于日志调整。排名靠前的参数是 top_k 和超时。top_k 太大模型容易看花眼超时太短慢接口经常被误杀。先把这两个调稳再看其他参数。5.3 一套可复用的排查顺序最后给一套排查链路技能网络出问题的时候按这个顺序看比乱试快很多。第一步先看注册。技能是否已经写入注册表enabled 是否为 true。很多时候排了半天结果是技能被误下线了。第二步看检索。把用户输入和当前的候选技能列表打印出来确认目标技能是否出现在候选里。如果没出现问题在描述或检索阈值。第三步看路由。模型或规则最终选中了哪个技能。如果选中了错误技能说明精排阶段有问题可能需要减少候选数量或优化描述。第四步看参数组装。查看实际发出去的 payload逐个字段检查类型和值。第五步看执行。技能服务本身有没有正常返回返回码是多少耗时多少。这一步要看技能服务的独立日志而不是只看 Agent 主日志。第六步看结果回传。回传的数据有没有被截断结构是否正常能否被下一个节点解析。每一步都能在日志里定位到对应节点。这也是为什么我强调日志要覆盖整个链路没有链路日志排查基本靠猜。回到 SkillNet 本身。EvoAgentX 系列里讨论的技能网络真正落地的价值不是把工具调用包装得花哨而是让 Agent 在多技能、多步骤、多角色场景下仍然可控。做过一次就会发现能不能跑通首先取决于技能注册表和描述写得好不好稳定不稳定取决于参数组装、输出转换和错误处理完不完善。如果只是学习可以先从 10 个技能的小项目开始把注册、检索、顺序编排跑通再逐步做分支、并行和动态生成编排。等你踩过一轮日志、检索、参数类型的坑再回头看很多“Agent 选错技能”的问题本质上都是工程问题。
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门