s16:一个并行分支失败后,journal 提前关闭,其他 Agent 的成功结果无法保存

Author: Bryce-borderCreated Sep 11, 2026Updated Sep 11, 2026

s16:一个并行分支失败后,journal 提前关闭,其他 Agent 的成功结果无法保存

问题

在 main 的 0dcafa2ae053a1ddd6a72f265431104b08a5aa13 上, ExecutionState.parallel()pipeline() 都使用默认的 asyncio.gather()。 一个分支抛出异常时,gather() 会立即向外抛错,但其他分支仍然在运行。

此时 WorkflowTool._call_locked() 会标记失败、关闭 journal、写入最终快照, 再发送 task_notification 并释放运行锁。其他 Agent 随后返回成功结果时, 会尝试写入已经关闭的 journal,导致结果无法保存。

这会影响本章介绍的断点续跑:已经执行成功的模型调用没有进入缓存,resume 时 可能需要重新调用,最终用量快照也可能遗漏这部分工作。

复现

已在 Linux / Python 3.12 环境中复现,使用受控 runner,不需要 API Key 或真实模型请求。

  1. 启动一个包含两个并行分支的工作流,让第一个分支等待一个事件。
  2. 确认第一个分支已经启动后,让第二个分支抛出 RuntimeError
  3. 此时第一个分支尚未完成,WorkflowTool.call() 就已经返回失败。
  4. 放行第一个分支,让它通过现有 MockAgentRunner 完成 ctx.agent()
  5. 该分支写入 journal 时出现 I/O operation on closed file,成功结果没有保存。

原版的复现输出如下:

Finalized before sibling completed: True
Late child errors: ['I/O operation on closed file.']
Saved successful results: 0
Final workflow status: failed

另外,针对 parallelpipeline 补充的两项回归测试,在修复前均失败。

建议修改

parallel() 使用 return_exceptions=True,等待已启动的分支结束后, 再抛出其中的异常。pipeline() 复用这一行为,每个 item 仍独立经过各阶段。

这样成功分支可以在 journal 关闭前保存结果,恢复时就能复用。 代价是失败通知需要等待其他分支结束。直接取消等待中的协程并不能停止 asyncio.to_thread() 中已经发出的模型请求,因此这里建议先等待分支结束。

目前已准备最小修复、现有测试文件中的回归用例,以及中英日三语章节说明。 想先确认:这一失败路径是否属于 s16 希望覆盖的教学范围?如果方向合适,再提交关联 PR。

说明:本次使用 Codex 协助分析和准备修改,复现及测试已在本地运行。

Source: shareAI-lab/learn-claude-code