条件、并行与汇合

本节目标

设计穷尽且可测试的条件、真正独立的并行分支和明确的汇合策略,并正确处理取消、迟到和部分失败。

条件分支先定义输入域

分支条件应读取已验证字段,而不是从自然语言里临时搜索关键词。例如风险节点输出:

{ // 条件节点的结构化判定结果,供后续 DAG 边选择而非让文本隐式控制流程
  "decision": "manual_review", // 命中人工复核分支;不能把它误当作自动批准或拒绝
  "reason_code": "AMOUNT_THRESHOLD", // 稳定机器原因码,便于指标、审计和规则版本对照
  "score": 0.82 // 仅作为决策证据/阈值输入;实际分支仍由明确规则决定
}

JSONC 教学表示

这是逐行说明版;传给严格 JSON 工作流接口前请删除 // 注释。

decision 必须属于允许枚举;工作流根据 approve / manual_review / reject 选择固定边。若 LLM 生成该对象,schema 通过也只证明结构合法,仍需业务规则检查和风险回退。

每个 switch 都要回答:

  • 空值、未知枚举和新版本值走哪里?
  • 多个条件同时为真时是优先级还是错误?
  • 没有条件命中时是否有安全默认分支?
  • 条件依赖的数据是否在重放时保持一致?

什么才可以并行

两个节点在图上没有依赖,不代表业务上可以安全并行。至少检查:

  1. 不读取对方尚未产生的数据;
  2. 不以不受控方式写同一资源;
  3. 各自有独立超时、幂等键和错误分类;
  4. 一个失败时,另一个已完成结果有明确处理策略;
  5. 总并发受到租户、依赖和成本预算限制。

库存检查和风险检查通常可并行;扣款必须等待二者成功。两个节点同时修改同一订单状态时,需事务、条件更新或状态版本,不能靠“通常很快”避免竞态。

汇合不是只有“等全部”

汇合方式继续条件必须定义的失败语义
all所有必需分支成功任一失败是否取消、等待或补偿其他分支
first success任一成功其余分支如何取消,晚到结果是否忽略
quorum达到明确数量/权重重复结果、超时和未达阈值如何处理
best effort收集期限内可用结果输出必须显式列出缺失和失败项

“取消”通常是协作信号,不保证正在执行的外部调用立即停止。已经提交的迟到结果必须经过状态版本和幂等规则,不能覆盖汇合后的最终状态。

Fan-out / Fan-in

批量处理 10 万个文件时,不要一次创建 10 万个内存任务。安全做法是:

  1. 分页产生工作项并记录总量或游标;
  2. 使用有上限的并发和每依赖限流;
  3. 每项有稳定 ID、attempt 和结果状态;
  4. fan-in 去重并核对成功、失败、跳过和仍在运行的数量;
  5. 到达截止时间后执行预先声明的 all/quorum/best-effort 策略。

消息系统可能重复投递;fan-in 计数不能简单 count += 1,应按唯一工作项记录状态,否则重复成功会让汇合提前满足。

flowchart LR
    A["已冻结的 item 清单 / revision"] --> B["有界 fan-out"]
    B --> C1["item-001: branch_id / operation_id / receipt"]
    B --> C2["item-002: branch_id / operation_id / receipt"]
    B --> C3["item-N: branch_id / operation_id / receipt"]
    C1 --> D["按唯一 item 状态汇合"]
    C2 --> D
    C3 --> D
    D --> E{"全部/配额/deadline 合同"}
    E -->|终态已核实| F["发布或下一步骤"]
    E -->|未知/缺失| G["对账、补偿或人工处理"]

每个 branch 应有稳定 branch_id,每个外部副作用另有 operation_id/幂等键。receipt 仅表示下游接受了哪个意图;汇合只能按受控状态表中的已核实 outcome 计数。这样既能识别重复消息,也能避免把“请求已发出”或 Agent 的自然语言声称误算为成功。

Agent 并行的额外约束

多个 Agent 并发检索或分析时,还要控制共享上下文污染、重复工具调用、同一文件写入和冲突结论。推荐让每个分支只产出不可变结果,汇合节点执行确定性校验与去重;若要合并自然语言结论,保留来源、branch_id、检索/模型版本和证据边界,不让“最后完成者”覆盖所有证据。跨分支写同一资源时,仍须在执行点重新授权并以版本/CAS 决定胜者;trace 或分支标签只用于关联,不能授权写入。

一个可验证的部分失败表

场景工作流状态自动动作人工可见证据
风险检查暂时超时running/retrying有限重试attempt 与错误码
库存永久不足business_rejected不执行扣款库存拒绝原因
风险通过、库存未知waiting/reconciliation查询幂等记录与外部终态operation_id、最后回执与状态
一项批处理失败failed/partial依策略重试或转人工失败 item ID 列表
汇合后收到旧结果unchanged拒绝旧状态版本迟到事件审计记录

常见错误与排查

  • 并行列表每次顺序不同:执行与日志排序使用稳定定义顺序,不依赖集合遍历。
  • first success 后重复提交:结果提交做 compare-and-set,其他分支迟到结果只记录不生效。
  • best effort 对下游伪装成完整结果:合同中加入 missing_items、failed_items 与 complete。
  • fan-in 提前结束:按唯一 item ID 去重,核对期望集合而不是累加消息数。
  • 把回执计为成功:记录 receipt 与 outcome 两个字段;只有后者满足汇合合同才推进。
  • 模型分支没有安全默认:未知输出转验证失败或人工处理,不自动选择高风险动作。

练习

设计“并行检查 100 个文件,全部通过后发布”的 fan-out/fan-in:

  1. 定义每个 item 的稳定 ID。
  2. 设定最大并发、单项超时和整体 deadline。
  3. 说明第 99 项超时、第 100 项迟到时的状态。
  4. 模拟第 50 项成功消息重复到达,验证不会提前发布。
  5. 将策略改成 95% quorum,说明哪些失败仍必须阻止发布。

自测

  1. “图上无依赖”为什么不足以证明可以并行?
  2. 取消信号为何不能替代幂等和迟到结果检查?
  3. all、quorum 和 best effort 的输出合同有何不同?
  4. Agent 分支结果汇合时为什么要保留证据来源?

下一步

继续 调度、超时、重试与背压。

参考资料