|
|
@@ -0,0 +1,76 @@
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
type: review
|
|
|
|
|
|
|
|
node_id: review:2026-10-22-issue34-branch-review
|
|
|
|
|
|
|
|
title: "#34 OCR 整份文档解析 合并前验收与审查报告"
|
|
|
|
|
|
|
|
date: 2026-10-22
|
|
|
|
|
|
|
|
severity: approved
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# #34 OCR 整份文档解析 合并前验收与审查报告
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
- 分支 `fix/issue34-ocr-document`,基线 `25a90eb`(1.3.10),Head `cb1e08f`(17 个提交,目标 1.3.11;本报告随后追加一个文档提交)。
|
|
|
|
|
|
|
|
- 设计 `designs/2026-10-21-issue34-ocr-document-design.md`(已获人类批准:H1/H5/H6 批准,H2 授权另开 #35/#36,H3 待下游确认,H4 发布逐步等指令);计划 `plans/2026-10-21-issue34-ocr-document.md`。
|
|
|
|
|
|
|
|
- **结论**: 实现与验证完成,Critical 0、Important 已全部修复。**发布(合并/tag/上传/Release/关 #34)未执行,等人类指令。**
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## 1. 需求覆盖(issue #34 → 实现)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| issue 期望 | 实现 | 证据 |
|
|
|
|
|
|
|
|
| --- | --- | --- |
|
|
|
|
|
|
|
|
| 上传按类型设 filename/Content-Type | 库按**内容**判定(H5 有意替换「调用方声明」): 图片魔数优先 → 前 1KB 内 `%PDF-` → `doc.pdf`,其余沿用 `image.jpg`;三个方法共用 | `test_monkey_ocr.py::TestSniffMedia`、`TestUploadLabelPerMethod`、`TestParseDocument::test_upload_label_matrix_for_the_document_entry`;活体 `recognize_text(PDF)` 返回真实文本而非幻觉串 |
|
|
|
|
|
|
|
|
| 逐块 类型/页码/bbox/文字,服务阅读序 | `OcrDocumentElement`(继承 `OcrLayoutElement` + `text`);只读 `_middle.json`;表格为 HTML | `test_monkey_document.py`(§5.1 契约逐行);4 份真实包离线冒烟 29/89/30/29 块全部与 middle/content_list(反转义)一致 |
|
|
|
|
|
|
|
|
| 整篇 Markdown | `OcrDocumentResult.markdown`,按名精确配对 `.md` | 同上;真实包 `markdown == .md` 原文 |
|
|
|
|
|
|
|
|
| 长文档超时与熔断确认 | 库内无延迟型判定(grep 已证);`timeout_s` 非总耗时上界,硬上限 = `call_deadline_s ≤ lease_ttl_s`;文档线独立 scope 配方 | `TestNoLatencyJudgement`(含已有失败计数/半开探针两种前置状态)、持续慢速下载 → `CallDeadlineExceeded`、README「运维须知」 |
|
|
|
|
|
|
|
|
| `parse_layout` 不动 | 签名/返回类型/错误文案不变;仅上传分支(H6)与共用第一段协议新增 `download_url` 校验 | 独立审查对 5501 个输入与 2486 个场景做基线差分,仅 `%PDF-` 字样载荷的上传标签有差异(已登记) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## 2. 本会话证据(命令 → 结果)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 声明 | 命令 | 结果 |
|
|
|
|
|
|
|
|
| --- | --- | --- |
|
|
|
|
|
|
|
|
| 全套通过 | `pytest tests/ -q --cov=src/polygateway`(退出码取自文件,无管道) | 退出 0;**2239 passed, 23 skipped, 113 deselected(slow)**;总覆盖 96% |
|
|
|
|
|
|
|
|
| 格式/lint/依赖契约 | `make ci` | 退出 0;108 files formatted、ruff All checks passed、`Contracts: 1 kept, 0 broken` |
|
|
|
|
|
|
|
|
| 复杂度 | `radon cc <ocr.py, _monkey_document.py, monkey_ocr.py> -n C -s` | 无 C 及以上(仅 `openai_compat.py::complete` 为未触及的既有 C) |
|
|
|
|
|
|
|
|
| 活体 | `pytest tests/integration/test_monkey_ocr_document_live.py -m slow` | 3 passed(8.85s);默认套件 3 deselected |
|
|
|
|
|
|
|
|
| 真实多页 PDF 端到端 | `OcrClient.from_env("OCR_DOC")` 调 `rich.pdf`(4 页)/`tbl.pdf`(2 页含表) | 89/30 个元素,与直连存证一致;表格 HTML 取出;各 1 次尝试 |
|
|
|
|
|
|
|
|
| 红→绿 | 每任务 `scratchpad/T*-red.log` → `T*-green.log` | T1–T6 红灯原因均为目标行为缺失(导入失败或 `AttributeError: no attribute 'parse_document'`) |
|
|
|
|
|
|
|
|
| 测试非空转 | 变异检查(还原/改坏实现 → 测试转红 → 恢复) | 本会话十余类变异全部被抓到: 异常边界放错位置(47 个失败)、`status==500`/前缀判据、同步解析、体积检查先于错误状态、`_summarize`、早期装配校验、整包上限、`aread`/有界读取、`reasoning_applies`、探针归还、`download_url` 校验、`isinstance` 分派;独立审查另做 86 个变异、0 存活 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## 3. 评审轮次与处置
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 轮次 | 审查者 | 结论 | 处置 |
|
|
|
|
|
|
|
|
| --- | --- | --- | --- |
|
|
|
|
|
|
|
|
| 设计论证 | 独立 Plan subagent(含本机 MonkeyOCR 源码核对) | 弃「content_list 对位」改 middle-only 等 | 全部采纳(设计 §12) |
|
|
|
|
|
|
|
|
| 设计审查 | Codex(只读) | 否→修订后进入;9 条全部成立 | 全部修订(设计「审查记录」) |
|
|
|
|
|
|
|
|
| 计划审查 | Codex(只读) | 修订后进入执行;10 条全部成立,第 1 条为我方计划缺陷 | 全部修订(计划 §5) |
|
|
|
|
|
|
|
|
| 合并前独立验证 | 全新上下文 verifier | 无 Critical;Important 4 条 | I-1 异常漏出、I-2 整包上限无测试、I-3 业务词汇、I-4 三处空转测试——**全部修复**并用变异证明 |
|
|
|
|
|
|
|
|
| **整分支 Codex 审查** | Codex | **未完成**: 沙箱报 usage limit,只读了设计/计划/部分代码即终止,无任何分级结论 | **以全新上下文的独立 Claude 对抗式审查替代**(见下行);Codex 额度恢复后可补跑,不影响本报告其余结论 |
|
|
|
|
|
|
|
|
| 对抗式代码审查 | 全新上下文 Claude(自写模糊/变异脚本,假服务压测) | 建议合并;Critical 0,Important 3 | 见 §4 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## 4. 对抗式审查发现与处置
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| # | 发现 | 核验 | 处置 |
|
|
|
|
|
|
|
|
| --- | --- | --- | --- |
|
|
|
|
|
|
|
|
| I-1 | `_summarize` 由 `hasattr` 改 `isinstance`,鸭子类型 transport 结果以 `AttributeError` 漏出 | 本机复现(`SimpleNamespace(text=...)`) | 改回**结构分派**(有 `markdown` → 文档;有 `text` → 文本;其余版面),加回归测试 |
|
|
|
|
|
|
|
|
| I-2 | 文档路径的错误体 `aread()` 无上限(600 MiB 的 500 使 RSS 涨到 1.8 GiB) | 属实(基线非流式 `get` 同样,但新代码声称有界) | 错误体只读前 64 KiB 并复制成已缓冲响应;加测试证明只消费上限那么多 |
|
|
|
|
|
|
|
|
| I-3 | 畸形 `download_url`(控制字符/坏方括号/越界端口)让 `httpx.InvalidURL` 漏出;探针不归还 | 本机复现;`parse_layout` 基线同样 | 在共用的 `_post_parse` 一处校验,归瞬时错误、不发第二段请求;`parse_layout` 顺带受益(CHANGELOG 登记);**不限制主机**(基线接受绝对地址) |
|
|
|
|
|
|
|
|
| M-1 | 嗅探副作用: 前 1KB 含 `%PDF-` 字样的非 PDF 文本被改标;`BM` 两字节魔数太弱 | 属实 | BMP 加 DIB 头大小校验;CHANGELOG/README 登记副作用 |
|
|
|
|
|
|
|
|
| M-2 | 字节上限≠内存上限(JSON 解析后可达 23 倍;成员条数无上限) | 属实,仅服务异常时成立 | README 改为诚实口径;不实现 EOCD 预读(真实样本约 72 KB/页) |
|
|
|
|
|
|
|
|
| M-3 | `to_thread` 不是「立即生效」: GIL 下事件循环仍有停顿,取消后线程自然收尾 | 属实 | 文档改「基本不被占住」并给实测数 |
|
|
|
|
|
|
|
|
| M-4 | `parse_layout` 的 ZIP 路径仍会漏原生异常(基线即有) | 属实,按设计保持原样 | **后续项**(见 §6) |
|
|
|
|
|
|
|
|
| M-5/6/7 | 设计文档结局表与实现不一致;类 docstring 过时;测试注释含业务词汇 | 属实 | 全部修正 |
|
|
|
|
|
|
|
|
| M-8 | `page_idx` 必须等于页序,服务若跳页则整份失败 | 设计取舍 | 保留;列入未验证项 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## 5. 没能验证的部分(如实登记)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
- **线上服务**: 旋转页/带批注页/加密 PDF 的 `doc.pdf` vs `image.jpg` 差分;带密码 PDF 的服务端报错文案(若不在标记集合,走瞬时重试并计熔断);线上 backend 是否 sync-only(队头阻塞);真实 httpcore 逐次读超时行为(MockTransport 不执行它);`need_drop` 恒为 False 的线上证实。
|
|
|
|
|
|
|
|
- **样本局限**: 多行/多 span 文本块、`inline_equation` span、嵌套 caption/footnote 三条路径无真实样本,仅由构造 fixture 测试(已在 CHANGELOG/README 标注「未经实测」)。
|
|
|
|
|
|
|
|
- **未执行**: `make wiki-check`(需外部 Gitea Wiki 克隆路径,站点自 2026-08-02 下线);Redis 限流/熔断后端与 Postgres 遥测对新入口的组合(无对应环境新增用例,既有套件中的后端用例通过);整分支 Codex 审查(额度)。
|
|
|
|
|
|
|
|
- **下游**: H3(CHSAnalyzer3 是否把「服务不可用」一族按延期重投处理)未向下游证实。
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## 6. 后续项与待人类指令
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 项 | 状态 |
|
|
|
|
|
|
|
|
| --- | --- |
|
|
|
|
|
|
|
|
| #35 共享 `_classify_status` 对确定性坏输入 500 仍归瞬时 | 已开 |
|
|
|
|
|
|
|
|
| #36 `recognize_text` 不校验 `success` | 已开 |
|
|
|
|
|
|
|
|
| 建议新开: `parse_layout` ZIP 路径的原生异常漏出(OverflowError/NotImplementedError/RuntimeError)与 `download_url` 主机限制 | **待人类决定是否开 issue** |
|
|
|
|
|
|
|
|
| 发布: 合并 main → push → tag `v1.3.11` → 构建 → 上传 registry → 验证已发布 → 建 Release/挂仓库 → 关闭 #34 | **未执行,逐步等人类指令**(CLAUDE.md §4.4.1);CHANGELOG 仍为「1.3.11(未发布)」,定版日期在发布时填 |
|