在处理文本数据时,有时我们需要将长文本按照指定的最大长度进行分块处理,以便更好地进行处理和分析,当前知识库+LLM产品都需要为用户上传的文档进行知识工程处理(通常是对文档进行分段,然后进行向量化,最后在问答时向量搜索知识库以获得答案)。
为了实现这一目标,我们可以使用一个方便的工具类——文本分块器(TextChunker)。
本文重点介绍一种文本自动分段方法的操作流程,借鉴了fastGPT、dify等知识库产品,并使用java语言实现一个分块工具类来帮助开发者深入理解和快速上手。
为什么需要对文档分段,首先LLM对输入长度有着天然的限制,比如目前性能较好的gpt3.5-16k,对输入的长度支持高达到16k,我们对分段的自动设置为LLM的max_tokens的40%,这里我们预设为中文场景,token换算成汉字要打对折,而且我们还要预留LLM响应的token数量,所以取值40%(fastGPT源码显示其取值为max_tokens*0.45,因为我们希望生成尽可能多的问答对,所以需要预留更多输出token,故本例取值向下取到40%),生成问答对不是一种必然选项,将再后续文章中展开加以说明。
每两个分割的段落的收尾部分有一定重叠,本例为25%(max_tokens*0.4*0.25,fastGPT源码亦取值0.25),两段内容首尾有一定重叠可避免生硬分割导致的知识割裂现象。
工具类概述文本分块器的主要功能是将输入的文本按照指定的最大长度进行分块处理,并返回分块后的结果和每个分块的令牌数量。
以下是该工具类的主要功能点:分块处理:将输入的文本按照最大长度进行分块处理,确保每个分块的长度不超过最大长度。
重叠处理:为了避免将句子分开,当一个分块的长度超过最大长度减去重叠长度时,将前一个句子添加到前一块的末尾。
令牌计数:通过调用countpromptTokens方法,可以根据指定的类型计算每个分块中的令牌数量。
这个方法可以根据具体需求进行定制。
使用方法使用文本分块器非常简单。
下面直接提供Java版完整代码:import java.util.ArrayList;import java.util.List;public class TextChunker { public static class ChunkInfo { private final List<String> chunks; private final int tokens; public ChunkInfo(List<String> chunks, int tokens) { this.chunks = chunks; this.tokens = tokens; } public List<String> getChunks() { return chunks; } public int getTokens() { return tokens; } } public static ChunkInfo splitTextIntoChunks(String text, int maxLen) { int overlapLen = (int) (maxLen * 0.25); // 重叠长度 try { String[] splitTexts = text.split("(?<=[。
!?;.!?;])"); // 使用正则表达式分割文本成句子 List<String> chunks = new ArrayList<>(); String preChunk = ""; String chunk = ""; int tokens = 0; for (String sentence : splitTexts) { chunk += sentence; if (chunk.length() > maxLen - overlapLen) { preChunk += sentence; } if (chunk.length() >= maxLen) { chunks.add(chunk); tokens += countpromptTokens(chunk, "system"); // 计算令牌数量 chunk = preChunk; preChunk = ""; } } if (!chunk.isEmpty()) { chunks.add(chunk); tokens += countpromptTokens(chunk, "system"); // 计算令牌数量 } return new ChunkInfo(chunks, tokens); } catch (Exception e) { throw new RuntimeException(e); } } private static int countpromptTokens(String text, String type) { // 在这里实现你的令牌计数逻辑 // 此函数根据指定的类型计算块中的令牌数量 // 你应该替换此部分以实现实际的令牌计数逻辑 return 0; } public static void main(String[] args) { String inputText = "这是一个示例文本。
它包含多个句子。
每个句子都不应被拆分。
块的最大长度为30个字符或更少。
运行应输出测试结果。
"; int maxChunkLength = 30; // 该值为每个分块的最大长度 应使用模型最大token长度*0.4 ChunkInfo chunkInfo = splitTextIntoChunks(inputText, maxChunkLength); List<String> chunks = chunkInfo.getChunks(); int tokens = chunkInfo.getTokens(); System.out.println("总令牌数:" + tokens); for (int i = 0; i < chunks.size(); i++) { System.out.println("块 " + (i + 1) + ":"); System.out.println(chunks.get(i)); System.out.println(); } }}导入必要的类和方法:import java.util.ArrayList;import java.util.List;1. 创建一个TextChunker对象:TextChunker chunker = new TextChunker();2. 调用splitTextIntoChunks方法进行文本分块处理:String inputText = "这是一个示例文本。
它包含多个句子。
每个句子都不应被拆分。
块的最大长度为30个字符或更少。
运行应输出测试结果。
";int maxChunkLength = 30;TextChunker.ChunkInfo chunkInfo = chunker.splitTextIntoChunks(inputText, maxChunkLength);3. 获取分块结果和令牌数量:List<String> chunks = chunkInfo.getChunks();int tokens = chunkInfo.getTokens();4. 处理分块结果和令牌数量:你可以根据需要对每个分块进行进一步处理,比如进行文本分析、生成问答对等。
同时,总令牌数tokens也可以帮助你估计文本的复杂度和计算成本。
总结文本分块器(TextChunker)是一个实用的工具类,它可以帮助我们将长文本按照指定的最大长度进行分块处理。
通过使用该工具类,我们可以更好地管理和处理文本数据,提高文本处理任务的效率和准确性。
此为知识工程的第一步(属于知识清洗的一部分),可方便后续对知识进一步加工。
希望本文能够帮助你了解文本分块器的基本原理和使用方法。
如果你有任何疑问或者想了解更多相关的内容,请随时留言。
谢谢阅读!