文档预处理与入库流水线
完成解析、OCR、切片、向量化与对账
生命周期 · 阶段四 开发实现(离线侧) | 上游:环境就绪(04)| 下游:查询链路(06)
入库流水线六阶段:接收 → 解析 → OCR → 切片 → 向量化 → 索引。这是 RAG 效果的地基——垃圾进,垃圾出,检索质量上限由入库质量决定。
flowchart TD
A["文档上传/接入"] --> B{"格式识别"}
B -->|文本型| C["直接解析"]
B -->|图片型/扫描件| D["OCR 识别"]
C --> E["清洗去噪
去页眉页脚/水印"]
D --> E
E --> F["切片 Chunking"]
F --> G["向量化 Embedding"]
G --> H[("写入索引
向量 + 原文 + 元数据")]
style H fill:#f0fff4,stroke:#4abf60
一、接收(Ingestion)
职责:接收上游文档,统一登记、去重、分发。
- 接入方式:API 上传、Web 端拖拽、消息队列(Kafka/RabbitMQ)、定时同步(网盘/SFTP/钉钉文档)
- 支持格式:PDF、Word、Excel、PPT、HTML、Markdown、TXT、图片、压缩包
- 登记信息:文档 ID、文件名、来源、上传人、部门、密级、版本、哈希值
- 去重策略:按文件内容哈希(MD5/SHA256)判重,重复文档不入库或仅更新元数据
- 版本管理:同一文档更新时,新版本入库、旧版本下线(保留可回溯)
质量检查点:
- [ ] 重复上传同一文件不会产生重复切片
- [ ] 损坏文件、加密 PDF、0 字节文件有明确报错而非静默失败
- [ ] 超大文件(如 1000 页 PDF)不阻塞队列
二、解析(Parsing)
职责:把各格式文档提取为结构化文本(正文、标题层级、表格、图注)。
| 格式 | 推荐工具 | 说明 |
|---|---|---|
| PyMuPDF / pdfplumber | 文本型 PDF 直接抽取;表格用 pdfplumber | |
| Word | python-docx | 保留标题层级 |
| Excel | pandas / openpyxl | 每个 Sheet 转为 Markdown 表格 |
| HTML | BeautifulSoup | 去广告、导航等噪声 |
| 通用 | Unstructured / Apache Tika | 一套 API 解析几十种格式 |
关键处理:
- 版面分析:区分正文 / 页眉页脚 / 水印 / 目录,剔除噪声
- 阅读顺序:多栏 PDF 按正确顺序拼接(常见的解析乱序坑)
- 表格处理:表格转为 Markdown/HTML 结构保留,整表尽量不切断
- 元数据提取:标题、章节、页码——切片时要随切片携带
质量检查点:
- [ ] 抽取文本与原文抽查比对,无乱码、无漏段、无重复段
- [ ] 表格行列还原正确(抽查 10 张表)
- [ ] 页眉页脚、水印文字已被过滤
三、OCR(光学字符识别)
职责:对扫描件、图片型 PDF、照片做文字识别。只对需要的页面做(先判断是否已有文本层)。
- 触发判断:解析阶段发现页面无文本层或文本量过低 → 转 OCR 分支
- 引擎选型:PaddleOCR(开源、中文强)/ Tesseract / 商用 API(精度高)
- 预处理:倾斜校正、去噪、二值化,可显著提升识别率
- 结构还原:识别结果按版面重排,避免文字顺序错乱
- 置信度处理:低置信度片段打标记,可用于后续质量评估
已知短板(测试重点关注):
- 手写字、印章遮挡文字、模糊扫描件识别率骤降
- 竖排版、繁体、艺术字识别差
- 表格线还原错乱导致行列错位
质量检查点:
- [ ] 扫描件关键字段识别准确率抽样 ≥ 目标值(如 98%)
- [ ] 中英混排、数字与日期识别正确(金额、日期是重灾区)
- [ ] OCR 失败的页面有告警与人工兜底流程
四、切片(Chunking)
职责:把长文本切成适合检索的片段。切片质量直接决定召回精度——切太碎语义不完整,切太大噪声多且耗 token。
常见切片策略
| 策略 | 原理 | 适用 |
|---|---|---|
| 固定长度 | 每 500 字一段 | 简单场景,基线方案 |
| 递归分割 | 先按段落→句子→字符逐级切,尽量保完整语义(LangChain 默认) | 通用推荐 |
| 按结构切 | 按标题/章节边界切 | 有清晰目录的文档(手册、制度) |
| 语义切片 | 用 Embedding 计算相邻句相似度,在语义突变处断开 | 质量要求高,成本略高 |
| 父子切片 | 检索用小切片(准),生成用其父切片(上下文全) | 精度与上下文兼得,推荐进阶 |
关键参数经验值
- chunk_size:中文 300~800 字起步,问答类偏小,叙述类偏大
- overlap(重叠):10%~20%(如 500 字 + 50~100 重叠),防止关键句被切断
- 表格:整表一个切片;超长表按行组切并保留表头
- 每个切片至少携带可追溯元数据:文档 ID、版本、来源位置和更新时间;章节、页码、权限标签等字段按文档类型与业务要求配置
质量检查点:
- [ ] 抽查切片可独立读懂,无"断了半句话"
- [ ] 切片长度分布合理(无大量超短/超长切片)
- [ ] 表格未被腰斩,表头随行携带
五、向量化(Embedding)
职责:把每个切片转成语义向量。
- 模型:BGE / M3E / text-embedding-3 等(见 02 篇选型)
- 预处理:部分模型需加指令前缀(如 BGE 查询侧加 "为这个句子生成表示…")
- 批量编码:批量推理提升吞吐,注意限流
- 归一化:向量 L2 归一化后用内积即余弦相似度
- 维度:bge-large 1024 维、text-embedding-3-small 1536 维——建库时确定,不可中途更换模型(换模型 = 全量重建)
质量检查点:
- [ ] 用项目语料建立相似、易混和无关文本对,记录分数分布并建立自己的冒烟基线;不同模型的分数不可直接套用统一阈值
- [ ] 批量编码无静默丢片(入向量数 = 切片数)
- [ ] 空切片、超长切片有拦截或拆分
六、索引(Indexing)
职责:向量 + 原文 + 元数据写入向量数据库,建立可检索索引。
向量库核心设计
// Collection Schema 示例
{
"collection": "kb_docs",
"fields": {
"id": "主键(自动生成)",
"vector": "FLOAT_VECTOR(1024)",
"text": "切片原文",
"doc_id": "所属文档ID",
"doc_name": "文档名",
"page": "页码",
"section": "章节",
"department": "所属部门(权限过滤用)",
"security": "密级",
"version": "版本号",
"created_at": "入库时间"
},
"index": "HNSW(m=16, efConstruction=200)",
"metric": "COSINE"
}
索引类型对比
| 索引 | 特点 | 适用 |
|---|---|---|
| HNSW | 查询快、召回高,内存占用大 | 默认推荐 |
| IVF_FLAT | 可控折中,需训练 | 大规模 |
| FLAT | 暴力精确检索 | 小数据量、评测基准 |
混合检索(Hybrid Search)
- 向量检索(语义)+ BM25 关键词检索(精确词、编号、人名)→ RRF 融合
- 金融/法律等专有名词多的场景强烈建议开混合检索
质量检查点:
- [ ] 入库条数 = 切片条数,无丢失
- [ ] 元数据完整(随机抽 10 条核对)
- [ ] 相似度检索 top1 命中预期切片(冒烟)
- [ ] 文档删除后,其切片同步物理删除
- [ ] 权限字段(部门/密级)随切片正确写入
七、流水线整体测试要点(详见 08 篇)
- 端到端正确性:上传 → 可检索 → 可回答
- 幂等性:重复上传不产生重复数据
- 删除一致性:删文档/换版本后检索不到旧数据
- 异常容错:坏文件、大文件、并发上传
- 性能:单文档入库耗时、1000 文档批量入库吞吐
本篇交付与下一步
本阶段应留下源文件清单、解析产物、切片配置、嵌入版本、导入批次和文档/切片/向量对账结果。接口返回成功不代表入库完成;还要验证可检索、无重复、无缺失向量且失败队列已处理。下一步进入 06-检索与生成链路设计。