有依赖关系的工具如何并行执行并处理失败?

考查重点:DAG依赖与失败传播。按依赖调度就绪节点并限制并发,成功后解锁后继;失败区分安全重试、阻断下游与可选降级,已发生的副作用按业务补偿并报告状态

本题目录

参考答案

核心回答

先把工具调用组织成有向无环图 DAG:节点是一次调用,边表示后一个调用需要前一个调用的结果。只并行运行依赖已经满足的节点,用并发上限和超时控制资源;节点成功后保存结果,再解锁后继,需要多个输入的节点要显式等齐。

失败时先判断影响范围和是否适合重试。临时故障且调用可安全重复,可以有限重试;必要依赖失败就阻断相应下游,可选信息缺失则按业务规则降级。取消尚未开始或仍可协作取消的工作,可以减少浪费,但取消等待不代表远端操作停止,更不代表已经完成的写入被回滚。最终返回每个分支的实际状态,需要补偿的副作用单独处理。

用一个依赖图说明哪些调用能并行

以下是假设的订单分析任务,箭头只表示数据依赖:

flowchart LR
    A["A:查订单"] --> B["B:按订单查物流"]
    A --> D["D:生成分析结果"]
    B --> D
    C["C:查天气(可选)"] -.-> D

A 和 C 无数据依赖,可以同时开始;B 要等 A 返回订单信息。D 必须等订单和物流都成功;天气是可选项,可以约定在截止时间前有结果就加入,否则明确省略。虚线表示可选输入,并不要求在天气失败后阻断整项任务。

这也说明“拓扑上入度为零”只是最初的就绪条件。执行过程中要维护依赖是否成功、结果是否可用以及分支是否已终结,不能只看到前驱“不再运行”就释放后继。

调度器需要保存什么

每个节点至少保存依赖、输入输出、状态、尝试次数和截止时间。可以区分 pending、running、succeeded、failed、blocked、cancelled;远端写操作超时但结果未知时,还需要保留“待确认”状态。

调度过程是:

  1. 校验依赖图无环,并检查工具、参数和资源是否可用。
  2. 将当前就绪节点放入队列,在全局或工具级并发限制内启动。
  3. 节点完成后先保存可供下游使用的结果,再更新其后继的依赖状态。
  4. 必要前驱失败且不再重试时,将受影响后继标为阻断;无依赖关系的分支是否继续,按任务的部分完成策略决定。

并发分支写共享状态时,也要规定合并方式。例如按节点编号分别存结果,再统一汇总;不能让两个分支读同一个旧值后互相覆盖。进程内可以用锁或明确的状态归并函数,跨进程需要相应的持久化与一致性机制。

asyncio.gather() 能等待一组调用,但不会自动分析输入依赖、设计状态合并或处理业务补偿。Python 3.11 起的 asyncio.TaskGroup 在一个子任务发生普通异常时,会取消同组其余任务并等待它们结束;这种组级策略也不等于业务依赖图的失败传播规则。

重试之前先判断这次调用是否已经生效

失败情形 处理重点
参数错误、权限拒绝、确定的业务校验失败 修正输入或结束分支,重复调用通常无益
限流、暂时不可用,且重复调用安全 在总时间预算内退避重试,限制次数;支持时遵循服务端等待提示
查询超时 按查询是否有副作用、剩余时间决定重试或降级
扣款、创建订单等写操作超时 先用业务请求号查询状态;有幂等协议才据此安全续试,不能把超时当作“没有执行”

幂等键要绑定同一个业务意图和请求参数,并由接收端实施去重、结果复用或唯一性约束;只在日志里写一个随机编号不会自动产生幂等性。每次技术重试可有不同的 attempt_id,但应保留相同的业务幂等标识。

取消、回滚和补偿是三件事

asyncio 的取消通常在协程下一次可取消的等待点抛出 CancelledError,清理放在 finally 中。已经在线程池执行的函数一般不能靠取消对应的 Future 强行停止;远端服务是否停止,更取决于它是否支持取消协议。

例如 AWS Step Functions 的并行状态失败后,已经调用的 Lambda 函数仍可能继续运行。这说明编排器结束某个分支,并不证明外部副作用没有发生。

如果分支已经创建资源,后续又失败,可以按业务定义撤销资源、释放预留或进入人工核对;这属于补偿操作。补偿也可能失败,应记录并重试或交给后续处理。只有处在同一事务边界内且事务保证覆盖相关写入时,才能依赖事务回滚,不能把多服务补偿说成一次原子回滚。

最终结果应说明哪些节点成功、哪些失败或被阻断、哪些结果未知,以及补偿进度。这样调用方才能继续处理,而不是只收到一个掩盖部分成功的“执行失败”。

对应教程

参考资料

  • 国内:Guide:Workflow、Graph 与 Loop:借鉴节点、边、状态归并以及失败分类的说明,展开就绪调度、共享状态和有限重试;业务补偿另行说明其不具备原子回滚保证。
  • 国内:Datawhale:构建你的 Agent 框架:采用工具注册表配合线程池和 asyncio.gather() 的示例,解释“能并发调用”与“能按依赖调度”之间还差哪些职责。
  • 国内:阿里云:错误处理:参考任务及并行状态的重试、捕获和退避机制,说明重试应有边界,并在重试耗尽后转入明确的失败路径。
  • 国内:腾讯云:工作流编排:采用连线表达输出到输入依赖、无依赖分支可并行以及环路校验的机制,解释示例中工具的启动次序。
  • 国外:Python:协程与任务:核对 TaskGroup 的异常传播、取消等待和协程清理条件,避免把取消描述为强制终止。
  • 国外:AWS:Parallel workflow state:使用并行状态失败后已启动 Lambda 仍可能继续运行的明确约定,解释编排结束与副作用停止之间的区别。
  • 国外:Python:concurrent.futures:依据正在执行的 Future 无法取消的约定,限定线程池工具的取消能力。

相关面经

3 篇

东方财富

AI 应用开发工程师
校招秋招一面
查看原面经 →
工具之间有依赖,怎么并行执行并处理错误?

百度

后端开发
秋招一面
查看原面经 →
Agent 连续调 5 个工具,第二或第三个失败,恢复机制怎么做?

阿里巴巴(淘天)

大模型 Agent
校招
查看原面经 →
项目提到了多个工具调用链路,调度策略是如何设计的?是否有异常fallback策略?

收录题集

3 个来源,共4 份题集

Vibe Coding模拟面试

阿东玩 AI · AgentGuide · 1 条原题

查看题集 →
原文作者
阿东玩AI

Harness 与 Skills

沉默王二 · 王二讲 Agent · 1 条原题

查看题集 →

工程化踩坑:死循环、状态丢失与成本控制

Onefly · zero2Agent · 1 条原题

查看题集 →

OpenClaw 教程面试题

Onefly · zero2Agent · 2 条原题

查看题集 →

相关题目

4 题