多调用、并行与依赖

本节目标

  • 区分同一回合多 call 与可安全并行;
  • 用依赖图而非模型输出顺序决定调度;
  • 定义 all、best effort、first success 等汇合语义;
  • 处理部分成功、并发冲突、补偿和审批。

多个 Call 不等于并行许可

模型在一轮中返回多个调用,只说明它提出了多个动作。执行前为每个 call 标注:

  • 读取/写入的资源;
  • 输入依赖;
  • 是否需要前一步输出;
  • tenant/权限与审批;
  • 幂等和并发控制;
  • 失败对其他动作的影响;
  • 是否可取消/补偿。

只有独立、无资源竞争、已分别授权、失败可安全汇合且并行不改变业务语义时才并发。

建立 DAG

案例:“为客户下单并发确认邮件”:

lookup_customer ─┐
                 ├→ create_order → send_confirmation
check_inventory ─┘
  • lookup_customer 与 check_inventory 可并行;
  • create_order 依赖两者;
  • send_confirmation 需要新 order ID,必须后置;
  • 若创建订单成功而邮件失败,订单是否保留由业务策略决定。

模型的 call 数组顺序不能代替这张依赖图。若依赖只能在运行时发现,应拆回下一模型回合或让确定性 workflow 编排。

三种汇合策略

策略何时用输出要求
all所有结果缺一不可任一失败则不进入下游
best effort独立搜索/采集保留成功项和明确缺失项
first success等价来源竞速取消/忽略其余并记录赢家
quorum多来源一致性预先定义数量与冲突处理

每个结果仍按 call ID 独立封装,不能把结果拼成一段让模型猜对应关系。

汇合时也不能“按数组位置 zip”。本库的 validate_result_set 先按 (provider response ID, call ID) 建立唯一映射,再对每项重算 operation、主体、工具合同和 call binding;结果列表调换顺序可以接受,缺失、重复、未知身份或交换审计身份必须失败。这个检查解决关联完整性,不代表各 handler 已经可以安全并行。

并发限制与尾延迟

并行可以降低总时长,也会:

  • 放大下游瞬时负载;
  • 触发限流;
  • 增加部分失败;
  • 产生队头阻塞;
  • 让重试风暴更严重。

使用最大并发、每依赖连接池、总 deadline、取消和熔断。观测单 call 与 fan-out 整体的 p95/p99,而不是只看平均。

写操作与竞争

两个 call 同时修改同一订单可能出现丢失更新。常见控制:

  • 数据库事务;
  • 乐观版本号/If-Match;
  • compare-and-set;
  • 资源级串行队列;
  • 唯一约束;
  • 幂等键;
  • 业务状态机。

“模型认为可并行”不是并发控制。

审批应覆盖完整预览

多个写调用可先生成一张计划:

1. 创建订单:商品 A × 2,¥200
2. 使用优惠券 C10,-¥10
3. 发送确认到 user@example.com

批准应绑定每项参数和组合版本。执行中参数或计划变化时重新批准。不要先批准“下单”,后续让模型自由增加收件人或金额。

部分成功与补偿

跨服务通常没有单一 ACID 事务。需要明确:

  • 继续:邮件失败但订单有效,进入 PARTIAL_SUCCESS 并重试邮件;
  • 补偿:订单创建后库存预留失败,取消订单;
  • 人工修复:补偿也失败,生成诊断与工单;
  • 不可逆:消息已发送,不能靠删除记录“撤销”。

补偿不是 rollback 的同义词,它本身也可能失败且需要幂等。

Provider 并行能力

截至 2026-07-19:

  • OpenAI 文档说明模型可在一轮提出多个 function call,也可用 parallel_tool_calls=false 限制为零或一个;当前使用内置工具时不能进行 parallel function calling,fine-tuned model 在一轮调用多个函数时 strict 也会被关闭;
  • Gemini 文档描述 parallel 与 compositional function calling;
  • Anthropic 客户端 tool use 可以返回多个 tool_use block。

这些能力只影响调用表达。应用仍需决定调度、依赖、并发、权限和汇合。能力不足时安全串行化,并用端到端测试证明语义不变。

动手实践

给定:

  1. 查客户;
  2. 查库存;
  3. 创建订单;
  4. 扣款;
  5. 发送确认;
  6. 写审计记录。

完成:

  • 画 DAG;
  • 标出读/写资源;
  • 选择每个 fan-out 的汇合策略;
  • 定义创建成功、扣款失败的最终状态;
  • 为重试分配幂等键;
  • 说明哪些 call 需要统一预览审批;
  • 写出补偿失败后的人工路径。

常见错误

  • Provider 返回多个 call 就全部并行;
  • 用数组位置而不是 call ID 关联结果;
  • “邮件失败”让整个订单显示为未发生;
  • 多写操作共享一个含糊审批;
  • 并发写没有版本或唯一约束;
  • 补偿操作没有幂等与监控;
  • 并发失败后所有分支同步无限重试。

自测

  1. 判断两个 call 可并行需要哪些条件?
  2. All 与 best effort 的用户可见语义有何不同?
  3. 为什么补偿不等于数据库 rollback?
  4. Provider parallel tool calls 与应用并发调度有何区别?
  5. 多写操作的审批为什么要绑定完整计划?

下一步:结果、错误与不可信数据。

参考资料

来源获取日期:2026-07-19。并行/内置工具兼容规则应在目标 API 与模型版本上复核。