LLM Agent 中的并行工具调用:你可能尚未意识到的耦合测试
大多数工程师之所以选择并行工具调用,是因为他们希望自己的 Agent 运行得更快。工具执行占 Agent 总延迟的 35–60%,具体取决于工作负载——编码任务处于高端,深度研究任务则处于中端。同时运行独立的调用是显而易见的优化方案。但接下来的情况却让大多数团队感到意外。
一旦你启用了并行执行,工具设计中隐藏的每一个假设都会变得显而易见。在顺序执行时可靠工作的工具,在并发运行时可能会悄无声息地失效。原本稳定的行为变得不可预测,而且失败往往不会产生错误——只是在充满自信地返回一个错误的答案。
并行工具调用主要不是一项性能特性。它是一次非自愿的架构审计。
并行工具执行的实际工作原理
在深入探讨失败模式之前,有必要精确了解其机制。并行工具执行是模型做出的决定,而不是你的编排层做出的决定。当模型在单次响应中发出多个 tool_use 块时,你的运行器(runner)应当调用所有这些工具,并在下一步推理之前统一返回它们的结果。模型看不到中间结果——它会一次性看到所有内容。
大多数框架都会提供一个标志来抑制这种行为。OpenAI 的 API 包含 parallel_tool_calls,默认值为 true。Anthropic 没有提供直接的标志——相反,Claude 会根据请求的工具是否看起来独立来做出决定。这种行为在不同模型系列中也不一致:OpenAI 的推理模型(o3、o4-mini)要么忽略,要么完全拒绝 parallel_tool_calls 参数,如果你尝试显式设置它,会返回 400 错误。
这种不一致性在生产环境中至关重要。如果你在多个提供商之间进行路由,或者在模型版本之间进行升级,你不能假设并行行为是稳定的。无论你是否请求过,你的编排层都需要处理多工具响应。
执行模式本身非常直接:当模型在单轮中发出 N 个工具调用时,你的运行器会同时分派所有 N 个调用,等待所有调用完成,并在继续推理之前返回整批结果。延迟的降低完全来自于墙上时钟时间(wall-clock time)的重叠——一批三个独立的 200 ms 工具调用只需 200 ms,而不是 600 ms。
耦合隐藏时的三种失败模式
顺序执行是宽容的。如果工具 A 对工具 B 有隐性依赖,顺序执行会自动强制执行该依赖。你通常甚至不知道这种依赖关系的存在,因为代码一直以相同的顺序运行。并行执行则取消了这种宽容。
上下文依赖(Context dependency):工具 A 悄悄地从一个共享上下文变量中读取数据,而该变量本应由工具 B 填充。在顺序执行中,B 总是运行在 A 之前。在并行执行中,A 在 B 填充上下文之前运行,读取了陈旧或空的数据,并返回了一个看起来有效但基于错误输入计算的结果。没有抛出异常。Agent 带着错误答案继续运行。
共享状态变更(Shared state mutation):两个工具根据在写入完成前读取的状态,向同一个资源(文件、数据库行、缓存值)写入数据。这是经典的“读取-修改-写入”竞态条件。工具 A 读取当前值 (100),计算增量 (+10),并写回 110。工具 B 读取相同的初始值 (100),计算不同的增量 (+20),并写回 120。最终值是 120,但预期的值应该是 130。两个工具都没有报错。这种不一致性是无声的。
执行时序依赖(Execution timing dependency):这是最微妙的失败。一个工具中的逻辑隐式地假设另一个工具已经运行——不是因为它读取了输出,而是因为第一个工具的副作用是第二个工具的前提条件。例如,一个工具创建数据库记录,另一个工具写入相关记录;一个工具初始化会话,另一个工具向该会话添加数据;或者一个工具获取资源锁,另一个工具在锁定的资源上操作。在顺序执行中,前提条件总是能得到满足。在并行执行中则不然。
这些失败都有一个共同点:它们不是崩溃。Agent 循环继续运行,模型处理结果,下一步在损坏的状 态下进行。当错误的输出显现时,执行轨迹已经向前推进了好几步,与原始并行调用的因果联系已变得不可见。
幂等性测试:在并行化之前对工具进行分类
最简单且最可靠的分类方法是在决定是否并发运行每个工具之前,问三个问题。
它是原子的吗(Is it atomic)? 工具是否只做一件事,没有其他并发工具可以观察到的中间状态?搜索查询是原子的——它读取并返回,没有其他事情发生。文件重命名在大多数文件系统中不是原子的——存在一个旧名称已消失而新名称尚未显示的窗口。
它是幂等的吗(Is it idempotent)? 如果这个工具对相同的输入运行两次,最终世界的状态是否相同?GET 请求是幂等的。除非你显式处理去重,否则创建记录的 POST 请求不是幂等的。每次调用都发送通知邮件的工具不是幂等的。
- https://arxiv.org/html/2603.18897
- https://medium.com/google-cloud/what-parallel-tool-calling-revealed-about-my-agent-design-7bd7e0f5f523
- https://agentic-patterns.com/patterns/parallel-tool-execution/
- https://medium.com/@bhagyarana80/llm-agents-and-race-conditions-debugging-multi-tool-ai-with-langgraph-b0dcbf14fa67
- https://community.openai.com/t/what-models-support-parallel_tool_calls-and-when-to-use-it/1310788
- https://futureagi.substack.com/p/why-do-multi-agent-llm-systems-fail
- https://www.getmaxim.ai/articles/multi-agent-system-reliability-failure-patterns-root-causes-and-production-validation-strategies/
