RAG系列:解析優化 - 不同文件類型統一轉換成Markdown
引言
在 RAG 系統中,文件解析是構建知識庫和實現高效信息檢索的關鍵環節。隨著系統需要處理的文件類型日益增多(如PDF、Word、Excel、PPT、HTML等),如何高效解析并利用這些異構數據成為核心挑戰。
由于每種文件類型的數據結構和內容(如PDF的復雜排版、Excel的表格結構、PPT的多媒體元素)都不一致,在實際的應用場景中,我們需要對不同文件類型實現不同的解析器,解析之后還需要根據不同文件的結構實現對應的文檔分塊方法。整體來說,這樣的系統實現和維護成本都是比較高的,要把每種文件類型的解析都做到比較好是比較繁瑣的,但是文件解析如果做的不好,就會不同程度地影響后續 RAG 系統流程,該種方案流程如下:
可能你會問,為什么需要這么多的解析器呢?不能把各種文件直接讀取成字節流后丟給 LLM 嗎?以當前 LLM 的能力,無論是什么內容都需要得組織成文本,它才能處理。而 PDF、Word、網頁等其實都是富文本,里面除了文字,通常還包含公式、圖片、表格、統計圖,排版方式也各不相同,甚至其中的格式也是很重要的內容,而這也是文件解析的作用,把各種富文本轉換成 LLM 好理解的純文本的過程。
為什么是 Markdown
- 統一數據格式,降低解析復雜度:Markdown 以純文本形式保留了基本結構(標題、列表、代碼塊、表格),同時去除了冗余的格式信息(如字體、顏色);所有文件統一轉換為 Markdown 后,RAG 系統只需針對一種格式進行后續處理(如分段、分句、實體提取),顯著降低代碼復雜度。
- 提升信息提取與語義理解能力: Markdown 的標題語法(
#
、##
)可直接映射為文檔結構,便于模型快速定位關鍵信息;Markdown 的表格和代碼塊標記(如`````)能保留原始數據的排版邏輯,幫助模型理解數據關系;通過-
、>
等符號標記的列表和引用內容,可輔助模型識別要點和上下文關聯。去除原始文件中的樣式、腳注、頁眉頁腳等非核心內容,使文本更聚焦于語義信息。 - 優化存儲與檢索效率:Markdown 文件體積小,適合大規模知識庫的存儲和快速加載;基于 Markdown 的結構化文本可直接生成倒排索引,提升檢索速度和準確性。
- 增強可維護性與可解釋性:開發者可直接通過文本編輯器查看 Markdown 內容,便于調試和驗證解析結果;Markdown 文件易于進行版本管理(如Git),支持團隊協作和歷史追溯。
統一轉換成 Markdown 之后,可優化成如下流程:
統一轉成 Markdown 之后,只需要將注意力專注在 Markdown 解析和處理上就可以了,這樣可以大幅降低整個系統的開發、維護成本。
而將 PDF 轉 Markdown、Word 轉 Markdown 等有很多成熟的工具可以使用這樣就沒必要自己寫解析器來處理了,比如 MinerU[1] - 上海人工智能實驗室開源的項目、MarkItDown - Microsoft AutoGen 團隊開源的項目等等,這些項目都是專門用于將各種文件格式轉換為 Markdown,可以參考借鑒和使用。
本文完整代碼地址[2]
MinerU 使用方法
今天就以 CPU 運行的方式來簡單介紹下 MinerU 的使用方法(沒有找到類似 Nodejs 的工具,只能使用 Python 來介紹了...),更多內容到 Github 上使用cpu快速體驗[3]查看。
安裝 magic-pdf
conda create -n .venv 'pythnotallow=3.12' -y
conda activate .venv
pip install -U "magic-pdf[full]" -i https://mirrors.aliyun.com/pypi/simple
以上是官方示例。如果你沒有安裝 conda(不建議使用 uv,有兼容性問題),可以直接使用 Python 的命令創建虛擬空間安裝:
python3.12 -m venv .venv
source .venv/bin/activate
pip install -U "magic-pdf[full]" -i https://mirrors.aliyun.com/pypi/simple
(截止到發文時)這里需要注意,Python 的版本需要在 3.10 ~ 3.12 之間,不然 magic-pdf 會被自動降級到 0.6.1。
下載模型文件
模型文件可以從 Hugging Face 或 Model Scope 下載,由于網絡原因,國內用戶訪問 HF 可能會失敗,請使用 ModelScope。
pip install modelscope
wget https://gcore.jsdelivr.net/gh/opendatalab/MinerU@master/scripts/download_models.py -O download_models.py
python download_models.py
修改配置文件【可選】
完成第二個步驟后,腳本會自動生成用戶目錄下的 magic-pdf.json 文件,并自動配置默認模型路徑。 您可在【用戶目錄】下找到 magic-pdf.json 文件。
windows的用戶目錄為 "C:\Users\用戶名",
linux用戶目錄為 "/home/用戶名",
macOS用戶目錄為 "/Users/用戶名"。
您可修改該文件中的部分配置實現功能的開關,如表格識別功能:
{
// other config
"layout-config":{
"model":"doclayout_yolo"
},
"formula-config":{
"mfd_model":"yolo_v8_mfd",
"mfr_model":"unimernet_small",
"enable":true// 公式識別功能默認是開啟的,如果需要關閉請修改此處的值為"false"
},
"table-config":{
"model":"rapid_table",
"sub_model":"slanet_plus",
"enable":true,// 表格識別功能默認是開啟的,如果需要關閉請修改此處的值為"false"
"max_time":400
}
}
代碼示例
import os
from magic_pdf.data.data_reader_writer import FileBasedDataWriter, FileBasedDataReader
from magic_pdf.data.dataset import PymuDocDataset
from magic_pdf.model.doc_analyze_by_custom_model import doc_analyze
from magic_pdf.config.enums import SupportedPdfParseMethod
defmain():
# args
pdf_file_name = "2024少兒編程教育行業發展趨勢報告.pdf"# replace with the real pdf path
name_without_suff = pdf_file_name.split(".")[0]
# prepare env
local_image_dir, local_md_dir = "output/images", "output"
image_dir = str(os.path.basename(local_image_dir))
os.makedirs(local_image_dir, exist_ok=True)
image_writer, md_writer = FileBasedDataWriter(local_image_dir), FileBasedDataWriter(
local_md_dir
)
# read bytes
reader1 = FileBasedDataReader("")
pdf_bytes = reader1.read(pdf_file_name) # read the pdf content
# proc
# Create Dataset Instance
ds = PymuDocDataset(pdf_bytes)
# inference
if ds.classify() == SupportedPdfParseMethod.OCR:
infer_result = ds.apply(doc_analyze, ocr=True)
# pipeline
pipe_result = infer_result.pipe_ocr_mode(image_writer)
else:
infer_result = ds.apply(doc_analyze, ocr=False)
# pipeline
pipe_result = infer_result.pipe_txt_mode(image_writer)
# draw model result on each page
infer_result.draw_model(os.path.join(
local_md_dir, f"{name_without_suff}_model.pdf"))
# get model inference result
model_inference_result = infer_result.get_infer_res()
# draw layout result on each page
pipe_result.draw_layout(os.path.join(
local_md_dir, f"{name_without_suff}_layout.pdf"))
# draw spans result on each page
pipe_result.draw_span(os.path.join(
local_md_dir, f"{name_without_suff}_spans.pdf"))
# get markdown content
md_content = pipe_result.get_markdown(image_dir)
# dump markdown
pipe_result.dump_md(md_writer, f"{name_without_suff}.md", image_dir)
# get content list content
content_list_content = pipe_result.get_content_list(image_dir)
# dump content list
pipe_result.dump_content_list(
md_writer, f"{name_without_suff}_content_list.json", image_dir)
# get middle json
middle_json_content = pipe_result.get_middle_json()
# dump middle json
pipe_result.dump_middle_json(md_writer, f'{name_without_suff}_middle.json')
if __name__ == "__main__":
main()
運行之后,會在本地生成提取到的圖片、中間過程文件以及最終生成的 Markdown 文件。
生成的 Markdown 文檔:
原始文檔:
可以看到,提取的結果還是比較符合預期的,圖片也都正確提取出來了。
結語
今天通過簡單介紹 MinerU 的使用方法的同時,來給大家帶來一種文件解析優化的思路,希望能對大家有所啟發。
引用鏈接
[1]
MinerU: https://github.com/opendatalab/MinerU
[2]
本文完整代碼地址: https://github.com/laixiangran/ai-learn-python/blob/main/app/file2Markdown.py
[3]
使用cpu快速體驗: https://github.com/opendatalab/MinerU/blob/master/README_zh-CN.md#%E4%BD%BF%E7%94%A8cpu%E5%BF%AB%E9%80%9F%E4%BD%93%E9%AA%8C