文档解析策略模式、Milvus+MySQL双写、智能去重与共享向量
文档处理全流程
══════════════
用户上传文档 (PDF/DOCX/MD)
│
▼
┌──────────────────────────────┐
│ 1. 文档解析 (hub-doc-parser) │
│ ParserFactory → 策略选择 │
│ PDF → PdfDocumentParser │
│ DOCX → WordDocumentParser │
│ MD → MarkdownDocParser │
│ → 提取纯文本 │
└──────────────┬───────────────┘
▼
┌──────────────────────────────┐
│ 2. 文本分块 (TextChunker) │
│ 递归字符分块: │
│ · 块大小: 500字符 │
│ · 重叠: 100字符 │
│ · 段落边界感知 │
└──────────────┬───────────────┘
▼
┌──────────────────────────────┐
│ 3. 去重检查 (DuplicateCheck) │
│ SHA-256哈希 → 精确匹配 │
│ 向量相似度 → 语义去重 │
│ (阈值: >0.8) │
└──────────────┬───────────────┘
▼
┌──────────────────────────────┐
│ 4. 向量化 + 双写 │
│ 文本 → Embedding → 向量 │
│ Milvus: 存向量 + 搜索 │
│ MySQL: 存元数据 + 全文索引│
└──────────────────────────────┘
ParserFactory 根据文件类型选择不同的解析策略:
ParserFactory (策略模式):
┌─────────────────────┐
│ ParserFactory │
│ getParser(type) │
└─────────┬───────────┘
│
┌──────────────┼──────────────┐
│ │ │
▼ ▼ ▼
┌────────────┐ ┌────────────┐ ┌────────────┐
│ PdfParser │ │ WordParser │ │ MdParser │
│ (PDFBox) │ │ (POI) │ │(CommonMark)│
└────────────┘ └────────────┘ └────────────┘
策略接口: DocumentParser.parse(inputStream) → String
新增格式只需:
1. 实现 DocumentParser 接口
2. 注册到 ParserFactory
3. 无需修改任何现有代码
TextChunker 分块算法:
原始文本:
"第一段内容。这是关于AI的知识。
第二段内容。机器学习是AI的子集。
第三段内容。深度学习是机器学习的子集。"
配置: chunkSize=50, overlap=10
Chunk 1: "第一段内容。这是关于AI的知识。第二段内容。机器" (50字符)
Chunk 2: "器学习是AI的子集。第三段内容。深度学习是机器学" (50字符)
↑ 重叠10字符
Chunk 3: "机器学习的子集。" (剩余部分)
关键特性:
· 优先在段落边界 (\n\n) 处分割
· 段落过长时用滑动窗口强制分割
· overlap确保上下文连续性
VectorIngestService 实现了向量数据库和关系数据库的双写:
VectorIngestService 双写架构:
文档块 → Embedding模型 → 向量
│
┌───────────┴───────────┐
▼ ▼
Milvus MySQL
┌──────────────┐ ┌──────────────────┐
│ 存储向量 │ │ 存储元数据 │
│ 向量搜索 │ │ 全文索引搜索 │
│ (语义匹配) │ │ (关键词匹配) │
│ │ │ │
│ collection: │ │ document_chunk: │
│ vector_id │ │ id, document_id │
│ embedding │ │ content │
│ metadata │ │ vector_id ←─────│── 共享向量指针
└──────────────┘ │ chunk_index │
│ content_hash │
└──────────────────┘
优势:
· Milvus擅长语义搜索 (理解"年假"≈"带薪休假")
· MySQL擅长精确搜索 (匹配"Q4-2024-001"编号)
· 两者互补,通过RRF融合
DuplicateCheckService 两阶段去重: 阶段1: SHA-256精确匹配 (快速) ┌──────────────────────────────────┐ │ hash = SHA-256(文件内容) │ │ SELECT * FROM document │ │ WHERE content_hash = hash │ │ AND user_id = current_user │ │ │ │ 命中 → 返回 "文档已存在" (2001) │ │ 未命中 → 进入阶段2 │ └──────────────────────────────────┘ 阶段2: 向量语义相似度 (深度) ┌──────────────────────────────────┐ │ 对新文档取样几个chunk │ │ 在Milvus中搜索相似文档 │ │ 相似度 > 0.8 → 可能是同一文档 │ │ │ │ 返回 "相似文档已存在" (2002) │ │ 可选择强制上传或取消 │ └──────────────────────────────────┘
ingestSharedFromExisting()方法:当检测到新文档内容与已有文档内容相同时,新文档的chunk直接复用已有chunk的vector_id。在MySQL中插入新的DocumentChunk记录,但vector_id指向已有的Milvus向量。删除时采用引用计数,只有引用为0时才真正删除向量。
hub-doc-parser 模块负责将用户上传的文档解析为可检索的文本块,供 RAG 系统使用。采用策略模式(Strategy Pattern)实现多格式支持。
文档解析引擎完整流程:
用户上传文档
│
▼
ParserFactory.getDocumentParser(contentType)
│
├─ application/pdf → PdfDocumentParser (PDFBox 3.0.3)
├─ application/...word.. → WordDocumentParser (POI 5.3.0)
└─ text/markdown → MarkdownDocParser (CommonMark 0.22.0)
│
▼
DocumentParser.parse(inputStream) → 纯文本
│
▼
TextChunker.chunk(text) → List<ParsedChunk>
│
▼
向量入库 (hub-vector-store → Milvus)
public interface DocumentParser {
boolean supports(String contentType); // 是否支持此MIME类型
String parse(InputStream inputStream); // 解析为纯文本
String supportedExtension(); // 支持的文件扩展名
}
// 新增格式零侵入:
@Component
public class TxtParser implements DocumentParser { ... }
// Spring 自动发现 → ParserFactory 自动注册
| 解析器 | 依赖 | 格式 | 特性 |
|---|---|---|---|
| PdfDocumentParser | Apache PDFBox 3.0.3 | 提取文本内容,处理多页文档 | |
| WordDocumentParser | Apache POI 5.3.0 | .docx | 提取段落、表格、列表等结构化内容 |
| MarkdownDocumentParser | CommonMark 0.22.0 | .md | 解析Markdown为纯文本,保留结构 |
index(块索引)、content(块内容)、metadata(元数据Map),为后续向量化和来源引用提供完整信息。
分块策略:
| 步骤 | 模块 | 操作 |
|---|---|---|
| 1. 上传 | hub-api | DocumentController 接收文件 |
| 2. 解析 | hub-doc-parser | ParserFactory 选择解析器,解析为文本 |
| 3. 分块 | hub-doc-parser | TextChunker 切分为 ParsedChunk 列表 |
| 4. 向量化 | hub-vector-store | VectorIngestService 双写 Milvus + MySQL |
| 5. 检索 | hub-llm-core | HybridSearchService 混合语义+关键词搜索 |
DocumentParser 接口定义统一的解析行为,每种格式一个策略实现类。ParserFactory 根据 MIME 类型路由到对应策略。新增格式(如 TXT、Excel)只需实现接口 + @Component,零侵入现有代码。这是开闭原则(OCP)的典型应用。
FAILED,记录错误信息。提供 reparse 接口支持用户重试。当前不做 OCR(过重),先支持文本层 PDF,后续可通过实现新的 DocumentParser 接入 OCR 服务,渐进式增强。