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