目录

[Project] 7. Orchestrator 模式 — 日耗五亿Token,我的节约自救方案

目录

Orchestrator 模式:日耗五亿Token,我的节约自救方案

日耗五百万 token,我闲庭信步;日耗五千万 token,我酣畅淋漓;日耗五亿 token,我汗流浃背。

背景与问题

单 Session 的上下文成本

LLM 的每次调用都需要将完整的对话历史传入,这意味着 context 随工具调用数线性增长。一个复杂任务执行 50 次工具调用后,第 51 次调用要携带前 50 条历史–不仅成本高,LLM 在过长的 context 中还容易失焦、漏掉关键信息。

Figure 1 **Figure 1.1 - Single session context grows linearly with every tool call**

无约束 Agent 的退化路径

如果不做任何限制,一个能力完整的 agent 面对复杂任务时会走向最简路径:

1
2
3
[user] → 帮我实现这个功能
[agent] → spawn_full_sub_agent(task="帮我实现这个功能")
// orchestration 能力完全失效,变成透明代理

read 和 write 混在一起执行,调查阶段的探索性操作和修改操作共享同一个执行流,出了问题无法定位是"看错了"还是"改错了"。

设计目标

本架构要同时解决三个问题:

  • context 膨胀:主干 session 只积累决策,不积累执行细节
  • 职责混乱:强制分离"调查"和"修改",禁止全能 agent
  • 模型资源浪费:按任务复杂度分配模型,简单任务不用高价模型

三包架构总览

三个问题指向同一个根源:主 session 承担了不该由它承担的事–代码探索、文件修改、执行调试全部混入同一条 context 链。解法也因此收敛到同一个方向:让主 session 只做规划,把执行外包给专门的子单元,并控制子单元的结果如何回流。落地这个方向需要三个层次的能力–精准搜索(减少代码探索的 context 开销)、执行单元管理(强制 read/write 分离)、调度策略强制(防止主 session 退化为全能执行者)–对应三个协作的 extension 包。

包依赖关系

三个包形成清晰的层次:

  • pi-extension-tool-semble:独立工具包,无依赖,提供语义代码搜索能力
  • pi-extension-session-subagent:基础执行层,依赖 pi-coding-agent,提供 spawn 三件套
  • pi-extension-session-orchestrator:调度增强层,依赖 subagent 包,通过 hook 强化主 session 的调度能力
Figure 2 **Figure 2.1 - Three-package dependency hierarchy**

各包职责定位

  • semble:让 agent 不做盲目的全文件搜索,精准取相关代码片段
  • subagent:把"生成一个子 LLM session 来执行任务"变成可调用的工具
  • orchestrator:限制主 session 工具集,强制规划与执行分离,管理 session tree 分支

工具权限总览

工具主 Sessionread sub-agentwrite sub-agent
read
semble_search
semble_find_related
scratchpad
task
git_read
diff
bash✅(只读操作)
edit
write
spawn_read_sub_agent
spawn_write_sub_agent
spawn_full_sub_agent

设计说明:为什么 read 工具保留给主 session?

主 session 保留了 readsemble_searchsemble_find_related 等只读工具,而不是强制所有读取都通过 spawn_read_sub_agent

原因是:有时 orchestrator 已经知道要读什么–任务描述里带着路径、或上一轮已经拿到了文件列表。这时如果还派 sub-agent 去读、原路返回、不压缩,是纯粹的 token 浪费(sub-agent 的 system prompt + context fork 开销全白付)。

优化原则:已知要读什么时,直接用 read / semble 读,把信息带入主干决策,不要为"走流程"而多绕一圈。

write 的同理优化

同样的逻辑适用于写:当 orchestrator 已经明确知道要写什么(内容、路径、格式都清晰),直接 spawn_write_sub_agent,跳过 read 阶段。不必先确认现状再写,该跳过就跳过。

这两个工具的存在是为了分离关注点,但分离不等于必须每次都走两步–orchestrator 的核心职责是判断哪些步骤可以省。

树形上下文结构

三包架构的核心承诺是:无论子任务执行了多少步,主干 session 始终保持精简。这在技术上如何实现?答案在于 session 存储层的数据结构–树形 context,而不是线性 context。这不是优化手段,是整个架构得以成立的前提。

线性 Context 的本质缺陷

传统 LLM session 的消息历史是一条链,所有内容必须追加在同一条线上。无论执行什么工具、读了多少文件、犯了多少错、重试了多少次–所有这些都进入 context,且无法选择性隐藏。

Figure 3 **Figure 3.1 - Linear context: all noise enters the main chain**

到第 N 步时,LLM 要在一个混杂着无效操作、错误信息、重试历史的 context 里工作。

树形结构:每条分支独立持有自己的视角

树形 context 让每个节点只能看到"从根到自己"这条路径上的消息。兄弟分支互不干扰,父节点看不到子节点的执行细节。

Figure 4 **Figure 3.2 - Tree context: trunk only sees lean tool_result, branches hold full execution**

Fork 点:子 Agent 从哪里开始看世界

fork 点是主干当前的 leaf ID。forkWorkspace() 将新 SessionManager 的 leaf 指向这个位置。sub-agent 启动时继承主干到此为止的所有消息(规划 context),但之后的所有新消息只写入自己的分支。

sub-agent 知道"我们在解决什么问题"(因为继承了主干历史),但它的探索过程不会污染主干。

主干只追加结论,分支承载过程

sub-agent 执行了多少步,主干完全不知道–主干只在 fork 点之后新增一条 tool_result(精简结论)。这是协议层面的保证:pi 的工具协议决定了工具的返回值以 tool_result 形式进入调用方的 context,而不是把整个子 session 的历史贴进来。

为什么树形才能做到主干精简

这是架构前提,不是优化手段。

线性 context 从设计上就无法做到主干精简–所有内容必须追加在同一条链上,你没有办法把某段历史"留在别处"。树形结构通过给每个 sub-agent 一个独立的 session 实例(指向同一文件但有独立的 leaf 指针),让分叉和隔离在存储层面成为可能。

并行分支:同一 Fork 点派生多个 Agent

多个 sub-agent 可以从同一个 fork 点同时创建分支,并行执行:

Figure 5 **Figure 3.3 - Parallel branches from the same fork point, trunk collects lean results**

三个 SessionManager 实例指向同一 session 文件但各自持有独立的 leaf 指针,写入时互不干扰。

/tree 导航:过程永久可回溯

因为 sub-agent 写入持久化文件而非 inMemory,session tree 里的每一条分支都永久保留。用 /tree 可以随时进入任意 sub-agent 的分支,查看它读了哪些文件、执行了哪些命令、推理过程是什么–完整的审计轨迹。

为什么省 Token

第 3 节解释了树形结构如何让主干精简在技术上得以成立。但"精简"本身是个抽象描述–节省的究竟是哪些 token,量级是多少?这一节从四个具体机制入手,把架构优势转化为可量化的 token 成本差异。这些机制并非同质:其中三个(机制一、三、四)是结构性保证,不依赖 LLM 的发挥;机制二是概率性收益。第 4.6 节会展开说明这一区别。

问题根源:Context 随执行线性膨胀

假设一个复杂任务需要 10 个子任务,每个子任务 50 次工具调用。不做任何隔离时,第 10 个子任务的 LLM 调用要携带前 9×50=450 条历史消息,context 随任务数线性增长。

Figure 6 **Figure 4.1 - Without orchestrator: context grows by 50 messages per task**

机制一:主干只见结论,细节留在分支

orchestrator 模式下,10 个子任务完成后,主干 context 只新增了 10 条 tool_result。子任务内部的 450 次工具调用全在分支里。

Figure 7 **Figure 4.2 - With orchestrator: trunk context grows by 1 message per task regardless of sub-agent steps**

主干 context 大小 = O(任务数),不是 O(总工具调用数)。这是结构性保证,不依赖 agent 行为。

机制二:Context 隔离,每个 Agent 专注自身

每个 sub-agent 只有自己领域相关的 context,不会看到其他子任务的执行历史。专注的 context 减少“在 500 条历史里找关键信息”的认知负担,降低幻觉概率,减少重试。这是四个机制里唯一的概率性收益,不是结构性保证——第 4.6 节会展开这一区别。

机制三:Semble 拦截盲读,按需取片段

一个 10000 行的文件,read 全文需要传入约 10000 行的 context。semble_search 针对查询返回最相关的 5 个 chunk,每个 chunk 约 30 行,传入 context 的只有 150 行–压缩比约 67:1。

Figure 8 **Figure 4.3 - Semble reduces file content in context by ~67x**

机制四:模型分层,复杂任务才用高价模型

list files in src/ 用 haiku($0.25/M token),refactor auth module 用 opus($15/M token)–价差 60 倍。content-driven 模型路由让每次调用都用"刚好够用"的模型。

省的是结构性开销,不是靠 Agent 更聪明

四个机制里有一条分界线值得点清楚。

机制一、三、四是结构性保证——不管 LLM 发挥得好不好,省法都在发生:

  • 机制一:工具协议 + session tree 分支决定了主干只接收 tool_result,数学事实
  • 机制三:semble 拦截 hook 在 tool_call 层面强制介入,必然触发
  • 机制四:task tier 路由在每次 spawn 时执行,不依赖 LLM 判断

就算每个 sub-agent 都很低效、出了很多错,主干 context 依然保持精简。

机制二(Context 隔离)是概率性收益:专注的 context → 更少幻觉 → 更少重试 → 省 token。这条链的每一步都有概率性——LLM 也可能在专注的 context 里犯错,也可能第一次就做对。工业界说"多 agent 比单 agent 更省 token",指的就是这种二阶效应。

这套架构的保守主义:先靠结构性保证建立确定性的底线,机制二的智能优势是锦上添花,不是核心保障。

从实测数据来看,引入这套架构后 token 消耗降低约 20%。这个数字是四个机制综合效果扣除 sub-agent 自身开销后的净值,说明结构性节省远超 spawn 的固定成本。

等效压缩视角:每次 Spawn 都是一次归档

以上四个机制从不同角度切入,这里提供一个统一的整体视角,把它们收拢到同一幅图景里。

每次 spawn 本质上是对那一阶段工作的一次压缩归档。 sub-agent 执行了 50 步工具调用,产生了大量中间状态–读了哪些文件、执行了哪些命令、推理了哪些假设。这些内容对主干而言被"压缩"成一条 tool_result:Findings、Risks、Next Steps。50 条消息 → 1 条消息,压缩比约 50:1。

但这里的"压缩"和传统有损压缩不同,准确的描述是有损侧信道的无损压缩:

  • 主干看到的是有损摘要(tool_result)–执行细节被省略,主干 context 保持精简
  • 原始数据完整保留在分支(branch)–通过 /tree 随时可以取回,不是真的丢失
Figure 9 **Figure 4.4 - Each spawn compresses N steps into 1 tool_result; original preserved in branch**

传统压缩是破坏性的–原始数据可能丢失。这里的"压缩"是归档性的–原始数据换了个地方存放,主干不再看到它,但它仍然完整存在。如果主干需要某个执行细节,可以通过 /tree 进入 branch 取回。

把这个视角与机制一、三、四对应:机制一是这个压缩归档的直接体现;机制三(semble 精准取片段)是 sub-agent 内部在做同样的事,只不过粒度更细–不读整个文件,只取相关 chunk;机制四(模型分层)是在决定每次归档操作用多贵的"处理器"。三个机制在同一个逻辑下统一:减少进入任何一层 context 的冗余信息。

实际效果

引入这套架构后,两个现象值得记录。

一是 session 压缩几乎不再需要。过去单 session 跑复杂任务,context 会膨胀到需要手动触发压缩(或被自动压缩截断);现在主干 context 始终保持在任务数量级,长任务跑完主干依然轻量,压缩这件事基本从日常操作里消失了。

二是 token 消耗降低约 20%。这个数字是四个机制综合效果扣除 sub-agent 自身开销(每次 spawn 有固定的系统提示成本)后的净值。净节省 20% 说明结构性节省远超 spawn 的开销成本。

基础层:pi-extension-session-subagent

整个架构的基础能力是"把一个 LLM session 变成可调用的工具"。没有这一层,orchestrator 就没有可以委派的执行单元。subagent 包做的正是这件事:把 spawn 操作封装成三个工具,让调用方可以像调用函数一样启动一个有完整推理能力的子 session。

三种 spawn 工具

subagent 包注册了三种工具,对应三种执行角色:

  • spawn_read_sub_agent:只读 agent,只能观察和汇报,不能修改任何文件或状态
  • spawn_write_sub_agent:读写 agent,可以执行文件修改、命令运行等操作
  • spawn_full_sub_agent:完整工具集,orchestrator 包会将其从主 session 工具列表中移除

工具能力矩阵

三种 agent 的工具集来自 tools-config.ts 中的 DEFAULT_TOOLS_CONFIG:

1
2
3
4
common:    ["read", "scratchpad", "task", "git_read", "diff",
            "semble_search", "semble_find_related"]
readExtra: ["bash"]        // bash 受 READ_AGENT_CONSTRAINTS 提示约束
writeExtra:["bash", "edit", "write"]

read agent = common + readExtra,write agent = common + writeExtra。

四种 context mode

  • fork(默认):知识传递模式。继承父 session 的消息历史,但经过蒸馏–只保留 user 消息和 assistant 的 text 块,剥除 thinking 块和所有 toolCall/tool_use 块。

    剥除行为信号是刻意的:assistant 的 thinking 和工具调用历史会通过 in-context learning 影响 sub-agent 的操作方式,使其模仿 orchestrator 的行为模式,即使系统提示已经把它定义为 read/write worker。过滤之后,sub-agent 知道"我们在解决什么问题"(来自 user 消息和 assistant 的分析文字),但不会被"父 session 怎么操作"所污染。

  • fresh:空白 context,只有系统提示。适合真正自包含的任务,比如"检查这个目录下有没有文件"–不依赖父 session 的任何规划信息。

  • fork_full:完整 session 克隆,原始历史原样传入,不做任何过滤。仅用于 sub-agent 与父 session 扮演同一角色的延续场景(极少见)。

  • auto:无条件路由到 fork。如果需要 fresh,必须显式传 mode="fresh"–不要依赖 auto 来触发 fresh。

Session 生命周期

sub-agent 是按需创建、执行完即释放的 session:

Figure 10 **Figure 5.1 - Sub-agent spawn, execute, and return sequence**

结构化输出格式

所有 sub-agent 的系统提示都注入了 SUB_AGENT_OUTPUT_GUIDELINES,要求最终回复包含五个固定 section:

1
2
3
4
5
## Conclusion   - 核心结论
## Findings     - 关键发现要点
## Risks        - 风险和注意事项
## Open Questions - 未解决的问题
## Next Steps   - 具体的后续行动建议

orchestrator 消费 tool_result 时按优先级处理:先看 Risks 和 Open Questions(有无阻塞项),再看 Findings(建立事实基础),最后看 Next Steps(决策建议)。

Read-only 约束注入原理

read agent 的约束不依赖工具层面的沙箱,而是通过系统提示注入 READ_AGENT_CONSTRAINTS,明确列出允许和禁止的 bash 操作:

  • 允许:ls、find、grep、git log、git diff 等只读操作
  • 禁止:任何写文件(>>>)、sed -irmnpm installgit commit 等修改操作

当 read agent 发现需要修改的内容时,应在 Next Steps 中描述,由 orchestrator 派发 write agent 执行。

调度层:pi-extension-session-orchestrator

有了 subagent 提供的执行单元,下一个问题是:谁来决定什么时候派哪种 agent,以及如何防止主 session 自己去执行任务。orchestrator 包在 subagent 之上加了一层调度策略,通过三个 hook 把主 session 强制塑造成一个只规划不执行的角色。

核心哲学:规划与执行分离

orchestrator 的主 session 只做三件事:理解请求、分解任务、汇总结论。它从不直接执行 bash 命令、读写文件或调用 API。所有执行都委托给 sub-agent。

这不是靠提示词约束的–bash/edit/write 工具在 session_start 时就从主 session 的工具列表中物理移除,想退化都做不到。

三个 Hook 的拦截点

orchestrator 通过三个 hook 实现全部能力,不注册任何工具:

Figure 11 **Figure 6.1 - Three hook interception points in orchestrator lifecycle**

工具集限制(session_start)

session_start hook 调用 pi.setActiveTools() 将主 session 的工具限制为:

1
common tools + mainExtra(spawn_read_sub_agent, spawn_write_sub_agent)

spawn_full_sub_agent 被完全排除在外。如果 subagent 包未安装(找不到 spawn 工具),hook 会向 UI 发送错误通知并提前退出。

Orchestrator 提示注入(before_agent_start)

before_agent_start hook 在系统提示末尾追加 [ORCHESTRATOR MODE] 段落,明确告知 agent:

  • 你是调度器,只规划和委派,不直接执行
  • 有 read-only 直接访问(read、semble_search、semble_find_related)
  • 没有 bash/edit/write 工具
  • 工作流:理解 → task(plan) → spawn_read(仅当不确定时)→ spawn_write → 汇总

hook 同时重新断言工具集(防止其他 extension 的 hook 在中间恢复了被移除的工具)。

Workspace 分支与主干精简(tool_call)

tool_call hook 拦截所有 spawn 调用,做两件事:

  1. 模型选择:分析 task 描述内容,选择匹配复杂度的模型
  2. workspace 注入:调用 forkWorkspace() 创建分支 SessionManager,注入到 spawn 参数的 _workspaceSessionManager 字段

sub-agent 使用这个 workspace SM 而不是 inMemory SM,其所有消息写入 session tree 的分支,主干只追加一条 tool_result。

forkWorkspace 实现原理

实现非常简洁,只用了两个 SessionManager API:

1
2
3
const ws = SessionManager.open(sessionFile);  // 同一个文件,独立实例
ws.branch(trunkLeafId);                       // leaf 指向主干当前位置
// 下次 sub-agent 写入时,自动在此 fork 点创建新分支
Figure 12 **Figure 6.2 - Trunk only appends tool_result; branch holds full execution**

内容驱动模型分层(Task Tier)

模型选择根据 task 描述的内容复杂度,而不是 agent 类型(read/write)决定:

1
2
3
COMPLEX_TASK = /architect|refactor|migrat|implement|debug|fix|rewrite/i   high tier
SIMPLE_TASK  = /list|find|grep|count|search|summarize|check if/i           low tier
其他                                                                         medium tier

Tier 对应的实际模型来自 ~/.pi/agent/model-routing.json,或自动按 cost + 名称模式(opus → high、sonnet → medium、haiku → low)检测。主 session 始终使用用户当前选中的模型,不做覆盖。

/tree 可见性

因为 sub-agent 写入的是持久化的 session tree 分支(而不是 inMemory),用户可以在任务完成后通过 /tree 导航到任意 sub-agent 的分支,查看其完整的执行过程–每一次读文件、每一条 bash 命令、每一次推理都保留在那里。

搜索层:pi-extension-tool-semble

规划与执行分离解决了"由谁做"的问题,但还有一个隐性开销没有被覆盖:代码探索。无论是 orchestrator 还是 sub-agent,处理代码任务时都需要定位相关文件和函数。如果每次都靠全文件读取或穷举 grep,省下来的主干 context 开销会被代码读取开销抵消。semble 包解决的正是这个问题。

两个工具封装了 semble CLI:

  • semble_search:自然语言或符号名语义搜索,返回最相关的代码 chunk(默认 top 5)
  • semble_find_related:给定 file:line,找项目中的相似实现,用于横向探索

两者比 grep/read 更 token 高效:semble 只返回相关片段,而不是整个文件。

bash grep 自动重写

tool_call hook 拦截 bash 调用,用正则匹配以下两种模式并自动替换命令:

1
2
3
4
5
6
# 被拦截并重写为 semble search
grep -r "pattern" ./src
rg "pattern" ./src

# 重写后
semble search "pattern" "/abs/path/to/src"

复合命令(含 |&;)不做重写,避免破坏管道逻辑。

大文件盲读拦截

tool_call hook 同时拦截 read 工具调用。判断条件:

  • 调用没有指定 offsetlimit(说明是全文读取)
  • 文件估计行数 > 300(LINE_THRESHOLD,可通过环境变量覆盖)
  • 该文件所在目录未曾被 semble 搜索过(SearchTracker.hasSearched())

满足条件则 block: true,返回提示:先用 semble 定位,确认是目标文件后再读片段。

SearchTracker 状态管理

SearchTracker 记录每次 semble_search 搜索过的目录:

Figure 13 **Figure 7.1 - SearchTracker gates blind file reads within a turn**

每个 turn 开始时清空记录,确保跨 turn 不会误放行。

协作工作流

理解了各个组件的作用和省 token 的原理,接下来看它们如何在实际任务中协同运转。一个典型的 orchestrator 任务会经历:规划 → 侦察 → 实施 → 验证这几个阶段,每个阶段对应不同的工具调用组合。

标准执行序列

完整的 orchestrator 工作流:

Figure 14 **Figure 8.1 - Full orchestrator execution sequence**

Skip-read 优化路径

如果 orchestrator 已经从之前的 context 中知道目标文件和修改内容,直接派发 write sub-agent,跳过 read 阶段:

1
2
❌ 浪费:spawn_read → 读到已知信息 → spawn_write
✅ 正确:直接 spawn_write(已知信息就在主干 context 里)

orchestrator 系统提示明确强调:“skip the read phase when you already have enough context”。

Context 流向

  • 用户消息 → 进入主干,orchestrator 可见
  • 主干工具调用(spawn)→ fork 点创建,记录在主干历史
  • sub-agent 执行(50 步)→ 全部写入分支,主干不可见
  • tool_result → 写入主干,orchestrator 可见(精简结论)
  • scratchpad → 存在 tool_result details 里,branch 切换后自动重建,跨 spawn 持久

工具配置定制

DEFAULT_TOOLS_CONFIG 定义默认工具白名单,支持两级覆盖:

  1. ~/.pi/agent/tools.json:全局用户配置
  2. <cwd>/.pi/agent/tools.json:项目级配置(优先级更高)

后者覆盖前者,都覆盖默认值。可以给特定项目添加额外工具(如 db_query)而不影响其他项目。

模式辨析

描述完架构细节,最后回到一个定性问题:这套架构到底是什么模式?它和几个相似的概念–路由模式、多 agent 系统–有什么本质区别?厘清边界有助于在更广的技术语境里定位它,也有助于判断哪些场景适合用它、哪些场景不需要它。

与路由模式(Router Pattern)的区别

路由模式的核心是单次分发:收到请求 → 判断类型 → 转发给对应处理器 → 返回结果。路由器本身无状态,不持有任务上下文,不汇总结论,不决策下一步。

orchestrator 有本质不同:

  • 有全局状态:scratchpad 和 task 在多轮 spawn 之间持久化
  • 多轮迭代:根据每轮结果决定下一步,不是一次性分发
  • 主动汇总:把多个 sub-agent 的结论综合成最终答案
  • 决策循环:spawn_read → 评估 → spawn_write → 验证 → 汇总
Figure 15 **Figure 9.1 - Router (stateless dispatch) vs Orchestrator (stateful multi-round delegation)**

与真正多 Agent 系统(MAS)的区别

经典 MAS(Multi-Agent System)的核心要素:

要素经典 MAS本架构
自主性agent 自主决定何时行动sub-agent 被动等待 spawn
对等通信agent 间可直接发消息严格单向:orchestrator ↔ sub-agent
持久存在agent 持续运行,有自己的目标sub-agent 执行完即销毁
共享环境感知多 agent 主动感知同一环境变化sub-agent 只感知 orchestrator 注入的任务描述

sub-agent 本质上是会推理的函数调用–接受输入(task 描述 + fork context),产生输出(结论),然后消失。它没有自己的目标函数,没有对环境的主动感知,没有与兄弟 agent 通信的能力。

真正的 MAS 需要:持久化 agent loop、共享可读写环境(黑板/消息总线)、agent 基于感知自主决策行动。工业界把这种 orchestrator 架构称为 “multi-agent” 是宽泛用法。

这套架构的本质定位

准确的定位是三层复合:

Figure 16 **Figure 9.2 - Orchestrator architecture as composition of three patterns**
  • Hierarchical Agent:单一决策中心(orchestrator),执行工具碰巧也是 LLM
  • CQRS(Command Query Responsibility Segregation):read/write 强制分离,通过物理移除工具而不是靠约定
  • 树形 Context 管理:分支隔离 + 主干精简 + 永久审计轨迹

它不是 MAS,不是 Router,是一个通过能力边界强制来防止职责退化的层次委派模式。

适用边界:节省的是不确定性的代价

走完整个架构的细节,可以回头看一个更本质的问题:这套机制到底在省什么?答案是–探索过程的不确定性产生的 context

主 session 没有 bash 权限,根本原因不是不信任它,而是因为探索过程天然不确定–要读多少文件?会遇到几次错误?需要重试几轮?这些无法提前预知的步骤一旦发生在主干,就全部沉积在 context 里。把探索外包给 sub-agent,实际上是把这种不确定性"装进容器":

  • read sub-agent 是探索读的容器。乱读、误读、重试,都在分支里。主干只看到结论
  • write sub-agent 是探索写的容器。试改、测试失败、再改,过程全在分支。主干不知道

**这意味着 sub-agent 的收益正比于任务的不确定性。**不确定性越高–不知道从哪里下手、文件结构复杂、需要大量探索–spawn 的收益越显著。

**反过来,已经确定的事情不必 spawn。**如果 orchestrator 已经知道要读哪个文件、要改什么内容,直接用 readedit/write 工具完成反而更高效–每次 spawn 有固定的系统提示成本和 context fork 开销,为"确定的事"支付这个成本是纯浪费。

极端情况:如果整个任务从头到尾都是确定的(路径已知、改动内容明确),主 session 直接处理完全合理。这不是架构的"退化",而是正确识别了 spawn 的适用边界–它解决的始终是不确定性产生的 context 膏胀,确定性任务本来就不是它的目标对象。

1
2
不确定性高 → spawn sub-agent(把探索隔离在分支里)
不确定性低 → 主 session 直接做(省去 spawn 的固定成本)

这也解释了一个表面上奇怪的设计:为什么同样是"读文件",有时候直接 read,有时候要 spawn_read_sub_agent?区别不在操作类型,而在于不知道要读什么时才开始往 sub-agent 里装。知道了,直接读就行。