# 🚀 中文优化 Token 文本切分器:递归 + 权重分隔符 + 重叠块 在构建 **知识库检索(RAG)**、**长文档问答**、**摘要生成** 等 LLM 应用时,如何合理切分文本是一件至关重要的事情。 常见的 `按字数切分` 或 `固定长度切分` 方法,存在以下不足: * ❌ **语义割裂**:切分点落在句子中间,导致语义残缺; * ❌ **粒度失衡**:有的分块过长超出 Token 限制,有的又过短浪费; * ❌ **缺乏上下文**:块之间完全独立,问答时语义衔接不畅。 为此,我实现了一个 **针对中文优化的 Token 文本切分器** —— `ChineseTokenTextSplitter`。它具有以下特性: ✅ **递归分割**:从大粒度到小粒度逐步切分; ✅ **权重分隔符**:保证重要段落优先完整; ✅ **Token 控制**:块大小符合模型输入上限; ✅ **Overlap 重叠**:保持上下文连续; ✅ **支持自定义正则**:灵活适配 Markdown / 特殊文本格式。 --- ## ✨ 设计思路 ### 1. 多级分隔符 + 权重 * 每个分隔符带有 **权重值**,保证优先用高权重(例如段落、标题)切分。 * 如果块仍然过大,则使用低权重分隔符(例如逗号)继续递归切分。 ### 2. Token 粒度控制 * 使用 [SharpToken](https://github.com/dmitry-brazhenko/SharpToken) 计算 Token 数量。 * 每个分块不会超过设定的 `_chunkSize`(如 900 Tokens)。 ### 3. Overlap 机制 * 在相邻块之间引入 `_chunkOverlap` 个 Token 重叠(如 160 Tokens),保证跨块语义连续。 --- ## 🔍 流程图 下面是一张文本切分流程图,展示了切分器的工作原理: ```mermaid flowchart TD A[输入文本] --> B{是否匹配高权重分隔符?} B -- 否 --> C[返回原文本作为单块] B -- 是 --> D[按分隔符切分文本] D --> E{子块是否超出 Token 限制?} E -- 否 --> F[加入 GoodSplits] E -- 是 --> G{是否有剩余分隔符?} G -- 否 --> H[直接输出超长块] G -- 是 --> I[递归调用SplitTextRecursive] F --> J[合并 GoodSplits] I --> J H --> J J --> K[清理换行与格式] K --> L[应用 Overlap 重叠策略] L --> M[输出最终切分结果] ``` 该流程保证了 **从大到小分割 → 控制块大小 → 应用重叠** 的完整处理链路。 --- ## 🔍 核心代码 ```csharp public class ChineseTokenTextSplitter { private readonly List<(string Pattern, int Weight)> _separators; private readonly bool _keepSeparator; private readonly int _chunkSize; private readonly int _chunkOverlap; private readonly GptEncoding _encoding; public ChineseTokenTextSplitter( string modelName = "gpt-3.5-turbo-0613", List<(string Pattern, int Weight)> separators = null, bool keepSeparator = true, int chunkSize = 900, int chunkOverlap = 160) { _separators = separators ?? new List<(string, int)> { ("\n\n", 10), // 段落 ("^#+\\s.*", 9), // Markdown 标题 ("。|!|?", 8), // 中文句号 ("\\.\\s|\\!\\s|\\?\\s", 8), // 英文句号 (";|;\\s", 5), // 分号 (",|,\\s", 3) // 逗号 }; _keepSeparator = keepSeparator; _chunkSize = chunkSize; _chunkOverlap = chunkOverlap; _encoding = GptEncoding.GetEncodingForModel(modelName); } public List SplitText(string text) { return SplitTextRecursive(text, _separators.OrderByDescending(s => s.Weight).ToList()); } // 递归分割逻辑(省略部分...) } ``` --- ## 🛠️ 使用示例 ```csharp var splitter = new ChineseTokenTextSplitter( chunkSize: 500, chunkOverlap: 100, separators: new List<(string, int)> { ("\\[SECTION\\]", 10), // 特定标记 ("\n\n", 8), // 段落 ("。", 5) // 句号 } ); string text = @" # 引言 这是一个测试文本。它包含多个句子,需要被合理分割。 [SECTION] 这是一个特殊标记分隔的部分,用于演示自定义规则。 "; var chunks = splitter.SplitText(text); foreach (var chunk in chunks) { Console.WriteLine($"[Chunk] {chunk}"); } ``` 输出示例: ``` [Chunk] # 引言 这是一个测试文本。它包含多个句子,需要被合理分割。 [Chunk] [SECTION] 这是一个特殊标记分隔的部分,用于演示自定义规则。 ``` --- ## 🔎 普通切分 vs 权重切分 假设有如下文本: ``` # 摘要 本文介绍了一种新的中文文本切分方法。 它支持递归切分与重叠块。 效果优于传统方法。 # 结论 该方法适用于 RAG 知识库和长文档问答。 ``` ### 普通按句号切分 ``` [Chunk] # 摘要 本文介绍了一种新的中文文本切分方法。 [Chunk] 它支持递归切分与重叠块。 [Chunk] 效果优于传统方法。 [Chunk] # 结论 该方法适用于 RAG 知识库和长文档问答。 ``` 👉 问题:标题和正文混在一起,割裂语义。 ### 权重分隔符切分 ``` [Chunk] # 摘要 本文介绍了一种新的中文文本切分方法。 它支持递归切分与重叠块。 效果优于传统方法。 [Chunk] # 结论 该方法适用于 RAG 知识库和长文档问答。 ``` 👉 优势:标题单独成块,语义更完整,检索与问答效果更佳。 --- ## 📌 应用场景 1. **RAG 知识库构建** * 将文档按 Token 粒度切分,存入向量数据库,提高召回率与相关性。 2. **长文档问答 / 摘要** * 结合 Overlap,避免语义断裂,提升答案准确度。 3. **混合文档处理** * Markdown、技术文档、OCR 原文,均可通过 **自定义正则** 适配。 --- ## ⚡ 总结 新版 `ChineseTokenTextSplitter` 具备以下优势: * 🔹 **递归切分**:保证块不过长; * 🔹 **权重分隔符**:保证重要段落不被拆散; * 🔹 **Overlap 重叠**:保持跨块语义; * 🔹 **可定制性强**:支持自定义正则 + 权重。 它不仅适合中文,还能扩展到 **多语言文档处理**,是构建 LLM 知识库与长文本处理的有力工具。 --- 👉 下一步优化方向: * **语义分割(Embedding 辅助)**:当分隔符不足时,智能选择切分点; * **动态块大小**:根据段落重要性调整 chunkSize; * **可视化调试工具**:让用户直观查看分块效果。 Loading... # 🚀 中文优化 Token 文本切分器:递归 + 权重分隔符 + 重叠块 在构建 **知识库检索(RAG)**、**长文档问答**、**摘要生成** 等 LLM 应用时,如何合理切分文本是一件至关重要的事情。 常见的 `按字数切分` 或 `固定长度切分` 方法,存在以下不足: * ❌ **语义割裂**:切分点落在句子中间,导致语义残缺; * ❌ **粒度失衡**:有的分块过长超出 Token 限制,有的又过短浪费; * ❌ **缺乏上下文**:块之间完全独立,问答时语义衔接不畅。 为此,我实现了一个 **针对中文优化的 Token 文本切分器** —— `ChineseTokenTextSplitter`。它具有以下特性: ✅ **递归分割**:从大粒度到小粒度逐步切分; ✅ **权重分隔符**:保证重要段落优先完整; ✅ **Token 控制**:块大小符合模型输入上限; ✅ **Overlap 重叠**:保持上下文连续; ✅ **支持自定义正则**:灵活适配 Markdown / 特殊文本格式。 --- ## ✨ 设计思路 ### 1. 多级分隔符 + 权重 * 每个分隔符带有 **权重值**,保证优先用高权重(例如段落、标题)切分。 * 如果块仍然过大,则使用低权重分隔符(例如逗号)继续递归切分。 ### 2. Token 粒度控制 * 使用 [SharpToken](https://github.com/dmitry-brazhenko/SharpToken) 计算 Token 数量。 * 每个分块不会超过设定的 `_chunkSize`(如 900 Tokens)。 ### 3. Overlap 机制 * 在相邻块之间引入 `_chunkOverlap` 个 Token 重叠(如 160 Tokens),保证跨块语义连续。 --- ## 🔍 流程图 下面是一张文本切分流程图,展示了切分器的工作原理: ```mermaid flowchart TD A[输入文本] --> B{是否匹配高权重分隔符?} B -- 否 --> C[返回原文本作为单块] B -- 是 --> D[按分隔符切分文本] D --> E{子块是否超出 Token 限制?} E -- 否 --> F[加入 GoodSplits] E -- 是 --> G{是否有剩余分隔符?} G -- 否 --> H[直接输出超长块] G -- 是 --> I[递归调用SplitTextRecursive] F --> J[合并 GoodSplits] I --> J H --> J J --> K[清理换行与格式] K --> L[应用 Overlap 重叠策略] L --> M[输出最终切分结果] ``` 该流程保证了 **从大到小分割 → 控制块大小 → 应用重叠** 的完整处理链路。 --- ## 🔍 核心代码 ```csharp public class ChineseTokenTextSplitter { private readonly List<(string Pattern, int Weight)> _separators; private readonly bool _keepSeparator; private readonly int _chunkSize; private readonly int _chunkOverlap; private readonly GptEncoding _encoding; public ChineseTokenTextSplitter( string modelName = "gpt-3.5-turbo-0613", List<(string Pattern, int Weight)> separators = null, bool keepSeparator = true, int chunkSize = 900, int chunkOverlap = 160) { _separators = separators ?? new List<(string, int)> { ("\n\n", 10), // 段落 ("^#+\\s.*", 9), // Markdown 标题 ("。|!|?", 8), // 中文句号 ("\\.\\s|\\!\\s|\\?\\s", 8), // 英文句号 (";|;\\s", 5), // 分号 (",|,\\s", 3) // 逗号 }; _keepSeparator = keepSeparator; _chunkSize = chunkSize; _chunkOverlap = chunkOverlap; _encoding = GptEncoding.GetEncodingForModel(modelName); } public List<string> SplitText(string text) { return SplitTextRecursive(text, _separators.OrderByDescending(s => s.Weight).ToList()); } // 递归分割逻辑(省略部分...) } ``` --- ## 🛠️ 使用示例 ```csharp var splitter = new ChineseTokenTextSplitter( chunkSize: 500, chunkOverlap: 100, separators: new List<(string, int)> { ("\\[SECTION\\]", 10), // 特定标记 ("\n\n", 8), // 段落 ("。", 5) // 句号 } ); string text = @" # 引言 这是一个测试文本。它包含多个句子,需要被合理分割。 [SECTION] 这是一个特殊标记分隔的部分,用于演示自定义规则。 "; var chunks = splitter.SplitText(text); foreach (var chunk in chunks) { Console.WriteLine($"[Chunk] {chunk}"); } ``` 输出示例: ``` [Chunk] # 引言 这是一个测试文本。它包含多个句子,需要被合理分割。 [Chunk] [SECTION] 这是一个特殊标记分隔的部分,用于演示自定义规则。 ``` --- ## 🔎 普通切分 vs 权重切分 假设有如下文本: ``` # 摘要 本文介绍了一种新的中文文本切分方法。 它支持递归切分与重叠块。 效果优于传统方法。 # 结论 该方法适用于 RAG 知识库和长文档问答。 ``` ### 普通按句号切分 ``` [Chunk] # 摘要 本文介绍了一种新的中文文本切分方法。 [Chunk] 它支持递归切分与重叠块。 [Chunk] 效果优于传统方法。 [Chunk] # 结论 该方法适用于 RAG 知识库和长文档问答。 ``` 👉 问题:标题和正文混在一起,割裂语义。 ### 权重分隔符切分 ``` [Chunk] # 摘要 本文介绍了一种新的中文文本切分方法。 它支持递归切分与重叠块。 效果优于传统方法。 [Chunk] # 结论 该方法适用于 RAG 知识库和长文档问答。 ``` 👉 优势:标题单独成块,语义更完整,检索与问答效果更佳。 --- ## 📌 应用场景 1. **RAG 知识库构建** * 将文档按 Token 粒度切分,存入向量数据库,提高召回率与相关性。 2. **长文档问答 / 摘要** * 结合 Overlap,避免语义断裂,提升答案准确度。 3. **混合文档处理** * Markdown、技术文档、OCR 原文,均可通过 **自定义正则** 适配。 --- ## ⚡ 总结 新版 `ChineseTokenTextSplitter` 具备以下优势: * 🔹 **递归切分**:保证块不过长; * 🔹 **权重分隔符**:保证重要段落不被拆散; * 🔹 **Overlap 重叠**:保持跨块语义; * 🔹 **可定制性强**:支持自定义正则 + 权重。 它不仅适合中文,还能扩展到 **多语言文档处理**,是构建 LLM 知识库与长文本处理的有力工具。 --- 👉 下一步优化方向: * **语义分割(Embedding 辅助)**:当分隔符不足时,智能选择切分点; * **动态块大小**:根据段落重要性调整 chunkSize; * **可视化调试工具**:让用户直观查看分块效果。 最后修改:2025 年 09 月 25 日 © 允许规范转载 打赏 赞赏作者 支付宝微信 赞 如果觉得我的文章对你有用,请随意赞赏