返回AI笔记
AI Day 19

AI Day 19: 生产级RAG(1):文档解析与Chunking工程 — 数据质量决定RAG上限

文档解析与Chunking工程是生产级RAG系统的"数据地基"——将非结构化文档(PDF/Word/HTML/扫描件)精确转化为结构化、可检索的知识片段(Chunks)。解析质量和Chunking策略决定了RAG系统的理论上限:检索再精准、模型再强大,如果源数据就是错的或碎片化的,最终输出必然低质。这是一个80%工程量花在数据处理上的典型领域。

2026-04-20
DocumentChunkingPDFUnstructured.ioLlamaParseDoclingSemanticTableOCRDataFinancialRAG

日期:2026-04-20 阶段:第二阶段 — 工程实践 (Day 16-30) 标签Document Parsing Chunking PDF Extraction Unstructured.io LlamaParse Docling Semantic Chunking Table Extraction OCR Data Pipeline Financial Documents RAG Quality


学习路径树

AI/LLM 深度技术学习 50天计划
├── 第一阶段:模型基础 (Day 1-15) ✅
│   ├── Day 1:  Transformer架构与LLM基础 ✅
│   ├── Day 2:  模型量化与本地部署 ✅
│   ├── Day 3:  训练过程深度:Pre-training / SFT / RLHF / DPO ✅
│   ├── Day 4:  Prompt Engineering与上下文学习(ICL)原理 ✅
│   ├── Day 5:  RAG架构:检索增强生成全链路 ✅
│   ├── Day 6:  向量数据库与Embedding模型 ✅
│   ├── Day 7:  Fine-tuning实战:LoRA / QLoRA / Adapter ✅
│   ├── Day 8:  推理优化:vLLM / TensorRT-LLM / SGLang ✅
│   ├── Day 9:  长上下文技术:RoPE扩展 / Ring Attention ✅
│   ├── Day 10: 多模态模型:Vision-Language架构 ✅
│   ├── Day 11: Reasoning模型:CoT / o1 / R1 / Extended Thinking ✅
│   ├── Day 12: Agent框架:ReAct / Tool Use / Planning ✅
│   ├── Day 13: MCP协议与Tool生态 ✅
│   ├── Day 14: 模型评估:Benchmark / Arena / 安全评估 ✅
│   └── Day 15: 阶段复习与架构总结 ✅
│
├── 第二阶段:工程实践 (Day 16-30)
│   ├── Day 16: LLM应用架构设计 — API Gateway/路由/缓存 ✅
│   ├── Day 17: LLM安全与Guardrails — 生产环境防护体系 ✅
│   ├── Day 18: LLM可观测性 — 日志/Tracing/指标/告警 ✅
│   ├── Day 19: 生产级RAG(1) — 文档解析与Chunking工程 ← 你在这里
│   ├── Day 20: 生产级RAG(2) — Retrieval优化与Reranking
│   ├── Day 21: 生产级RAG(3) — 评估框架与持续迭代
│   ├── Day 22-25: Agent系统工程化(状态管理/错误恢复/成本控制)
│   └── Day 26-30: 多模型编排/部署策略/端到端项目
│
├── 第三阶段:金融零售AI应用 (Day 31-42)
│   ├── Day 31-35: 金融AI(风控模型/智能投顾/合规/反欺诈)
│   ├── Day 36-40: 零售AI(推荐系统/智能客服/供应链预测/营销)
│   └── Day 41-42: CeFi x DeFi x AI融合架构
│
└── 第四阶段:面试冲刺 (Day 43-50)
    ├── Day 43-46: 系统设计面试(LLM平台/RAG/Agent/推荐)
    ├── Day 47-49: 产品/架构面试模拟
    └── Day 50: 总结与作品集

核心概念

一句话定义

文档解析与Chunking工程是生产级RAG系统的"数据地基"——将非结构化文档(PDF/Word/HTML/扫描件)精确转化为结构化、可检索的知识片段(Chunks)。解析质量和Chunking策略决定了RAG系统的理论上限:检索再精准、模型再强大,如果源数据就是错的或碎片化的,最终输出必然低质。这是一个80%工程量花在数据处理上的典型领域。

金融类比

RAG数据处理 = 银行数据治理(Data Governance) —— 垃圾进,垃圾出

银行数据治理:                              RAG文档处理:
├── 数据采集层                              ├── 文档采集层
│   ├── 多源头接入(核心系统/外部数据)       │   ├── 多格式接入(PDF/Word/HTML/图片)
│   ├── 数据格式标准化(ISO 8583)            │   ├── 格式标准化(统一Markdown/JSON)
│   ├── 数据质量校验(完整性/一致性)          │   ├── 解析质量检查(文字准确率/结构保留)
│   └── 异常数据处理(缺失值/重复)            │   └── 异常处理(乱码/截断/表格错位)
│                                           │
├── 数据加工层                              ├── Chunking加工层
│   ├── 数据清洗(去噪/脱敏)                 │   ├── 文本清洗(去页眉页脚/水印)
│   ├── 数据分类(客户/交易/产品)             │   ├── 文档分段(按语义/结构/固定大小)
│   ├── 数据关联(360度客户视图)              │   ├── Metadata标注(来源/日期/类别)
│   └── 数据分层(ODS/DW/DM)                │   └── 层次组织(Parent-Child/索引)
│                                           │
├── 数据质量管理                            ├── 数据质量Gate
│   ├── 质量规则(准确性/及时性/完整性)       │   ├── 质量规则(字符准确率>98%/结构完整)
│   ├── 质量报表(数据健康度Dashboard)        │   ├── 质量报表(解析成功率/Chunk质量分布)
│   ├── 质量闭环(发现→修复→验证)            │   ├── 质量闭环(检测→修复→重新索引)
│   └── 数据血缘(Lineage追踪)               │   └── 文档血缘(Chunk→原文溯源)
│                                           │
└── 核心原则: 数据质量决定分析上限           └── 核心原则: 解析质量决定RAG上限
    → 风控模型再好,喂垃圾数据也不行             → Embedding再好,Chunk是碎片也没用
    → 银行花50%预算在数据治理                    → RAG工程80%精力在数据处理
    → "Data is the new oil"                      → "Parsing is the new bottleneck"

为什么Day 5学过RAG还要再学3天

Day 5学的Day 19-21补充的
RAG架构全貌(Naive RAG/Advanced RAG/Modular RAG)深入每个环节的工程细节
Chunking概念(知道有这回事)7种Chunking策略对比、实验数据、最佳实践
Embedding选型(了解有哪些)解析质量如何影响Embedding效果
Reranking概念(知道要做)Day 20深入:Cross-Encoder/ColBERT/LLM-as-Reranker
评估指标(听说过RAGAS)Day 21深入:评估框架搭建、持续迭代流程

Day 5是"知道RAG是什么",Day 19-21是"能把RAG做到生产级"。差距就像"知道银行要做风控"和"能设计一套实时风控引擎"。


知识点1: 文档解析——为什么这是RAG最难的部分

文档格式挑战全景

真实世界的文档 = 一团混乱的非结构化信息

格式挑战:
├── PDF (地狱级)
│   ├── 原生PDF: 有文字层,但排版信息丢失(多栏/脚注/页眉混在一起)
│   ├── 扫描PDF: 纯图片,需要OCR才能提取文字
│   ├── 混合PDF: 部分页面原生、部分扫描(金融报表常见)
│   ├── 加密PDF: 需要密码或权限才能读取
│   └── 表格PDF: 表格线是画出来的,不是真正的表格结构
│
├── Word/DOCX
│   ├── 样式不规范(用加粗代替标题、手动空格代替缩进)
│   ├── 嵌入对象(图表/公式/OLE对象)
│   ├── 修订模式(Track Changes)残留
│   └── 兼容性问题(WPS/LibreOffice vs MS Office)
│
├── HTML/Web页面
│   ├── 动态渲染(JavaScript生成内容)
│   ├── 广告/导航/侧边栏噪音
│   ├── 分页加载(需要翻页或滚动)
│   └── 结构不标准(div乱嵌套、table做布局)
│
├── PPT/Slides
│   ├── 信息密度极低(一页就几个关键词)
│   ├── 图表/SmartArt无法直接提取文字含义
│   ├── Speaker Notes可能包含关键信息
│   └── 动画/渐进显示增加解析复杂度
│
├── 图片/扫描件
│   ├── OCR准确率受扫描质量影响(倾斜/模糊/低分辨率)
│   ├── 手写体识别困难
│   ├── 印章/水印干扰
│   └── 多语言混排(中英文混合文档)
│
└── 特殊格式
    ├── Excel (结构化但Embedding不友好)
    ├── Email (.eml/.msg,含附件嵌套)
    ├── Markdown/RST (相对友好)
    └── 代码文件 (需要语义理解)

为什么PDF解析是"地狱"

PDF的设计初衷是精确还原打印效果,而不是结构化存储信息。这导致:

PDF内部数据结构(简化):
{
  "page": 1,
  "objects": [
    {"type": "text", "x": 72, "y": 700, "content": "第一章", "font": "SimHei-Bold", "size": 18},
    {"type": "text", "x": 72, "y": 680, "content": "1.1 背景介绍", "font": "SimHei", "size": 14},
    {"type": "text", "x": 72, "y": 650, "content": "本报告旨在...", "font": "SimSun", "size": 12},
    {"type": "text", "x": 300, "y": 650, "content": "数据来源:...", "font": "SimSun", "size": 12},
    {"type": "line", "x1": 72, "y1": 500, "x2": 520, "y2": 500},  // 这是表格线?分隔线?装饰?
    {"type": "image", "x": 72, "y": 300, "width": 448, "height": 180}  // 图表?照片?扫描文字?
  ]
}

核心困难

  1. 没有语义标签 — PDF不知道"第一章"是标题,只知道它是18号加粗字
  2. 多栏布局 — 两列文字的阅读顺序靠坐标推断,经常出错
  3. 表格没有结构 — 表格线是画出来的线段,需要几何推理才能还原行列
  4. 脚注/页眉混入正文 — 页面底部的脚注和正文内容在同一层
  5. 跨页内容 — 一个段落/表格跨两页,中间被页码/页眉打断

扫描件 vs 原生PDF

维度原生PDF(Digital-born)扫描PDF(Scanned)
文字提取直接提取,准确率~100%需OCR,准确率85-98%
表格识别需要几何推理OCR+几何推理,更难
处理速度快(毫秒级/页)慢(秒级/页)
中文处理字体嵌入则OKOCR中文准确率较低
金融场景电子生成的报表/合同历史档案/签章扫描件/银行回单
推荐工具PyMuPDF/pdfplumberTesseract+Layout分析 或 VLM

知识点2: 2025-2026文档解析工具链

工具全景对比

工具类型核心优势主要局限适用场景价格(2026)
Unstructured.io开源+商业格式覆盖最全(25+格式)、Pipeline完善表格精度一般、大文档慢通用企业场景开源免费 / API $0.01/页
LlamaParse商业API基于LLM的智能解析、表格强依赖API、成本较高复杂PDF/表格密集1000页免费/天 → $3/1000页
Docling (IBM)开源学术论文/技术文档强、布局分析好非英语支持弱技术/学术文档免费开源
Marker开源PDF→Markdown转换质量高、速度快只支持PDF/图片PDF批量转换免费开源
PyMuPDF (fitz)开源库速度极快、底层控制力强需要自己处理布局逻辑原生PDF文字提取免费开源
pdfplumber开源库表格提取精准、API简洁只支持原生PDFPDF表格提取免费开源
Azure Doc Intelligence商业API企业级、预训练模型多(发票/收据/身份证)锁定Azure生态企业合规场景$1.5/1000页
Google Document AI商业APIOCR强、多语言、自定义模型训练锁定GCP多语言/扫描件$1.5/1000页
多模态VLM解析LLM API理解能力最强、能处理任何格式成本高、速度慢、幻觉风险复杂/非标文档~$0.03-0.10/页

各工具深度分析

Unstructured.io — 当前事实标准

定位: RAG数据处理的"瑞士军刀"

核心能力:
├── 支持格式: PDF/DOCX/PPTX/HTML/EML/CSV/TSV/TXT/MD/RST/XML/EPUB...
├── 解析策略:
│   ├── fast:    基于规则的快速提取(适合原生PDF)
│   ├── hi_res:  Layout分析模型+OCR(适合复杂布局)
│   ├── ocr_only: 纯OCR(适合扫描件)
│   └── auto:    自动判断使用哪种策略
├── 元素类型:
│   ├── Title / NarrativeText / ListItem
│   ├── Table / FigureCaption / Image
│   ├── Header / Footer / PageBreak
│   └── Formula / Address / EmailAddress
├── Chunking内置:
│   ├── by_title:   按标题分段
│   ├── by_page:    按页分段
│   ├── by_similarity: 按语义相似度分段
│   └── basic:     固定大小分段
└── 集成:
    ├── LangChain / LlamaIndex 原生支持
    ├── 输出格式: JSON / CSV / Markdown
    └── 部署: 本地Docker / Serverless API

关键代码示例

from unstructured.partition.pdf import partition_pdf
from unstructured.chunking.title import chunk_by_title

# Step 1: 解析PDF
elements = partition_pdf(
    filename="financial_report.pdf",
    strategy="hi_res",           # 高精度模式
    infer_table_structure=True,  # 推断表格结构
    languages=["chi_sim", "eng"],# 中英文OCR
    extract_images_in_pdf=True,  # 提取图片
)

# Step 2: 按标题Chunking
chunks = chunk_by_title(
    elements,
    max_characters=1500,         # 每个Chunk最大字符数
    new_after_n_chars=1000,      # 超过此长度开始寻找分割点
    overlap=200,                 # 重叠字符数
    combine_text_under_n_chars=200,  # 太短的段落合并
)

# Step 3: 查看结果
for chunk in chunks:
    print(f"Type: {chunk.category}")
    print(f"Text: {chunk.text[:100]}...")
    print(f"Metadata: {chunk.metadata.to_dict()}")

LlamaParse — LLM驱动的智能解析

定位: "用AI理解文档"而非"用规则提取文字"

核心差异:
  传统解析: PDF → OCR/规则提取 → 原始文本 → 后处理
  LlamaParse: PDF → 多模态LLM理解 → 结构化Markdown → 直接可用

优势:
├── 表格理解: 能理解复杂合并单元格、嵌套表格
├── 图表描述: 自动生成图表的文字描述
├── 上下文保留: 理解"同上"/"见表1"等指代关系
├── 格式清洁: 输出干净的Markdown,无需后处理
└── 指令定制: 可以通过Prompt指导解析行为

局限:
├── 依赖API: 无法本地部署(截至2026Q1)
├── 成本: 大规模处理成本显著
├── 延迟: 每页2-5秒(vs传统工具100ms/页)
├── 幻觉: LLM可能"补全"不存在的内容
└── 数据隐私: 文档需要上传到云端

多模态VLM解析 — 2025-2026的新范式

核心思路: 直接把PDF页面截图喂给GPT-4o/Claude,让VLM"看"文档

工作流程:
  PDF页面 → 转为图片(300DPI) → 发送给VLM → 返回结构化文本

Prompt模板:
  "请将这张文档图片转换为结构化Markdown。
   保留所有表格(用Markdown表格格式),
   保留标题层级,
   图表请用文字描述其内容和关键数据点。"

优势:
├── 理解力最强: 能处理任何人类能读懂的文档
├── 零配置: 不需要针对格式写规则
├── 表格/图表: 直接"看懂"复杂布局
└── 多语言: 天然支持任何语言

成本估算(2026):
  GPT-4o:  ~$0.03/页 (低分辨率) / ~$0.08/页 (高分辨率)
  Claude:  ~$0.04/页 (标准) / ~$0.10/页 (高分辨率)
  Gemini:  ~$0.01/页 (价格最低)

适用场景:
├── 非标文档(每种格式不同): 合同/法律文件/手写笔记
├── 小批量高价值: 100页以内的关键文档
├── 质量>成本: 金融合规/法律审计场景
└── 兜底策略: 传统工具解析失败时的Fallback

2026生产环境推荐组合

推荐架构: 分层解析策略(Tiered Parsing)

Tier 1 — 快速通道 (80%文档):
  原生PDF/DOCX/HTML → Unstructured.io (fast模式) → 标准Chunks
  成本: ~$0.001/页, 速度: 100ms/页

Tier 2 — 精确通道 (15%文档):
  复杂PDF/表格密集 → Unstructured.io (hi_res) + pdfplumber → 结构化Chunks
  成本: ~$0.01/页, 速度: 1-2s/页

Tier 3 — 智能通道 (5%文档):
  扫描件/非标格式/解析失败 → VLM(GPT-4o/Claude) → 高质量Chunks
  成本: ~$0.05/页, 速度: 3-5s/页

路由逻辑:
  if 文档是原生PDF/DOCX/HTML:
      → Tier 1
  elif 包含复杂表格 or Tier 1质量分<阈值:
      → Tier 2
  elif 扫描件 or Tier 2失败:
      → Tier 3
  else:
      → 人工审核队列

知识点3: Chunking工程深度

为什么Chunking如此关键

Chunking的本质: 决定"检索单元"的粒度和质量

类比: 图书馆整理书架
├── 粒度太粗(整本书作为一个单元):
│   → 搜"利率计算公式",返回整本《金融数学》—— 信息淹没
│
├── 粒度太细(每句话作为一个单元):
│   → 搜"利率计算公式",返回"其中r为年利率" —— 缺乏上下文
│
└── 粒度合适(按章节/主题组织):
    → 搜"利率计算公式",返回完整的利率计算章节 —— 既精准又完整

Chunking直接影响:
  1. 检索精度: Chunk太大 → 语义稀释 → 召回不精准
  2. 检索召回: Chunk太小 → 上下文丢失 → 无法回答复杂问题
  3. LLM生成质量: 喂给LLM的Context质量 = Chunk质量
  4. Token成本: Chunk越大 → 每次检索消耗的Token越多

7种Chunking策略详解

策略1: Fixed-Size Chunking (固定大小切分)

原理: 按固定字符数/Token数切分,到达阈值就切

优点: 实现最简单、速度最快、行为可预测
缺点: 完全无视语义边界,可能把一句话/一个段落切成两半

参数:
  chunk_size:   每个Chunk的大小 (通常500-2000字符 / 128-512 tokens)
  chunk_overlap: 相邻Chunk的重叠部分 (通常10-20% of chunk_size)

示例:
  原文: "利率风险是银行面临的主要风险之一。|切割点|当市场利率上升时..."

适用: 快速原型、对质量要求不高的场景
不适用: 任何生产环境(通常作为Baseline)

策略2: Recursive Character Splitting (递归字符切分)

原理: 按优先级尝试不同分隔符切分,尽量在自然边界切割

分隔符优先级(默认):
  ["\n\n", "\n", " ", ""]
  → 先尝试按段落切 → 段落太大则按行切 → 行太长则按词切

LangChain实现:
  text_splitter = RecursiveCharacterTextSplitter(
      chunk_size=1000,
      chunk_overlap=200,
      separators=["\n\n", "\n", "。", "!", "?", ";", " ", ""]
  )

中文优化: 添加中文句号/分号作为分隔符

优点: 比Fixed好很多,尊重基本文本结构
缺点: 仍然不理解语义,只是在"语法边界"切割
适用: 大多数场景的默认选择,性价比最高

策略3: Semantic Chunking (语义切分)

原理: 用Embedding计算相邻句子的语义相似度,在语义跳变处切割

工作流程:
  1. 将文档按句子拆分
  2. 计算每个句子的Embedding向量
  3. 计算相邻句子的余弦相似度
  4. 相似度低于阈值的位置 = 语义边界 = 切割点

示例:
  句子1: "银行的核心业务是存贷款"          ─┐ 相似度0.85 → 不切
  句子2: "贷款利率由央行基准利率决定"        ─┘
  句子3: "区块链技术正在改变金融行业"        ← 与上文相似度0.32 → 切!

关键参数:
  breakpoint_threshold: 语义跳变阈值 (percentile方式: 通常取5th-10th percentile)
  embedding_model: 用于计算语义的Embedding模型
  buffer_size: 计算相似度时的滑动窗口大小

LlamaIndex实现:
  from llama_index.core.node_parser import SemanticSplitterNodeParser
  splitter = SemanticSplitterNodeParser(
      buffer_size=1,
      breakpoint_percentile_threshold=95,
      embed_model=embed_model,
  )

优点: Chunk内语义一致性高,检索质量显著提升
缺点: 需要计算Embedding(慢/有成本),Chunk大小不可控
适用: 对检索质量要求高、文档语义跳跃大的场景

策略4: Document-Structure Chunking (基于文档结构)

原理: 利用文档自身结构(标题/章节/列表)作为切割依据

工作流程:
  1. 解析文档结构(识别H1/H2/H3标题层级)
  2. 按标题层级切分(每个H2下的内容 = 一个Chunk)
  3. 保留层级关系作为Metadata

示例(金融报表):
  H1: 年度报告                     ← 不切(太粗)
  ├── H2: 公司概况                  ← Chunk 1
  ├── H2: 财务摘要                  ← Chunk 2
  │   ├── H3: 收入分析              ← Chunk 2a (如果H2太大则细切)
  │   └── H3: 利润分析              ← Chunk 2b
  └── H2: 风险因素                  ← Chunk 3

Metadata附加:
  {
    "chunk_text": "收入分析的具体内容...",
    "title_path": "年度报告 > 财务摘要 > 收入分析",
    "heading_level": 3,
    "document_name": "2025年报.pdf",
    "page_numbers": [12, 13]
  }

优点: 语义最自然(文档作者已经做好了分段)
缺点: 依赖文档有良好结构(很多文档结构混乱)
适用: 技术文档/报告/法规等结构化程度高的文档

策略5: Sliding Window Chunking (滑动窗口)

原理: 固定窗口大小,按固定步长滑动,生成大量重叠Chunk

参数:
  window_size: 窗口大小 (如1000字符)
  step_size:   步长 (如200字符)
  → 重叠率 = 1 - step/window = 80%

效果:
  文档: [A B C D E F G H I J K L M N O P Q R S T]
  Chunk1: [A B C D E F G H I J]
  Chunk2: [C D E F G H I J K L]        ← 与Chunk1重叠80%
  Chunk3: [E F G H I J K L M N]
  ...

优点: 信息完全不丢失(每个位置被多个Chunk覆盖)
缺点: Chunk数量爆炸(是原文的window/step倍),存储和检索成本高
适用: 关键文档/法律合同(宁可多花钱也不能漏信息)

策略6: Parent-Child Chunking (父子层级)

原理: 创建两层Chunk——小的Child Chunk用于精确检索,大的Parent Chunk用于提供上下文

工作流程:
  1. 先按较大粒度创建Parent Chunks (如2000字符)
  2. 每个Parent内再切分为多个Child Chunks (如500字符)
  3. 检索时: 用Child Chunk匹配查询
  4. 返回时: 返回对应的Parent Chunk给LLM

类比:
  图书馆 → 先用索引卡片(Child)找到相关位置 → 再把整本参考书(Parent)拿给读者

示例:
  Parent Chunk (2000字符): "第三章 风险管理框架 ... [完整章节内容]"
    ├── Child 1 (500字符): "3.1 市场风险度量方法 VaR..."
    ├── Child 2 (500字符): "3.2 信用风险评估模型..."
    ├── Child 3 (500字符): "3.3 操作风险分类..."
    └── Child 4 (500字符): "3.4 流动性风险管理..."

  查询: "VaR计算方法" → 匹配Child 1 → 返回Parent给LLM

LlamaIndex实现:
  from llama_index.core.node_parser import SentenceWindowNodeParser
  node_parser = SentenceWindowNodeParser.from_defaults(
      window_size=3,          # 每个句子窗口包含前后3个句子
      original_text_metadata_key="window",
  )

优点: 检索精度高(小Chunk) + 上下文充分(大Parent)
缺点: 存储翻倍、实现复杂度增加
适用: 生产级RAG系统的首选策略之一

策略7: Agentic Chunking (Agent驱动)

原理: 用LLM作为Agent来决定如何切分——让AI"理解"后决定哪里该切

工作流程:
  1. LLM逐句/逐段阅读文档
  2. 判断当前内容和已有Chunk是否属于同一主题
  3. 如果是 → 追加到当前Chunk
  4. 如果不是 → 结束当前Chunk,开始新Chunk
  5. (可选) LLM为每个Chunk生成摘要作为Metadata

Prompt示例:
  "你是一个文档分段专家。请判断以下新段落是否与当前Chunk属于同一主题:
   当前Chunk主题: {current_chunk_summary}
   新段落: {new_paragraph}
   回答YES(属于同一主题,继续追加)或NO(不同主题,开始新Chunk)。"

优点: 语义理解最深、分段质量最高
缺点: 成本极高(每个切割点都要调用LLM)、速度极慢、不确定性
适用: 少量高价值文档(金融合规文件/法律合同)、离线预处理

Chunk Size实验数据

基于多个公开基准测试的经验数据(2025-2026):

Chunk Size vs 检索质量 (Recall@10):
┌─────────────┬──────────────┬──────────────┬──────────────┐
│ Chunk Size   │ 通用文档     │ 技术文档      │ 金融报表      │
├─────────────┼──────────────┼──────────────┼──────────────┤
│ 128 tokens  │ 0.62 (太碎片) │ 0.58         │ 0.55         │
│ 256 tokens  │ 0.71         │ 0.68         │ 0.65         │
│ 512 tokens  │ 0.78 ★最佳   │ 0.76 ★最佳   │ 0.72         │
│ 1024 tokens │ 0.75         │ 0.74         │ 0.78 ★最佳   │
│ 2048 tokens │ 0.68 (太稀释) │ 0.70         │ 0.75         │
└─────────────┴──────────────┴──────────────┴──────────────┘

关键发现:
├── 通用文档: 512 tokens(约300-400中文字)效果最好
├── 技术文档: 512 tokens,但需配合结构化切分
├── 金融报表: 偏大(1024 tokens),因为表格/数据需要完整上下文
├── 法律合同: 偏大(1024-2048),条款不能切断
└── 对话/FAQ: 偏小(256 tokens),QA对天然就是好的Chunk

经验法则:
  最优Chunk Size ≈ 一个完整"知识点"的平均长度
  → 需要在你的特定数据上做实验,没有万能参数

Overlap设计

Overlap的作用: 防止关键信息恰好落在切割边界

Overlap比例推荐:
├── Fixed/Recursive: 10-20% of chunk_size
├── Semantic: 通常不需要(语义完整)
├── Parent-Child: 通过父Chunk天然覆盖
└── Sliding Window: 通过步长控制,通常50-80%

Overlap过小的问题:
  "...利率上调50个基点。|切断|这导致银行净息差扩大..."
  → Chunk 1只知道"上调50基点",不知道影响
  → Chunk 2只知道"净息差扩大",不知道原因

Overlap过大的问题:
  → Chunk数量膨胀
  → 存储成本增加
  → 检索时同一内容出现多次(需要去重)

Metadata附加策略

Metadata = Chunk的"身份证" —— 检索时的重要信号

必须附加的Metadata:
├── source:        来源文件路径/URL
├── page_number:   原文页码(用于溯源)
├── chunk_index:   在文档中的位置序号
├── created_at:    解析时间
└── document_type: 文档类型(报告/合同/法规)

推荐附加的Metadata:
├── title_path:    标题层级路径("年报>财务>收入分析")
├── section_type:  内容类型(正文/表格/列表/脚注)
├── language:      语言(zh/en/mixed)
├── confidence:    解析置信度(OCR场景)
├── summary:       LLM生成的Chunk摘要(用于Hybrid Search)
└── entities:      提取的命名实体(公司名/金额/日期)

金融场景特有Metadata:
├── fiscal_period: 财务期间(2025Q3/FY2025)
├── company:       公司名称/股票代码
├── document_class: 文档分类(10-K/招股书/审计报告)
├── currency:      涉及币种
└── regulatory_ref: 关联法规编号

Metadata用于:
  1. 过滤检索范围(只搜"2025年报告")
  2. 结果排序(优先最新文档)
  3. 溯源(用户点"查看来源"跳转原文)
  4. 权限控制(按文档密级/部门过滤)

知识点4: 表格与结构化数据处理

表格是RAG的"老大难"

为什么表格难处理:
├── 检测难: PDF中表格由线段/文字组成,需要几何推理判断这是不是表格
├── 提取难: 合并单元格/嵌套表格/跨页表格破坏结构
├── 表示难: 表格转文字后丢失行列关系
└── 检索难: 表格内容的Embedding效果差(数字+短文本)

金融场景尤其严重:
  银行年报 → 50%以上内容是表格
  监管报表 → 几乎全是表格
  合同条款 → 费率表/还款计划表
  交易记录 → 流水/对账单

表格处理流程

Step 1: 表格检测(Table Detection)
  方法:
  ├── 规则: 检测PDF中的线段组合(有线表格)
  ├── 模型: DETR/TableTransformer(无线表格也能检测)
  └── VLM: 直接问LLM"这页有表格吗?在哪里?"

Step 2: 表格提取(Table Extraction)
  方法:
  ├── pdfplumber: 最精准的开源表格提取(原生PDF)
  ├── Camelot: 基于OpenCV的表格提取
  ├── img2table: 图片中的表格提取
  └── VLM: GPT-4o/Claude直接"看"表格

Step 3: 表格表示(Table Representation)
  方案对比:
  ├── Table-as-Markdown:
  │   | 项目 | 2024 | 2025 |
  │   |------|------|------|
  │   | 收入 | 100M | 120M |
  │   优点: Embedding效果较好、LLM理解力强
  │   缺点: 复杂表格表达力不足
  │
  ├── Table-as-JSON:
  │   {"headers":["项目","2024","2025"],"rows":[["收入","100M","120M"]]}
  │   优点: 结构精确、可编程处理
  │   缺点: Embedding效果差
  │
  ├── Table-as-NaturalLanguage:
  │   "2024年收入为100M,2025年收入增长至120M..."
  │   优点: Embedding效果最好、语义最完整
  │   缺点: 丢失精确数值关系
  │
  └── 推荐: Markdown存储 + NL描述作为辅助Metadata

嵌套表格/跨页表格

嵌套表格(Nested Table):
  银行产品费率表常见: 表格内嵌套子表格
  ┌─────────────────────────────────┐
  │ 产品类别 │ 子产品 │ 费率          │
  ├──────────┼────────┼──────────────┤
  │ 贷款     │ 房贷   │ LPR+30bp     │
  │          ├────────┼──────────────┤
  │          │ 消费贷 │ 5.8%-12.6%   │
  ├──────────┼────────┼──────────────┤
  │ 存款     │ 活期   │ 0.2%         │
  │          ├────────┼──────────────┤
  │          │ 定期   │ 1.5%-2.65%   │
  └─────────────────────────────────┘

  处理策略: 拆分为扁平化表格 + 保留层级信息作为前缀
  → "贷款-房贷: LPR+30bp"
  → "贷款-消费贷: 5.8%-12.6%"
  → "存款-活期: 0.2%"

跨页表格(Cross-Page Table):
  问题: 表格从第5页开始,第6页继续,中间被页眉/页脚打断

  处理策略:
  1. 检测: 页面底部有未闭合的表格 → 标记为"待续"
  2. 合并: 下一页顶部的表格行与上一页合并
  3. 验证: 检查列数是否一致、数据类型是否匹配

  工具支持:
  ├── pdfplumber: 可以手动实现跨页合并
  ├── Unstructured: 部分支持(不完美)
  ├── LlamaParse: 较好支持(LLM理解上下文)
  └── VLM: 发送相邻两页图片,让LLM合并

知识点5: 多语言与金融文档

中英文混合处理

金融文档中英混合的挑战:
  "本行2025年净利息收入(Net Interest Income, NII)达到RMB 523.6亿元,
   同比增长8.2%。Non-Performing Loan(NPL) ratio为1.38%,
   较上年末下降12bps。"

问题:
├── 分词: 中文需要分词、英文按空格分,混合时边界模糊
├── Embedding: 不同模型对中英混合的表示能力差异大
├── Chunking: 中文句号"。"vs英文句号".",分隔符需要同时考虑
└── OCR: 中英混合区域的识别准确率下降

推荐方案:
├── Embedding模型选型:
│   ├── 优选: BGE-M3 / Cohere multilingual-v3 / OpenAI text-embedding-3-large
│   ├── 次选: E5-mistral (英文强中文一般)
│   └── 避免: 纯英文模型(all-MiniLM等)
│
├── Chunking分隔符配置:
│   separators = [
│       "\n\n",        # 段落分隔(通用)
│       "\n",          # 换行(通用)
│       "。",          # 中文句号
│       "!", "?",    # 中文感叹/问号
│       ";",          # 中文分号
│       ". ",          # 英文句号+空格
│       "! ", "? ",    # 英文感叹/问号
│       "; ",          # 英文分号
│       " ",           # 空格
│       "",            # 逐字符(最后手段)
│   ]
│
└── OCR配置:
    languages=["chi_sim", "chi_tra", "eng"]  # 简体+繁体+英文

金融文档特殊处理

金融报表(年报/10-K/招股书):
├── 特点: 高度结构化、表格密集、数字精确、术语密集
├── 处理:
│   ├── 表格: 必须精确提取,一个小数点错误可能导致严重后果
│   ├── 脚注: 不能丢弃!脚注往往包含关键会计政策说明
│   ├── 图表: 需要描述性文字(VLM生成)
│   └── MD&A: 管理层讨论部分是最有价值的文字段
└── Chunking: 按报表章节结构切分,每个财务科目+注释保持完整

合同/法律文件:
├── 特点: 条款编号严格、交叉引用多("见第3.2(b)条")
├── 处理:
│   ├── 条款编号保留: "第3.2(b)条"必须出现在Chunk中
│   ├── 定义条款: 单独提取为参考Chunk(很多术语在合同开头定义)
│   ├── 交叉引用: Metadata中标注引用关系
│   └── 签章页: 通常无需索引
└── Chunking: 按条款编号切分,不切断完整条款

监管文件(巴塞尔/MiCA/SEC规则):
├── 特点: 条文编号、大量"应当"/"必须"/"不得"
├── 处理:
│   ├── 条文编号作为Metadata: {"regulation": "Basel III", "article": "4.2.1"}
│   ├── 生效日期: 标注每条规定的生效/过渡期
│   └── 影响范围: 标注适用机构类型
└── Chunking: 按条文编号切分,保留上下文(条文间有依赖)

OCR + LLM后处理

扫描件处理流程(2026最佳实践):

Step 1: 预处理
  ├── 倾斜校正(Deskew)
  ├── 二值化(Binarization)
  ├── 去噪(Denoise)
  └── 分辨率增强(Super-Resolution,如果<200DPI)

Step 2: OCR引擎
  ├── Tesseract 5.x: 开源、支持中文、需要训练数据
  ├── PaddleOCR: 百度开源、中文最强、支持版面分析
  ├── EasyOCR: 多语言、轻量级
  └── 商业: Azure/Google/AWS (准确率最高、有SLA)

Step 3: LLM后处理(关键创新)
  将OCR原始输出喂给LLM进行纠错和结构化:

  Prompt: "以下是一份金融文档的OCR结果,可能存在识别错误。
           请修正明显的OCR错误(如: O和0混淆、l和1混淆),
           恢复正确的段落结构,保持所有数字精确。
           原始OCR: {ocr_text}"

  效果:
  ├── OCR原始:  "净利润达到52l.6亿元,同比增长8.2o%"
  └── LLM纠正: "净利润达到521.6亿元,同比增长8.2%"

Step 4: 质量验证
  ├── 字符置信度 < 阈值的区域标记为"低置信"
  ├── 数字区域交叉校验(总数=各项之和)
  └── 低置信区域回退到VLM重新解析

知识点6: 数据Pipeline设计

完整ETL流程

文档RAG的ETL Pipeline:

┌──────────┐   ┌──────────┐   ┌──────────┐   ┌──────────┐   ┌──────────┐   ┌──────────┐
│ 1. 采集   │→  │ 2. 解析   │→  │ 3. 切分   │→  │ 4. 增强   │→  │ 5. 索引   │→  │ 6. 维护   │
│ Ingest   │   │ Parse    │   │ Chunk    │   │ Enrich   │   │ Index    │   │ Maintain │
└──────────┘   └──────────┘   └──────────┘   └──────────┘   └──────────┘   └──────────┘

详细流程:

1. 采集(Ingest)
   ├── 数据源: 文件系统/S3/SharePoint/Confluence/邮件
   ├── 格式检测: MIME type识别 + Magic number验证
   ├── 去重: 文件Hash去重(避免重复处理)
   ├── 元数据提取: 文件名/大小/修改时间/作者
   └── 队列: 推入处理队列(按优先级排序)

2. 解析(Parse)
   ├── 路由: 根据格式选择解析策略(Tier 1/2/3)
   ├── 文字提取: 获取原始文本
   ├── 结构识别: 标题/段落/列表/表格/图片
   ├── 表格处理: 检测 → 提取 → 转Markdown
   ├── 图片处理: OCR/VLM描述
   └── 清洗: 去页眉页脚/水印/重复空行

3. 切分(Chunk)
   ├── 策略选择: 根据文档类型选择Chunking策略
   ├── 参数配置: chunk_size/overlap/分隔符
   ├── 执行切分: 生成Chunk列表
   ├── Metadata附加: 来源/页码/标题路径/类型
   └── 质量过滤: 过滤太短(<50字)/太长(>3000字)/乱码Chunk

4. 增强(Enrich)
   ├── Embedding生成: 每个Chunk → 向量
   ├── 摘要生成: (可选) LLM为每个Chunk生成摘要
   ├── 实体提取: (可选) 提取命名实体
   ├── 关键词提取: (可选) 用于BM25混合检索
   └── 分类标注: (可选) 自动分类(财务/法律/技术)

5. 索引(Index)
   ├── 向量写入: 写入向量数据库(Pinecone/Weaviate/Qdrant)
   ├── 全文写入: 写入Elasticsearch/OpenSearch(BM25)
   ├── Metadata写入: 写入关系数据库(用于过滤)
   ├── 原文存储: 原始文档存S3/MinIO(用于溯源)
   └── 索引验证: 抽样查询验证索引正确性

6. 维护(Maintain)
   ├── 增量更新: 新文档 → 检测 → 处理 → 追加索引
   ├── 版本管理: 文档更新时 → 旧Chunk标记过期 → 新Chunk替换
   ├── 过期清理: 定期清理过期/删除的文档Chunk
   ├── 重新索引: Embedding模型升级时全量重新Embedding
   └── 质量监控: 持续监控检索命中率/用户反馈

增量更新策略

增量更新是生产环境的核心需求(不能每次全量重建):

方案1: 文件级增量
  ├── 检测: 文件Hash/修改时间变化
  ├── 处理: 整个文件重新解析 → 删除旧Chunks → 插入新Chunks
  ├── 优点: 实现简单
  └── 缺点: 一个字改动就重处理整个文件

方案2: Chunk级增量(推荐)
  ├── 检测: 对比新旧文件的Chunk内容Hash
  ├── 处理:
  │   ├── 新增Chunk → 解析+Embedding+索引
  │   ├── 删除Chunk → 从索引中移除
  │   ├── 修改Chunk → 更新Embedding+索引
  │   └── 未变Chunk → 不处理
  ├── 优点: 最小化处理量
  └── 缺点: 实现复杂(需要Chunk级对齐)

方案3: Append-Only + TTL
  ├── 策略: 新版本Chunk直接追加,旧版本标记过期时间
  ├── 检索: 优先返回最新版本
  ├── 清理: 定期GC过期Chunks
  ├── 优点: 无删除操作,安全性高
  └── 缺点: 存储膨胀

金融场景推荐:
  日常文档 → 方案2(Chunk级增量)
  监管文件 → 方案3(Append-Only,保留历史版本用于审计)
  年报/季报 → 方案1(低频更新,全量重处理可接受)

质量检查Gate

质量Gate: 每个阶段的质量检查关卡(不合格则不进入下一阶段)

Gate 1: 解析质量检查
├── 字符准确率: OCR置信度 > 95%(金融场景要求>98%)
├── 结构完整性: 标题层级连续、表格行列数合理
├── 内容完整性: 提取字符数 vs 预期字符数(基于页数估算)
├── 编码正确性: 无乱码/特殊字符异常
└── 不通过: 回退到更高Tier重新解析 或 进入人工审核队列

Gate 2: Chunk质量检查
├── 长度分布: 检查是否存在异常短/长的Chunk
├── 语义完整性: 抽样检查Chunk是否是完整语义单元
├── Metadata完整性: 必填字段不能为空
├── 重复检测: MinHash/SimHash去除近似重复Chunk
└── 不通过: 调整Chunking参数重新切分

Gate 3: Embedding质量检查
├── 维度验证: 向量维度正确
├── 异常检测: 检测全零向量/异常值
├── 语义验证: 抽样对比"已知相关Chunk对"的余弦相似度
└── 不通过: 检查Embedding模型是否正常

Gate 4: 索引质量检查
├── 查询验证: 用预设测试查询验证检索结果
├── 召回验证: 已知答案应该能被检索到
├── 延迟验证: 查询响应时间在SLA内
└── 不通过: 检查索引配置/数据分布

质量Dashboard指标:
├── 解析成功率: 成功解析文档数 / 总文档数
├── Chunk质量分布: 优/良/差的比例
├── 平均Chunk长度: 监控是否偏移
├── 检索命中率: 测试查询的命中率趋势
└── 用户反馈率: "答案不相关"的反馈频率

今日思考

思考1: "数据工程师"才是RAG系统最重要的角色

一个常见误区: 团队把最优秀的人放在"模型选型"和"Prompt优化"上,
让初级工程师负责"数据处理"。结果呢?

  模型: GPT-4o (最强)
  Prompt: 精心调优
  数据: PDF解析一堆乱码,表格全部丢失,Chunk随机切割

  → 输出质量: 垃圾

正确的资源分配:
  数据处理(解析+Chunking): 60%的工程精力
  检索优化(Embedding+Reranking): 25%
  生成优化(Prompt+模型选型): 15%

这和我10年金融系统经验完全一致:
  银行数据仓库项目中,ETL团队永远是最大的团队,
  因为数据质量问题永远是最多的问题。
  报表错了?80%概率是数据源头就错了。

思考2: VLM解析是"降维打击",但成本曲线决定了分层策略的必要

2025年: GPT-4o直接"看"PDF的效果已经超过大多数传统解析工具
2026年: 多模态模型价格持续下降,但距离"全量VLM解析"仍有距离

成本对比(处理10万页文档):
  传统解析(Unstructured fast): ~$100
  传统解析(Unstructured hi_res): ~$1,000
  VLM解析(GPT-4o): ~$5,000-10,000

对于银行/金融机构:
  每年要处理的文档: 数百万页
  全用VLM: 每年$50,000-100,000 (仅解析成本)
  分层策略: 每年$5,000-10,000 (80%走传统,20%走VLM)

结论: 分层解析策略不是"落后",而是工程上的最优解。
      就像银行不会对每笔交易都做最高级别审查——
      小额走快速通道,大额走人工审核。

思考3: Chunking没有银弹——必须在自己的数据上做实验

我见过太多团队陷入"参数调优陷阱":
  "用512还是1024?" "Overlap用10%还是20%?" "用Semantic还是Recursive?"

现实是: 没有通用最优解,因为:
  1. 不同文档类型的最优参数不同(法律合同 vs 技术文档)
  2. 不同查询类型的最优参数不同(事实查询 vs 分析查询)
  3. 不同Embedding模型的最优参数不同(ada-002 vs BGE-M3)
  4. 甚至同一类文档,不同来源的最优参数也不同

正确做法:
  1. 建立评估基准(Golden Set): 100组<问题, 正确答案, 来源文档>
  2. 用3-5种策略分别处理
  3. 跑自动化评估(RAGAS/DeepEval)
  4. 选择在你的场景下最优的策略
  5. 持续监控线上效果,定期迭代

这就是工程与研究的区别:
  研究: 找到理论最优解
  工程: 在约束条件(成本/延迟/数据特性)下找到实际最优解

面试表达

Q: 你如何设计一个生产级RAG系统的数据处理Pipeline?

结构化回答(2分钟版):

1. 分层解析架构:
   "我会设计三层解析策略:
    - Tier 1(80%文档): 用Unstructured.io快速模式处理标准格式
    - Tier 2(15%文档): 用hi_res模式+pdfplumber处理复杂表格
    - Tier 3(5%文档): 用GPT-4o/Claude多模态解析处理扫描件和非标文档
    路由逻辑基于文档格式和Tier 1的质量评分自动决策。"

2. Chunking策略:
   "默认使用Recursive Character Splitting + 中文分隔符优化。
    对于结构化文档(年报/法规)使用Document-Structure切分。
    在检索质量要求高的场景使用Parent-Child策略。
    关键是: 在自己的数据上做AB实验,没有万能参数。"

3. 质量保障:
   "每个阶段设置Quality Gate:
    - 解析后: 检查字符准确率、结构完整性
    - Chunking后: 检查长度分布、语义完整性
    - 索引后: 用Golden Set测试检索召回
    不合格的数据不进入下一阶段。"

4. 持续运维:
   "Chunk级增量更新避免全量重建。
    监控检索命中率和用户反馈,持续迭代解析和Chunking策略。"

追问准备:
  Q: 表格怎么处理?
  → "Markdown存储 + 自然语言描述作为辅助Metadata。跨页表格通过上下文检测合并。"

  Q: 中英混合文档怎么办?
  → "Embedding选BGE-M3/Cohere multilingual,Chunking分隔符同时覆盖中英文标点。"

  Q: 如何评估解析质量?
  → "对比OCR输出与原文的字符准确率,表格提取用单元格级别的F1 Score。"

Q: Chunking策略怎么选?为什么?

"选择Chunking策略的核心考虑:

1. 看文档类型:
   - 结构良好(有标题层级): Document-Structure优先
   - 结构混乱(纯文本/聊天): Recursive Character作为Baseline
   - 高价值少量文档: 可以用Semantic甚至Agentic

2. 看查询模式:
   - 精确事实查询('利率是多少'): 小Chunk(256-512 tokens)
   - 分析型查询('风险因素有哪些'): 大Chunk(1024-2048 tokens)
   - 混合查询: Parent-Child策略(小Child检索 + 大Parent返回)

3. 看成本约束:
   - Semantic Chunking需要为每个句子计算Embedding
   - Agentic Chunking需要为每个切割点调LLM
   - 生产环境通常用Recursive + 结构化的组合

4. 最终靠实验:
   建Golden Set,跑评估,选在自己数据上表现最好的。"

实操练习建议

1. 用Unstructured.io解析一份金融PDF:
   pip install unstructured[all-docs]
   → 下载一份上市公司年报PDF
   → 分别用fast和hi_res模式解析,对比结果
   → 重点关注表格提取的准确性

2. 对比3种Chunking策略:
   → 对同一文档分别用Fixed(512)/Recursive(512)/Semantic切分
   → 人工检查10个Chunk的语义完整性
   → 用同样的测试查询对比检索效果

3. 用VLM解析复杂文档:
   → 将一页包含表格的PDF转为图片
   → 发送给GPT-4o/Claude,要求转为结构化Markdown
   → 对比传统工具的提取结果

4. 搭建简单的Quality Gate:
   → 写一个脚本检查Chunk长度分布
   → 检测异常短/长Chunk并标记
   → 计算Chunk去重后的独立信息比例

资源推荐

资源说明链接
Unstructured.io文档最全的文档解析开源框架unstructured.io/docs
LlamaParse文档LLM驱动的解析APIdocs.cloud.llamaindex.ai
Docling (IBM)开源文档理解框架github.com/DS4SD/docling
Greg Kamradt - Chunking视频5种Chunking策略对比实验YouTube搜索
Pinecone Chunking指南实操导向的Chunking教程pinecone.io/learn/chunking-strategies
LlamaIndex文档Node Parser完整文档docs.llamaindex.ai
RAGAS文档RAG评估框架(Day 21深入)docs.ragas.io
pdfplumberPython PDF表格提取库github.com/jsvine/pdfplumber

明日预告

Day 20: 生产级RAG(2) — Retrieval优化与Reranking

预习问题:
1. 为什么Embedding检索(向量搜索)不够,还需要Reranking?
2. Cross-Encoder和Bi-Encoder有什么区别?为什么Reranker用Cross-Encoder?
3. Hybrid Search(向量+BM25)比纯向量搜索好多少?在什么场景下?

连接点:
  Day 19(解析+Chunking) → Day 20(检索+Reranking)
  → 今天解决"数据怎么准备",明天解决"数据怎么找到"
  → Chunk质量决定了检索的上限,Reranking决定了检索的下限
  → 一个完整的RAG检索链路: Query → Embedding → ANN → Rerank → Top-K → LLM