XMM Proposal Agent · 并行传送带 · D049 / D050

它现在是什么,实测给了什么,以及该怎么改

一个 planner 决定 K 件互不冲突的工作,K 个独立进程并发执行,一个 reduce 单写者提交账本。 机制是对的;但它并行的那根轴,不是通往 proposal 的那根轴。

观测窗口 2026-07-26 08:05–10:09 UTC 47 ideas · 12 roles · 4 engines K: 3 → 10(AIMD 单调爬升)

01 — 两根轴

传送带调度的不是「阶段」,是 (idea × role) 的格子

理解整个架构只需要一件事:工作单元不是「一个 idea」也不是「一个 role」,而是一次 shift —— 某个 role 在某个 idea 上干一件有界的活,末尾留一行 3-line handoff。 没有 worker 端到端拥有一个 idea。

于是全局状态是一张矩阵:行是 47 个 idea,列是 pipeline 的 12 个 role。 每次 batch,planner 从这张矩阵里挑出 K 个互相独立的格子并发点掉。

观测到的一批调度(2026-07-26 08:47 UTC)
idea M0R1R2 R3R4R5 R6R7
I021 M101··wk1··
I017 M83··wk2····
I024 M31··wk3·····
I039 NGC 5907··wk4·····
I018 M83 ULXwk5····
I005 NGC 4631····
…另 41 个········

历史 shift 已完成 该批次的 5 个并发格子 未触及

图 1 — 一个真实批次实际调度的 5 个格子。注意它们全部落在不同的行: 并行发生在 idea 之间,从不发生在同一个 idea 内部。这不是巧合,是 planner 独立性谓词第 1 条强制的。

独立性谓词 —— 为什么必须「不同 idea」

并行规划 prompt 要求同一 batch 内任意两个 shift 满足全部四条:

  1. 不同 idea —— 一个 idea 文件最多一个写者(单写者不变式)。
  2. 无 D023 冲突 —— 同一 idea 的 R4 攻击与 R5 裁决不能同批,对抗性必须跨 shift 分离。
  3. 无数据依赖 —— 不能有一个 shift 读另一个 shift 正在写的产物。
  4. 排除 in-flight —— 上一批还在跑的 idea 不能碰,这是 prefetch 流水线的前提。

第 1 条是所有设计后果的源头。它把并行度的上界钉在「当前有多少个可推进且互不相干的 idea」上, 而不是「后端能承受多少并发」上。

02 — 时序

一个 batch 的四拍,加一个错峰的第五拍

PLAN 是串行的、昂贵的、LLM 的;COMPOSE 是确定性的、毫秒级的;MAP 是唯一并行的部分; REDUCE 是全局唯一写者。PREFETCH 想把下一批的 PLAN 藏到 MAP 底下。

一个 batch 的四拍加一个错峰拍 PLAN 串行需 20 到 24 分钟且位于临界路径;COMPOSE 为确定性毫秒级步骤;MAP 是 K 个 worker 并发,并以一个全栅栏结束,快的 worker 空转等最慢的;REDUCE 是全局单写者;PREFETCH 在 MAP 期间并行规划下一批。 1 · PLAN 2 · COMPOSE 3 · MAP 4 · REDUCE planner · hermes / nim-large 读 47 个 idea 账本 + 调度约束 实测 20–24 min · 在临界路径上 compose 确定性 毫秒级 wk4 R2 I039 wk3 R2 I024 wk1 R5 I021 wk2 R3 I017 wk5 R1 I018 每个 worker = 一个独立 CLI 进程,只写自己的 idea 文件 + 自己的 frag 5 · PREFETCH 无产出 PREFETCH 在 MAP 期间规划下一批(排除 in-flight ideas) 全栅栏 · 快的空转等最慢的 reduce 全局单写者 合并所有 frag 规划占临界路径 44% 槽位利用率 52% prefetch 命中 0 / 1 提交 5 个 shift / 124 min
图 2 — 一个 batch,横向按实测时长成比例。三个关键结构事实: PLAN 在临界路径上MAP 以全栅栏结束(灰条就是空转的槽位); PREFETCH 的 planner 在 REDUCE 执行期间仍然活着

谁真正在执行 —— 不是「一个个跑」

planner 永远是一次 hermes 调用(默认 nim-large),它不看 route。 worker 是 K 个真并发的独立进程,引擎由 packet 的 route 加配额门共同决定, 失败沿链回退:

worker 引擎解析与回退链 route 为 hermes 时解析出单元素链,没有回退;route 为 claude 或 codex 时按配额比排序四级链;R2 由 driver 路由而非 planner。frag 非空即视为完成;为空且用时不足 300 秒则回退下一个引擎,为空且用时超过 300 秒则整条链放弃并记为 noop。 packet .route ROUTE = HERMES hermes 单元素链 · 失败就是失败,没有第二次机会 ROUTE = CLAUDE / CODEX claude codex agy hermes-terra 配额比决定谁先 R2 — 由 DRIVER 路由,不由 PLANNER claude 若 ratio<1.0 codex 若 ratio<1.5 hermes 配额比是「相对匀速节奏的周消耗」,烧得快 → 更早自动降级到 hermes frag 非空? proof-of-work 是 → done 清除引擎 cooldown 否 · <300s → 回退 标记 4h cooldown 否 · ≥300s → noop 放弃整条链
图 3 — 引擎解析与回退。注意 route = hermes 的 shift 得到一条单元素链: 没有任何回退。观测到的那一批里,5 个 worker 有 4 个是 hermes。

03 — 与 pipeline 的关系

role 是一条严格串行的链;batch 是横切它的一刀

这是全部张力的所在:proposal 的交付依赖单个 idea 走完整条链的延迟, 而 batch 并行的是不同 idea 的不同位置

pipeline 的 role 链与并行的关系 上游 R0 与 M0 供给 R1 与 R2;R2 到 R3 到 R4 到 R5 对同一个 idea 是不可压缩的串行链,最多两轮 R4 到 R5 修复,只有 R5 能写 Gate ledger;R5 的出口是 kill、park、needs-data 回到 M0,或晋升到 R6 到 R7 到 R8。四类全局阶段是 serial-only,出现时整批塌成 K 等于 1。 上游 · 连续 DIVERGE R0 覆盖矩阵 M0 候选源包 R1 有源事实 R2 生成 idea CONVERGE · 每个 IDEA 严格串行 R3 冻结设计 R4 kill-shot R5 四门裁决 ≤2 轮 repair 唯一的 Gate ledger 写者 QUANTIFY → EMIT · AO 事件驱动 R6 可行性 R7 起草 R8 TAC 模拟 kill · 墓地 park needs-data 回到 M0 取数 SERIAL-ONLY —— 出现时整批塌成 K = 1 R5-Pairwise Elo 锦标赛 · portfolio 重打分 · D048 降级淘汰 D043 gate-drift 修复(一次正文假阳性就足以抢占整个并行池)
图 4R2→R3→R4→R5 对同一个 idea 是不可压缩的串行链, 再加最多两轮 R4→R5 修复;只有 R5 能写 Gate ledger。 并行谓词禁止同一 idea 在一批里前进两步,所以这条链的长度完全不受 K 影响

当前 47 个 idea 分布在哪

seed38
killed5
parked2
developing1
proposal-ready0
图 5 — idea 账本的生命周期直方图,实测 2026-07-26。 proposal-ready = 0 是显式的零,不是缺数据。 与 planner 自报的 role 直方图一致:R3 = 6、R5 = 5、R4 = 1, 而 R2 / R0 / R1 / R6-lite / L 全部为 0(饥饿)。

这就是架构层面的核心问题

并行度加到 10,推进的是横向:更多 seed 拿到 R3 冻结。 但 proposal 需要的是纵向:某一个 idea 走到 R6 → R7。 而纵向的临界路径长度(R2→R3→R4→R5→repair→R6→R7,每步一个 shift) 完全不受 K 影响 —— 因为独立性谓词第 1、2 条禁止同一 idea 在一批里前进两步。

04 — 实测

124 分钟买到了什么

墙钟124 min08:05 → 10:09 UTC
提交的 shift53 个计划批次中的 2 个
规划占临界路径44%20 + 22 + 12 min
槽位利用率52%10105s / (5 × 3863s)
prefetch 命中0 / 1背景烧掉 39 min
白烧的 worker 时间64 min研究已完成,未落盘
单次运行的实测甘特图 批次一的规划耗时 20 分钟且未产出文件;批次二规划 22 分钟后并发五个 worker,其中三个在 12 分钟内完成却要在栅栏前空转约 53 分钟等待最慢的那个;同期的 prefetch 规划耗时 39 分钟且无产出;批次三规划 12 分钟后只调度了一个 worker。 08:00 08:15 08:30 08:45 09:00 09:15 09:30 09:45 10:00 10:15 BATCH 1 PLAN — 只输出散文,未写文件 BATCH 2 PLAN 22 min wk4 R2 I039 wk3 R2 I024 wk1 R5 I021 wk2 R3 I017 wk5 R1 I018 noop · 工具预算耗尽 prefetch 无产出 · 工具预算耗尽 全栅栏 BATCH 3 PLAN 12 min → 只调度 1 个 worker(serial-only 的 gate-drift 修复)
图 6 — 实测甘特图。三段红色是同一个根因: 模型把活干完了但没落盘(散文式规划 / 工具调用预算耗尽)。 灰条是空转的槽位:三个短 worker 在 12 分钟内完成,却要等约 53 分钟。
符合预期

单写者不变式、idea 级互斥锁、frag proof-of-work、摘要 sidecar、引擎链回退 —— 全部按设计工作。 5 个 worker 并发写各自的 idea 文件没有冲突;reduce 正确合并;校准日志记全了含 noop 的 5 条。

重复出现的 idea 不是 bug:同一个 idea 再次被调度是 R2→R3→R4→R5 的角色推进。

不符合

失败模式没有被任何机制接住。「说完但没写文件」发生三次, 驱动只能看见文件的有无,于是一次记 strike、一次记 noop、一次静默丢弃 39 分钟。 已修:write-first 协议 + 把 harness 的工具调用上限从 250 提到 500。

不符合

AIMD 在测一个恒为零的信号。共享后端 30 分钟窗口 255 请求、0 个 429、0 失败, 于是自适应并行度的 decrease 分支结构性永不触发,K 只会 3→4→…→上限 12。 而真正的约束是栅栏尾延迟和独立 shift 的供给 —— 两者都不在这个信号里。

设计取舍

lint 假阳性能抢占整个并行池。gate-drift 检查把某个 idea 正文里的 「is not resolved here」匹配成状态词 RESOLVED;而该修复是 serial-only, 于是 K = 6 塌成只调度 1 个,花掉 12 min 规划 + 322s 工作去改一个否定句。

05 — 重新想

把并行换一根轴:从「跨 idea 铺开」到「单 idea 打深」

以下是提案,不是实测结论。核心论点只有一句:既然交付物的临界路径是单个 idea 的串行链, 那么并行预算应该花在压缩那条链的每一步,而不是同时开更多条链。

关键发现:「不同 idea」比真实的写冲突约束严得多

独立性谓词第 1 条的理由是「一个 idea 文件最多一个写者」。但查 role 规格:

所以真实不变式是「每个 idea 每批最多一个 Gate-ledger 变更者」, 而不是「每个 idea 每批最多一个 worker」。当前谓词把最有价值的一种并行 —— 同一个 idea 上的 N 路异构 R4 攻击 —— 直接禁掉了, 而设计原则恰恰要求它:一个同源的 R4 可以作为基线攻击,但不能是绑定性 kill / reframe 的唯一独立对手。

现在 · 跨 idea 铺开

  • wk1 · R5 I021
  • wk2 · R3 I017
  • wk3 · R2 I024
  • wk4 · R2 I039
  • wk5 · R1 I018

5 个 idea 各前进一步。没有任何一个 idea 更接近 proposal。

提案的四条改动

  1. 谓词从「不同 idea」放宽到「每 idea ≤ 1 个 ledger 变更者」。 并行的语义从「K 个 idea」变成「K 个不冲突的写者」。不需要新机制, 只需要改 planner 谓词 —— worker 模板里 per-role-per-idea 的文件命名规则本来就已经要求了。
  2. 栅栏换成滚动补位。worker 完成即从队列取下一件,不等最慢的那个。 这是唯一能把 52% 槽位利用率提上去的改动。
  3. 规划降级为「便宜的确定性调度 + LLM 只写 task 正文」。 现在一次 PLAN 要 20–24 分钟、占临界路径 44%,因为 LLM 在重读 47 个账本做优先级排序。 但优先级规则(阻塞优先 → 节流 → 筛选下限 → 配额/收敛 → 阶段 FIFO)是可执行代码, 不是判断题,其中一部分已经由脚本在算了。让程序选出候选格子,LLM 只负责给选中的格子写任务正文。
  4. 给「冠军深度」一个显式预算。47 个 idea / 38 个 seed / 0 个 proposal-ready 说明选择压力不够。按 Elo 与门通过数选 2–3 个冠军,规定每轮至少 N 个槽位必须花在冠军的链上, 剩下的槽位才做广度。

仍待决定的两件事

一,冠军是谁选的?可以是 R5 portfolio(但它 serial-only,会抢批次), 也可以是确定性打分(门通过数 + Elo + 距 R6 的步数)。倾向后者 —— 它属于调度器,不属于科学判断。

二,异构 R4 的裁决怎么合并?3 路 R4 会产出 3 份 critique。 D023 只要求攻击者 ≠ 作者,没规定多攻击者的合并规则。 最小可行方案是 R5 在下一批读全部 3 份、FATAL 取并集、CORRECTION 去重 —— 但这必须写进 R5 的 role 规格,否则会变成裁量空间。