本文适合谁看: 正在选型文档解析工具的 AI 工程师、RAG 系统开发者、Agent 工作流搭建者。
测试范围: 4 款主流工具在真实场景下的准确率、速度、格式支持、LLM 集成能力全面对比,含可复现代码。
为什么要重新比较这四个工具?
PDF 解析是 RAG 系统里最容易被低估的环节——大家花了大量时间调 embedding 模型、优化检索策略,却用 pdfplumber 几行代码把文档读进来,然后奇怪为什么 AI 回答老是出错。 问题往往就在解析层。 2025 年这个赛道发生了根本性的变化:VLM(视觉语言模型)开始主导高精度解析,纯规则方案在复杂文档面前逐渐力不从心。选错工具,下游的 chunk 质量、召回率、生成质量全部会受影响。 这篇文章对比的四个工具分别代表了不同的技术路线:
| 工具 | 技术路线 | 定位 |
|---|---|---|
| MinerU | VLM + Pipeline 双引擎 | 开源高精度,RAG/学术首选 |
| Unstructured | 规则 + 轻量模型 | 企业级 ETL,格式最全 |
| LlamaParse | LLM 语义解析 | LlamaIndex 生态,付费云服务 |
| PyMuPDF | 纯规则,速度极快 | 轻量提取,原生 PDF 文字首选 |
一、工具基本信息
| 维度 | MinerU | Unstructured | LlamaParse | PyMuPDF |
|---|---|---|---|---|
| 开源 / 商业 | ✅ 开源(Apache 2.0) | ⚠️ 核心开源,企业版收费 | ❌ 商业 SaaS(有免费额度) | ✅ 开源(AGPL / 商业双协议) |
| 技术路线 | VLM(1.2B)+ OCR Pipeline | 规则 + 轻量 CV 模型 | GPT-4o 语义解析 | 纯规则,无模型 |
| 主要输出格式 | Markdown、JSON | JSON(Element List) | Markdown、JSON、结构化 | 纯文本、HTML、JSON、图片 |
| 支持格式PDF、Word、PPT、图片、HTML、EPUB | PDF、Word、PPT、Excel、HTML、图片、邮件等 20+ | PDF、Word、PPT、图片、网页 | PDF、XPS、EPUB(只读文字) | |
| 本地部署 | ✅ 支持,推荐 GPU ≥8GB | ✅ 支持,Docker 部署 | ❌ 仅云端 API | ✅ 纯本地,无任何依赖 |
| OCR 语言数 | 109 种 | ~30 种 | 依赖 GPT-4o(支持主流语言) | ~40 种(需额外配置 Tesseract) |
| GitHub Stars | 30k+ | 10k+ | —(闭源产品) | 5k+ |
二、Benchmark 对比
2.1 综合解析质量(OmniDocBench)
OmniDocBench 是目前公认最全面的文档解析 benchmark,覆盖 9 种文档类型(学术论文、教材、财报、试卷、报纸、手写体等),由 CVPR 2025 收录。
说明:OmniDocBench 使用"编辑距离归一化误差"评分,分数越低越准确。下表转换为直觉友好的"准确率"形式(1 - 误差)。LlamaParse 和 PyMuPDF 无 OmniDocBench 官方结果,数据来自公开对比测试。
| 工具 | 综合准确率 | 文字提取 | 公式(LaTeX) | 表格(HTML) | 阅读顺序 |
|---|---|---|---|---|---|
| MinerU 2.5 | 90.7% | 93.2% | 87.4% | 85.6% | 94.1% |
| Unstructured(API) | ~68% | 75% | 22% | 61% | 70% |
| LlamaParse | ~76% | 82% | 65% | 74% | 78% |
| PyMuPDF(原生PDF) | ~82%* | 91% | ❌ 不支持 | 54% | 85% |
PyMuPDF 对原生可选文字 PDF 表现优秀,但遇到扫描件/图片型 PDF 准确率骤降至 <40%。 关键发现:
- MinerU 在公式和复杂表格上领先幅度最大,这是 VLM 引擎的核心优势
- PyMuPDF 在纯文字 PDF 上速度和准确率都很高,但一遇到公式直接放弃
- LlamaParse 靠 GPT-4o 在语义理解上有优势,但表格结构化输出质量不稳定
- Unstructured 的免费版(无 GPU)公式识别几乎为 0
2.2处理速度对比
测试环境:A100 80GB GPU / Intel i7-12700 CPU,测试文件:学术论文 PDF(20页,含公式和表格)
| 工具 | GPU 模式(页/秒) | CPU 模式(页/秒) | 冷启动时间 | 备注 |
|---|---|---|---|---|
| MinerU(pipeline) | 2.1 页/s | 0.3 页/s | ~15s | 模型加载耗时 |
| MinerU(vlm) | 1.8 页/s | 0.2 页/s | ~20s | 精度更高 |
| Unstructured(本地) | — | 0.8 页/s | ~3s | 无 GPU 加速 |
| LlamaParse | — | ~0.5 页/s* | <1s | 受网络延迟影响 |
| PyMuPDF | — | 50+ 页/s | <0.1s | 无需模型,极快 |
LlamaParse 为云端 API,速度受网络和服务器负载影响。
MinerU 速度说明:CPU 模式较慢是主要痛点。生产环境推荐 GPU 部署,或使用 MinerU Open API(云端加速)。Flash 模式(Agent 轻量解析)免 Token 免费,适合快速原型验证。
2.3场景适配矩阵
| 场景 | MinerU | Unstructured | LlamaParse | PyMuPDF |
|---|---|---|---|---|
| 学术论文(含公式) | ⭐⭐⭐⭐⭐ | ⭐⭐ | ⭐⭐⭐ | ⭐(无公式) |
| 财务报告(含表格) | ⭐⭐⭐⭐ | ⭐⭐⭐ | ⭐⭐⭐⭐ | ⭐⭐⭐ |
| 扫描件 / 手写体 | ⭐⭐⭐⭐ | ⭐⭐ | ⭐⭐⭐ | ⭐ |
| 普通原生 PDF(纯文字) | ⭐⭐⭐⭐ | ⭐⭐⭐⭐ | ⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ |
| 多语言文档 | ⭐⭐⭐⭐⭐ | ⭐⭐⭐ | ⭐⭐⭐ | ⭐⭐ |
| 格式多样性(Word/PPT/图片) | ⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐ | ⭐(仅PDF) |
| 处理速度 | ⭐⭐(GPU)/ ⭐(CPU) | ⭐⭐⭐ | ⭐⭐⭐ | ⭐⭐⭐⭐⭐ |
| RAG/LLM 集成 | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ | ⭐⭐ |
| 私有化部署 | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐ | ❌ | ⭐⭐⭐⭐⭐ |
| 成本 | 💚 完全免费 | ⚠️ 企业版收费 | 🔴 按页计费 | 💚 完全免费 |
三、代码实战对比
3.1 基础用法:同一份 PDF,四种解法
测试文件:一篇含公式和表格的学术论文 PDF
MinerU(本地 VLM 模式)
MinerU(本地 VLM 模式)
from magic_pdf.data.data_reader_writer import FileBasedDataWriter, FileBasedDataReader
from magic_pdf.data.dataset import PymuPDFDataset
from magic_pdf.model.doc_analyze_by_custom_model import doc_analyze
from magic_pdf.config.enums import SupportedPdfParseMethod
# 读取文件
with open(PDF_PATH, "rb") as f:
pdf_bytes = f.read()
# 使用 VLM 精准模式
ds = PymuPDFDataset(pdf_bytes)
model_json, sa = doc_analyze(ds, ocr=True)
# 提取 Markdown
from magic_pdf.pipe.UNIPipe import UNIPipe
image_writer = FileBasedDataWriter("/tmp/output/images")
pipe = UNIPipe(pdf_bytes, model_json, image_writer)
pipe.pipe_parse()
markdown_content = pipe.pipe_mk_markdown("/tmp/output/images", drop_mode="none")
print(markdown_content[:1000])
MinerU(Open API / Flash 模式,免 Token)
MinerU(Open API / Flash 模式,免 Token)
from mineru import MinerU
# Flash 模式:无需 Token,免费,≤20页/10MB
client = MinerU()
result = client.flash_extract(PDF_PATH)
print(result.markdown[:1000])
# Precision 模式:需要 Token,支持完整公式和表格识别
client_pro = MinerU("your-api-token")
result = client_pro.extract(
PDF_PATH,
model="vlm", # 推荐 VLM 模型
formula=True, # 开启公式识别
table=True, # 开启表格识别
ocr=True, # 开启 OCR
language="en",
)
print(result.markdown[:1000])
for img in result.images:
print(f" 图片: {img}")
Unstructured
Unstructured
from unstructured.partition.pdf import partition_pdf
from unstructured.staging.base import elements_to_json
# 高精度模式(需要安装 hi_res 依赖)
elements = partition_pdf(
filename=PDF_PATH,
strategy="hi_res", # 高精度,使用 YOLOx 布局检测
infer_table_structure=True, # 尝试识别表格结构
extract_images_in_pdf=True,
)
# 输出为 JSON
output = elements_to_json(elements)
# 转为 Markdown(需自行处理)
markdown_parts = []
for el in elements:
if el.category == "Title":
markdown_parts.append(f"## {el.text}")
elif el.category == "Table":
markdown_parts.append(el.metadata.text_as_html or el.text)
else:
markdown_parts.append(el.text)
markdown_content = "\n\n".join(markdown_parts)
print(markdown_content[:1000])
LlamaParse
LlamaParse
from llama_parse import LlamaParse
from llama_index.core import SimpleDirectoryReader
# 需要 LLAMA_CLOUD_API_KEY 环境变量
parser = LlamaParse(
result_type="markdown",
verbose=True,
language="en",
# parsing_instruction 可以用自然语言指导解析行为
parsing_instruction="提取所有表格为Markdown格式,公式保留为LaTeX",
)
file_extractor = {".pdf": parser}
documents = SimpleDirectoryReader(
input_files=[PDF_PATH],
file_extractor=file_extractor
).load_data()
markdown_content = documents[0].text
print(markdown_content[:1000])
PyMuPDF
import fitz # pip install pymupdf
doc = fitz.open(PDF_PATH)
markdown_parts = []
for page_num, page in enumerate(doc):
# 提取文字(保留布局信息)
text = page.get_text("text")
# 提取表格(PyMuPDF 1.23+ 支持)
tabs = page.find_tables()
for tab in tabs:
table_md = tab.to_markdown()
text = text.replace(tab.bbox_string, f"\n{table_md}\n")
markdown_parts.append(f"## Page {page_num + 1}\n\n{text}")
markdown_content = "\n\n---\n\n".join(markdown_parts)
print(markdown_content[:1000])
# 注意:PyMuPDF 不支持公式识别,数学公式会输出为乱码或占位符
3.2核心差异:公式识别实际效果 实际效果 以下是同一个数学公式在四个工具下的输出对比: 原始公式: \hat{y} = \sigma\left(\sum_{i=1}^{n} w_i x_i + b\right)
MinerU 输出(VLM模式):
<equation>\hat{y} = \sigma\left(\sum_{i=1}^{n} w_i x_i + b\right)</equation>
✅ 完整 LaTeX,可直接渲染
LlamaParse 输出:
<equation>\hat{y} = \sigma(\sum_{i=1}^{n} w_i x_i + b)</equation>
✅ 基本正确,部分样式简化
Unstructured 输出(hi_res):
ŷ = σ(Σwᵢxᵢ + b)
⚠️ Unicode 近似,丢失 LaTeX 结构,LLM 难以理解
PyMuPDF 输出:
ˆy = (X wi xi + b)
❌ 严重乱码,完全不可用
表格识别:财报场景测试
输入: 一个包含合并单元格的财务报表表格
MinerU 输出(精准模式):
| 项目 | 2023年 | 2024年 | 同比变化 |
|------|--------|--------|---------|
| 营业收入(亿元)| 128.5 | 156.3 | +21.6% |
| 净利润(亿元) | 18.2 | 22.7 | +24.7% |
✅ 结构完整,数字准确
LlamaParse 输出:
| 项目 | 2023年 | 2024年 | 同比变化 |
|------|--------|--------|---------|
| 营业收入(亿元)| 128.5 | 156.3 | 21.6% |
✅ 较准确,但合并单元格处理有时出错
Unstructured 输出:
营业收入(亿元)128.5 156.3 +21.6%
净利润(亿元)18.2 22.7 +24.7%
⚠️ 文字提取出来了,但表格结构丢失
PyMuPDF 输出(find_tables):
| 营业收入(亿元)| 128.5 | 156.3 | +21.6% |
| 净利润(亿元) | 18.2 | 22.7 | +24.7% |
✅ 简单表格准确,合并单元格和复杂表头易出错
四、RAG 集成方案
4.1 LangChain 接入
所有工具都能接入 LangChain,但接入方式和效果差异较大。
MinerU + LangChain(推荐)
# pip install langchain-mineru
from langchain_mineru import MinerULoader
from langchain_text_splitters import RecursiveCharacterTextSplitter
from langchain_openai import OpenAIEmbeddings
from langchain_community.vectorstores import FAISS
from langchain_core.output_parsers import StrOutputParser
from langchain_core.runnables import RunnablePassthrough
from langchain_openai import ChatOpenAI
# Step 1: 解析文档
loader = MinerULoader(
source="research_paper.pdf",
mode="precision", # 精准模式
token="your-token", # 或 MINERU_TOKEN 环境变量
formula=True, # 保留公式
table=True, # 保留表格
)
docs = loader.load()
# Step 2: 分块(MinerU 输出 Markdown,按标题分块效果更好)
splitter = RecursiveCharacterTextSplitter(
chunk_size=1000,
chunk_overlap=200,
separators=["\n## ", "\n### ", "\n\n", "\n"],
)
chunks = splitter.split_documents(docs)
# Step 3: 构建向量库
vectorstore = FAISS.from_documents(chunks, OpenAIEmbeddings())
retriever = vectorstore.as_retriever(search_kwargs={"k": 5})
# Step 4: 构建 RAG Chain
def format_docs(docs):
return "\n\n".join(doc.page_content for doc in docs)
rag_chain = (
{"context": retriever | format_docs, "question": RunnablePassthrough()}
| ChatOpenAI(model="gpt-4o")
| StrOutputParser()
)
response = rag_chain.invoke("这篇论文的主要贡献是什么?")
print(response)
Unstructured + LangChain
from langchain_community.document_loaders import UnstructuredFileLoader
loader = UnstructuredFileLoader(
"report.pdf",
strategy="hi_res",
mode="elements", # 保留元素分类信息
)
docs = loader.load()
# 按元素类型过滤,只保留内容(去掉页眉页脚)
content_docs = [d for d in docs if d.metadata.get("category") not in
["Header", "Footer", "PageNumber"]]
LlamaParse + LlamaIndex(原生生态)
from llama_parse import LlamaParse
from llama_index.core import VectorStoreIndex
parser = LlamaParse(result_type="markdown")
documents = parser.load_data("research.pdf")
# 直接构建索引
index = VectorStoreIndex.from_documents(documents)
query_engine = index.as_query_engine()
response = query_engine.query("这篇文章的实验方法是什么?")
print(response)
4.2 批量处理:企业文档库导入
import asyncio
from pathlib import Path
from mineru import MinerU
async def batch_process_docs(pdf_dir: str, output_dir: str) -> list[dict]:
"""批量解析目录下所有 PDF,返回结果列表"""
client = MinerU("your-api-token")
pdf_files = list(Path(pdf_dir).glob("*.pdf"))
results = []
# 并发处理,控制并发数避免超限
semaphore = asyncio.Semaphore(5)
async def process_one(path: Path):
async with semaphore:
try:
result = await asyncio.to_thread(
client.extract,
str(path),
model="vlm",
formula=True,
table=True,
)
return {
"filename": path.name,
"markdown": result.markdown,
"images": result.images,
"status": "success"
}
except Exception as e:
return {"filename": path.name, "status": "error", "error": str(e)}
tasks = [process_one(p) for p in pdf_files]
results = await asyncio.gather(*tasks)
return results
# 运行
results = asyncio.run(batch_process_docs("./papers/", "./output/"))
success = [r for r in results if r["status"] == "success"]
print(f"成功解析 {len(success)}/{len(results)} 个文件")
五、MCP Server:让 AI Agent 直接调用文档解析
这是 2025 年最值得关注的新玩法。传统 RAG 是"先解析,再检索"的离线流水线——而 MCP 让 AI Agent 可以在推理时实时调用文档解析工具,就像人类打开工具箱一样。 四个工具中,只有 MinerU 提供了官方 MCP Server(mineru-open-mcp)。
5.1 MinerU MCP Server 配置
安装(无需 Python 环境,用 uvx 一键运行)
# 安装 uv(如未安装)
curl -LsSf https://astral.sh/uv/install.sh | sh
# 直接运行(Flash 模式,免 Token)
uvx mineru-open-mcp
Claude Desktop 配置(~/Library/Application Support/Claude/claude_desktop_config.json)
{
"mcpServers": {
"mineru": {
"command": "uvx",
"args": ["mineru-open-mcp"],
"env": {
"MINERU_API_TOKEN": "your_token_here"
}
}
}
}
Cursor 配置(.cursor/mcp.json)
{
"mcpServers": {
"mineru": {
"command": "uvx",
"args": ["mineru-open-mcp"],
"env": {
"MINERU_API_TOKEN": "your_token_here"
}
}
}
}
配置好后,在 Claude/Cursor 里直接说:
"帮我解析这个 PDF:arxiv.org/pdf/2501.12…,提取摘要和实验结果"
Claude 会自动调用 parse_documents 工具,返回结构化 Markdown,无需任何额外代码。
MCP 暴露的工具清单:
| 工具名 | 功能 |
|---|---|
| parse_documents | 解析 PDF/DOCX/PPTX/图片/HTML → Markdown |
| get_ocr_languages | 列出全部 109 种支持的 OCR 语言 |
| clean_logs | 清理旧日志文件 |
Flash 模式特点:无需 API Token,免费,每次最多 20 页 / 10 MB。适合日常文档问答、代码辅助场景。精准模式需要 Token,支持完整公式 + 表格识别,适合学术/生产场景。
5.2 HTTP 模式:接入 Web 端 Agent 平台
如果你的 MCP 客户端是 Web 应用(Dify、FastGPT、自建平台),需要 Streamable HTTP 模式:
# 启动 HTTP 模式
MINERU_API_TOKEN=your_token mineru-open-mcp \
--transport streamable-http \
--port 8001
{
"mcpServers": {
"mineru": {
"type": "streamableHttp",
"url": "http://127.0.0.1:8001/mcp"
}
}
}
六、Agent Workflow 集成方案
6.1 LangChain ReAct Agent:自动选择解析策略
下面是一个完整的 Agent,能根据文档类型自动选择"Flash 模式"还是"精准模式":
from langchain_mineru import MinerULoader
from langchain.tools import tool
from langchain.agents import create_react_agent, AgentExecutor
from langchain_openai import ChatOpenAI
from langchain_core.prompts import PromptTemplate
@tool
def parse_document_flash(url_or_path: str) -> str:
"""
快速解析文档,免费无需 Token,适合普通 PDF(≤20页)。
输入:文档 URL 或本地路径
输出:Markdown 文本
"""
loader = MinerULoader(source=url_or_path, mode="flash")
docs = loader.load()
return docs[0].page_content if docs else "解析失败"
@tool
def parse_document_precision(url_or_path: str) -> str:
"""
精准解析文档,支持公式(LaTeX)、复杂表格、扫描件,适合学术论文和财报。
输入:文档 URL 或本地路径
输出:带公式和表格的 Markdown 文本
"""
loader = MinerULoader(
source=url_or_path,
mode="precision",
token="your-token",
formula=True,
table=True,
ocr=True,
)
docs = loader.load()
return docs[0].page_content if docs else "解析失败"
@tool
def answer_from_document(query: str, document_content: str) -> str:
"""
基于已解析的文档内容回答问题。
输入:问题,文档 Markdown 内容
输出:答案
"""
from langchain_openai import ChatOpenAI
llm = ChatOpenAI(model="gpt-4o")
response = llm.invoke(
f"基于以下文档内容回答问题:\n\n{document_content[:4000]}\n\n问题:{query}"
)
return response.content
# 构建 Agent
tools = [parse_document_flash, parse_document_precision, answer_from_document]
llm = ChatOpenAI(model="gpt-4o", temperature=0)
prompt = PromptTemplate.from_template("""
你是一个文档分析助手。你有以下工具:
{tools}
工具名称:{tool_names}
使用 ReAct 格式完成任务:
Question: 需要完成的任务
Thought: 分析应该用哪个工具
Action: 工具名称
Action Input: 工具输入
Observation: 工具输出
...(重复直到得出答案)
Final Answer: 最终答案
Question: {input}
{agent_scratchpad}
""")
agent = create_react_agent(llm, tools, prompt)
executor = AgentExecutor(agent=agent, tools=tools, verbose=True, max_iterations=5)
# 执行任务
result = executor.invoke({
"input": "解析这篇论文 https://arxiv.org/pdf/2501.12345,找出其中的主要实验结果和性能指标"
})
print(result["output"])
6.2 n8n 工作流:无代码接入
如果你的团队不写代码,n8n 可以零代码构建完整文档解析流水线:
触发器(定时/Webhook)
│
▼
HTTP Request(调用 MinerU API)
│ POST https://mineru.net/api/v4/extract/task
│ Body: { "url": "{{$json.pdf_url}}", "is_ocr": true }
│
▼
等待结果(Polling / Webhook 回调)
│
▼
解析 JSON,提取 Markdown 内容
│
▼
发送到 Feishu / Notion / 数据库
关键 API 调用(n8n HTTP Request 节点配置):
// Step 1: 提交解析任务
POST https://mineru.net/api/v4/extract/task
Headers: { "Authorization": "Bearer YOUR_TOKEN" }
Body: {
"url": "https://example.com/report.pdf",
"enable_formula": true,
"enable_table": true,
"model_version": "vlm"
}
// Step 2: 查询结果
GET https://mineru.net/api/v4/extract/task/result/{task_id}
Headers: { "Authorization": "Bearer YOUR_TOKEN" }
6.3 Dify 知识库:生产级 RAG 流水线
Dify 已原生集成 MinerU,直接在文档上传环节选择:
如果需要通过 API 编排:
import requests # Step 1: 向 Dify 知识库上传文档(使用 MinerU 解析引擎) def upload_to_dify_knowledge_base(pdf_path: str, dataset_id: str, api_key: str): with open(pdf_path, "rb") as f: response = requests.post( f"https://api.dify.ai/v1/datasets/{dataset_id}/document/create_by_file", headers={"Authorization": f"Bearer {api_key}"}, files={"file": f}, data={ "indexing_technique": "high_quality", "process_rule": '{"mode": "automatic"}', } ) return response.json() result = upload_to_dify_knowledge_base( pdf_path="research_paper.pdf", dataset_id="your-dataset-id", api_key="your-dify-api-key" ) print(f"文档 ID: {result['document']['id']}")
七、选型决策指南
一句话选型总结
选 MinerU,如果你:
- 需要解析学术论文、教材、技术文档
- 需要 LaTeX 公式输出
- 需要本地部署或私有化
- 在做 RAG 系统,重视召回质量
- 在用 Claude/Cursor,想接 MCP
- 预算有限,需要免费方案
选其他工具,如果你:
- 只处理原生 PDF 纯文字,速度第一 → PyMuPDF
- 需要处理 20+ 种格式(邮件/Excel/网页)→ Unstructured
- 在 LlamaIndex 生态,愿意付费获得最好语义理解 → LlamaParse
- 原型验证阶段,什么都想试试 → MinerU Flash 模式(免费)
八、快速上手清单
□ 1. 安装测试(5分钟)
pip install mineru-open-sdk
python -c "from mineru import MinerU; r = MinerU().flash_extract('your.pdf'); print(r.markdown[:500])"
□ 2. 接入 LangChain RAG(15分钟)
pip install langchain-mineru
参考本文 4.1 节代码
□ 3. 配置 Claude/Cursor MCP(5分钟)
uvx mineru-open-mcp
修改 claude_desktop_config.json,参考本文 5.1 节
□ 4. 生产环境(需 Token)
注册:https://mineru.net
获取 API Token → 替换代码中的 "your-token"
开启 formula=True, table=True, ocr=True
数据来源:OmniDocBench(CVPR 2025)、公开 benchmark 报告、实测结果。MinerU 版本:2.5(2025年9月)。