并行工具调用:用因果提交屏障收敛分支结果
同一计划里的工具分支并行完成,并不意味着最快返回者可以先修改事实。本文设计分支结果暂存、依赖闭包、决策版本与事务性提交屏障,让乱序结果只产生一次可解释的下游推进。
本文要点
- 主题聚焦:AI Agent、MCP、DevOps
- 适合人群:独立开发者、全栈工程师、AI 应用开发者
- 核心价值:快速理解背景、实践路径、常见误区和工程建议
旅行 Agent 同时查询航班、签证规则和预算,看似只需 Promise.all。但航班先返回后若直接预订,稍后到达的签证结果可能已经否决行程;价格查询超时后重试,两份不同报价又可能分别唤醒下游。并行缩短的是等待时间,不应改变“哪些证据共同授权一次动作”的因果条件。
Agent 运行时状态机定义整个运行如何规划、等待和恢复;本篇只处理同一计划节点展开的多个工具分支如何收敛。多 Agent 结果溯源与冲突仲裁决定多个候选主张如何取舍,而这里更早:在预注册的依赖尚未满足前,任何候选都没有进入仲裁或产生效果的资格。
冻结分支集合和闭包条件
执行前发布不可变 parallelToolPlan.v1,包含 planId、revision、canonical state version、policy epoch、主体、每个 branch ID、工具 descriptor、输入摘要、branchMode、依赖边、deadline 和 barrier policy。分支只允许 READ_ONLY、已验证 DRY_RUN 或 EFFECT_INTENT_ONLY;网关按 descriptor 和能力拦截外部写,单靠自报 effectClass 不算准入。ALL_REQUIRED 适合否决性检查;QUORUM(k) 只适合合同已说明可互换的独立来源。运行中看见慢分支后临时降低 k,会把性能优化变成业务规则变更。
schema: parallelToolPlan.v1
plan: trip-42@7
baseStateVersion: 311
barrier: ALL_REQUIRED
branches:
- {id: fare, tool: flight.quote@4, mode: READ_ONLY, required: true}
- {id: visa, tool: travel.requirements@9, mode: READ_ONLY, required: true}
- {id: budget, tool: wallet.limit@2, mode: READ_ONLY, required: true}
decisionContract: tripDecision@5
每个 branch 状态为 PENDING -> RUNNING -> SUCCEEDED | FAILED | CANCELLED_CONFIRMED。本地 deadline 先进入 WAIT_EXPIRED;若远端终态未知则进入 OUTCOME_UNKNOWN 并使 barrier 不可提交,不能把“没等到”伪装成远端停止。结果先写隔离 staging,键为 (planId, revision, branchId, attemptId),保存 request/result digest、工具版本、网关执行 receipt 与 completeness。重复回调摘要不同标记 RESULT_EQUIVOCATION。只有 policy 明确允许缺失时,已确认的失败才能转为带理由的 NOT_REQUIRED。
W3C PROV 的 entity、activity 与 derivation 关系可表达“决策用了哪些分支结果”,但 provenance 图本身不会提供串行化。plan 聚合状态限定为 OPEN -> COLLECTING -> READY_TO_DECIDE -> COMMITTING -> COMMITTED,取消或不可满足时进入 ABORTED。聚合器从 staging 重算所需集合和 resultRoot;消息中的“已全部完成”布尔值不可信。
屏障只有一个提交口
唯一接受点是 commitDecision 数据库事务。它锁定 plan 记录,核对 revision、当前 canonical state version、policy/cancel generation、required branch 的互斥终态、result root 与 decision contract;随后写不可变 decision receipt、将 plan 置为 COMMITTED,并插入唯一 (planId, revision) 的下游 outbox。PostgreSQL 的事务隔离和唯一约束可实现这一原子边界,但应用仍要在条件更新中携带版本;一次普通 SELECT 后再 UPDATE 不能阻止并发 replanning。
下游只消费 decision receipt,不能订阅单个 branch 的 success 事件。预订、发信或改数据只能由屏障后的 outbox 发起,并携带业务幂等键和当前授权;并行分支最多生成 effect intent。若旧工具无法证明只读,也不支持 prepare/release、稳定 effect ID 与结果查询,就不得进入并行集合。MCP 超时、重试与幂等约束分支调用,多 Agent 任务 Claim阻止迟到 attempt;二者都不能替代 barrier。
重规划会产生 revision 8,并原子提升旧 revision 的 cancel generation。旧分支可以完成并留作审计,但它的 result root 永远不能满足新 plan。资源在并行期间变化时,commit 必须比较 baseStateVersion 或重新验证,例如预算余额从 311 变为 312,就让 plan 进入 STALE_BASE,而不是把三个内部一致的旧结果提交到新世界。PostgreSQL 事务隔离解释数据库层异常,本协议额外连接外部证据版本。
对每一种完成顺序做故障注入
测试生成全部 branch 完成排列,并注入重复成功、一个分支永久挂起、deadline 后远端迟到写、假只读工具访问写端点、两个聚合器并发、outbox ACK 丢失、replan 与 commit 竞争、canonical state 在屏障前变化。预期屏障前外部效果为 0、旧 revision 推进为 0、一个 plan revision 的 decision/outbox 均最多一份、required branch 为 UNKNOWN 时提交为 0,任何 equivocation 都阻断自动决策。
量化门要求一万组随机调度与故障轨迹同 reference reducer 的终态一致率 100%;下游事件可回溯完整 required result root 的比例 100%;重复效果为 0;barrier ready 到 commit 的 p99 小于 500 毫秒;超时计划在 60 秒内进入明确终态。上线先 shadow 计算 barrier 而仍由串行路径执行,再对纯读汇总开放 5%,最后接管有副作用的决策。回滚时 CAS 把 executor alias 切回串行实现并提升 plan epoch;旧实现必须读取同一 staging 和 decision receipt,已提交 plan 绝不因切换而重跑。
Related