opendatalab 出品的 PDF → Markdown/JSON 解析引擎,
GitHub 59.5k stars。本文基于 master 分支(v3.0.9)
浅克隆源码逐行通读,覆盖 VLM / Pipeline / Hybrid / Office 四后端完整链路。
MinerU 3.0 是 目前开源 PDF 解析最完整的生产级方案。它不是靠单一大模型硬抗, 而是把 VLM(Qwen2-VL-2B 微调)+ 传统级联 Pipeline + Hybrid 混合 三条路线打包成可配置的后端,再叠加 Office/DOCX 原生解析。 代码质量成熟,工程化到位(FastAPI 异步任务、Gradio UI、Docker 编排一应俱全), 值得作为子能力接入任何 Agent / RAG 系统。
一个包、三大后端、七个 CLI、四种推理框架 —— MinerU 把 PDF 解析做成了瑞士军刀。
master 分支当前版本。3.0 的核心升级:Office 原生解析、异步任务端点、Pipeline v1.5 benchmark 达 86.2。
mineru/version.py:1Python 版本门槛抬得不低,用上了新语法(list[str] 原生泛型、str | None 联合类型)。
mineru / mineru-api / mineru-gradio / mineru-router / mineru-vllm-server / mineru-lmdeploy-server / mineru-models-download。
| Extra | 触发条件 | 关键依赖 | 场景 |
|---|---|---|---|
[vlm] | 默认 VLM 后端 | torch≥2.6 transformers≥4.57.3 | Qwen2-VL 直接加载 |
[vllm] | 高吞吐 VLM | vllm≥0.10.1.1 | 多 PDF 并发 |
[lmdeploy] | 国产加速 | lmdeploy≥0.10.2 | 昇腾/MACA |
[mlx] | macOS ARM | mlx-vlm≥0.3.3 | M1/M2/M3 原生 |
[pipeline] | 传统级联 | onnxruntime paddlepaddle* | 无 GPU / CPU-only |
MinerU 不是一条管线,而是四条 —— 通过 backend 参数切换,共享同一套中间 JSON 格式。
单一 VLM 快但对 VRAM 有门槛;单一 Pipeline 精但慢且计算量大。给用户留出选择权是工程上更诚实的做法 —— 你有 A100 就跑 VLM,你只有 CPU 就跑 Pipeline。
官方宣传的 "1.2B 参数 VLM 超越 Gemini 2.5 Pro",读源码后发现底下是 Qwen2-VL-2B 微调。
# mineru/backend/vlm/vlm_analyze.py:80-102 from transformers import Qwen2VLForConditionalGeneration, AutoProcessor model = Qwen2VLForConditionalGeneration.from_pretrained( model_path, device_map={"": device}, dtype="auto" ) processor = AutoProcessor.from_pretrained(model_path, use_fast=True)
基座是阿里开源的 Qwen2-VL-2B。MinerU 用 6550 万页 PDF 做领域微调,把通用 VLM 调成了文档专家 —— 这是"小模型打败大模型"的典型范例,不靠参数量,靠高质量领域数据。
最兼容,本地直接加载 HF 权重。默认选项。
vlm_analyze.py:79-104高吞吐,支持 async LLM + 自定义 logits 处理器。
vlm_analyze.py:118-160国产加速(TurboMind/PyTorch/MACA),支持昇腾。
vlm_analyze.py:162+macOS 13.5+ Apple Silicon 原生,M 系列芯片首选。
vlm_analyze.py:105-113用 pypdfium2 把每页渲染成 PNG,分辨率由 --dpi 控制。
每页图像 + prompt 送入 Qwen2-VL,产出结构化 JSON(bbox + type + content)。
ModelSingleton @ vlm_analyze.py:40-50单页结果通过 append_page_blocks_to_middle_json() 合并到全文档结构。
按块类型渲染:公式用 $...$、表格直出、图像嵌路径。
# mineru/backend/vlm/utils.py:94-110 def set_default_batch_size() -> int: gpu_memory = get_vram(device) if gpu_memory >= 16: batch_size = 8 elif gpu_memory >= 8: batch_size = 4 else: batch_size = 1
没有 VLM 之前的老路,到现在依然是精度基准。四个独立模型串起来,每一步都能单独验证。
识别前先做 mask 处理,避免上一步的 bbox 污染下一步。
_apply_mask_boxes_to_image @ batch_analyze.py:63-85OCR 出来的空字符串块直接丢弃,减少后续处理噪声。
_prune_empty_ocr_text_blocks @ batch_analyze.py:96-110表格里可能嵌公式/图像,这一步单独拎出来做二次识别。
_extract_table_inline_objects @ batch_analyze.py:214-302300+ 行的主循环,协调四个模型的数据流。
__call__ @ batch_analyze.py:303-550# mineru/backend/pipeline/pipeline_magic_model.py:18-42 PP_DOCLAYOUT_V2_LABELS_TO_BLOCK_TYPES = { "image": BlockType.IMAGE, "table": BlockType.TABLE, "display_formula": BlockType.INTERLINE_EQUATION, "text": BlockType.TEXT, # ... } VISUAL_MAIN_TYPES = (BlockType.IMAGE, BlockType.TABLE, BlockType.CHART, BlockType.CODE) VISUAL_CHILD_TYPES = (BlockType.CAPTION, BlockType.FOOTNOTE)
LAYOUT=1 · MFR=16 · OCR_DET=8 —— 公式识别批量最大(小图多),布局检测串行(大图)。batch_analyze.py:35-47
Pipeline 给结构,VLM 补细节。表格和公式交给 VLM 二次识别,精度和速度都能接受。
# mineru/backend/hybrid/hybrid_analyze.py:1-150 def hybrid_analyze(pdf_bytes, lang_list, parse_method="auto", ...): # 1. 判断是否需要 OCR(行 50-58) _ocr_enable = ocr_classify(pdf_bytes, parse_method) # 2. 需要 OCR 则走 Pipeline OCR if _ocr_enable: ocr_res_list = ocr_det(...) # 3. 表格/公式等难块交给 VLM 二次验证 # 4. 最后融合中间 JSON
单 VLM 的失误主要在表格结构(合并单元格、嵌套表),而 Pipeline 的 SLANet 对结构化表格有很强的归纳偏置。Hybrid 让它们各补各的短板,在 OmniDocBench v1.5 上 Pipeline 后端能打到 86.2 分。
3.0 版本才加的原生 Office 解析,让 DOCX → Markdown 走纯文本路径,不再经 PDF 渲染。
# mineru/backend/office/docx_analyze.py:11-29 def office_docx_analyze(file_bytes, image_writer=None): file_stream = BytesIO(file_bytes) results = convert_binary(file_stream) # DocxConverter middle_json = result_to_middle_json(results, image_writer) return middle_json, results
读 DOCX 底层 XML 结构 —— 段落、表格、图片、样式。
把 DOCX 转 HTML 作为中间态,再统一映射到 Middle JSON。
图片不内嵌,通过写入器接口输出到外部存储(S3/本地)。
office_middle_json_mkcontent.py 有 1037 行之长,表格 HTML → Markdown 转换是已知精度瓶颈。复杂表格(跨行跨列、嵌套)建议仍走 PDF→VLM 路径。
四后端共享同一套 middle_json 数据结构,这是 MinerU 架构能解耦的关键。
# 统一中间表示 middle_json = { "meta_info": {...}, "doc_title": str, "doc_layout_result": [...], "para_blocks": [ { "type": BlockType, "blocks": [ { "type": BlockType, "lines": [ {"spans": [ { "type": ContentType, "content": str, "bbox": [x1, y1, x2, y2], } ]} ] } ] } ] }
# mineru/backend/vlm/vlm_middle_json_mkcontent.py:58-90 block_lang = detect_lang(block_text) if block_lang in {'zh', 'ja', 'ko'}: # CJK:换行不加空格 para_text += content else: # 西文:处理连字符 if is_hyphen_at_line_end(content): para_text += content[:-1] # 删掉行尾连字符 else: para_text += f"{content} "
README 里的 "109 languages" 对应的是 magika 库的语言识别能力(guess_suffix_or_lang.py:43-54),MinerU 自己只是用 CJK/西文两套换行策略加上 fast-langdetect 做块级识别。
delimiters = {
'display': {'left': '$$', 'right': '$$'}, # 行间公式
'inline': {'left': '$', 'right': '$'} # 行内公式
}
# 可通过 config.yaml 改成 \[...\] / \(...\)
从命令行到 REST API 再到 Gradio UI 和 Docker 编排,MinerU 把部署路径铺得很全。
mineru -p input.pdf -o output/ —— 最简单,单文件处理。
异步任务队列(3.0 新增),任务 24h 自动清理,支持 ZIP 打包响应。
mineru/cli/fast_api.py:130-149网页界面上传 PDF,实时进度展示,结果在线预览。
mineru/cli/gradio_app.py多阶段 Dockerfile + API/Router/Redis 编排,带模型下载预热。
docker/# mineru/cli/fast_api.py:130-149 @dataclass class ParseRequestOptions: files: list[UploadFile] lang_list: list[str] backend: str # "vlm" / "pipeline" / "hybrid-ocr" parse_method: str # "auto" / "txt" / "ocr" formula_enable: bool table_enable: bool server_url: Optional[str] # 远程 VLM 服务器 return_md: bool return_middle_json: bool return_model_output: bool return_content_list: bool return_images: bool response_format_zip: bool
默认走 ModelScope(国内快),可切换 HuggingFace。环境变量 MINERU_MODEL_SOURCE=modelscope,权重缓存在 ~/.mineru/models/vlm/Qwen2-VL-2B/。
读源码时让我眼前一亮的几处细节 —— 都是只有跑过生产才会做的事。
模型加载用 ModelSingleton._lock = threading.RLock() 保护,避免并发请求重复加载 2B 模型(重载一次就是 4GB VRAM)。
VLM 服务挂了自动 fallback 到 Pipeline,不让请求直接 500。这是生产级 API 的基本素养。
mineru/cli/common.py中日韩自动不加行尾空格,西文智能删连字符 —— 跨语言 Markdown 看起来才正常。
vlm_middle_json_mkcontent.py:58-90同步+异步两个版本并存,FastAPI 可直接用 aio_doc_analyze() 跑并发推理。
四个后端完全解耦,共享同一套 middle_json,渲染/测试/可视化都能复用,不用每加一个后端重写一套输出。
tests/unittest/test_e2e.py 跑真实 PDF,覆盖 pipeline/vlm/hybrid 三套后端 + txt/ocr 两种解析模式。
源码/issue 综合看下来几个容易翻车的点 —— 提前知道就能绕开。
默认 batch_size 按 VRAM 自动调,但多页并发 + 大图仍可能爆。手动传 --batch-size 1 最稳。
PDF 中混排全角标点,转 Markdown 可能重复。靠 full_to_half_exclude_marks() 做清洗,但仍有边界 case。
SLANet/UNet 对标准表格强,对跨行跨列、嵌套表格识别率下降明显。对策:用 Hybrid 让 VLM 二次验证。
hybrid_analyze.pyget_vram() 在 CPU 模式下返回系统 RAM,需要注意别误判。macOS + mlx 则绕过此检测。
看完源码,想清楚怎么把它接入现有业务 —— 这才是读源码的真正目的。
教材 PDF → Markdown 清洗,走 Pipeline 后端(精度优先),公式/表格保真好,能直接灌入题库。
参考报告的 PDF 摄取走 VLM 后端,快速提取结构化大纲,喂给下游 LLM 做改写。
作为子能力暴露 mineru-api REST 给 Agent 调用,DOCX/PDF 双入口,用 Hybrid 兜底。
轻量场景:直接 pip install -U "mineru[vlm]",本地 Qwen2-VL-2B 够用。
服务化:跑 mineru-api + Redis 队列,24h 自动清理,无需造轮子。
多项目共享:独立部署一个 mineru.kang-kang.com,通过 REST 给 Hermes/HiClaw/法考多项目复用,省去每个项目各自部署 2GB 模型。