企业级RAG性能优化与质量治理(2):文档解析与Chunk工程决定知识库上限

文章摘要

很多团队把RAG效果差归因于Embedding模型、向量数据库或大模型,却忽略了最前面的文档解析与Chunk工程。PDF乱码、表格丢失、标题层级消失、旧版本混入和固定长度粗暴切分,会在数据进入向量库前就破坏知识。本文建立一套生产级文档管道:文件识别、结构解析、清洗、质量门禁、结构分块、父子Chunk、Metadata、版本管理、增量索引和自动评测,并给出Spring AI实现框架。

一、企业RAG的效果上限在哪里

完整链路:

原始文件
→ 文档解析
→ 内容清洗
→ 结构恢复
→ Chunk
→ Metadata
→ Embedding
→ 向量库
→ 检索
→ Rerank
→ 大模型回答

如果最前面的解析已经丢失信息:

正确内容没有进入Chunk

后面的Embedding、检索和大模型都无法恢复。

所以企业RAG的第一条原则是:

先保证知识被正确解析和组织,再讨论检索算法。

二、不要把“文件上传成功”等同于“知识入库成功”

一个文件经过多个状态:

UPLOADED
→ TYPE_DETECTED
→ PARSING
→ PARSED
→ QUALITY_CHECKED
→ CHUNKED
→ EMBEDDED
→ INDEXED
→ VERIFIED

任何一步失败,都不应该直接标记为可用。

建议状态:

UPLOADED
PARSING
PARSE_PARTIAL
OCR_REQUIRED
MANUAL_REVIEW
CHUNKING
INDEXING
INDEXED
VERIFIED
FAILED

三、文件类型识别不能只看扩展名

用户上传:

document.pdf

实际可能是:

  • 标准文字PDF;
  • 扫描PDF;
  • 加密PDF;
  • 损坏文件;
  • 图片改扩展名;
  • 内嵌附件PDF;
  • 混合文本与扫描页。

需要检测:

MIME
Magic Number
是否加密
页数
文件大小
是否包含文本层
是否包含图片
语言

伪模型:

public record FileInspection(
        String detectedType,
        long sizeBytes,
        int pages,
        boolean encrypted,
        boolean hasTextLayer,
        boolean requiresOcr
) {
}

四、解析器应该按文档类型路由

Markdown → Markdown Reader
HTML → Jsoup Reader
DOCX/PPTX → Tika或专用解析器
普通PDF → PDF版面解析
扫描PDF → OCR+版面分析
复杂表格PDF → Docling等结构解析器
图片 → OCR或视觉模型

统一接口:

public interface EnterpriseDocumentParser {

    boolean supports(FileInspection inspection);

    ParsedDocument parse(
            StoredFile file,
            ParseOptions options
    );
}

路由器:

@Component
public class ParserRouter {

    private final List parsers;

    public EnterpriseDocumentParser route(
            FileInspection inspection
    ) {
        return parsers.stream()
                .filter(parser ->
                        parser.supports(inspection)
                )
                .findFirst()
                .orElseThrow(() ->
                        new IllegalArgumentException(
                                "没有可用解析器"
                        )
                );
    }
}

五、ParsedDocument必须是结构化对象

不要只返回:

一大段纯文本

推荐:

public record ParsedElement(
        String elementId,
        ElementType type,
        String text,
        int page,
        BoundingBox boundingBox,
        List sectionPath,
        Map metadata
) {
}

元素类型:

TITLE
SECTION_HEADER
PARAGRAPH
LIST
TABLE
CODE
FORMULA
IMAGE_CAPTION
FOOTNOTE
HEADER
FOOTER

结构信息决定后续如何分块。

六、解析阶段需要保留哪些信息

至少保留:

文件名
文档ID
文档版本
页码
章节路径
元素类型
页面坐标
表格ID
图片ID
语言
解析器版本
OCR置信度

这些信息用于:

  • 来源引用;
  • 页面跳转;
  • 结构分块;
  • 表格处理;
  • 质量检查;
  • 问题追溯;
  • 重新解析。

七、清洗不是简单去掉空格

需要处理:

页眉页脚

跨页重复,容易污染检索。

页码

保留为Metadata,不应混入正文。

目录

目录和正文高度重复,可能导致错误召回。

水印

机密
仅供内部使用
公司名称

每页重复会影响Embedding。

断行

PDF可能把一句话拆成:

企业级RAG需要
完整的文档治理。

需要根据标点、坐标和段落结构合并。

连字符

英文换行:

retriev-
al

应恢复为:

retrieval

八、清洗必须可追溯

不要覆盖唯一原始结果。

保存:

raw_parse.json
cleaned_parse.json
chunk_result.json

同时记录:

parser_version
cleaner_version
chunk_strategy_version

当检索结果错误时,可以回到每一步定位。

九、质量门禁是生产系统的必要组件

public record DocumentQualityReport(
        double emptyPageRatio,
        double garbledRatio,
        double duplicateLineRatio,
        double ocrPageRatio,
        int tableCount,
        int warningCount,
        QualityStatus status
) {
}

质量规则示例:

空白页比例 > 30% → OCR_REQUIRED
乱码率 > 5% → MANUAL_REVIEW
有效文本字符  2.1 住宿标准
正文:……

十一、Chunk领域模型

public record KnowledgeChunk(
        String chunkId,
        String parentId,
        String documentId,
        String documentVersion,
        String content,
        String contentType,
        List sectionPath,
        int pageStart,
        int pageEnd,
        Map metadata
) {
}

Chunk ID必须稳定。

可以使用:

document_id
+document_version
+element范围
+chunk_strategy_version

十二、固定长度分块的正确用途

固定Token分块仍然有价值,但应该作为最后一层保护:

结构单元超过Embedding上限
→ TokenTextSplitter再次拆分

而不是用它替代文档结构。

Spring AI示意:

TokenTextSplitter splitter =
        TokenTextSplitter.builder()
                .withChunkSize(600)
                .withMinChunkSizeChars(200)
                .withMinChunkLengthToEmbed(20)
                .withKeepSeparator(true)
                .build();

中文项目还应配置中文标点。

十三、父子Chunk解决精度与完整性的矛盾

子Chunk

  • 200—500 Token;
  • 主题单一;
  • 负责向量召回。

父Chunk

  • 完整条款或章节;
  • 负责提供回答上下文。

流程:

查询
→ 搜索子Chunk
→ 获取parent_id
→ 加载父Chunk
→ 去重和压缩
→ 交给模型

父子Chunk适合:

  • 合同;
  • 制度;
  • 技术文档;
  • 长条款;
  • API说明。

十四、表格必须走独立分支

TABLE元素
→ 恢复行列结构
→ 保存Markdown
→ 保存JSON
→ 按整表或行分块
→ 数字计算进入结构化查询

表格Chunk附带:

table_id
title
headers
unit
row_range
page_range

不要让TokenSplitter从表格中间切断。

十五、图片和图表怎么办

图片可能包含:

  • 流程图;
  • 架构图;
  • 产品截图;
  • 数据图表;
  • 扫描文字。

处理策略:

图片分类
→ OCR或视觉模型描述
→ 保存图片引用
→ 与图注和章节绑定

图表数据如果用于精确问答,应提取结构化数据,不只保存自然语言描述。

十六、Metadata决定企业RAG能否治理

最小字段:

document_id
document_version
chunk_id
parent_id
tenant_id
status
effective_date
expired_at
section_path
page_start
page_end
content_type
parser_version
chunk_strategy_version

用途:

  • 多租户隔离;
  • 版本过滤;
  • 有效期过滤;
  • 来源引用;
  • 删除;
  • 增量更新;
  • 检索评测。

十七、文档版本不能靠文件名判断

制度最终版.pdf
制度最终版2.pdf
制度最新正式版.pdf

文件名无法表达真实版本关系。

应该维护:

document_id
version
status
effective_date
expired_at
supersedes_version

发布新版本:

解析新版本
→ 入库新Chunk
→ 验证检索
→ 激活新版本
→ 旧版本标记EXPIRED

不要先删除旧版本。

十八、增量索引如何实现

计算元素或Chunk哈希:

String contentHash = sha256(
        normalizedContent
);

对比:

新增Chunk → 写入
修改Chunk → 更新向量
删除Chunk → 删除或失效
未变化Chunk → 跳过

这样可以避免整库重建。

十九、解析器升级也可能需要重建索引

即使原文件不变,以下变化也会改变Chunk:

  • 解析器版本;
  • OCR模型;
  • 表格模型;
  • 清洗规则;
  • Chunk策略;
  • Embedding模型。

因此索引版本应包含:

parser_version
cleaner_version
chunk_version
embedding_version

二十、Spring AI ETL架构

Spring AI ETL核心:

DocumentReader
→ DocumentTransformer
→ DocumentWriter

企业实现:

@Service
public class EnterpriseRagIngestionService {

    private final ParserRouter parserRouter;
    private final DocumentQualityService qualityService;
    private final ChunkingService chunkingService;
    private final VectorStore vectorStore;

    public void ingest(StoredFile file) {
        FileInspection inspection =
                inspect(file);

        EnterpriseDocumentParser parser =
                parserRouter.route(inspection);

        ParsedDocument parsed =
                parser.parse(
                        file,
                        ParseOptions.defaults()
                );

        qualityService.validate(parsed);

        List chunks =
                chunkingService.chunk(parsed);

        List documents =
                chunks.stream()
                        .map(this::toSpringDocument)
                        .toList();

        vectorStore.add(documents);
    }
}

二十一、批量Embedding需要限流和断点

不能一次写入数十万Chunk。

建议:

按Token批次
→ 调用Embedding
→ 写入VectorStore
→ 保存批次状态
→ 失败后重试

状态:

batch_id
start_chunk
end_chunk
status
retry_count
error_code

需要区分:

  • 临时限流;
  • 永久格式错误;
  • 单个Chunk超长;
  • 数据库写入失败。

二十二、入库完成后必须做检索验证

每个文档可以生成几个自动验证问题:

标题是什么?
生效日期是什么?
某关键条款是什么?
表格中的某项数据是多少?

如果基础问题无法检索到正确Chunk,文档不应进入正式可用状态。

状态:

INDEXED
→ VERIFYING
→ VERIFIED

二十三、Chunk质量如何评测

完整性

正确答案所需内容是否在同一Chunk或父Chunk中。

纯度

Chunk是否包含多个无关主题。

可检索性

用户问题能否召回它。

可引用性

是否能定位章节和页码。

重复率

多个Chunk是否高度重复。

指标:

chunk_answer_coverage
chunk_topic_purity
duplicate_chunk_ratio
parent_retrieval_success
citation_completeness

二十四、建立文档类型策略库

strategies:
  policy:
    parser: layout
    chunker: clause
    parent-child: true

  contract:
    parser: layout
    chunker: clause
    preserve-tables: true

  manual:
    parser: layout
    chunker: heading

  faq:
    parser: structured
    chunker: qa-pair

  log:
    parser: text
    chunker: token-window

不要让所有文件使用同一套参数。

二十五、生产级观测指标

parse_success_rate
parse_partial_rate
ocr_required_rate
manual_review_rate
average_parse_time
average_chunks_per_document
duplicate_chunk_ratio
embedding_failure_rate
index_verification_pass_rate

还要关联:

parser_version
chunk_strategy
retrieval_success
answer_quality

才能知道哪个环节真正改善了效果。

二十六、常见失败模式

只保存纯文本

丢失结构和来源。

所有PDF都用同一个Reader

扫描件和复杂表格效果差。

全部固定500 Token

条款与表格被切断。

没有版本状态

旧制度和新制度冲突。

解析成功就直接入库

乱码内容进入生产知识库。

修改分块后不重建测试基线

不知道效果变好还是变差。

二十七、本篇落地清单

□ 文件类型识别
□ 解析器路由
□ 结构化ParsedDocument
□ 页眉页脚清理
□ OCR质量检查
□ 表格独立处理
□ 结构优先分块
□ Token上限保护
□ 父子Chunk
□ 完整Metadata
□ 文档版本管理
□ 增量索引
□ 入库后检索验证
□ 解析和Chunk指标

总结

企业RAG的文档管道应该是:

识别
→ 解析
→ 清洗
→ 质量门禁
→ 结构分块
→ Metadata
→ 版本治理
→ Embedding
→ 索引验证

真正的质量提升往往不来自更大的模型,而来自:

文档没有丢
结构没有乱
表格没有散
版本没有冲突
Chunk保留完整语义

下一篇将继续进入:

企业级RAG混合检索实战:BM25、向量召回、Reranker与动态Top K。

延伸阅读

如果你正在关注企业级 AI 应用、RAG、Agent、MCP 与大模型工程化落地,欢迎访问 智元界

https://www.zyentor.com/

智元界将持续分享可运行的技术实战、架构设计、问题排查与企业应用案例。