002.大模型 RAG 进阶多格式文档解析

一:基础认知与准备

1.1. 常见文档类型与解析需求

在真实企业环境中,会遇到各种数据源,特别是在金融领域的财报、报表等场景下,文档解析的需求尤为突出。

常见的文档类型

扩展名称 支持文件类型 适用场景示例
csv .csv 表格数据提取
docx .doc, .docx Word 文档解析
pdf .pdf PDF 文本 / 布局提取
image .jpeg .png .tiff 等 图片 OCR 文字识别
pptx .ppt, .pptx 幻灯片内容提取
xlsx .xls, .xlsx Excel 表格解析

1.2. 文档解析挑战认知

理解文档解析的难点

在考虑解析 PDF 文件时,我们需要根据当前的技术栈发展情况,并结合实际的业务诉求,综合考量这其中的技术难点,因为每一项技术难点所涉及的技术方案都会需要一个算法 / 技术手段去突破。

而开发者从解析的效果去考虑,可以从简单的做起,逐步突破难点,这对于开发人员自身的自信心提升也是一种正向的导向。在整个 PDF 解析过程中,以下几项是比较难处理的:

Pasted image 20260715153520.png


二:技术选型与工具对比

2.1. 主流工具对比与选择

技术选型决策

工具性能对比表

工具 核心优势 适用场景 性能代价
unstructured.io 支持 50+ 格式,生态完善 多源数据 ETL 入口 处理速度较慢
PyMuPDF 解析速度 >200 页 /分钟 纯文本 / 简单 PDF 批量处理 无 OCR 能力
Marker 代码 / 公式支持优秀 技术白皮书 / 学术文献 需 GPU 加速
MinerU 数学公式识别精准 科技 / 专利类文档 高计算负载
DoclingAI 表格提取精度 98%+ 金融财报 / 科研报告 仅专注表格
DeepDoc 中文优化 + 端到端方案 中文 RAG 系统建设 需 API 调用

2.2. 文档解析技术差异

PDF 解析技术核心差异

PDF 解析技术的核心差异体现在对文档结构的处理逻辑上:

OCR 生态 / 大模型

OCR(光学字符识别)最终的目的是将非结构化的图像信息,转化为结构化的、可计算和可理解的数据,所以本质上是对图片内容的理解,可以考虑的开源组件如下:

Pasted image 20260715154027.png

Unstructured.io 作为集成框架,通过 strategy 参数实现后端自适应切换:

这种混合架构使其在金融财报(表格提取精度 98%+)和科研报告等场景中表现突出。

Unstructured 的核心优势


三:unstructured.io 库入门与实践

3.1. 环境准备与安装

1. 基本安装(纯文本处理)

pip install unstructured
uv add unstructured

2. 全量安装(多类型文档处理)

针对需要处理多类型文档(如 PDF、Office 格式、图片等)的场景,全量安装会包含 docx、pptx、pdf、image 等扩展依赖,适合企业级全场景文档处理需求:

包含本地推理能力(支持 PDF / 图片 OCR 等)

pip install "unstructured[local-inference]"

支持所有文档类型(不含本地推理,需依赖外部 API)

pip install "unstructured[all-docs]"
uv add "unstructured[all-docs]"

3. 特定文档类型安装

如需进一步精简依赖,可按目标文件类型单独安装扩展模块,格式为 unstructured[<extra>],支持同时指定多个扩展,以逗号分隔:

仅安装 PDF 和 DOCX 处理能力

pip install "unstructured[pdf, docx]"

4. Serverless API 安装

Serverless API 通过优化处理流程,将文档处理启动时间从 30 分钟缩短至 3 秒以内,并支持多区域横向扩展,有效提升了高并发场景下的吞吐量:

pip install unstructured-client

5. Docker 安装

docker pull downloads.unstructured.io/unstructured-io/unstructured:latest
docker run -dt --name unstructured downloads.unstructured.io/unstructured-io/unstructured:latest
docker exec -it unstructured bash

3.2. 核心系统依赖配置

1. Tesseract OCR:图像文本识别

提供图像文本识别能力,是处理扫描版 PDF 和图片文件的核心组件。

windows 安装:

macOS 安装:

brew install tesseract
brew install tesseract-lang

Linux 安装:

sudo apt-get install tesseract-ocr
sudo apt-get install tesseract-ocr-chi-sim  # 中文简体支持

验证安装:

tesseract --list-langs  # 查看已安装的语言包

2. Poppler:PDF 内容提取底层引擎

通过 pdf2image 库将 PDF 转换为图像格式,为后续 OCR 处理提供输入。

macOS 安装:

brew install poppler

Linux 安装:

sudo apt-get install poppler-utils

验证安装:

import os

# mac系统
# 设置 poppler 工具路径到环境变量
os.environ["PATH"] = "/opt/homebrew/bin:" + os.environ.get("PATH", "")

# windows 系统
# 将此处路径替换为你自己的 poppler\bin 目录路径
# poppler_path = "C:\\Poppler\\bin"

# 将 poppler 路径临时添加到当前会话的环境变量中,os.pathsep 是自动添加路径分隔符(在 Windows 上是分号;)
# os.environ["PATH"] = poppler_path + os.pathsep + os.environ.get("PATH", "")
pdfinfo -v

3. Pandoc:富文本格式转换

处理 EPUB、RTF 等富文本格式的转换工具,必须使用 2.14.2 及以上版本以确保 RTF 文件解析兼容性。

4. libmagic:跨平台文件类型检测

Linux 和 macOS 系统需手动安装,Windows 环境可忽略此依赖。

5. 常见依赖问题解决方案

注意事项:本地完整安装可能触发依赖链报错(如 "Could not build wheels for pikepdf"),需预先安装 qpdf、libheif 和 pillow 等图像处理依赖。

3.3. unstructured 核心功能理解

功能分类与作用

一般来说,这些功能分为几类:

# UnstructuredIO 核心组件
from unstructured.partition.auto import partition
from typing import List
from unstructured.documents.elements import Element

# 使用 partition 函数自动检测文件类型并解析, 默认 strategy 策略是 auto,还会有 fast 策略,速度比 image-to-text models 的快 100 倍
elements: List[Element] = partition(filename="RAG评估.md", strategy="auto")

# 元素的文本内容
print(elements[0].text)
print("===========================")

# 元素的类型
print(elements[0].category)
print("==================")

# 元素的元数据
print(elements[0].metadata.__dict__)
print("===========================")

基于元素的方法优势

为什么这种基于元素的方法如此重要?

元数据的应用价值

这些元数据让你能够:

从本质上讲,unstructured 不仅仅是"读取"PDF 文档;它理解文档并进行解构它,这对于基于正则表达式来进行抓取的方式来说,这种解析方式无疑是一种对文档的更强大、更具语义性的理解。

3.4. Partition 功能实践

partition 通用参数如下

from typing import List, Dict, Any, Optional, Sequence
from pathlib import Path

# 自定义解析函数,支持任意类型的文件格式
def parse_file_with_unstructured(file_path: str):
    """
    使用 UnstructuredIO 解析单个文件
    Args:
        file_path: 文件路径
    Returns:
        Dict: 包含解析结果和统计信息的字典
    """
    try:
        # 使用 partition 函数自动检测文件类型并解析, 默认 strategy 策略是 auto,还会有 fast 策略,速度比 image-to-text models 的快 100 倍
        elements: List[Element] = partition(filename=file_path, strategy="auto")

        # 分析解析结果
        analysis = {
            "file_path": file_path,
            "file_extension": Path(file_path).suffix.lower(),
            "total_elements": len(elements),
            "element_types": {},
            "elements": elements,
            "text_content": "",
            "statistics": {}
        }

        # 统计元素类型
        for element in elements:
            element_type = type(element).__name__
            analysis["element_types"][element_type] = analysis["element_types"].get(element_type, 0) + 1

        # 提取文本内容
        text_parts = []

        for element in elements:
            if hasattr(element, 'text') and element.text:
                text_parts.append(element.text)

        analysis["text_content"] = "\n\n".join(text_parts)

        # 计算统计信息
        analysis["statistics"]["total_characters"] = len(analysis["text_content"])

        print(f"   解析完成")
        print(f"   元素总数: {analysis['total_elements']}")
        print(f"   元素类型: {analysis['element_types']}")
        print(f"   总字符数: {analysis['statistics']['total_characters']}")
        print(f"   文本内容: {analysis['text_content'][:200]} ")

    except Exception as e:
        print(f"文件解析失败: {e}")
        return {}

1. Markdown 文档解析

parse_file_with_unstructured("RAG评估.md")
from unstructured.partition.md import partition_md
from typing import List
from unstructured.documents.elements import Element

# 使用 partition_md 函数检测 markdown 文件类型解析, include_page_breaks 若希望在 Markdown 中标识页面断点(少见场景)
elements: List[Element] = partition_md(filename="RAG评估.md", languages=["zho"], include_page_breaks=True)

# 元素的元数据
print(elements[0].metadata.__dict__)
print("===========================")

# 元素的文本内容
print(elements[0].text)
print("===========================")

# 元素的类型
print(elements[0].category)
print("===========================")

2. HTML 文档解析

parse_file_with_unstructured("html-tags-decode.html")

支持 URL 输入、headers、ssl 验证选项

from unstructured.partition.html import partition_html
from typing import List
from unstructured.documents.elements import Element

# 使用 partition_html 函数检测 html 网页类型解析
elements = partition_html(url="https://docs.unstructured.io/welcome",
                          headers={"User-Agent": "MyBot"},
                          ssl_verify=False,
                          include_page_breaks=False,
                          encoding="utf-8")
# elements: List[Element] = partition_html(url="https://docs.unstructured.io/welcome", languages=["zho"])

# 元素的元数据
print(elements[1].metadata.__dict__)
print("===========================")

# 元素的文本内容
print(elements[1].text)
print("===========================")

# 元素的类型
print(elements[1].category)
print("===========================")

3. EXCEL 文档解析

parse_file_with_unstructured("销售数据统计.xlsx")
from unstructured.partition.xlsx import partition_xlsx
from typing import List
from unstructured.documents.elements import Element

# 使用 partition_xlsx 函数检测 excel 文件类型并解析
elements: List[Element] = partition_xlsx(filename="销售数据统计.xlsx", languages=["zho"])

# 元素的元数据
print(elements[0].metadata.__dict__)
print("===========================")

# 元素的文本内容
print(elements[0].text)
print("===========================")

# 元素的类型
print(elements[0].category)
print("===========================")

4. CSV 文档解析

parse_file_with_unstructured("训练数据.csv")
from unstructured.partition.csv import partition_csv
from typing import List
from unstructured.documents.elements import Element

# 使用 partition_csv 函数检测 csv 文件类型并解析
elements = partition_csv(filename="训练数据.csv", encoding="utf-8")

# 元素的元数据
# print(elements[0].metadata.__dict__)
print("===========================")

# 元素的文本内容
print(elements[0].text[:400])
print("===========================")

# 元素的类型
print(elements[0].category)
print("===========================")

5. Word 文档解析

parse_file_with_unstructured("数组.docx")
from unstructured.partition.docx import partition_docx
from unstructured.partition.doc import partition_doc
from typing import List
from unstructured.documents.elements import Element

# 使用 partition_docx 函数检测 word 文件类型并解析,include_page_breaks 当文档支持“分页”时,以标识不同页的边界
elements = partition_docx(filename="数组.docx", encoding="utf-8", include_page_breaks=True)

# 元素的元数据
print(elements[0].metadata.__dict__)
print("===========================")

# 元素的文本内容
print(elements[0].text[:400])
print("===========================")

# 元素的类型
print(elements[0].category)
print("===========================")

6. Image 图片解析

模型下载说明

parse_file_with_unstructured("PDF解析截图.png")
from unstructured.partition.image import partition_image
from typing import List
from unstructured.documents.elements import Element

# 使用 partition_image 函数检测 png 类型并解析,strategy="ocr_only" 使用 ocr 来进行图片内容文字识别
elements = partition_image(filename="PDF解析截图.png",
                           strategy="ocr_only",
                           languages=["eng", "chi_sim"],
                           include_page_breaks=False)

# 元素的元数据
print(elements[0].metadata.__dict__)
print("===========================")

# 元素的文本内容
print(elements[0].text[:400])
print("===========================")

# 元素的类型
print(elements[0].category)
print("===========================")

需要注意

7. PDF 文件解析

parse_file_with_unstructured("甬兴证券 -AI 行业点评报告:海外科技巨头持续发力 AI,龙头公司中报业绩亮眼 .pdf")

表格提取功能已集成至 unstructured 库核心模块,无需再向 unstructured-inference 传递 extract_tables 参数。通过 elements 对象的 category 属性可精准筛选表格元素。

from unstructured.partition.pdf import partition_pdf
from typing import List
from unstructured.documents.elements import Element

# 使用 partition_pdf 函数检测 pdf 类型并解析
elements = partition_pdf(filename="甬兴证券 -AI 行业点评报告:海外科技巨头持续发力 AI,龙头公司中报业绩亮眼 .pdf",
                         strategy="hi_res", # 使用 hi_res 模式进行高精度解析
                         extract_images_in_pdf=True, # 提取 pdf 中的图片
                         extract_image_block_types=["Table", "Image"], # 提取表格和图片
                         extract_image_block_output_dir="./images", # 保存图片到 images 目录
                         languages=["eng", "zho"],
                         split_pdf_page=True, # 大文件分块处理,优化性能
                         infer_table_structure=True, # 是否尝试推断表格结构,会下载一个 ocr 模型
                         include_page_breaks=True) # 是否包含页码信息

# 元素的元数据
print(elements[0].metadata.__dict__)
print("===========================")

# 元素的文本内容
print(elements[0].text[:400])
print("===========================")

# 元素的类型
print(elements[0].category)
print("===========================")

8. Element 对象核心字段

返回的 Element 对象包含以下核心字段:

9. category 元素类型详解

10. metadata 元数据详解

11. 下游 RAG 应用优化

在检索增强生成(RAG)系统中:

12. 额外配置:指定 OCR Agent

如果你想切换 OCR 引擎(例如用 Paddle OCR 做中文识别更好),可以在环境中设置 OCR_AGENT

使用 Tesseract(默认)

export OCR_AGENT="unstructured.partition.utils.ocr_models.tesseract_ocr.OCRAgentTesseract"

或使用 Paddle OCR(若已安装)

export OCR_AGENT="unstructured.partition.utils.ocr_models.paddle_ocr.OCRAgentPaddle"

并确保你安装了对应依赖(Tesseract 二进制 + 语言包,或 Paddle、Google SDK)。这能显著影响中文识别质量。

3.6. 常见问题与解决方案

partition_pdf 导入错误处理

hi_res 本地安装复杂问题

表格转换结果错位修正

中文识别问题

混合中英文本方向 / 版式混乱

性能优化策略(经验型):


四:LlamaIndex 框架介绍

4.1. LlamaIndex 介绍

LlamaIndex 是一个为开发「上下文增强」的大语言模型应用的框架(也就是 SDK)。上下文增强,泛指任何在私有或特定领域数据基础上应用大语言模型的情况。例如:

Pasted image 20260715175856.png

4.2. LlamaIndex 的核心模块

Pasted image 20260715180023.png

4.3. 框架对比:LangChain vs LlamaIndex

对比维度 LangChain LlamaIndex(原 GPT Index)
定位与设计 通用型 LLM 应用框架(可构建 Agent、Tool、RAG 等各种系统) 专注文档理解与 RAG 的索引、检索、问答框架
核心理念 “链式调用(Chains)” 和 “工具组合(Tools)”,强调可编排性 “数据接口(Data Index + Query Engine)”,强调数据到知识的映射
文档加载能力 DocumentLoader
支持多格式文档(PDF、HTML、TXT)
深度集成解析器(如 LlamaParse、Unstructured、PandasReader)
数据解析深度 主要提取纯文本,结构化需手动实现 提供高层结构抽象(Document → Node → Index),保留层级关系
索引结构 VectorStore(Chroma、FAISS、Milvus 等)为核心,开发者需手动管理 提供多种索引:VectorStoreIndex
, SummaryIndex
, KnowledgeGraphIndex
上下文压缩 / rerank 需额外配置 Reranker 或 ContextCompressor 原生支持 Context Compression / Node PostProcessor
多文档检索 可通过 MultiQueryRetriever
ParentDocumentRetriever
实现
内置 ComposableGraph
实现多索引融合检索
Agent 能力 强(LangChain 是 Agent 生态核心框架) 弱(更偏向数据检索与知识问答)
生态与扩展性 最大的 LLM 生态,插件、工具链最丰富 与数据密集型 RAG 项目结合最紧(适合文档知识库类应用)
适用场景 多步骤推理、Agent 系统、工具调用、企业助手 文档问答、知识检索、企业知识库、研究型报告分析
示例语法简洁度 代码偏工程风格,组件组装较多 封装层高,一行代码即可构建 query engine
性能优化方向 优化在链路编排与检索召回效率 优化在文档解析、chunk 切分与上下文压缩
代表项目 Chatbot、智能助理、Agent 系统、数据问答 企业知识库问答、PDF 报告分析、学术 RAG 系统

五:LlamaIndex 集成与进阶

5.1. LlamaIndex 常用组件

常用组件:

from llama_index.core import SimpleDirectoryReader
from llama_parse import LlamaParse

# 如果文档结构复杂,优先使用 LlamaParse
# parser = LlamaParse(api_key="YOUR_LLAMA_CLOUD_API_KEY")
# documents = parser.load_data("sample.pdf")

# 或者使用简单读取器
documents = SimpleDirectoryReader(input_files=["RAG评估.md"]).load_data()

print(documents[0].metadata)
print("===========================")
print(documents[0].text)
print("===========================")

5.2. LlamaIndex 集成 unstructured

LlamaIndex 与Unstructured 的关系

因此:

两种集成方式对比

对比点 使用 unstructured.partition 直接解析 使用 LlamaIndex UnstructuredReader
底层调用 直接调用 unstructured 官方API(partition() 内部封装了 partition(),简化调用
灵活度 可访问所有底层参数(如 strategyhi_res_modelocr_languages 封装后部分参数隐藏,仅暴露常用接口
可控性 可自定义处理流程(过滤、正则、chunk 策略) 自动化程度高,但定制难度大
集成便捷性 需手动将结果转为 Document 自动输出为 Document 列表
依赖管理 由开发者决定何时安装哪些后端(pdfminer, tesseract) 自动导入必要模块,错误提示更友好
适用场景 高级研发 / 多源异构文档处理 快速原型 / 小规模项目

方式一:直接使用 UnstructuredReader

推荐场景:快速测试 / 教学 / 单格式文件读取

优点:

缺点:

from llama_index.readers.file.unstructured import UnstructuredReader
from pathlib import Path

reader = UnstructuredReader()
documents = reader.load_data(file=Path("甬兴证券 -AI 行业点评报告:海外科技巨头持续发力 AI,龙头公司中报业绩亮眼 .pdf"))

print("打印列表长度:" + str(len(documents)))
print("==================================")
print("打印解析的文本内容:" + documents[0].text[:100])
print("==================================")
print("打印元数据信息:" + str(documents[0].metadata))

方式二:独立使用 unstructured.partition + 自定义逻辑

推荐场景:生产级 RAG 应用 / 多格式数据管线 / 高可控性需求

优点:

from unstructured.partition.auto import partition
# 使用 LlamaIndex 的 Document 对象,将解析后的元素转换为 Document 对象
from llama_index.core import Document

# 使用 partition 函数自动检测文件类型并解析
elements = partition(
    filename="甬兴证券 -AI 行业点评报告:海外科技巨头持续发力 AI,龙头公司中报业绩亮眼 .pdf",
    strategy="hi_res",
    split_pdf_page=True,
    infer_table_structure=True,
    languages=["eng", "chi_sim"])

# 将解析后的元素转换为 Document 对象
docs = [
    Document(text=e.text,
             metadata={"source":"甬兴证券 -AI 行业点评报告:海外科技巨头持续发力 AI,龙头公司中报业绩亮眼 .pdf",
                       "type": e.category})
    for e in elements]

方式三:最佳混合方案(推荐实践)

结合两者优势:

from llama_index.readers.file.unstructured import UnstructuredReader
from unstructured.partition.auto import partition
from llama_index.core import Document
from pathlib import Path

def smart_load(file_path):
    """
    智能文档加载器:根据文件类型选择最佳解析策略
    Args:
        file_path: 文件路径
    Returns:
        解析后的 Document 对象列表
    """
    file_path = Path(file_path)
    file_ext = file_path.suffix.lower()

    # 定义复杂文件类型(需要高精度解析)
    complex_types = {
        '.pdf',     # PDF 文档(可能包含表格、图像、复杂布局)
        '.png', '.jpg', '.jpeg', '.gif', '.bmp', '.tiff',  # 图片文件(需要 OCR)
        '.docx', '.doc',  # Word 文档(可能包含复杂格式)
        '.pptx', '.ppt',  # PowerPoint(复杂布局)
        '.xlsx', '.xls'   # Excel(表格结构)
    }
    # 简单文件类型(可以用 Reader 直接处理)
    simple_types = {
        '.txt', '.md', '.csv', '.html', '.xml', '.json'
    }

    if file_ext in complex_types:
        # 复杂文件使用底层解析,获得更好的结构识别
        try:
            elements = partition(
                filename=str(file_path),
                # 使用 hi_res 模式进行高精度解析
                strategy="hi_res",
                # 支持中文、英文
                languages=["eng", "chi_sim"],
                # 推断表格结构
                infer_table_structure=True
            )
            # 将解析元素转换为 Document 对象
            return [Document(text=e.text, metadata={
                "source": str(file_path),
                "element_type": type(e).__name__,
                "file_type": file_ext
            }) for e in elements if e.text.strip()]  # 过滤空文本
        except Exception as e:
            # 回退到 Reader
            reader = UnstructuredReader()
            return reader.load_data(file=file_path)
    else:
        # 简单文件或未知类型优先使用Reader
        try:
            # 直接使用 Reader 进行简单解析
            reader = UnstructuredReader()
            # 加载解析后的文档,返回 Document 对象列表
            docs = reader.load_data(file=file_path)
            return docs
        except Exception as e:
            # 回退到底层解析
            elements = partition(filename=str(file_path), strategy="auto")
            return [Document(text=e.text, metadata={"source": str(file_path)}) for e in elements]

这种做法在实际 RAG 框架开发中非常常见:

总结一句话

场景 推荐方式 理由
初学者 / 快速 Demo UnstructuredReader() 封装好,一行搞定
RAG 系统 UnstructuredReader() 输出直接是 Document
生产系统 / 多文件管线 partition() 可完全控制 OCR / 分块 / 过滤
精细元数据追踪(页码 / 坐标 / 字体) partition() 元数据更丰富

5.3. 基础索引案例实现

基础文档解析实现

from llama_index.core import VectorStoreIndex
from llama_index.embeddings.openai import OpenAIEmbedding
from llama_index.llms.openai import OpenAI          
from llama_index.core.settings import Settings
from dotenv import load_dotenv
load_dotenv()

# 设置为全局默认 Embedding 模型
Settings.embed_model = OpenAIEmbedding(
    model="text-embedding-3-small",
    api_key=os.getenv("OPENAI_API_KEY"),
    api_base=os.getenv("OPENAI_BASE_URL", "https://api.openai.com/v1")
)

# 设置为全局默认 LLM
Settings.llm = OpenAI(
    model="gpt-3.5-turbo",
    api_key=os.getenv("OPENAI_API_KEY"),
    api_base=os.getenv("OPENAI_BASE_URL", "https://api.openai.com/v1")
)

# 解析 pdf 文档
documents = smart_load("甬兴证券 -AI 行业点评报告:海外科技巨头持续发力 AI,龙头公司中报业绩亮眼 .pdf")

# 构建索引
index = VectorStoreIndex.from_documents(documents)

# 生成查询引擎
query_engine = index.as_query_engine()

# 测试提问
response = query_engine.query("请用中文总结这些文档的主要内容")
print(response)

六:特定领域应用

金融领域:财报解析与问答

医疗领域:文献分析与检索

法律领域:合同解析与条款提取

教育领域:教材内容分析与问答


学习资源与参考

官方资源

LlamaIndex 的更多功能

此外,LlamaIndex 针对生产级的 RAG 系统中遇到的各个方面的细节问题,总结了很多高端技巧(Advanced Topics),对实战很有参考价值,非常推荐有能力的同学阅读。

技术文档