From e1afd6e36b3b31bef8de8dac4d504d5fb8e95745 Mon Sep 17 00:00:00 2001 From: let5sne Date: Sun, 12 Jul 2026 21:07:06 +0800 Subject: [PATCH] docs(personal): design scanned PDF OCR pipeline --- ...6-07-12-personal-scanned-pdf-ocr-design.md | 162 ++++++++++++++++++ 1 file changed, 162 insertions(+) create mode 100644 docs/superpowers/specs/2026-07-12-personal-scanned-pdf-ocr-design.md diff --git a/docs/superpowers/specs/2026-07-12-personal-scanned-pdf-ocr-design.md b/docs/superpowers/specs/2026-07-12-personal-scanned-pdf-ocr-design.md new file mode 100644 index 00000000..14e91abe --- /dev/null +++ b/docs/superpowers/specs/2026-07-12-personal-scanned-pdf-ocr-design.md @@ -0,0 +1,162 @@ +# 个人 AI 助理扫描 PDF OCR 设计 + +## 1. 背景与目标 + +个人 AI 助理已经支持 PDF 收藏、私有 OSS、异步解析和个人知识检索,但当前解析器只提取 PDF 文本层。扫描件没有文本层时,资料会进入 `FAILED / PERSONAL_PARSE_EMPTY`。 + +本次目标是在不改变普通 PDF 快速路径、不引入 Tesseract 等新 OCR 服务的前提下,复用现有视觉模型,为扫描 PDF 提供异步、分批、可观测、可重试的完整 OCR。 + +## 2. 范围 + +### 包含 + +- 仅在 PDF 文本提取结果为空时触发视觉 OCR。 +- 按每批 20 页处理,持续处理到整份文件完成。 +- 单文件最多 200 页;超过时明确失败并提示拆分文件。 +- 记录总页数、已处理页数、成功页数、失败页数和失败页码。 +- 全部批次完成后再将资料置为 `READY` 并写入检索片段。 +- 单页失败不终止整份文件;完成后允许只重试失败页。 +- 删除资料时清理 OCR 中间结果、最终片段和私有 OSS 对象。 + +### 不包含 + +- 不对已有文本层的 PDF 再做 OCR。 +- 不新增第三方 OCR 供应商或 Tesseract 依赖。 +- 不在同步上传请求中执行 PDF 渲染或视觉模型调用。 +- 不允许部分 OCR 内容在资料 `READY` 前进入检索。 + +## 3. 方案选择 + +采用“现有异步解析 Worker + PDF OCR 批次状态表”的方案。 + +未采用的方案: + +- 单次 Worker 内完整 OCR:实现简单,但长文件会长期占用 worker,进程重启后难以从页级进度恢复。 +- 只识别前 20 页:成本可控,但会永久丢失用户资料内容,不符合个人知识库完整性要求。 +- 本地 Tesseract:增加部署依赖,且与项目现有视觉 OCR 技术边界不一致。 + +## 4. 架构与组件 + +### 4.1 `PersonalIngestionWorker` + +保持普通文档现有行为。PDF 文本层解析为空时: + +1. 获取 PDF 页数。 +2. 页数超过 200 时,将资料置为 `FAILED / PERSONAL_PDF_PAGE_LIMIT`。 +3. 创建或恢复 OCR 任务,将资料保持为 `PARSING`。 +4. 每次 worker 调度领取一个最多 20 页的批次。 +5. 批次完成后继续排队下一批,直到所有页处理结束。 +6. 聚合成功页面文本,统一切片、写入 `aihr_personal_fragment`,最后置为 `READY`。 + +### 4.2 PDF 页面渲染器 + +新增单一职责组件 `PersonalPdfPageRenderer`: + +- 输入 PDF 字节和页码范围。 +- 使用项目现有 PDFBox 依赖将页面渲染成受限分辨率 JPEG/PNG。 +- 对单页像素尺寸和输出字节设置上限,防止压缩炸弹和内存失控。 +- 不负责模型调用、数据库写入或状态流转。 + +### 4.3 视觉 OCR 适配器 + +新增 `PersonalVisionOcr` 接口,生产实现复用现有 `category=vision`,缺失时按项目规则回退启用的 `category=chat` OpenAI-compatible 模型。 + +- 输入:渲染后的单页图片、页码。 +- 输出:规范化文本;空文本视为该页失败。 +- Prompt 只要求忠实提取文字、保留标题与段落,不总结、不补写。 +- 模型未配置时,资料置为 `FAILED / PERSONAL_OCR_MODEL_UNAVAILABLE`,不返回假内容。 + +### 4.4 OCR 状态表 + +新增 `aihr_personal_ocr_job`,按 `tenant_id + owner_user_id + item_id` 隔离: + +- `total_pages` +- `processed_pages` +- `success_pages` +- `failed_pages` +- `failed_page_numbers_json` +- `next_page` +- `status`: `PENDING/RUNNING/RETRY/COMPLETED/FAILED` +- `attempt_count` +- `last_error_code` +- 时间字段 + +新增 `aihr_personal_ocr_page` 保存页级中间结果: + +- 所属 item、页码、状态、OCR 文本、尝试次数、脱敏错误码。 +- 唯一键为 `tenant_id + owner_user_id + item_id + page_number`。 +- 中间文本只用于最终聚合,不进入检索接口。 + +## 5. 数据流 + +1. H5 上传 PDF,接口仍立即返回 `QUEUED`。 +2. Worker 下载私有 OSS 对象并执行普通 PDF 文本解析。 +3. 有文本:沿用当前解析、切片和 `READY` 流程。 +4. 无文本:创建 OCR job,资料进入扫描 PDF OCR 流程。 +5. Worker 领取 20 页批次,逐页渲染、调用视觉模型、保存页级结果并更新进度。 +6. 批次结束后释放 worker;后续调度继续领取下一批。 +7. 全部页面处理结束: + - 至少一页成功:按页码聚合文本,写最终片段;若有失败页,资料仍为 `READY`,同时保留 `PERSONAL_OCR_PARTIAL` 提示信息。 + - 全部页面失败:资料置为 `FAILED / PERSONAL_OCR_EMPTY`。 +8. 用户在资料详情查看进度和失败页,可触发“重试失败页”。 + +## 6. 状态与前端表现 + +资料详情响应增加可选 OCR 字段,旧客户端可忽略: + +- `ocrRequired` +- `ocrTotalPages` +- `ocrProcessedPages` +- `ocrSuccessPages` +- `ocrFailedPages` +- `ocrFailedPageNumbers` +- `ocrStatus` + +H5 展示: + +- `PARSING`:`正在识别扫描 PDF:20/86 页`。 +- 部分成功:`已收录,2 页识别失败`,提供“重试失败页”。 +- 超过 200 页:`文件超过 200 页,请拆分后重新上传`。 +- 无视觉模型:`扫描 PDF 识别服务未配置`。 + +## 7. 错误与恢复 + +- 单页模型超时或空结果:记录页级失败,继续下一页。 +- 批次进程中断:通过 job 的 `next_page` 和页级唯一键幂等恢复。 +- 重复调度:领取 job 时使用状态条件更新,避免两个 worker 同时处理同一批次。 +- 重试只处理失败页,不重复调用已成功页面。 +- 错误信息只保存受控错误码,不落模型原始响应、密钥或完整堆栈。 +- 删除资料后,未开始的 worker 通过 owner/item/status 条件失去领取资格;清理任务删除 OCR job/page。 + +## 8. 成本与资源边界 + +- 每批 20 页。 +- 单文件最多 200 页。 +- 同一资料同一页默认最多 3 次 OCR 尝试。 +- 页面渲染分辨率和图片字节设置固定上限。 +- 继续遵守 `AIHR_AI_RUNTIME_ENABLED` 与 `AIHR_AI_CHAT_ENABLED` 成本闸门;关闭时不外发 OCR 请求。 + +## 9. 测试策略 + +按 TDD 实现: + +1. 普通文本 PDF 不调用 OCR。 +2. 扫描 PDF 文本为空时创建 OCR job。 +3. 201 页 PDF 返回明确页数上限错误。 +4. 每次只领取最多 20 页。 +5. 批次中断后从未完成页继续,成功页不重复调用。 +6. 单页失败不阻断其他页;最终状态与成功/失败计数正确。 +7. 全部失败时资料为 `FAILED`,部分成功时资料为 `READY` 并带失败页提示。 +8. 重试只处理失败页。 +9. 删除资料清理 OCR 中间结果。 +10. 双用户、双租户不能读取或重试对方 OCR 任务。 +11. 用 `关于修订证书管理办法的通知.pdf` 做本地真实烟测:8 页全部处理,最终 `READY`,可检索并带个人引用。 + +## 10. 验收标准 + +- 现有普通 PDF、文本、网页收藏回归不受影响。 +- 8 页扫描 PDF 可以异步进入 `READY`,页面显示真实进度。 +- 21 页以上文件能跨批次继续,不截断剩余页面。 +- 超过 200 页明确拒绝,不静默截断。 +- OCR 中间文本在完成前不可检索。 +- 失败页可单独重试,删除后无 DB、向量或 OSS 残留。