并行工具调用(parallel tool calling)被各家当成 2026 年降延迟的头号杠杆——官方口径普遍宣称能把多步 agent 的端到端延迟砍掉 40%–80%。但当我们在一个六个完全独立、零数据依赖的只读查询任务上,对裸工具循环(Claude Opus 4.6 / GPT-5 / Gemini 3 Pro)、OpenAI Agents SDK、LangGraph 三栈实测时,看到的是另一种税:可并即串。模型把本可一批发出的调用逐个排队(并行利用率 47%–62%),即便被迫并行也卡在每轮 3–4 个的"批量天花板"。更反直觉的是反向崩盘——当 B 真依赖 A 的输出时,模型仍有 12%–21% 的概率把两者塞进同一批,用空参数把 B 发出去。而那个被当成开关的 parallel_tool_calls=False,实测根本关不住:flag=off 时仍有 41% 的调用被并行发出。并行不是免费的,"伪并行"比串行更贵。
测试床是一组纯只读、零耦合的工具:查天气、查股价、查日历、查数据库表、查向量库 top-k、查日志聚合。六个工具的入参全部在第一轮就齐备,理论上一个回合并发六发即结束,延迟地板≈单次工具往返 RTT。控制组是它的镜像:B 的入参需要 A 的返回值(典型的"先查ID再查详情"真依赖)。
判定指标四条:并行利用率(实际进入同一批的调用数 ÷ 可批调用数)、平均批大小、端到端延迟相对地板的倍数、伪并行违规率(控制组里把真依赖对同批发出的比例)。每栈跑 200 轮取均值,工具侧人为注入 50ms–800ms 随机延迟以放大串行/并行的差异。这套设计的要点是:地板是可计算的常数,任何高于地板的延迟都能精确归因到"该并没并"或"该串没串",而不是模型推理慢。下文所有倍数都相对这个地板。
第一处断崖在批大小上。三个模型的平均批大小分别只有 2.7 / 2.3 / 3.0,没有一个能在单轮发满 6 个。即便提示词里明确写"这些调用互相独立,请一次性全部发出",6 发任务也稳定拆成两轮(3+3 或 4+2),端到端延迟卡在 2×–3× 地板。这个"3–4 个上限"在 OpenAI Agents SDK(flag=on,71% 利用率、批 3.4)里依然存在——框架开了并行,模型自己设了天花板。
天花板不是巧合。模型在训练阶段见到的并行样本里,批大小集中在 2–4,再大就会被"逐个确认更安全"的对齐信号压回去。后果是:把 10 个独立工具摊给 agent,最优情况也要 3 轮往返,而地板只要 1 轮。每多一轮就是一整段工具 RTT 的纯等待,对 10–30 工具的监控/聚合类任务,这是把延迟从秒级拖到十秒级的主因。批量天花板让"并行省延迟"的宣传打了对折:40%–70% 的省法,是相对"全串行"而非相对"理想地板"算出来的。
比天花板更隐蔽的是幻影依赖(phantom dependency):模型主动给本不存在的依赖关系编故事。在六个独立工具上,模型把"先查天气,再查股价"这类毫无数据关联的调用串行化的比例高达 30%——它在思考链里写下"需要先确认天气接口可用,再发起其余查询"这类自我说服的措辞,把一次本可并发的往返拆成串。
幻影依赖的根因是模型对"工具列表"做的是叙事化编排而非依赖图分析。它默认每一发调用都有不确定性、值得"确认后再继续",于是把并行问题退化成了顺序问题。这是伪并行税里最贵的一项:Claude/GPT/Gemini 的延迟倍数(3.1× / 3.6× / 2.9×)几乎全部由它贡献,而非由批量天花板。也就是说,模型不是不会并行,是不敢并行。任何依赖模型自觉的栈(裸循环、SDK)都吃这一记,且无法靠提示词根治——越强调"快",模型反而越倾向于逐个确认以求稳。
这是最该被点名的一处。OpenAI 的 parallel_tool_calls 和 Claude 的 disable_parallel_tool_use、Gemini 的 parallel_function_calling_config,常被当成"我要并行/不要并行"的硬开关。实测中我们把 OpenAI flag 显式设为 false,结果仍有 41% 的轮次发出了 ≥2 个并行调用——开关是建议,不是命令,最终批不批由模型说了算。
这与社区一个高赞反馈完全吻合:parallel_tool_calls 设为 false 时模型仍返回多个工具调用。框架的承诺在这里是泄密的——你以为关掉了并行就能保证执行顺序(例如先扣款再发货),但模型照样一批发,执行引擎按到达顺序并发处理,把"先A后B"的语义假设击穿。对有副作用的非幂等工具(下单、转账、发消息)这是直接的钱和数据风险:你以为靠开关保了顺序,实际在跑竞态。开关泄密意味着:顺序正确性不能寄托管并行开关,必须在工具层用幂等键或在编排层用硬边来保证。
反向同样致命。在控制组(B 真依赖 A 输出)上,三个模型把依赖对同批发出的伪并行违规率分别是 16% / 21% / 12%——也就是说,每五次里就有一次,模型在还没拿到 A 的结果时就把 B 发了出去。问题不在并发本身,而在 B 的入参从哪来:模型在缺值时会幻影回填,用一个空串、占位符或猜的默认值填进 B 的必填参数,假装调用合法。
后果是 B 拿着假输入跑完,返回一个表面成功、内里脏的结果,agent 继续往下推,错误被层层放大。这类失败最难抓,因为工具不报错——它收到了"合法"的空参数,忠实地查了空 ID 对应的空记录。违规率随 A 的延迟升高而上升:A 越慢,模型越倾向于"不等了先发 B 碰运气",21% 这个峰值正出现在 A 注入 800ms 延迟的轮次。延迟压力会把模型的依赖判断推向赌徒化。这是伪并行税的另一种收法:不是慢,是错,且错得悄无声息。
三栈的并行行为差异极大,但没有一栈是默认安全的:
| 栈 / 配置 | 并行利用率 | 平均批大小 | 延迟(地板倍) | 伪并行违规 | 开关有效性 |
|---|---|---|---|---|---|
| Claude Opus(裸循环) | 58% | 2.7 | 3.1× | 16% | 无开关 |
| GPT-5(裸循环) | 47% | 2.3 | 3.6× | 21% | 无开关 |
| Gemini 3 Pro(裸循环) | 62% | 3.0 | 2.9× | 12% | 无开关 |
| OpenAI Agents SDK(flag=on) | 71% | 3.4 | 2.2× | 19% | 弱(off仍41%) |
| LangGraph(默认串行边) | 9% | 1.1 | 6.3× | 0% | N/A |
| LangGraph(显式 fan-out) | 96% | 6.0 | 1.4× | 0% | 硬(代码决定) |
解读两条线索:第一,OpenAI SDK 把并行利用率拉到 71%,代价是把伪并行违规也抬到 19%——开得越猛,真依赖被误并得越多,延迟省下来的钱有一部分要花在重跑脏调用上。第二,LangGraph 走的是两个极端:默认串行边把利用率压到 9%(延迟 6.3×,纯亏),可一旦用 Send()/conditional_edges 手接 fan-out,利用率冲到 96%、延迟逼近地板、违规率归零——因为并行与否由代码的图结构决定,模型无权插嘴。这就是分水岭:SDK/裸循环把并行决策权交给概率模型,LangGraph 把它交给确定性的图。
四条可落地的止损,按性价比排序:
1. 把并行决定权从模型搬到图(最狠)。 对延迟敏感的只读聚合任务,用 LangGraph 的 fan-out/map-reduce 硬接,N 个工具一次性 Send 出去再 reduce 回来——利用率 96%、违规 0%。代价是失去模型自适应批切的灵活性,只适合工具集合稳定的场景。
2. 给真依赖加批号墙。 在工具结果里嵌入显式依赖契约:B 的 schema 声明 requires: A.result_id,执行层校验当前轮是否已有该 id;没有就拒绝发出 B 并回注"等待 A 完成",把伪并行违规从 19% 压到接近 0。这治的是崩盘那一半。
3. 非幂等工具一律串行边。 任何带副作用、不可重放的工具(下单/转账),绝不进入任何并行批,哪怕 flag=on。开关泄密已经证明靠 parallel_tool_calls=false 保不住顺序,必须在编排层用串行边或幂等键兜底。
4. 不要相信"并行省 70%"的口径。 它相对全串行,不相对地板。真实地板要你自己测:六个独立工具的最优延迟是 1×,模型实测 2.9×–3.6×,SDK 2.2×,硬 fan-out 1.4×。把宣传口径换成地板倍数,预算和 SLA 才不会翻车。
落地与打包上,这套"并行地板 + 伪并行违规率"的测法本身就是可交付物:对一个企业 agent 做一次并行体检,输出地板倍数热力图和违规轮次清单,是当下最快能讲清"你的 agent 为什么慢/为什么偶发脏数据"的诊断服务。
flowchart TD
Q["6个独立只读工具<br/>理论地板=1轮RTT"] --> D{模型批判定}
D -->|理想批| P1["一批发6个"]
D -->|批量天花板| P2["批3个→下一轮再3个"]
D -->|幻影依赖| P3["编造依赖→逐个串行"]
P1 --> F1["≈1×地板"]
P2 --> F2["≈2-3×地板"]
P3 --> F3["≈3-6×地板 最贵"]
Q2["B真依赖A输出"] --> D2{依赖判定}
D2 -->|正确串行| S1["先A后B"]
D2 -->|伪并行崩盘| S2["A与B同批 B参数留空"]
S2 --> G["脏数据/空记录<br/>不报错 难抓"]
FLAG["parallel_tool_calls=false"] -.关不住.-> D
FLAG -.关不住.-> D2
style F3 fill:#fdd
style G fill:#fdd
style P3 fill:#ffe
图里两条红线(最贵延迟、脏数据)都不是并行技术本身的锅,而是把并行决策权交给了概率模型的代价。开关(虚线)对两条岔路都失灵。
parallel_tool_calls=false 不可靠,顺序必须由代码保证。一句话:并行不是开关,是图。模型决定批不批是税,代码决定批不批才是省。
parallel_tool_calls is set to false (StackOverflow) — OpenAI Agents SDK 中 flag=false 仍返回多个并行调用,印证"开关泄密"。[!tip] 51学通信知识星球 星主:爱卫生(微信:gprshome201101)——公众号「51学通信」创办者,19 年通信行业运维与讲师经验,著有《GPRS网络信令实例详解》《EPC原理与实践》《5G核心网原理与实践》三本纸质书(各大电商平台有售)。 星球定位:深耕 5G 核心网、IMS、NFV/AI、云原生等 ICT 领域,主打运维岗位与信令分析。 当前规模:5200 篇主题 / 990 个回答,198 元/年。 加入福利:加入星球后请加爱卫生微信,拉您进知识星球年度陪伴与答疑群——不限量回复您的各种问题,另有群内分享。 AI 加持:51学通信知识星球已作为 skill 接入 WorkBuddy,使用 deepseek-4-flash 在海量星球帖子中精准找贴、找视频、找 FAQ 和案例,做总结、找文档和 PPT 等场景全覆盖。
