title: “RAGFlow-04-DeepDoc模块” categories: [“RAGFlow”]
RAGFlow-04-DeepDoc模块
模块概览
1.1 职责与定位
DeepDoc 模块是 RAGFlow 的文档解析引擎,负责将非结构化文档转换为结构化文本。主要职责包括:
- 多格式文档解析:支持 PDF、Docx、Excel、PPT、图片、HTML、Markdown 等
- 布局识别:使用深度学习模型(YOLO)识别文档布局(标题、段落、表格、图片)
- OCR 文字识别:使用 PaddleOCR 识别图片中的文字
- 表格识别与结构化:使用 Table Transformer 模型识别表格结构,转换为 HTML/Markdown
- 图片理解:使用多模态 LLM(如 GPT-4V)描述图片内容
- 版面分析:识别阅读顺序、合并段落、过滤页眉页脚
1.2 核心能力
文档类型支持:
| 文档类型 | 解析器 | 支持格式 | 核心能力 |
|---|---|---|---|
| PdfParser | 布局识别、OCR、表格识别、阅读顺序 | ||
| Word | DocxParser | .docx | 样式保留、表格提取、图片描述 |
| Excel | ExcelParser | .xlsx, .xls, .csv | 工作表解析、公式提取 |
| PPT | PptParser | .pptx | 幻灯片文本与图片提取 |
| 图片 | ImageParser | .jpg, .png, .bmp | OCR + 多模态 LLM 描述 |
| HTML | HtmlParser | .html | DOM 解析、样式保留 |
| Markdown | MarkdownParser | .md | 结构保留、代码块识别 |
1.3 技术栈
深度学习模型:
- 布局识别:YOLO v8(物体检测)
- OCR:PaddleOCR(文字识别)
- 表格识别:Table Transformer(结构化提取)
- 多模态理解:GPT-4V / Gemini(图片描述)
依赖库:
- PDF 处理:PyMuPDF(fitz)、pdfplumber
- 图像处理:OpenCV、PIL
- 文档处理:python-docx、openpyxl、python-pptx
- HTML 处理:BeautifulSoup、lxml
1. 模块架构图
flowchart TB
subgraph "解析器层"
PdfParser[PDF 解析器<br/>PdfParser]
DocxParser[Docx 解析器<br/>DocxParser]
ExcelParser[Excel 解析器<br/>ExcelParser]
PptParser[PPT 解析器<br/>PptParser]
ImageParser[图片解析器<br/>ImageParser]
HtmlParser[HTML 解析器<br/>HtmlParser]
MdParser[Markdown 解析器<br/>MarkdownParser]
end
subgraph "AI 能力层"
LayoutModel[布局识别模型<br/>YOLO v8]
OCR[OCR 引擎<br/>PaddleOCR]
TableModel[表格识别模型<br/>Table Transformer]
VisionLLM[多模态 LLM<br/>GPT-4V/Gemini]
end
subgraph "工具层"
ImageProcessor[图像处理<br/>OpenCV]
TextMerger[文本合并<br/>NLP算法]
LayoutAnalyzer[版面分析<br/>阅读顺序]
FormatConverter[格式转换<br/>HTML/Markdown]
end
subgraph "输入"
File[原始文档<br/>PDF/Docx/Excel等]
end
subgraph "输出"
Sections[文本段落列表<br/>sections]
Tables[表格列表<br/>tables]
Images[图片列表<br/>images]
end
File --> PdfParser
File --> DocxParser
File --> ExcelParser
File --> ImageParser
PdfParser --> LayoutModel
PdfParser --> OCR
PdfParser --> TableModel
DocxParser --> VisionLLM
ImageParser --> OCR
ImageParser --> VisionLLM
LayoutModel --> LayoutAnalyzer
OCR --> TextMerger
TableModel --> FormatConverter
LayoutAnalyzer --> Sections
TextMerger --> Sections
FormatConverter --> Tables
VisionLLM --> Images
Sections --> Output[(输出到 RAG Pipeline)]
Tables --> Output
Images --> Output
2. 核心功能详细剖析
2.1 PDF 解析器
2.1.1 解析流程
sequenceDiagram
autonumber
participant Task as TaskExecutor
participant Parser as PdfParser
participant Layout as 布局识别模型
participant OCR as OCR 引擎
participant Table as 表格识别模型
Task->>Parser: __call__(filename, binary, from_page, to_page)
Parser->>Parser: __images__() - 转换为图片
note right of Parser: PDF → PIL Image (zoomin=3倍)
Parser->>Layout: _layouts_rec(zoomin)
Layout->>Layout: YOLO 模型推理
Layout-->>Parser: 返回边界框 [x0, y0, x1, y1, class]
note right of Layout: class: title/text/table/figure
Parser->>OCR: OCR 识别各个 bbox
OCR-->>Parser: 返回文本与置信度
Parser->>Table: _table_transformer_job(zoomin)
Table->>Table: 识别表格结构(行列)
Table-->>Parser: 返回 HTML 表格
Parser->>Parser: _text_merge() - 合并段落
Parser->>Parser: _extract_table_figure() - 提取表格与图片
Parser->>Parser: _concat_downward() - 垂直合并
Parser->>Parser: _final_reading_order_merge() - 阅读顺序
Parser-->>Task: 返回 sections + tables
2.1.2 核心代码
# deepdoc/parser/pdf_parser.py
class PdfParser:
def __call__(self, filename, binary=None, from_page=0, to_page=100000, zoomin=3, callback=None):
"""解析 PDF"""
# 1. 转换为图片
self.__images__(filename if not binary else binary, zoomin, from_page, to_page, callback)
# 2. 布局识别(YOLO)
self._layouts_rec(zoomin)
callback(0.63, "Layout analysis finished")
# 3. 表格识别(Table Transformer)
self._table_transformer_job(zoomin)
callback(0.65, "Table analysis finished")
# 4. 文本合并(基于位置)
self._text_merge(zoomin=zoomin)
callback(0.67, "Text merged")
# 5. 提取表格与图片
tbls = self._extract_table_figure(True, zoomin, True, True)
# 6. 垂直合并(段落连接)
self._naive_vertical_merge()
# 7. 向下合并(跨页段落)
self._concat_downward()
# 8. 最终阅读顺序合并
self._final_reading_order_merge()
# 9. 返回结果
return [(b["text"], self._line_tag(b, zoomin)) for b in self.boxes], tbls
def __images__(self, fnm, zoomin, from_page, to_page, callback):
"""PDF 转图片"""
doc = fitz.open(fnm)
for i, page in enumerate(doc):
if i < from_page or i >= to_page:
continue
# 放大 zoomin 倍(提高 OCR 精度)
mat = fitz.Matrix(zoomin, zoomin)
pix = page.get_pixmap(matrix=mat)
img = Image.frombytes("RGB", [pix.width, pix.height], pix.samples)
self.images.append(np.array(img))
callback(0.6 * (i - from_page) / (to_page - from_page), f"Page {i}")
def _layouts_rec(self, zoomin):
"""布局识别"""
from deepdoc.vision.layout_recognizer import LayoutRecognizer
layout_model = LayoutRecognizer()
for i, img in enumerate(self.images):
bboxes = layout_model(img) # YOLO 推理
# bboxes: [[x0, y0, x1, y1, confidence, class_id], ...]
for box in bboxes:
x0, y0, x1, y1, conf, cls = box
# OCR 该区域
text = self._ocr_bbox(img, x0, y0, x1, y1)
self.boxes.append({
"text": text,
"x0": x0 / zoomin,
"x1": x1 / zoomin,
"top": y0 / zoomin,
"bottom": y1 / zoomin,
"page_number": i,
"layoutno": cls # 0=title, 1=text, 2=table, 3=figure
})
def _table_transformer_job(self, zoomin):
"""表格识别"""
from deepdoc.vision.table_recognizer import TableRecognizer
table_model = TableRecognizer()
for i, img in enumerate(self.images):
# 找到标记为 table 的 bbox
table_boxes = [b for b in self.boxes if b["layoutno"] == 2 and b["page_number"] == i]
for box in table_boxes:
# 裁剪表格区域
table_img = img[int(box["top"]):int(box["bottom"]), int(box["x0"]):int(box["x1"])]
# 识别表格结构
html = table_model(table_img)
# 存储表格
self.tables.append({
"html": html,
"page": i,
"bbox": (box["x0"], box["top"], box["x1"], box["bottom"])
})
def _text_merge(self, zoomin):
"""文本合并"""
# 按页码、Y 坐标排序
self.boxes.sort(key=lambda x: (x["page_number"], x["top"], x["x0"]))
# 合并同一行的 boxes
merged = []
for box in self.boxes:
if not merged or abs(merged[-1]["top"] - box["top"]) > 5:
merged.append(box)
else:
# 同一行,横向合并
merged[-1]["text"] += " " + box["text"]
merged[-1]["x1"] = max(merged[-1]["x1"], box["x1"])
self.boxes = merged
关键点:
- Zoomin 参数:PDF 转图片时放大 3 倍,提高 OCR 精度,最后坐标除以 3 还原
- 布局识别:YOLO 模型输出 [x0, y0, x1, y1, conf, class],class 表示类型(标题/正文/表格/图片)
- 表格识别:Table Transformer 输出 HTML 格式,保留行列结构
- 文本合并:基于位置关系(Y 坐标相近)合并同一行的文本块
2.2 Docx 解析器
2.2.1 核心代码
# deepdoc/parser/docx_parser.py
class DocxParser:
def __call__(self, filename, binary=None):
"""解析 Docx"""
if binary:
doc = docx.Document(io.BytesIO(binary))
else:
doc = docx.Document(filename)
sections = []
tables = []
# 1. 遍历段落
for para in doc.paragraphs:
text = para.text.strip()
if not text:
continue
# 判断样式(标题/正文)
style = para.style.name
layoutno = 0 if "Heading" in style else 1
sections.append((text, {"layoutno": layoutno}))
# 2. 遍历表格
for table in doc.tables:
html = self._table_to_html(table)
tables.append((None, (0, 0, 0, 0, 0), html))
# 3. 处理图片(多模态 LLM 描述)
for rel in doc.part.rels.values():
if "image" in rel.target_ref:
image_data = rel.target_part.blob
img = Image.open(io.BytesIO(image_data))
# 调用多模态 LLM
vision_llm = LLMBundle(tenant_id, LLMType.IMAGE2TEXT)
description = vision_llm.describe(image_data)
sections.append((f"[图片描述] {description}", {"layoutno": 3}))
return sections, tables
def _table_to_html(self, table):
"""表格转 HTML"""
html = "<table>"
for row in table.rows:
html += "<tr>"
for cell in row.cells:
html += f"<td>{cell.text}</td>"
html += "</tr>"
html += "</table>"
return html
2.3 Excel 解析器
2.3.1 核心代码
# deepdoc/parser/excel_parser.py
class ExcelParser:
def __call__(self, filename, binary=None):
"""解析 Excel"""
if binary:
wb = openpyxl.load_workbook(io.BytesIO(binary))
else:
wb = openpyxl.load_workbook(filename)
sections = []
# 遍历工作表
for sheet in wb.worksheets:
# 转换为 HTML 表格
html = "<table>"
for row in sheet.iter_rows():
html += "<tr>"
for cell in row:
value = str(cell.value) if cell.value else ""
html += f"<td>{value}</td>"
html += "</tr>"
html += "</table>"
# 转换为 Markdown(可选)
markdown = self._html_to_markdown(html)
sections.append((markdown, {"layoutno": 2}))
return sections
def _html_to_markdown(self, html):
"""HTML 表格转 Markdown"""
from html_to_markdown import convert
return convert(html)
3. 关键数据结构
3.1 解析结果结构
# sections: List[Tuple[str, Dict]]
sections = [
("第一章 引言", {"layoutno": 0, "page_number": 1, "top": 100}),
("本文介绍了...", {"layoutno": 1, "page_number": 1, "top": 150}),
("[图片描述] 架构图显示了...", {"layoutno": 3, "page_number": 2})
]
# tables: List[Tuple[Any, Tuple, str]]
tables = [
(None, (1, 100, 500, 200, 400), "<table><tr><td>...</td></tr></table>"),
(None, (2, 50, 600, 100, 300), "<table>...</table>")
]
# layoutno 含义:
# 0 = 标题 (title)
# 1 = 正文 (text)
# 2 = 表格 (table)
# 3 = 图片 (figure)
4. 性能优化建议
1. 布局识别优化:
- 使用 GPU 加速 YOLO 推理(速度提升 10 倍)
- 批量处理图片(batch_size=4)
2. OCR 优化:
- 使用 PaddleOCR GPU 版本
- 跳过置信度低的区域(< 0.5)
3. 表格识别优化:
- 仅对标记为 table 的区域执行识别
- 使用 ONNX 加速推理
4. 内存优化:
- 逐页处理,避免一次性加载所有页面
- 及时释放图片内存
5. 最佳实践
1. Zoomin 参数选择:
- 普通 PDF:zoomin=3
- 扫描件/低分辨率:zoomin=5
- 高分辨率:zoomin=2
2. 多模态 LLM 使用:
- 图片内容复杂时使用(如图表、架构图)
- 纯文字图片优先使用 OCR(成本低)
3. 表格处理:
- Excel 保留公式(可选)
- 合并单元格需特殊处理