为什么每个 Family Member 仍是独立 Run
一句话
使用同一份 prepared input 不等于共享同一次运行:每个 family member 都有自己的执行身份、配置、workspace、deadline 和最终记录,需要 capability 时还必须使用自己的 Plan/Broker。
先看一个例子
两个 Agent 使用同一矩阵。Agent A 只能调用 metrics.read,写入 workspace A;Agent B 可以调用另一项能力,写入 workspace B。即使它们处理相同数据,也不能因为属于同一个 family 就共用权限表、execution ID 或目录。
如果 A 超时,系统还要能把 A 记为 timeout,而不是关闭 B 的输入或把 B 也当作失败。若 B 从未运行,关闭 family 时应把它记为 closed_unrun,不能悄悄把这个 runner 留给下一次请求。
真实机制
Host 用 PreparedRunnerConfig 为每个 member 冻结自己的 RunConfig、InvocationRef、可选 Plan/Broker factory 和可选 workspace。创建 runner 时,family 会先检查 artifact/profile/import/memory 与 image 是否兼容,并深拷贝 CapabilityGrants 等可变配置。
Authority 还有更严格的隔离:
- 需要 capability 的 member 使用不同的 Plan 对象和 Broker;即使两个 Plan 的内容等价,也不能复用同一 mutable authority 对象;不使用 capability 时 Plan 与 Broker 同时为空;
- Broker factory 必须绑定该 member 的 exact Plan pointer、Plan digest 和 execution identity;
- Invocation ID、execution ID 和非空 workspace Ref 在整个 family 生命周期内不得重复;
- request 的
run_id必须等于 runner 持有的InvocationRef.ExecutionID; - caller 不能额外传入
trustedPrepare,避免在 sealed family input 之外再偷偷注入状态。
Member 生命周期是:
new → running → terminal → closed
Run 只能成功进入一次。第二次或并发调用会得到 consumed/busy error,不会重新进入 Guest。Family 同时限制总 consumer 数与 active 数:总数上限阻止再创建 runner;active 上限在 Guest 前拒绝本次调用,但保留 runner 的 new 状态,容量释放后可以再调用同一个 runner。只有真正开始后的 success、Guest error、timeout、cancellation 和 trap 才会消费这个 runner。
Runner 在执行前关闭会进入 closed_unrun;执行中关闭会等待当前 Run 按 context cancellation 规则终止。Family 关闭时若仍有 active member,会拒绝关闭;active member 全部结束后,它让未运行的 runner 进入 terminal,释放 image/body/runtime resources,并保持 Close 幂等。
每个 terminal member 产生 body-free record,包含 family/input identity、member/Run/Invocation/execution identity、Plan 与 frozen grants digest、物理处置、outcome,以及可选 final workspace digest。Record 不保存 ndarray、source、response、credential 或 Host path。Acceptance report 只在 family 成功关闭后,把这些身份与 Host 声明的 source commit/tree 汇总;它不会自行访问 Git 或替代 Broker receipt、workspace receipt。
PreparedFamilyRunnerFactory 可把这种 runner 接到 subagent orchestrator,但 factory 只负责冻结并复核 child authority tuple。并发、重试、选择和发布仍属于 orchestrator。
为什么重要
技术上: 输入共享不会把权限、取消、workspace 或 cleanup 合并成一个大生命周期。一个 member 的 mutation 和 terminal outcome 不会改变 sibling 的 authority 或输入。
产品和业务上: 平台可以让多个 Agent 围绕同一数据并行探索,又能单独追踪每个候选是否完成、失败、超时或未运行,并只发布明确选中的 workspace root。
不能推出什么
- Family member 不是 workflow continuation,也不会继承 sibling 的 Python frame、result 或 receipt。
- Single-use runner 不提供自动 retry;要重试必须由 Host 创建新的、有新身份的 member,并判断外部 effect 是否允许。
- Body-free member record 不是 external-world truth,也不替代 capability receipt。
- Family close 不会自动选择或合并 workspace。
术语卡
- Family member:Prepared Family 创建的一个有限、single-use consumer。
- PreparedRunnerConfig:绑定该 member 执行配置、authority 与 workspace 的 Host 配置。
- InvocationRef:Host 持有的 Agent/turn/output/attempt/execution 身份关系。
- Terminal record:记录一个 member 最终处置及相关身份的 body-free Host record。
closed_unrun:runner 未执行便被关闭或随 family 结束的终态。
继续阅读
- 前置:Prepared Family 到底共享什么
- 后续:为什么纯 AST 优化现在真的是 Pass
- 说明文档:
implementation/09-prepared-family.md、claims/claim-matrix.md - 源码 owner:
runtime/engine/wazero/prepared_family_runner.go、runtime/engine/wazero/prepared_lifecycle.go、runtime/engine/wazero/prepared_family_subagent.go、runtime/subagent/orchestrator.go