多 Agent 不是多开几个终端:Pi 的 Sub-agent 取舍

三个 Agent 同时进入一个仓库:一个改认证接口,一个补前端调用,一个修测试。十分钟后,它们都说 任务完成了。合并时才发现,前端基于旧接口生成,测试重写了共享 Fixture,认证 Agent 又在最后一次 格式化中覆盖了另一条分支的改动。
从进程列表看,这是多 Agent。从工程结果看,它只是多个模型同时碰同一份状态。
启动第二个 Pi 进程并不难。工程难点是:谁定义子任务,谁拥有工作区,什么上下文可以传递, 失败怎样传播,取消后如何处理已发生的副作用,结果凭什么进入主分支。本文的核心结论是: 可控的 Sub-agent 需要一条清晰主线:Task Contract 固定任务,工作区和权限限制执行,Artifact Envelope 返回证据,独立 Review Gate 决定是否接收。
Pi 的小核心选择正好暴露了这条边界。它不在核心规定一种 Sub-agent 拓扑,但提供进程、Session、 SDK、Extension 和 Package 等组合入口。团队可以据此构建自己的流程,也必须自己承担调度语义。
一、先区分"第二个执行者"和"受控子任务"

一个 Shell 命令可以启动另一个 Pi,一个 SDK 调用可以创建另一个 AgentSession,Extension 也可以 注册一个调用子进程的 Tool。这些能力证明第二个执行者可以存在,却没有自动回答下面的问题:
text
任务从哪个父任务产生?
它读取的是哪个代码快照?
允许修改哪些文件和外部系统?
完成、部分完成、失败和取消分别怎样表达?
结果由谁验收,什么时候才允许合并?
这条主线背后需要检查七类对象:
| 对象 | 需要回答的问题 |
|---|---|
| Task | 要解决的具体问题是什么 |
| Context | 允许依赖哪些事实与代码快照 |
| Capability | 可以调用哪些工具、凭证和网络 |
| Workspace | 在哪里读写,谁拥有这些写入 |
| Run | 当前执行到了哪个状态 |
| Artifact | 返回了什么可检查的结果与证据 |
| Gate | 谁决定接受、重试、拒绝或合并 |
缺少其中任何一项,主 Agent 得到的往往只是一段"我完成了"的自然语言,而不是可接管的工作。
二、Pi 实际提供了什么,又刻意没有提供什么


Pi v0.82.1 README 明确把 Sub-agent 排除在核心默认能力之外,并给出 tmux、Extension 或第三方 Package 等组合方向。到 2026-08-12 检查当前 main,小核心立场仍然存在。当前 SDK 文档同时把 "构建能启动 Sub-agent 的自定义 Tool"列为程序化用例,Extension 示例目录也提供相关示例。
这组事实应该被准确表述为:
text
Pi core 不内置唯一的 Sub-agent 工作流。
Pi 提供足以构建多会话、多进程与自定义工具的组合原语。
示例与原语不等于生产级调度、隔离、恢复和验收已经成立。
这种选择有合理收益。不同团队需要的拓扑差异很大:只读研究可以共享代码目录。并行改动可能需要 独立 Worktree。高风险操作需要隔离凭证。长任务需要持久 DAG。简单审查只需一次父子调用。若核心 固定其中一种,其他场景就要围绕它迁就。
代价也不能省略:状态协议、并发限制、日志、取消、重试、产物格式和合并策略都转移给 Extension 作者或外部 Orchestrator。Pi 的可扩展性减少了核心预设,并没有让控制面免费出现。
三、先写 Task Contract,再写 Prompt

主 Agent 对子 Agent 说"检查一下认证模块",看似简洁,实际上把范围、证据、权限和终态全部留给 模型猜。可靠的交接应先变成可验证合同:
yaml
task_id: auth-race-review
parent_task_id: release-20260812
objective: 找出刷新令牌流程中的并发安全问题
input_revision: 5f2c1a7
scope:
read: [src/auth/**, tests/auth/**]
write: []
capabilities: [read, search, test_list]
constraints:
network: denied
secrets: denied
deliverables:
- artifacts/auth-review.md
- artifacts/claims.jsonl
exit_criteria:
- 覆盖登录、刷新和登出三条路径
- 每条确认结论带文件、Symbol 与证据级别
Prompt 负责帮助模型理解任务。合同负责让系统判断任务是否越界、产物是否齐全。 两者的区别在失败时最明显:Prompt 失败通常只能重读对话,合同失败可以指出缺少哪个字段、哪项 退出条件未满足。
合同还应固定 input_revision。如果子 Agent 在旧提交上完成分析,而主分支已经移动,调度器不能 把"内容看起来合理"当作仍然有效。它要么重新基线化,要么把结果标成 stale,交给人工决定是否 还能复用。
四、Context Isolation 会省上下文,也会损失证据
Sub-agent 常见卖点是:子 Agent 自己阅读大量文件,只把摘要交给主 Agent。它确实能保护主会话, 避免把几十次搜索和失败尝试全部塞进主上下文。但摘要是一种有损压缩,它可能丢掉:
- 被否定的假设和失败命令。
- 两个来源之间的冲突。
- "尚未验证"被压成肯定句的边界。
- 只在特定版本成立的限制。
- 结果生成时所依赖的代码快照。
所以交接不能只有 summary。更稳妥的 Artifact Envelope 至少包含:
json
{
"taskId": "auth-race-review",
"status": "partial",
"summary": "确认一处竞态,另有一处受缺失集成环境阻断",
"claims": "artifacts/claims.jsonl",
"evidence": ["src/auth/token.ts#refresh", "artifacts/test.log"],
"inputRevision": "5f2c1a7",
"unverified": ["数据库事务隔离级别"],
"sideEffects": [],
"recommendedNextAction": "启动测试数据库后复跑用例"
}
主 Agent 平时读取摘要,关键判断再沿证据句柄回查。这样 Context Isolation 才是"把细节移出主 上下文",而不是"把细节永久丢掉"。
五、并行写入先划定共享状态与所有权
两个 Agent 修改不同文件也可能冲突。一个改 OpenAPI Schema,另一个按旧 Schema 写客户端。一个 更新依赖,另一个基于旧锁文件运行测试。两个任务都写同一个生成目录或测试数据库,也会互相污染。
因此并发决策应基于共享状态,而不是只比较文件路径。至少要检查:
text
代码与配置依赖
Schema 与生成物
数据库与消息队列
缓存、构建目录和测试端口
Git Index 与工作树
远端 API 和外部副作用
对于会写代码的任务,独立 Worktree 或临时 Clone 是更清楚的起点。每个任务绑定自己的 Revision、 分支和输出目录。合并由主流程串行执行。即便如此,也不能自动宣称没有冲突,Gate 仍要重新运行 共享测试、Schema 校验和语义审查。
一种实用的写权限策略是:研究与审查默认只读。实现任务获得明确目录或 Worktree。数据库迁移、 部署、发布、删除和外部消息继续保留人工门。并行度应由可隔离状态决定,而不是由可用模型数决定。
六、状态必须持久化,取消必须处理副作用

如果任务状态只存在主 Agent 的自然语言记忆里,主会话压缩、进程退出或模型失败都会让调度信息 消失。一个最小 Run Record 应放在模型之外:
json
{
"runId": "run-0182",
"taskId": "auth-race-review",
"status": "running",
"attempt": 2,
"worker": "review-agent",
"workspace": "worktrees/auth-review",
"startedAt": "2026-08-12T10:00:00+08:00",
"deadlineAt": "2026-08-12T10:20:00+08:00",
"budget": {"maxTurns": 16},
"dependsOn": ["map-auth-flow"]
}
建议至少区分:
text
queued → claimed → running
↘ succeeded → reviewing → accepted | rejected
↘ failed → retrying | escalated
↘ cancelling → cancelled_with_effects | cancelled_clean
cancelled 不应只有一个布尔值。若子 Agent 已写文件、创建 Issue、修改数据库或触发远端任务,停止 模型输出并不会撤销副作用。控制面需要记录 sideEffects、清理动作和迟到结果策略。旧 Attempt 在 取消后返回的产物必须携带版本号,不能覆盖新 Attempt 的结果。
重试也必须幂等。安全重试可以复用同一个 task_id 并增加 attempt。具有外部写入的步骤则需要 幂等键、补偿动作或人工确认。否则"自动恢复"可能把一次失败变成两次扣款、两条消息或两次发布。
七、第一项多 Agent 能力:独立只读审查

多 Agent 并不天然提高质量。若多个 Agent 使用相同模型、相同上下文和相同提示,它们可能复制 相同假设。并行写代码还会增加合并成本。最容易得到正收益的第一步通常是:
text
实现 Agent 产生 Diff 与测试证据
↓
只读 Review Agent 独立检查当前产物
↓
主流程按严重程度决定修复、拒绝或接受
这条路线的优势很具体:审查 Agent 不需要写权限,不会制造第二份冲突 Diff。输入可以冻结为当前 SHA。输出是问题清单和证据,不需要复杂合并。生产者与审核者分离也能降低"自己证明自己正确"的 确认偏差。
Review Gate 不应只问"另一个 Agent 是否说通过",而应检查:
- 产物 SHA 是否与审核对象一致。
- 审核者是否没有参与该产物生产。
- 事实、测试、边界和未验证项是否分别记录。
- 失败是否只有一条明确返工路线。
- 修改后哪些结论可以继承,哪些必须失效。
这条链路的质量增量来自角色和权限分离,而非 Agent 数量。
八、什么时候应该并行,什么时候坚持单 Agent

可以用五个问题做拆分判断:
| 问题 | 是时更适合拆分 | 否时的风险 |
|---|---|---|
| 子任务是否真正独立 | 可并行读取或在隔离工作区写入 | 等待与合并成本吞掉加速 |
| 是否需要不同权限 | 研究只读、实现可写、审查只读 | 所有 Agent 权限过宽 |
| 是否需要不同专长 | 安全、前端、数据各有明确产物 | 只是重复生成相似答案 |
| 是否能定义验收产物 | 有 Schema、Diff、测试或报告 | 主 Agent 只能相信摘要 |
| 失败是否可隔离恢复 | 有 Attempt、取消和补偿语义 | 一个失败污染整个工作区 |
小改动、强顺序依赖、共享状态很多或无法定义退出条件时,单 Agent 往往更快也更可靠。多 Agent 降低的是部分墙钟时间,不保证降低 Token、工具调用和人工审核总成本。本文没有运行真实 Pi 多 Agent Orchestrator,因此具体加速比与成本保持 UNKNOWN_REAL_COST_AND_SPEEDUP。
九、按风险逐级升级

从独立只读审查开始,先固定输入 SHA、审查范围、问题格式和接受门槛。它稳定后,再并行代码搜索、 资料核对和测试失败分类等只读任务。只有任务边界、依赖图和合并测试都清楚,才让实现 Agent 在独立 Worktree 写入。任务跨会话或跨机器时,再引入持久任务图、租约、心跳、重试与补偿。每次升级前都要 实测 Token、墙钟时间、问题发现率和人工成本,上一层无法证明收益时不增加并发。
Pi 没有把单一 Sub-agent 调度拓扑写死在核心。工程团队应先把任务、状态、证据、权限和验收从模型 上下文中拿出来。做到这一步,第二个 Agent 才是可管理的执行单元。否则它只是第二个不受控变量。
参考资料
- Pi Coding Agent README(v0.82.1),固定版本的小核心与 Sub-agent 组合立场,检查日期 2026-08-12。
- Pi Coding Agent README(main),当前 Philosophy、Session 与扩展入口,检查日期 2026-08-12。
- Pi SDK Documentation,
AgentSession与程序化工作流能力,检查日期 2026-08-12。 - Pi Extensions Documentation,自定义 Tool、事件与 Extension 能力,检查日期 2026-08-12。
- Pi Sub-agent Extension Example,当前示例入口。仅作为组合实现样本,不作为生产调度验证。