Files
cc-web/.planning/multi-agent-v2-audit/findings.md
2026-07-24 08:00:31 +08:00

86 lines
14 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 调研发现
## 上游 Multi-agent V2
- 本机 `codex-cli` 版本为 `0.144.1`。
- `codex features list` 显示:`multi_agent` 已稳定且启用;`multi_agent_v2` 为 `under development` 且默认关闭;旧 `collaboration_modes` 已移除但配置值仍显示启用。
- 因此 V2 目前应按实验性、显式开关能力看待,不能假设所有 app-server 客户端都自动获得新行为。
- 本机二进制进一步暴露了 V2 的配置面:并发线程上限、等待超时上下限/默认值、用量提示、根/子代理提示文案、工具命名空间、隐藏 `spawn_agent` 元数据、仅非 code mode 启用,以及 `custom` / `explicitRequestOnly` / `proactive` 三种 `multiAgentMode`。
- 二进制中的 app-server 类型仍沿用 `CollabAgentToolCall` 和 `SubAgentActivity`,并新增/保留 `agentNickname`、`agentRole`、`agentPath`、`depth`、`parentThreadId` 等代理身份与层级元数据。
- 已从 OpenAI 官方 `openai/codex` 仓库的 `rust-v0.144.1` 标签对应提交 `44918ea1` 下载源码归档。V2 不是单一 UI 开关:涉及 feature config、上下文提示、agent control、独立的 `multi_agents_v2` handlers、CSV 批量派工、app-server `MultiAgentMode` 协议字段及线程状态/通知。
- V2 工具语义发生实质变化:`spawn_agent` 强制 `task_name` 并返回规范任务路径;默认继承全部历史,也可用 `fork_turns=none|all|N`;`fork_context` 在 V2 明确报错。
- V1 的 `send_input`、`resume_agent`、`close_agent` 被 V2 的 `send_message`(不触发新 turn)、`followup_task`(触发/续跑 turn)、`interrupt_agent`(中断但保留代理)、`list_agents` 取代。
- V2 `wait_agent` 不再接收目标列表,也不返回代理最终正文;它等待整个代理树的 mailbox 活动,用户 steer 也会提前唤醒。最终内容应经 mailbox/事件链消费。
- V2 引入规范层级路径(如 `/root/task1/task3`)和嵌套代理树,可按路径前缀列举;这会把 cc-web 从“按 agent id 展示卡片”推进到“代理树 + mailbox 事件”模型。
- `MultiAgentMode` 有 `custom`、`explicitRequestOnly`、`proactive` 三种策略,并作为 developer 上下文片段注入;默认 Ultra 推理会选 proactive,其余选 explicit-request-only(除非配置自定义提示)。
- 另有 `spawn_agents_on_csv` 批量任务:按 CSV 每行派生 worker、并发执行、worker 用 `report_agent_job_result` 回报、最后导出结果 CSV。它是独立的批处理能力,不是基础聊天 UI 必须支持的协议。
- app-server v2 schema 已出现 `multiAgentMode`,但 0.144.1 的 `thread/start` 处理器暂时把入参绑定为 `_multi_agent_mode`,并在 start/resume/fork 响应中固定回 `explicitRequestOnly`;还需确认 `turn/start` 是否才是当前有效入口。
- 已确认 `thread/start.multiAgentMode` 与 `turn/start.multiAgentMode` 都在 0.144.1 源码中标为 deprecated/ignored,注释明确要求用 Ultra reasoning effort;因此 cc-web 不应实现新的 `multiAgentMode` 控件或依赖其返回值。
- 相比 `rust-v0.143.0`,V2 工具 schema 基本未变,0.144.1 最重要的协议变化是事件标准化:`wait_agent` 从专用 `collab_waiting_begin/end` 改为 `item/started` + `item/completed` 的 `collabAgentToolCall(tool=wait)`;spawn/message/interrupt 从专用 `sub_agent_activity` event 改为 completed `subAgentActivity` turn item。
- 这意味着 cc-web 若只监听旧顶层 collab/sub-agent 通知会漏状态;若已有统一 `item/started`/`item/completed` 分发并识别这两种 item,则基本兼容。
- 官方提交记录对应三项连续迁移:canonical sub-agent activity(#31299)、canonical collab tool call(#31300)、canonical collab wait(#31301);另有 Ultra + 高并发提示(#31621)。
- OpenAI 最新稳定版实际已是 `0.145.0`(2026-07-21 发布),而非本机的 0.144.1。官方发布说明明确称“Stabilized the opt-in multi-agent V2 experience”,覆盖可配置子代理模型/推理等级/并发、角色恢复和代理导航,并在 #34383 将 V2 标记为 stable。
- 0.145.0 同时包含:统一多代理设置到 `agents`(#33550)、遵循子代理模型默认值(#33631)、恢复 V2 agent roles(#33657)、父线程持有的子代理线程只读(#33841)、agent picker 存活性/路径选择(#33921/#33922),并移除了 CSV-backed agent jobs(#34413)。因此 0.144.1 的 `spawn_agents_on_csv` 不能作为最新稳定版能力建议。
- 已获取 0.145.0 官方源码提交 `25af12f7`。V2 的核心工具仍是 `spawn_agent` / `send_message` / `followup_task` / `interrupt_agent` / `list_agents` / `wait_agent`,`fork_turns` 与 mailbox wait 语义保持不变;canonical `subAgentActivity` / `collabAgentToolCall` 事件路线也仍存在。
- 0.145.0 新增 `[agents]` 统一配置,包含 enabled、最大线程数、最大嵌套深度、默认子代理模型、默认子代理推理强度、interrupt message 等;旧 `features.multi_agent_v2` 仍保留兼容配置和部分 UI/工具参数。
- 精确配置语义:`multi_agent_v2` 在 0.145.0 为 stable 但 `default_enabled=false`;`agents.enabled` 默认 true 且旧 feature 开关优先。`agents.max_depth` 的 schema 明确写“V2 忽略”,所以 V2 的路径树不能按 V1 深度上限推断。
- V2 的 spawn `model` / `reasoning_effort` 在 0.145.0 默认不暴露,只有 `features.multi_agent_v2.expose_spawn_agent_model_overrides=true` 才进入工具 schema;`agent_type` 也只有实际配置角色时才暴露。cc-web 不应在固定 developer instructions 中假设这些字段总可用。
- 0.145.0 允许 full-history fork 叠加 model/reasoning override,只禁止 full-history 同时覆盖 agent_type;这再次证明 cc-web 当前硬编码的 `fork_context`/override 规则已经过时,最佳修复是让模型遵循运行时工具 schema,而不是在 cc-web 重述易漂移规则。
- 0.145.0 新增 child terminal turn → direct parent 的标准 completion envelope,结果以不触发新 turn 的 inter-agent communication 写入父代理 mailbox。因此即使 cc-web 没抓到 child 的独立 app-server 通知,Codex 核心仍可让 `wait_agent` 唤醒并把结果交给父代理;服务端 child route 缺口应定级为“实时卡片/状态观测缺口”,不是核心代理结果丢失。
- app-server v2 仍以 canonical TurnItem 生命周期为主,旧 `SubAgentActivity` 顶层 event 只保留给兼容/raw consumers。cc-web 当前使用 `item/completed(subAgentActivity)` 的方向正确。
- 已将当前用户实际使用的 Codex 从 0.144.1 升级为 0.145.0,路径仍为 `~/.local/bin/codex`;npm 包和 CLI 版本一致。升级后 `multi_agent_v2` 状态为 stable/false,未自动开启实验/opt-in 能力。
- 升级后仍有两个 2026-07-02/07-18 启动的 app-server 子进程,其 `/proc/<pid>/exe` 指向已删除的旧安装目录;它们不会热切换到 0.145.0。当前 ccweb 只检测到本轮一个 running 对话,但本轮不重启服务,避免中断正在进行的会话。
## cc-web 当前实现
- codebase-memory 初始索引只含 10 个源码文件,漏掉 `server.js` 与 `lib/codex-app-runtime.js`;执行 full reindex 后节点数仍未变化。`.cbmignore` 并未排除这些源码,推测是索引器对超大 JS 文件的提取限制。本轮对未索引文件按仓库规则降级到 `rg`/定点源码读取。
- 前端 `public/app.js` 已识别 `subAgentActivity`,并把它归并到 `collab_agent_tool_call` 子代理卡片;回归脚本覆盖 agentPath、agentThreadId、role、taskDescription、各 activity kind 与普通工具防误判。
- 后端 `lib/codex-app-runtime.js` 已明确处理 `item/started`、`item/completed`、`collabAgentToolCall`、`subAgentActivity`,并保留 started→completed 的输入合并与持久化。
- `server.js` 已有 canonical item lifecycle 路由和 collab 状态归并;说明 0.144.1 最关键的事件迁移在 cc-web 中已有针对性实现与回归。
- `lib/codex-app-runtime.js` 的 lifecycle 处理可兼容 V2:`item/started` 建立工具项,`item/completed` 合并并结束;`subAgentActivity` 会把 agentPath/agentThreadId/prompt/role 规范化成子代理卡片状态,V2 wait 的 `collabAgentToolCall` 也走统一路径。
- **明确缺口:** `CODEX_APP_COLLABORATION_INSTRUCTIONS` 仍注入 V1 规则:要求使用/推断 `fork_context`,并要求 `wait_agent` 持续等待“最终状态”。V2 会拒绝 `fork_context`,且 wait 只报告 mailbox 活动、不返回最终正文。这些开发者指令会直接诱导 V2 工具调用失败或空转等待,应优先改为版本中立/V2 兼容指令。
- `codexAppCollabToolName` 与 collab fallback 仍主要识别 V1 动作(spawn/wait/send_input/resume/close),未显式识别 V2 的 `send_message`、`followup_task`、`interrupt_agent`、`list_agents`。其中 `send_message` 被折叠为 `send_input`,其余需评估对历史恢复和状态归并的影响。
- **明确缺口:Ultra 被 cc-web 丢弃。** 上游 V2 用 `ReasoningEffort::Ultra` 触发 proactive,但 `server.js` 的允许集合、Codex App 模型字符串解析、`lib/agent-runtime.js` 的 CLI 参数解析,以及前端推理强度选项都只到 `xhigh`。即使 `~/.codex/config.toml` 配置 `model_reasoning_effort = "ultra"`,cc-web 也不会把它编码进 session model/turn collaboration settings。
- mock/regression 目前反而断言了旧的“重复 wait_agent 直到 final”指导,说明修复 V2 指令时必须同步调整 mock 摘要与回归断言,避免测试把旧语义锁死。
- **明确缺口:V2 spawn 的 child thread 未进入服务端路由表。** `handleCodexAppNotification` 只对 `collabAgentToolCall` 调 `syncCcwebMcpChildAgentsFromCollabItem`;0.144.1 V2 spawn 发的是 completed `subAgentActivity`。runtime/前端虽能画卡片,但 `ccwebMcpChildThreads` 没有登记该 child,随后 child 自己的 agentMessage/turn 通知会成为 unrouted,实时最终消息与状态无法可靠回填。
- 现有 mock 的多代理场景仍模拟 V1 `collabAgentToolCall(tool=spawn_agent)` 后再发 child turn;没有模拟“parent 收到 V2 subAgentActivity → 注册 child → 路由 child turn”的真实链路。因此当前回归通过不能证明 V2 live routing 完整。
- **嵌套代理树也未覆盖。** 即便 child 已被登记,`processCcwebMcpChildNotification` 只处理其 agentMessage 与 turn 生命周期,不处理 child 发出的 `subAgentActivity`;V2 grandchild 的创建/消息会被忽略。基础一层代理修复应为 P0,完整嵌套树可作为 P1。
- `recoverCcwebMcpChildThreadsFromPersistedToolCalls` 能从已持久化的 `subAgentActivity` 恢复 child map,但只在 cc-web 启动时的残留 turn 恢复路径调用;它不能弥补当前活跃 turn 中 V2 child 的即时注册缺口。
- V2 `SubAgentActivityItem` 本身不含原始 task prompt,cc-web 目前只能用 agentPath basename 生成标题;若要保留任务描述,需要额外关联同 call id 的 spawn 请求或接受“仅路径标题”的降级。这属于体验优化,不是协议阻断。
- 本机 `~/.codex/config.toml` 当前模型为 `gpt-5.6-sol`、推理强度 `xhigh`,且未配置 `multi_agent_v2`;结合 `codex features list` 可确认当前 V2 实际未启用。因此这些缺口目前不会破坏现有 V1 会话,但会阻断/削弱未来显式启用 V2。
## 差距与建议
### P0:启用 V2 前必须跟进
1. Ultra 全链路:`CODEX_REASONING_LEVELS`、Codex/Codex App 模型字符串解析、CLI `model_reasoning_effort` 参数、前端 Thinking picker 和回归用例全部加入 `ultra`。
2. 删除 V1 字段级 developer instructions:不再注入 `fork_context` 和“wait 直到 final”的规则;仅保留 cc-web 自有 title 规则,并要求遵循当前运行时工具 schema/描述。
### P1:建议同步完成
1. parent 收到 `subAgentActivity(kind=started)` 时登记 child thread,随后能路由 child 的 agentMessage/turn 通知并回填实时卡片;同一路径递归处理 child 发出的 grandchild activity。
2. mock 改成真实 V2 序列:completed-only `subAgentActivity`、空 receiver 的 wait item、child completion/mailbox、嵌套路径;保留 V1 用例做双栈回归。
3. UI 的“关闭”在 V2 中实际只是 interrupt,不能伪装成销毁;应调整文案/状态或明确隐藏与可恢复语义。代理展示应保留 canonical task path,避免只取 basename 后同名冲突。
### 无需跟进
- 不新增 `multiAgentMode` 控件:0.145.0 仍标记 deprecated/ignored,proactive 由 Ultra 驱动。
- 不做 CSV fanout UI:`spawn_agents_on_csv` 已从 0.145.0 移除。
- 不默认强开 `multi_agent_v2`:它虽 stable 但仍 opt-in/default false;应继续尊重用户 Codex 配置。
- `[agents]` 配置由 Codex app-server 直接读取,cc-web 暂无需复制一套配置管理 UI。
### 已兼容
- canonical `item/started` / `item/completed` 分发。
- `collabAgentToolCall` 与 `subAgentActivity` 的 runtime/前端归并和基础卡片渲染。
- app-server `collaborationMode.settings` 的 model/reasoning/developer_instructions 形状及线程级 MCP 注入。
## 实施结果
- Ultra 已贯通本地 Codex 配置、会话模型字符串、Codex CLI `model_reasoning_effort`、Codex App `collaborationMode.settings.reasoning_effort` 与前端 Thinking picker。
- 固定注入的 V1 `fork_context` / 重复 `wait_agent` 指导已删除,改为只要求遵循当前 runtime tool schema 与工具描述。
- 服务端会从父级或任意已登记 child 的 `subAgentActivity` 递归登记子线程,保留直接 `parentThreadId`、`spawnToolId`、canonical `agentPath` 与任务描述。
- 子代理操作与状态改为“中断 / 已中断”,保留后续 turn 恢复可能;前端卡片 tooltip/footer 展示 canonical path,避免同名 basename 混淆。
- mock 同时保留 V1 `collabAgentToolCall(spawn_agent)` 序列,并新增 V2 completed-only activity、child→grandchild、空 receiver wait 与终态回填序列。
- 静态检查、定向 subagent 回归和完整 `npm run regression` 均通过;`multi_agent_v2` 仍保持 opt-in/default false,未由 cc-web 强制开启。