当 Agent 开始调用 Skill:复杂度是如何被指数放大的?

发布时间:2026/5/26 20:33:22

当 Agent 开始调用 Skill:复杂度是如何被指数放大的? 一、从线性到指数Agent系统的复杂度本质在传统软件工程中复杂度通常是线性增长的。你增加一个函数系统的复杂度增加一点你增加一个模块系统的复杂度增加一些。虽然有偶然复杂度和本质复杂度之分但总体来说复杂度与代码规模大致呈线性关系。有经验的架构师可以通过良好的设计将复杂度控制在可接受的范围内。但Agent系统完全不同。当Agent开始动态调用Skill时系统的复杂度不是线性增长而是指数级放大。这不是夸张而是由Agent系统的三个根本特征决定的动态决策、组合爆炸、反馈循环。理解这个复杂度是如何被放大的是理解为什么MCP不是锦上添花而是必需品的关键。如果你不相信复杂度会指数放大你就不会理解为什么需要协议层来约束它。如果你不理解约束的必要性你就会成为第四章里那个等到系统崩溃才追悔莫及的陈浩。本章将从数学和工程的角度拆解这个复杂度放大过程。二、复杂度的第一个放大器动态决策vs静态路由在传统软件中调用关系是静态确定的。当你在代码中写下if user_input equals A then call_function_a时这个调用关系在编译时就已经确定。你可以在部署之前通过代码审查、单元测试、集成测试来验证所有的调用路径。即使是最复杂的静态系统比如一个拥有上千个函数的企业资源计划系统其调用关系也是有限的、可枚举的。你可以画出调用图可以计算圈复杂度可以识别出所有可能的执行路径。虽然路径数量可能很大但它是有限的、确定的。Agent系统的调用关系是在运行时动态决定的。Agent收到用户输入后通过大模型的推理来决定下一步调用哪个Skill。这个决策不是基于if else逻辑而是基于用户的自然语言输入这是无限可能的。基于当前的对话历史这是不断变化的。基于Agent的系统Prompt这是静态的但可被上下文覆盖。基于大模型的概率性输出相同输入可能产生不同输出。这意味着你无法在部署之前枚举Agent所有可能的调用路径。调用关系不是代码中写死的而是模型想出来的。假设一个Agent有N个可用的Skill。在静态系统中调用关系是固定的最多N条有向边。但在Agent系统中Agent可以在任何时候调用任何Skill并且可以以任意顺序组合它们。对于一个需要T步才能完成的任务Agent可能的调用序列数量是N的T次方。当N等于二十、T等于五时可能的序列数量是三百二十万。当N等于五十、T等于十时这个数字是五十的十次方约等于九点七乘以十的十六次方比地球上的沙粒还多。当然实际路径远少于这个理论上界因为大模型的输出受到训练数据和Prompt的约束。但关键在于这个空间大到无法穷举测试大到无法用静态规则覆盖大到必然存在你从未预料到的调用路径。而这些从未预料到的路径正是事故的源头。三、复杂度的第二个放大器Skill之间的隐式依赖在传统软件中依赖关系是显式声明的。函数A调用函数B这个依赖在代码中清晰可见。如果你要修改函数B你可以通过静态分析找到所有调用它的函数评估影响范围。但在Agent系统中Skill之间的依赖是隐式的、动态的、不可预测的。当一个Agent按顺序调用Skill A和Skill B即使Skill A和Skill B在代码层面没有任何关系它们之间也产生了时序依赖。Skill B的执行依赖于Skill A的输出。这种依赖不是在代码中声明的而是Agent在运行时即兴创造的。开发者无法提前知道Agent会把哪些Skill组合在一起也无法预测这些组合会产生什么样的副作用。隐式依赖带来几个严重问题。第一变更影响范围不可控。当你修改Skill A的输出格式时你无法知道Agent是否会在某个从未测试过的场景下把这个输出传给Skill B。如果Skill B不兼容新的输出格式系统就会在运行时崩溃。第二状态污染。Skill A可能修改了某个系统状态比如更新了缓存或修改了全局变量。Skill B假设这个状态是某种样子但Skill A的修改可能超出了Skill B的预期。因为没有显式的依赖声明这种状态污染很难被定位。第三事务边界模糊。Agent调用Skill A成功调用Skill B失败。此时系统处于什么状态Skill A的操作是否需要回滚在静态系统中你可以使用数据库事务来保证原子性。但在Agent系统中Skill调用可能跨越多个服务、多个事务边界回滚几乎不可能。有N个Skill时可能的Skill对数量是N乘以N减一除以二这是平方级增长。但Skill之间的隐式依赖不仅存在于二元组还可能存在于三元组、四元组直到任意长度。实际上任何长度大于一的调用序列都可能产生隐式依赖。当N增长时隐式依赖的数量会远远超过显式依赖成为系统复杂度的主要来源。四、复杂度的第三个放大器反馈循环和状态空间爆炸Agent系统是一个闭环控制系统。Agent观察当前状态包括用户输入、对话历史、Skill执行结果做出决策调用某个Skill执行决策观察新状态然后再次决策。这意味着Skill的执行结果会成为Agent下一轮决策的输入。这形成了一个反馈循环。反馈循环在控制理论中是好事它是系统能够自主的基础。但反馈循环也带来了一个经典问题状态空间爆炸。Agent系统的状态包括对话历史这是一段文本序列理论上无限长。包括用户目标用自然语言表达无限可能。包括已执行的Skill列表及其结果。包括系统内部状态如缓存、数据库、外部服务状态。这些状态的组合数量是天文数字。Agent的当前状态几乎总是独一无二的它从未在训练数据中出现过也从未在测试中出现过。状态空间爆炸是致命的原因有三。第一无法穷举测试。你不可能测试Agent在所有可能状态下的行为。即使你只测试百分之一的状态组合测试用例的数量也会超过宇宙的原子数量。第二无法保证行为边界。因为你无法预知Agent会进入什么样的状态你也就无法保证Agent的行为不会超出预期的边界。那个从未见过的状态可能会触发Agent做出从未见过的错误行为。第三错误难以复现。用户报告了一个错误Agent在某种情况下调用了不该调用的Skill。你尝试复现但你的测试环境和生产环境的对话历史、缓存状态、外部服务状态都不同。你可能永远无法复现这个错误也就无法修复它。假设Agent系统只有十个布尔状态变量状态空间大小是二的一千零二十四次方这是一千零二十四很小。但Agent的状态不是布尔变量而是对话历史的长度假设最多一百条消息每条消息来自一万种可能的自然语言表达这已经是一万的一百次方种可能。Skill执行结果每个Skill可能返回成功或失败以及不同的数据这又是指数级。实际上Agent系统的状态空间不仅是指数级的而且是超指数级的状态数量随着对话轮数的增加而指数级增长而对话轮数本身也是一个变量。这种复杂度的量级已经远远超出了传统软件工程的应对能力。这正是为什么需要MCP这样的协议层不是因为它能解决复杂度复杂度是本质的无法被解决而是因为它能约束复杂度将Agent的行为限制在一个可治理的边界内。五、复杂度的第四个放大器概率性决策的不确定性传统软件是确定性的给定相同的输入你总是得到相同的输出。这意味着你可以通过一次测试来验证一个行为并且这个验证结果是永久有效的。Agent系统是概率性的。大模型是一个概率模型给定相同的Prompt和上下文它每次可能生成不同的输出。这意味着同样的用户请求Agent可能做出不同的决策。同样的测试用例通过一次不代表永远通过。一个今天正常工作的Agent明天可能因为模型版本更新而行为异常。确定性系统的复杂度是状态空间乘以路径数量。概率性系统的复杂度需要再乘以一个不确定性因子也就是模型输出的概率分布。假设Agent在某个状态下有三个可能的决策每个决策的概率分别是零点七、零点二、零点一。这意味着系统有三条可能的执行路径每条路径有特定的概率。当系统执行一百步时可能的执行路径数量是三的一百次方每条路径有特定的概率。你不仅要考虑系统可能走哪些路径还要考虑每条路径的概率是多少。而概率本身是模型的一个属性随着模型版本、温度参数、甚至硬件的变化而变化。概率性决策导致两个严重问题。第一测试失效。你测试了一百次Agent都做出了正确决策。你信心满满地上线。第一百零一次模型输出了一个低概率的错误决策系统出问题了。这不是程序错误这是概率性的本质特征。你无法通过增加测试次数来消除它你只能通过约束来降低它的发生概率和影响范围。第二难以调试。当系统出现问题时你问为什么Agent做出了这个错误决策答案是因为模型输出了这个令牌。但这没有提供任何可操作的信息。你不知道是Prompt的问题、上下文的问题、模型本身的问题还是纯粹的随机性。调试一个概率性系统就像调试一个每次运行结果都不同的程序。六、复杂度的乘数效应四个放大器如何叠加前面我们讨论了四个复杂度放大器。动态决策导致调用路径数量指数级增长。隐式依赖导致Skill之间的关系不可预测、不可管理。状态空间爆炸导致系统行为无法被穷举测试。概率性决策引入不确定性使测试和调试失效。这些放大器不是独立的它们会相互叠加产生乘数效应。举一个具体的例子。假设有二十个Skill任务需要五步完成每个Skill有三种可能的输出结果模型在每一步有三个可能的决策。那么可能的调用序列数量约等于二十的五次方也就是三百二十万。可能的执行结果组合约等于三的五次方也就是二百四十三。可能的决策路径约等于三的五次方也就是二百四十三。总的状态空间约等于三百二十万乘以二百四十三乘以二百四十三约等于一百九十亿。这是一百九十亿种可能的状态。而这是一个极其简单的Agent系统只有二十个Skill只执行五步每个Skill只有三种输出。真实系统的数字会比这大无数倍。这个数学分析揭示了一个关键洞察Agent系统的复杂度不是一个可以被管理或优化的问题而是一个必须被约束的问题。你无法通过更好的代码组织、更多的测试、更精细的Prompt来解决这种指数级的复杂度。这不是工程实现的问题而是系统本质的问题。你能做的只有一件事引入约束。通过协议层限制Agent能做什么、不能做什么。通过控制平面在运行时拦截、审计、审批Agent的行为。通过设计将Agent的行为空间限制在一个可治理的范围内。这就是MCP存在的根本原因。七、从复杂度分析到MCP的设计原则理解了复杂度的来源和放大机制就能推导出MCP的核心设计原则。原则一用协议层替代硬编码。指数级增长的调用路径无法通过硬编码覆盖。MCP通过统一的协议让Agent和Skill之间的交互标准化而不是为每一对组合编写定制代码。原则二在Skill层建立治理边界。隐式依赖无法被消除但可以被观测和约束。MCP将所有Skill调用收敛到网关层使得隐式依赖变得可观测、可审计。原则三用策略约束状态空间。状态空间爆炸无法被测试覆盖但可以被策略约束。MCP的权限模型限制了Agent能进入的状态子集将无限的状态空间缩小到可治理的范围。原则四用控制和审计应对概率性。概率性决策无法被消除但可以被控制和审计。MCP的审计日志记录每一次决策和执行人工审批机制在概率性错误发生时提供最后一道防线。Peta这样的MCP控制平面正是这些原则的生产级实现。Peta的网关层统一收口所有调用策略引擎提供确定性的权限控制审计日志提供完整的可观测性审批工作流提供人在回路的安全网。八、小结复杂度放大是MCP存在的数学理由本章的核心结论可以总结为以下几点。第一动态决策导致调用路径数量指数级增长。这是Agent系统复杂度的第一个放大器。第二隐式依赖导致Skill之间的关系不可预测、不可管理。这是第二个放大器。第三状态空间爆炸导致系统行为无法被穷举测试。这是第三个放大器。第四概率性决策引入不确定性使测试和调试失效。这是第四个放大器。第五这四个放大器相互叠加将Agent系统的复杂度推到了一个传统软件工程方法无法应对的量级。第六Agent系统的复杂度不是一个可以被管理或优化的问题而是一个必须被约束的问题。MCP就是提供这种约束的协议层。在下一章我们将进一步探讨一个反直觉的观点Skill越多系统越聪明还是越危险这个问题的答案将帮助我们更深刻地理解能力增长和风险增长之间的关系。

相关新闻