docs: revise M4 plan per independent review (4I/6M)

This commit is contained in:
2026-07-22 05:46:14 -04:00
parent 84e23dc5c3
commit 547141cf0a
5 changed files with 42 additions and 12 deletions
+12
View File
@@ -80,6 +80,11 @@
"id": "design:m4-migration",
"label": "M4 迁移验证设计(GovDoc→CHS,发 v1.0)",
"type": "design"
},
{
"id": "plan:m4-migration",
"label": "M4 迁移实现计划(T0-T14)",
"type": "plan"
}
],
"links": [
@@ -138,6 +143,13 @@
"relation": "implements",
"evidence": "T1-T10 逐任务落地设计 §3-§12",
"added": "2026-07-22T02:09:43.346922+00:00"
},
{
"source": "plan:m4-migration",
"target": "design:m4-migration",
"relation": "implements",
"evidence": "T0-T14 逐节实现设计 §4-§10/§15",
"added": "2026-07-22T09:33:05.357964+00:00"
}
]
}
+4 -2
View File
@@ -1,6 +1,6 @@
# Research Wiki 索引
> 自动生成,更新时间:2026-07-22 08:58 UTC
> 自动生成,更新时间:2026-07-22 09:33 UTC
## design (10)
- [2026-07-20-m1-core-design](designs/2026-07-20-m1-core-design.md) `design:2026-07-20-m1-core-design`
@@ -25,15 +25,17 @@
- [P6 混合浸泡首跑基线与记分板三重伪击穿修复](findings/p6-soak-baseline.md) `finding:p6-soak-baseline`
- [P7 OCR soak 验收: 99.73% 与 13 不变量全 PASS](findings/p7-ocr-soak.md) `finding:p7-ocr-soak`
## plan (8)
## plan (10)
- [2026-07-20-m1-core-plan](plans/2026-07-20-m1-core-plan.md) `plan:2026-07-20-m1-core-plan`
- [2026-07-20-m2-distributed-plan](plans/2026-07-20-m2-distributed-plan.md) `plan:2026-07-20-m2-distributed-plan`
- [2026-07-21-m25-resilience-plan](plans/2026-07-21-m25-resilience-plan.md) `plan:2026-07-21-m25-resilience-plan`
- [2026-07-21-m3-ocr-plan](plans/2026-07-21-m3-ocr-plan.md) `plan:2026-07-21-m3-ocr-plan`
- [2026-07-22-m4-migration-plan](plans/2026-07-22-m4-migration-plan.md) `plan:2026-07-22-m4-migration-plan`
- [M1 核心里程碑实现计划](plans/m1-core-plan.md) `plan:m1-core-plan`
- [M2 分布式实现计划](plans/m2-distributed.md) `plan:m2-distributed`
- [M2.5 治理韧性实现计划](plans/m25-resilience.md) `plan:m25-resilience`
- [M3 OCR 实现计划](plans/m3-ocr.md) `plan:m3-ocr`
- [M4 迁移实现计划(T0-T14)](plans/m4-migration.md) `plan:m4-migration`
## schema (1)
- [表结构: llm_calls(遥测 18 字段)](schemas/llm-calls.md) `schema:llm-calls`
+3
View File
@@ -41,3 +41,6 @@
- [2026-07-22 05:33 UTC] 重建索引: 28 篇页面
- [2026-07-22 08:58 UTC] 新增 design: M4 迁移验证设计(GovDoc→CHS,发 v1.0) (design:m4-migration)
- [2026-07-22 08:58 UTC] 重建索引: 30 篇页面
- [2026-07-22 09:33 UTC] 新增 plan: M4 迁移实现计划(T0-T14) (plan:m4-migration)
- [2026-07-22 09:33 UTC] 新增边: plan:m4-migration --implements--> design:m4-migration
- [2026-07-22 09:33 UTC] 重建索引: 32 篇页面
@@ -32,7 +32,7 @@
### T0 基础设施与基线(GovDoc + CHS 一次做完)
- [ ] worktree: `git -C reference/GovDoc-SaaS worktree add /Users/yuchengzhang/Projects/m4-worktrees/GovDoc-SaaS -b feat/polygateway-migration`;CHS 同式。验证: 两 worktree 内 `git status` 干净、分支正确;reference/ 本体仍在 main。
- [ ] worktree: `git -C reference/GovDoc-SaaS worktree add /Users/yuchengzhang/Projects/m4-worktrees/GovDoc-SaaS -b feat/polygateway-migration`;CHS 同式。验证: 两 worktree 内 `git status` 干净、分支正确;reference/ 本体仍在 main。**前置**: `~/Projects/m4-worktrees/` 写权限已随人类门批准(设计 §4-B);首次写入若权限系统仍询问,按该会话授权放行。
- [ ] conda 环境: `conda create -n GovDoc-SaaS python=3.11 -y` → WT/G 内 `make install`;`conda create -n chs python=3.13 -y` → WT/C 内 `conda run -n chs pip install -r requirements.txt -r requirements-dev.txt`。任一失败即停,与用户对齐(设计 §14 风险)。
- [ ] editable 装库: 两环境各 `pip install -e "/Users/yuchengzhang/Projects/PolyGateway[redis,structured]"`(chs 加 `postgres`)。验证: `conda run -n <env> python -c "import polygateway; print(polygateway.__version__)"`
- [ ] 基线留档: WT/G `make ci`、WT/C `make test`(worktree 内容 = main,即旧版基线)。控制台全文落 `tests/outputs/m4/{govdoc,chs}_baseline.log`,并整理**pass/skip 剖面 + 预期测试增删清单**(G 侧将删 test_breaker.py 及 test_imports 两行;C 侧将删的治理自测文件名逐一列出)至 `tests/outputs/m4/baseline_profile.md`。CHS 侧 requires_db/requires_redis 的 skip 属基线组成部分。
@@ -40,21 +40,21 @@
### T1 GovDoc 冒烟 spike(迁移文档 §6-3)
- [ ] 从 PGW `.env` 用 python(dotenv)读实验室网关真实凭据,生成 WT/G `.env`(单源 `LLM__{PROVIDER}__1__*` + `PGW_TELEMETRY_BACKEND=sqlite` + 缓存 namespace=`govdoc-saas` + Redis db3);不提交 `.env`
- [ ] spike 脚本放 PGW scratchpad(不进 GovDoc 仓库): `from_env()` 装配 → `assert isinstance(client, docagent_core.protocols.LLMProvider)``AgentLoop(client, retryable_exceptions=(TransientError, AllSourcesExhausted))` 真实跑一次最小对话 → 打印 LLMResponse 全字段 → 查询遥测 db 该 call_id 行存在
- [ ] 从 PGW `.env` 用 python(dotenv)读实验室网关真实凭据,生成 WT/G `.env`;必填键全集(config.py `_require` 实测): 单源 `LLM__{PROVIDER}__1__*``PGW_TELEMETRY_BACKEND=sqlite` + `PGW_TELEMETRY_SQLITE_PATH``PGW_CACHE_BACKEND=redis` + `PGW_CACHE_NAMESPACE=govdoc-saas` + `PGW_CACHE_TTL_S``PGW_LIMITER_BACKEND`/`PGW_BREAKER_BACKEND``REDIS_URL`(db3)不提交 `.env`
- [ ] spike 脚本放 PGW scratchpad(不进 GovDoc 仓库),两段: ① 真实冒烟——`from_env()` 装配 → `assert isinstance(client, docagent_core.protocols.LLMProvider)``await client.chat(...)` 真实调用一次 → 打印 LLMResponse 全字段 → 查询遥测 db 该 call_id 行存在;② AgentLoop 接线验证(离线)——实测签名 `AgentLoop(llm, max_steps=N, retryable_exceptions=...)`(max_steps 必填)且 `run()` 需要 ToolDispatcher 与 Thinking+JSON 格式,故用 fake LLMProvider + 桩 dispatcher 构造可终止对话,验证 retryable_exceptions=(TransientError, AllSourcesExhausted) 注入后步级重试对库异常可触发
- [ ] 验证: spike 输出 + 遥测行;失败即首个缺口(按 §0 缺口口径处理)。
- [ ] 提交点: WT/G 仅 `.env.example` 若有同步则提交,否则无提交。
### T2 GovDoc 配置迁移(§6-4)
- [ ] `.env.example` 按 govdoc-saas.md §5 映射改键(平铺 → `LLM__{PROVIDER}__1__*`;新增 namespace/`PGW_*` 键;`REDIS_CACHE_TTL` 语义改 TTL>0 必填,注释说明)。`.env` 同步(python 内改写)。
- [ ] `.env.example` 按 govdoc-saas.md §5 映射改键(平铺 → `LLM__{PROVIDER}__1__*`;新增 T1 列出的 `PGW_*` 必填键;旧键 `REDIS_CACHE_TTL` **改名**为库键 `PGW_CACHE_TTL_S` TTL>0 必填,注释说明)。`.env` 同步(python 内改写)。
- [ ] 先失败后通过: 删一个关键键跑 `from_env()` 须报缺配置错(防御验证),补回后通过。
- [ ] 验证: spike 重跑通过。提交点: `git commit`(.env.example + 相关注释)。
### T3 GovDoc 装配进入口(§6-5)
- [ ] 实测 `src/govdoc/` 现有 api 入口(骨架期可能仅 `api/deps.py`);把装配段(约 5 行,设计文档 §7-G4 形态: from_env + AgentLoop retryable_exceptions 显式传参 + lifespan/shutdown 处 `await client.aclose()`)接入 api lifespan;若 arq worker 入口尚不存在则仅 api 侧,并在 commit message 说明。
- [ ] 先失败后通过: 注错测试——用假 transport 抛 TransientError,断言 AgentLoop 步级重试确实触发(防"静默失效",迁移文档 B15)。
- [ ] 先失败后通过: 注错测试——用 fake LLMProvider(chat 首调抛 TransientError,次调成功)+ 桩 dispatcher(同 T1-② 的 harness),断言 AgentLoop 步级重试确实触发(防"静默失效",迁移文档 B15);不传 retryable_exceptions 时同一 fake 必须失败(红),显式传参后通过(绿)。
- [ ] 验证: `make ci` 绿。提交点: commit。
### T4 GovDoc embedding 迁移(设计 §7-G5)
@@ -78,6 +78,8 @@
### T7 CHS shim 与异常分支(设计 §8-C2;二选一已钉死)
- [ ] **WT/C `.env` 生成**(与 T1 对称,CHS 段全部真实调用的前置): python(dotenv)从 PGW `.env` 读实验室网关凭据配 `VLM__{PROVIDER}__1__*`(经实验室网关调 qwen-vl 系模型;若用户要求生产同款 dashscope 直连源,凭据向用户索取,严禁编造);`OCR__MONKEY__{1,2}__*`(10.77.0.20:7866/7867,api_key=none,TRUST_ENV=false);`JUDGE__{PROVIDER}__1__*`(实验室中转 + claude 模型名);`PGW_LIMITER_BACKEND=redis`/`PGW_BREAKER_BACKEND=redis`/`PGW_TELEMETRY_BACKEND=postgres`(PG polygateway 库 DSN,从 PGW `.env` 读)/`PGW_CACHE_BACKEND=none`(灰度期关缓存)、`REDIS_URL`(db3)。不提交。
- [ ] **依赖声明**(设计 §5 开发期承诺): WT/C `requirements.txt``polygateway[redis,postgres,structured]>=0.1` 行(CHS 无 `[project]` 依赖段,载体是 requirements.txt;editable 安装在 T0 已就位,此行是声明)。
- [ ] 新增 `app/providers/pgw_shims.py`: PgwVlmProvider 骨架(字段映射按 chsanalyzer.md §3,以 WT/C `app/ports.py` 实测签名为准):
```python
@@ -103,14 +105,15 @@ class PgwVlmProvider:
```
- [ ] 异常分支**不做翻译 shim**,`app/workers/tracking.py` 直接 `except GatewayUnavailableError`(理由: C10 清场本就删除 Provider 错误族,翻译层是死代码;库异常自带 scope/reason/retry_after_s/per_source_reasons,G1 已闭)。arq defer 时长改读 `exc.retry_after_s`(注意 M2.5 后上限 300s,迁移文档追记已声明属期望)。
- [ ] 先失败后通过: shim 字段映射单测(含 per_source_reasons 透传、usage 换算)先红后绿。
- [ ] 验证: `conda run -n chs pytest tests/unit/test_worker_tasks.py <shim 单测> -v` 绿。提交点: commit。
- [ ] 先失败后通过(两组): ① shim 字段映射单测(含 usage 换算)先红后绿;② tracking.py 改动——既有 `tests/unit/test_tracking.py` 的 defer 路径用例在改 except 库异常后先红(旧 import ProviderUnavailableError 失配),改写断言消费 `exc.retry_after_s`/`per_source_reasons` 后绿。
- [ ] 验证: `conda run -n chs pytest tests/unit/test_tracking.py <shim 单测文件> -v` 绿。提交点: commit。
### T8 CHS VLM scope 切换(§8-C3,漂移修订后单段灰度)
- [ ] `app/container.py` VLM 治理装配(约 96 行)换 `GatewayClient.from_env("VLM")` + PgwVlmProvider;extractors/classifiers 经业务端口零改动;灰度期 CacheMW 关闭(`PGW_CACHE_BACKEND=none`,迁移文档 §8 裁决);`.env` 键原样继承(`VLM__QWEN__1__*` 命名即库约定)。
- [ ] **保真校验**(迁移类硬门): 对照 chsanalyzer.md §7 审计表逐条核验迁移后行为(重点: 六道闸语义、预扣结算、探针租约、RequestRejected 二分、429 细分、ResultInvalid 不熔断);发现库侧未落地项即缺口回补。蓝本对照读 reference/CHSAnalyzer(main)。
- [ ] 双实现对拍: 同一小批样本(3-5 张)旧栈(reference 侧代码逻辑,经基线记录)与新栈输出结构对齐;真实调用走实验室网关配额,串行执行。
- [ ] 红绿豁免声明: 本任务是**行为保持型切换**(无新行为可先红),测试证据由"审计表 26 条保真核验 + 双实现对拍 + 既有测试全绿"共同构成,豁免 §0 先红后绿要求。
- [ ] 验证: 提取/分类相关单测 + integration 绿。提交点: commit。
### T9 CHS OCR scope 切换(§8-C4)
@@ -135,14 +138,14 @@ def _call_llm(self, prompt: str) -> str:
return asyncio.run(_once())
```
- [ ] JSON 解析换库 `JsonRepairStrategy`(删手写 find/rfind);`JUDGE__{PROVIDER}__1__*` 配实验室中转 + claude 模型名(真实值向用户/PGW .env 读取)。**先真实探测一次**中转是否代理该模型;不通即停,回人类门(设计 §6 回退)。
- [ ] JSON 解析换库 `JsonRepairStrategy`(import 路径 `polygateway.structured.json_repair.JsonRepairStrategy`——非顶层导出,用 `parse(text)` 方法;删手写 find/rfind);桥接函数首行加 `asyncio.get_running_loop()` 探测断言(设计 §12: 确认调用方无事件循环,防嵌套 run)。`JUDGE__{PROVIDER}__1__*` 已随 T7 `.env` 就位。**先真实探测一次**中转是否代理该模型;不通即停,回人类门(设计 §6 回退)。
- [ ] 先失败后通过: test_eval_judge 桩测试(含解析失败重问路径)先红后绿 + 真实 judge 调用一次输出落 tests/outputs/m4/。
- [ ] 提交点: commit(judge 独立文件,单独可回滚)。
### T11 CHS 清场(§8-C6)
- [ ] 执行 §1 删除清单;`errors.py` Provider 族删除(业务异常保留);`config.py` 治理解析段删除;`ports.py` 限流/熔断端口删除;`.env.example` 定稿(新增 `PGW_LIMITER_BACKEND=redis``PGW_BREAKER_BACKEND=redis``PGW_TELEMETRY_BACKEND=postgres` 指 PG polygateway 库、缓存 namespace=`chsanalyzer:{scope}`)。
- [ ] 验证: `make test` 全绿(skip 剖面≥基线,分母按增删清单修正)+ `grep -rE "from app.(providers.(governance|invokers|selector|streaming)|coordination.(limiter|scripts|provider_gate))" app/ core/` 零命中
- [ ] 验证: `make test` 全绿(**skip 不增**,分母按增删清单修正)+ 残留 import 双向 grep(按被删模块名 `governance|invokers|selector|streaming|limiter|scripts|provider_gate``app/ core/ tests/` 全范围查,兼顾 `from app.providers import governance` 形态;tests/ 内命中即该测试属预期删除清单或需改写)
- [ ] 提交点: commit。
### T12 CHS 验收与回归
@@ -156,8 +159,9 @@ def _call_llm(self, prompt: str) -> str:
### T13 v1.0.0 发布(设计 §10;需用户提供 Gitea token)
- [ ] 先用 0.1.0 做试发布演练: `python -m build` → twine 传 `https://gitea.iomgaa.online/api/packages/iomgaa/pypi` → 临时 conda 环境 `pip install --index-url .../pypi/simple/ polygateway==0.1.0` 回装成功。不通则退 git+https(设计 §5)并告知用户。
- [ ] PGW `pyproject.toml` bump `1.0.0` + changelog(README 或 CHANGELOG 节)→ `make ci` 全绿(发布门)→ build → 上传 → 干净环境回装 `==1.0.0` 并重跑**两项目**测试套件绿 → 两项目 pyproject 定稿 `polygateway[...]==1.0.*` + README/Makefile 记安装命令 → PGW 打 tag `v1.0.0`
- [ ] PGW `pyproject.toml` bump `1.0.0` + changelog(README 或 CHANGELOG 节)→ `make ci` 全绿(发布门)→ build → 上传 → 干净环境回装 `==1.0.0` 并重跑**两项目**测试套件绿 → 依赖定稿: GovDoc 根 `pyproject.toml``polygateway[redis,structured]==1.0.*`,CHS `requirements.txt``polygateway[redis,postgres,structured]==1.0.*`(CHS 无 [project] 依赖段)+ 两项目 README/Makefile 记 Gitea index 安装命令 → PGW 打 tag `v1.0.0`
- [ ] token 处置: 用户提供后写 `~/.pypirc` 或环境变量,不入任何仓库。
- [ ] 库仓库是否同步推 Gitea 托管: **未拍板**,发布完成后与用户另议(设计 §10 末句),本计划不执行。
- [ ] 提交点: PGW commit(bump/changelog)+ 两 worktree 各一 commit(依赖定稿)。
### T14 文档同步与收尾(设计 §15 七项)
+9
View File
@@ -0,0 +1,9 @@
---
type: plan
node_id: plan:m4-migration
title: "M4 迁移实现计划(T0-T14)"
date: 2026-07-22
---
# M4 迁移实现计划(T0-T14)