docs: amend architecture per migration reverse constraints
Resolve 13 gaps flagged by the three migration documents: finalize chat() signature with per-call cache namespace/salt, fix middleware onion ordering (per-attempt source-scoped admission inside retry loop), structured AllSourcesExhausted fields, est_tokens in SourceConfig, per-scope resilience config keys, probe lease TTL, settle/release idempotency, shared limiter/breaker backend semantics, multimodal digest in telemetry, OCR trust_env and health check, API stability rules, and correct the Q3 embedding claim.
This commit is contained in:
@@ -252,9 +252,7 @@ flowchart TB
|
|||||||
end
|
end
|
||||||
subgraph PolyGateway["PolyGateway: GatewayClient"]
|
subgraph PolyGateway["PolyGateway: GatewayClient"]
|
||||||
M1[TelemetryMW 遥测+成本] --> M2[CacheMW 响应缓存]
|
M1[TelemetryMW 遥测+成本] --> M2[CacheMW 响应缓存]
|
||||||
M2 --> M3[BreakerMW 熔断]
|
M2 --> M5["RetryMW 重试循环<br/>每次尝试: 选源 → 熔断门(源) → 限流 permit(全局+源)"]
|
||||||
M3 --> M4[RateLimitMW 限流]
|
|
||||||
M4 --> M5[RetryMW 重试+退避+换源]
|
|
||||||
M5 --> T["Transport 端口<br/>OpenAICompat(httpx,默认) / OpenAISDK(可选) / MonkeyOCR"]
|
M5 --> T["Transport 端口<br/>OpenAICompat(httpx,默认) / OpenAISDK(可选) / MonkeyOCR"]
|
||||||
end
|
end
|
||||||
subgraph 状态后端["可插拔状态后端"]
|
subgraph 状态后端["可插拔状态后端"]
|
||||||
@@ -264,7 +262,7 @@ flowchart TB
|
|||||||
end
|
end
|
||||||
业务侧 -->|"await client.chat(...) / ocr.recognize_text(...) / ocr.parse_layout(...)"| PolyGateway
|
业务侧 -->|"await client.chat(...) / ocr.recognize_text(...) / ocr.parse_layout(...)"| PolyGateway
|
||||||
M1 -.-> B3
|
M1 -.-> B3
|
||||||
M2 & M3 & M4 -.-> B1 & B2
|
M2 & M5 -.-> B1 & B2
|
||||||
```
|
```
|
||||||
|
|
||||||
分层要义:**决策逻辑(中间件算法)只有一份;易变处全部是端口**——状态存哪(后端)、协议怎么发(transport)、源怎么选(selector)、输出怎么解析(structured strategy)、账记到哪(telemetry)。
|
分层要义:**决策逻辑(中间件算法)只有一份;易变处全部是端口**——状态存哪(后端)、协议怎么发(transport)、源怎么选(selector)、输出怎么解析(structured strategy)、账记到哪(telemetry)。
|
||||||
@@ -275,22 +273,24 @@ flowchart TB
|
|||||||
|
|
||||||
### 4.3 默认中间件层序及理由(外→内)
|
### 4.3 默认中间件层序及理由(外→内)
|
||||||
|
|
||||||
**遥测 → 缓存 → 熔断 → 限流 → 重试 → transport**
|
**遥测 → 缓存 → 重试循环(每次尝试: 选源 → 熔断门(源) → 限流 permit(全局+源) → transport)**
|
||||||
|
|
||||||
|
> 2026-07-20 修订(CHS 迁移文档缺口 G3): 初版把熔断/限流画在重试循环外,与"每次重试重新过限流闸"的理由自相矛盾,且熔断/限流是 **per-source** 的——源在循环内才被选出,准入只能发生在循环内。修订后与 CHSAnalyzer 实践(`governance.py:120-167` 逐次尝试执行选源→熔断→permit)一致。
|
||||||
|
|
||||||
| 相对顺序 | 理由 |
|
| 相对顺序 | 理由 |
|
||||||
|---|---|
|
|---|---|
|
||||||
| 遥测最外 | 观测一切,包括缓存命中与各类失败;任何路径都留痕 |
|
| 遥测最外 | 观测一切,包括缓存命中与各类失败;任何路径都留痕 |
|
||||||
| 缓存在熔断/限流外 | 缓存命中不应消耗限流配额,也不应被开路的熔断挡住(命中不打网关) |
|
| 缓存在重试循环外 | 缓存命中不打网关:不消耗限流配额、不受熔断状态影响 |
|
||||||
| 熔断在限流外 | 开路时直接拒绝,不占用限流租约、不排队等配额 |
|
| 重试循环拥有"尝试"的全部编排 | 每次尝试 = 选源(跳过冷却源)→ 该源熔断门(开路视为该源不可用,换源)→ 全局+该源限流 permit → transport;换源、逐次遥测、熔断计数都在循环内(与 D13 自研理由同构) |
|
||||||
| 重试在限流内 | 每次重试是一次真实网络请求,必须重新过限流闸;否则重试风暴击穿配额。此顺序意味着限流后端看到的是"含重试的真实请求数" |
|
| 熔断门先于限流 | 开路源直接跳过,不占限流租约、不排队等配额 |
|
||||||
| 换源在重试循环内 | `TransientError`/`SourceDeadError` 触发选下一源(§6),同一逻辑调用的多次尝试可落在不同源上 |
|
| 每次尝试独立过限流闸 | 重试是真实网络请求,必须重新准入,否则重试风暴击穿配额;限流后端看到的是"含重试的真实请求数" |
|
||||||
|
|
||||||
层序与取舍最终是配置——项目可增删层(如无 Redis 环境去掉 CacheMW),但改默认顺序需理解上表理由。
|
层序与取舍最终是配置——项目可增删层(如无 Redis 环境去掉 CacheMW),但改默认顺序需理解上表理由。
|
||||||
|
|
||||||
### 4.4 一次调用的生命周期(walkthrough)
|
### 4.4 一次调用的生命周期(walkthrough)
|
||||||
|
|
||||||
1. **缓存命中**: TelemetryMW 记录(cache_hit=True, latency_ms=0)→ CacheMW 返回,不触达任何更内层。
|
1. **缓存命中**: TelemetryMW 记录(cache_hit=True, latency_ms=0)→ CacheMW 返回,不触达任何更内层。
|
||||||
2. **正常路径**: 穿过熔断(闭路)→ 限流 acquire permit(并发/RPM/TPM 三闸,token 预扣)→ RetryMW 首次尝试 → selector 选源 → transport 发请求、流式解析(看门狗包裹)、收 usage 帧 → permit 按实际 usage settle(多退少补)→ 回程写缓存 → 遥测记成功(含 ttft/max_inter_token/成本)。
|
2. **正常路径**: RetryMW 开始第一次尝试 → selector 选源(跳过冷却中的源)→ 该源熔断门(闭路)→ 限流 acquire permit(全局+该源,并发/RPM/TPM 三闸,token 按 `est_tokens` 预扣)→ transport 发请求、流式解析(看门狗包裹)、收 usage 帧 → permit 按实际 usage settle(多退少补)→ 回程写缓存 → 遥测记成功(含 ttft/max_inter_token/成本)。
|
||||||
3. **瞬时错误**(超时/5xx/429/SSE 异常): transport 翻译为 `TransientError` → RetryMW 指数退避+jitter(取 Retry-After 提示与退避的较大值)后换源重试;每次尝试独立 call_id、独立过限流闸、失败即报熔断计数与遥测。
|
3. **瞬时错误**(超时/5xx/429/SSE 异常): transport 翻译为 `TransientError` → RetryMW 指数退避+jitter(取 Retry-After 提示与退避的较大值)后换源重试;每次尝试独立 call_id、独立过限流闸、失败即报熔断计数与遥测。
|
||||||
4. **源死亡**(401/403/欠费): `SourceDeadError` → 该源熔断 force_open + 本地冷却备忘 → 立即换下一源,不退避等待。
|
4. **源死亡**(401/403/欠费): `SourceDeadError` → 该源熔断 force_open + 本地冷却备忘 → 立即换下一源,不退避等待。
|
||||||
5. **请求被拒**(400/坏输入): `RequestRejectedError` → 不重试不换源,直接上抛;遥测记录。
|
5. **请求被拒**(400/坏输入): `RequestRejectedError` → 不重试不换源,直接上抛;遥测记录。
|
||||||
@@ -318,10 +318,14 @@ flowchart TB
|
|||||||
|
|
||||||
新增字段(库扩展): `source_name`(多源溯源)、`cost`(pricing 换算,可为 None)、`usage_source`(measured/estimated)。
|
新增字段(库扩展): `source_name`(多源溯源)、`cost`(pricing 换算,可为 None)、`usage_source`(measured/estimated)。
|
||||||
|
|
||||||
### 5.2 其他类型
|
**API 稳定性约定(2026-07-20,迁移文档反向约束)**: ① 公共类型新增字段必须带默认值——三项目测试中逐字段传参的 fake 构造才能零改动;② 错误四分类从 `polygateway` 顶层命名空间导出——业务侧步级重试要引用它们(GovDoc/Video-Tree 现有 `(TimeoutError, OSError)` 异常元组迁移后会**静默失效**,必须显式替换为库异常);③ `GatewayClient` 提供显式 `aclose()` 与 async context manager 生命周期 API;④ 被取消的调用尽力而为记遥测(error="cancelled",finally 中记录,绝不因遥测延迟取消传播,写失败静默)。
|
||||||
|
|
||||||
|
### 5.2 其他类型与 `chat()` 公共签名
|
||||||
|
|
||||||
`ChatRequest`(model/messages/结构化输出参数/per-call 覆盖项)、`Usage`(tokens + elapsed,OCR 无计费填 0)、`OcrTextResult`(text + 溯源三件套 source_name/usage/raw)、`OcrLayoutResult`(elements 含 bbox/type + page_size + 溯源)。全部 frozen dataclass。空结果语义:合法"无内容"用空值/None 表达,调用失败必须走异常——二者严格区分。
|
`ChatRequest`(model/messages/结构化输出参数/per-call 覆盖项)、`Usage`(tokens + elapsed,OCR 无计费填 0)、`OcrTextResult`(text + 溯源三件套 source_name/usage/raw)、`OcrLayoutResult`(elements 含 bbox/type + page_size + 溯源)。全部 frozen dataclass。空结果语义:合法"无内容"用空值/None 表达,调用失败必须走异常——二者严格区分。
|
||||||
|
|
||||||
|
**`chat()` 公共签名定稿(2026-07-20,GovDoc 迁移缺口 G1/G2)**: `chat(messages, *, session_id=None, parent_call_id=None, cache_salt=None, cache_namespace=None, structured=None, stream=True)`。要点: ① `session_id`/`parent_call_id` 与三项目现有 `LLMProvider.chat` Protocol 逐字兼容——这是"调用点零改动"承诺的前提;② **per-call `cache_namespace`**: GovDoc 是单 client 服务多租户、tenant 每请求变化,装配级 namespace 只是默认值,per-call 传入时覆盖并进入缓存 key(§7.5);③ `cache_salt` per-call 可传(Video-Tree 跨 epoch 重采样)。
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 6. 错误模型
|
## 6. 错误模型
|
||||||
@@ -336,6 +340,8 @@ flowchart TB
|
|||||||
| `ResultInvalidError` | 调用成功但内容不可解析(JSON 修不好、ZIP 缺关键文件) | ❌(策略层可选二次尝试) | ❌ | ❌(熔断记**成功**) |
|
| `ResultInvalidError` | 调用成功但内容不可解析(JSON 修不好、ZIP 缺关键文件) | ❌(策略层可选二次尝试) | ❌ | ❌(熔断记**成功**) |
|
||||||
| `CircuitOpenError` / `AllSourcesExhausted` | 开路 / 全源耗尽 | 调用方决定: wait / fail-fast 可配 | — | — |
|
| `CircuitOpenError` / `AllSourcesExhausted` | 开路 / 全源耗尽 | 调用方决定: wait / fail-fast 可配 | — | — |
|
||||||
|
|
||||||
|
**scope 级不可用的结构化语义(2026-07-20,CHS 迁移缺口 G1)**: `AllSourcesExhausted`/`CircuitOpenError` 必须携带结构化字段——`retry_after_s: float | None`(建议恢复等待,取各源冷却与 Retry-After 的最小值)、`reason` 枚举(承接 CHS `ProviderUnavailableError` 的 7 种: circuit_open / retry_exhausted / stalled / quota_exhausted / no_sources / backpressure_timeout / probe_pending)、`per_source_reasons: dict[str, str]`。CHS 的"scope 级不可用 → arq 延期重投、不消耗业务失败预算"(`workers/tracking.py:406-428`)依赖 `retry_after_s` 复现。
|
||||||
|
|
||||||
### 6.2 翻译规则(transport 层职责)
|
### 6.2 翻译规则(transport 层职责)
|
||||||
|
|
||||||
| 输入 | 翻译为 |
|
| 输入 | 翻译为 |
|
||||||
@@ -378,10 +384,11 @@ flowchart TB
|
|||||||
- `InMemoryLimiter`: 同一契约的进程内实现(semaphore + 滑动窗口计数);单进程场景下语义等价。
|
- `InMemoryLimiter`: 同一契约的进程内实现(semaphore + 滑动窗口计数);单进程场景下语义等价。
|
||||||
- **配额满行为可配**: `wait`(等待,配 stall 判定——本地等待超窗 + 全局无进展超窗双条件才判卡死)或 `fail-fast`(立即抛)。
|
- **配额满行为可配**: `wait`(等待,配 stall 判定——本地等待超窗 + 全局无进展超窗双条件才判卡死)或 `fail-fast`(立即抛)。
|
||||||
- 全局活性信号: `mark_progress()`/`progress_age_s()`("最近一次出餐"时刻)供背压 stall 判定,移植 `CHSAnalyzer limiter.py:193`。
|
- 全局活性信号: `mark_progress()`/`progress_age_s()`("最近一次出餐"时刻)供背压 stall 判定,移植 `CHSAnalyzer limiter.py:193`。
|
||||||
|
- **契约补强(2026-07-20,CHS 迁移缺口 G6)**: `settle()`/`release()` 幂等(重复调用无副作用);装配期守卫——`timeout_s ≤ permit 租约 TTL`(防租约先于请求过期)、`stall_window ≥ 最慢源 TTFT 上限`(防误判卡死),违反直接报错拒绝装配。
|
||||||
|
|
||||||
### 7.4 熔断
|
### 7.4 熔断
|
||||||
|
|
||||||
**状态机**(算法一份): 闭路 --连续失败达阈值--> 开路(冷却)--冷却到期--> 半开(只放**一个**探针,防惊群)--成功--> 闭路 / --失败--> 开路。`force_open` 支持 SourceDeadError 一击即熔。按 source_name 分别计数。
|
**状态机**(算法一份): 闭路 --连续失败达阈值--> 开路(冷却)--冷却到期--> 半开(只放**一个**探针,防惊群)--成功--> 闭路 / --失败--> 开路。`force_open` 支持 SourceDeadError 一击即熔。按 source_name 分别计数。半开探针名额是**带 TTL 的租约**(移植 CHS `scripts.py:96-105`): 探针持有者死亡后租约自动过期释放,防"探针永远在路上"死锁;`release_probe` 幂等(2026-07-20,CHS 迁移缺口 G5)。
|
||||||
|
|
||||||
- `InMemoryBreakerState`: 移植 Video-Tree `breaker.py`(时钟由调用方注入,纯确定性可测)。
|
- `InMemoryBreakerState`: 移植 Video-Tree `breaker.py`(时钟由调用方注入,纯确定性可测)。
|
||||||
- `RedisBreakerState`: 移植 CHSAnalyzer `provider_gate.py`,含 **epoch fencing**(防旧世代进程污染新状态)。
|
- `RedisBreakerState`: 移植 CHSAnalyzer `provider_gate.py`,含 **epoch fencing**(防旧世代进程污染新状态)。
|
||||||
@@ -403,11 +410,13 @@ flowchart TB
|
|||||||
|
|
||||||
### 7.7 多源与选源
|
### 7.7 多源与选源
|
||||||
|
|
||||||
`SourceConfig`: name/provider/base_url/api_key/model/超时组/限额组(单源并发/RPM/TPM)/enable_thinking。聚合自环境变量 `{SCOPE}__{PROVIDER}__{N}__{FIELD}`(§9)。`SourceSelector` 端口: `round_robin` / `least_inflight` 首发。**逻辑角色**: Video-Tree 式 SEARCH/JUDGE/VL/EVOLVE 多角色 = 命名的 client 配置组,`from_env()` 支持按角色前缀装配多个 client;禁止两个角色静默共享同一实例却在配置上看似独立(Video-Tree `evolve_llm = llm` 别名的教训——共享必须显式)。
|
`SourceConfig`: name/provider/base_url/api_key/model/超时组/限额组(单源并发/RPM/TPM)/`est_tokens`(TPM 预扣常量,亦作 usage 缺失时的保守兜底,移植 CHS `config.py:55`;2026-07-20 缺口 G2 补)/enable_thinking。聚合自环境变量 `{SCOPE}__{PROVIDER}__{N}__{FIELD}`(§9)。`SourceSelector` 端口: `round_robin` / `least_inflight` 首发。**逻辑角色**: Video-Tree 式 SEARCH/JUDGE/VL/EVOLVE 多角色 = 命名的 client 配置组,`from_env()` 支持按角色前缀装配多个 client;禁止两个角色静默共享同一实例却在配置上看似独立(Video-Tree `evolve_llm = llm` 别名的教训——共享必须显式)。
|
||||||
|
|
||||||
|
**多 client 共享状态后端(2026-07-20,VT 迁移缺口 R5)**: 限流/熔断状态的 key 以 scope+source 为单位,与 client 实例解耦;多个逻辑角色的 client **显式注入同一个状态后端实例**时即共享全局并发/RPM/TPM 闸(Video-Tree `TREE_BUILD_API_CONCURRENCY` 跨 SEARCH+VL 共享 semaphore 的语义由此承接)。共享必须显式注入,禁止隐式全局。
|
||||||
|
|
||||||
### 7.8 遥测与成本
|
### 7.8 遥测与成本
|
||||||
|
|
||||||
**必录字段**(继承三项目 15 字段规范): call_id、parent_call_id、session_id、model、provider、source_name、messages(JSON)、response、thinking、prompt_tokens、completion_tokens、usage_source、latency_ms、ttft_ms、max_inter_token_ms、cache_hit、error、**cost**。链路: `session_id`/`parent_call_id` 由调用方传入贯穿(agent step → LLM call)。
|
**必录字段**(继承三项目 15 字段规范): call_id、parent_call_id、session_id、model、provider、source_name、messages(JSON)、response、thinking、prompt_tokens、completion_tokens、usage_source、latency_ms、ttft_ms、max_inter_token_ms、cache_hit、error、**cost**。链路: `session_id`/`parent_call_id` 由调用方传入贯穿(agent step → LLM call)。`messages` 落库前对多模态 part 先摘要(与缓存 key 共用同一摘要函数,§7.5)——Video-Tree 现状 base64 整段进 SQLite 导致 db 膨胀(`llm.py:330`),库内修复(2026-07-20,VT 迁移缺口 R12)。
|
||||||
|
|
||||||
- 后端: `SQLiteRecorder`(默认;WAL + busy_timeout、`INSERT OR IGNORE` 幂等、`asyncio.to_thread` 桥接、初始化/写入失败全降级不冒泡)与 `PostgresRecorder`。
|
- 后端: `SQLiteRecorder`(默认;WAL + busy_timeout、`INSERT OR IGNORE` 幂等、`asyncio.to_thread` 桥接、初始化/写入失败全降级不冒泡)与 `PostgresRecorder`。
|
||||||
- **单一 helper 铁律**: 遥测调用点收敛为一个内部函数/上下文管理器;Video-Tree 与 GovDoc 各有 4-5 处逐字复制的 `record_llm_call(15 个参数)` 是本条的直接教训。
|
- **单一 helper 铁律**: 遥测调用点收敛为一个内部函数/上下文管理器;Video-Tree 与 GovDoc 各有 4-5 处逐字复制的 `record_llm_call(15 个参数)` 是本条的直接教训。
|
||||||
@@ -429,7 +438,7 @@ flowchart TB
|
|||||||
| `OcrTextPort.recognize_text(image: bytes)` | `POST /ocr/text` | multipart 上传 → JSON `{content}` | `OcrTextResult`(多行纯文本) |
|
| `OcrTextPort.recognize_text(image: bytes)` | `POST /ocr/text` | multipart 上传 → JSON `{content}` | `OcrTextResult`(多行纯文本) |
|
||||||
| `OcrLayoutPort.parse_layout(image: bytes)` | `POST /parse` | multipart → JSON(download_url) → GET ZIP → 解包 `*_middle.json` | `OcrLayoutResult`(elements 含 bbox + page_size) |
|
| `OcrLayoutPort.parse_layout(image: bytes)` | `POST /parse` | multipart → JSON(download_url) → GET ZIP → 解包 `*_middle.json` | `OcrLayoutResult`(elements 含 bbox + page_size) |
|
||||||
|
|
||||||
设计要点:输入统一 `bytes`(路径读取/多帧批量拼接留业务侧);bbox 返回 OCR 原生页面坐标,几何映射(裁剪偏移/归一化/marker 推算)留业务侧;`None`/空表达"合法无内容",异常表达"调用失败";ZIP 内容不可解析抛 `ResultInvalidError`(坏图≠坏服务);OCR 走同一中间件栈(无 token 计费,Usage 填 0,elapsed 照记);多后端经 provider 注册表扩展(GLM 已在 CHSAnalyzer 白名单,输入形态为 URL,届时封装在其 invoker 内部,端口签名不变)。数值防御(bbox 有限性/顺序/退化校验)随协议解析下沉进库。
|
设计要点:输入统一 `bytes`(路径读取/多帧批量拼接留业务侧);bbox 返回 OCR 原生页面坐标,几何映射(裁剪偏移/归一化/marker 推算)留业务侧;`None`/空表达"合法无内容",异常表达"调用失败";ZIP 内容不可解析抛 `ResultInvalidError`(坏图≠坏服务);OCR 走同一中间件栈(无 token 计费,Usage 填 0,elapsed 照记);多后端经 provider 注册表扩展(GLM 已在 CHSAnalyzer 白名单,输入形态为 URL,届时封装在其 invoker 内部,端口签名不变)。数值防御(bbox 有限性/顺序/退化校验)随协议解析下沉进库。OCR transport 支持 per-source `trust_env` 开关(LAN 直连绕过本地代理,Video-Tree `ocr.py:46` 教训;2026-07-20 缺口 R9);端口族含 `check_health() -> bool` 逐源健康预检(Video-Tree A/B 评测的启动门;缺口 R10)。
|
||||||
|
|
||||||
### 7.11 音频占位
|
### 7.11 音频占位
|
||||||
|
|
||||||
@@ -464,6 +473,7 @@ src/polygateway/
|
|||||||
- **载体**: `pydantic-settings` + `.env`(工程配置);缺失关键配置直接报错,严禁硬编码默认值兜底(三项目共同铁律)。
|
- **载体**: `pydantic-settings` + `.env`(工程配置);缺失关键配置直接报错,严禁硬编码默认值兜底(三项目共同铁律)。
|
||||||
- **多源命名**: `{SCOPE}__{PROVIDER}__{N}__{FIELD}`(如 `LLM__QWEN__1__API_KEY`、`OCR__MONKEY__1__BASE_URL`),聚合为 `list[SourceConfig]`;SCOPE 支持逻辑角色前缀(§7.7)。
|
- **多源命名**: `{SCOPE}__{PROVIDER}__{N}__{FIELD}`(如 `LLM__QWEN__1__API_KEY`、`OCR__MONKEY__1__BASE_URL`),聚合为 `list[SourceConfig]`;SCOPE 支持逻辑角色前缀(§7.7)。
|
||||||
- **韧性参数键名**沿用三项目习惯(`LLM_TIMEOUT` / `LLM_MAX_RETRIES` / `LLM_RETRY_BASE_DELAY` / `LLM_RETRY_MAX_DELAY` / `LLM_CIRCUIT_BREAKER_THRESHOLD` / `LLM_CIRCUIT_BREAKER_COOLDOWN` / `LLM_TTFT_TIMEOUT` / `LLM_INTER_TOKEN_TIMEOUT`),降低三项目迁移改名成本。
|
- **韧性参数键名**沿用三项目习惯(`LLM_TIMEOUT` / `LLM_MAX_RETRIES` / `LLM_RETRY_BASE_DELAY` / `LLM_RETRY_MAX_DELAY` / `LLM_CIRCUIT_BREAKER_THRESHOLD` / `LLM_CIRCUIT_BREAKER_COOLDOWN` / `LLM_TTFT_TIMEOUT` / `LLM_INTER_TOKEN_TIMEOUT`),降低三项目迁移改名成本。
|
||||||
|
- **per-scope 韧性配置(2026-07-20,CHS 迁移缺口 G4)**: 韧性参数支持按 scope 覆盖——`{SCOPE}__RETRY__MAX_ATTEMPTS` / `{SCOPE}__BREAKER__FAIL_THRESHOLD` / `{SCOPE}__BREAKER__COOLDOWN_S` / `{SCOPE}__BACKPRESSURE__STALL_WINDOW_S` / `{SCOPE}__SELECTOR` / `{SCOPE}__GLOBAL__MAX_CONCURRENCY|RPM|TPM`(CHS 现状: VLM 与 OCR 两 scope 参数各异)。平铺键(`LLM_*`)是单 scope 场景的简写;两者并存时 scope 键优先。
|
||||||
- **装配只有两条路**: `GatewayClient.from_env()`/`from_settings(settings)`(工厂,覆盖 90% 用户;补上三项目每次手写、GovDoc 缺失的"配置→client"一段)或构造函数全量依赖注入(测试/高级用户)。库内部任何组件**不得自读环境变量**(显式优于隐式)。
|
- **装配只有两条路**: `GatewayClient.from_env()`/`from_settings(settings)`(工厂,覆盖 90% 用户;补上三项目每次手写、GovDoc 缺失的"配置→client"一段)或构造函数全量依赖注入(测试/高级用户)。库内部任何组件**不得自读环境变量**(显式优于隐式)。
|
||||||
- 后端选择即配置: 如 `PGW_LIMITER_BACKEND=memory|redis`、`PGW_TELEMETRY_BACKEND=sqlite|postgres`、`PGW_QUOTA_FULL=wait|fail_fast`(命名待 M1 设计文档定稿)。
|
- 后端选择即配置: 如 `PGW_LIMITER_BACKEND=memory|redis`、`PGW_TELEMETRY_BACKEND=sqlite|postgres`、`PGW_QUOTA_FULL=wait|fail_fast`(命名待 M1 设计文档定稿)。
|
||||||
|
|
||||||
@@ -485,6 +495,8 @@ src/polygateway/
|
|||||||
## 11. 三项目迁移路径(库的验收标准)
|
## 11. 三项目迁移路径(库的验收标准)
|
||||||
|
|
||||||
> **验收定义**: 每个项目删除自己的治理实现文件,换成 `from polygateway import ...` + 配置,原测试全部通过。**凡替换不掉的能力,就是库的边界缺口**,回补后重验。这条标准同时是防"造没人用的空中楼阁"的机制:每个里程碑都有真实接入方。
|
> **验收定义**: 每个项目删除自己的治理实现文件,换成 `from polygateway import ...` + 配置,原测试全部通过。**凡替换不掉的能力,就是库的边界缺口**,回补后重验。这条标准同时是防"造没人用的空中楼阁"的机制:每个里程碑都有真实接入方。
|
||||||
|
>
|
||||||
|
> 每个项目的**详细迁移文档**(删除清单、组件映射、调用点清单、配置迁移、分步回滚、旧版行为审计、反向约束)见 `research-wiki/migrations/<project>.md`;本节保持概要。迁移文档暴露的架构缺口已于 2026-07-20 修订进本文各节(检索"迁移缺口"可定位全部修订点)。
|
||||||
|
|
||||||
### 11.1 GovDoc-SaaS(难度低,首个迁移)
|
### 11.1 GovDoc-SaaS(难度低,首个迁移)
|
||||||
|
|
||||||
@@ -535,6 +547,7 @@ src/polygateway/
|
|||||||
|---|---|---|
|
|---|---|---|
|
||||||
| Q1 | 打包与分发: 内网 pip index / git+ssh 依赖 / submodule? | git+ssh 起步,稳定后内网 index |
|
| Q1 | 打包与分发: 内网 pip index / git+ssh 依赖 / submodule? | git+ssh 起步,稳定后内网 index |
|
||||||
| Q2 | Python 最低版本 | 3.11(覆盖三项目: 3.11×2 + 3.13×1) |
|
| Q2 | Python 最低版本 | 3.11(覆盖三项目: 3.11×2 + 3.13×1) |
|
||||||
| Q3 | Embedding 客户端是否纳入(GovDoc `retrieval/embedding.py` 与 Video-Tree `adapters/embedding.py` 各有一套独立重试实现,是第三处重复) | 建议 M2 纳入,复用同一治理栈 |
|
| Q3 | Embedding 客户端是否纳入。**勘误(2026-07-20,VT 迁移文档 R11)**: 初版称"各有一套独立重试实现"不实——GovDoc 的 `OpenAICompatEmbedding` 有自研退避,但 Video-Tree 的 `RemoteEmbeddingProvider` 是**同步 SDK 裸调、无任何重试**;纳入库还需异步化其端口 | 仍建议 M2 纳入(理由更新为: 消灭无治理的裸调 + 统一重试),需含端口异步化 |
|
||||||
|
| Q6 | CHSAnalyzer 的 judge(`core/eval/judge.py`,同步裸调 anthropic SDK)迁移路径: 走 OpenAI 兼容中转网关(零库改动)还是库提供 Anthropic 原生 transport(D2 有端口预留,未排里程碑) | 待人类拍板(CHS 迁移文档 G7) |
|
||||||
| Q4 | conda 环境名 | `PolyGateway` |
|
| Q4 | conda 环境名 | `PolyGateway` |
|
||||||
| Q5 | 本仓库工程脚手架(git init、`.claude/` skills、Makefile、pyproject、import-linter)何时落地 | 本文档终审通过后、M1 编码前一次落地 |
|
| Q5 | 本仓库工程脚手架(git init、`.claude/` skills、Makefile、pyproject、import-linter)何时落地 | 本文档终审通过后、M1 编码前一次落地 |
|
||||||
|
|||||||
Reference in New Issue
Block a user