返回文章列表
AgentTool Orchestration并发一致性可靠性

工具调用冲突调度:串行、并行与仲裁器怎么选(Agent Orchestration)

当 Agent 同时调用多个工具时,真正的难题不是“能不能并行”,而是“并行后怎么保证一致性与可恢复”。本文把工具冲突分成资源冲突、数据依赖、副作用竞态与配额冲突四类,给出从串行到 DAG 并发的调度策略,并提供可落地的仲裁器(arbiter)设计与实现清单。

2026年3月4日
Synthly 团队
预计阅读 15 分钟
Agent 并发调用多个工具时的调度、仲裁与一致性控制示意图

📷 Photo by Jonathan Borba via Pexels

你以为的问题是“并行”,实际的问题是“一致性”

把工具调用想成数据库事务会更接近现实:

  • 读请求(Read):可重试、可缓存
  • 写请求(Write):有副作用,需要幂等与补偿

当你把两类请求混着并行,冲突就出现了。

本文默认你已经具备结构化工具调用的基本功(schema、回执、容错)。如果还没有,建议先看:


一、先分类:工具冲突到底有哪些?

把冲突分清,调度策略才不会变成玄学。

1)资源冲突(Resource Contention)

  • 同一个账号的速率限制(API rate limit)
  • 同一份文件的写锁
  • 同一条会话的“唯一进行中任务”

2)数据依赖(Data Dependency)

  • B 的输入来自 A 的输出(显式依赖)
  • B 的决策需要 A 的回执字段(隐式依赖)

3)副作用竞态(Side-effect Race)

  • 并行发两封重复邮件
  • 并行创建两张重复工单
  • 并行修改同一条记录,后写覆盖前写

4)配额/预算冲突(Budget Conflict)

  • token/费用预算耗尽
  • 工具调用次数超限
  • 端到端时延超限(p95 目标)

结论:并发不是“快”,而是“需要治理”


二、三种基础调度模型:串行、受控并行、DAG 并行

1)串行(Serial):默认方案,先把正确性做稳

适用:

  • 高副作用任务(写操作多)
  • 工具不稳定、失败率高
  • 没有补偿机制

串行的关键不是“顺序执行”,而是:

  • 每步有回执校验
  • 写操作幂等(见下文)
  • 失败可在检查点重入

2)受控并行(Controlled Parallel):并行读、串行写

很多业务的最优解是:

  • 读请求尽量并行(查资料、拉配置、读数据库)
  • 写请求严格串行或按资源加锁(发信、下单、写库)

这能拿到大部分性能收益,同时控制风险面。

3)DAG 并行(Task Graph):用依赖图明确可并行边界

当任务能被拆成依赖明确的子任务时,DAG 是最清晰的表达。

一个简化示意(Mermaid):

flowchart LR
  A[解析用户意图] --> B[拉取联系人列表]
  A --> C[生成邮件草稿]
  B --> D[校验联系人权限]
  C --> E[合规模板检查]
  D --> F[发送邮件(写)]
  E --> F

注意:DAG 并行的前提是“节点契约清晰”。否则你只是在把不确定性扩散到更多节点。


三、仲裁器(Arbiter):把“并发决策权”从模型收回来

一个可上线的系统,建议把这些决策做成规则/策略,而不是让模型即兴决定:

  • 是否可并行
  • 是否需要锁
  • 重试次数与退避
  • 超时预算
  • 风险动作是否需要人工确认(HITL)

你可以把仲裁器看成“运行时安全壳”。

1)仲裁器的最小职责

  • 资源锁:按 resourceKey(例如 user:123mailbox:abc)加锁
  • 幂等键:为所有写操作生成 idempotencyKey
  • 预算管理:token/tool/time 三类预算
  • 策略分级:对不同工具/不同风险等级应用不同重试/降级

2)一个最小的接口形态(TypeScript 伪代码)

type ToolRisk = 'READ' | 'WRITE_LOW' | 'WRITE_HIGH';

type ToolRequest = {
  tool: string;
  risk: ToolRisk;
  resourceKey?: string;
  idempotencyKey?: string;
  timeoutMs: number;
  maxRetries: number;
};

type ArbiterDecision =
  | { action: 'ALLOW' }
  | { action: 'QUEUE'; reason: string }
  | { action: 'DENY'; reason: string }
  | { action: 'REQUIRE_APPROVAL'; reason: string };

interface Arbiter {
  decide(req: ToolRequest): Promise<ArbiterDecision>;
  onResult(req: ToolRequest, result: unknown): Promise<void>;
}

这不是“工作流引擎”,但已经能解决多数稳定性问题。


四、一致性与可恢复:并发系统的两条生命线

1)幂等:并发与重试的基础设施

强烈建议:

  • 所有写操作都带 idempotencyKey
  • 工具侧尽可能支持“幂等创建”(server-side idempotency)
  • 不支持时,在你的系统侧做去重(先查再写/写前锁)

2)补偿(Compensation):不要迷信回滚

很多外部系统不支持真正回滚(发出去的邮件收不回)。

补偿思路:

  • 创建后立刻能“撤销/关闭/标记作废”
  • 发送前改为“生成草稿 + 人工确认”
  • 写操作拆成两段:预提交(prepare)→ 提交(commit)

3)乐观并发 vs 悲观锁

  • 悲观锁:简单可靠,但吞吐受限
  • 乐观并发:吞吐更高,但需要冲突检测与补偿

对于 Agent 系统,建议:

  • 默认悲观锁(尤其是写操作)
  • 对“读多写少”的路径,逐步引入乐观并发

五、如何选:一张工程决策表

场景推荐调度原因
写操作多、不可逆、工具不稳定串行 + 锁 + 审批风险面大,先稳
读操作多、写操作少且可幂等受控并行(并行读、串行写)性能收益大、风险可控
子任务依赖清晰、节点契约稳定DAG 并行 + 仲裁器可并行边界明确
工具经常 429/超时队列化 + 退避 + 预算避免重试风暴

六、上线 Checklist(把“并行”变成可运营能力)

  • 冲突分类:资源/依赖/副作用/预算四类都有处理策略
  • 仲裁器:锁、幂等键、预算、重试策略集中管理
  • 事件日志:每次工具调用可追溯(耗时、错误类型、决策)
  • 并行边界:并行读、串行写是默认;DAG 并行需节点契约
  • 补偿方案:高风险写操作有撤销/作废/草稿机制
  • 保护阈值:单任务调用次数上限、全链路超时预算

常见问题

我能不能让模型自己决定哪些步骤并行?

可以做探索,但不建议作为生产默认。并行决策涉及风险与资源治理,更适合用仲裁器的规则来控制。模型可以“提议并行”,但最终执行应由系统裁决。

并行后怎么向用户展示过程?

建议用事件流(Event Stream)而不是“聊天拼接”。每个节点有 status(queued/running/succeeded/failed)与可点击的回执摘要。前端实践可以参考:

想看更多工程化文章见 /articles,也可以在 /apps/new 体验 Agent 能力。