Files
MinerU/docs/next/implementation-plan.md
myhloli 7f250e2331 feat(api)!: make Tasks the only asynchronous parsing API
Converge the SDK, Gradio, remote CLI, Doclib and Router on Tasks with hash-based uploads, per-file result URLs, task listing and optional model output. Preserve Files and the synchronous file_parse wrapper while removing standalone Uploads/Jobs and local-path HTTP inputs.

Migrate contract, lifecycle, transport and real-document regression coverage; update documentation and HTTP examples. Validation: 1452 passed, 3 conditional skips, plus targeted final PDF checks.

BREAKING CHANGE: clients and workers must use Tasks; standalone Uploads/Jobs endpoints and local-source/inline HTTP options have been removed.
2026-10-22 21:39:42 +08:00

42 KiB
Raw Permalink Blame History

实现计划与任务拆分

状态: Draft 读者: 编程 Agent、核心开发者、代码审查者 范围: 将 docs/next/ 中的设计文档拆成可落地、可验证、可并行领取的工程任务 非目标: 替代专题设计文档;一次性重写现有实现;要求所有任务在同一个 PR 完成

1. 使用方式

本文件是给编程 Agent 使用的任务手册。Agent 领取任务时,应只做一个任务卡片内的内容,并以该任务的“完成边界”和“验证方式”为准。

任务状态约定:

状态 含义
ready 需求清楚,可以开始实现。
blocked 需要先完成依赖任务。
verify-first 代码可能已经部分实现;先补测试或确认行为,再决定是否改代码。
design-needed 仍需产品或架构确认,不应直接实现。
done 当前代码和测试已满足完成边界;后续只在回归或重构时触碰。

Agent 通用规则:

  1. 先读本任务引用的专题文档和代码路径。
  2. 如果现有代码已经满足任务,不重写;补测试或补文档锁定行为。
  3. 任务只改“建议修改范围”内的文件;发现邻近问题时另开任务。
  4. 不改变隐私边界:没有显式 remote,不得上传用户文档。
  5. 不改变 tier 语义:默认选择不解析为 flash;结果记录实际使用的实体 tier。
  6. doclib 的 parsed/ 目录只持久化 JSON,不落盘 Markdown / Content List / HTML。
  7. 新增公开函数必须有类型注解,项目内部 import 使用 relative import。
  8. 每个任务结束前必须运行该任务列出的验证命令;不能运行时要说明原因。

2. 任务卡片模板

每个任务都按同一结构理解:

ID:
状态:
目标:
依赖:
参考文档:
建议修改范围:
具体步骤:
完成边界:
验证方式:
禁止范围:

完成边界是最重要的部分。Agent 只有在完成边界全部满足后,才可以声称任务完成。

3. 里程碑总览

3.1 当前代码审计范围

本计划已经结合当前代码做过多轮快速审计,但不是完整逐行 review。最近一次对齐时间为 2026-06-16,已检查的关键路径:

代码路径 结论
mineru/doclib/services/parse_svc.py 已有 ingest、parse request、页码覆盖判断、JSON 批次写入、FTS 临时 Markdown、tier/remote 路由的基础实现。
mineru/doclib/background/compaction.py 已有 done parse batch 合并和 JSON 按 page_idx 合并逻辑。
mineru/doclib/server.py 新 doclib server 已接管 Interface 路由;GET /docs/{sha256}/content 已支持 read-time Markdown、limit、after、content_ranges 和 next_request。
mineru/doclib/client.py 同步 Doclib client 已实现 Interface;route metadata 与 server 由测试约束一致。
mineru/doclib/core/fts.py、services/search_svc.py FTS 已存 tier;搜索结果已返回来源 tier,并支持 tier、min_tier、file_type 过滤。
mineru/doclib/locators.py 已有 short_id 相关 block/page/char cursor helper;P0 引用模型收敛为稳定 page/block locator。
mineru/parser/base.py ParseResult.from_dict() / from_json() 已实现;from_dict() 支持 schema 2.0 与 pdf_info / schema 1.0 legacy 兼容分支。
mineru/parser/__init__.py Tool SDK parse() / parse_async() 已有 tier 参数;任何入口都不接受 backend 入参。
mineru/types.py Tier、TIER_ORDER 与 Middle JSON typed dataclass 在此定义;尚无 Middle JSON normalize / validator。
mineru/errors.py 已有 MineruError、engine_error 映射和错误 envelope helper,但部分新 code 仍未补齐。
mineru/cli/main.py mineru parse/scan/watch/search/find/list/show/server/config/invalidate/forget/cleanup/usage/telemetry 已按 NEXT 入口组织。
mineru/parser/api_server.py、mineru/parser/api_client.py v1 parse API、uploads/files、tiers、usage 和 API-backed parser 已有基础;callback/webhook 本地差异和 chat/responses 仍需明确边界。

因此,任务分成三类:

类型 含义
需要实现 当前代码缺失目标能力。
部分实现 当前代码已有基础,但与文档目标还有缺口。
已实现需锁定 当前代码看起来已满足目标,任务重点是补测试、防回退、修小边界。

Agent 领取任务时,应先读本节。如果任务标记为“已实现需锁定”,不要重写实现,优先补测试。索引中“已实现需锁定”对应任务卡片的 verify-first 状态。

3.2 里程碑

里程碑 目标 结果
M0 锁定当前行为与测试骨架 后续任务有稳定测试入口。
M1 Middle JSON 和 ParseResult 基础设施 JSON 批次可恢复、校验、渲染。
M2 doclib 主链路 watch/ingest/parse/cache/compaction/read-time render 可闭环。
M3 tier、privacy、parse-server 路由 默认选择、local/remote、质量 tier 行为可验证。
M4 CLI / SDK / API 对齐 用户入口使用同一语义和错误模型。
M5 Agent-native 输出 locator、marker、citation 基础可用。
M6 Telemetry doclib server 聚合、flush、CLI 管理入口和隐私边界可验证。

建议顺序:

M0 -> M1 -> M2 -> M3 -> M4 -> M5
M6 可独立并行推进,但不能影响 parse / watch / search 主流程。

M1 和 M2 的部分测试任务可以并行,但 ParseResult.from_dict()、JSON batch 读取和 read-time render 有依赖关系。

3.3 任务状态索引

任务 当前代码状态 Agent 领取方式
M0-001 已实现 已有 doclib / parser / CLI 单元测试骨架;新增任务直接复用。
M0-002 已实现需锁定 已有 JSON-only 行为基础;继续补高风险边界测试。
M0-003 需要实现 新增 Middle JSON fixtures。
M1-001 已实现需锁定 from_dict() / from_json() 已实现并有契约测试;继续补 legacy 兼容分支边界测试。
M1-002 需要实现 新增 normalize helper。
M1-003 需要实现 新增 validator。
M1-004 部分实现 先用 validator 跑 HTML 输出,再最小修。
M1-005 需要实现 新增 bbox_known()。
M2-001 已实现 parse_batch_json_path() 已被 parse_svc / compaction 复用。
M2-002 已实现 cache coverage 与 compaction 均使用配置 data_dir;已有相关测试。
M2-003 已实现需锁定 未覆盖页入队、active parse 复用和 priority bump 已有实现与测试;继续补失败边界。
M2-004 已实现需锁定 compaction JSON 合并已有实现与测试;继续锁定重复页/invalidated 边界。
M2-005 部分实现 GET /docs/{sha256}/content 已支持 Markdown / progressive reading;Content List / HTML read-time render 仍需确认。
M2-006 已实现需锁定 FTS 临时 Markdown、JSON-only、tier-gated 更新已有实现;继续补回归测试。
M3-001 已实现需锁定 tier=None 已在入队前解析为实体 tier;需补默认选择错误码边界。
M3-002 已实现需锁定 local 不自动 remote 已有实现;补调用边界测试。
M3-003 已实现需锁定 remote 失败 fallback local 已有实现;补 via=local 和 tier 能力测试。
M3-004 已实现需锁定 tier mismatch 不降级已有实现;补错误 code / message 边界测试。
M4-001 部分实现 CLI 已调用 doclib read-time render;JSON/HTML/Content List 输出边界仍需对齐。
M4-002 已实现需锁定 Tool SDK parse() / parse_async() 已有 tier 参数(任何入口都不接受 backend 入参,kit 层 --backend 专家参数已于 2026-09 移除);补 tier 边界测试。
M4-003 部分实现 Doclib client 已有映射;API-backed parser 等仍需统一。
M4-004 部分实现 api_server/client 已有基础,需按文档补差异测试。
M5-001a 已实现需锁定 doclib locator helper 已实现;补 Structured Content / marker 复用边界。
M5-002 ready Markdown block marker 尚未完整实现。
M5-003 已实现需锁定 搜索结果已返回 tier,并支持过滤;补 CLI/JSON 边界。
M6-001 已实现需锁定 telemetry DB schema、状态管理、聚合与 flush 已实现(mineru/doclib/telemetry/ + background/telemetry_flush.py);继续补隐私边界测试。
M6-002 已实现需锁定 `mineru telemetry status

4. M0: 测试与基线

M0-001 建立 doclib parse flow 测试入口

状态: done

目标:

为 doclib 的解析主链路建立最小测试入口,后续任务可以在同一测试目录中增量添加用例。

依赖:

  • 无。

参考文档:

建议修改范围:

  • tests/ 或项目现有测试目录。
  • mineru/doclib/services/parse_svc.py 仅在测试需要暴露小 helper 时修改。

具体步骤:

  1. 查找项目现有测试布局。
  2. 如果已有 doclib 测试目录,在其中新增 parse flow 测试文件。
  3. 如果没有,创建最小测试目录和命名约定。
  4. 提供可复用的临时 data_dir fixture。
  5. 提供可复用的 fake db / sqlite test db fixture。
  6. 提供可复用的 fake parser result fixture,能返回 {"pages": [...]}。

完成边界:

  • 存在一个可运行的 doclib parse flow 测试文件。
  • 测试能创建临时 data_dir,不污染 ~/.mineru。
  • 至少一个 smoke test 能启动必要服务对象或 helper,并通过。

验证方式:

.venv/bin/python -m pytest tests -q

禁止范围:

  • 不改解析逻辑。
  • 不改生产默认 data_dir。

M0-002 锁定 doclib 只持久化 JSON 的行为

状态: verify-first

目标:

用测试固定 doclib 的持久化产物模型: parsed/ 下只保存 JSON 批次文件,其他格式读取时生成。

依赖:

  • M0-001。

参考文档:

建议修改范围:

  • tests/...
  • mineru/doclib/services/parse_svc.py

具体步骤:

  1. 构造一个 parse task,包含 sha256、tier、page_range。
  2. mock 本地 parser,使它返回带 pages 的 ParseResult-like 对象。
  3. 调用 ParseService.process_doc()。
  4. 检查 parsed/<sha-prefix>/<sha>/<tier>/ 中只有 .json 文件。
  5. 检查 JSON 内容形态为 {"pages": [...]}。
  6. 检查没有 output.md、content_list.json、structured_content.json、output.html。

完成边界:

  • 测试证明 doclib parse 完成后只落 JSON 文件。
  • 如果当前代码已经满足,只提交测试。
  • 如果当前代码不满足,只做最小修改使其满足。

验证方式:

.venv/bin/python -m pytest tests -q -k "json"

禁止范围:

  • 不实现新 render 格式。
  • 不改 Tool SDK 的 ParseResult.save() 行为;该行为不等同 doclib 持久化规则。

M0-003 建立 Middle JSON fixture

状态: ready

目标:

为 Pipeline、VLM、Office、HTML 至少建立可用于 validator / render / round-trip 的最小 fixture。

依赖:

  • 无。

参考文档:

建议修改范围:

  • tests/fixtures/middle_json/
  • tests/...

具体步骤:

  1. 新增最小 Pipeline fixture,至少包含一个 page、一个 text block、line、span。
  2. 新增最小 VLM fixture,体现一行一 span 的粒度。
  3. 新增最小 Office fixture,允许 unknown bbox。
  4. 新增最小 HTML fixture,覆盖 HTML parser 的目标结构。
  5. 每个 fixture 包含 pages 顶层结构。

完成边界:

  • 每个 fixture 都能被 JSON parser 读取。
  • fixture 不包含真实用户文档内容。
  • fixture 中的页码、block index、bbox 语义清楚。

验证方式:

.venv/bin/python -m pytest tests -q -k "fixture or middle"

禁止范围:

  • 不引入大型 PDF 或 Office 文件。
  • 不把模型输出原始大对象放入 fixture。

5. M1: Middle JSON 与 ParseResult

M1-001 实现 ParseResult.from_dict() / from_json()

状态: verify-first

目标:

让 API JSON、doclib JSON 批次和缓存结果能恢复为 ParseResult。

依赖:

  • M0-003。

参考文档:

建议修改范围:

  • mineru/parser/base.py
  • mineru/types.py
  • tests/...

具体步骤:

  1. 阅读 ParseResult.to_dict() 当前输出。
  2. 支持输入 {"pages": [...]}。
  3. 支持输入 canonical envelope 的 pages。
  4. 支持输入旧结构 {"pdf_info": [...]},如 normalize 尚未实现,可先在本任务内只做最小兼容。
  5. 将 page dict 转为 PageInfo.from_dict()。
  6. 实现 from_json(),只负责 JSON decode 后调用 from_dict()。
  7. 增加 round-trip 测试。

完成边界:

  • ParseResult.to_json() -> ParseResult.from_json() round-trip 可用。
  • {"pages": [...]} 可恢复。
  • 至少 Pipeline/Office fixture 可恢复并 render Markdown。
  • 无效输入给出明确异常。

验证方式:

.venv/bin/python -m pytest tests -q -k "ParseResult or roundtrip"

禁止范围:

  • 不重写 render。
  • 不改变 ParseResult.markdown() / content_list() 对外行为。

M1-002 实现 normalize_middle_json()

状态: ready

目标:

统一接收 canonical envelope、当前 {"pages": [...]}、旧 {"pdf_info": [...]} 三种结构,并输出标准 envelope。

依赖:

  • M1-001 可并行,但最终应互相调用。

参考文档:

建议修改范围:

  • mineru/parser/base.py
  • tests/...

具体步骤:

  1. 在 parser 层复用 mineru.parser.MIDDLE_JSON_SCHEMA_VERSION,并定义 normalize_middle_json(payload, *, tier=None, sha256=None)。
  2. 如果 payload 已有 schema_version 和 pages,直接接受;_meta 在当前 P0 写出结构中可缺省。
  3. 如果 payload 是 {"pages": [...]},包装成 canonical envelope。
  4. 如果 payload 是 {"pdf_info": [...]},迁移为 pages。
  5. 旧 _backend 不迁移:schema 2.0 不记录 backend,未知字段按第 7 条向后兼容保留原值。
  6. tier 只记录实际使用的 tier,不记录 requested_tier。
  7. 保持未知字段向后兼容,不主动丢弃 _meta 扩展。

完成边界:

  • 三种输入结构都可 normalize。
  • 输出包含 schema_version、pages、_meta。
  • 不产生 requested_tier / resolved_tier 字段。

验证方式:

.venv/bin/python -m pytest tests -q -k "normalize_middle_json"

禁止范围:

  • 不改 backend 输出逻辑。
  • 不强制所有调用点一次性迁移。

M1-003 增加 Middle JSON P0 validator

状态: ready

目标:

提供轻量 validator,能发现 P0 结构错误,同时允许后续 schema 演进。

依赖:

  • M1-002。

参考文档:

建议修改范围:

  • validator 生产入口位置待定
  • tests/...

具体步骤:

  1. 定义 ValidationIssue 类型。
  2. 实现 validate_middle_json(payload) -> list[ValidationIssue]。
  3. validator 内部先调用 normalize 或要求输入已 normalize,二者择一并文档化。
  4. 校验 pages 是 list。
  5. 校验 page 至少有 page_idx。
  6. 校验 block 至少有 index、type、bbox、lines 或 blocks。
  7. 允许 Office unknown bbox,但 issue 中能提示 bbox_known=false 场景。
  8. 对 _backend 旧字段给 warning,不给 fatal。

完成边界:

  • fixtures 通过 validator。
  • 人工破坏 page/block 必填字段时 validator 能报 issue。
  • validator 不因 Office bbox unknown 直接失败。

验证方式:

.venv/bin/python -m pytest tests -q -k "validator"

禁止范围:

  • 不引入重型 JSON schema 依赖,除非项目已有。
  • 不把所有 backend 私有字段列为公开必填。

M1-004 修正 HTML parser 的 typed structure 输出

状态: verify-first

目标:

确保 HTML parser 输出满足 PageInfo / Block / Line / Span 的必填字段契约。

依赖:

  • M0-003。
  • M1-003。

参考文档:

建议修改范围:

  • mineru/parser/html.py
  • mineru/types.py 仅在确有必要时修改。
  • tests/...

具体步骤:

  1. 运行 HTML parser 现有测试或新增最小 HTML 解析测试。
  2. 用 validator 检查 HTML 输出。
  3. 如果缺少 bbox、index、page_size 或 _backend 合法值,最小修正。
  4. 对没有真实 bbox 的内容使用 unknown bbox 约定。
  5. 确认 render Markdown 不回退。

完成边界:

  • HTML fixture 能生成合法 pages。
  • validator 无 fatal issue。
  • ParseResult.markdown() 可输出。

验证方式:

.venv/bin/python -m pytest tests -q -k "html and middle"

禁止范围:

  • 不重写 HTML converter。
  • 不引入浏览器运行时。

M1-005 定义 bbox_known() helper

状态: ready

目标:

为 unknown bbox 提供统一判断,避免 Office/HTML 等 backend 用假 bbox 被误当真实坐标。

依赖:

  • M1-003。

参考文档:

建议修改范围:

  • mineru/types.py 或未来 validator 模块
  • tests/...

具体步骤:

  1. 确认当前 unknown bbox 表达。
  2. 定义 bbox_known(bbox) -> bool。
  3. 处理 None、空列表、全 0、负数或 sentinel 值。
  4. 在 validator issue 中使用该 helper。
  5. 添加 Office unknown bbox 测试。

完成边界:

  • helper 有明确 docstring。
  • validator 能区分 unknown bbox 和非法 bbox。
  • 不改变已有 backend 坐标数值。

验证方式:

.venv/bin/python -m pytest tests -q -k "bbox"

禁止范围:

  • 不要求 Office 立即补真实 bbox。

6. M2: doclib JSON 批次与缓存

M2-001 抽出 JSON 批次路径 helper

状态: done

目标:

把 doclib JSON 批次文件路径生成逻辑集中,避免 process_doc()、cache check、compaction 各自拼路径。

依赖:

  • M0-002。

参考文档:

建议修改范围:

  • mineru/doclib/services/parse_svc.py
  • mineru/doclib/background/compaction.py
  • tests/...

具体步骤:

  1. 找出 _safe_filename()、_json_file_exists_by_batch() 和 compaction 中的重复路径逻辑。
  2. 抽出 helper,例如 parse_batch_json_path(data_dir, sha256, tier, page_range, done_at)。
  3. 所有路径都使用传入的 data_dir,不能硬编码 ~/.mineru。
  4. 更新 process、cache check、compaction 调用。
  5. 补测试覆盖 page range 文件名。

完成边界:

  • 生产代码不再在多个地方手写 batch JSON 路径。
  • 测试 data_dir 不会误读 ~/.mineru。
  • 现有 JSON 文件命名保持兼容。

验证方式:

.venv/bin/python -m pytest tests -q -k "batch_json_path or parse"

禁止范围:

  • 不迁移旧文件名。
  • 不改变 JSON 内容结构。

M2-002 修正 cache coverage 对 data_dir 的依赖

状态: done

目标:

确认 cache coverage 判断使用当前 ParseService 的 data_dir,而不是固定 ~/.mineru。

依赖:

  • M2-001。

参考文档:

建议修改范围:

  • mineru/doclib/services/parse_svc.py
  • tests/...

具体步骤:

  1. 编写测试: 临时 data_dir 中存在 done batch JSON。
  2. 调用 request_parse() 请求同一页码。
  3. 期望返回 cached done。
  4. 删除 JSON 文件后再次请求,期望重新入队。
  5. 如果测试失败,修正 data_dir 依赖。

完成边界:

  • JSON 存在时命中缓存。
  • JSON 丢失时不命中缓存。
  • 测试不访问真实用户目录。

验证方式:

.venv/bin/python -m pytest tests -q -k "cache and data_dir"

禁止范围:

  • 不改变 parses 表 schema。

M2-003 实现或锁定增量页码入队

状态: verify-first

目标:

确保请求页码只对未覆盖页面创建新 parse record,并返回本次请求需要等待的 parse ids。

依赖:

  • M2-002。

参考文档:

建议修改范围:

  • mineru/doclib/services/parse_svc.py
  • tests/...

具体步骤:

  1. 准备已有 done batch: page_range=1-5。
  2. 请求 page_range=1-10。
  3. 期望只创建 6-10 的 pending batch。
  4. 准备已有 pending batch: page_range=6-10。
  5. 再请求 1-10,期望不重复创建,而是提升 priority。
  6. 测试 force=True 时跳过 done cache,但复用 active batch。
  7. 测试 force=True 只为 active 未覆盖页创建新 parse。
  8. 测试返回 wait_parse_ids、created_parse_ids、reused_parse_ids。
  9. 测试 force wait parse 失败后,本次请求失败,但旧 done batch 仍然有效。
  10. 测试 invalidated batch 不参与覆盖判断。

完成边界:

  • 覆盖判断按页集合工作。
  • pending/parsing 覆盖时只提升优先级。
  • force 跳过 done cache,但可复用 active batch,只为未覆盖页创建新任务。
  • response 能表达 wait / created / reused parse ids。
  • invalidated batch 不参与缓存命中。

验证方式:

.venv/bin/python -m pytest tests -q -k "pages_uncovered or coverage"

禁止范围:

  • 不改默认页码策略。

M2-004 补齐 compaction JSON 合并测试

状态: verify-first

目标:

用测试锁定 compaction 按 page_idx 合并 JSON 的行为。

依赖:

  • M2-001。

参考文档:

建议修改范围:

  • mineru/doclib/background/compaction.py
  • tests/...

具体步骤:

  1. 创建同一 sha256 + tier 下两个有效 done row。
  2. 创建对应 JSON 文件。
  3. 调用 compaction。
  4. 检查旧 JSON 被删除。
  5. 检查新 JSON 覆盖合并后的页。
  6. 添加重复页测试,确认 done_at 较新的有效批次覆盖较旧批次。
  7. 添加 invalidated batch 测试,确认它不会被合入新 JSON。

完成边界:

  • compaction 合并 parse row。
  • compaction 合并 JSON 文件。
  • compaction 不生成 Markdown / Content List。
  • compaction 忽略 invalidated batch。

验证方式:

.venv/bin/python -m pytest tests -q -k "compaction"

禁止范围:

  • 不改变 compaction 定时策略。

M2-005 实现 read-time render from JSON batches

状态: verify-first

目标:

确保 doclib 对外返回 Markdown / Content List / HTML 时,从已保存 JSON 批次读取并转换,而不是读取持久化派生文件。

依赖:

  • M1-001。
  • M2-003。

参考文档:

建议修改范围:

  • mineru/doclib/services/parse_svc.py
  • mineru/doclib/server.py
  • mineru/doclib/client.py
  • tests/...

具体步骤:

  1. 定位当前 parse_content() 或等价读取入口,并迁移为 GET /docs/{sha256}/content。
  2. 实现按 sha256 + tier + page_range 收集已完成 JSON 批次。
  3. 使用 ParseResult.from_dict() 恢复 pages。
  4. 按请求格式调用 markdown()、content_list()、structured_content() 或 HTML render。
  5. 如果请求页未被覆盖,返回明确状态或错误,不生成空内容。
  6. 保持输出文件写入只发生在用户指定 output 时。

完成边界:

  • doclib 不需要 output.md 也能返回 Markdown。
  • JSON 输出返回 Middle JSON pages。
  • Content List 输出从同一 JSON 转换。
  • 缺页时返回可操作错误或 pending 状态。

验证方式:

.venv/bin/python -m pytest tests -q -k "parse_content or read_time_render"

禁止范围:

  • 不把 Markdown/Content List 写入 parsed/。
  • 不改 Tool SDK save()。

M2-006 FTS 只消费临时 Markdown,不持久化

状态: verify-first

目标:

确认搜索索引更新可以使用临时 Markdown 文本,但不会在 doclib 产物目录落盘。

依赖:

  • M0-002。

参考文档:

建议修改范围:

  • mineru/doclib/services/parse_svc.py
  • mineru/doclib/core/fts.py
  • tests/...

具体步骤:

  1. mock ParseResult.markdown() 返回可搜索文本。
  2. 执行 process_doc。
  3. 检查 FTS 收到文本。
  4. 检查 parsed 目录无 Markdown 文件。
  5. 检查低 tier 不覆盖高 tier FTS。

完成边界:

  • FTS 有内容。
  • Markdown 不落盘。
  • tier-gated 更新符合 flash < basic < standard < advanced。

验证方式:

.venv/bin/python -m pytest tests -q -k "fts"

禁止范围:

  • 不改搜索 query 语义。

7. M3: tier、privacy 与 parse-server 路由

M3-001 默认 tier 在入队前解析为实际 tier

状态: verify-first

目标:

确保默认选择不作为任务 tier、缓存目录或产物目录持久化;最终只记录实际使用的实体 tier。

依赖:

  • M2-003。

参考文档:

建议修改范围:

  • mineru/doclib/services/parse_svc.py
  • mineru/doclib/background/parse_server_health.py
  • tests/...

具体步骤:

  1. 构造 health: local supported tiers = ["basic", "standard"]。
  2. 请求 tier=None。
  3. 期望入队 tier 为 standard。
  4. 构造 health: local supported tiers = ["basic"]。
  5. 期望入队 tier 为 basic。
  6. 构造 health: only flash 或空。
  7. 期望返回 quality_tier_unavailable 或等价错误。

完成边界:

  • 不创建默认选择专用 parse row。
  • 不创建默认选择专用缓存目录。
  • 不记录 requested_tier 字段。
  • 默认选择永不选择 flash。

验证方式:

.venv/bin/python -m pytest tests -q -k "default and tier"

禁止范围:

  • 不改变显式 tier=flash 行为。

M3-002 隐私边界: local 失败不自动 remote

状态: verify-first

目标:

用测试和最小实现保证没有显式 remote 时,不会调用 Remote Parse Server。

依赖:

  • M3-001。

参考文档:

建议修改范围:

  • mineru/doclib/services/parse_svc.py
  • mineru/parser/api_client.py 仅用于 mock 调用边界。
  • tests/...

具体步骤:

  1. mock local parse-server unavailable。
  2. mock remote parse-server healthy。
  3. 请求 remote=False。
  4. 期望返回本地能力不足错误。
  5. 确认没有构造指向 remote URL 的 MinerUApiParser。
  6. 请求 remote=True。
  7. 期望允许 remote。

完成边界:

  • local 请求不上传。
  • 错误包含可操作 suggestion。
  • remote 请求才允许使用 remote URL。

验证方式:

.venv/bin/python -m pytest tests -q -k "privacy or remote"

禁止范围:

  • 不新增全局配置来默认允许 remote。

M3-003 remote 失败可以 fallback 到 local

状态: verify-first

目标:

用户已显式允许 remote 时,如果 remote 不可达,可以 fallback 到 local,但不能改变结果审计。

依赖:

  • M3-002。

参考文档:

建议修改范围:

  • mineru/doclib/services/parse_svc.py
  • tests/...

具体步骤:

  1. 设置 privacy=remote。
  2. mock remote unhealthy。
  3. mock local healthy 且支持请求 tier。
  4. 执行解析。
  5. 期望使用 local URL。
  6. 完成后 via=local。

完成边界:

  • fallback 只发生在用户已允许 remote 时。
  • via 记录实际执行路径。
  • tier 能力仍需匹配。

验证方式:

.venv/bin/python -m pytest tests -q -k "fallback"

禁止范围:

  • 不对文件损坏、加密等非网络错误做 fallback。

M3-004 parse-server tier mismatch 错误

状态: verify-first

目标:

当 parse-server 不支持请求 tier 时,返回稳定错误,不自动降级。

依赖:

  • M3-001。

参考文档:

建议修改范围:

  • mineru/doclib/services/parse_svc.py
  • mineru/errors.py
  • tests/...

具体步骤:

  1. mock parse-server supported tiers = ["basic"]。
  2. 请求 tier="standard"。
  3. 期望 tier_mismatch。
  4. 确认不会改成 basic。
  5. 确认不会改成 flash。

完成边界:

  • 错误 code 稳定。
  • 错误 message 包含 available tiers。
  • 无降级 parse row。

验证方式:

.venv/bin/python -m pytest tests -q -k "tier_mismatch"

禁止范围:

  • 不改变默认选择逻辑。

8. M4: CLI / SDK / API 对齐

M4-001 mineru parse 使用 doclib read-time render

状态: verify-first

目标:

确保 mineru parse 的 Markdown/Text/JSON 输出来自 doclib JSON 批次,而不是派生产物文件。

依赖:

  • M2-005。

参考文档:

建议修改范围:

  • mineru/cli/commands/parse.py
  • mineru/cli/output.py
  • mineru/doclib/client.py
  • tests/...

具体步骤:

  1. 检查 CLI parse 当前调用链。
  2. JSON 格式输出 Middle JSON 或 response envelope,按 CLI 文档确定。
  3. Markdown/Text/HTML 格式通过 doclib client 请求 read-time render。
  4. --output 写用户指定路径,不写 doclib parsed/。
  5. --no-wait 未完成时返回任务状态。

完成边界:

  • CLI 不依赖 output.md。
  • CLI 输出格式符合文档。
  • 缓存命中时可直接输出。

验证方式:

.venv/bin/python -m pytest tests -q -k "cli and parse"

禁止范围:

  • 不在普通 mineru parse 暴露 backend 参数。

M4-002 Tool SDK parse() 增加 tier 参数

状态: verify-first

目标:

让 Tool SDK 支持 tier 参数;backend 不进入 Tool SDK 签名。backend 由 tier 派生,仅作为内部实现细节(2026-09 修订:kit 层的 --backend 专家参数已移除,下文涉及 backend 入参的步骤与完成边界作废)。

依赖:

  • M1-001。

参考文档:

建议修改范围:

  • mineru/parser/__init__.py
  • mineru/parser/pdf.py
  • mineru/parser/base.py
  • tests/...

具体步骤:

  1. 查清 parse() 当前签名。
  2. 增加 tier: str | None = None。
  3. backend 显式传入时覆盖 tier。
  4. tier=flash 映射到 flash backend。
  5. tier=basic 映射到默认 basic backend。
  6. tier=standard 映射到默认 standard backend。
  7. tier=None 在 Tool SDK 的 PDF/image 场景中如果不能发现非 flash 质量 tier,应报错,不静默 flash;Office/HTML 这类仅支持 flash tier 的输入按实际能力归一为 flash。

完成边界:

  • parse(path, tier="flash") 可用。
  • parse(path, tier="basic") 可用或返回明确能力错误。
  • parse(path, backend="pipeline") 兼容。
  • backend 覆盖 tier 有测试。

验证方式:

.venv/bin/python -m pytest tests -q -k "parser and tier"

禁止范围:

  • 不让 Tool SDK 隐式启动 doclib server。
  • 不让 Tool SDK 隐式 remote 上传。

M4-003 统一错误 envelope 到 CLI/SDK 异常

状态: ready

目标:

API-backed parser、Doclib SDK、本地 parser 的错误都能被统一捕获,并保留 code。

依赖:

  • M3-004。

参考文档:

建议修改范围:

  • mineru/errors.py
  • mineru/parser/api_client.py
  • mineru/doclib/client.py
  • tests/...

具体步骤:

  1. 定义或补齐 MineruError 子类和 code 属性。
  2. 将 _V1APIError 映射到 MineruError。
  3. Doclib client 收到 error envelope 时抛出统一异常。
  4. CLI 根据 code 输出修复建议。
  5. 增加 quality_tier_unavailable 等错误测试。

完成边界:

  • 三类入口都能捕获 MineruError。
  • 异常保留 type、code、param 或等价信息。
  • CLI 不吞掉机器可读 code。

验证方式:

.venv/bin/python -m pytest tests -q -k "error"

禁止范围:

  • 不改所有错误文案,只保证结构和核心 code。

M4-004 Local Parse Server API 与官方 API 差异测试

状态: ready

目标:

确保 Local Parse Server 可以复用 v1 API 客户端,同时允许本地差异。

依赖:

  • M4-003。

参考文档:

建议修改范围:

  • mineru/parser/api_server.py
  • mineru/parser/api_client.py
  • tests/...

具体步骤:

  1. 测试 GET /v1/health。
  2. 测试 GET /v1/tiers。
  3. 测试 POST /v1/tasks 的最小本地路径 source。
  4. 未实现 webhook 时 health features 应标记 false。
  5. 使用同一个 MinerUApiParser 调用本地 server。

完成边界:

  • Local Parse Server 不需要实现官方全部能力,但 endpoint 行为和差异符合文档。
  • 本地路径越权被拒绝。
  • 客户端可复用。

验证方式:

.venv/bin/python -m pytest tests -q -k "api_server or local_source"

禁止范围:

  • 不实现公网计费、CDN、Webhook 完整能力。

9. M5: Agent-native 输出

M5-001a 锁定 doclib locator helper

状态: verify-first

目标:

锁定当前 doclib locator / cursor helper 行为,确保 public locator 使用 1-based 页号和块号,且不包含本地路径。

依赖:

  • ADR-0011。
  • ADR-0012。

参考文档:

建议修改范围:

  • mineru/doclib/locators.py
  • tests/...

具体步骤:

  1. 确认 locator_for_block(page_no, block_no) 使用 1-based public number。
  2. 确认 page_ref()、block_ref()、block_char_ref() 使用 short_id、实体 tier、1-based page/block 和 0-based char offset。
  3. 确认 parse_content_cursor() 能解析 page / block / char 三种 cursor。
  4. 补充或保留测试,避免 0-based page/block 泄漏到 public cursor。
  5. 确认 marker / continuation 不包含本地绝对路径。

完成边界:

  • locator helper 行为由测试锁定。
  • page/block public number 均为 1-based。
  • char offset 为 block 渲染文本内 0-based offset。
  • cursor 中不包含 path 或 filename。

验证方式:

.venv/bin/python -m pytest tests -q -k "locator"

禁止范围:

  • 不实现独立 hash 型引用 ID。
  • 不实现 span/cell/list-item locator。

M5-002 Markdown locator marker

状态: ready

目标:

Markdown 输出可选择携带 Agent locator marker。

依赖:

  • M5-001a。

参考文档:

建议修改范围:

  • mineru/render/union_make.py
  • mineru/render/markdown.py
  • tests/...

具体步骤:

  1. 找到 add_markers 参数当前行为。
  2. 为 block 输出前增加可选 marker。
  3. marker 包含稳定 page/block locator。
  4. 默认不输出 marker。
  5. CLI/SDK 需要显式 opt-in。

完成边界:

  • markdown(add_markers=False) 输出不变。
  • markdown(add_markers=True) 输出 marker。
  • marker 可被正则稳定解析。

验证方式:

.venv/bin/python -m pytest tests -q -k "marker"

禁止范围:

  • 不改变 Markdown 默认视觉格式。

M5-003 搜索结果返回来源 tier

状态: verify-first

目标:

让 Agent 知道搜索 snippet 来自哪个 tier,以便决定是否升级解析。

依赖:

  • M2-006。

参考文档:

建议修改范围:

  • mineru/doclib/services/search_svc.py
  • mineru/doclib/core/fts.py
  • mineru/doclib/client.py
  • tests/...

具体步骤:

  1. 确认 FTS 表是否已有 tier 字段。
  2. search response 中暴露来源 tier。
  3. CLI search 可显示或 JSON 输出该字段。
  4. 如果没有 tier,返回 null 或 omit,并补 TODO issue。

完成边界:

  • 搜索结果机器可读地包含来源 tier。
  • flash snippet 能被识别。
  • 高 tier 更新 FTS 后来源 tier 更新。

验证方式:

.venv/bin/python -m pytest tests -q -k "search and tier"

禁止范围:

  • 不实现搜索排序重构。

10. M6: Telemetry

M6-001 实现 telemetry DB 状态、聚合与 flush

状态: verify-first

目标:

实现 doclib server 侧 telemetry 状态管理、本地聚合存储和周期 flush。纯工具层不得具备 telemetry 能力。

依赖:

参考文档:

建议修改范围:

  • mineru/doclib/migrations/001_init.sql
  • mineru/doclib/services/
  • mineru/doclib/background/
  • mineru/doclib/app.py
  • tests/...

具体步骤:

  1. 增加 telemetry 状态表或配置记录,保存 consent_state、内部 installation_id、flush lock 和 last flush 状态。
  2. 增加 telemetry 聚合表,按 period、metric name、dimensions hash upsert 累加。
  3. 实现 metric / dimension 白名单,不允许透传请求体、异常对象、配置对象或任意 dict。
  4. 实现 doclib API / CLI source / caller 的 best-effort 透传入口。
  5. 实现 flush 任务,成功后删除已确认上报的聚合记录。
  6. flush 失败不得影响 parse、watch、search 或任意 doclib API。
  7. 用户关闭 telemetry 时停止写入新 metric,并清除未上报聚合数据。

完成边界:

  • consent_state=disabled 时不写入新 telemetry 聚合。
  • consent_state=enabled 时 metrics 按小时级 period 聚合。
  • flush 成功后本地聚合记录被清理。
  • flush 失败保留记录等待下次重试。
  • telemetry 不记录路径、文件名、query、snippet、文档内容、traceback 或 API Key。

验证方式:

.venv/bin/python -m pytest tests -q -k "telemetry"

禁止范围:

  • 不在 parser SDK、mineru-kit parse 或 mineru-kit api-server 中加入 telemetry 能力。
  • 不实现每事件实时上报。
  • 不阻塞主业务链路等待 telemetry。

M6-002 实现 mineru telemetry CLI

状态: verify-first

目标:

提供用户可见的 telemetry 状态查看和开关命令。

依赖:

  • M6-001。

参考文档:

建议修改范围:

  • mineru/cli/main.py
  • mineru/cli/commands/
  • mineru/doclib/base.py
  • mineru/doclib/client.py
  • mineru/doclib/server.py
  • mineru/doclib/types.py
  • tests/...

具体步骤:

  1. 增加 mineru telemetry status。
  2. 增加 mineru telemetry enable。
  3. 增加 mineru telemetry disable。
  4. 增加 mineru telemetry preview,只展示将上报的聚合摘要,不展示敏感字段。
  5. 增加 mineru telemetry flush,触发一次手动 flush。
  6. 首次启动选择流程如暂不实现 UI,应至少让 consent_state=unset 可观测,并在 CLI/server status 中提示。

完成边界:

  • 用户可以查看当前 consent 状态。
  • 用户可以开启和关闭 telemetry。
  • 关闭时清理未上报聚合数据。
  • preview 不包含路径、文件名、query、snippet、文档内容、traceback 或 API Key。
  • 命令失败时返回稳定错误 code。

验证方式:

.venv/bin/python -m pytest tests -q -k "telemetry or cli"

禁止范围:

  • 不要求实现图形化首次启动选择。
  • 不向 parser/tool 层传递 telemetry 配置。

11. 可并行性

可并行组 任务
Fixtures / validator M0-003、M1-003、M1-005
doclib JSON behavior M0-002、M2-003、M2-004
privacy / tier tests M3-001、M3-002、M3-004
CLI / SDK M4-001、M4-002,需避免同文件冲突
Agent locator M5-001a 可独立锁定;M5-002 依赖 locator helper
Telemetry M6-001 可独立推进;M6-002 依赖 M6-001

不建议并行:

  • M3-001 与 M3-004 同时改 _resolve_tier()。
  • M1-001 与 M2-005 同时改 ParseResult.from_dict() 调用边界。

12. PR 切分建议

PR 包含任务 目标
PR-1 M0-003 Middle JSON fixture。
PR-2 M1-001, M1-002, M1-003 Middle JSON normalize / round-trip / validate。
PR-3 M2-003, M2-004 doclib page coverage 和 compaction 边界锁定。
PR-4 M2-005, M2-006 read-time render 和 FTS。
PR-5 M3-001, M3-002, M3-003, M3-004 tier / privacy / parse-server 路由。
PR-6 M4-001, M4-002, M4-003 CLI / SDK / 错误模型。
PR-7 M5-001a, M5-002, M5-003 Agent-native locator / marker / search tier。
PR-8 M6-001, M6-002 Telemetry DB、flush 和 CLI。

每个 PR 都应能独立通过测试,并保留向后兼容。

13. 完成定义

P0 完成需要满足:

  1. mineru parse 能在本地完成 ingest、cache check、parse task、JSON batch 写入、read-time Markdown 输出。
  2. watch 能创建 flash 任务并更新搜索索引。
  3. doclib parsed/ 目录只保存 JSON。
  4. 默认选择不产生独立缓存目录,不解析为 flash。
  5. 未显式 remote 时不会上传。
  6. Local Parse Server 支持 tier discovery 并可被 doclib 调用。
  7. ParseResult.from_json() 可恢复 JSON 批次。
  8. Middle JSON validator 覆盖 P0 必填字段。
  9. 错误 code 对 CLI/SDK/API 可见。
  10. Telemetry consent、聚合、flush 和 CLI 管理入口可验证。
  11. Agent-native 输出具备稳定 page/block locator、next_request、truncated 和 CLI next marker。
  12. workflows 中 13.1 到 13.7 的端到端验收可以通过测试或手工脚本验证。

P1 完成需要满足:

  1. Agent 主动读取默认升级到非 flash 质量 tier。
  2. Markdown 可选输出 locator marker。
  3. 高质量解析完成后刷新搜索索引。

14. Agent 领取任务时的最终检查

每个任务完成前,Agent 必须回答:

  1. 我改了哪些文件?
  2. 哪些完成边界已经满足?
  3. 哪些验证命令已经运行,结果是什么?
  4. 是否有未完成但相关的后续任务?
  5. 是否触碰了任务禁止范围?

如果第 5 项答案是“是”,该任务不应合并,除非用户或维护者明确批准。