Skip to content
预计 1 分钟右侧目录可快速定位

Task、TaskGroup 与 Structured Concurrency ​

前置:asyncio 到底在调度什么:Coroutine、Event Loop 与 Awaitable
核心问题:并发启动多个 Tool 后,如果一个失败或父任务取消,怎样保证其他子任务不会继续失控运行?

启动后台 Tool stream 后应保存在 TaskGroup/registry 中并在 Run 结束时 await/cancel。三个并行读 Tool 放进一个 TaskGroup;一个失败时默认取消 sibling,是否符合业务还需上层 policy。

Mental Model ​

asyncio 的数据流围绕 Task ownership:coroutine 被调度,I/O await 让出 event loop,parent scope 管理 cancellation/timeout,Semaphore/Queue 控制容量。

Task ​

Task 是 Event Loop 对 coroutine 的已调度执行单元。创建 Task 意味着它开始与调用方并发推进,因此必须明确 owner:谁等待结果、谁接收异常、父作用域退出时谁负责取消。

create_task() 后既不保存引用也不 await,是典型 orphan-task 来源。

TaskGroup ​

TaskGroup 把一组 child Task 收进明确作用域。退出作用域前,child 必须全部到达 terminal;child 失败时,Python 会按 TaskGroup 语义协调 sibling cancellation,并通过 ExceptionGroup 汇总异常。

在 Harness 中,它适合表达“这一批 Tool 调用共同属于当前 step”,而不是让后台任务脱离 Run 生命周期。

一批并行 Tool 的生命周期 ​

假设一个研究节点同时查三个数据源:

python
async with asyncio.TaskGroup() as tg:
    a = tg.create_task(search_web())
    b = tg.create_task(search_docs())
    c = tg.create_task(search_db())

如果 search_db() 立刻抛出不可恢复异常,TaskGroup 会把这批工作作为一个作用域收尾;不会出现父节点已经返回失败、search_web() 却还在后台继续跑的状态。

但“一个 sibling 失败就取消全部”是否符合业务,是更上层的 Runtime policy。若允许 partial result,可以把每个 Tool 自己转换成结构化 ToolResult,让 TaskGroup 只负责生命周期,Runtime 再决定是否继续聚合。

gather vs TaskGroup ​

asyncio.gather 是把多个 awaitable 的结果聚合成有序集合的 API,失败/取消策略与 TaskGroup 不同;选择它时应明确是否希望 sibling 独立完成、是否收集异常,而不是把两者视作语法替换。

gather 便于收集多个 awaitable,可选择返回异常;TaskGroup 更强调父子生命周期和失败传播。选择应从业务 failure semantics 出发,而不是追求某个 API 更新。

Structured Concurrency ​

Structured Concurrency 把 child task 的生命周期绑定到 parent scope:父作用域不能在子任务仍失控运行时悄悄结束,异常和 cancellation 也沿明确的父子关系传播。TaskGroup 是 asyncio 中实现这一原则的主要工具。

在 Runtime 中,一批并行 Tool / Node 应有显式 owner;scope 退出前 child 必须完成或取消,避免 orphan task 和异常丢失。

Failure Modes ​

Fire-and-forget Task 脱离父作用域 ​

症状: create task 后不保存/await。

为什么危险: 父 Run 完成或取消时 child 仍运行,异常丢失。

处理: 相关 child 用 TaskGroup/明确 owner,离开 scope 前 terminal。

如何验证 ​

Case断言
Fire-and-forget Task 脱离父作用域父 scope 抛异常时所有 child 已完成/取消。

自测 ​

  1. 父任务启动三个子任务后自己结束,但子任务还在后台跑。Structured Concurrency 为什么认为这种生命周期不完整?
  2. TaskGroup 中一个子任务失败时,其余兄弟任务应怎样处理,才能让批次的取消与错误边界可推理?

Sources ​

AI Engineering · Agent · Harness · Backend Systems