BotOf TechAI / IoT / Full-Stack / 植物养护
返回首页RAG 成熟度模型(一):索引工程与文档理解

RAG 成熟度模型(一):索引工程与文档理解

·5 分钟阅读·

很多团队做 RAG 的第一版都长这样:

PDF -> 文本提取 -> 固定长度切块 -> embedding -> vector DB

这条链路的问题在于:它把“文档”错误地当成了“纯文本”。生产系统里的知识并不只存在于句子里,还存在于标题层级、表格行列、页码、脚注、版本、权限、图注、代码块、阅读顺序和上下文位置里。只要这些结构在入库时丢掉,后面的 rerank、prompt、LLM 再强也只能在错误信息上做推理。

这一篇讨论 RAG 成熟度的第一层:索引工程。核心问题不是“用哪个向量库”,而是:

  1. 原始文档如何变成可验证的结构化元素;
  2. chunk 如何保留跨段上下文;
  3. embedding 模型如何匹配语言、领域和检索方式;
  4. 索引 schema 如何同时支持 dense、sparse、metadata、ACL、版本和引用追溯。

一、成熟度分层:从文本切块到证据索引

可以把 RAG 索引层分成 5 个成熟阶段。

阶段索引方式典型能力主要风险
L0原文全文塞 prompt无真正索引长文不可控,成本高
L1固定长度 chunk + dense vector能做语义召回表格、标题、版本全部弱化
L2结构化解析 + metadata支持过滤、引用、权限仍然容易 chunk 孤岛化
L3父子块 + contextual chunk检索粒度和生成粒度分离索引成本上升
L4dense + sparse + multi-vector语义、关键词、细粒度 token 匹配并行存储和融合复杂
L5图索引 / 树摘要 / 版本化证据库支持全局总结、多跳、时序追溯构建和维护成本高

成熟系统不会把所有文档一视同仁。FAQ、合同、代码、财报、研究论文、客服聊天记录需要不同解析策略。真正的工程边界是:入库时能不能还原“这个证据来自哪里、在什么结构中、适用于哪个版本、谁有权看、置信度是多少”

二、文档解析:RAG 最常见的隐藏瓶颈

文档解析不是简单 OCR。复杂 PDF 里至少有 5 类结构会影响检索质量:

  • 阅读顺序:双栏论文、页眉页脚、脚注会打乱文本顺序;
  • 表格结构:如果表格被拉平成几行文本,数字关系会丢失;
  • 标题层级:chunk 缺少父标题时,模型不知道段落主题;
  • 版面元素:图注、公式、列表、代码块需要不同保留方式;
  • 页码和坐标:引用必须能回到原文位置,否则用户无法验证。

开源解析工具可以按“通用 ETL”和“深度文档理解”两类看。

工具适合场景技术特点风险
Docling企业文档、PDF、Office、图片、音频转文本强调 advanced PDF understanding、reading order、table structure、公式、统一 DoclingDocument 表示、本地运行Python 生态,复杂文档解析速度和模型依赖需要压测
Unstructured多格式文档 ETL、分区、清洗、chunking、embedding 前处理partition 思路清晰,生态成熟,适合把非结构化文档转为 element 流高质量 PDF 场景常需要策略调参
MinerU中文/科研/复杂 PDF,转 Markdown/JSON目标是把复杂 PDF/Office 转成 LLM-ready Markdown/JSON,对表格、公式、版面友好工程集成时要关注模型体积、GPU/CPU 性能
Marker快速 PDF -> Markdown/JSON速度快、输出简洁,适合批量知识库原型对极复杂版面需要人工抽样评估
RAGFlow想要开箱即用的文档理解 + RAG 引擎把深度文档理解和 RAG workflow 打包成系统自定义底层 pipeline 的灵活度低于自建

我的建议:

  • 纯文本/Markdown/网页:自己写结构解析器,保留标题路径和 DOM/Markdown 层级;
  • Office/PDF 混合知识库:Docling 或 Unstructured 做第一层解析,再做自定义清洗;
  • 中文 PDF、论文、表格密集文档:MinerU / Marker 做候选,按样本集比较表格、公式和阅读顺序;
  • 不想自建 ingestion pipeline:RAGFlow 可以作为验证基线,但生产系统仍应保留独立评测集。

三、元素流:不要直接从文本进入 chunk

成熟 ingestion pipeline 不应该输出纯字符串,而应该先输出 element stream:

{
  "doc_id": "policy-2026-001",
  "version": "2026.1",
  "element_id": "p12-table-2-row-4",
  "type": "table_row",
  "title_path": ["财务制度", "差旅", "酒店标准"],
  "page": 12,
  "bbox": [108, 220, 510, 286],
  "text": "上海 | 一线城市 | 酒店上限 | 800 元/晚",
  "neighbors": ["p12-heading-3", "p12-table-2-row-3", "p12-table-2-row-5"],
  "parser_confidence": 0.91
}

这样做的收益很直接:

  1. 表格行可以单独检索,同时能回到整张表;
  2. 段落可以带标题路径,避免“这段话在说什么”丢失;
  3. 页面坐标可以支持 PDF 高亮;
  4. 解析置信度可以进入 rerank 或拒答策略;
  5. 文档版本和 ACL 可以在召回前过滤,而不是召回后删。

如果 ingestion 阶段只保留 text,生产后期想补引用、权限、版本追溯会非常痛苦。

四、chunk 策略:检索粒度和生成粒度要分离

固定长度 chunk 的问题是,它把两件事混在一起:

  • 检索需要“小粒度”,便于精确匹配;
  • 生成需要“大上下文”,便于模型理解完整含义。

成熟方案通常用父子块:

parent block: 一个章节 / 一个表格 / 一个函数文件 / 一段完整制度条款
child block: 句群 / 表格行 / 函数定义 / 小段落

检索时用 child block,进入 prompt 时返回 parent 或相邻窗口:

hits = dense_search(query, level="child", top_k=50)
parents = group_by_parent(hits)
context = expand_parent_window(parents, max_tokens=6000)

这样做避免两个失败:

  • chunk 太大:embedding 被多个主题污染,召回不准;
  • chunk 太小:召回到一句话,但模型缺少上下文,生成乱补。

五、Contextual chunk:给 chunk 补“位置语义”

Anthropic 的 Contextual Retrieval 方案强调:在建索引前,为每个 chunk 补一段说明,告诉检索器“这个 chunk 在整篇文档中处于什么位置”。这不是摘要替代原文,而是给 embedding 和 BM25 增加 disambiguation。

例如原 chunk:

上限为 800 元/晚,超出部分需要审批。

增强后:

本文档是 2026 年中国区差旅报销制度。本段位于“酒店标准 / 一线城市”章节,说明上海、北京、深圳等一线城市酒店报销上限。

上限为 800 元/晚,超出部分需要审批。

适合做 contextual chunk 的场景:

  • 法务、财务、制度文档;
  • 长章节中大量代词、省略主语;
  • 表格行、列表项、FAQ 答案脱离标题后语义不完整;
  • 多版本文档,需要说明适用时间。

不适合的场景:

  • 错误码表、API 参数表这种结构本身已经明确的文档;
  • 高频更新小文本,因为每次更新都要重新生成上下文;
  • 高合规场景中无法接受 LLM 生成的上下文污染索引,除非上下文也能审计。

六、Embedding 模型比较:不要只看 leaderboard

Embedding 选择要看 6 个维度:

  1. 语言:中文、英文、多语言、跨语言;
  2. 上下文长度:512、8K、32K;
  3. 输出维度和存储成本;
  4. 是否支持 instruction;
  5. 是否支持 sparse / multi-vector;
  6. 推理成本:CPU、GPU、批量吞吐、量化支持。
模型开放方式强项适合场景注意点
BGE-M3MITdense、sparse、multi-vector 三合一;100+ 语言;8192 token中英混合、希望一个模型同时支撑 hybrid 检索1024 维,multi-vector 存储成本高
Qwen3-EmbeddingApache-2.0 版本可用0.6B/4B/8B 多尺寸;32K;100+ 语言;支持自定义维度和 instruction中文/多语言企业知识库,长文本检索,追求最新开源性能4B/8B 推理成本明显高,线上要做 TEI/vLLM 压测
multilingual-e5-large-instruct开放权重instruction retrieval 成熟,多语言稳定问答、跨语言检索、需要 instruction 查询模板文档侧和查询侧格式要严格一致
Nomic Embed Text v1.5开放权重英文通用语义检索,维度可控英文知识库、轻量部署中文和跨语言不是第一选择
Jina Embeddings v3开放权重/商业生态混合多语种、多任务、长文本能力多语言搜索和分类任务混合使用前确认 license 与部署边界

关键结论:生产 RAG 不应该只依赖单一 dense embedding。即使 BGE-M3 或 Qwen3-Embedding 很强,关键词、数字、代码符号、专有名词仍然需要 sparse 或 BM25 补位。

七、索引 schema:把证据当一等公民

一个成熟的 chunk 表不是只有 textembedding

CREATE TABLE rag_chunks (
  chunk_id         text PRIMARY KEY,
  doc_id           text NOT NULL,
  parent_id        text,
  version          text NOT NULL,
  title_path       text[],
  element_type     text,
  page             int,
  bbox             jsonb,
  text             text NOT NULL,
  contextual_text  text,
  metadata         jsonb,
  acl              text[],
  valid_from       timestamptz,
  valid_until      timestamptz,
  parser_confidence real,
  created_at       timestamptz DEFAULT now()
);

再配多路索引:

-- dense vector
embedding vector(1024)

-- sparse/BM25 可走外部 search engine,也可用 tsvector
search_vector tsvector

-- 权限/版本/时间过滤
CREATE INDEX ON rag_chunks USING gin (acl);
CREATE INDEX ON rag_chunks (doc_id, version);
CREATE INDEX ON rag_chunks (valid_from, valid_until);

生产里最容易被忽视的是 ACL 前置过滤。如果先检索 top-k,再删除用户无权访问的结果,top-k 位置已经被污染,最终进入 prompt 的证据会不足。正确做法是把租户、权限、版本、时间条件尽量推到检索引擎内部。

八、索引平台选择:先看数据规模和事务需求

存储/索引强项适合不适合
pgvector和 Postgres 共存,事务、JOIN、ACL、版本管理方便中小规模企业知识库,已有 Postgres 团队百亿级向量、复杂分片、多租户超大规模
Qdrant向量检索工程成熟,payload filter 强,hybrid query 体验好中大型向量检索、过滤多、服务独立部署强事务和复杂关系查询
Milvus高规模向量数据库,支持 dense+sparse hybrid大规模向量、云原生、专门搜索团队小团队轻量项目可能过重
Weaviatehybrid search、schema、模块化能力强希望搜索和对象 schema 一体化已有稳定数据库栈时迁移成本高

选择规则:

  • 文档少于 100 万 chunk,团队已有 Postgres:先用 pgvector;
  • 需要独立向量服务、payload filter、hybrid query:Qdrant;
  • 需要大规模向量、多集合、高吞吐:Milvus;
  • 想要对象 schema + hybrid search 一体:Weaviate。

九、索引质量的离线评测

索引层至少要有下面几类测试集:

测试类型目的
标准问答 -> gold evidence看正确 chunk 是否能被召回
数字/表格问题看表格解析和 row-level index 是否可靠
版本问题看旧版本是否被排除
权限问题看 ACL 是否在召回前生效
跨标题问题看 parent-child 和 contextual chunk 是否有效
无答案问题看系统是否错误召回弱相关内容

指标:

recall@20
mrr@20
ndcg@20
gold_parent_recall
acl_violation_rate
version_error_rate
parser_error_bucket

不要等到生成答案再评测。索引层如果 recall@20 不合格,后面所有生成效果都是偶然。

十、一套推荐的索引底座

如果要做一个成熟企业 RAG,我会这样起步:

文档解析:
  Markdown/HTML -> 自研结构解析
  PDF/Office -> Docling + MinerU 抽样对比

元素模型:
  element stream -> parent/child chunk -> source span

上下文增强:
  只对制度、合同、长文档做 contextual chunk

Embedding:
  中文/多语言:Qwen3-Embedding-0.6B 或 BGE-M3
  需要 dense+sparse+multi-vector 统一:BGE-M3

索引:
  pgvector 起步
  规模扩大后迁移 Qdrant/Milvus

必备 metadata:
  doc_id, version, parent_id, title_path, page, bbox, acl, valid_from, valid_until, parser_confidence

这套方案的重点不是“最先进”,而是可验证、可迁移、可迭代。索引层要先把证据建模清楚,检索层才有优化空间。

参考资料