# MinerU 接入 FairScan PC Server 对接文档 > 本文档记录 MinerU 在本机的环境信息、API 用法,以及如何将其接入 FairScan PC 服务器, > 替换当前的模拟处理逻辑。 --- ## 1. 本机环境信息 > **统一环境**:MinerU 和 OCRmyPDF 共用一个 conda 环境 `MinerU`, > PC 服务器始终通过 `conda activate MinerU` 启动。 | 项目 | 值 | |------|-----| | MinerU 源码路径 | `F:/datasets_rm/MinerU/` | | **已安装版本** | **3.0.9** | | **最新版本** | **3.2.2**(446 commits 差距) | | Conda 环境 | `D:/ProgramData/miniconda3/envs/MinerU/` | | Python | 3.10.20 | | PyTorch | 2.6.0+cu124 | | CUDA | 12.4 | | GPU | NVIDIA GeForce RTX 4060 Laptop GPU (8 GB VRAM) | | Transformers | 4.57.6 | | onnxruntime | 1.23.2 | | Pipeline 模型 | ✅ 已下载(HF cache: `C:/Users/32892/.cache/huggingface/hub/models--opendatalab--PDF-Extract-Kit-1.0`) | | VLM 模型 | ✅ 已下载(HF cache: `C:/Users/32892/.cache/huggingface/hub/models--opendatalab--MinerU2.5-2509-1.2B`) | | HF Hub 离线模式 | ✅ `HF_HUB_OFFLINE=1`(main.py 启动时设置) | | OCRmyPDF | ✅ v15.4.4 已安装(源码 `F:/datasets_rm/ocRmypdf`,同一 conda 环境) | | Tesseract | ❌ 待安装(OCRmyPDF 必需依赖) | --- ## 2. 前置准备:升级 MinerU(强烈建议) 当前安装的 3.0.9 与最新 3.2.2 差距较大(446 commits),主要改进包括: - **`aio_do_parse()` 异步接口** — 可直接 await 调用,不阻塞 FastAPI 事件循环 - **并发锁优化** — Layout/MFR/OCR 使用独立推理锁,减少 GPU 争用 - **PDF 渲染修复** — 大量 PDFium 资源泄漏和崩溃修复 - **图像分析** — 新增 `image_analysis` 参数 - **Client-side 输出生成** — 新增 `client_side_output_generation` 选项 ### 2.1 拉取最新代码 ```bash cd F:/datasets_rm/MinerU git checkout main git pull origin main git checkout mineru-3.2.2-released ``` ### 2.2 更新安装 ```bash conda activate MinerU pip install -e . ``` ### 2.3 验证 ```bash # 检查版本 python -c "from mineru.version import __version__; print(__version__)" # 应为 3.2.2 # 验证模型可用 python -c " from mineru.utils.models_download_utils import auto_download_and_get_model_root_path print('Pipeline:', auto_download_and_get_model_root_path('models/README.md', 'pipeline')) print('VLM:', auto_download_and_get_model_root_path('/', 'vlm')) " ``` > **注意**:如果之后需要用 `model_source=local` 指定自定义模型路径,才需要创建 `~/.mineru.json` 配置文件。默认的 HuggingFace 缓存模式不需要。 --- ## 3. MinerU 编程接口 ### 3.1 核心函数:`do_parse` ```python from mineru.cli.common import do_parse, read_fn from mineru.utils.enum_class import MakeMode from pathlib import Path def do_parse( output_dir: str, # 输出目录路径 pdf_file_names: list[str], # PDF 文件名列表(不含扩展名) pdf_bytes_list: list[bytes], # PDF 文件字节列表 p_lang_list: list[str], # 语言列表("ch", "en", "japan" 等) backend: str = "pipeline", # "pipeline" | "vlm-auto-engine" | "hybrid-auto-engine" parse_method: str = "auto", # "auto" | "txt" | "ocr" formula_enable: bool = True, table_enable: bool = True, server_url: str | None = None, # 远程服务器 URL(仅 http-client 后端) f_dump_md: bool = True, # 输出 .md 文件 f_dump_middle_json: bool = True, # 输出 _middle.json f_dump_model_output: bool = True, # 输出 _model.json f_dump_orig_pdf: bool = True, # 输出原始 PDF 副本 f_dump_content_list: bool = True, # 输出 _content_list.json f_draw_layout_bbox: bool = True, # 输出带布局框的 PDF f_draw_span_bbox: bool = True, # 输出带 span 框的 PDF f_make_md_mode: MakeMode = MakeMode.MM_MD, # Markdown 模式 start_page_id: int = 0, end_page_id: int | None = None, # None = 所有页 **kwargs, ) ``` ### 3.2 `read_fn` 辅助函数 ```python from mineru.cli.common import read_fn # 读取 PDF 文件为 bytes pdf_bytes = read_fn("F:/path/to/doc.pdf") # 也支持图片文件(自动转为 PDF bytes) png_bytes = read_fn("scan.png") ``` ### 3.3 输出目录结构 Pipeline 后端(`backend="pipeline"`)输出: ``` {output_dir}/ {pdf_name}/ auto/ # parse_method="auto" {pdf_name}.md # ★ Markdown 输出(主要产物) {pdf_name}_middle.json # 中间解析结果 {pdf_name}_model.json # 模型原始输出 {pdf_name}_content_list.json {pdf_name}_origin.pdf # 原始 PDF 副本 {pdf_name}_layout.pdf # 布局可视化 {pdf_name}_span.pdf # Span 可视化 images/ # 提取的图片 ``` ### 3.4 语言代码 | 代码 | 语言 | |------|------| | `ch` | 简体中文 | | `ch_server` | 中文服务器版(较快) | | `ch_lite` | 中文轻量版 | | `en` | 英语 | | `japan` | 日语 | | `korean` | 韩语 | | `chinese_cht` | 繁体中文 | --- ## 4. 接入方案 ### 方案 A:直接异步 API 调用(强烈推荐,需 v3.2.2) 升级到 v3.2.2 后,可以直接使用 `aio_do_parse()` — MinerU 原生异步接口,无需 `asyncio.to_thread()`。 **优点**: - **原生 async**,直接 await,不阻塞 FastAPI 事件循环 - 最简单,不需要进程间通信 - 可直接获取输出文件路径 **前提**: - FairScan PC 服务器在 MinerU conda 环境中运行 - `F:/datasets_rm/MinerU` 已通过 `pip install -e .` 安装 **实现思路**: ```python # ---- pc-server/main.py 新增代码 ---- from pathlib import Path from mineru.cli.common import aio_do_parse, read_fn async def real_mineru_processing(task_id: str): """使用 MinerU 异步接口真实处理 PDF""" task = tasks_db.get(task_id) if task is None: return file_name = task.get("fileName", "document.pdf") base_name = Path(file_name).stem upload_path = Path(task["uploadPath"]) process_type = task.get("processType", "ocrpdf") lang = task.get("options", {}).get("lang", "ch") task["status"] = "processing" task["progress"] = 10 task["message"] = "MinerU processing started..." output_dir = TASKS_DIR / task_id output_dir.mkdir(exist_ok=True) pdf_bytes = read_fn(upload_path) try: if process_type == "markdown": await aio_do_parse( output_dir=str(output_dir), pdf_file_names=[base_name], pdf_bytes_list=[pdf_bytes], p_lang_list=[lang], backend="pipeline", f_dump_md=True, f_dump_middle_json=False, f_dump_model_output=False, f_dump_orig_pdf=False, f_dump_content_list=False, f_draw_layout_bbox=False, f_draw_span_bbox=False, ) md_path = output_dir / base_name / "auto" / f"{base_name}.md" if md_path.exists(): art_id = str(uuid.uuid4()) artifacts_db[task_id] = [{ "artifactId": art_id, "fileName": f"{base_name}.md", "fileSize": md_path.stat().st_size, "fileType": "md", "filePath": str(md_path), }] artifacts_map[art_id] = artifacts_db[task_id][0] task.update(status="completed", progress=100, message="MinerU Markdown completed") return elif process_type == "ocrpdf": await aio_do_parse( output_dir=str(output_dir), pdf_file_names=[base_name], pdf_bytes_list=[pdf_bytes], p_lang_list=[lang], backend="pipeline", f_dump_md=False, f_dump_middle_json=False, f_dump_model_output=False, f_dump_orig_pdf=False, f_dump_content_list=False, f_draw_layout_bbox=True, f_draw_span_bbox=False, ) layout_pdf = output_dir / base_name / "auto" / f"{base_name}_layout.pdf" if layout_pdf.exists(): art_id = str(uuid.uuid4()) artifacts_db[task_id] = [{ "artifactId": art_id, "fileName": f"{base_name}_ocr.pdf", "fileSize": layout_pdf.stat().st_size, "fileType": "pdf", "filePath": str(layout_pdf), }] artifacts_map[art_id] = artifacts_db[task_id][0] task.update(status="completed", progress=100, message="OCR PDF completed") return task["status"] = "failed" task["message"] = "MinerU did not produce output" except Exception as e: task["status"] = "failed" task["message"] = f"MinerU error: {str(e)}" logger.error(f"MinerU task {task_id} failed: {e}") ``` ### 方案 B:子进程调用(备选) 通过 `subprocess` 调用 `mineru` CLI: ```python import subprocess import asyncio async def mineru_subprocess(task_id: str): task = tasks_db[task_id] upload_path = task["uploadPath"] output_dir = TASKS_DIR / task_id cmd = [ r"D:/ProgramData/miniconda3/envs/MinerU/python.exe", "-m", "mineru.cli.client", "-p", str(upload_path), "-o", str(output_dir), "-b", "pipeline", "-l", "ch", ] proc = await asyncio.create_subprocess_exec( *cmd, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE, ) # 轮询进度(可选:监控 stdout 中的进度信息) while True: line = await proc.stdout.readline() if not line: break # 解析进度... returncode = await proc.wait() if returncode == 0: task["status"] = "completed" else: task["status"] = "failed" ``` **优点**:进程隔离,MinerU 崩溃不影响 FairScan 服务。 **缺点**:进度监控困难,需要 IPC。 ### 方案 C:MinerU FastAPI 服务 运行 MinerU 自带的 FastAPI 服务 `mineru-api` 作为微服务,FairScan 通过 HTTP 调用。 这一方案与 pc-api-spec.md 中对原子服务的建议一致,但实现复杂度更高。 --- ## 5. 与 pc-api-spec.md 的对应关系 根据接口规范,两种 `processType` 与 MinerU 的映射: | processType | MinerU 后端 | 输出文件 | 文件类型 | |-------------|-----------|---------|---------| | `markdown` | `backend="pipeline"` | `{name}.md` | `text/markdown` | | `ocrpdf` | `backend="pipeline"` + `f_draw_layout_bbox=True` | `{name}_layout.pdf` | `application/pdf` | 两种类型共用同一个 MinerU `do_parse` 调用,仅输出选项不同。 --- ## 6. 接入步骤建议 ### Step 1:升级 MinerU 到最新版 ```bash cd F:/datasets_rm/MinerU git checkout main && git pull origin main git checkout mineru-3.2.2-released conda activate MinerU pip install -e . ``` 验证: ```bash python -c "from mineru.cli.common import aio_do_parse; print('OK')" ``` ### Step 2:切换 PC 服务器运行环境 ```bash conda activate MinerU cd E:/race_save/FairScan_cyy/FairScan/pc-server python main.py ``` ### Step 3:替换 `simulate_processing` 为真实 MinerU 调用 在 `main.py` 中将 `simulate_processing` 替换为 `real_mineru_processing`(参考方案 A 的实现)。 ### Step 4:端到端测试 1. 用小 PDF(1-2 页)先用 `parse_method="txt"` 测试(速度快) 2. 确认无误后切换为 `parse_method="auto"`(完整 OCR+公式+表格) 3. 测试处理完成后产物下载 --- ## 7. 注意事项 | 项目 | 说明 | |------|------| | **GPU 显存** | RTX 4060 有 8GB VRAM。pipeline 后端约需 4-6GB,VLM 后端约需 6-8GB。建议用 pipeline 后端。 | | **处理速度** | 普通 A4 PDF,pipeline 后端约 3-8 秒/页(取决于内容复杂度)。 | | **语言** | 默认传 `ch`(简体中文)。FairScan 可扩展语言选择功能。 | | **页数限制** | 可用 `start_page_id` / `end_page_id` 限制处理范围。 | | **大文件** | PDF > 100 页建议分批处理。 | | **超时** | 单次处理时间与页数成正比,不要设置过短的 HTTP 超时。 | | **锁模型** | `do_parse` 不是线程安全的。FastAPI 的 `async` 端点应在线程池中调用,避免阻塞事件循环。 | | **错误处理** | `do_parse` 出错会抛出异常,需捕获并设置 `task["status"] = "failed"`。 | --- ## 8. 关键参考文件 | 文件 | 说明 | |------|------| | `F:/datasets_rm/MinerU/mineru/cli/common.py` | `do_parse()` 主入口 | | `F:/datasets_rm/MinerU/mineru/cli/client.py` | CLI 参数定义 | | `F:/datasets_rm/MinerU/mineru/cli/output_paths.py` | 输出路径解析 | | `F:/datasets_rm/MinerU/mineru/utils/config_reader.py` | 配置读取 | | `F:/datasets_rm/MinerU/mineru/utils/enum_class.py` | 枚举类型定义 | | `F:/datasets_rm/MinerU/mineru.template.json` | 配置文件模板 | | `E:/race_save/FairScan_cyy/FairScan/pc-server/main.py` | FairScan PC 服务器(需修改) | | `E:/race_save/FairScan_cyy/FairScan/requirements/pc-api-spec.md` | API 接口规范 |