ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

RAG 实战教程(三):PDF、Word、Markdown 多文档知识库实战——文档解析、清洗与智能索引

2026/8/24 14:51:26 拓冰建站 浏览量
RAG 实战教程(三):PDF、Word、Markdown 多文档知识库实战——文档解析、清洗与智能索引 RAG 系列第三篇。本篇将在第二篇「Python Qdrant 搭建第一个 RAG 知识库」的基础上继续升级实现一个更加接近真实企业场景的知识库系统。我们将支持PDF 文档Word 文档Markdown 文档HTML 网页文档清洗Metadata 管理增量索引多格式统一入库最终实现企业文档 | | ↓ PDF / Word / Markdown / HTML | ↓ Document Loader | ↓ 文本清洗 | ↓ 结构化 Chunk | ↓ Embedding | ↓ Qdrant 向量数据库 | ↓ RAG 问答系统一、为什么真实 RAG 最难的是数据而不是模型很多人在第一次接触 RAG 时会认为RAG 的核心就是调用大模型。实际上真正落地之后你会发现模型往往不是最大的问题。企业真正困难的是我的资料在哪里 如何读取 如何切分 如何保持结构 如何避免垃圾内容进入知识库 如何知道答案来自哪一页例如一个企业知识库可能包含产品文档 ├── 产品介绍.pdf ├── API 接口文档.md ├── 用户手册.docx ├── 售后规则.html ├── 内部培训 PPT └── 数据库说明文档这些文件有几个共同问题1. 文件格式不同PDF文字 图片 表格 扫描页 页眉页脚Word标题 正文 列表 表格 图片Markdown标题层级 代码块 列表 链接HTML标签 脚本 广告 导航如果直接丢给 Embedding效果通常不会很好。2. 文档结构容易丢失例如原始 PDF第三章 用户退款规则 3.1 普通退款 用户购买后 7 天内可以申请退款。 3.2 特殊情况 超过 7 天需要人工审核。如果简单提取可能变成第三章 用户退款规则 3.1 普通退款 用户购买后7天内可以申请退款 3.2特殊情况 超过7天需要人工审核标题关系丢失。Embedding 不知道普通退款属于用户退款规则3. 企业需要引用来源一个真正可用的 RAG不是答案 可以退款。而应该答案 用户购买后 7 天内可以申请退款。 来源 用户协议.pdf 第 18 页 第三章 用户退款规则所以我们必须保存{content:...,metadata:{source:用户协议.pdf,page:18,title:退款规则}}二、第三篇最终目标这一篇完成之后我们的 RAG 架构数据接入层 PDF | Word | Markdown | HTML ↓ Document Loader ↓ Text Cleaner ↓ Smart Chunk ↓ Embedding ↓ Qdrant ↓ 用户问题 ↓ Query Embedding ↓ Vector Search ↓ Context ↓ LLM ↓ 答案 来源三、项目结构升级第二篇项目rag-demo/升级rag-enterprise/ ├── data/ │ │ ├── pdf/ │ ├── word/ │ ├── markdown/ │ └── html/ │ ├── loaders/ │ │ ├── pdf_loader.py │ ├── word_loader.py │ ├── markdown_loader.py │ └── html_loader.py │ ├── processors/ │ ├── cleaner.py │ └── splitter.py ├── index/ │ ├── embedding.py │ └── vector_store.py ├── pipeline/ │ └── index_pipeline.py ├── app.py ├── requirements.txt └── README.md这就是一个更加接近生产环境的结构。四、安装依赖创建requirements.txt内容openai qdrant-client python-dotenv pymupdf python-docx beautifulsoup4 markdown安装pipinstall-rrequirements.txt五、统一 Document 数据结构这是整个系统非常重要的一步。不要让不同 Loader 返回不同格式。例如PDF 返回{text:...,page:1}Word 返回{content:...,}后面一定会混乱。所以我们定义统一格式{content:,metadata:{source:,type:,page:None,title:}}创建models.py代码fromdataclassesimportdataclassfromtypingimportOptionaldataclassclassDocument:content:strsource:strdoc_type:strpage:Optional[int]Nonetitle:Optional[str]Nonedefmetadata(self):return{source:self.source,type:self.doc_type,page:self.page,title:self.title}以后所有 Loader都返回Document六、PDF 文档解析PDF 是企业 RAG 中最常见的数据来源。安装pipinstallpymupdfPyMuPDF 导入importfitz创建loaders/pdf_loader.py代码importfitzfrommodelsimportDocumentdefload_pdf(path):pdffitz.open(path)documents[]forpage_number,pageinenumerate(pdf,start1):textpage.get_text()ifnottext.strip():continuedocuments.append(Document(contenttext,sourcepath,doc_typepdf,pagepage_number))returndocuments测试fromloaders.pdf_loaderimportload_pdf docsload_pdf(data/pdf/example.pdf)fordocindocs:print(doc.page)print(doc.content[:200])输出1 RAG 是一种检索增强生成技术... 2 Embedding 可以把文本转换成向量...七、PDF 最大的问题扫描件现实中很多 PDF不是文字 PDF。例如扫描合同.pdf 扫描发票.pdf 扫描说明书.pdf打开你能看到文字。但是程序page.get_text()返回空原因里面其实是图片。需要OCR流程PDF ↓ 图片 ↓ OCR ↓ 文字 ↓ Chunk常见方案PaddleOCR Tesseract Azure OCR Google Vision后续企业版 RAG 会专门讲 OCR。八、Word 文档解析Word 使用python-docx安装pipinstallpython-docx创建loaders/word_loader.py代码fromdocximportDocumentasDocxDocumentfrommodelsimportDocumentdefload_word(path):docDocxDocument(path)texts[]forparagraphindoc.paragraphs:textparagraph.text.strip()iftext:texts.append(text)content\n.join(texts)return[Document(contentcontent,sourcepath,doc_typeword)]测试docsload_word(data/word/manual.docx)print(docs[0].content)九、Markdown 文档解析Markdown 在技术知识库里非常常见。例如GitHub Wiki 技术博客 API 文档 项目 README创建loaders/markdown_loader.py代码frommodelsimportDocumentdefload_markdown(path):withopen(path,r,encodingutf-8)asf:contentf.read()return[Document(contentcontent,sourcepath,doc_typemarkdown)]但是 Markdown 有一个优势它有结构。例如# RAG ## 什么是 RAG 内容... ## 什么是 Embedding 内容...我们后面可以根据# ## ###进行智能切分。十、HTML 网页解析很多企业知识来自官网 帮助中心 API 文档 博客HTML 需要清理删除script style 导航 广告安装pipinstallbeautifulsoup4创建loaders/html_loader.py代码frombs4importBeautifulSoupfrommodelsimportDocumentdefload_html(path):withopen(path,r,encodingutf-8)asf:htmlf.read()soupBeautifulSoup(html,html.parser)fortaginsoup([script,style]):tag.decompose()textsoup.get_text(\n)return[Document(contenttext,sourcepath,doc_typehtml)]十一、统一 Loader 接口现在PDFload_pdf()Wordload_word()Markdownload_markdown()HTMLload_html()虽然可以工作。但是项目越来越大后调用会很乱。所以统一创建loaders/__init__.py代码fromloaders.pdf_loaderimportload_pdffromloaders.word_loaderimportload_wordfromloaders.markdown_loaderimportload_markdownfromloaders.html_loaderimportload_htmldefload_document(path):ifpath.endswith(.pdf):returnload_pdf(path)ifpath.endswith(.docx):returnload_word(path)ifpath.endswith(.md):returnload_markdown(path)ifpath.endswith(.html):returnload_html(path)raiseException(f不支持文件类型:{path})以后新增格式只需要增加ifsuffix:returnloader()即可。十二、文档清洗原始文本通常很脏。例如PDF第 1 页 公司名称 正文内容 第 2 页 公司名称 正文内容页眉重复。需要清洗。创建processors/cleaner.py代码importredefclean_text(text):# 删除多余空白textre.sub(r\s, ,text)# 删除特殊字符textre.sub(r[^\w\s\u4e00-\u9fa5。,.!?],,text)returntext.strip()测试text RAG 是一种技术。 print(clean_text(text))输出RAG 是一种技术。十三、为什么清洗非常重要因为 Embedding 不是理解人类格式。它看到RAG RAG RAG 123456 公司名称这些垃圾内容。会影响向量质量最终导致搜索不准所以好的 RAG首先需要好的数据。未完下一部分继续写智能 Chunk、Metadata 索引、多文档入库、增量更新、完整企业级 Pipeline。