فهرست منبع

docs(E2E): 记录真实链路缺陷与分段提速方案

汇总 900016 至 900022 的推进位置、根因、修复证据和当前可信边界,并明确完整 E2E 尚未通过。

记录 Phase1/2 断点恢复缺口,给出无模型重放、受控续跑、分段验证和最终全链路门禁顺序。
SamLee 1 روز پیش
والد
کامیت
e7b0bb0bf2
1فایلهای تغییر یافته به همراه419 افزوده شده و 0 حذف شده
  1. 419 0
      script_build_host/REAL_E2E_DEBUG_REPORT_2026-07-20.md

+ 419 - 0
script_build_host/REAL_E2E_DEBUG_REPORT_2026-07-20.md

@@ -0,0 +1,419 @@
+# 真实脚本构建 E2E 调试报告(2026-07-20)
+
+## 1. 结论
+
+本轮已经补齐并实际验证了两个硬门槛:
+
+1. Host 可以通过内部测试专用 Runtime Factory 使用真实 Qwen、真实 AgentRunner、
+   持久化 TraceStore 和明确的内部 Principal/Auth 运行;非 `test` 环境会 fail-closed。
+2. Script Build 的 16 个 Planner、Worker、Validator preset 都有完整、可冻结的模型配置。
+
+第三个硬门槛——“真实旧版输入 → Qwen → Phase 1/2/3 → MySQL 原子发布 →
+旧 detail API 回读”——**尚未完整通过**。目前真实链路已经稳定推进到:
+
+`真实旧版输入 → Phase 1 → Direction ACCEPT/发布 → Phase 2 → Structure Worker →
+Structure Validator passed → Structure ACCEPT → Paragraph 规划`
+
+Paragraph 之后的 Compose、CandidatePortfolio、Phase 3 Root、Final UoW 和旧 detail
+回读还没有在同一条真实运行中全部通过。因此当前不能宣称“真实 E2E 已完成”。
+
+用户明确要求暂不做数据库账号/权限隔离,本轮没有扩大该范围。
+
+## 2. 本轮测试输入和停止状态
+
+- 真实输入来源:上一代 MySQL 数据,而不是 fake fixture。
+- 脱敏输入身份:`execution_id=50`、`topic_build_id=230`、`topic_id=400`、
+  `account_name=创业邦`。
+- 模型:通过百炼 OpenAI-compatible endpoint 调用配置的 Qwen 模型。
+- 模型 thinking:关闭;请求具有 300 秒超时和 2 次 SDK 重试。
+- 本地持久化:`.local/agent-data` 下的 Trace、Task Ledger、Artifact、owner lock 和
+  E2E report。
+- MySQL:经本地 `127.0.0.1:13306` 的受管隧道连接真实输入/测试输出库。
+
+收到停止指令后已执行:
+
+- 终止正在运行的 900022 E2E 进程;
+- 调用 900022 的正式 stop API;
+- stop API 返回 `stopping`,`stopped_operation_ids=[]`;
+- 900022 的 stop/fence 意图已经持久化,不允许继续派发新工作;
+- E2E CLI 写出失败报告
+  `.local/agent-data/e2e-reports/20260720T081451Z-8233aee0.json`。
+
+这里的 `stopping` 而非 `stopped` 是一个需要继续修的恢复问题:原 Planner 所在进程
+先被终止,另一个临时 Host 可以写 stop/fence,但不能等待已经消失的进程内 Planner
+Task 正常收尾。它不代表模型仍在后台运行;当前没有 `internal_e2e` 进程。
+
+## 3. 三个硬门槛的当前状态
+
+| 硬门槛 | 当前状态 | 已有证据 | 剩余工作 |
+|---|---|---|---|
+| 真实 Runtime Factory + Auth | 已实现,针对性测试通过 | 真实 Qwen tool-call smoke 成功;内部 Runtime/Auth 测试通过;非 test 环境 fail-closed | 最终全量门禁 |
+| 16 个角色模型配置 | 已实现,针对性测试通过 | manifest 覆盖 16 个 preset,Validator/Worker 温度和迭代上限可冻结 | 最终全量门禁 |
+| 可执行真实全链路 E2E | 部分通过 | 真实输入、Phase 1、Direction、Phase 2 Structure 已跑通 | Paragraph→Compose→Portfolio→Phase 3→MySQL final→legacy detail |
+
+16 个 preset 为:
+
+1. `script_planner`
+2. `script_direction_worker`
+3. `script_pattern_retrieval_worker`
+4. `script_decode_retrieval_worker`
+5. `script_external_retrieval_worker`
+6. `script_knowledge_retrieval_worker`
+7. `script_retrieval_validator`
+8. `script_candidate_validator`
+9. `script_structure_worker`
+10. `script_paragraph_worker`
+11. `script_element_set_worker`
+12. `script_candidate_compare_worker`
+13. `script_compose_worker`
+14. `script_candidate_portfolio_worker`
+15. `script_root_worker`
+16. `script_root_validator`
+
+## 4. 真实运行中发现的问题、修复和验证
+
+### 4.1 Runtime 和模型接入问题
+
+#### 问题:Host 缺少可直接执行的真实内部 Runtime Factory
+
+此前生产装配要求外部注入 Runner、PrincipalProvider 和 BuildAuthorizer,但项目内没有
+一条可以直接运行真实 Qwen E2E 的内部装配路径。
+
+修复:
+
+- 新增内部测试专用 Runtime Factory;
+- 注入真实 `AgentRunner`、Qwen LLM call 和 `FileSystemTraceStore`;
+- 注入显式内部 Principal 和 Authorizer;
+- 仅允许 `environment=test`,其他环境直接拒绝启动;
+- API Key 只从 Secret settings 读取,不写入报告、Trace 或源码。
+
+验证:内部 Runtime/Auth 针对性测试通过,真实 Qwen tool-call smoke 约 2.01 秒成功。
+
+#### 问题:Qwen thinking、超时和重试不可配置
+
+默认 thinking 会显著增加工具型调用的延迟;SDK 没有项目级请求超时和瞬态失败重试。
+900016 的真实运行遇到一次 provider `InternalServerError`,因为当时 `max_retries=0`
+直接中断。
+
+修复:
+
+- `create_qwen_llm_call` 新增 `enable_thinking`、`request_timeout_seconds`、
+  `max_retries`;
+- 默认内部 E2E 关闭 thinking;
+- SDK 配置 300 秒请求超时和 2 次重试;
+- 单次调用仍可以显式覆盖 `extra_body`,不会丢失调用方参数。
+
+验证:Qwen Runtime options 单测和真实 tool-call smoke 通过。
+
+#### 问题:工具 schema 无法正确解析 postponed annotation / PEP 604 Union
+
+真实 Runner 注册工具时,部分注解因 `from __future__ import annotations` 仍是字符串,
+以及 `A | B` Union 没有完整展开,导致模型看到的工具 schema 不准确。
+
+修复:使用 `get_type_hints` 解析实际类型,并补齐 PEP 604 Union schema。
+
+验证:Agent 工具 schema 新增针对性测试并通过。
+
+### 4.2 E2E 驱动器问题
+
+#### 问题:轮询会无限容忍服务端 500
+
+早期 E2E poll 把 mission endpoint 的 404/500 都当成“刚启动,继续等”,持久错误会表现
+成假卡死。
+
+修复:只允许启动阶段前 5 次 404/500,之后立即暴露原始 HTTP 错误。
+
+验证:
+
+- 前 5 次失败、第 6 次成功的测试通过;
+- 持续 500 在第 6 次直接失败的测试通过。
+
+#### 问题:CLI 只支持 start 新 build
+
+当前 `internal_e2e` 每次都创建新 build,所以每发现一个后段问题,都要重新付出 Phase 1
+和 Phase 2 前段的模型时间。
+
+当前只完成了问题确认,尚未修复。详见第 7 节提速方案。
+
+### 4.3 Phase 1 / Retrieval 状态语义问题
+
+#### 问题:把 `BLOCKED` 子任务误认为终态
+
+900017 中 Retrieval 子任务被 Planner `BLOCK` 后,父 Direction 一直处于
+`waiting_children`。Framework 中 `BLOCKED` 是可恢复状态,不会关闭父任务。
+
+修复:
+
+- Planner policy 明确:不可用 Retrieval 必须 `CANCEL`,不能用 `BLOCK` 丢弃;
+- `BLOCK` 只用于 Host 定义的 Root phase boundary;
+- dispatch 在父任务仍有 blocked child 时返回稳定、可操作的 `TASK_CHILD_BLOCKED`,提示
+  CANCEL 或 UNBLOCK。
+
+验证:blocked-child 不关闭父任务的针对性测试通过。
+
+### 4.4 Validator 读不到当前候选的问题
+
+#### 问题:Candidate Validator 只能看到历史 evidence,看不到本 Attempt 的冻结产物
+
+900018 中 Direction/候选 Validator 无法读取正在验证的 artifact,只能反复查询不相关的
+evidence,最终既浪费 token 又不能形成可靠 verdict。
+
+修复:
+
+- `query_validation_evidence` 返回 validation snapshot 中的 `current_artifacts`;
+- `current_artifacts` 是当前 Attempt 的精确冻结业务产物;
+- 历史 Retrieval 仍通过独立的 `current_evidence` 返回;
+- Validator prompt 要求优先读取 `current_artifacts`,只有存在 raw artifact ref 才读取原图。
+
+验证:候选 Validator 工具面和 frozen artifact 读取测试通过;后续真实 Structure Validator
+已经用该路径给出 passed。
+
+### 4.5 Budget 统计问题
+
+#### 问题:真实 Qwen 上下文超过旧的测试型 token 上限
+
+Runner 会累计多轮 input/output token。原 Task 32k、Phase 2 2m 上限对真实工具型运行过低,
+正常验证也会被误判为 budget exhausted。
+
+修复:
+
+- Task 上限提高到 1,000,000;
+- Phase 2 总上限提高到 10,000,000;
+- Planner 合同要求 `max_tokens=1,000,000`;
+- 工具 schema 同步合法范围。
+
+这不是要求模型必须消耗这些 token,而是防止 Host 用不现实的测试阈值误杀正常运行。
+
+#### 问题:执行时间把 Planner 等待时间算进 Worker/Validator budget
+
+旧统计按最早 started 到最晚 completed 的墙钟跨度计算,多个阶段之间 Planner 思考和等待
+也被计费,导致尚未运行很久的 Task 被判超时。
+
+修复:只累加每个 Attempt/Validation 自己的 `completed_at - started_at` 区间。
+
+验证:跨两小时但实际阶段合计 20 秒的回归测试通过。
+
+### 4.6 Phase 2 容器生命周期问题
+
+#### 问题:模型提前 dispatch 非执行态容器
+
+900019 中 Planner 创建空 CandidatePortfolio/Compose 后立即 dispatch;这些容器在 adoption
+closure 为空时本来不可执行。随后模型试图 CANCEL 唯一 Portfolio,破坏 Phase 2 闭合路径。
+
+修复:
+
+- Planner policy 增加严格的 7 步生命周期;
+- 先建 Portfolio 和 Compose 容器,再完成本地候选;
+- Compose 通过 REVISE 写入精确 accepted decision closure 后才能 dispatch;
+- Portfolio 在 Compose ACCEPT 后用相同方式闭合;
+- Host 禁止 CANCEL 唯一 CandidatePortfolio;
+- 容器不可执行错误改成可操作提示。
+
+验证:唯一 Portfolio 不可取消测试通过;900020/900021 已不再出现提前 dispatch/cancel。
+
+### 4.7 business scope 与 write scope 混用
+
+#### 问题:模型把业务输入闭包和候选写空间当成同一个 namespace
+
+900020 中 Portfolio scope 没有与 accepted Direction 对齐,Compose/本地候选 scope 也没有
+正确嵌套;同时模型把业务 scope 当成 workspace 写路径,导致输入合同和 SQL workspace
+分别失败。
+
+修复:
+
+- Portfolio `scope_ref` 必须等于 accepted Direction scope;
+- Compose 和本地创作 scope 必须逐级嵌套;
+- 内部整稿运行统一使用 `write_scope=["script-build://writes"]`;
+- prompt 和工具 schema 区分不可变输入兼容性与候选 SQL 写入 namespace。
+
+验证:900021 已成功创建 scope 正确的 Portfolio、Compose 和 Structure。
+
+### 4.8 Structure Worker 工具使用不收敛
+
+#### 问题:Structure Worker 不知道最短的 workspace 写入顺序
+
+900020 中 Worker 在多个工具之间循环,消息数和 token 快速增长。
+
+修复:Structure prompt 写明精确步骤:读取冻结输入 → 创建各 Paragraph shell → 检查
+workspace → 保存唯一 Structure artifact → submit Attempt。Paragraph、Compose 和 Portfolio
+也分别补了最短工具路径。
+
+验证:900021 的 Structure Worker 收敛到约 22 条消息、约 33k token,并通过独立
+Validator 和 Planner ACCEPT。
+
+### 4.9 accepted Structure 与 Paragraph scope 不兼容
+
+#### 问题:错误合同直到 Worker 启动才失败
+
+900021 中 Structure scope 为:
+
+`.../compose/candidate-1/structure/main`
+
+5 个 Paragraph 被规划为它的兄弟:
+
+`.../compose/candidate-1/paragraph/p1` 等。
+
+Paragraph 又把 accepted Structure 作为 immutable input,因此 producer/consumer scope
+既不相等也不互相包含,必然触发 `INPUT_SCOPE_MISMATCH`。两个 Worker 已失败,另外三个
+也开始重复同一错误,所以该轮被停止。
+
+修复:
+
+- Paragraph scope 必须等于或嵌套在 accepted Structure scope,例如
+  `{structure_scope}/paragraph/p1`;
+- 在 PLAN、REVISE、SPLIT、DISPATCH 四个边界增加 accepted-input scope guard;
+- guard 同时检查 ACCEPT action、producer Task、expected task kind 和声明 scope;
+- 无效合同现在会在启动 Worker 前失败,并给 Planner 可纠正的稳定错误。
+
+验证:新增回归测试证明兄弟 Paragraph scope 在 Worker dispatch 前被拒绝;相关 32 个
+Phase 2/Internal E2E 针对性测试全部通过。该修复尚未进行新的真实 Paragraph 运行,因为
+用户要求先停止并出报告。
+
+### 4.10 900022 的状态
+
+900022 使用同一真实输入重新启动,在收到停止指令时仍处于 Phase 1 Retrieval 阶段:Root
+从 `needs_replan` 进入 `waiting_children`,3 个 Retrieval 子任务已开始。没有发现新的业务
+缺陷;该轮仅因调整测试策略而人工停止。
+
+## 5. 运行批次记录
+
+| Build | 推进位置 | 停止原因 | 对应动作 |
+|---|---|---|---|
+| 900001–900015 | Runtime/E2E bring-up 的早期批次 | 均为人工取消;当前 JSON 报告只有 `CancelledError`,不足以可靠逐条还原 | 不对缺少证据的批次编造错误归因;其问题已按代码修复类别归入第 4 节 |
+| 900016 | Phase 1 | 百炼/OpenAI-compatible provider 瞬态 5xx,SDK 无重试 | 增加 timeout/retries |
+| 900017 | Phase 1 Direction 等待 children | Retrieval 被 BLOCK 后父任务永远不闭合 | BLOCK/CANCEL 语义和 dispatch guard |
+| 900018 | Direction/候选 validation | Validator 看不到当前 frozen artifact;budget 偏小 | `current_artifacts` + budget 修正 |
+| 900019 | Phase 2 容器创建 | 提前 dispatch 空容器,随后 CANCEL 唯一 Portfolio | 7 步生命周期 + Host guard |
+| 900020 | Phase 2 Structure | business scope/write scope 混用;Structure Worker 循环 | scope policy + 精确 Worker 工具顺序 |
+| 900021 | Phase 2 Paragraph | Paragraph 与 accepted Structure 为兄弟 scope | 四边界 accepted-input scope guard |
+| 900022 | Phase 1 Retrieval | 用户要求停止,切换为局部调试策略 | 已终止进程并写 stop/fence |
+
+所有 E2E JSON 都位于 `.local/agent-data/e2e-reports/`。这些文件被设计为本机运行证据,
+不应提交包含环境细节或大体积 Trace 的 `.local` 目录。
+
+## 6. 已执行测试和当前可信边界
+
+本轮已执行并通过:
+
+- Agent Qwen Runtime options + schema annotation:3 passed;
+- Host internal runtime/E2E/phase contracts/normalization:此前批次 28 passed;
+- Host phase agent tools/contracts/internal:此前批次 30 passed;
+- 最新 Phase 2 contracts、agent tools、internal runtime、internal E2E:32 passed;
+- 最新 scope/blocked-child/unique-portfolio 定向回归:3 passed;
+- Ruff 对最新 planning/test 修改:通过;
+- MySQL final publication 定向测试:4 passed、1 skip。该 skip 是用户明确暂缓的数据库
+  账号隔离,不等于完整 MySQL release gate 通过。
+
+真实运行已经证明:
+
+- 能读取完整旧版真实输入;
+- 能用真实 Qwen 进行工具调用;
+- Phase 1 能形成并接受 Direction;
+- Direction publication 和 Phase 2 continuation 可用;
+- 能创建 Portfolio/Compose/Structure 的细粒度任务树;
+- Structure 能写候选 workspace、冻结 artifact、由独立 Validator 验证并 ACCEPT。
+
+尚未证明:
+
+- 修复后的真实 Paragraph Worker/Validator/ACCEPT;
+- Compose 对 Structure/Paragraph 的精确 closure 和结构化脚本生成;
+- CandidatePortfolio ACCEPT 和 Phase 2 checkpoint;
+- Phase 3 continuation、Root Worker、Root Validator、Root ACCEPT;
+- 单事务 MySQL final publication 在这条真实运行上的成功;
+- publication/artifact pointer、branch0 canonical digest 和 legacy detail 的最终一致;
+- 一条从 start 到旧 detail readback 的完整成功报告;
+- Agent/Host 全量测试、严格 mypy、compileall、完整 MySQL release gate 的最终一次统一通过。
+
+## 7. 为什么慢,以及后续如何提速
+
+### 7.1 当前慢点
+
+不是 token 成本,而是调试循环粒度不合理:
+
+1. E2E CLI 每次固定 start 新 build;
+2. 每次必须重跑已经验证过的 Phase 1、Retrieval、Direction 和 Structure;
+3. 后段第一个真实错误出现后,这一轮前面的时间不能直接复用;
+4. 当前 `/resume` 重点覆盖 Phase 3,不能重新启动已有 Phase 2 Planner;
+5. `run_phase_two` 一旦检测到已存在 Phase 2 模型输出,会 fail-closed 为
+   `MissionRecoveryRequired`,这是安全设计,但缺少后续受控 replan 入口;
+6. CLI 被 Ctrl-C 后会关闭受管数据库隧道,所以另一个诊断进程需要重新建立隧道。
+
+### 7.2 已有恢复能力
+
+现有代码不是完全不能恢复:
+
+- Ledger、Attempt、Validation、Operation 和 Trace 都是 durable;
+- Framework 的 `recover_operations` 能把崩溃中状态归一化;
+- 未真正开始的 pending stage 可以安全 resume;
+- Worker 已提交而 Validator 未开始时可以只续 Validator;
+- 已开始但未提交的 Worker/Validator 会 STOP/NEEDS_REPLAN,不会伪装成功;
+- Phase 3 `/resume` 可以处理 already-complete、finalize-only、Root blocked continuation;
+- finalize 使用唯一不可变身份,可以重试同一发布而不生成第二份正文。
+
+### 7.3 当前恢复缺口
+
+- E2E CLI 没有 `--script-build-id`;
+- Phase 1/2 没有“接管 owner → recover operations → 恢复 Planner trace → 从当前 ledger
+  replan”的完整入口;
+- stop 后的 build 按 fencing 合同不可解封,这是正确的,因此不能拿已 stop 的 900021
+  强行续跑;
+- 新进程对原进程内 Planner Task 的停止收敛仍会停在 `stopping`;
+- 当前失败报告只记录顶层异常;人工 Ctrl-C 都显示 `CancelledError`,缺少 last checkpoint、
+  root/task/operation 摘要和最后稳定 DomainError。
+
+## 8. 后续测试方案
+
+后续不再采用“每修一个小问题就从真实输入 start 一遍”的方式。
+
+### 阶段 A:无模型快速重放
+
+1. 把 900021 暴露的合同形态做成最小 Ledger/contract fixture;
+2. 对 PLAN、REVISE、SPLIT、DISPATCH 逐入口重放;
+3. 用真实 Coordinator + FileStore + fake deterministic executor 构造
+   `Direction ACCEPT → Structure ACCEPT` checkpoint;
+4. 直接测试 Paragraph workspace 写入、Validator evidence、Compose closure、Portfolio closure;
+5. 对 Phase 3/finalize 使用不可变 accepted artifact fixture 做 MySQL rollback/readback 测试。
+
+这一层应在秒级到分钟级完成,不调用真实模型。
+
+### 阶段 B:补受控断点恢复
+
+新增内部测试能力,但不放松生产 fencing:
+
+1. E2E CLI 增加 `--script-build-id` 和 `--resume`;
+2. 新 Host 先获取同一 build 的 OwnerLease/epoch;
+3. 调用 Framework `recover_operations`;
+4. pending/未开始阶段只 resume 原 Operation;
+5. 已开始未提交阶段归一化为 stopped/needs_replan,再让同一 Planner trace 创建新 Attempt;
+6. Worker 已提交时只续 Validator;
+7. 复用原 InputSnapshot、accepted Decision、Artifact 和消息父链,不重复 Phase 1;
+8. stopped/stopping、未知 child binding、半发布状态继续 fail-closed,绝不直接改数据库状态;
+9. E2E report 增加 checkpoint、root 状态、active operation、last DomainError 和 resume 分类。
+
+### 阶段 C:从中段开始的真实模型测试
+
+1. 先从 `Structure ACCEPT` 测 Paragraph→Compose→Portfolio;
+2. 再从 `Portfolio ACCEPT` 测 Phase 3 Root;
+3. 再从 `Root ACCEPT` 单独测 finalize→publication→legacy detail;
+4. 每段成功后固定为回归测试,失败只重跑该段。
+
+### 阶段 D:最终只做一次全链路门禁
+
+局部段全部通过后,再新建一个干净 build,执行:
+
+`真实旧版输入 → 真实 Qwen → Phase 1 → Phase 2 → Phase 3 → Root ACCEPT →
+MySQL Final UoW → publication/artifacts published → accepted pointer → branch0 digest →
+build success → legacy detail readback`
+
+随后统一运行 Agent 全量、Host 全量、MySQL release gate、Ruff、format check、strict
+mypy、compileall 和 `git diff --check`。只有这些全部通过,第三个硬门槛才标记为完成。
+
+## 9. 建议执行顺序和时间预估
+
+1. 先补断点恢复/分段 harness 和详细失败报告:约 1–2 小时;
+2. 无模型覆盖 Paragraph→Portfolio 的状态和合同:约 30–60 分钟;
+3. 真实模型按后半段分段运行并修复:取决于新缺陷数量,预计 1–3 小时;
+4. 最终完整 E2E 和全量门禁:约 1–2 小时。
+
+这是调试预估,不是完成承诺。后半段尚未首次真实跑通,任何新的框架级缺陷都会增加时间;
+但采用分段和恢复后,不再为同一个后段错误重复支付整个前半程时间。