从0打造编码Agent 实验 实验 ≈ 45 分钟 页内实验 ×5
A7 子 agent:一个 agent 忙不过来、或需要别人把关时,怎么分工? 实验课

实验课:有 / 无子 agent 的真实对照

有无子 agent 的真实对照
把本章的组件串起来,用 T4 的 6 次真实运行和 4 条子 agent trace 给本课项目的里程碑 M7 拍板:主窗口与子窗口的 token 流、交接在哪一步丢信息、预算与深度闸在真实事件序列上的计数、声称与验收的逐条对照、两条泳道。页面加载时,预算与深度闸的每个移植都与原实现逐条自检。
🧪

本课要做的:用真实数据给本课项目的里程碑 M7 拍板——子 agent 的权限、预算、深度、交接写什么、父 agent 怎么对待报告——并确认页面上的每个移植都和原实现一致。

需要的时间:约 45 分钟。全部计算在浏览器里完成,不联网,也不需要跑模型。

数据从哪来

从已有出发 · 累计数据流 点已学环节看它回答什么
每一轮循环都经过

实验台一:主窗口与子窗口

主窗口与子窗口:每轮请求多大,交接与报告各多少 token(真实 T4 运行) 主 agent 的请求子 agent 的请求其中搜索结果父→子 交接子→父 报告
seed 点任意一根柱,下方看那一轮的细节

数据:本课项目 miniagent 的 T4 真实运行(Qwen/Qwen3-8B-MLX-4bit,本机 mlx_lm.server,2026-09-26;src/data/precomputed/a07-subagents.json)。柱高 = 服务端 usage 的 prompt_tokens;绿色段与流带 = 用录制模型 tokenizer 数的内容 token(不含聊天模板的标记,所以比柱高的增量略小)。三列共用同一纵轴。思考截取约 500 字、工具输出约 900 字。

实验台二:交接在哪一步丢信息

交接对照:同一个任务,子 agent 拿到的东西不同(真实 T4 运行) seed
先猜:读下面两段交接原话。哪一个子 agent 报回的「真实调用点 + 正确行号」更多?

数据:本课项目 miniagent 的 T4 真实运行(Qwen/Qwen3-8B-MLX-4bit,2026-09-26)。约束检测是对交接原话做的正则匹配(规则悬停可见);引用分类按任务仓源码逐行判断,调用点匹配容差 ±2 行,与验收脚本 verify.py 相同。每种配置只有 2 个 seed:只能说明这些丢失发生过,不能说明频率。

实验台三:预算与深度闸

轮数预算算谁的:在真实事件序列上跑 Hermes 的 consume / refund 自检中…
预算

「调用者深度」= 发起派生的那个 agent 在树里的深度(主 agent = 0,它派出的子 agent = 1……)。✓ = 这一层还能再派子 agent;✕ = 被闸住(悬停看原实现返回的报错原文)。Claude Code 一列按官方文档(2026-09-26 核对)的「默认最多 3 层,环境变量 CLAUDE_CODE_MAX_SUBAGENT_SPAWN_DEPTH 可改」;Codex v2 一列按 pin 里的测试 multi_agents_tests.rs:2420(v2 的 spawn 不看 agent_max_depth)。

数据:事件序列来自本课项目 T4 的真实 trace(父 agent 上限 30、子 agent 15、深度上限 1);预算语义移植自 Hermes 56dc01d9(MIT);期望值由 gen_a07_subagents.py 直接加载 Hermes 原 IterationBudget 类,以及 bun / rustc / python 在四份 pin 源码的逐字切片上跑出(04-实践项目/scripts/parity/)。

实验台四:声称与验收

干活的 agent 说「做完了」,外部验收怎么说(每行一次真实运行)

判定口径:「明确声称」= 最后一段话断言测试已通过 / 分析已完成 / 已写进文件;「含糊」= 用 should、will 表达;「未声称」= 没有这类断言,或最后根本没写话。T1 由本章作者按这个规则逐条标注,依据原句就是每行显示的那一句,展开可读全文核对;T4 与项目录制脚本的正则判断逐条一致。验收全部来自 harness 在 agent 视野之外跑的检查,与模型说了什么无关。

T1:26 次运行逐轮回放(从上表跳过来) 请求模型回复工具调用工具结果harness 注入

数据:本课项目 miniagent 的真实运行(src/data/project/agent-traces.json,2026-09-26 导出;模型 Qwen/Qwen3-8B-MLX-4bit,本机 mlx_lm.server)。token 数取自服务端 usage;思考截取约 600 字、正文与工具输出约 900 字(保留头尾)。耗时受本机同时运行的其他任务影响,只能粗看;token 与轮数才可比。

实验台五:泳道与同文件写

谁在什么时候干活:主 agent 与子 agent 的泳道(真实 T4 运行)

横轴是录制时的真实时刻(父子两条 trace 的时间戳对齐)。录制时本机同时在跑 LLM 课的训练,生成速度约 25 token/s,秒数只能粗看;要看的是先后与重叠:主 agent 在 delegate 之后整段等待,没有任何两个 agent 同时动手。

如果真有两个 agent 同时写同一个文件 · 构造输入 · 真算法(Pi 的 withFileMutationQueue 移植,在浏览器的事件循环里真跑)
agent A:第 0 ms 读 CALLERS.md,想 30 ms,追加 billing.py 那一行后写回;agent B:第 10 ms 读,想 10 ms,追加 us_feed.py 那一行后写回(用 setTimeout 排的时间;标签页在后台时浏览器会放慢计时器,两次读仍然都发生在任何一次写之前)。两行内容取自任务的标准答案(expected.json)。下面按实际发生的先后列出每一步。

任务

🎯

任务 1 · 主窗口的账:在实验台一切 seed 1 和 seed 2,把三列的「窗口峰值」和「合计 total」抄下来。大纲 M7 的验收 ① 说「用子 agent 时主上下文的增量小于不用时」:4 组对照里哪一组不成立?点那一列的第 1 轮柱子,写下原因。你会怎么改这条验收的写法,让它量的正好是「隔离」这件事?

任务 2 · 交接写什么:在实验台二的两个 seed 上各猜一次再揭示。然后给本课项目的 with_decisions 设计一条规则,让 seed 1 那种「父 agent 看到了行号却没交出去」不再发生(提示:harness 能不能自动附上什么,而不是指望父 agent 自己写?)。

任务 3 · 预算放在哪一层:在实验台三把运行切到 t4-sub-dec-s2,先「父子共用一份」找出父 agent 刚好跑完所需的最小上限,再切「父子各一份」。把两个数和你的选择写进 DECISIONS.md;再在深度闸表里把 Hermes 与 OpenCode 都设成 0,解释为什么结果不同。

任务 4 · 谁说了算:实验台四已经只显示「说完成了、却没通过」的行。挑一条 T1 和一条 sub 行点「跳过去」,找出声称与事实分开的那一刻。然后写一条「父 agent 收到 delegate 报告之后先核对什么」的规则,逐条推演它能拦下 T4 的哪几次。

任务 5 · 读自检:实验台三右上角的「与原实现一致」对照的是哪几份原实现?哪一部分是逐字切片真跑,哪一部分是按原文转写(提示:Hermes 的循环闸门)?

📝

把结果写进项目:在 04-实践项目/DECISIONS.md 记下 ① 子 agent 的权限是否严格小于父 agent,依据是第 4 课的只读强度表;② 预算独立还是共享,附任务 3 的两个数;③ 深度上限与超限时的报错;④ 交接规则(任务 2)与报告核对规则(任务 4)。这四条就是本章里程碑 M7 的决策材料(见章首页)。

读数时注意

🔍 作废重录的那一次:工具的 bug 变成了「子 agent 的结论」

第一次录的 t4-sub-text-s1,子 agent 调了 grep({"pattern": "parse_date", "path": ".", "glob": "**/*"}),得到 (no matches);它的报告开头是「No calls to `parse_date` (or its aliases) were found in files outside the `tests/` directory. The initial search across …」,父 agent 的最后一句是「The delegate search found no calls to `parse_date` (or its aliases) outside `tests/`, meaning no compatibility checks are needed. Thus, the …」。原因:harness 缺陷:grep 工具把 glob 只和文件名比较,子 agent 用 glob='**/*' 时永远返回 (no matches),子 agent 据此报告「没有调用点」,父 agent 照单全收。已修(tests.test_tools.OtherTools.test_grep_glob_accepts…。项目先写失败测试再修,同配置重录;原 trace 留在 traces/superseded/,本章所有数字都不含它。

子 agent 的演进:每一代都被上一代的某个真数字逼出来
  1. 所有 harness 的默认形态;本课项目 T4 的「不用子 agent」两次运行
    解决了不需要交接:主 agent 亲眼看到每一行搜索结果,不会有信息在转手时丢失。
    卡在两次 grep 结果进主上下文 563 / 1,120 token,之后每轮跟着重发(A5);要判对兼容性还得读 7 个文件(内容 1,818 token),两次都没读——找到 5/6 与 5/6,兼容性判对 3/6 与 2/6。
  2. learn-claude-code s06、CoreCoder AgentTool、OpenCode task、Claude Code 子 agent、本课项目 delegate
    解决了搜索的中间结果留在子 agent 自己的窗口里;4 组对照里 3 组主上下文峰值更低。
    卡在sub-text-s2 的子 agent 在自己的 grep 里看到了 5 个真实调用点,报回的 9 个「调用点」里 9 个是 import、注释或定义行;父 agent 照抄,verifier 找到 0/6。
  3. 本课项目 with_decisions、Hermes 的 context 字段、Codex v2 的 fork_turns(默认 all)、Claude Code 的 fork
    解决了子 agent 拿到了父 agent 的全部约束;sub-dec-s1 的子 agent 报回的真实调用点行号 5/6。
    卡在两次仍然 0/6:s1 的子 agent 一个文件都没读就猜兼容性,父 agent 全程 write_file 0 次却说「已写进 CALLERS.md」;s2 的子 agent 正则写错,15 轮预算只用了 3 轮就交差。
  4. OpenCode subagent_depth、Codex agent_max_depth、Hermes max_spawn_depth(默认都是 1);Claude Code 默认 3 层;Hermes、CoreCoder、lcc、本课项目的子 agent 预算独立
    解决了递归有上界;子 agent 的轮数不挤占父 agent:sub-dec-s1 的父 agent 只需要 3 轮,父子共用一个计数时得给到 7。
    卡在预算防的是停不下来,不防过早收工:T4 的 4 个子 agent 各有 15 轮,最多只用了 4 轮。
  5. learn-claude-code s17 独立评估器、Codex Guardian(A3)、Anthropic cwc-long-running-agents 的 evaluator、Hermes 后台自省 fork
    解决了完成与否不再由干活的 agent 自己说了算;评审者没有写工具,不能顺手「修好」来掩盖问题。
    卡在T1 与 T4 里最后一句话明确声称完成 20 次,verifier 判失败 12 次;评审者也是模型,也会被说服 → 确定性的判决在 A9。
旁支 多个 agent 同时改代码:worktree 隔离工作目录,同一文件的写串行
learn-claude-code s13、Codex(0.156 起默认开 worktree)、CoreCoder FILE_MUTATION_LOCK、Pi withFileMutationQueue
本课项目的子 agent 是同步的、只读的,T4 里不会发生写冲突;并行写同一个文件时才需要它们。worktree 不是沙箱。
旁支 Pi:核心不内置子 agent,交给扩展
Pi 31b513e3 README「No sub-agents」;examples/extensions/subagent
不是做不出来:子 agent 扩展 1,141 行,权限确认扩展 34 行,都放在核心之外。

数字口径:本课项目 miniagent 用 Qwen/Qwen3-8B-MLX-4bit 本机录制的 T4(2026-09-26,找全 parse_date 调用点,6 次运行 + 4 条子 agent trace;单一模型、每种配置 2 个 seed,只说明现象存在,不说明比例)。token 取自服务端 usage;「内容 token」用录制模型的 tokenizer 现数。耗时不作比较(本机有其他任务同时占用)。

这一课的结论

同一个「找全调用点」的任务,子 agent 改变的是信息在哪个窗口里:搜索的中间结果留在子窗口,主窗口只收报告。它同时引入了三个新的失败点:交接写什么(约束与已做过的判断会丢)、每一层跑多少(预算与深度按层算)、报告信不信(自述会错,要有外部检查)。把这三个选择写进 DECISIONS.md,本章的里程碑就完成了。

下一章:一次任务里,父 agent、子 agent、评审者都在调模型。换模型、换 provider、控成本怎么办?A8 讲模型接入与计费。