# RAG-cut 自研 AI 助手知识库 Demo —— 以**高质量文档切片**为核心,召回为辅。 重建文档 ingestion / 切片流水线,针对现有 AFE 知识库的两类问题: 1. **解析不足**:Word / PDF / Excel 中的图片、表格无法有效提取 2. **切分不佳**:同一逻辑段落被硬切到不同 chunk,图片丢失或错位 | 项 | 说明 | | --- | --- | | 仓库 | `http://192.168.3.110:3000/AFE_RAG/RAG-CUT.git` | | 需求文档 | [`docx/自研搭建AI助手知识库.pdf`](docx/自研搭建AI助手知识库.pdf) | | 样例文档 | `自研RAG代表文档/` | --- ## 功能概览 | 能力 | 说明 | | --- | --- | | 多格式解析 | pdf / doc / docx / ppt / pptx / ppsx / xlsx / csv / md / txt / html / json / xml / log / 常见图片 | | PDF 双引擎 | MinerU(优先)+ PyMuPDF 回退;统一噪声过滤(页眉页脚、页码、**目录 TOC**) | | 四种切分模式 | `default` · `delimiter` · `parent_child` · `by_row`(Demo 默认选结构感知) | | 语义切分 | PDF / Word 在 `default` 下走 `heading_layout_multimodal`;超长章节按子标题/长度拆分(不产父片) | | 父子标识符 | 独立模式 `parent_child`:子片检索、父片上下文 | | 表格切分 | `by_row`:自动识别表头 / 说明行;Q&A 表可按行 1 片 | | Demo UI | 原文预览 · 切片列表 · Markdown 预览 · **历史切片回看** · BM25 召回测试 | | CLI / API | `scripts/chunk_cli.py` · `/api/chunk` · `/api/results` · `/api/recall` | 未支持:`.wps`(可先转为 docx/pdf)。独立图片暂不做 OCR。 --- ## 快速开始 ### 1. 克隆与依赖 ```bash git clone http://192.168.3.110:3000/AFE_RAG/RAG-CUT.git cd RAG-CUT pip install -r requirements.txt ``` 另需本机工具(按文档类型): | 场景 | 依赖 | | --- | --- | | `.doc` / `.ppt` 等 | **LibreOffice**(`soffice`)或 Windows 上的 **Microsoft Word/PowerPoint** | | PDF 高精度解析 | MinerU(`requirements.txt` 已含 `mineru[all]`) | | MinerU 图内文字回填 | 本机 Tesseract(可选,见下方环境变量) | ### 2. 启动 Demo ```bash cd backend python run.py ``` 浏览器打开:**http://127.0.0.1:8000**(后端同时托管前端静态页)。 前后端分离开发时另开终端: ```bash cd frontend python run.py # http://127.0.0.1:5173 ,API 默认指向 :8000 ``` 可选参数:`--host 0.0.0.0 --port 8000 --no-reload`(后端)· `--port 5173`(前端) ### 3. 运行测试 ```bash cd backend python -m pytest tests/ -q ``` ### 环境变量(PDF) | 变量 | 默认 | 说明 | | --- | --- | --- | | `RAG_CUT_PDF_ENGINE` | `auto` | `auto` 优先 MinerU,失败回退 PyMuPDF;可强制 `mineru` / `pymupdf` | | `RAG_CUT_MINERU_CMD` | — | MinerU 可执行路径(不在 PATH 时) | | `RAG_CUT_MINERU_API_URL` | — | 复用已启动的 MinerU API,避免每次切分重复加载模型 | | `RAG_CUT_MINERU_TIMEOUT` | `540` | 单次 MinerU 解析超时(秒) | | `RAG_CUT_MINERU_OCR` | `1` | MinerU 图片无 OCR 时用 Tesseract 回填;`0` 关闭以加快切分 | 详见 [`docx/mineru-integration.md`](docx/mineru-integration.md)。 ### 运行栈 | 类别 | 选型 | | --- | --- | | Python | 3.12+ | | Web | FastAPI + Uvicorn | | PDF | MinerU / PyMuPDF + pdfplumber | | Excel | openpyxl | | 召回 | 内置 BM25(无向量库) | --- ## 技术架构 ``` 上传文件 │ ▼ 格式路由 (registry) │ ▼ 解析器 (parsers) ──► 有序 Block 流(heading / paragraph / table / image …) │ PDF:MinerU(优先)或 PyMuPDF → 噪声过滤 → 跨页表合并 ▼ 版面增强 (layout_meta) ──► 标题层级、图片/表格上下文、order_index │ ▼ 目录过滤 (filter_toc_blocks) ──► 全格式剔除「目录 / Contents」区 │ ▼ 切分策略 (split_policy / pdf_strategy) │ ▼ 切分器 (splitters) ──► Block 分组(含父子切片关联) │ ▼ 渲染器 (renderer) ──► Markdown Chunk + embedding_text / retrieval 标记 ``` **核心设计**:先解析为不可拆分的 Block,再按结构切分。`image` / `table` 为原子块,不会被拆到不同 chunk。 ### 目录结构 ``` RAG-cut/ ├── backend/ │ ├── rag_cut/ │ │ ├── models.py / pipeline.py / renderer.py / layout_meta.py │ │ ├── split_policy.py / retrieval.py │ │ ├── parsers/ # word / ppt / pdf / xlsx / text / image + MinerU │ │ │ └── pdf/ # PyMuPDF 流水线、噪声过滤、跨页表合并 │ │ └── splitters/ # default / heading / pdf_semantic / by_row / delimiter / parent_child │ ├── api/main.py # FastAPI + 静态前端 │ ├── tests/ │ └── run.py ├── frontend/ # Demo(index.html + css + js) ├── scripts/ # chunk_cli · run_all_samples ├── storage/ # uploads / assets / results(运行时,已 gitignore) ├── docx/ # 需求 PDF、MinerU / 腾讯云切分说明 ├── 自研RAG代表文档/ # 样例语料 └── requirements.txt ``` --- ## 各格式处理 统一流水线:**解析 → Block →(目录过滤)→ 切分 → Markdown Chunk**。 | 扩展名 | 解析器 | 要点 | 推荐切分 | | --- | --- | --- | --- | | `.pdf` | `PdfParser` | MinerU / PyMuPDF → 噪声过滤 → 跨页表合并 | `default`(语义,`max≈2600`) | | `.doc` `.docx` | `WordParser` | 转 PDF 后复用 PDF 流水线 | 同上 | | `.ppt` `.pptx` `.ppsx` | `PptParser` | 转 PDF;页码 ≈ 幻灯片序号 | `default` | | `.xlsx` `.xls` `.csv` | `SpreadsheetParser` | 检测 preamble / 表头 / 数据起始行 | **`by_row`**(Demo 需手动选) | | `.md` `.html` | `TextParser` | 按标题 / 段落拆块 | `default` / `delimiter` / `parent_child` | | `.json` | `TextParser` | 顶层 key / 数组元素 → `code` 块 | `default` | | `.txt` `.xml` `.log` | `TextParser` | 按空行分段 | `default` | | 图片 | `ImageParser` | 整图 1 个 Block | 整图 1 片 | ### 统一产出 | 项目 | 说明 | | --- | --- | | Block 类型 | `heading` / `paragraph` / `table` / `image` / `list` / `code` | | Chunk | Markdown + `meta`(`heading` / `pages` / `embedding_text` / `retrieval` / 可选 `parent_chunk_id`) | | 图片 | `storage/assets/{doc_id}/`;JSON 内为 `assets/{doc_id}/…` | | 结果缓存 | `storage/results/{doc_id}.json`(历史回看 / 召回) | | 中间产物 | `_conversion/`(Office→PDF)、`_mineru/`(MinerU 输出) | ### Word / PPT 转 PDF 优先 LibreOffice(`soffice --headless`),Windows 可回退 Office COM。转换失败时 API 返回 500 并附带各后端错误信息。 ### Excel / CSV `detect_spreadsheet_layout` 识别常见模板(说明行 + 表头 + 数据区);Q&A 评测表(列名含 query/answer 等)适合 `rows_per_chunk=1`。 > `.xls` 依赖 openpyxl,老二进制格式可能失败,建议先转 xlsx。 --- ## PDF / Word 语义切分 `.pdf` / `.doc` / `.docx` 在 `mode=default` 时走 `splitters/pdf_semantic.py`(策略名:`heading_layout_multimodal`)。 Word 先转 PDF,再与 PDF 共用规则。自动策略下默认 `max_chunk_size=2600`、`overlap=120`;Demo 默认表单约为 `2200` / `120`。 ### 流水线 ``` PDF/Word │ ├─ 解析:MinerU(优先)或 PyMuPDF → Block 流 ├─ 噪声过滤:页眉/页脚/页码、文档目录、装饰小图 ├─ 跨页表格合并 ├─ 版面增强:标题层级、图片/表格上下文 ├─ pdf_strategy:打标签(操作手册 vs 年报) └─ heading_layout_multimodal → Chunk ``` ### 核心规则:`heading_layout_multimodal` 目标:同一逻辑小节(标题 + 正文 + 同节截图/表格)尽量落在同一 chunk。 | 步骤 | 行为 | | --- | --- | | 1. 再过滤噪声 | 跳过页码、running header、目录区、过小装饰图 | | 2. 识别标题 | 解析器已标 `heading`,或正则/字号启发式补识别 | | 3. 维护标题路径 | 标题栈记录章节层级 | | 4. 按标题开新片 | 同级或更高级标题时 flush,开新 chunk | | 5. 图文同节 | 正文、image、table 跟在当前标题路径下 | | 6. 超长二次切 | 超过 `max_chunk_size` 时按子标题/长度拆分;**不单独保留父片** | 可识别标题形态示例:`1.` / `1.2.3` / `A.` / `Chapter 2` / `一、` / `(一)`;操作步骤 `Step N …` 作正文保留。封面 Logo 等无语义组标 `retrieval=false`。 ### 文档类型标签:`pdf_strategy` 写入 `split_config.pdf_chunk_strategy`(Demo 策略面板可显示): | 标签 | 典型特征 | 说明 | | --- | --- | --- | | `pdf_feature_step_screenshot` | 步骤标题多、操作词多、截图密度高 | 操作手册型 | | `pdf_outline_report` | 「年报/财务报表」等词,或文件名含 annual/report/年报 | 年报/报告型 | 当前两类切分算法相同,标签用于结果标注与后续策略分叉预留。 --- ## 切分模式(全格式) 后端 **4** 种模式(`SplitMode`):`default` / `delimiter` / `parent_child` / `by_row`。 | 模式 | 设计意图 | 行为 | | --- | --- | --- | | `default` | 非表格正文 | PDF/Word 走语义切分;其他按标题/页/长度打包;`image`/`table` 不拆 | | `delimiter` | 正文含可匹配标识符 | 按自定义标识符切开,再按长度二次切 | | `parent_child` | 层级标识符 | 父标识符切父片,子标识符切子片;检索用子片 | | `by_row` | 表格 | 每片 = 表头 + N 行 Markdown | **Demo**:始终显式传 `mode`(默认 `default`)。切 Excel/CSV 时请选手动「按行切分」。 **自动策略**(`choose_split_config`):仅当 API/Python **不传** `mode`/`config` 时生效——表格类选 `by_row`,PDF/PPT 等选 `default` 并覆盖 `max_chunk_size`。CLI 默认带 `--mode default`,不会走该自动选型。 ### 父子标识符(`mode=parent_child`) 1. 按 `parent_delimiter` 切父片;超长再按长度二次切 2. 再按 `child_delimiter`(可选)切子片 3. 父片:`is_section_parent=true`,`retrieval=false` 4. 子片:`is_sub_chunk=true`,`retrieval=true`,`parent_chunk_id` 指向父片 | 参数 | 说明 | | --- | --- | | `parent_delimiter` | 必填;不写入切片正文 | | `child_delimiter` | 可选;缺省则仅按子级最大长度拆 | | `max_chunk_size` | 父级最大长度 | | `child_max_size` | 子级最大长度(≤ 父级,硬上限 1500) | | `overlap` | 超长二次切分重叠 | ### `default` 决策树(摘要) ``` .pdf / .doc / .docx → heading_layout_multimodal 单 table 且含 rows → 仅「不传 mode」时自动 by_row;显式 default 则走通用切分 有足够标题 → 标题大纲切;超长按子标题/长度拆(不产父片) 有 page、标题不足 → 按页;单页超限再按长度 否则 → 按 max_chunk_size 打包 ``` ### SplitConfig | 参数 | 默认 | 说明 | | --- | --- | --- | | `mode` | `default` | 见上表 | | `delimiter` | — | `delimiter` 模式必填 | | `parent_delimiter` / `child_delimiter` | — | `parent_child`:父必填,子可选 | | `max_chunk_size` | 1500 | 自动策略常覆盖为 1800–2600 | | `child_max_size` | 512 | `parent_child` 子级上限 | | `overlap` | 150 | 超长二次切分重叠 | | `header_row_start` / `header_row_end` | 1 | 表头行(1-based) | | `start_row` | 2 | 数据起始行 | | `rows_per_chunk` | 1 | 每片数据行数 | 可解析扩展名(21 种):`.pdf` `.doc` `.docx` `.ppt` `.pptx` `.ppsx` `.xlsx` `.xls` `.csv` `.md` `.txt` `.html` `.htm` `.json` `.xml` `.log` `.jpg` `.jpeg` `.png` `.bmp` `.gif`。需求中的 `.wps` 尚未接入。 --- ## Demo 前端 三栏:**原文** · **切片列表** · **切片预览**;左侧配置切分模式与参数;下方可测召回。 | 区域 | 说明 | | --- | --- | | 上传 | 拖拽 / 选择文件 | | 切分模式 | 默认(结构感知)/ 通用标识符 / 父子标识符 / 按行 | | 策略面板 | 切分后回显 mode / max_size / overlap / PDF 策略 | | 历史切片 | 列出 `storage/results` 中已切文档,可回看 / 删除 | | 原文 | PDF iframe / docx(mammoth)/ 文本 / 图片 | | 切片列表 | 类型标签、父子标记、字符数 | | 召回测试 | query + top_k;`retrieval=false` 的片不进候选 | | API 面板 | 可折叠请求/响应 JSON | --- ## CLI / API ### CLI ```bash python scripts/chunk_cli.py "path/to/file.pdf" --preview 3 python scripts/chunk_cli.py "path/to/file.xlsx" --mode by_row --rows-per-chunk 5 -o storage/result.json python scripts/chunk_cli.py "readme.md" --mode delimiter --delimiter "##" python scripts/chunk_cli.py "readme.md" --mode parent_child \ --parent-delimiter "##" --child-delimiter "###" \ --max-chunk-size 2000 --child-max-size 512 ``` 批量样例(若有 `data/`):`python scripts/run_all_samples.py` ### API ```bash # 健康检查 curl http://127.0.0.1:8000/health # 切分(不传 mode → 服务端自动策略) curl -X POST http://127.0.0.1:8000/api/chunk -F "file=@./your.pdf" # 切分(显式参数,与 Demo 一致) curl -X POST http://127.0.0.1:8000/api/chunk \ -F "file=@./your.xlsx" \ -F "mode=by_row" \ -F "rows_per_chunk=5" # 历史结果 curl http://127.0.0.1:8000/api/results # 召回(先切分拿到 doc_id) curl -X POST http://127.0.0.1:8000/api/recall \ -H "Content-Type: application/json" \ -d '{"doc_id":"abcdef123456","query":"如何修改交易密码","top_k":5}' ``` 主要端点:`GET /health` · `POST /api/chunk` · `GET/DELETE /api/results…` · `POST /api/recall`。 ### Python ```python from pathlib import Path from rag_cut import chunk_document, SplitConfig, SplitMode from rag_cut.retrieval import recall_chunks # 不传 config → 自动策略 result = chunk_document(Path("doc.pdf")) result = chunk_document( Path("doc.docx"), config=SplitConfig(mode=SplitMode.DEFAULT, max_chunk_size=2600, overlap=120), ) hits, n = recall_chunks("login password", result.chunks, top_k=5) ``` 将 `backend/` 加入 `PYTHONPATH`,或通过 `scripts/chunk_cli.py` 调用。 ### 输出字段(节选) ```json { "filename": "guide.docx", "doc_id": "61c2a152cd5e", "split_mode": "default", "split_config": { "mode": "default", "max_chunk_size": 2600, "pdf_chunk_strategy": "pdf_feature_step_screenshot", "chunk_strategy": "heading_layout_multimodal" }, "block_count": 269, "chunk_count": 48, "chunks": [ { "index": 0, "content": "# …\n\n![image3](assets/61c2a152cd5e/image3.png)", "char_count": 431, "block_types": ["heading", "paragraph", "image"], "meta": { "heading": "1 Getting Started", "pages": [3], "embedding_text": "…", "retrieval": true } } ], "assets_dir": "storage/assets/61c2a152cd5e" } ``` --- ## 期望效果对照 | 需求 | 状态 | | --- | --- | | 图片保留原文位置 | ✅ PDF / Word / PPT(转 PDF) | | 表格提取为 Markdown | ✅ PDF / Excel / CSV | | Word 同标题内容同 chunk | ✅ 语义切分 | | PDF 多栏阅读顺序 | ✅ 双栏检测 + 行合并 | | 页眉页脚 / 页码噪声过滤 | ✅ MinerU 类型跳过 + 位置启发式 | | 文档目录不进切片 | ✅ 全格式 `filter_toc_blocks` | | 手册截图文字不混入正文 | ✅ 大图区域过滤 | | 父子切片(超长章节自动) | ❌ 已从默认切分移除 | | 父子标识符切分 | ✅ `parent_child` | | 召回入参/出参可检视 | ✅ `/api/recall` + Demo | | 历史切片回看 | ✅ `/api/results` + Demo | | wps | ⏳ 未实现 | --- ## 后续计划 - [ ] PDF OCR / 视觉描述增强 - [ ] 向量召回(Embedding) - [ ] wps 格式支持 --- ## 参考 - [`docx/自研搭建AI助手知识库.pdf`](docx/自研搭建AI助手知识库.pdf) — 需求 - [`docx/mineru-integration.md`](docx/mineru-integration.md) — MinerU 接入 - [`docx/tencent-cloud-document-splitting-settings.md`](docx/tencent-cloud-document-splitting-settings.md) — 腾讯云切分参考 - [`CLAUDE.md`](CLAUDE.md) / [`AGENTS.md`](AGENTS.md) — AI 协作说明