Files
GraphRAGAgent/docs/mineru_specification.md
plf b02d3378fc GraphRAG Studio — initial commit: multimodal RAG system with KG visualization
Full-stack application for document-to-knowledge-graph pipeline:
- Backend: FastAPI + LangGraph ReAct agent + DeepSeek + MinerU parsing
- Frontend: React 19 + Vite + D3.js + shadcn/ui
- Pipeline: MinerU parsing → LangExtract entity extraction → KG building

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-07 17:30:04 +08:00

681 lines
20 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# MinerU 文档解析规范文档
> 基于 [opendatalab/MinerU](https://github.com/opendatalab/MinerU) 官方文档及云端 API 调研
> 版本基线2026-03-04
---
## 目录
- [一、支持的原始输入文件格式](#一支持的原始输入文件格式)
- [1.1 支持格式清单](#11-支持格式清单)
- [1.2 输入限制](#12-输入限制)
- [1.3 OCR 语言支持](#13-ocr-语言支持)
- [二、云端 API 输出格式规范](#二云端-api-输出格式规范)
- [2.1 输出文件总览](#21-输出文件总览)
- [2.2 content_list.json 字段规范](#22-content_listjson-字段规范)
- [2.3 middle.json 字段规范](#23-middlejson-字段规范)
- [2.4 Markdown 输出规范](#24-markdown-输出规范)
- [2.5 调试与可视化文件](#25-调试与可视化文件)
- [三、布局信息规范](#三布局信息规范)
- [3.1 坐标系定义](#31-坐标系定义)
- [3.2 布局分类体系Pipeline 后端)](#32-布局分类体系pipeline-后端)
- [3.3 布局分类体系VLM 后端)](#33-布局分类体系vlm-后端)
- [3.4 内容层级与标题级别](#34-内容层级与标题级别)
- [3.5 布局精度提取指南](#35-布局精度提取指南)
- [四、云端 API MVP 必要字段](#四云端-api-mvp-必要字段)
- [4.1 认证配置](#41-认证配置)
- [4.2 创建解析任务 — 请求规范](#42-创建解析任务--请求规范)
- [4.3 查询任务结果 — 响应规范](#43-查询任务结果--响应规范)
- [4.4 批量任务接口](#44-批量任务接口)
- [4.5 MVP 最小可用请求示例](#45-mvp-最小可用请求示例)
---
## 一、支持的原始输入文件格式
### 1.1 支持格式清单
| 格式 | 扩展名 | 说明 |
|------|--------|------|
| **PDF** | `.pdf` | 核心能力 — 文本型 / 扫描型 / 混合型均支持 |
| **Word** | `.doc`, `.docx` | 旧版和新版 Word 文档 |
| **PowerPoint** | `.ppt`, `.pptx` | 旧版和新版演示文稿 |
| **图片** | `.png`, `.jpg`, `.jpeg` | 单页图片文档,支持 EXIF 方向自动校正 |
| **HTML** | `.html` | 需指定 `MinerU-HTML` 模型版本 |
### 1.2 输入限制
| 约束项 | 限制值 |
|--------|--------|
| 单文件最大体积 | **200 MB** |
| 单文件最大页数 | **600 页** |
| 云端 API 每日免费额度 | **2,000 页**(最高优先级),超出部分降低优先级 |
### 1.3 OCR 语言支持
MinerU 内置 OCR 引擎支持 **109 种语言**,可通过 `language` 参数指定文档主语言(默认 `zh` 中文)。常用语言代码:
| 代码 | 语言 | 代码 | 语言 |
|------|------|------|------|
| `zh` | 中文 | `en` | 英文 |
| `ja` | 日文 | `ko` | 韩文 |
| `fr` | 法文 | `de` | 德文 |
---
## 二、云端 API 输出格式规范
### 2.1 输出文件总览
云端 API 任务完成后,返回一个 ZIP 压缩包(通过 `full_zip_url` 获取),解压后包含以下文件:
```
output/
├── auto/
│ ├── auto.md # 多模态 Markdown含图片引用
│ └── images/ # 提取的图片资源
│ ├── img_0_0.png
│ ├── table_0_1.png
│ └── ...
├── auto_nlp/
│ └── auto_nlp.md # 纯文本 NLP Markdown无图片
├── middle.json # 富元数据中间格式(完整层级结构)
├── content_list.json # 扁平化内容块列表(按阅读顺序)
├── layout.pdf # 布局分析可视化(调试用)
├── span.pdf # Span 级别标注Pipeline 后端,调试用)
└── model.json # 原始模型推理结果(调试用)
```
| 文件 | 用途 | 推荐场景 |
|------|------|---------|
| `content_list.json` | 扁平化内容块,按阅读顺序 | **推荐用于下游 NLP/KG 管道对接** |
| `middle.json` | 完整层级结构,含丰富元数据 | 需要精确布局信息或二次开发 |
| `auto/auto.md` | 多模态 Markdown | 人工阅读、LLM 直接消费 |
| `auto_nlp/auto_nlp.md` | 纯文本 Markdown | 纯文本 NLP 处理 |
| `layout.pdf` | 布局可视化 | 调试、验证解析质量 |
---
### 2.2 content_list.json 字段规范
`content_list.json` 是一个 **JSON 数组**,每个元素是一个内容块,按文档阅读顺序排列。
#### 2.2.1 公共字段(所有类型共有)
| 字段 | 类型 | 说明 |
|------|------|------|
| `type` | `string` | 内容类型:`text` / `image` / `table` / `equation` / `code` / `list` |
| `page_idx` | `int` | 所在页码(**0-indexed** |
| `bbox` | `[x0, y0, x1, y1]` | 边界框坐标,归一化到 **01000** 范围 |
#### 2.2.2 文本块type: "text"
```json
{
"type": "text",
"text": "段落正文内容...",
"text_level": 0,
"page_idx": 0,
"bbox": [72, 120, 540, 145]
}
```
| 字段 | 类型 | 说明 |
|------|------|------|
| `text` | `string` | 文本内容 |
| `text_level` | `int \| null` | 标题级别:`null``0` = 正文,`1` = 一级标题,`2` = 二级标题,依此类推 |
#### 2.2.3 图片块type: "image"
```json
{
"type": "image",
"img_path": "images/img_0_0.png",
"image_caption": ["Figure 1: System architecture"],
"image_footnote": ["Source: internal report"],
"page_idx": 1,
"bbox": [100, 200, 500, 600]
}
```
| 字段 | 类型 | 说明 |
|------|------|------|
| `img_path` | `string` | 图片文件相对路径 |
| `image_caption` | `string[]` | 图片标题列表 |
| `image_footnote` | `string[]` | 图片脚注列表 |
#### 2.2.4 表格块type: "table"
```json
{
"type": "table",
"img_path": "images/table_0_1.png",
"table_body": "<html><body><table><tr><td>...</td></tr></table></body></html>",
"table_caption": ["Table 1: Performance comparison"],
"table_footnote": ["* p < 0.05"],
"page_idx": 2,
"bbox": [50, 300, 950, 700]
}
```
| 字段 | 类型 | 说明 |
|------|------|------|
| `img_path` | `string` | 表格截图相对路径 |
| `table_body` | `string` | 表格 HTML 表示(`<table>` 标签) |
| `table_caption` | `string[]` | 表格标题列表 |
| `table_footnote` | `string[]` | 表格脚注列表 |
#### 2.2.5 公式块type: "equation"
```json
{
"type": "equation",
"text": "E = mc^2",
"text_format": "latex",
"img_path": "images/eq_0_0.png",
"page_idx": 3,
"bbox": [200, 400, 800, 450]
}
```
| 字段 | 类型 | 说明 |
|------|------|------|
| `text` | `string` | 公式的 LaTeX 表示 |
| `text_format` | `string` | 固定值 `"latex"` |
| `img_path` | `string` | 公式截图相对路径 |
#### 2.2.6 代码块type: "code")— VLM 后端
```json
{
"type": "code",
"sub_type": "code",
"code_body": "def hello():\n print('hello')",
"code_caption": ["Listing 1: Example function"],
"page_idx": 4,
"bbox": [80, 100, 920, 300]
}
```
| 字段 | 类型 | 说明 |
|------|------|------|
| `sub_type` | `string` | `"code"``"algorithm"` |
| `code_body` | `string` | 代码文本内容 |
| `code_caption` | `string[]` | 代码块标题(可选) |
#### 2.2.7 列表块type: "list")— VLM 后端
```json
{
"type": "list",
"sub_type": "text",
"list_items": ["第一项", "第二项", "第三项"],
"page_idx": 5,
"bbox": [72, 200, 540, 350]
}
```
| 字段 | 类型 | 说明 |
|------|------|------|
| `sub_type` | `string` | `"text"``"ref_text"`(参考文献列表) |
| `list_items` | `string[]` | 列表项内容 |
---
### 2.3 middle.json 字段规范
`middle.json` 是 MinerU 的富元数据中间格式,保留完整的文档层级结构。
#### 2.3.1 顶层结构
```json
{
"_backend": "pipeline | vlm | hybrid",
"_version_name": "2.7.4",
"pdf_info": [ ... ]
}
```
| 字段 | 类型 | 说明 |
|------|------|------|
| `_backend` | `string` | 使用的解析后端 |
| `_version_name` | `string` | MinerU 版本标识 |
| `pdf_info` | `array` | 按页组织的解析结果数组 |
#### 2.3.2 页级结构pdf_info 数组元素)
```json
{
"page_idx": 0,
"page_size": [595.0, 842.0],
"preproc_blocks": [ ... ],
"para_blocks": [ ... ],
"images": [ ... ],
"tables": [ ... ],
"interline_equations": [ ... ],
"discarded_blocks": [ ... ]
}
```
| 字段 | 类型 | 说明 |
|------|------|------|
| `page_idx` | `int` | 页码0-indexed |
| `page_size` | `[float, float]` | 页面尺寸 `[宽, 高]`(原始 PDF 坐标系,单位 pt |
| `preproc_blocks` | `array` | 未分段的预处理块 |
| `para_blocks` | `array` | **已分段的内容块**(主输出) |
| `images` | `array` | 提取的图片块 |
| `tables` | `array` | 提取的表格块 |
| `interline_equations` | `array` | 行间公式块 |
| `discarded_blocks` | `array` | 被过滤的内容(页眉、页脚、页码等) |
#### 2.3.3 内容块层级结构
内容块采用三级层级:**Block → Line → Span**
**一级块Level 1— 容器块:**
```json
{
"type": "table",
"bbox": [x0, y0, x1, y1],
"blocks": [ ... ]
}
```
| 字段 | 类型 | 说明 |
|------|------|------|
| `type` | `string` | `"table"``"image"` |
| `bbox` | `[x0, y0, x1, y1]` | 边界框坐标(原始 PDF 坐标系) |
| `blocks` | `array` | 包含的二级块 |
**二级块Level 2— 语义块:**
```json
{
"type": "text",
"bbox": [x0, y0, x1, y1],
"lines": [ ... ]
}
```
| `type` 值 | 说明 |
|-----------|------|
| `text` | 正文段落 |
| `title` | 标题 |
| `image_body` | 图片主体 |
| `image_caption` | 图片标题 |
| `image_footnote` | 图片脚注 |
| `table_body` | 表格主体 |
| `table_caption` | 表格标题 |
| `table_footnote` | 表格脚注 |
| `interline_equation` | 行间公式 |
| `index` | 目录项 |
| `list` | 列表项 |
**行结构Line**
```json
{
"bbox": [x0, y0, x1, y1],
"spans": [ ... ]
}
```
**Span 结构(最小粒度):**
```json
{
"bbox": [x0, y0, x1, y1],
"type": "text",
"content": "具体文本内容",
"score": 0.95
}
```
| 字段 | 类型 | 说明 |
|------|------|------|
| `bbox` | `[x0, y0, x1, y1]` | 边界框坐标 |
| `type` | `string` | `text` / `image` / `table` / `inline_equation` / `interline_equation` |
| `content` | `string` | 文本内容text 类型)|
| `img_path` | `string` | 图片路径image/table 类型)|
| `score` | `float` | 模型置信度0.0~1.0 |
---
### 2.4 Markdown 输出规范
| 文件 | 特点 |
|------|------|
| `auto/auto.md` | 图片以 `![](images/img_x_x.png)` 引用;表格保留为 Markdown 表格或 HTML公式使用 `$...$``$$...$$` 定界符 |
| `auto_nlp/auto_nlp.md` | 纯文本,图片/表格替换为占位文本描述;适合直接送入 NLP 管道 |
---
### 2.5 调试与可视化文件
| 文件 | 格式 | 说明 |
|------|------|------|
| `layout.pdf` | PDF | 每页叠加带编号的检测框,不同颜色区分内容类型,验证布局分析准确性和阅读顺序 |
| `span.pdf` | PDF | 用不同颜色线框标注页面内容的 span 类型(仅 Pipeline 后端),排查文本丢失和公式识别问题 |
| `model.json` | JSON | 原始模型推理结果,包含 `category_id``poly`(四边形坐标)、`score`(置信度) |
---
## 三、布局信息规范
### 3.1 坐标系定义
MinerU 使用两套坐标系,取决于输出文件:
| 坐标系 | 适用文件 | 范围 | 原点 | 说明 |
|--------|---------|------|------|------|
| **归一化坐标** | `content_list.json` | `0 1000` | 左上角 | 页面宽高均映射到 0~1000 |
| **原始 PDF 坐标** | `middle.json` | 实际 pt 值 | 左上角 | 与 PDF 页面尺寸一致(如 A4 = 595×842 |
| **归一化比例坐标** | `model.json`VLM | `0.0 1.0` | 左上角 | 宽高均映射到 0~1 |
**bbox 格式统一为:`[x0, y0, x1, y1]`**
```
(x0, y0) ─────────────────── (x1, y0)
│ │
│ 内容区域 │
│ │
(x0, y1) ─────────────────── (x1, y1)
```
- `x0, y0`:左上角坐标
- `x1, y1`:右下角坐标
### 3.2 布局分类体系Pipeline 后端)
`model.json` 中的 `category_id` 枚举:
| category_id | 类型 | 说明 |
|-------------|------|------|
| 0 | `title` | 标题 |
| 1 | `plain_text` | 正文文本 |
| 2 | `abandon` | 丢弃区域(页眉/页脚/页码等) |
| 3 | `figure` | 图片 |
| 4 | `figure_caption` | 图片标题 |
| 5 | `table` | 表格 |
| 6 | `table_caption` | 表格标题 |
| 7 | `table_footnote` | 表格脚注 |
| 8 | `isolate_formula` | 独立行间公式 |
| 9 | `formula_caption` | 公式标题 |
| 13 | `embedding` | 嵌入内容 |
| 14 | `isolated` | 隔离内容 |
| 15 | `OCR_text` | OCR 识别文本 |
### 3.3 布局分类体系VLM 后端)
VLM 后端使用字符串类型标识,分类更细:
| type 值 | 说明 |
|---------|------|
| `text` | 正文 |
| `title` | 标题 |
| `equation` | 公式 |
| `image` | 图片 |
| `image_caption` | 图片标题 |
| `image_footnote` | 图片脚注 |
| `table` | 表格 |
| `table_caption` | 表格标题 |
| `table_footnote` | 表格脚注 |
| `code` | 代码块 |
| `code_caption` | 代码标题 |
| `list` | 列表 |
| `header` | 页眉discarded |
| `footer` | 页脚discarded |
| `page_number` | 页码discarded |
| `aside_text` | 边栏文字discarded |
| `page_footnote` | 页面脚注discarded |
| `ref_text` | 参考文献 |
| `algorithm` | 算法伪代码 |
| `phonetic` | 注音 |
### 3.4 内容层级与标题级别
`content_list.json` 中的 `text_level` 字段标识文档结构层级:
| text_level | 含义 | 对应 Markdown |
|------------|------|--------------|
| `null``0` | 正文 | 无标记 |
| `1` | 一级标题 | `# Heading` |
| `2` | 二级标题 | `## Heading` |
| `3` | 三级标题 | `### Heading` |
| `4` | 四级标题 | `#### Heading` |
| `5+` | 更深层标题 | `#####+ Heading` |
### 3.5 布局精度提取指南
针对不同数据类型的精确提取建议:
#### 文本提取
```python
# 从 content_list.json 提取所有正文文本
texts = [
block for block in content_list
if block["type"] == "text"
]
# 按页过滤
page_0_texts = [b for b in texts if b["page_idx"] == 0]
```
#### 标题层级提取
```python
# 提取文档大纲结构
headings = [
{"level": block["text_level"], "text": block["text"], "page": block["page_idx"]}
for block in content_list
if block["type"] == "text" and block.get("text_level") and block["text_level"] >= 1
]
```
#### 表格数值提取
```python
# 表格以 HTML 形式存储在 table_body 中,可用 BeautifulSoup 解析
from bs4 import BeautifulSoup
tables = [b for b in content_list if b["type"] == "table"]
for table in tables:
soup = BeautifulSoup(table["table_body"], "html.parser")
rows = []
for tr in soup.find_all("tr"):
cells = [td.get_text(strip=True) for td in tr.find_all(["td", "th"])]
rows.append(cells)
```
#### 空间位置定位
```python
# 利用 bbox 判断内容在页面中的位置
def get_position(bbox, threshold=500):
"""判断内容在页面的上半部分还是下半部分(归一化坐标 0-1000"""
y_center = (bbox[1] + bbox[3]) / 2
return "upper" if y_center < threshold else "lower"
# 判断两个块是否水平相邻(同一行)
def is_same_row(block_a, block_b, tolerance=20):
return abs(block_a["bbox"][1] - block_b["bbox"][1]) < tolerance
```
---
## 四、云端 API MVP 必要字段
### 4.1 认证配置
| 配置项 | 值 | 获取方式 |
|--------|-----|---------|
| Token | Bearer Token 字符串 | [mineru.net/apiManage/token](https://mineru.net/apiManage/token) 注册后获取 |
**请求头格式(所有接口通用):**
```
Authorization: Bearer {your_token}
Content-Type: application/json
```
---
### 4.2 创建解析任务 — 请求规范
**接口:** `POST https://mineru.net/api/v4/extract/task`
#### 请求体字段
| 字段 | 类型 | 必填 | 默认值 | 说明 |
|------|------|------|--------|------|
| `url` | `string` | **是** | — | 待解析文件的公网可访问 URL |
| `is_ocr` | `bool` | 否 | `false` | 是否强制启用 OCR扫描件建议开启 |
| `enable_formula` | `bool` | 否 | `true` | 是否启用公式识别 |
| `enable_table` | `bool` | 否 | `true` | 是否启用表格识别 |
| `language` | `string` | 否 | `"zh"` | 文档主语言代码 |
| `model` | `string` | 否 | 自动选择 | 模型版本:`pipeline` / `vlm` / `MinerU-HTML` |
| `data_id` | `string` | 否 | — | 自定义业务标识(用于关联追踪) |
| `callback_url` | `string` | 否 | — | 任务完成后的回调通知 URL |
#### MVP 最小必填字段
```json
{
"url": "https://example.com/document.pdf"
}
```
> 仅 `url` 为必填,其余参数均有合理默认值。
---
### 4.3 查询任务结果 — 响应规范
**接口:** `GET https://mineru.net/api/v4/extract/task/{task_id}`
#### 响应体字段
| 字段 | 类型 | 说明 |
|------|------|------|
| `task_id` | `string` | 任务唯一标识 |
| `state` | `string` | 任务状态(见下方枚举) |
| `err_msg` | `string \| null` | 错误信息(失败时) |
| `full_zip_url` | `string \| null` | 完整输出 ZIP 下载地址(成功时) |
| `file_name` | `string` | 原始文件名 |
| `batch_id` | `string \| null` | 批量任务 ID如有 |
#### 任务状态枚举
| state | 说明 |
|-------|------|
| `pending` | 排队等待中 |
| `processing` | 正在解析 |
| `done` | 解析完成 |
| `failed` | 解析失败(查看 `err_msg` |
---
### 4.4 批量任务接口
#### 4.4.1 批量获取上传 URL
**接口:** `POST https://mineru.net/api/v4/file-urls/batch`
用于获取文件上传的预签名 URL适合本地文件上传场景
#### 4.4.2 批量创建任务
**接口:** `POST https://mineru.net/api/v4/extract/task/batch`
请求体中 `files` 数组包含多个文件的解析参数。
#### 4.4.3 批量查询结果
**接口:** `GET https://mineru.net/api/v4/extract-results/batch/{batch_id}`
---
### 4.5 MVP 最小可用请求示例
#### Python 实现
```python
import os
import time
import requests
MINERU_API_TOKEN = os.getenv("MINERU_API_TOKEN")
BASE_URL = "https://mineru.net/api/v4"
HEADERS = {
"Authorization": f"Bearer {MINERU_API_TOKEN}",
"Content-Type": "application/json",
}
# ① 创建解析任务(仅需 url 一个必填字段)
resp = requests.post(
f"{BASE_URL}/extract/task",
headers=HEADERS,
json={
"url": "https://example.com/sample.pdf", # 必填:文件公网 URL
# "is_ocr": False, # 可选:默认 false
# "enable_formula": True, # 可选:默认 true
# "enable_table": True, # 可选:默认 true
# "language": "zh", # 可选:默认中文
},
)
task_id = resp.json()["task_id"]
print(f"Task created: {task_id}")
# ② 轮询查询结果
while True:
result = requests.get(
f"{BASE_URL}/extract/task/{task_id}",
headers=HEADERS,
).json()
state = result["state"]
print(f"State: {state}")
if state == "done":
zip_url = result["full_zip_url"]
print(f"Download: {zip_url}")
break
elif state == "failed":
print(f"Error: {result['err_msg']}")
break
time.sleep(5)
# ③ 下载并解压结果
import zipfile, io
zip_data = requests.get(zip_url).content
with zipfile.ZipFile(io.BytesIO(zip_data)) as zf:
zf.extractall("./mineru_output/")
print("Files:", zf.namelist())
```
#### cURL 实现
```bash
# 创建任务
curl -X POST https://mineru.net/api/v4/extract/task \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{"url": "https://example.com/sample.pdf"}'
# 查询结果
curl https://mineru.net/api/v4/extract/task/{task_id} \
-H "Authorization: Bearer YOUR_TOKEN"
```
#### MVP 检查清单
- [ ] 已在 [mineru.net](https://mineru.net/) 注册账号
- [ ] 已在 [Token 管理页](https://mineru.net/apiManage/token) 获取 API Token
- [ ] 已将 Token 配置到 `.env` 文件:`MINERU_API_TOKEN=xxx`
- [ ] 准备了公网可访问的测试文件 URLPDF/DOCX/PPT/图片)
- [ ] 安装了 `requests` 库:`pip install requests`