text-splitter
- Rust Crate: text-splitter
- Python 绑定: semantic-text-splitter(很遗憾无法使用相同的包名)
大型语言模型(LLMs)可用于许多任务,但通常有限制的上下文大小,可能小于您想要使用的文档。要使用较长的文档,您通常需要将文本分割成小块以适应这个上下文大小。
这个 crate 提供了将较长文本分割成小块的方法,旨在最大化所需的块大小,同时尽可能在语义上合理的边界处进行分割。
开始使用
通过以下命令将其添加到您的项目中:
cargo add text-splitter
按字符数分割
使用这个 crate 最简单的方法是使用默认实现,它使用字符计数作为块大小。
use text_splitter::TextSplitter;
// 每个块的最大字符数
let max_characters = 1000;
// 默认实现使用字符计数作为块大小
let splitter = TextSplitter::new(max_characters);
let chunks = splitter.chunks("您的文档文本");
使用 Hugging Face 分词器
需要激活 tokenizers
功能并将 tokenizers
添加到依赖项中。下面的示例使用 from_pretrained()
,还需要启用 tokenizers 的 http
功能。
cargo add text-splitter --features tokenizers
cargo add tokenizers --features http
use text_splitter::{ChunkConfig, TextSplitter};
// 也可以使用其他实现了 text_splitter crate 中 ChunkSizer trait 的类型
use tokenizers::Tokenizer;
let tokenizer = Tokenizer::from_pretrained("bert-base-cased", None).unwrap();
let max_tokens = 1000;
let splitter = TextSplitter::new(ChunkConfig::new(max_tokens).with_sizer(tokenizer));
let chunks = splitter.chunks("您的文档文本");
使用 Tiktoken 分词器
需要激活 tiktoken-rs
功能并将 tiktoken-rs
添加到依赖项中。
cargo add text-splitter --features tiktoken-rs
cargo add tiktoken-rs
use text_splitter::{ChunkConfig, TextSplitter};
// 也可以使用其他实现了 text_splitter crate 中 ChunkSizer trait 的类型
use tiktoken_rs::cl100k_base;
let tokenizer = cl100k_base().unwrap();
let max_tokens = 1000;
let splitter = TextSplitter::new(ChunkConfig::new(max_tokens).with_sizer(tokenizer));
let chunks = splitter.chunks("您的文档文本");
使用范围作为块容量
您还可以选择将块容量指定为一个范围。
一旦块的长度达到该范围内的值,它就会被返回。
始终可能返回小于 start
值的块,因为添加下一段文本可能会使其大于 end
容量。
use text_splitter::{ChunkConfig, TextSplitter};
// 每个块的最大字符数。将填充块直到它在这个范围内。
let max_characters = 500..2000;
// 默认实现使用字符计数作为块大小
let splitter = TextSplitter::new(max_characters);
let chunks = splitter.chunks("您的文档文本");
Markdown
以上所有示例也适用于 Markdown 文本。如果启用 markdown
功能,您可以以与 TextSplitter
相同的方式使用 MarkdownSplitter
。
cargo add text-splitter --features markdown
use text_splitter::MarkdownSplitter;
// 每个块的最大字符数。也可以使用范围。
let max_characters = 1000;
// 默认实现使用字符计数作为块大小。
// 也可以使用与 `TextSplitter` 相同的所有分词器实现。
let splitter = MarkdownSplitter::new(max_characters);
let chunks = splitter.chunks("# 标题\n\n您的文档文本");
代码
以上所有示例也适用于可以用 tree-sitter 解析的代码。如果启用 code
功能,您可以以与 TextSplitter
相同的方式使用 CodeSplitter
。
cargo add text-splitter --features code
cargo add tree-sitter-<language>
use text_splitter::CodeSplitter;
// 每个块的最大字符数。也可以使用范围。
let max_characters = 1000;
// 默认实现使用字符计数作为块大小。
// 也可以使用与 `TextSplitter` 相同的所有分词器实现。
let splitter = CodeSplitter::new(tree_sitter_rust::language(), max_characters).expect("无效的 tree-sitter 语言");
let chunks = splitter.chunks("您的代码文件");
方法
为了尽可能保留块内的语义意义,每个块由能够适应下一个给定块的最大语义单位组成。对于每种分割器类型,都定义了一组语义级别。以下是使用的步骤示例:
- 按递增的语义级别分割文本。
- 检查每个级别的第一项,选择第一项仍然适合块大小的最高级别。
- 将这个级别或更高级别的相邻部分合并成一个块,以最大化块长度。合并时始终包括更高语义级别的边界,以确保块不会无意中跨越语义边界。
如果使用 chunks
方法,用于分割文本的边界按升序排列:
TextSplitter
语义级别
- 字符
- Unicode 字素簇边界
- Unicode 单词边界
- Unicode 句子边界
- 换行符的递增序列长度。(换行符为
\r\n
、\n
或\r
)每个唯一长度的连续换行符序列被视为其自身的语义层级。因此,2个换行符的序列比1个换行符的序列具有更高的层级,依此类推。
分割不会发生在字符级别以下,否则可能会得到字符的部分字节,这可能不是有效的 Unicode 字符串。
MarkdownSplitter
语义层级
Markdown 根据 CommonMark
规范进行解析,同时包含一些可选功能,如 GitHub 风格的 Markdown。
- 字符
- Unicode 字素簇边界
- Unicode 单词边界
- Unicode 句子边界
- 软换行(单个换行),在 Markdown 中不一定是新元素。
- 内联元素,如:文本节点、强调、加粗、删除线、链接、图片、表格单元格、内联代码、脚注引用、任务列表标记和内联 HTML。
- 块级元素,如:段落、代码块、脚注定义、元数据。还包括可以包含其他"块"类型元素的引用块或表格/列表中的行/项目,以及包含项目的列表或表格。
- 主题分隔符或水平线。
- 按级别划分的标题
分割不会发生在字符级别以下,否则可能会得到字符的部分字节,这可能不是有效的 Unicode 字符串。
CodeSplitter
语义层级
- 字符
- Unicode 字素簇边界
- Unicode 单词边界
- Unicode 句子边界
- 语法树的递增深度。因此,函数会比函数内的语句具有更高的层级,依此类推。
分割不会发生在字符级别以下,否则可能会得到字符的部分字节,这可能不是有效的 Unicode 字符串。
关于句子的说明
有许多确定句子断点的方法,准确度各不相同,许多方法需要使用机器学习模型。我们不尝试寻找完美的句子断点,而是依赖 Unicode 的句子边界方法。在大多数情况下,这种方法足以在段落过大时找到合适的语义断点,并避免了其他方法的性能损耗。
功能标志
文档格式支持
功能 | 描述 |
---|---|
code | 启用 CodeSplitter 结构,通过 tree-sitter 解析器 解析代码文档。 |
markdown | 启用 MarkdownSplitter 结构,通过 CommonMark 规范解析 Markdown 文档。 |
分词器支持
依赖功能 | 支持版本 | 描述 |
---|---|---|
rust_tokenizers | ^8.0.0 | 允许 (Text/Markdown)Splitter::new 接受任何提供的分词器作为参数。 |
tiktoken-rs | ^0.5.0 | 允许 (Text/Markdown)Splitter::new 接受 tiktoken_rs::CoreBPE 作为参数。这对于为 OpenAI 模型分割文本很有用。 |
tokenizers | ^0.20.0 | 允许 (Text/Markdown)Splitter::new 接受 tokenizers::Tokenizer 作为参数。这对于分割兼容 Hugging Face 分词器的模型文本很有用。 |
灵感来源
这个 crate 的灵感来自 LangChain 的 TextSplitter。但在研究其实现后,我们发现有潜力提高性能并实现更好的语义分块。
非常感谢 unicode-rs 团队的 unicode-segmentation crate,它处理了匹配 Unicode 单词和句子规则的大部分复杂性。