RAG的递归分块如何应用于处理?

摘要:递归分块的思想是: 先尝试用最大的分隔符切,切完如果某个块还是太大,就换一个更小的分隔符继续切,直到所有块都在 chunkSize 以内。 需注意,此方法最小分隔符需设置为"",也就是单字
递归分块的思想是: 先尝试用最大的分隔符切,切完如果某个块还是太大,就换一个更小的分隔符继续切,直到所有块都在 chunkSize 以内。 需注意,此方法最小分隔符需设置为"",也就是单字拆分或者单token拆分,保证能无限细分拆下去,作为最终兜底,直到都<chunkSize 此处使用Java示例表示此分块方式的思路 public class ChunkSpilt { /** * 分隔符列表,优先级从高到低排列 * \n\n是大段落,\n是小段落,。是句子,能按段落切就按段落切,段落太长了才按句子切,句子还太长才按逗号切,只有在万不得已的时候才会像固定大小分块那样按字符硬切 * 缺点:中英文标点不同,看文章是中文文章还是英文文章 */ private static final String[] separators = {"\n\n", "\n", "。", ",", ",", " ", ""}; /** * chunk最大大小(字符) */ private static final int chunkSize = 50; public static void main(String[] args) { String test = "C++11以前有很多原因不能提供一个通用的split,因为绝大部分人想要的类似于vector<string> split(const string& s, const string& delimiters = \" \") 这种函数是进不了C++标准库的。首先,它返回的是一个vector,而用户想要的可能是list、array,甚至是其它任何用户自定义的容器,这时候就需要自己再拷贝一次。总之就是split返回的结果是一个集合、但是不能限定为任何指定的容器,最好是lazy的,真正需要访问下一个的时候才去split下一个,符合这样的要求的split才能进入C++标准库。"; List<String> spilt = spilt(test); System.out.println(spilt.size()); for (String s : spilt) { System.out.println(s); } } /** * chunk分割 */ private static List<String> spilt(String text) { if (text.length() < chunkSize) return Collections.singletonList(text); return spilt(0, text, null); } /** * 递归逐层分割chunk * * @param step 当前层数 * @param text 原始文本 * @param lastSpiltStrings 当前层分割的结果 * @return 返回的结果 */ private static List<String> spilt(int step, String text, List<String> lastSpiltStrings) { if (step >= separators.length) { // 当前层无法再分割,直接返回 return lastSpiltStrings; } String separator = separators[step]; if (lastSpiltStrings == null) { lastSpiltStrings = List.of(text.split(separator)); } else { // 使用当前层分隔符分割 List<String> temp = new ArrayList<>(); for (String lastSpiltString : lastSpiltStrings) { temp.addAll(Arrays.asList(lastSpiltString.split(separator))); } lastSpiltStrings = temp; } // 校验每个string是否大于chunkSize,大于则继续拆分,没有了直接返回 return lastSpiltStrings.stream().noneMatch(each2 -> each2.length() > chunkSize) ? lastSpiltStrings : spilt(step + 1, text, lastSpiltStrings); } }