《Codex 的封装》从 2026-09-02 起记录我在 Codex 上的子代理调度规则:根代理协调和验收,代码交给 Sol 实现者,高风险改动加 Astra 审查,需要运行时证据时加 QA,窄问题交给 Luna。规则写好以后,我一直没有回头看它在真实会话里的执行情况。
这篇用本机 9 月以来的 rollout 回答六个问题:规则有没有被遵守,哪些规则起了作用,哪些规则在制造多余的工作,钱花在了哪里,任务到底完成得怎么样,模型能力的波动造成了什么。结论直接改写了那篇文章的派发与容错规则。
数据与口径
Codex 把每个线程写成 ~/.codex/sessions 或 archived_sessions 下的一个 JSONL rollout。统计用到的字段:
| 记录 | 字段 | 用途 |
|---|---|---|
session_meta | thread_source、agent_role、parent_thread_id、cli_version、multi_agent_version | 区分根线程与子线程,还原父子关系和角色 |
turn_context | model、effort、sandbox_policy、approval_policy | 每一轮实际使用的模型、档位和权限 |
function_call | spawn_agent 的 agent_type、model、reasoning_effort、fork_turns | 派发参数是否完整、是否与角色一致 |
| 工具输出 | agent thread limit reached | 容量失败 |
token_count | total_token_usage | 输入、缓存输入、输出 token |
task_complete | last_agent_message | 审查与 QA 的结论 |
范围是 2026-09-01 到 10-13 上午的 463 个 rollout 文件:70 个用户发起的根任务(不含 1 个自动审查线程,43 个派了子代理)、377 个子代理线程、398 次 spawn_agent,CLI 从 0.152.1 到 0.162.1。日期按 UTC。按两次规则变化分成三段:
- E1(09-01 至 09-13):GPT-5.6 子代理,线程上限 09-08 前为 2。
- E2(09-14 至 09-26):GPT-5.6 子代理,经过 0.154.0 全篇复核。
- E3(09-27 至 10-13 上午):GPT-6 角色,根代理先是 Astra,09-29 起改为 GPT-6.1 Sol。
口径说明:
- 一个根线程可能分成多个分页文件,token 计数在文件之间累加,所以取每个线程的最大值,不能相加。
- 派发参数里的
message是加密的,统计只看参数结构,不看任务正文。 - 积分按定价页的 Standard 单价(每百万 token:Astra 250 / 25 / 1,250,GPT-6.1 Sol 50 / 2.5 / 250,GPT-6 Sol 50 / 5 / 250,Luna 2.5 / 0.25 / 12.5,依次是输入、缓存输入、输出)乘以 token 数估算。它不含 Fast 倍率,也不等于订阅额度的实际扣减。
- 审查结论按
APPROVE、BLOCK等关键词匹配,可能漏判自由格式的结论。 - 根代理直接改了哪些文件,只统计了
apply_patch记录;通过 shell 或脚本写文件的操作没有扫描。 codex doctor另报告了 150 个subagent:other来源的线程,多半来自自动审查,本文没有分析。
规则遵守情况
| 指标 | E1 | E2 | E3 |
|---|---|---|---|
spawn_agent 次数 | 214 | 105 | 79 |
| 显式给出 model 与 effort | 214 | 105 | 79 |
| 使用自定义角色 | 191 | 74 | 78 |
| 角色、模型、effort 与角色文件一致 | 191/191 | 74/74 | 74/78 |
fork_turns 为 none | 214 | 105 | 79 |
| 线程上限失败 | 21 | 0 | 0 |
| 子代理继续派发 | 0 | 0 | 0 |
文本规则的执行几乎没有缺口。E3 的 4 次不一致都发生在 09-27 切换当天,仍是 GPT-5.6 的派发;切换之后没有 GPT-5.x 子代理。E2 有 31 次派发没带自定义角色(20 次不填、11 次用内置 default),这些线程拿不到角色文件里的 developer_instructions,也就没有"不提交、不委派、返回证据包"这些约束。E3 只剩 1 次。
E2 里有 8 个根任务的根代理用了 ultra,31 次没带角色的派发中有 16 次出在这些任务里;另外 15 次和 E1 的 23 次都出在没用 ultra 的任务里。数据只能说明二者相关,E3 的下降更可能来自 09-14 和 09-27 两次规则修改。模型目录对 ultra 的说明是 "Maximum reasoning with automatic task delegation",现行规则据此要求 ultra 会话仍按派发理由和预算执行。
起作用的规则
线程上限与复用。 21 次派发失败(连同 followup_task 与 send_message 共 27 次 agent thread limit reached)都发生在 09-04 到 09-08,这段时间上限大部分时候是 2。09-08 改为 6 并要求优先用 followup_task 复用匹配的线程,09-09 之后五周内没有再出现;09-09 之后单个根任务最多派出 36 个线程,没有调用过关闭工具。旧文"完成的线程不释放槽位"来自上限为 2 的 0.153.4 桌面会话,不能推广到现在。
根代理不写生产代码。 按 apply_patch 记录,E3 根代理直接改的文件约九成是 Markdown 计划和文档,其余是 Codex 配置文件、一个 .gitignore、脚本生成的 logo 设计稿和一份 HTML 报告,没有通过 apply_patch 改生产或测试代码。唯一一次违规在 09-08:根代理直接改了仓库里 7 个 tsx、1 个 ts 测试文件和 package.json。
审查确实拦下了问题。 E3 的 17 个审查线程里 13 个至少给过一次 BLOCK,最后 11 个以 APPROVE 收尾。实现、审查、修复、再审查这条回路在工作。
| 审查线程 | E1 | E2 | E3 |
|---|---|---|---|
| 至少 BLOCK 一次 | 7/41 | 11/25 | 13/17 |
制造多余工作的规则
所有代码都必须委派。 旧规则要求任何生产或测试代码改动都交给 Sol 实现者,根代理不得自己改。E3 的 21 个根任务里有 17 个派了子代理。按触发派发的那条用户消息逐条核对,下面这些请求本可以由根代理直接完成:
| 用户消息 | 派出的子代理 |
|---|---|
commit & push(8 条这样的消息中只有这一条触发派发) | Astra 审查一个 |
修一条 pnpm dev 启动报错 | 实现一个 |
| 手机调试中回复"已进入场景,开始游玩" | Astra 审查一个 |
| 分析项目并生成一份 HTML 报告 | 侦察、实现各一个 |
| 回复"允许"(批准一个操作) | 两次分别派出实现一个、QA 一个 |
| 回复"retry" | 未带角色的 Sol 一个 |
Codex 文档写明,项目或技能指令要求委派时 Codex 会委派,而子代理流程比单代理运行消耗更多 token,因为每个子代理各自做模型与工具调用。Subagents "所有代码必须委派"正是这样一条项目指令。旧版文章没有写这条规则的理由,我推断是根代理当时用 Astra、比 Sol 贵得多;09-29 根代理换成 GPT-6.1 Sol 以后,根代理的单价不高于实现者,这个理由不再成立。
协调开销。 在派了子代理的根任务里,根代理的输出 token 占根加子代理输出总量的 33%(E3)。前两段按口径不同在 15% 到 31% 之间,不足以说明趋势,但 E3 的三分之一已经说明委派把相当一部分工作变成了协调。E3 平均每个派了子代理的根任务调用 send_message 69 次、wait_agent 50 次、followup_task 32 次。委派把一部分工作从实现变成了沟通和等待。
名义上的只读。 E3 根代理的 212 个轮次里有 207 个运行在 danger-full-access / never,其余 5 个是 workspace-write。子代理继承父会话的实时权限,审查和侦察线程没有一个轮次以 read-only 运行,角色文件里的 read-only 从未生效。config.toml 里写的是 workspace-write,但会话在界面里选了完全权限,配置值只影响新会话和 codex exec 的默认值。
钱花在哪里
E3 的估算积分按角色和模型分布:
| 角色 | 模型 | 线程 | 积分占比 |
|---|---|---|---|
| 根代理 | gpt-6-astra | 8 | 42% |
| 实现 | gpt-6-sol | 34 | 21% |
| 审查 | gpt-6-astra | 17 | 15% |
| QA | gpt-6-sol | 15 | 8% |
| 根代理 | gpt-6.1-sol | 13 | 6% |
| 未带角色 | gpt-6-sol | 1 | 5% |
| 实现 | gpt-5.6-sol | 3 | 2% |
| 侦察 | gpt-6-luna | 8 | 0% |
表中不含 1 个仍在 gpt-5.6-terra 上的 QA 线程;09-29 从 Astra 切到 GPT-6.1 Sol 的那个根任务整体按 GPT-6.1 Sol 计价。
Astra 根代理加 Astra 审查约占 57%。Astra 根代理与 GPT-6.1 Sol 根代理的单任务积分相差约十倍,其中五倍来自单价,其余来自任务大小不同,这一对比只说明方向。另一处可省的地方是缓存:子代理的输入约 98% 命中缓存,GPT-6.1 Sol 的缓存输入单价是 GPT-6 Sol 的一半,实现与 QA 换成 GPT-6.1 Sol 后,同样的 token 量约便宜 38%。
任务到底完成得怎么样
前五节看的是规则执行和成本,这一节看结果。我按用户消息、失败轮次和最终回复,逐个判读了 09-14 以来的 39 个根任务。判读只有一个人,没有第二人复核,比例只作参考:
| 结果 | 任务数 |
|---|---|
| 按要求完成 | 20 |
| 纠正后完成 | 5 |
| 部分完成 | 11 |
| 放弃 | 2 |
| 偏离目标 | 1 |
委派的 33 个任务里 14 个按要求完成,每个任务平均被纠正 1.0 次,汇总轮次时间中位 48 分钟;直接完成的 6 个全部按要求完成,平均纠正 0.17 次,中位 7 分钟。直接完成的都是小任务,两组不能直接比较;只看 30 分钟以内的 11 个委派任务,8 个按要求完成、2 个放弃,中位 14 分钟。能说的是:数据里看不到委派带来的质量提升。
84 小时根代理轮次时间的去向:
- 界面类任务的验收错位。 一个浏览器扩展项目的 7 个界面任务占了 56%。它们的共同点是本地测试和门禁全部通过,我看了却觉得不能用或不好看,然后反复返工。有一次根代理在一个可丢弃的原型上打磨了很久,我指出它改错了地方、又多花了约 100 美元,并追问当初给的目标。
- 等待子代理。 有计时的轮询等待约 11 小时(1,036 次,单次不超过约 60 秒);09-26 之前另有 294 次没有计时,按平均 37.5 秒估算,合计约 14 小时。这段时间子代理在干活,不全是浪费,但单个实现者加根代理空等的组合把一部分工作变成了等待。
- 上下文压缩。 压缩耗时从 09-26 起才有记录:216 次计时的压缩中位 66 秒,合计 6.9 小时,其中 2.3 小时在子代理线程里,不计入上面的 84 小时;09-14 以来的压缩共 491 次。
- 满载和断流。 失败轮次约 206 分钟,加上我重发前的约 60 分钟,合计约 4.4 小时;没有一个任务最终卡死在容量上。
- 权限或环境阻塞。 至少 90 分钟,例如浏览器进程被沙箱拒绝启动以后,会话一直等到我回来。
按出现频率排序的失败方式:交付结果被纠正(15 个任务、34 次纠正)、中途停下需要我催"继续"或"还剩什么"(9 个任务)、环境与权限阻塞(约 8 个任务)、满载或断流(根代理 3 个任务、子代理 5 个任务)、目标漂移与交互摩擦(3 个任务)。最贵的问题出在验收方式和任务规格上,模型能力和算力是第二位的。
模型能力的波动
我原以为"算力潮汐"是按时段起伏的,数据给出的是另外四种形态:
| 形态 | 本机证据 | 规律 |
|---|---|---|
| 满载失败 | 35 个失败轮次里 25 个是 Selected model is at capacity,其余是额度耗尽 5 个、断流 4 个、其他 1 个;除 GPT-6 Luna 外所有在用模型都出现过 | 集中在少数日子;10 月的几次只打在 GPT-6.1 Sol 上,同一会话里的 GPT-6 Sol 和 Astra 子代理照常工作 |
| 速度骤降 | Astra 在 09-12、GPT-6 Sol 在 09-30 的日中位速度降到平时的一半 | 按天发生;多数模型按小时的中位速度只差 1.1 到 1.5 倍,GPT-6 Sol 的 2.7 倍也来自 09-30 那一天 |
| 额度耗尽 | 周额度在 09-10、09-16、09-29 三次打满 | 来自 Astra 的大量使用;10 月最高 43% |
| 模型间的稳定差距 | 输入 10 万到 20 万 token 时,GPT-6.1 Sol(根代理,xhigh)中位 33 秒,GPT-6 Sol(子代理)13 秒;6.1 Sol 的延迟还随上下文增长,5 万以下约 17 秒,25 万以上约 41 秒 | 模型特性,不是波动;外部实测的 6.1 Sol 速度同样在 15 到 26 tok/s |
两组对比里,6.1 Sol 的样本都是根代理、6 Sol 的样本都是子代理,角色本身也影响速度(Astra 根代理比子代理慢约 1.3 倍),扣除这一点后约为 2 倍(原始比较 2.6 倍);同一时刻、都作为根会话的对照实验里是 1.8 倍(见第 8 节)。
外部记录能对上几天:GPT-6.1 Sol 发布后的头两天 OpenAI 确认过负载高峰,与本机 09-30 GPT-6 Sol 降速和当天的满载失败对得上;10-13 OpenAI 状态页有 GPT-6.1 Sol 在 Codex 的降级事件,与本机当天的失败只对得上日期,具体时刻要看状态页用的时区;09-12 OpenAI 修复过一次 Astra 的服务配置问题,与本机 Astra 降速只对得上日期。没有找到任何按时段的规律。
Codex 自己的行为:HTTP 和流中断会自动重试,0.161、0.162 起遵循服务端的重试建议;这些重试失败,或者遇到满载、额度耗尽,整轮就结束,本机观察到的失败里没有一次自动换模型。最长的一次,一个跑了 75 分钟、累计 1,260 万输入 token 的根代理轮次在满载时结束,等我重发。官方文档里有 model/rerouted 事件,本机一次都没出现过。
根代理对照实验
2026-10-13 的根代理对照实验:同一时刻用四种配置跑同一个只读分析任务(读 6 个源文件,写 600 到 900 字、每句带行号引用的说明,最后列三个风险),四个都是根会话,上下文 4 万到 5.5 万 token。引用准确性由一个独立审查者逐条核对:
| 配置 | 总耗时(含约 10 秒启动) | 模型时间 | 输出 token(推理占比) | 引用核对 |
|---|---|---|---|---|
gpt-6.1-sol xhigh | 210 s | 190 s | 5,348(36%) | 47 处,0 错,2 处偏差 1 到 3 行 |
gpt-6.1-sol medium | 108 s | 92 s | 2,505(4%) | 43 处,0 错,4 处偏差 |
gpt-6-sol xhigh | 134 s | 107 s | 6,385(56%) | 47 处,0 错,2 到 3 处偏差;唯一发现一个未被使用的字段 |
gpt-6.1-sol xhigh + Fast | 209 s | 188 s | 6,332(40%) | 45 处,0 错,2 处偏差 |
四份答案都没有错误引用,差距在样本能分辨的范围以内。这次只有一个任务、各跑一次,能说明的有限:在这种读代码、写说明的任务上,gpt-6.1-sol 把 effort 从 xhigh 降到 medium,耗时减半,引用准确性没有可见损失;Fast 那一组的生成速度高约 20%,但 rollout 里看不出 Fast 是否确实生效,单次对比也分不清这是 Fast 的效果还是随机波动,总耗时没有缩短;gpt-6-sol xhigh 的总耗时比 gpt-6.1-sol xhigh 短约三分之一。据此本机根代理的默认 effort 从 xhigh 改为 medium,难题在会话里用 /model 切回 xhigh;规则要求根代理在 L3 及以上的任务里提醒一次,因推理深度不够而失败时停下请用户切换,不在 medium 上重试(见第 9 节)。换一天重跑,才能把模型特性和当天的波动分开。
据此做出的改动
改动已经部署到本机,并写进《Codex 的封装》:
- 直接完成为默认。 只有可并行的独立工作、不该装进根上下文的大块内容、触发条件要求的独立验证、用户明确要求这四种理由才派发;git 操作、单条命令、文档报告、配置、设备操作和约三个文件以内的明显修复不派发。
- 级别与积分权重。 按本机约 98% 缓存命中的 token 构成折算,以 GPT-6.1 Sol 为 1,Luna 约 0.08、GPT-6 Sol 约 1.6、Astra 约 8;L0 到 L4 的预算是 0、2、4、14、20,L2 及以上派发前报告账本;给已有线程分配新工作的
followup_task也计入预算。结构移植自 Claude Code 侧的 dispatch-policy。 - Astra 闸门。 常规审查改用 GPT-6.1 Sol;Astra 每个任务最多一次,只在 L3 及以上、针对一个具体的高风险问题串行使用,不复用线程。
- 按职责命名角色。
implementer、qa、reviewer、astra_reviewer、recon。实现和 QA 用 GPT-6 Sol,常规审查用 GPT-6.1 Sol,两者由我预授权互为后备;implementer、qa、reviewer不固定模型,model 和 effort 由每次派发给出;按原因升档:没尽力就提 effort,能力不够就换更强的角色。luna_recon_max并入recon。 - 失败处理。 子代理满载或断流时,根代理告知我并在后备模型上从最后完成的步骤重派一次,这次重派不受级别预算限制;再失败就依次改为直接做、缩小或串行任务、停下报告续跑点。不自建重试循环,额度耗尽时停止派发。
- 验收与进度。 界面类任务做到第一个可见里程碑就停下给截图;同一验收项连续两轮没有可量化的进展就停下给选项;超过约 30 分钟的工作先复述目标和验收标准,长步骤前写进度行;没有审批提示的阻塞立即停下说明;无关任务开新线程。
- 派发契约。
agent_type与fork_turns = "none"必填(0.162.0起不填fork_turns就复制完整父历史);审查改为对抗式,第二轮审查开新线程。 - 根代理默认 medium。 按第 8 节的对照实验,根代理默认 effort 从 xhigh 改为 medium;L3 及以上的任务里,根代理提醒我可以用
/model切到 xhigh;它因推理深度不够而失败时,停下请我切换,不在 medium 上重试。 - 如实写明权限。 继续以完全权限运行会话;只读角色只由指令约束。
- Windows 沙箱改为 MXC。 探针输出
MXC_OK后把[windows] sandbox从unelevated改为mxc。
部署后用只读 codex exec 烟测验证:子线程 rollout 显示 recon 以 gpt-6-luna、effort low 运行;qa 和 reviewer 分别按派发值在 GPT-6.1 Sol 和 GPT-6 Sol 上运行,都是 read-only。
下次复盘看什么
| 指标 | 本次基线(E3) | 期望方向 |
|---|---|---|
| 派了子代理的根任务占比 | 17/21 | 明显下降,小任务回到 L0 |
| 每个派发任务的子代理数 | 4.6 | 下降 |
| 根代理输出占比 | 33% | 下降 |
| Astra 积分占比 | 57% | 大幅下降 |
| 审查线程至少 BLOCK 一次 | 13/17 | 保持;常规审查改用 GPT-6.1 Sol 后若明显下降,要检查漏判 |
| 线程上限失败 | 0 | 保持 0 |
| 按要求完成的根任务(单人判读) | 20/39 | 上升,界面任务先过可见检查点 |
| 每个委派任务被纠正的次数 | 1.0 | 下降 |
| 失败轮次(其中满载) | 35(25),六周 | 后备重派后,因满载停下等人的次数下降 |
| GPT-6.1 Sol 根代理匹配延迟 | 33 s | 改为 medium 后复测,同时看返工是否增加 |
| 上下文压缩(09-26 起有计时) | 216 次、6.9 h | 下降 |
统计脚本保存在本机 ~/.codex/scripts/:responses.py 抽取每次模型响应的延迟与 token,speed_report.py 出速度与波动报表,failures.py 统计失败与中断轮次,dispatch.py 统计派发合规和每条消息触发的派发。不做每周例行,换模型或改规则之后跑一次。
还没有验证的:后备重派在真实满载时的表现;根代理对照实验只跑了一个任务,要换一天重跑;GPT-6.1 Sol 与 GPT-6 Sol 作为实现者的质量差异,需要拿同样的任务在两个 worktree 里各跑一次;PreToolUse hook 能否在 v2 派发路径上拦截 spawn_agent;L2 及以上的派发是否真的在第一次派发前报告账本。