在对数据进行清洗+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.元信息标注:

给每个块打上元信息标签,比如学科分类、章节等,方便之后入向量库。

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐