开发日志5-RAG知识库构建step2-切块
在对数据进行清洗+VLM图片-文本替换后,我们可以进行数据切块了。这里切块是基于LangChain框架完成的,LangChain 是一个 LLM 编程框架,我们开发一个基于 LLM 应用时可以从中获取需要的组件;并且对于常规的应用流程,LangChain已经有了内置的标准化方案了。
一、切块的主流方法:
1.固定长度切块:
优点是速度快逻辑简单易实现,缺点是它无脑按字符数/Token数切割,无法保证句子完整性,完全不管语义。比如会把"但是"、"然而"这种转折词切到下一段,造成前后文意思完全相反的严重后果。
2.递归切块:
利用LangChain 的RecursiveCharacterTextSplitter按照优先级逐级尝试,比如先找段落边界->没有再找句子边界->还没有就找句号“。”->最后再找空格。这样在90%的情况下能保持段落完整性,而且Word、Markdown、网页爬虫数据等各种格式都能处理。
3.语义切块:
利用LangChain 的 SemanticChunker、LlamaIndex,用embedding模型计算每两句话之间的"语义相似度",当相似度突然跌破阈值就在那里切块。这种方法按照逻辑切,虽然聪明但它“慢”且“贵”,因为每切一次都要调用 Embedding API,并且按 token 计费,比较适合在高价值文本比如金融分析报告、法律文书等上面使用。
4.特殊格式切块:
主要是为了代码和表格设计的,利用LangChain 的 LanguageParser(处理代码)、pdfplumber(处理表格)工具,确保不将代码、表格切割开造成语义不完整的碎片信息。
5.LLM增强切块:
利用LlamaIndex 的 Node Post-processors、LangGraph,让LLM先读一遍文档为其生成摘要、关键信息等等,也可以补充上下文背景,然后把这些内容注入到每个chunk中,相当于给每个chunk配备了说明书,可以告诉检索系统chunk的内容、与前后文关联等。这个方法最聪明不过也最贵。
二、我的选择
在之前清洗数据的时候我有意识地让数据比较规整,按照二级标题##、三级标题###等等划分,并且在用视觉模型VLM生成文本替换图片的时候用图表分析开始和结束标签包裹了内容,所以在这里按照层级划分基本能够满足知识点完整性的要求,图标的起始结束标签也可以作为切块的一句,保证一张图的描述不会从中间断开。且对于后三种方法用在我们的项目中有点“大材小用”了,它们更适合处理高价值信息,且时间成本、价格成本都比较高。综合以上情况,第二种方法看起来比较合适。
之后,考虑到无论按照哪个层级划分,都无法适用所有文档,比如按照三级标题划分,有的文档是三级标题下的内容刚好是一个完整知识点,长度也合适;有的文档三级标题下包含的内容却非常长,三级标题下的四级标题中的内容的长度才比较合适作为一个chunk且能够保证知识点完整性;也有出现文档一个标题下有几张图表,造成文本特别长的情况。根据上述情况,我采用了按照层级结构化切片+递归切片的双层逻辑。
三、具体实现
1.采用LangChain中的两种工具:
MarkdownHeaderTextSplitter:按 Markdown 标题分块; RecursiveCharacterTextSplitter:块太大时自动切小。
2.保护图表:
虽然清洗后数据中文本大致上已经按照层级划分了,但“图片-文本替换”时会引入新的层级符号,比如下面这种情况:

如果“结构化切块”时直接按照###符号划分很可能把图表说明部分从中间切开造成语义不完整了,所以这里我将图表解析中的###符号替换成不常见的短语:

之后再把短语替换回去就可以了。
3.第一层结构化切块:
使用为Markdown 格式文档设计的层级化文本分割器MarkdownHeaderTextSplitter,它可以识别 Markdown 的标题(## / ### 等)并按照标题的层级结构切分文档,这保证了每个切块都是完整的语义单元,不割裂知识点。此外它还可以自动给切块绑定标题元数据方便后续检索。

这步完成后我们把"VLM_HASH"还原成#。
4.第二层递归切块:
分割优先级是“四级标题 → 五级标题 → 图表 → 换行符 → 句号”,chunk_size设置成600token,太小会上下文碎片化,回答不完整;太大会混入无关内容,大模型答非所问。举个例子:
比如第一层切块后得到了一个chunk:

它的token是850>600,那么就会触发第二层递归切块,首先按照四级标题####切块,得到以下结果:
chunk1:

chunk2:

上面两个chunk的token都小于600,就完成了切块;如果chunk1仍然token>600,再按照####切块,还不行->按照图表切块......依次递归进行下去。
顺带一提,我设置chunk_overlap=0,也就是块和块之间字符重叠为0,这主要是因为数据清洗后结构比较规整,加上切块逻辑的完善,一个chunk基本可以表示一个完整知识点,强行设置块与块之间的字符重叠反而会给chunk引入不相关知识点的知识碎片。
5.元信息标注:
给每个块打上元信息标签,比如学科分类、章节等,方便之后入向量库。
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐



所有评论(0)