
AI Agent 的能力边界正在从“会不会更多技能”转移到“能不能在合适的时机调用技能”。在 EvoAgentX Talk 里聊到 SkillNet 这个方向时我有一个很强烈的判断可编排的智能体技能网络才真正决定了一个 Agent 是“工具集合”还是“复杂任务执行者”。如果技能只是零散地挂在系统里模型再强也不知道这一单该调哪个、哪几个可以组合、组合之间的先后顺序又是什么。SkillNet 想解决的本质上就是让技能之间产生关系能被检索到、能被组合起来、能在一次任务里被稳定调用。先把结论放在这里以后评价一个 Agent不再只看它接了多少个大模型、有多少个 API而是看它的技能网络能不能被检索、组合、编排。不是一个技能多强而是技能之间配合得多好。1. 单个技能再强也解决不了“技能之间怎么配合”的问题1.1 复杂任务真正吃掉时间的不是单个步骤而是步骤之间的衔接很多刚开始做 Agent 的人会陷入一个误区只要我给它足够多的工具它就能处理足够复杂的任务。实际上不是。我见过一个很典型的场景。让 Agent 生成一份运营周报它需要四件事从指标库拉数据、根据数据生成一段解读、把解读配到模板里、最后导出成文档。拆开来看这四个能力都有现成的 API 或函数。可一旦串起来问题就来了第一步拉到的数据格式是不是第二步生成解读时需要的格式解读要用到指标的历史趋势第一步有没有把这个趋势一起返回第三步填充模板时如果某段解读超过了模板的字符限制是截断还是重新生成第四步导出失败时要不要重跑第三步这些问题没有一个属于“单个技能行不行”全部落在“技能之间怎么衔接”。你会发现Agent 的失败通常不是某一个工具坏了而是步骤之间的数据交接、状态传递、异常处理没有设计好。这也是为什么单次调用很快乐一旦做真实任务就处处卡壳。1.2 function calling 只解决了“能调用”没解决“什么时候调用哪些技能”很多人会把 function calling 和技能网络混为一谈。其实它们不是一层的问题。function calling 解决的是“模型能不能发起一次函数调用”它让模型不再只是输出一段文字而是能触发外部系统动作。这一步当然很重要。但它只回答了“能不能调”并没有回答当前上下文里哪些技能是相关的这些技能之间有没有顺序依赖有没有多个技能可以并行执行如果某个技能返回的结果不符合预期下一步是换技能、重试还是终止任务举一个更容易理解的类比。function calling 相当于给了员工一部电话他能拨号。但拨给谁、先拨谁、对方占线怎么办、聊完之后要做什么这些属于业务流程和组织协同的问题。SkillNet 想做的更像是后者把技能放进一张网络里让 Agent 知道谁和谁相邻谁依赖谁谁可以替代谁。一个没有技能网络的 Agent就算把所有 API 都塞进上下文它在决策时仍然是“盲选”。它不知道技能之间的相似度、依赖关系和使用边界只能靠模型对函数描述的语感去猜。这种猜测在技能少的时候还能用技能一旦超过几十个命中率就会明显下降。1.3 技能网络的核心价值让技能从静态清单变成可协作的节点所以我理解的 SkillNet不是一个“技能列表”而是一张有结构的图。在这张图里每个技能是一个节点。节点上有能力描述、输入输出、版本、运行状态、依赖关系。节点之间可以有边表示“这个技能的输出可以成为那个技能的输入”“这两个技能不能同时执行”“这个技能必须在那个技能之后运行”。当 Agent 接下一个复杂任务时它做的事情不是“从列表里挑一个函数”而是在技能网络里“找一条路径”。路径就是一组技能组合。找到路径之后它再按路径去调用调用的中间结果在节点之间传递。到了这一步Agent 已经不再是简单的函数调用器而变成一个会做任务拆解和执行编排的执行器。这也是为什么“可编排”会成为关键词。可编排意味着技能的调用顺序不是写死在代码里的而是可以根据任务目标、上下文条件和执行中间结果动态决定的。动态决定的正确性恰恰取决于技能网络结构设计得好不好。2. 技能节点应该长什么样可检索、可组合、可调用的前提2.1 技能描述的核心是“边界”不是“功能广告”要让技能能被检索、组合、调用前提是技能节点必须能被 Agent 理解。很多团队第一步就输在这里技能描述写得像产品宣传页。比如“本技能可以智能分析各类数据并提供深度洞察”“本技能是一个强大的文档处理工具”。这句话模型读完之后根本无法判断它应该在哪个环节使用更不敢确定它输出什么。我更建议把技能描述重点放在边界上。一个技能至少要说清楚四件事它适合什么场景。它不适合什么场景。输入需要什么条件。输出是什么结构。看一个示例结构{ skill_id: fetch_metric, version: 0.1.0, name: 获取业务指标, description: 从指标库中拉取指定时间区间的业务指标返回结构化的时间序列。适合需要最新运营数据的场景不适合做预测、归因和指标口径解释。当输入指标名不存在时会返回错误码 INVALID_METRIC。, input_schema: { type: object, properties: { metric: { type: string, description: 指标名例如 revenue }, start_time: { type: string, description: 开始时间ISO 8601 格式 } }, required: [metric, start_time] }, output_schema: { type: object, properties: { points: { type: array, description: 时间序列点 } } }, preconditions: [指标库账号可用, 指标口径已映射], expected_latency_ms: 2000 }这里面最重要的不是 JSON Schema而是 description 里的“边界”。它明确说了“不适合做什么”这对模型来说反而是最有用的检索信号。因为它能帮助模型在决策时把不相关技能排除掉。2.2 输入、输出、前置条件和副作用是组合的接口技能能不能被组合取决于接口是不是清楚。如果每个技能的输出都是结构化数据且字段语义明确那么“技能 A 的输出作为技能 B 的输入”就是可执行的。如果技能 A 返回一段自然语言技能 B 又要从自然语言里重新抽字段那么组合链路就会非常脆弱。不是跑不通而是每多一层自然语言传递信息损耗和不确定性就高一层。写接口时有几个要点值得留意输出字段不要用模糊命名。data、result、info这类字段对模型不友好。明确输出的时间粒度和口径。比如“返回 2024 年每日收入”和“返回最近 30 天总收入”差异很大。前置条件要可检查。比如“登录状态有效”“文件路径存在”“数据集已加载”这些条件最好能在调用前被 Agent 或编排器显式检查。副作用也要写。比如“会写入数据库”“会发送通知”“会创建临时文件”。如果 Agent 不知道副作用它很可能在应该只读的环节调用了有写入行为的技能。组合的本质是接口匹配。接口越清楚Agent 就越不容易在组合时发明多余的中间步骤。2.3 注册中心与技能索引先让 Agent 知道“有哪些技能、什么时候用”有了技能节点还需要一个地方让 Agent 发现它们。这就是注册中心或技能索引要做的事情。技能索引至少需要两级能力第一级是名字和描述级别的检索。Agent 根据任务目标先用语义检索找到候选技能。第二级是字段级别的匹配。Agent 把当前任务需要的输入字段和技能的输入 schema 做匹配确认“这个技能能接收我手上已有的数据吗”。很多设计会把所有技能描述一次性塞进 Prompt。这在技能数量少的时候没有大问题但技能一旦增多有两个副作用一是 token 消耗快速上升二是无关技能的描述会干扰模型判断。更好的方式是“按需取用”先让 Agent 根据任务生成技能检索请求再从注册中心取回最相关的若干个技能描述放入当前执行上下文。这其实就是 SkillNet 里“检索”环节要解决的问题。不是把网络全部暴露给模型而是给模型一个可以查询的接口让它按任务动态拉取。从工程经验看技能索引的质量往往比模型本身更影响最终效果。索引里的描述写得清楚检索命中率就高描述写得含糊模型再聪明也无从选择。3. 检索、组合、调用把一次任务变成一条可追踪的技能链路3.1 检索不是把所有技能塞进上下文而是按需取用第一次把完整技能列表塞进 Prompt 的人都会发现一个现象技能一多模型开始“眼花缭乱”。它会在无关的技能上反复纠结甚至给出完全错误的调用意图。真正合理的做法是分两步走任务理解。Agent 先拆解当前任务需要哪些能力生成一个候选技能查询。技能检索。从技能索引里召回 Top K 个相关技能再进入决策。比如任务目标是“把会议纪要转成待办事项再同步到项目管理工具”。技能查询可能包含两个方向文本解析能力和项目管理工具对接能力。检索召回之后Agent 只需要在召回结果里做选择而不是从 200 个技能里盲目扫一遍。检索条件可以包括文本语义相似度输入字段匹配度技能适用场景和任务场景的一致性技能最近使用的成功率技能版本是否与当前环境兼容这些信号都能帮助 Agent 在任务开始前就把搜索空间缩小到合理范围。3.2 组合从“多个技能顺序执行”到“依赖关系可控的编排”组合是 SkillNet 里最“值钱”的部分。它决定了 Agent 能不能把一个复杂任务拆解成一组有序的技能调用。最简单的组合是顺序执行A 做完把结果交给 B。很多真实任务其实比顺序复杂至少还会遇到三种情况分支如果 A 的结果是某种类型就走 B如果是另一种类型就走 C。并行A 和 B 互相不依赖可以同时执行等两者结果都回来后再给 C。聚合A、B、C 三个技能的结果需要汇总后再交给 D 做统一处理。在工程上我倾向于先用有向无环图来描述组合关系。每个节点是一个技能调用每条边是一次数据传递。这样做的最大好处是每一步都有据可查。你可以知道一条任务链路里到底执行了哪几个技能、哪个技能是瓶颈、哪个节点失败了、失败发生在什么数据上。如果不用图而是让模型每次从头开始想“先做什么、再做什么”那么相同任务每次执行路径都可能不一样结果稳定性会很差。注意这里说的不是“完全禁止模型动态决策”而是动态决策要遵循一个可追踪的结构。模型可以决定分支走向但不能让整条执行链变成一团乱麻。3.3 调用参数填充、状态传递、超时与重试至少要有一条观测链路到了真正调用技能的一步很多细节会决定成败。首先是参数填充。模型经常会把日期格式写错、把必填字段漏掉、把枚举值传成自由文本。所以调用前最好有一层校验把模型生成的参数和技能的 input_schema 做一次硬校验。不合法就让模型基于报错信息重新生成参数而不是直接硬调。其次是状态传递。前一个技能的输出要缓存成一个明确的中间状态让后一个技能可以直接引用不要每次都让模型从对话历史里重新理解。对话历史越长模型越容易丢字段。把关键结果放到结构化状态里能显著减少“明明算出来了后面又忘了”的情况。第三是超时和重试。技能调用有三种失败超时、返回错误码、返回内容不符合预期。三者处理方式不同超时可以重试一次但要注意幂等性。返回错误码需要看是参数错误、权限错误还是服务不可用。返回内容不符合预期通常要继续往下游传递一个“质量标记”而不是直接把错误内容当正常输入。这些信息全部要进日志。没有日志的 Agent 调用本质上就是在赌。出了问题只能重跑重跑还不知道能不能复现。4. 可编排的深层变化从人定义流程到 Agent 在约束内组合流程4.1 静态流水线适合确定性任务动态编排适合非确定任务很多人一听“可编排”第一反应是把流程画成一个固定的流水线每个节点固定执行某个技能。这种静态流水线也有价值它适合任务类型非常固定的场景比如“上传文件 - 解析 - 校验 - 入库”。这类场景用传统 workflow 引擎就够了甚至不需要 Agent。但真实世界里大量任务是非确定性的。同一个目标今天的数据来源可能不同用户的表达方式可能不同中间某个步骤的结果可能让后续方案完全不同。这时候固定流程就不够用了。动态编排的意思是Agent 根据任务目标在技能网络里选择一条合适的路径而且这条路径可能随执行过程变化。比如第一步调用结果的置信度很低Agent 可以选择先调用清洗技能再去做分析如果置信度高就直接分析。这种能力很有吸引力但它不适合作为第一个版本的目标。第一步还是先把静态链路跑稳再逐步让模型参与分支决策。4.2 编排要同时处理分支、并行、聚合和上下文共享当任务复杂到一定程度编排器至少要能表达四类控制逻辑顺序技能 A 完成后执行技能 B。分支基于某个条件选择技能 B 或技能 C。并行同时执行技能 D 和技能 E等待结果汇聚。循环如果技能 F 的输出不满足校验条件则重新调用或换技能 G。很多 Agent 框架在“顺序执行”上做得很好但分支和循环往往被忽略。没有分支复杂一点的判断逻辑就写不进去没有循环出错了就只能直接终止。所以当你选择技能网络方案时先确认它对分支、并行、循环的表示能力够不够。另外还有一个关键问题上下文共享。多个技能执行时它们能看到哪些上下文如果一个技能需要看到前三个技能的所有中间产物而另一个技能只需要看到最终结果那编排器就要有上下文范围的控制不能一股脑全传。4.3 动态编排不是让模型自由发挥而是给策略加上护栏我经常看到一种误导性的说法动态编排就是“让 Agent 自己决定怎么做”听过之后大家以为只要把模型接进来什么流程都不用管了。这是非常危险的误解。动态编排的正确姿势是在安全边界内动态选择。护栏至少有三个技能范围护栏。Agent 只能在已注册的、有权限的技能里选择不能自己去“发明”技能。操作类型护栏。某些技能是只读的某些技能有副作用。只读任务不能调用有写操作的技能。人工确认护栏。当某个技能会产生不可逆影响时编排器要停一下把用户拉进来确认。护栏不是限制 Agent而是让 Agent 的自主性发生在受控范围内。没有护栏的自主在真实系统里大概率会变成不可预测的事故源。尤其是那些涉及发送消息、修改数据、扣减资源的技能一定要有人工批准或者强校验。5. 从 Demo 到生产SkillNet 落地的五个坑和一套排查顺序5.1 五个容易翻车的环节根据我做工程的经验技能网络看起来简单真正落地的坑全在工程细节里。第一技能描述与实际行为不一致。描述说“只读”代码里却写入了数据库。这种不一致一旦出现模型对技能边界的判断就会全面失准。第二技能输入输出缺乏版本管理。技能 A 的返回结构改了技能 B 还按旧字段取数组合链路瞬间断裂。技能网络一旦有多个节点版本一定要跟着接口走。第三没有给技能设置最大执行时间和重试次数。一个慢接口会拖垮整条编排链路一个坏接口会无限重试耗尽资源。第四把所有中间结果都塞到 Prompt 里。多个技能执行完之后中间产物越来越多模型上下文越来越长最后不是模型不会做而是上下文已经装不下。第五缺少人工中断的入口。任务一旦开始用户只能在旁边看着发现问题也没办法及时介入。这种系统的可用性会很差。这些坑都可以归为一个核心问题只关注了“模型能不能做出正确决策”没有关注“系统能不能被安全地运行和维护”。5.2 排查顺序先确定是哪一层坏了再决定修哪里技能网络一旦出问题最常见的错误是“一上来就怀疑模型不行”。但模型通常只是最后一环。更有效的排查顺序是这样先看任务链路是否生成。Agent 有没有把任务拆成有效技能组合如果链路本身不对说明是检索或任务理解的问题。再看技能检索是否命中。它选中的技能是这个任务真正需要的吗描述、索引、召回策略是否需要调。再看参数传递是否正确。节点之间的字段类型、单位、时间口径、枚举值是否匹配。很多组合失败都发生在这一层。再看执行状态。技能本身有没有报错超时权限不足资源不足最后才看模型决策。如果链路、检索、参数、执行全部正常但最终输出不合理才需要优化模型策略或技能定义。这个顺序的核心逻辑是从系统和数据问题里先排除掉确定性错误再处理模型的不确定性。如果链路、参数、权限这些硬伤还在调 Prompt 和换模型都没有意义。5.3 哪些场景现在还不适合上技能网络不是所有任务都应该做成技能网络。以下情况我通常会劝退任务只有三五个固定步骤且流程永远不变。用传统工作流更好。调用频率极低每次调用场景差异极大。搭技能网络的成本很难收回。没有日志和监控基础设施。先补基础设施再用 Agent不然出了事都不知道怎么查。技能边界说不清楚。如果团队自己都描述不清技能适合什么、不适合什么那模型大概率也理解不了。技能网络更适合的场景是技能至少几十个、任务组合方式多样、执行路径需要根据任务动态变化、团队有基本工程化能力。它不是一个“加了就能变智能”的魔法而是一种需要长期维护的基础设施。6. 一套务实的落地路径从小链路到技能网络6.1 先跑通一条最小技能链路我强烈建议不要第一次就设计一个 20 个技能的大网络。先挑一个真实业务场景把两条、三条技能串成最小链路跑通。所谓跑通标准是输入一个任务目标。Agent 能稳定生成正确的技能组合。每一步技能输出都能正确传递到下一步。最终结果符合业务需求。日志完整记录了整条链路。如果这些条件有一条不满足先原地解决不要继续扩技能。6.2 再补注册、描述和检索验证最小链路跑通后再开始做技能注册中心并认真写每个技能的描述。这一步的建议是每个技能描述都要写“适合什么”和“不适合什么”。输入输出 schema 要落到字段级别不要只写大概。给每个技能加上版本号、负责人、调用成本和最近成功率。单独测试检索效果准备 50 个任务描述看看每次能不能召回正确的技能。检索命中率不用追求 100%但至少要稳定在合理水平。如果命中率不高先优化描述再考虑换检索方案不要一上来就上更重的模型。6.3 最后做编排、观测与版本管理当技能数量和任务复杂度上来之后才需要把编排能力正式化。这时建议补充四样东西编排引擎。支持顺序、分支、并行、循环。状态管理器。保存每个技能输出的中间结果让后续技能按需引用。观测面板。能查看每次任务调用了哪些技能、每个技能耗时多少、失败原因是什么。版本管理。技能接口变更时能追溯哪些任务链路依赖于旧版本。这套路径的核心逻辑是先让单条链路可靠再让技能可发现最后让编排可追踪。每走一步都要有验证标准不要跳步。当你把技能网络的检索、组合、调用都做稳之后再回来看会发现Agent 的能力不再取决于某一个模型的“临场发挥”而取决于你把它放进了怎样的技能网络。这个网络有没有边界、有没有索引、有没有路径、有没有反馈决定了 Agent 到底是一个偶尔好用的原型还是一次可以长期依赖的生产能力。对大多数团队来说今天最该做的不是继续堆功能而是把自己已有的技能一个个定义清楚然后连成网络。这一步做得越扎实Agent 的上限就越高。