> ## Content Index
> Fetch the complete content index at: https://www.hubwiz.com/blog/llms.txt
> Use this file to discover other available public pages before exploring further.

# 子代理、fork 与上下文折叠：Agent 交接的三种做法
- URL: https://www.hubwiz.com/blog/subagent-fork-fold-context-handoff/
- Published: 2026-10-15T16:19:33.000Z
- Updated: 2026-10-15T16:20:32.000Z
- Description: 给 coding agent 派活时，最难的从来不是派出去，而是"派什么过去"。子代理拿到任务却没有背景，得重新发现 Node 版本、被 pin 的依赖和你说过的约束；而留在主上下文里的构建日志，又让窗口被 43K 的报错挤满。本文编译 Arthur 的 fork/fold 设计：已完成的工作折成一条 ~160 token 的 note，归档可回查；需要委派时让子代理继承父代理的当前视图。附三张成本账、缓存断点位置，以及作者自己承认"还没验证"的部分。
- Author: admin
- Tags: AI 编程, 多智能体, 上下文工程, Agent, pi

**一句话结论**：多 Agent 协作的瓶颈不在"派活"，而在**交接**。让子代理重做一遍上下文（重新发现依赖、约束、已有决策）成本高；把日志留在主线程，43K token 的构建输出又会一直占着窗口参与每一轮推理。Arthur 提出第三条路——**fold**：让 Agent 把**已经完成**的片段压成一条带句柄（handle）的 note，归档保留原文、主上下文只留结论；以及 **fork**：在派活**之前**让子代理继承父代理的当前视图。两者都不是压缩对话，而是**改变模型在后续轮次里看到什么**。

本文完整编译 Arthur《My take on subagents, forks and collapsing context》（2026-10-12）的设计、成本模型与实验计划，并做中文整理。文中成本数字来自作者按缓存价格做的**估算**（假设运行），作者明确表示：**折叠是否真的改善任务结果，尚未验证**。

---

## 一、问题：构建循环的"切线"把上下文吃掉了

作者描述的场景非常具体：你让 Agent 加一个导出功能，它在实现过程中发现项目构建不过，于是花了好几轮读报错、试修复、重新构建。等构建通过、回到原任务时——"我本来要干什么来着？"

问题的结构是这样的（用作者的记号）：

- **B**：构建之前的上下文（系统提示、你的需求、探索代码、写好的查询）；
- **E**：构建片段（episode）——报错日志、失败的猜测、你的纠正、最终通过的命令；
- **A**：构建之后的工作（继续写前端）。

今天主流的两种做法都有代价：

1. **内联 + 普通 compaction**：日志一直留在上下文里，直到**窗口快满**才触发总结。触发时机取决于上下文大小，而不是"构建"这件事**早就结束了**。作者举例的图上，一次构建产生了约 **42K** token，窗口 80K，典型的 compaction 点在 72K——上一轮构建的日志要跟后面所有工作一起挤在窗口里。
2. **子代理（subagent）**：把构建外包给一个全新的子代理，日志不进主上下文，还保住了父代理的 prompt-cache 前缀。但代价是**交接**：新子代理只知道任务描述里写了什么，它得重新发现 Node 版本、被 pin 的依赖、你早先给过的约束。而且你必须在**还不知道这段工作有多长**的时候就决定是否委派——一个"快速构建"如果变成七轮，事后无法撤回到子代理里。

作者还点了一个容易被忽略的成本：即使 prompt caching 让旧日志便宜了，那些 token **仍然占着窗口，模型在之后的每一轮里仍然要对它们做注意力计算**。

## 二、fold：做完再压，而不是等窗口满

fold 的思路是反过来的：**构建照常内联跑**（不预测、不提前委派），等它结束，Agent 再回头把这段"切线"折起来。

概念是这样的：

- **episode（片段）**：一段与主线任务有关、但可以整体结束的工作（比如"让它构建通过"）；
- **fold（折叠）**：Agent 识别出片段的**起点和终点**，写一条 note，把原始过程归档，从句柄（如 `b2e4`）引用。

原来的上下文变化是：

```text
今天：  B + E + A
折叠后：B + fold(E) + A        # fold(E) 约 160 token，E 原文进归档

```

作者给的例子：一段 **\~43K** token 的构建片段，折成 **\~160 token** 的一条 note，**缩小约 270 倍**。主线程从"我本来要干什么来着"变成"构建过了，继续前端"。

### 2.1 note 模板：五类信息，缺一不可

作者用的模板固定五段：

| 字段                     | 含义                        |
| ---------------------- | ------------------------- |
| **changed**            | 改了哪些文件、配置、外部状态            |
| **learned**            | 学到了什么，**包括前提条件（caveats）** |
| **decided / rejected** | 做了什么决定，**否决了什么**          |
| **open**               | 未完成、未验证的开放项               |
| **where**              | 如果不够再回头看哪里（句柄）            |

模板看起来和现有 compaction 提示词差不多（OpenCode 的 summarize 也要求 decisions、work state、next steps、文件路径与精确报错）。区别在于**应用的片段更小、时机更早**——而且 note 要**独立成立**。

### 2.2 最难的是保住"约束"，而不只是结论

这是全文最值得记的一个例子。假设 Agent 写了一个任务领取查询：

```sql
SELECT ... FROM jobs WHERE ... FOR UPDATE SKIP LOCKED

```

关键约束是：**选取和领取必须在同一个事务里**，这样行锁持续到领取被记录为止。

如果 note 只写"领取查询能用了"，那么后续任何一个把查询挪出事务的改动，都会让两个 worker 领取到同一个任务——而**归档里虽然有解释，note 却没有给 Agent 去看的理由**。

正确写法是：

> 领取查询可用；**选取与领取必须保持在同一事务内**（SKIP LOCKED 的行锁持续到 commit）。

作者还提醒 note 的原材料从哪来：片段里 Agent 在每次工具结果后的那些"这意味着什么、下一步试什么"的回复，是**输入**而不是成品——它们依赖上面已经看不见的输出，里面还混着后来被否决的猜测；note 要留下站得住的结论，并在需要时**引用精确的报错字符串或路径**。

### 2.3 选择的时机与风险

"直到构建通过"是一个容易事先定义好的折叠点，但"哪些文件会被读到、哪些早先决策会被用到、这段要多久"很难预测——fold 把**选择权交给了事后的 Agent**，因为它那时才有信息。

风险也在这里：选错片段会把无关工作总结进来，或者**一半日志留在上下文里**。归档虽然可以找回原文，但**找回本身要花时间，而且未必发生在错误之前**。作者对开放式工作（问题一直在变）的建议是：**干脆不要折，把片段保持打开**——再好的时机判断也无法预知二十轮之后需要什么。

## 三、fork：让子代理继承父代理的视图

fold 处理"内联执行完再收"，fork 处理"一开始就派出去"：对延续父任务的委派，与其让父代理把散落在上百条消息里的背景拼成一段交接提示词，不如**把父代理当前的消息数组复制一份**给子代理。

```text
fork(task) -> result, handle

```

实现上：复制父代理的 message 数组，在副本里用 task 作为**未完成工具调用的结果**，让子代理跑到结束，返回最后一条消息和子代理 transcript 的句柄。父代理仍然要指定任务，但**不必重建背景**——而且未改动的前缀理论上还能命中缓存。

fork 的两个坑：

1. **它继承父代理的错误猜测**——这恰恰是子代理本来要避免的问题。作者的包因此让 fork **基于已折叠后的视图**：早先查询工作如果已经折成 note，子代理拿到的是那条 note，而不是完整历史（被否决的猜测不会再传下去；但 note 里的结论如果是错的，依然会传下去；**未折叠的开放片段仍然全量继承**）。
2. **多个 fork 共享同一个工作树**，文件编辑必须协调。

## 四、一笔成本账：fork 比全新子代理贵多少

作者做了一个估算模型：父前缀 **100K** token、全新子代理的系统提示 **15K**；整段循环 30 轮共增加 **150K** token；缓存读 0.1x、缓存写 1.25x；全新子代理需要重读约 30K 父代理已经读过的材料；假设两边轮数与构建输出相同。

| 项目                     | 倍率               | fork        | 全新子代理       |
| ---------------------- | ---------------- | ----------- | ----------- |
| 前缀，每轮从缓存读              | 0.1x             | **300K**    | 45K         |
| 循环增量，写一次               | 1.25x            | 188K        | 188K        |
| 循环增量，随增长从缓存读           | 0.1x             | 225K        | 225K        |
| 重读父代理已读过的内容            | 写 1.25x + 读 0.1x | 0           | **\~125K**  |
| **合计（fresh-token 等价）** |                  | **\~713K**  | **\~583K**  |
| 按 $3/百万 token 计        |                  | **\~$2.14** | **\~$1.75** |

三个读法：

1. fork 的输入成本约为全新子代理的 **1.2 倍**；只看前缀会得出"近 7 倍"的错觉，因为漏掉了**两边都要承担的构建输出**。
2. **父上下文越长，fork 越贵**；缓存读越便宜，差距越小；缓存过期会额外产生一次前缀写。
3. 但如果父代理**已经折过**——假设前缀从 100K 降到 40K——fork 的总量会掉到 **\~533K**，**低于**全新子代理的 \~583K（还没算生成 note 的成本）。也就是说：**fold 让 fork 变得划算**。作者同时说明，对依赖很少的短查询，仍然应该用全新子代理。

## 五、评审者：给需求，不要给作者的解释

一个容易忽略的用法差异：如果你要找一个 Agent **评审**代码，fork 会把"作者为什么是对的"整段推理一起交过去——这恰好污染评审独立性。

作者的主张是：评审用**独立子代理**，输入 = **用户的原始需求 + diff + 相关代码**，不给实现者的结论。图里给了对照：拿到需求与 diff 的评审者找到了"worker 在事务外领取任务"的 bug；拿到父代理推理的评审者则"继承结论"，复述了"SKIP LOCKED 防止重复领取"。

代价也要说清：**同一个模型仍然有共同的盲区**，独立输入只能降低"附和作者"的概率。

## 六、缓存断点：fold 与 fork 都靠它省钱

成本估算的前提是**缓存前缀能被复用**。作者逐项列了各种操作对缓存的影响：

| 操作         | 变化            | 缓存行为                         |
| ---------- | ------------- | ---------------------------- |
| append     | 追加工具结果等消息     | 复用已有前缀，只处理新增尾部               |
| fork       | 复制父视图并追加 task | 复用父前缀；每个子代理处理自己的尾部           |
| fold       | 用 note 替换片段   | 复用**变化点之前**的条目，处理剩余上下文与 note |
| compaction | 用摘要替换旧对话      | 重写部分要重新处理，更早内容可能仍命中          |
| ask        | 加载归档上下文并提问    | 复用该上下文条目，或重算已过期部分            |

对工程实现更麻烦的是：**Agent 事先并不知道自己会折哪一段**。pi-episodes 的做法是在"最新的 fold note 或带时间戳的用户消息"处放一个额外断点；真实片段起点可能在这之后几轮，因此一次 fold 仍可能让中间那段重新处理。作者的测量计划包含这部分成本。

另外几个现实约束：Anthropic 缓存默认 5 分钟、可买 1 小时；较新的 OpenAI 模型默认 30 分钟；长工具调用或暂停会话都可能**超出缓存存活时间**。归档查询尤其容易触发全价读，需要单独计量。**系统提示与工具定义保持稳定**是共享缓存前缀的前提——Anthropic 把工具定义放在系统提示和消息之前，改一个工具定义会让其后所有内容的前缀失效。

## 七、数据模型与实现细节

包里的四个概念：

| 名称          | 内容                                |
| ----------- | --------------------------------- |
| **log**     | 每条线程一条只追加的消息列表（主线 + 每个 fork）      |
| **view**    | 模型实际看到的：主 log，其中被折叠的片段替换为 note    |
| **archive** | 被折叠片段与已结束 fork 的 log，附"碰过哪些文件"的索引 |
| **handle**  | 归档片段的 ID（如 b2e4）                  |

几个对重现对话保真度很关键的点：

- 用户消息与工具结果在**保存时**打时间戳；模型用这些可见 ID 来指定 `since`。**事后补打会改变已缓存文本**。
- fold **不能跨过**更早的 note 或 compaction 摘要，且保持工具调用与结果成对。
- 用户消息**保留原始措辞与时间戳**，这样构建过程中给你的纠正，回看时仍然是"那一轮里的一次纠正"。
- 工作树快照提供 diff；没有快照时退化为列出文件工具碰过的路径。**快照只在本会话内存活**，重启后无法再检查"归档之后文件是否变了"。
- 带句柄的 `recall` / `ask` 可以在归档里检索或追问（`ask` 把归档上下文临时挂上、答完即撤），成本按缓存是否命中差出数量级（warm \~$0.019 vs cold \~$0.19 的示例）。

## 八、怎么实测：pi-episodes

作者把它做成了 pi 的包：

```bash
pi install npm:pi-episodes

```

交互式里 `/fold` 会列出轮次让你选起点（`/fold t13` 直接指定），然后 Agent 写 note；没有交互选择器时，让 Agent 自己选。

作者建议的实测方法：**在一次真实的构建循环之后执行 fold，然后给 Agent 一个依赖该循环细节的任务**——看 note 是否保住了细节、或者 Agent 是否先去检索再行动。它正在验证的问题包括：

1. 模型能否**自己意识**到一个片段结束了，并选对起点；
2. note 是否留住了**前提条件**（还是犯"查询能用了"的错）；
3. 该回查时会不会回查，**文件触碰提示**是帮忙还是噪音；
4. fork 是否比全新子代理**更容易锚定**父代理的错误猜测；
5. **更小的上下文是否改善任务结果**（对比保留完整日志）。

后续想做的实验还包括：索引符号而不只是文件；用最近上下文主动提示归档线程（并测量是帮助还是分心）；用失败任务反推 note 里缺了哪些约束。

## 九、边界：作者自己承认不知道的部分

文章最诚实的两句话，值得原样保留：

- **"folding 是否改善任务结果，我还不知道。"** 图表与成本数字来自假设运行；这是一个**待验证的设计**，不是已验证的实践。
- **"最有证据缺口的情况，是 Agent 根本没意识到自己需要回查。"** 一条漏掉事务约束的 note，可能永远不会触发检索——除非后续文件读写恰好碰到同一个文件（shell 命令不会触发这个提示）。

此外，**归档里的答案反映的是当时的文件状态**；文件已改，解释就可能失效。作者说测量完成后会公布结果。

## 对工程团队的启示（中文整理者补充）

把这篇设计放在一起看，它其实给了三类可立即使用的动作，**都不依赖作者那个实验包**：

1. **交接提示词**写五段（changed / learned / rejected / open / where），并强制要求**约束**而不只是结论——尤其在事务、并发、权限这类"结论正确但前提会失效"的地方。
2. **委派决策延后**：不确定要多久的工作先内联跑，用"构建通过/测试通过"这类**天然结束点**作为折叠时机；开放式工作保持打开。
3. **评审与实现分离**：评审者只拿需求 + diff + 代码，不拿实现者的推理。

最后，任何自动折叠都要配**人工复核**：note 是摘要，摘要会丢信息；archive 是兜底，但"想不起来去查"是它救不了的失败模式。

---

## 来源

- Arthur, *My take on subagents, forks and collapsing context*（2026-10-12）：https://arthur.place/subagents-forks-collapsing-context
- 事件分割理论（Event Segmentation Theory）：Zacks & Swallow, *Event Segmentation*；Kurby & Zacks, *Segmentation in the perception and memory of events*
- Altmann & Trafton, *Memory for goals*（关于"开放项需要在暂停前留下线索"的设计依据）
- pi-episodes（实验包）：`pi install npm:pi-episodes`

## 延伸阅读（本站）

- [给你的编程 Agent 一份"自己的记忆"：本地、可移植、可溯源的 funes](https://www.hubwiz.com/blog/give-your-coding-agents-a-memory-you-own/)
- [一台终端、多台机器、多个 Agent：并行 coding agent 的实战栈](https://www.hubwiz.com/blog/parallel-coding-agents-multi-machine/)
- [多 Agent 团队到底值不值？50 个全栈应用实测](https://www.hubwiz.com/blog/do-agent-teams-pay-off/)
- [自建开源 coding agent 平台：OpenHands vs Munder Difflin](https://www.hubwiz.com/blog/self-host-open-source-coding-agent-platforms/)
- [专题：Agent / MCP / 自动化完全指南](https://www.hubwiz.com/blog/agent-stack/)

本文由汇智网编译整理，转载请注明出处。

> **这是「Agent / MCP」专题的一部分。**完整地图见：[Agent / MCP / 自动化完全指南](https://www.hubwiz.com/blog/agent-stack/)