图 1:本文封面,索引构建的五环流水线。
面试里被问「RAG 的分块有哪些切法」时,这个系列另有一篇真题解析专门拆解分块方法本身。本篇换个视角:分块只是索引构建流水线的中间一环,从原始文档到可检索索引,端到端要走解析、清洗、分块、嵌入、入库五步,每一步都有典型坑。索引质量在上线前就已定型——线上检索烂,多半是这里埋的雷。
第一环:文档解析。 PDF、Word、网页要变成结构化纯文本。典型坑是版式:多栏排版被按行读成左右交错的乱序文字,表格被打散成一列孤立的单元格,标题层级全部丢失。按文字坐标还原阅读顺序、按 DOM 结构提取正文,比直接抽文本可靠得多;扫描件还要先过 OCR,识别错误的文字会直接变成检索噪声。这一环偷懒,后面所有环节都在给垃圾输入擦屁股。
第二环:清洗。 去掉页眉页脚、页码、乱码、「内部资料请勿外传」这类模板行。坑在尺度:规则太松留下噪声,噪声会被嵌入成向量、稀释检索;规则太狠误删正文,答案直接被洗掉。清洗规则要按文档来源分别配置,并保留清洗前后的人工抽查环节。
第三环:分块。 把干净文本切成数百 token 的块并留重叠,方法细节见真题解析,这里只强调它在流水线里的位置:分块消费的是清洗后的文本,输出时要带上「来自哪篇文档、第几块」的元数据雏形,供入库环节使用。坑是块大小失衡——块太大噪声多,块太小上下文碎。
第四环:嵌入。 每个块算成一个高维向量。坑是通用嵌入模型在专业领域(医疗、法律、内部黑话)表征弱:语义相近的领域表述在向量空间里距离并不近,必要时换领域模型或用自有语料微调嵌入模型。还要记住一个约束:嵌入模型上线后很难换——换模型意味着全量重建索引,选型要当成架构决策来做。
第五环:入库。 向量连同元数据写入向量库建索引。最大的坑是漏挂元数据:来源、时间、权限、文档 ID 没有随向量入库,上线后想做「只查某个部门」「只查近半年」的过滤检索就没有地基。第二个坑是更新:文档修订后旧向量残留,新内容上线了旧答案还能被召回。正确做法是按文档 ID 先删旧向量、只重建受影响的块。
用一段迷你流水线把五环串起来(Node.js 22.15.0,嵌入用字符频次向量占位演示,node pipeline.js):
function upsertDocument(docId, text) {
for (let i = store.length - 1; i >= 0; i--) {
if (store[i].meta.docId === docId) store.splice(i, 1); // 先删该文档旧向量
}
chunk(clean(text), 40).forEach((c, idx) => {
store.push({ vec: fakeEmbed(c), meta: { docId, chunkIndex: idx, source: 'manual.pdf' } });
});
}
实测输出:
更新 doc-1: 删除旧向量 0 条,写入新向量 2 条
清洗后残留页脚? false
首块元数据 = {"docId":"doc-1","chunkIndex":0,"source":"manual.pdf"}
更新 doc-1: 删除旧向量 2 条,写入新向量 3 条
库内总向量数 = 3
页脚「- 3 -」和模板行被清洗环节去掉;每个块带着 docId 与 chunkIndex 入库;文档更新时旧的两条向量先被删除、新的三条写入,库里不留残影——这就是「按文档 ID 更新」的最小闭环。
图 2:五个环节的输入输出与典型坑,以及更新机制。
入库之前还有一次自检机会。 流水线跑完别急着上线:抽几十条真实问题做检索回放,看命中的块是不是答案所在;再抽一批块人工读一遍,看有没有被切断的句子、残留的页眉、乱序的表格。五环里任何一环返工,下游都要跟着重建——改动越早越便宜。索引侧的问题在构建阶段发现,代价是重跑一遍流水线;等用户在线上碰到,代价就是信任了。
收尾。 五个环节的记忆点:解析保结构、清洗讲尺度、分块定粒度、嵌入选模型、入库带元数据与更新机制。LlamaIndex 官方文档把这条链抽象为 IngestionPipeline——文档进、节点(块)出,分块器只是其中一个变换环节,这个抽象本身就说明:分块是流水线的中段,不是全部。你的知识库流水线在哪一环踩坑最多?欢迎分享。