Files
AIclinicalresearch/docs/02-通用能力层/02-文档处理引擎/03-PDF表格提取引擎设计方案.md
HaHafeng dc6b292308 docs(asl): Complete Tool 3 extraction workbench V2.0 development plan (v1.5)
ASL Tool 3 Development Plan:
- Architecture blueprint v1.5 (6 rounds of architecture review, 13 red lines)
- M1/M2/M3 sprint checklists (Skeleton Pipeline / HITL Workbench / Dynamic Template Engine)
- Code patterns cookbook (9 chapters: Fan-out, Prompt engineering, ACL, SSE dual-track, etc.)
- Key patterns: Fan-out with Last Child Wins, Optimistic Locking, teamConcurrency throttling
- PKB ACL integration (anti-corruption layer), MinerU Cache-Aside, NOTIFY/LISTEN cross-pod SSE
- Data consistency snapshot for long-running extraction tasks

Platform capability:
- Add distributed Fan-out task pattern development guide (7 patterns + 10 anti-patterns)
- Add system-level async architecture risk analysis blueprint
- Add PDF table extraction engine design and usage guide (MinerU integration)
- Add table extraction source code (TableExtractionManager + MinerU engine)

Documentation updates:
- Update ASL module status with Tool 3 V2.0 plan readiness
- Update system status document (v6.2) with latest milestones
- Add V2.0 product requirements, prototypes, and data dictionary specs
- Add architecture review documents (4 rounds of review feedback)
- Add test PDF files for extraction validation

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-23 22:49:16 +08:00

21 KiB
Raw Permalink Blame History

PDF 表格提取引擎设计方案

文档版本: v1.0
创建日期: 2026-02-23
最后更新: 2026-02-23
文档目的: 定义 PDF 表格提取引擎的统一架构,为系统综述/Meta 分析等场景提供精确的结构化表格数据
核心原则: 引擎对使用者透明 — 提交 PDF返回结构化表格无需关心底层实现
当前状态: MinerU Cloud API (VLM) 已接入并完成测试,其他引擎待逐步评测


1. 业务背景

1.1 核心需求

ASL 智能文献模块的全文复筛环节,需要从医学 PDF 文献中精确提取数据表格:

  • 系统综述 (Systematic Review): 基线特征表、结局指标表、不良事件表
  • Meta 分析: 效应值、置信区间、样本量等关键数值
  • 数据核验: 数值必须与原文 100% 一致,不容许任何精度损失

1.2 为什么独立建设

当前文档处理引擎基于 pymupdf4llm,定位是 PDF → Markdown 全文文本转换,在表格提取场景中存在严重缺陷:

问题 实测数据
8 篇 PDF 仅 1 篇输出结构化表格 表格检出率 12.5%
其余 7 篇表格退化为纯文本 行列结构完全丢失
不支持合并单元格 医学表格大量使用 rowspan/colspan

结论:全文文本提取和结构化表格提取是两个不同的能力,需要分别建设。


2. 引擎架构设计

2.1 核心理念

使用者不需要关心底层用了什么技术,只需要:提交 PDF → 获取结构化表格。

底层引擎可以是 MinerU、Qwen-VL、PaddleOCR、Docling 或任意其他方案,通过统一接口抽象,实现热切换和渐进升级。

2.2 统一架构

┌─────────────────────────────────────────────────────────────┐
│                  业务层 (使用者)                               │
│  ASL 全文复筛 / 系统综述数据提取 / Meta 分析                   │
│                                                             │
│  const tables = await tableEngine.extract(pdfBuffer);       │
│  // 只关心输入 PDF 和输出 tables不关心底层引擎               │
└───────────────────────────┬─────────────────────────────────┘
                            │
┌───────────────────────────▼─────────────────────────────────┐
│              PDF 表格提取引擎 (统一抽象层)                     │
│                                                             │
│  interface TableExtractionEngine {                           │
│    extract(pdf: Buffer): Promise<ExtractedTable[]>           │
│    extractFromUrl(url: string): Promise<ExtractedTable[]>    │
│  }                                                          │
│                                                             │
│  统一输出ExtractedTable[]                                   │
│  ┌──────────────────────────────────────────────────────┐   │
│  │ { title, headers, rows, mergedCells, footnotes,      │   │
│  │   pageNumber, confidence, rawHtml }                   │   │
│  └──────────────────────────────────────────────────────┘   │
│                                                             │
├─────────────────────────────────────────────────────────────┤
│                    引擎适配器 (可插拔)                         │
│                                                             │
│  ┌──────────────┐ ┌──────────────┐ ┌──────────────┐        │
│  │   MinerU     │ │  Qwen3-VL    │ │ PaddleOCR-VL │        │
│  │  Cloud API   │ │  多模态 LLM   │ │   百度 OCR    │        │
│  │  (VLM)       │ │              │ │              │        │
│  │  ✅ 已接入    │ │  📋 待评测    │ │  📋 待评测    │        │
│  └──────────────┘ └──────────────┘ └──────────────┘        │
│                                                             │
│  ┌──────────────┐ ┌──────────────┐ ┌──────────────┐        │
│  │ Qwen-OCR +   │ │   Docling    │ │  DeepSeek    │        │
│  │ Qwen-Long    │ │   (IBM)      │ │   LLM        │        │
│  │              │ │              │ │              │        │
│  │  📋 待评测    │ │  📋 待评测    │ │  ✅ 已测试    │        │
│  └──────────────┘ └──────────────┘ └──────────────┘        │
└─────────────────────────────────────────────────────────────┘

2.3 统一输出格式

无论底层使用哪个引擎,输出都遵循统一的 ExtractedTable 结构:

interface ExtractedTable {
  /** 表格标题 (如 "Table 1 Baseline characteristics") */
  title: string;
  /** 表头行 */
  headers: string[];
  /** 数据行 (二维数组) */
  rows: string[][];
  /** 合并单元格信息 */
  mergedCells?: MergedCell[];
  /** 脚注 */
  footnotes?: string[];
  /** 所在 PDF 页码 */
  pageNumber?: number;
  /** 引擎自信度 (0-1) */
  confidence?: number;
  /** 原始 HTML (供前端渲染或调试) */
  rawHtml?: string;
  /** 原始 Markdown (备选格式) */
  rawMarkdown?: string;
}

interface MergedCell {
  row: number;
  col: number;
  rowSpan: number;
  colSpan: number;
}

3. 候选引擎全景

3.1 引擎候选清单

引擎 类型 特点 成本 状态
MinerU Cloud API VLM 云端 表格结构最完整rowspan/colspan 支持 2000 页/天免费 已接入
Qwen3-VL 多模态 LLM 多模态理解最强,复杂表格语义识别好 按 token 计费 📋 待评测
Qwen-OCR + Qwen-Long OCR + LLM 组合 成本最低、功能最全的组合方案 极低 📋 待评测
百度 PaddleOCR-VL 1.5 VL OCR 医学场景案例多,准确率高,免费额度最多 官方免费额度多 📋 待评测
Docling (IBM) 本地部署 MIT 开源TableFormer 模型,可完全离线 免费 (本地部署) 📋 待评测
DeepSeek LLM 文本 LLM 从原始文本重构表格Markdown 输出 ~0.14 元/万 token 已测试

3.2 推荐分类

最佳性价比组合:

  1. Qwen-OCR + Qwen-Long — 成本最低,功能最全
  2. 百度 PaddleOCR-VL — 官方免费额度最多,技术最成熟

医学文献表格提取最佳选择:

  1. Qwen3-VL — 多模态理解最强,支持复杂表格
  2. 百度 PaddleOCR-VL 1.5 — 医学场景案例多,准确率高

数据合规 / 离线场景:

  1. Docling (IBM) — MIT 开源,完全本地部署

3.3 评测计划

按优先级逐步评测,使用同一组 8 篇医学 PDF 文献作为基准:

阶段 引擎 优先级 评测重点
已完成 MinerU Cloud API 作为 baseline
已完成 DeepSeek LLM 文本 LLM 方案的上限
P1 待测 Qwen3-VL 多模态 vs MinerU VLM 的表格精度
P1 待测 PaddleOCR-VL 1.5 免费额度 + 医学场景准确率
P2 待测 Qwen-OCR + Qwen-Long 验证最低成本方案的可行性
P2 待测 Docling 离线方案,评估部署成本

4. 已完成测试MinerU vs pymupdf4llm vs DeepSeek

4.1 测试概要

  • 测试对象: 8 篇真实医学 PDF 文献(含 1 篇中文),涵盖 RCT、队列研究
  • 测试方法: pymupdf4llm (本地) / MinerU Cloud API (VLM) / DeepSeek LLM (deepseek-chat)

4.2 核心结果

指标 pymupdf4llm MinerU API (VLM) DeepSeek LLM
结构化表格检出 3 个 (12.5%) 28 个 (100%) 24 个 (85%)
输出格式 纯文本 HTML <table> Markdown |..|
合并单元格 rowspan/colspan ⚠️ 文字描述
数值精度 原始 100% 保真 ⚠️ 可能翻译
总耗时 (8 篇) 16.1s ~50s 234.6s
综合评分 2.7/5 4.6/5 3.4/5

4.3 逐文件对比

# 文件 pymupdf4llm MinerU API DeepSeek LLM
1 S2589537025 (EClinMed) 0 表格 1 HTML 1 MD
2 Dongen 2003 0 结构化 4 HTML 3 MD
3 Ginkgo+Donepezil 0 结构化 3 HTML 3 MD
4 Ginkgo Community 0 结构化 6 HTML 6 MD
5 Ginkgo NPS 3 MD 3 HTML 3 MD
6 Herrschaft 2012 0 结构化 3 HTML 3 MD
7 Ihl 2011 0 结构化 3 HTML 3 MD
8 NIRS 队列研究 (中文) 0 结构化 5 HTML 2 MD

4.4 质量深度分析 (Herrschaft 2012 — Table 1)

原始表格: 5 列、18 行,"Type of dementia" 合并 3 行。

特征 pymupdf4llm MinerU API DeepSeek LLM
列数正确 无结构 5 列 4 列
行数完整 数据在 18 行 18 行
合并单元格 rowspan=3 ⚠️ 加粗标注
数值保真 含 ± ⚠️ 翻译行名

4.5 综合评分

维度 pymupdf4llm MinerU API DeepSeek LLM
表格检测率 1/5 5/5 4/5
结构保真度 1/5 5/5 4/5
数值精度 5/5 5/5 4/5
速度 5/5 3/5 2/5
合并单元格 1/5 5/5 3/5
中文支持 3/5 5/5 4/5
成本 5/5 4/5 3/5
综合 2.7 4.6 3.4

5. 技术实现设计

5.1 接口抽象

// common/document/tableExtraction/types.ts

/** 统一引擎接口 — 所有适配器必须实现 */
interface ITableExtractionEngine {
  readonly name: string;
  extract(pdf: Buffer, options?: ExtractionOptions): Promise<ExtractionResult>;
}

interface ExtractionOptions {
  language?: 'ch' | 'en' | 'auto';
  /** 指定页码范围,如 "1-5,8" */
  pageRanges?: string;
  /** 是否启用公式识别 */
  enableFormula?: boolean;
}

interface ExtractionResult {
  tables: ExtractedTable[];
  /** 引擎名称 */
  engine: string;
  /** 处理耗时 (ms) */
  duration: number;
  /** PDF 总页数 */
  pageCount: number;
  /** 原始 Markdown 全文 (可选) */
  fullMarkdown?: string;
}

5.2 引擎管理器

// common/document/tableExtraction/engineManager.ts

class TableExtractionEngineManager {
  private engines: Map<string, ITableExtractionEngine> = new Map();
  private defaultEngine: string = 'mineru';

  /** 注册引擎适配器 */
  register(engine: ITableExtractionEngine): void {
    this.engines.set(engine.name, engine);
  }

  /** 设置默认引擎 */
  setDefault(name: string): void {
    this.defaultEngine = name;
  }

  /** 提取表格 — 使用者唯一入口 */
  async extract(
    pdf: Buffer,
    options?: ExtractionOptions & { engine?: string }
  ): Promise<ExtractionResult> {
    const engineName = options?.engine || this.defaultEngine;
    const engine = this.engines.get(engineName);
    if (!engine) throw new Error(`Engine not found: ${engineName}`);
    return engine.extract(pdf, options);
  }
}

5.3 MinerU 适配器 (第一个实现)

// common/document/tableExtraction/engines/mineruEngine.ts

class MinerUEngine implements ITableExtractionEngine {
  readonly name = 'mineru';

  async extract(pdf: Buffer, options?: ExtractionOptions): Promise<ExtractionResult> {
    // 1. 请求上传 URL
    // 2. 上传 PDF
    // 3. 轮询等待解析完成
    // 4. 下载结果 ZIP
    // 5. 解析 HTML 表格 → ExtractedTable[]
    // ...
  }
}

5.4 未来适配器 (预留接口)

// 后续逐步实现
class Qwen3VLEngine implements ITableExtractionEngine { ... }
class PaddleOCRVLEngine implements ITableExtractionEngine { ... }
class QwenOCRLongEngine implements ITableExtractionEngine { ... }
class DoclingEngine implements ITableExtractionEngine { ... }

5.5 文件规划

backend/src/common/document/tableExtraction/
├── types.ts                    # 统一类型定义
├── engineManager.ts            # 引擎管理器 (统一入口)
├── htmlTableParser.ts          # HTML <table> → ExtractedTable 转换
├── engines/
│   ├── mineruEngine.ts         # MinerU Cloud API 适配器 ✅ 首个实现
│   ├── qwen3vlEngine.ts        # Qwen3-VL 适配器 (待实现)
│   ├── paddleOcrEngine.ts      # PaddleOCR-VL 适配器 (待实现)
│   ├── qwenOcrLongEngine.ts    # Qwen-OCR + Qwen-Long 适配器 (待实现)
│   ├── doclingEngine.ts        # Docling 适配器 (待实现)
│   └── deepseekEngine.ts       # DeepSeek LLM 适配器 (已测试,可选)
└── index.ts                    # 导出统一入口

6. 使用方式

6.1 业务层调用 (使用者视角)

import { getTableExtractionEngine } from '@/common/document/tableExtraction';

// 使用者不需要知道底层是 MinerU 还是 Qwen-VL
const engine = getTableExtractionEngine();
const result = await engine.extract(pdfBuffer, { language: 'auto' });

for (const table of result.tables) {
  console.log(`${table.title}: ${table.rows.length}× ${table.headers.length} 列`);
  // 直接使用结构化数据
}

6.2 管理员切换引擎

# backend/.env — 切换默认引擎
TABLE_EXTRACTION_ENGINE=mineru    # 当前默认
# TABLE_EXTRACTION_ENGINE=qwen3vl   # 未来切换
# TABLE_EXTRACTION_ENGINE=paddle    # 未来切换

# MinerU 配置
MINERU_API_TOKEN=your_token
MINERU_API_BASE=https://mineru.net/api/v4
MINERU_MODEL_VERSION=vlm

6.3 场景决策矩阵

场景 推荐引擎 说明
ASL 标题摘要初筛 pymupdf4llm (文本引擎) 不需要表格,只需全文文本
ASL 全文复筛 — 表格提取 PDF 表格提取引擎 自动选择最优引擎
系统综述数据提取 PDF 表格提取引擎 需要精确数值表格
Meta 分析效应值识别 表格引擎 + LLM 语义理解 提取 → 理解两步走
PKB 知识库入库 pymupdf4llm (文本引擎) 只需 Markdown 文本

7. MinerU Cloud API 接入指南 (当前默认引擎)

7.1 API 概览

项目 说明
服务商 OpenDataLab (上海人工智能实验室)
API 地址 https://mineru.net/api/v4
认证方式 Bearer Token
模型版本 vlm (视觉语言模型,推荐)
免费额度 2000 页/天
文件限制 单文件 ≤ 200MB≤ 600 页

7.2 核心流程

PDF 文件
  │
  ▼
Step 1: POST /file-urls/batch     → 获取预签名上传 URL + batch_id
  │
  ▼
Step 2: PUT {pre-signed URL}      → 上传 PDF 文件
  │
  ▼
Step 3: 云端 VLM 模型自动解析      → 识别表格/文本/图片
  │
  ▼
Step 4: GET /extract-results/batch/{batch_id}  → 轮询状态
  │
  ▼
Step 5: 下载结果 ZIP               → 含 .md (内嵌 HTML 表格) + .json + images

7.3 代码示例

import requests, time, zipfile, io

TOKEN = "your_token"
API = "https://mineru.net/api/v4"
headers = {"Authorization": f"Bearer {TOKEN}", "Content-Type": "application/json"}

# Step 1: 请求上传 URL
resp = requests.post(f"{API}/file-urls/batch", headers=headers, json={
    "files": [{"name": "paper.pdf", "data_id": "paper1"}],
    "enable_table": True,
    "model_version": "vlm",
})
batch_id = resp.json()["data"]["batch_id"]
upload_url = resp.json()["data"]["file_urls"][0]

# Step 2: 上传文件
with open("paper.pdf", "rb") as f:
    requests.put(upload_url, data=f)

# Step 3-4: 轮询等待
while True:
    time.sleep(10)
    r = requests.get(f"{API}/extract-results/batch/{batch_id}", headers=headers)
    results = r.json()["data"]["extract_result"]
    if all(x["state"] in ("done", "failed") for x in results):
        break

# Step 5: 下载解析
for result in results:
    if result["state"] == "done":
        zr = requests.get(result["full_zip_url"])
        with zipfile.ZipFile(io.BytesIO(zr.content)) as zf:
            for name in zf.namelist():
                if name.endswith('.md'):
                    md = zf.read(name).decode('utf-8')
                    # md 中包含 HTML <table> 格式的表格

7.4 输出格式

MinerU 的表格以 HTML <table> 嵌入 Markdown 中,完整保留合并单元格:

<table>
  <tr><td rowspan="3">Type of dementia</td><td>Probable AD</td><td>107 (54)</td></tr>
  <tr><td>Possible AD with CVD</td><td>73 (36)</td></tr>
  <tr><td>Probable VaD</td><td>20 (10)</td></tr>
</table>

8. 成本估算

8.1 MinerU (当前)

场景 文献数 平均页数 总页数 天数 费用
小型综述 20 篇 10 页 200 页 1 天 免费
中型综述 100 篇 10 页 1000 页 1 天 免费
大型综述 500 篇 10 页 5000 页 3 天 免费

8.2 各引擎预估成本对比

引擎 免费额度 超出后单价 500 篇 (5000 页) 预估
MinerU 2000 页/天 待确认 免费 (分 3 天)
Qwen-OCR + Qwen-Long 按 token ~0.004 元/千 token 约 10-20 元
PaddleOCR-VL 官方免费额度多 极低 接近免费
Qwen3-VL 按 token ~0.02 元/千 token 约 50-100 元
Docling 本地部署 仅算力成本 免费
DeepSeek LLM 按 token ~0.14 元/万 token 约 30-50 元

9. 测试脚本

9.1 已有脚本

脚本 路径 功能
三方对比测试 extraction_service/test_pdf_table_extraction.py pymupdf4llm / MinerU / DeepSeek 完整对比
结果分析 extraction_service/analyze_table_results.py 从提取结果生成对比报告

9.2 运行方法

cd AIclinicalresearch

# 运行全部三个方法
python extraction_service/test_pdf_table_extraction.py

# 单独运行某个方法
python extraction_service/test_pdf_table_extraction.py pymupdf
python extraction_service/test_pdf_table_extraction.py mineru
python extraction_service/test_pdf_table_extraction.py deepseek

# 生成对比报告
python extraction_service/analyze_table_results.py

9.3 测试输出

extraction_service/test_output/pdf_table_extraction/
├── pymupdf4llm/          # pymupdf4llm 提取结果
├── mineru/                # MinerU 提取结果
├── deepseek/              # DeepSeek 提取结果
├── raw_results.json       # 原始测试数据
└── comparison_report.md   # 综合对比报告

9.4 后续评测扩展

新引擎的评测脚本将遵循同样的结构,添加到 test_pdf_table_extraction.py 中:

python extraction_service/test_pdf_table_extraction.py qwen3vl
python extraction_service/test_pdf_table_extraction.py paddle
python extraction_service/test_pdf_table_extraction.py qwenocr

10. 路线图

Phase 1: 基础框架 + MinerU (当前)

  • MinerU Cloud API 对比测试
  • DeepSeek LLM 对比测试
  • 实现统一接口 ITableExtractionEngine
  • 实现 MinerUEngine 适配器
  • 实现 engineManager 引擎管理器
  • ASL 全文复筛集成

Phase 2: 多引擎评测

  • Qwen3-VL 评测 + 适配器
  • PaddleOCR-VL 1.5 评测 + 适配器
  • 同一基准集横向对比报告
  • 确定最优引擎组合策略

Phase 3: 性价比优化

  • Qwen-OCR + Qwen-Long 评测 (最低成本方案)
  • Docling 本地部署评测 (离线方案)
  • 引擎路由策略 (按文档复杂度自动选择引擎)

Phase 4: 生产加固

  • 提取结果缓存 (避免重复解析)
  • 批量提取队列 (pg-boss 异步任务)
  • 质量监控 (空表格/异常值检测)
  • 引擎降级策略 (主引擎不可用时自动切换)

11. 相关文档


维护人: 技术架构师
设计原则: 引擎对使用者透明,底层可热切换,以测试数据驱动选型