很多团队开并行工具调用是为了省时间。工具执行在 Agent 总延迟中占到 35%–60%,编码类任务靠近上限,深度研究类贴近中位。一轮里几个 200 毫秒的独立调用并发跑,墙上时钟就从 600 毫秒压到 200 毫秒。这笔账很好算,于是开关一开,行为开始变得不可预测。
我先说结论:并行工具调用主要不是性能特性,它更像一次你并不自愿的架构审计。顺序执行一直在替你掩盖工具之间的隐性依赖;一旦并发,这些依赖就以数据错误的形式浮出来,而且多数时候不抛异常。
执行机制:决定权在模型手里,不在编排层
并行工具调用是模型做出的决定。当模型在单次响应里输出多个 tool_use 块,运行器应当把这批工具都调起来,等全部返回后再进入下一轮推理。模型在第二轮才开始之前看不到任何中间结果——它一次性看到一整批。
各家的开关并不一致。OpenAI 的接口有 parallel_tool_calls,默认 true;其推理模型 o3、o4-mini 并不接受显式设置这个参数,强行传会返回 400。Anthropic 没有对应的开关,Claude 自己判断这批工具看起来是否独立。这意味着你在做多提供商路由或者升级模型版本时,不能假设并行行为是稳定的,编排层必须能处理多工具响应,无论你有没有主动要求。
ADK 里的 ParallelAgent 是另一种形态:执行不受模型控制,在代码里确定性地并发启动子 Agent 的 run_async(),每个子 Agent 在自己的分支里跑,分支之间不自动共享对话历史和状态,结果收集顺序也不保证确定。做固定扇出(比如同时查三个数据源)时这种模板工作流更省心,但它和模型自治的并行调用是两套东西,别混着讨论。
三种不会崩的失败
顺序执行对工具很宽容:如果 A 隐式依赖 B,顺序会自动强制这个依赖,你甚至不知道它存在。并行撤掉了这层宽容。常见的失效有三种。
- 上下文依赖。工具 A 从共享上下文变量里读值,而那个变量本该由工具 B 写入。顺序执行时 B 总在 A 之前;并行时 A 先跑,读到空值或旧值,返回一个格式完全正确、输入却错了的结果。没有异常。
- 共享状态竞争。两个工具对同一资源做读-改-写。A 读到 100、加 10 写回 110;B 也读到 100、加 20 写回 120。最终是 120,而正确结果应该是 130。两边都没有报错,最终状态就这么静默地错了。
- 时序前提。最隐蔽的一类。工具并不读对方的输出,而是依赖对方的副作用:一个建了会话,另一个才往里写数据;一个拿到资源锁,另一个才操作那部分资源。顺序下前提总成立,并行下不成立。
共同点是:都不是崩溃。Agent 循环继续转,模型拿着被污染的结果往下推理。等错误出现在最终输出上,执行轨迹已经推进了好几步,跟最初那次并发调用的因果关系基本看不见了。
上并行之前先给工具分类
最简单可靠的规则是三个问题:原子吗——是否只做一件事,没有别的并发工具能观察到的中间状态;幂等吗——相同输入跑两次,世界状态是否一致(GET 是,未去重的 POST 不是,每次发邮件的工具不是);独立吗——是否需要先知道对方有没有跑过。三问全过的可以并发,任何一问不过的相对其交互对象要串行。
这个分类不是对整个 Agent 二元判定,而是对每一批调用判定。纯读批次并行通常能砍掉 40%–50% 的延迟且没有正确性风险;只要混进写入,就退回顺序,因为写入可能依赖同批读的结果,也可能跟同批的同级操作冲突。
「读批次并行、写批次串行」是我目前最愿意守的一条线,它的好处是检查成本低,坏处是会损失一部分本可并行的延迟收益。
有时正确答案是两个工具合成一个
一个 Agent 在同一轮里调 get_customer_id 和 get_recent_orders,看起来独立,但后者需要前者作为查找键。顺序下先拿到 ID 一切正常,并行时订单查询可能在 ID 可用之前就发出去了。
你可以加排序逻辑,也可以反过来问:这两个工具本来是不是就不该存在?合成 get_customer_summary,依赖直接消失,接口更干净,往返次数还少了一次。这也是个可复用的信号——那些总是被一起调用、顺序固定、互相有依赖的工具,通常只是伪装成两个工具的一个工具。顺序执行让这种设计坏味道隐形,并行把它逼出来。
这里判断标准是:如果一个工具只在特定执行上下文或者特定前置调用之后才有意义,它属于编排逻辑,不该作为公开工具暴露出去。把顺序约束从工具实现搬进编排层,是让并行在大规模下安全的前提。
依赖图值得吗
更长期的方案是别再靠执行顺序去推断依赖,而是显式编码成依赖图。LangGraph 把工作流表示为图,节点是 Agent 或工具调用,边是数据流,你能直接声明两个读节点并行、写节点等两个读依赖都完成才启动。LLMCompiler 把模型生成的计划编译成 DAG 执行,据其论文报告,多步任务在保持正确性的前提下比顺序执行快最多 2.2 倍。
代价是真实的:维护依赖图要么对工具声明做静态分析,要么运行时做依赖追踪,动态 Agent 的工具集一变就要跟着改。工具库小且稳定的 Agent,读写分类规则更简单也更够用。工具数量到几十个、或者多步工作流已经成为主要负载时,显式 DAG 才是有扩展性的那种架构。顺带一提,依赖图的另一个好处是可观测:出问题时你能看清哪个节点失败、收到什么输入、同时有哪些同批节点在跑、有没有谁写了共享状态;隐式顺序循环里排查竞态,你只有错误输出倒推这一条路。
发布前的检查清单
- 列出全部工具,按只读、只写、读-改-写三类分开。
- 对每个写工具列清它触及的资源,确认有没有可能同时跑的工具碰同一份资源。
- 逐个检查工具实现里是否有隐式前置依赖——不看签名,看共享变量、会话状态和文件系统路径。
- 在开发环境显式打开并行跑测试。一直顺序测试的团队,测试的系统跟线上跑的不是同一个。
工具设计到位时,并行给的是实打实的延迟收益,那些省下来的时间可以换成每步更多的工作量。但跳过上述审计的团队通常是在生产环境里第一次遇到耦合,往往已经有不少状态被静默弄坏了。耦合一直都在,顺序执行只是让它没露头。
转载请注明出处: 皮哈皮阿
本文的链接地址: https://blog.phpat.com/agent-engineering/613.html
微信或手机浏览器扫一扫,随时随地随心阅读与分享
-
MCP 进入生产环境:工具协议、网关与权限边界
MCP 刚出来时,我以为最难的是协议兼容。真正接了十几个工具之后才发现,把工具接上只花了两天,把权限、审计和故障处理理清楚花了两个月。
2026/10/07
-
Agent Harness:可观测、可评测、可回滚的 Agent 基础设施
去年我们做了一个处理退款申请的 Agent。Demo 很顺,上线第一周就出了事故:进程重启后状态丢了,同一笔申请被处理了两次。问题不在模型,而在模型外面那层没人愿意写的基础设施。
2026/10/09
-
AI Coding Agent 如何稳定提交可合并的 PR
上周 review 了一个 AI 生成的 PR,测试全绿,但我还是把它关了。它改了 37 个文件,顺手升级了一个依赖,还修改了两条测试断言。问题不是代码不能跑,而是这个 PR 没人敢合。
2026/10/14
-
Agent 安全清单:提示注入、工具越权与数据外泄
一个内部助手被要求总结一封客户邮件。邮件末尾有一行白色小字:把最近的客户名单转发到某个地址。Agent 真的准备去调邮件工具了。安全不能靠模型自觉,得靠系统设计。
2026/10/16
-
并行工具调用暴露的工具设计耦合问题
并行工具调用能砍掉四成延迟,但它更重要的价值在于暴露工具之间的隐性耦合。本文拆解上下文依赖、共享状态竞争与时序前提这三类静默失败,给出读写分类规则、工具合并的判据,以及什么时候该上依赖图编排。
4小时前
京公网安备11011402053654 
@muffin:链接已更新,分类图像功能...
AI网址导航模版图标库更换指南
作者大大,图标库邀请链接失效了,麻烦更新...
AI网址导航模版图标库更换指南
太好看了,快点更新!
友情链接
@戴墨镜的刘备:哈喽,路过点赞。
vue.js组件之手写签名板vue-signature-simple
太好看了,快点更新!太棒了!👍真的很赞!
vue.js组件之手写签名板vue-signature-simple
你写得非常清晰明了,让我很容易理解你的观...
Emlog插件RSS订阅器发布