PDF → RAGFlow 拆分(三件套 + 方案A 单文件合并)
将 PDF 拆成 text.md(正文)、tables.md(表格)、images/(图片)三件套,再用 merge_md.py 合并为 merged.md 单文件(表格插入正文引用处、同 chunk),保证 RAGFlow 问答时正文与表格同时命中。RAGFlow 对 MD 兼容性最佳,目标是让它「零歧义」识别。
前置条件
- 托管 Python venv(Windows):
binaries/python/envs/default/Scripts/python.exe - 依赖:
pymupdf、pdfplumber、Pillow - 安装命令:
<venv-python> -m pip install pymupdf pdfplumber Pillow - 待拆分 PDF 的绝对路径
工作流(三步,务必按序执行)
步骤一:检测(跨页断表 + 合并单元格)→ 生成处理记录表 → 交互确认
- 运行
scripts/build_merge_rules.py <PDF路径> -o <输出目录> [--exclude 不合并关键词...] [--fix-span "标题:页数"...] - 脚本自动:
- 检测跨页断表(表序列+标题归属分组,跨 2 页以上为候选)
- 检测合并单元格(存在向下填充的表,排除被合并的续表块)
- 生成<输出目录>/<原始文件名>-处理记录表.md(含两部分,状态=待拆分):- 一、跨页断表记录:表标题/涉及页/表块位置/状态
- 二、合并单元格记录:表名/涉及页/表块位置/状态
- 交互式逐个确认每个候选「此表合并?(y/n)」(回车=合并;可用
--include 1,3,5跳过交互直接指定)
- 用户核对 PDF 原文:确认合并/不合并、修正跨页范围(--fix-span截断页数、--exclude排除) - 确认完成后生成
merge_rules.json(拆分必需输入)——确认好之后再拆分
步骤二:拆分生成三件套
- 运行
scripts/split_pdf.py <PDF路径> -o <输出目录> -m merge_rules.json [-t 文档标题] - 输出目录下生成:
-text.md:正文,按页## 第 N 页分段;图片相对引用images/xxx.png;表格区域用引用标识关联 tables.md
-tables.md:全部表格,跨页断表已合并(保留起始页标题/锚点,来源标注续表页),锚点回链 text.md;合并单元格已按方案B向下填充
-images/:图片按 xref 去重,方向已自动校正 - 更新处理记录表:运行
scripts/update_record.py -o <输出目录>
- 跨页断表记录:状态更新为「✅ 已拆分」或「✗ 未合并」
- 合并单元格记录:状态更新为「✅ 已拆分」
- 用户据此核对:哪些已拆分、哪些未拆分,再拿 tables.md 与原始 PDF 对比
步骤三:合并生成单文件(方案 A)+ 上传配置
> ⚠️ RAGFlow 不跨文件关联:text.md 里的「详见 tables.md#tbl-pXX-tY」只是超链接文本,RAGFlow 不会 follow 链接把目标表格拉进同一 chunk,问答时只能命中正文、看不到表格本体。必须合并成单文件再上传。
- 运行
scripts/merge_md.py <text.md> <tables.md> -o <输出目录>/merged.md
- 每个表格在其第一次被引用的正文位置正下方插入完整表格块(81 表 → 81 处插入)
- 表格块保留### 表格 第X页-表Y标题(作### ## #切块边界)+> 来源溯源行
- 引用行重写:「详见 tables.md#xxx」→「见下表」/「已并入上表」,无残留死链
- 图片相对引用 images/xxx.png 不变 - RAGFlow 上传:只上传 merged.md + images/(text.md / tables.md 不再单独上传)
- 按
references/ragflow-config.md向用户说明 RAGFlow 知识库配置要点
关键避坑(均已固化为脚本逻辑)
- 取图片 xref:用
page.get_images(full=True)拿 xref +page.get_image_rects(xref)拿位置。⚠️ 不要用get_image_info()的字段取 xref——它返回的是number而非 xref,会导致图片全丢。 - 图片上下颠倒:PDF 中图像 transform 矩阵第 4 位(d)<0 表示垂直翻转嵌入,直接提取的位图是倒的。用
page.get_image_info(xrefs=[xref])检查 transform,d<0 时用 PillowFLIP_TOP_BOTTOM翻转。 - 跨页断表检测:pdfplumber 可能把同页多个表合并为一个表块,且列数相同的续表会跨页误链。正确算法:把所有表块排成序列,按「表上方是否有标题」分组(有标题=新组开头,其后无标题表=续表),组跨 2 页及以上为候选。用户修正的跨页范围优先于自动检测。
- 合并表去重表头:续表块首行若与起始表表头归一化后相同,跳过该行。
- 正文跳过表格文字:用「块中心是否落入表格 bbox」判定,避免与 tables.md 重复。
- markdown 表格生成:过滤全空行、单元格
|转义为\|、首行后加|---|分隔。 - 合并单元格向下填充(方案B):PDF 的 rowspan 合并单元格提取后内容只在首行、后续行为空;标准 md 不支持合并单元格,为避免 RAG 切块时丢语义,用 forward-fill 把空单元格填充为同列上方最近的非空值。规则(防误填):
- 默认填满整个表(到表头列数),但列名含「LOD」/「备注」等数据列关键字的列跳过填充(避免数据列被错误继承)
- 表头行不参与填充(避免列名向下复制)
- 原始全空行跳过填充(pdfplumber 会把空白行提取为一行,填充后变伪数据行)
- 跨页合并时续表块首行若等于起始表数据首行(跨页重复首行),也需跳过去重 - PDF 原文漏填需人工补全:若某单元格 PDF 原文就是空白(非合并延续,如表4.5-1 明细表设置中 5 个明细表的字段/排序列制版漏填),forward-fill 无法自动补,需人工按同表同类内容补全后再跑拆分(或在生成后手工修订 tables.md)。
- 处理记录表:步骤一 build_merge_rules.py 生成「<原始文件名>-处理记录表.md」(跨页断表记录 + 合并单元格记录,含状态列,待拆分版);拆分后 update_record.py 更新状态(已拆分/未合并)。记录表不进入 RAGFlow,供执行中核对。
- 跨页合并表列对齐:续表块(PDF 下半页的表块)列数常少于起始表块(PDF 上半页省略了某些分类列),按 max 列数末尾补齐会导致续表行内容向左偏移、跨页列对不齐。修复:合并时把续表块的每行按起始表头列数前面补空对齐,使 LOD100/备注等数据列位置与起始页严格一致。
- 表头空列名自动修复:PDF 表格有物理列分隔但某些列无标题文字(如建筑构件参数表第28页-表2的「系统」「属性1」「属性2」等位置无标题),生成 tables.md 后 RAGFlow 解析时会把空表头列当独立列(自动命名如「属性信息」)导致多列错位。修复:split_pdf.py 在生成 markdown 时对表头空列做推断填名——数据列若全是 LOD 符号(-/△/▲)→ 推断 LODxxx;若是数字编号 → 序号;第 0 列空 → 序号;其他 → 占位「列N」。
- RAGFlow 不跨文件关联链接:分文件上传 text.md + tables.md 时,正文里的「详见 tables.md#锚点」只是普通文本,问答召回只会命中正文、不带表格。必须用 merge_md.py 合并为 merged.md 单文件(表格插入正文引用处、同 chunk),再上传 merged.md + images/。
校验(生成后必做)
- 表标题数 = 锚点数 = text.md 引用数(双向一致)
- 无重复「页码-表号」组合
- 表格行格式零异常(
|开头、行尾|、---分隔) - text.md 引用的图片文件都存在于 images/
- merged.md:表格标题数 = 81(或 tables.md 表数)、无重复锚点、引用行无残留 tables.md 死链、图片引用齐全
- 校验通过后再交付或上传
交付
- 展示 merged.md(方案 A 上传版)、text.md、tables.md
- 说明 ragflow_upload/ 路径与 RAGFlow 配置要点(见 references/ragflow-config.md)
- 提示:RAGFlow 上传 merged.md + images/,不要单独上传 text.md / tables.md