docs: sync OCR architecture wording and close G1 R9 R10
This commit is contained in:
@@ -58,7 +58,7 @@
|
||||
| `Permit{release, settle(actual_tokens:int)}`(ports.py:495-504) | 库 `Permit.settle(actual_usage)`(ARCH §7.3) | 语义同;settle 参数类型 int vs usage 需 M2 定稿 | 库内部端口,业务不触及 |
|
||||
| `ConcurrencyLimiter`(ports.py:508-529,含 `mark_progress`/`progress_age_s`/`source_stats`) | 库限流端口(ARCH §7.3 同款契约) | 对等(§7.3 已列 mark_progress/progress_age) | 无 |
|
||||
| `ProviderGate` + `ProviderGateDecision`(epoch/is_probe/probe_owner)(ports.py:406-491) | 库熔断端口 + epoch fencing(ARCH §7.4) | 状态机对等;probe TTL 见 §9-G5 | 无 |
|
||||
| `TableLocator.locate(image)`(ports.py:577-580) | 业务端口保留,内部改调 `OcrLayoutPort.parse_layout` | 库返回 elements 全量;项目只取首个 table bbox | `table_locator.py` 改写取数逻辑 |
|
||||
| `TableLocator.locate(image)`(ports.py:577-580) | 业务端口保留,内部改调 `OcrLayoutPort.parse_layout` | 库返回 elements 全量(para_blocks 提取,与 tables 列表 bbox 逐一相等——M3 设计 §1.2 35 样本取证);项目 shim 约 5 行: 取首个 type=="table" 元素 + `int()` 四元组 | `table_locator.py` 改写取数逻辑 |
|
||||
| 错误三分类 + `OcrResultInvalidError` + `ExtractionParseError` | 库四分类(ARCH §6.1) | Transient/SourceDead/RequestRejected 一一对应;`OcrResultInvalidError` 与 `ExtractionParseError` 合并进 `ResultInvalidError` | worker `_TERMINAL`/重试分支改 except 库异常 |
|
||||
|
||||
**ProviderUnavailableError 的专门论述**(errors.py:140-180): 这是项目独有的 **scope 级不可用**语义——不是"某次调用失败",而是"整个 VLM/OCR 作用域暂时无源可用,任务应延期且**不消耗业务失败预算**"。它携带 `scope`、7 种受控 `reason`(network_error/timeout/rate_limited/source_dead/circuit_open/retry_exhausted/stalled)、`retry_after_s`(读共享熔断门取全 scope 最早恢复时刻,governance.py:182-198)、`reasons`(per-source 失败原因字典)。消费点 `workers/tracking.py:406-428`:捕获后 `finish_deferred_attempt`(FAILED 但不计失败预算)并 `raise Retry(defer=retry_after_s 派生)`——**arq 级延期重投**。ARCHITECTURE §6.1 的承接:`CircuitOpenError`/`AllSourcesExhausted` 覆盖了"发生了什么",但**未定义结构化字段**(retry_after_s/reason/per-source reasons),而 tracking.py 的延期时长直接依赖 `retry_after_s`。承接方案:库两异常须携带 `retry_after_s`(熔断后端最早恢复时刻)与逐源原因;项目侧留 10 行翻译 shim 把库异常包成 `ProviderUnavailableError`(或 tracking.py 直接改 except 库异常)。字段缺失则该行为不可复现 → **⚠️ 架构缺口 G1**。
|
||||
@@ -147,6 +147,8 @@ stack = ExtractionProviderStack(
|
||||
| 换源重试失败跨源累计,`fails > max_attempts` → retry_exhausted(governance.py:255-260) | 重试预算是全局的不是 per-source | **保留**(M2 设计明确计数口径) |
|
||||
| 整圈 gate 全拒 → circuit_open 快失败;配额满则 poll+jitter 重探(governance.py:210-214, 283-285) | 区分"全熔断"与"配额满" | **保留**(映射 CircuitOpenError vs wait,G1) |
|
||||
| MonkeyOCR 两段协议(POST /parse → GET ZIP)+ bbox 有限性/顺序/退化校验(invokers.py:489-552, 427-479) | 数值防御 | **保留**(下沉库,ARCH §7.10 已承诺;首表选取留业务) |
|
||||
| `success!=true` 拒绝不带 status_code,gate 走"本地拒绝"分支不记成功(invokers.py:514-519 + governance.py:228-236) | 熔断口径 | **有意修复**(M3): 库附 status_code=200,按"响应即健康"记成功——200 响应确证服务活着 |
|
||||
| OCR 路径共用 `_translate_429`(insufficient_quota→SourceDead、Retry-After 解析) | 429 细分 | **有意放弃**(M3): MonkeyOCR 无鉴权无计费,429 语义不存在,防御性归 Transient |
|
||||
| 无响应缓存 | 相同图+指令重复付费 | **替换**:迁移后净增缓存(风险见 §8) |
|
||||
| judge 同步裸 SDK 零治理(judge.py:34-52) | 反面教材 | **修复**(§4) |
|
||||
|
||||
@@ -171,7 +173,7 @@ stack = ExtractionProviderStack(
|
||||
| R4 | 六道闸+契约 5 条、服务器时钟窗口、settle 落 acquire 窗口、transient 按 est 保守结算 | M2 | §7.3 大体覆盖 |
|
||||
| R5 | RequestRejected 二分(真实响应记成功/本地拒绝释放探针);换源重试跨源计数口径 | M2 | 须进 M2 设计 |
|
||||
| R6 | OCR ZIP 协议 + bbox 数值防御下沉;OCR Usage=0;glm 白名单预留 | M3 | §7.10 已覆盖 |
|
||||
| **G1** | ⚠️ `CircuitOpenError`/`AllSourcesExhausted` 未定义结构化字段:须携 `retry_after_s`(全 scope 最早恢复时刻,读熔断后端)、reason(circuit_open/retry_exhausted/stalled)、per-source reasons——否则 tracking.py:406-428 的"延期重投不耗失败预算"不可复现(§3) | M2 | **架构缺口**,修订 §6.1 |
|
||||
| **G1** | ✅ 已闭(M3 核实): 库 `GatewayUnavailableError` 一族自 M1 起携 `scope/reason/retry_after_s/per_source_reasons`(errors.py:74-105),chat/embedding/OCR 三循环抛出点均已填充且有契约测试钉住;项目侧仅剩约 10 行翻译 shim(库异常 → ProviderUnavailableError)或 tracking.py 直接 except 库异常 | M2 | 已闭 |
|
||||
| **G2** | ⚠️ `est_tokens`(TPM 预扣常量 + usage 缺失兜底,config.py:55)不在 ARCH §7.7 SourceConfig 字段清单;§7.3 `try_acquire(source, est_tokens)` 的 est 来源未定义 | M2 | **架构缺口**,修订 §7.7 |
|
||||
| **G3** | ⚠️ §4.3 层序图文矛盾:图示 熔断→限流→重试(重试最内),但理由要求"每次重试重新过限流闸"且熔断/限流是 per-source 的、选源在重试循环内(governance.py:120-167 实践为每次尝试执行 选源→冷却备忘→permit→熔断门)。洋葱不澄清"逐次准入"机制则多源语义无法成立 | M2 | **架构缺口**,澄清 §4.3/§4.4 |
|
||||
| **G4** | ⚠️ per-scope 韧性配置命名(`{SCOPE}__RETRY__*`/`BREAKER__*`/`BACKPRESSURE__*`/`SELECTOR`/`GLOBAL__*`)未进 ARCH §9,现文只有平铺 `LLM_*` 键;CHSAnalyzer 的 VLM/OCR 两 scope 参数各异,平铺键无法表达 | M2 | **架构缺口**,修订 §9 |
|
||||
|
||||
@@ -154,7 +154,7 @@ ocr = FrameOcrAdapter(OcrTextPort_from_env()) # 业务适配器包装库 OCR 端
|
||||
| 10 | 装配时 Redis 不可用 → 降级无缓存(main.py:91-97);TTL≤0 拒绝启动(redis_cache.py:27-31) | **保留** |
|
||||
| 11 | 重试全部落在同一源退避等待(单源无换源) | **替换**:退避与换源结合(§4.4 步 3),单源配置下行为退化为等价 |
|
||||
| 12 | 遥测 messages 字段全量 JSON 落 SQLite——**VLM 调用的 base64 图片整段进 telemetry.db**(llm.py:330,391 `json.dumps(messages)`) | **修复建议**:库遥测对多模态 part 摘要(⚠️ §9-R12,ARCHITECTURE 未覆盖) |
|
||||
| 13 | OCR:同步 requests + 线程局部 Session + `trust_env=False` 绕代理(ocr.py:41-47);双端点加锁轮询(ocr.py:108-109);单帧失败跳过返回空(ocr.py:117-119);行级过滤(len≤1)与帧内去重、`"帧N: "` 拼接(ocr.py:120-128);`check_health` 启动预检(ocr.py:64-70) | 传输/轮询/重试**替换**(库多源全治理);过滤/拼接/单帧跳过**保留业务侧**(§3 适配器);trust_env 与 health 见 ⚠️ §9-R9/R10 |
|
||||
| 13 | OCR:同步 requests + 线程局部 Session + `trust_env=False` 绕代理(ocr.py:41-47);双端点加锁轮询(ocr.py:108-109);单帧失败跳过返回空(ocr.py:117-119);行级过滤(len≤1)与帧内去重、`"帧N: "` 拼接(ocr.py:120-128);`check_health` 启动预检(ocr.py:64-70) | 传输/轮询/重试**替换**(库多源全治理);过滤/拼接/单帧跳过**保留业务侧**(§3 适配器);trust_env(per-source 键)与 health(逐源 dict,`resp.ok` 收紧为 2xx)已按 M3 设计 §10.1 落地,R9/R10 已闭 |
|
||||
| 14 | `tools/build_trees.py` 实测 `cache=None`(build_trees.py:223,239)——建树不走缓存,断点续跑靠 `progress.json`+完整性双重跳过(build_trees.py:273-279) | 断点续跑纯业务**保留**;迁移后建树可统一开启缓存(行为增强,重跑段内调用免费) |
|
||||
| 15 | 三处写死 `stream=True`,短请求也走 SSE+看门狗(llm.py:508) | **替换可选**:库放开非流式快路径(§7.1),默认行为不变 |
|
||||
| 16 | `Runner` 注入 telemetry 从未使用(runner.py:738 死注入);EVOLVE_LLM_* 死配置(§4 点 1) | **修复**(顺带清理,属迁移必要改动非 gold-plating) |
|
||||
@@ -189,8 +189,8 @@ ocr = FrameOcrAdapter(OcrTextPort_from_env()) # 业务适配器包装库 OCR 端
|
||||
| R6 | 非流式快路径、cache salt、`gather_bounded` | M1 | 已覆盖(§7.1/§7.5/D5) |
|
||||
| R7 | 库错误类型公共导出且可被业务 except(AgentLoop `retryable_exceptions` 注入 `TransientError`/`AllSourcesExhausted`) | M1 | 基本覆盖(§6),导出面需 M1 确认 |
|
||||
| R8 | 缓存 namespace 必填对单项目是新增负担 | M1 | 已覆盖(§7.5);建议 from_env 支持项目名默认 |
|
||||
| R9 | ⚠️ **架构缺口**:MonkeyOCR transport 需 per-source `trust_env=False`(LAN 直连绕代理,ocr.py:46 实测),§7.10 未提代理/trust_env 配置 | M3 | **缺口** |
|
||||
| R10 | ⚠️ **架构缺口**:OCR 端点健康预检(`check_health`,ocr.py:64-70,A/B 实验启动门消费)——库多源配置内聚后业务侧拿不到端点列表自检,库需暴露健康检查或等价能力 | M3 | **缺口** |
|
||||
| R9 | ✅ 已闭(M3): `SourceConfig.trust_env` + `{SCOPE}__{PROVIDER}__{N}__TRUST_ENV` 键 M1 已落地,MonkeyOcrTransport 与 openai_compat 同款尊重;LAN 直连配 `OCR__MONKEY__N__TRUST_ENV=false` | M3 | 已闭 |
|
||||
| R10 | ✅ 已闭(M3): `OcrClient.check_health() -> dict[str, bool]` 逐源并发预检(2xx=True,取消穿透);启动门语义 = `all(值)`,业务可逐源定位坏端点打日志 | M3 | 已闭 |
|
||||
| R11 | ⚠️ **架构缺口/勘误**:embedding 去向(Q3)——实测 `RemoteEmbeddingProvider` **无任何重试**(embedding.py:146-172,同步 OpenAI SDK 裸调),ARCHITECTURE §13-Q3"各有一套独立重试实现"对 Video-Tree 不成立(无治理反而更需要进库);且现端口为同步 `embed()`,库若 M2 纳入必为异步端口,业务调用点需适配 | M2 | **缺口**(Q3 决策输入) |
|
||||
| R12 | ⚠️ **架构缺口**:遥测 `messages` 字段对多模态 part 应摘要——现状 base64 整段进 SQLite(llm.py:330),§7.8 未规定,照搬会让库遥测继承 db 膨胀问题 | M1 | **缺口** |
|
||||
|
||||
|
||||
Reference in New Issue
Block a user