From 22ddc053aaa1239f1bfa0ecc0ee3e6cd69292c16 Mon Sep 17 00:00:00 2001 From: KevinZhao Date: Sun, 21 Jun 2026 13:49:03 +0000 Subject: [PATCH] =?UTF-8?q?docs(rfc):=20codex=20phase-2=20=E4=BA=8C?= =?UTF-8?q?=E6=AC=A1=E5=AE=9E=E6=B5=8B=20=E2=80=94=20passthrough=20?= =?UTF-8?q?=E6=8E=A8=E8=BF=9F=20/=20askuser=20schema=20=E6=8D=95=E8=8E=B7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 并行深挖 passthrough + askuser 后,初判结论翻转(§7/§8): §7 passthrough → 推迟(实测否定): §2 只验了 clientUserMessageId round-trip + turn/steer 的 ACK,没验效果。 补做 3 组效果探针(Bedrock gpt-5.5):长文 pivot / 数数追加 / tool 占用中 steer —— turn/steer 全部 ACK 成功但模型不采纳 steered 输入(silent no-op)。开 passthrough 会让 /urgent/并发消息被 codex 静默丢弃,比现有 Collect 模式更差。决策:不 ship,保持 Collect。教训:协议 ACK≠语义生效。 §8 askuser → 可实现(schema 已 live 捕获): 方法名 item/tool/requestUserInput(需 --enable default_mode_request_user_input, 旧猜名 request_user_input 错误致 server 退出)。请求/响应 schema、阻塞性、 空答案致 re-ask 死循环全部实测确认。难点:codex 真阻塞,要加回 claude askuser RFC 删掉的 pending 表/TTL/RPC 回写(~2-3d,独立 PR)。 推荐顺序更新:embedded_context(已交付#2219) → askuser → ~~passthrough~~推迟。 --- docs/rfc/codex-backend-phase2-feasibility.md | 76 +++++++++++++++++++- 1 file changed, 75 insertions(+), 1 deletion(-) diff --git a/docs/rfc/codex-backend-phase2-feasibility.md b/docs/rfc/codex-backend-phase2-feasibility.md index f9b656d6..f934d596 100644 --- a/docs/rfc/codex-backend-phase2-feasibility.md +++ b/docs/rfc/codex-backend-phase2-feasibility.md @@ -1,8 +1,10 @@ # Codex Backend — Phase 2 Feature 可行性分析 -> **状态**: 可行性分析(含 codex 0.141.0 实测探针,2026-06-21);**embedded_context 已实现**(§6) +> **状态**: 可行性分析(含 codex 0.141.0 实测探针,2026-06-21);**embedded_context 已实现**(§6);**§7/§8 二次实测后结论翻转** > **前置**: `codex-backend.md` v2 + `codex-backend-validation.md`(Phase 1 已交付 PR #2216) > **范围**: 三个 claude 专属 UX Feature 在 codex backend 的落地评估。 +> +> **⚠️ 结论翻转(2026-06-21 二次深度实测)**:初判 passthrough=可行/askuser=阻塞。实测后**对调**——passthrough 的 `turn/steer` 在 Bedrock gpt-5.5 上 ACK 成功但模型不采纳(silent drop,比 Collect 更差)→**推迟**;askuser 的 `requestUserInput` schema 已完整捕获→**可实现**。详见 §7、§8。 --- @@ -126,3 +128,75 @@ Phase 1 后,codex 与 kiro 的 Feature map **逐位相同**(7 项),三 | naozhi 侧代码 | 纯透传 | 纯透传(零文件读取,零新增安全面) | 弱于 claude 的静态保证,但匹配 dashboard 契约且零安全面。若未来要 claude 式强保证,可走方案 B(naozhi 服务端内联),但会引入路径穿越/大小限制/workspace confine 安全面,留作独立 RFC。 + +--- + +## 7. passthrough — **推迟(实测否定)** + +§2 初判"原语齐备、适配器可行"。但 §2 只验证了 `clientUserMessageId` round-trip 与 `turn/steer` 的 **ACK**,没验证 steer 对模型输出的**实际效果**。2026-06-21 补做 3 组效果探针(Bedrock gpt-5.5): + +| 探针 | turn 内容 | steer 内容 | 结果 | +|---|---|---|---| +| 1 | "10 段罗马帝国史"(长输出) | "IGNORE…只输出 PIVOT 并停" | steer ACK 成功;**输出未 pivot**,继续写罗马史,35s 未完成 | +| 2 | "慢数 1-8" | "数完后说 PIVOT" | 单 turn 5.5s 完成;**无 PIVOT**,无第二 turn | +| 3 | "跑 sleep 8 的 bash 再报告"(tool 占用 9s) | turn 刚 started 即 steer "末尾加 PIVOT" | steer ACK 成功 `{turnId}`;turn 9.2s 完成;**无 PIVOT** | + +**结论**:`turn/steer` 在 Bedrock gpt-5.5 上**返回成功但模型不采纳 steered 输入**——是 silent no-op。naozhi 若据此开 passthrough,会出现:用户发 `/urgent` 或并发消息 → naozhi ACK、round-trip slot、显示已发 → 但 codex 实际丢弃。**比现有 Collect 模式更差**(Collect 可靠排队+下一 turn 投递,不丢消息)。 + +**决策:不 ship。** `passthrough` 保持 false,codex 继续走 Collect 模式(可靠)。 + +未决/可能复活的条件:① 非-Bedrock(OpenAI 直连 gpt-5.x-codex)下 steer 是否生效未测——若生效可做 per-provider 门控;② codex 后续版本明确 steer 语义。届时重测再议。 + +> 教训:协议原语的"ACK 成功"≠"语义生效"。验证可行性必须测**端到端效果**,不能止于 RPC 返回码。 + +--- + +## 8. askuser — **可实现(schema 已捕获)** + +§3 初判"BLOCKED:requestUserInput schema 未验证"。2026-06-21 live 捕获补全(codex 0.141,`experimentalFeature/list` + 实跑): + +### 8.1 触发前提 +- 反向请求方法名确认为 **`item/tool/requestUserInput`**(`ServerRequest.json` 注册表 index[2])。 +- **默认关闭**,必须 `--enable default_mode_request_user_input`(`-c features.default_mode_request_user_input=true`)。RFC 旧猜名 `request_user_input` 错误(server 启动即 `Unknown feature flag` 退出)。 +- 另有 stable 且默认开的 `mcpServer/elicitation/request`(gated `tool_call_mcp_elicitation`),form/url schema 不同,非本卡片路径。 + +### 8.2 请求 schema(实测样本) +```json +{ "method":"item/tool/requestUserInput", "id":0, "params":{ + "threadId":"...", "turnId":"...", "itemId":"call_0", + "questions":[{ "id":"deploy_target", "header":"Deploy", + "question":"What do you want to deploy?", "isOther":true, "isSecret":false, + "options":[{"label":"API server (Recommended)","description":"..."}, ...] }], + "autoResolutionMs":null }} +``` +- question 必填 `id`/`header`/`question`;可选 `isOther`(允许自由文本)/`isSecret`(掩码)/`options`(null⇒自由文本题)。 +- option 只有 `label`+`description`,**label 即回传值**(无独立 value)。`questions` 是数组(一卡多问)。 + +### 8.3 响应 schema(实测解锁 turn) +```json +{ "id":0, "result":{ "answers":{ "":{ "answers":["<选中的 label>"] } } } } +``` +- `result.answers` 是**按 question id 键控的对象**(非数组);值 `{answers:[label,...]}`(数组支持多选)。 +- **校验宽松不报错**:错误/空 `result` 不会 JSON-RPC error,而是被当"用户未答"→模型**重发同一 requestUserInput**(观测到 id=1 重试)。故必须回**正确的 answers 键控结构**,否则死循环。 + +### 8.4 阻塞性(实测确认) +- `turn/start` 立即返回 result,但 **turn 不 completed** 直到反向请求被回答。挂起 12s 无 `turn/completed`;回答后才出 `item/agentMessage/delta` + `turn/completed`,并伴随 `serverRequest/resolved {requestId}`。 +- requestUserInput **不**带自己的 item/started/completed,只作为 reverse RPC(keyed by itemId=call_0)出现。 + +### 8.5 与 claude askuser 的根本差异(实现难点) +claude 在 `-p` 下 **fire-and-forget**(~3ms 自注 `is_error:true` tool_result,turn 正常结束,答案作**下一轮 user 消息**回流)。`askuser-question.md` §"推翻的原设计假设"**明确删掉了** pendingQuestion 表/TTL/阻塞等待。 + +codex **真阻塞**——这些机制要**全部加回来**: +- ReadEvent 解析 `item/tool/requestUserInput` → `cli.AskQuestion`(itemId=ToolUseID,questions→Items,options.label→Opt)。 +- pending 表持有 `requestId`+`itemId`+TTL(autoResolutionMs 可驱动)。 +- 用户答案**不是发新 turn**,而是回 `{result:{answers:{qid:{answers:[label]}}}}` 到那条阻塞 RPC(HandleEvent 或新写回路径)。 +- 用户不答 → 必须 TTL 自动回一个 decline-shaped answer 防 turn 永挂 + 模型重发死循环。 + +### 8.6 落地估时 / 风险 +- ~2-3 天。中风险:`default_mode_request_user_input` 标 `underDevelopment`(schema 可能变);阻塞语义要求 naozhi dispatch 持 turn 开(与 Collect/passthrough 的 turn 生命周期假设不同)。 +- 建议:独立 PR;先在 BuildArgs 加 flag + ReadEvent 解析 + pending 表 + 回写路径,TTL 兜底必须有。 + +### 8.7 推荐顺序更新(取代 §4) +1. ✅ embedded_context(已交付 PR #2219) +2. **askuser**(可实现,schema 齐备,~2-3d,独立 PR) +3. ~~passthrough~~ → **推迟**(§7,Bedrock steer 实测无效)