Semantica Split 模块实战指南:11 种文本分块策略与知识图谱保真切分
2026/9/15 14:03:21 网站建设 项目流程

Semantica Split 模块实战指南:11 种文本分块策略与知识图谱保真切分

【免费下载链接】semanticaGraph-Native Infrastructure for Context and Accountable AI Systems项目地址: https://gitcode.com/GitHub_Trending/sema/semantica

semantica.split是 Semantica 的文档切分模块,提供从通用递归切分、语义切分到实体感知、关系感知、结构感知等一整套分块策略,目标是让每个 chunk 在切分后依然保留完整语义上下文。本文将完整讲解模块的导出类、TextSplitter 统一入口、全部切分方法的参数与适用场景、Chunk 数据结构、配置管理方式,并结合仓库源码(semantica/split/目录)说明各方法的底层实现与自动降级机制,帮助你为 RAG 检索、知识图谱构建和 LLM 上下文准备选对切分策略。

为什么切分(Chunking)如此关键

大多数 LLM 与嵌入模型都有固定的上下文窗口,超过窗口长度的文档必须被切分。但朴素的切分方式(例如每 500 个字符一刀切,完全无视结构)会破坏语义上下文,带来三类典型故障:

  • 实体被切断:像 "Apple Inc." 这样的实体名被切到两个 chunk 里,两边都会丢失完整上下文;
  • 关系三元组悬空:关系三元组 "Steve Jobs founded Apple" 若在 "Steve Jobs" 处被切断,主体就被悬空,后续知识图谱构建会得到残缺的关系;
  • 混合主题产生质心向量:一个 chunk 同时混杂两个无关主题,嵌入后得到的质心向量与任何一个主题都不相似,检索时两边都匹配不上。

Semantica 的切分方法正是围绕避免这些故障模式设计的:实体感知切分保证实体跨度不越界,关系感知切分保证三元组落在同一 chunk 内,语义切分则只在主题真正变化时才切分。

模块导出的核心类

semantica.split通过init.py 对外导出统一分块器、各专用 chunker 以及若干底层切分函数:

Class作用
TextSplitter统一入口:通过method=切换策略,无需改动下游代码
Chunk输出数据结构{text, start_index, end_index, metadata, id}
SemanticChunker基于嵌入的主题漂移检测:只在内容真正变化时切分
StructuralChunker基于标题/章节的结构化文本切分
EntityAwareChunker防止命名实体跨 chunk 边界被切断
RelationAwareChunker保证 subject–predicate–object 三元组完整落在单个 chunk 内
HierarchicalChunker多层级切分,产生父子级 chunk 关系
SlidingWindowChunker固定尺寸滑窗切分,支持可配置重叠与步长
GraphBasedChunker基于图结构(社区检测/中心性)的切分
OntologyAwareChunker基于本体概念的切分(底层复用实体感知逻辑)

此外__init__.py还导出了SplitterTextSplitter的向后兼容别名)、SplitConfig/split_config配置对象、ProvenanceTracker溯源跟踪器,以及split_recursivesplit_by_tokenssplit_entity_aware等一组可直接调用的底层切分函数。

TextSplitter 支持的 method 值

Method适用场景
recursive通用文本:按段落 → 句子 → 单词依次切分
sentence对话式文本、问答
paragraph长篇文本,段落完整性重要
tokenLLM 上下文窗口限制
semantic_transformer存在主题转移的长文档
entity_aware知识图谱抽取流水线
relation_aware三元组完整性重要的 KG 流水线
structural带标题/段落结构的文本
sliding_window为 bi-encoder 检索提供高密度重叠
hierarchical多粒度检索
word/character/embedding_semantic/llm/graph_based/ontology_aware/community_detection/centrality_based/subgraph/topic_based/nltk/huggingface见下文"方法速查表"与源码_SPLIT_METHODS注册表

从源码看,methods.py 中的_SPLIT_METHODS字典共注册了 22 个内置方法,覆盖标准切分、KG/本体/图分析切分与专用切分三类。

快速开始

第一步:选择切分方法

from semantica.split import TextSplitter splitter = TextSplitter( method="recursive", # 见 Splitting Methods 表格 chunk_size=1000, chunk_overlap=200, )

第二步:切分纯文本

chunks = splitter.split(text) for chunk in chunks: print(f" Start: {chunk.start_index}, End: {chunk.end_index}") print(f" Method: {chunk.metadata.get('method')}") print(f" Preview: {chunk.text[:80]}...")

第三步:切分文档对象

split_documents()接受任何带.text属性的对象,或直接传字符串,不要求特定的文档类:

class Doc: def __init__(self, text, metadata=None): self.text = text self.metadata = metadata or {} doc = Doc(text="Annual report content...", metadata={"source": "annual_report.pdf"}) splitter = TextSplitter(method="structural") chunks = splitter.split_documents([doc]) for chunk in chunks: print(f" {chunk.text[:80]}...")

第四步:批量切分文档列表

# split_documents() 返回一个跨所有输入的扁平 List[Chunk] all_chunks = splitter.split_documents(docs) for chunk in all_chunks: print(chunk.text[:80])

从 splitter.py 的实现看,split_documents采用鸭子类型从对象上依次尝试textpage_contentcontent属性取文本,content为字节时会按 UTF-8 解码,content缺失但对象带path时还会尝试直接从文件路径读取;切分后文档级metadata会与 chunk 级元数据合并(文档元数据在前,chunk 元数据在后覆盖)。

切分方法速查表

Method如何切分适用场景
recursive段落 → 句子 → 单词(级联回退)通用默认值
semantic_transformer对句子做嵌入,在余弦相似度下降处切分RAG:主题一致性重要
entity_aware调整边界,保证实体跨度不被切断NER 流水线
relation_aware保证 subject–predicate–object 三元组落在同一 chunkKG 构建
sentence句子边界检测(正则、NLTK、spaCy)短文档、问答
paragraph段落边界切分长篇文章、报告
token通过 tiktoken 或 transformers 统计 token 数,硬性截止LLM 上下文窗口准备
word按单词数切分并带重叠简单的 token 近似切分
character固定字符数切分并带重叠;最快,无需 NLP简单批处理任务
sliding_window固定尺寸窗口按步长推进;重叠可配置密集检索(ColBERT、DPR)
structural标题/段落结构检测带明确标题层级的文本
embedding_semantic嵌入相似度边界(semantic_transformer的别名)RAG 嵌入一致性
hierarchical多层级:章节 → 段落 → 句子多粒度检索

此外,methods.py 还注册了llm(用 LLM 提示词寻找最优切分点,失败时回退递归切分)、graph_based(社区检测 Louvain 或中心性分析)、ontology_awarecommunity_detectioncentrality_basedsubgraph(k 跳邻域)、topic_basednltkhuggingface等方法。

如何选择切分策略

在决定使用哪个方法前,可以参考这份决策树:

  • 正在构建知识图谱?relation_aware(保持三元组完整);纯 NER 场景再用entity_aware
  • RAG 系统且检索质量优先?semantic_transformer
  • 需要为 bi-encoder 检索(ColBERT、DPR)提供密集重叠?sliding_window
  • 为固定窗口 LLM 准备提示词?token
  • 带标题的结构化文本?structural
  • 需要段落级连贯性?paragraphsentence
  • 追求快速切分、无 NLP 开销?recursivecharacter

TextSplitter 构造函数参数

from semantica.split import TextSplitter splitter = TextSplitter( method="semantic_transformer", # 切分策略:见 Splitting Methods 表格 chunk_size=1000, # 目标尺寸(字符数) chunk_overlap=200, # 相邻 chunk 之间的字符重叠 similarity_threshold=0.7, # 余弦相似度阈值(仅 semantic_transformer) model="all-MiniLM-L6-v2", # sentence-transformers 模型名(仅 semantic_transformer) ner_method="ml", # NER 方法(仅 entity_aware) relation_method="ml", # 关系抽取方法(仅 relation_aware) )
参数类型默认值说明
methodstr \| list[str]"recursive"切分策略,或一个方法列表作为回退链
chunk_sizeint1000目标尺寸,单位是字符(不是 token:如果之前用 token 计尺寸,大约乘以 4 来近似相同边界)
chunk_overlapint200相邻 chunk 之间的字符重叠
similarity_thresholdfloat0.7semantic_transformer的余弦相似度阈值:越低切分越多
modelstr"all-MiniLM-L6-v2"semantic_transformer使用的 sentence-transformers 模型名
ner_methodstr"ml"entity_aware的 NER 方法:"pattern"|"regex"|"ml"|"huggingface"|"llm"
relation_methodstr"ml"relation_aware的关系抽取方法:"ml"|"llm"|"huggingface"
tokenizerstr"gpt-4"token方法的 tiktoken 模型名:无法识别的名称自动回退到cl100k_base

关于chunk_overlap过小的警告。没有重叠时,跨越 chunk 边界的事实会在两个 chunk 里都不可见。相对于chunk_size的 10%~20% 重叠是安全下限:例如chunk_size=1000时,chunk_overlap应设置为100200

从 splitter.py 的实现看,method参数既支持单个字符串也支持列表——传列表即构成回退链;chunk_sizechunk_overlap缺省时会从全局split_config读取;其余参数存入self.options,并在构造时调用_load_method_configs()将每个方法在配置中的专有参数合并进来(方法配置优先)。

各切分方法的详细行为

Recursive(默认)

先尝试段落分隔,再尝试句子边界,最后是单词边界:仅在 chunk 超过chunk_size时才逐级回退:

splitter = TextSplitter(method="recursive", chunk_size=1000, chunk_overlap=200) chunks = splitter.split(text)

关键行为:

  • 尽可能保留段落与句子结构;
  • 优雅降级:产出的 chunk 永远不会大于chunk_size
  • 重叠保证跨 chunk 边界的上下文连续;
  • 不确定用哪个方法时,它是很好的起点。

源码佐证:在 methods.py 中,split_recursive的默认分隔符优先级为["\n\n", "\n", ". ", " ", ""],并要求切分点至少达到目标尺寸的 50% 才会被采用,否则继续尝试下一级分隔符,最后兜底按字符边界切分。

Semantic(语义切分)

用 sentence-transformers 模型为每个句子生成嵌入,当相邻句子之间的余弦相似度低于similarity_threshold时切分。每个 chunk 覆盖一个连贯主题:

from semantica.split import TextSplitter splitter = TextSplitter( method="semantic_transformer", model="all-MiniLM-L6-v2", # 任何 sentence-transformers 模型名 similarity_threshold=0.7, # 0.6 = 更多切分,0.8 = 更少切分 chunk_size=800, chunk_overlap=0, # 不需要:chunk 本身已经连贯 ) chunks = splitter.split(text)

关键行为:

  • 依赖sentence-transformers包:默认使用all-MiniLM-L6-v2,可通过model=配置;
  • 产出变长 chunk:有的主题短、有的主题长;
  • 若未安装sentence-transformers,自动回退到句子切分;
  • 因需要计算嵌入,比recursive慢;重复切分同一批文本时应缓存嵌入。

提示:语义切分需要足够的句子。semantic_transformer需要多个句子才能检测出主题转移。对于约 300 词以下的短文档,它的行为退化为句子切分,此时建议改用recursive

源码佐证:split_semantic_transformer(methods.py)先通过正则把文本切成句子,再用SentenceTransformer(model)批量编码,逐对计算余弦相似度(_cosine_similarity使用 numpy),低于阈值或超过chunk_size即产生 chunk;任何异常或缺少依赖时都会记录 warning 并回退到split_by_sentences

Entity-Aware(实体感知)

内部运行 NER,然后调整 chunk 边界,保证任何实体提及都不会被切到两个 chunk:

from semantica.split import TextSplitter # ner_method 会被透传给内部的 NERExtractor。 # 追求最高准确率用 "llm",追求速度用 "ml"(默认)。 splitter = TextSplitter( method="entity_aware", chunk_size=512, chunk_overlap=50, ner_method="ml", # "pattern" | "regex" | "ml" | "huggingface" | "llm" ) chunks = splitter.split(text) for chunk in chunks: print(f" entities in chunk: {chunk.metadata.get('entity_count', 0)}") print(f" preview: {chunk.text[:80]}...")

关键行为:

  • NER 在内部运行:实体抽取在切分器内部自动完成;
  • 每个 chunk 的实体对象可通过chunk.metadata["entities"]获取;
  • chunk 尺寸会在chunk_size附近略有浮动:边界调整不超过一个句子;
  • 支持所有实体类型:PERSON、ORGANIZATION、LOCATION、DATE 及自定义类型。

源码佐证:split_entity_aware(methods.py)先实例化NERExtractor(method=ner_method)提取全部实体,收集所有start_char/end_char构成实体边界集合,逐句拼接时若某句包含实体边界且preserve_entities=True,即使超过尺寸也不在此处切断。ner_method="ml"时还支持通过kwargs传入 NER 抽取器的附加选项。

Relation-Aware(关系感知)

保证 subject–predicate–object 三元组落在同一个 chunk 内——这对知识图谱流水线至关重要:

from semantica.split import TextSplitter # relation_method 会被透传给内部的 RelationExtractor。 # 追求最高准确率用 "llm",追求速度用 "ml"(默认)。 splitter = TextSplitter( method="relation_aware", chunk_size=512, relation_method="ml", # "ml" | "llm" | "huggingface" ) chunks = splitter.split(text) for chunk in chunks: print(f" relations in chunk: {chunk.metadata.get('relation_count', 0)}") for rel in chunk.metadata.get("relationships", []): print(f" {rel}")

关键行为:

  • 关系抽取在内部运行:无需预计算实体或三元组;
  • 关系对象可通过chunk.metadata["relationships"]获取;
  • 隐含实体感知行为:三元组中的两个实体同样保持完整;
  • 最适合作为Parse → Split → Extract → Build KG流水线中的切分步骤。

源码佐证:split_relation_aware(methods.py)先运行 NER(ner_method可通过 kwargs 传入,默认"ml"),再实例化RelationExtractor(method=relation_method)抽取关系;每个关系取min(subject.start_char, object.start_char)max(subject.end_char, object.end_char)作为三元组边界区间,逐句扫描时只要句子与某个三元组区间相交且preserve_triplets=True,就继续累积当前 chunk 而不切断。输出的 metadata 同时包含relation_countrelationshipstriplets三个键。

Structural(结构感知)

基于标题与段落边界的结构分析切分文本,每个标题或段落组形成一个 chunk 边界:

from semantica.split import TextSplitter splitter = TextSplitter(method="structural") chunks = splitter.split(text) for chunk in chunks: print(f" {chunk.text[:80]}...") print(f" start: {chunk.start_index}, end: {chunk.end_index}")

关键行为:

  • 作用于纯文本:不要求结构化文档格式;
  • 尊重标题层级(以#开头的行或全大写标题)与段落分隔;
  • 使用max_chunk_size=参数(而非标准的chunk_size=)控制最大尺寸;
  • StructuralChunker不可用,自动回退到recursive

源码佐证:split_structural(methods.py)在StructuralChunker可用时实例化 structural_chunker.py 中的类。StructuralChunker._extract_structure逐行识别五类结构元素——heading(Markdown#前缀或全大写短行、冒号结尾短行)、list-/*/+/数字序号)、code_block```或缩进块)、paragraph与表格;_group_elementsmax_chunk_size分组,并在遇到 level ≤ 2 的标题且respect_headers=True时提前断块。每个 chunk 的 metadata 含element_countelement_typesstructure_preserved: True

Sliding Window(滑窗切分)

固定尺寸窗口按步长推进,重叠可配置,适合需要高密度重叠的 bi-encoder 检索场景:

from semantica.split import SlidingWindowChunker chunker = SlidingWindowChunker(chunk_size=1000, overlap=200) chunks = chunker.chunk(text, preserve_boundaries=True)

关键行为:

  • stride默认取chunk_size - overlap,也可显式指定;
  • preserve_boundaries=True时优先在句子边界(.!?、换行)处回退切断,其次在单词边界(空格)处回退,且至少保留目标尺寸的 50%;
  • 参数校验严格:chunk_size必须为正数、overlap必须非负且小于chunk_sizestride必须为正数,否则抛出ValidationError(见 sliding_window_chunker.py);
  • 每个 chunk 的 metadata 含chunk_indexhas_overlap,边界保留模式下还有boundary_preserved标记。

Hierarchical(层级切分)

多层级切分(章节 → 段落 → 句子),为多粒度检索提供父子级 chunk 关系:

from semantica.split import HierarchicalChunker chunker = HierarchicalChunker( levels=["section", "paragraph", "sentence"], chunk_sizes=[2000, 1000, 500], ) chunks = chunker.chunk(text)

源码佐证:split_hierarchical(methods.py)先用正则\n#{1,6}\s+检测 Markdown 章节,若存在多个章节则对每个章节递归调用自身并降级到下一层;否则回退到段落级(split_by_paragraphs)或句子级(split_by_sentences)。HierarchicalChunker.chunk还会为每个 chunk 的 metadata 写入hierarchical: Truelevels字段(见 kg_chunkers.py)。

Chunk 数据结构

Chunk dataclass

@dataclass class Chunk: text: str # 该 chunk 的文本内容 start_index: int # 在源文本中的起始字符偏移 end_index: int # 在源文本中的结束字符偏移 metadata: Dict[str, Any] # 方法特定字段:见下表 id: Optional[str] = None # 可选 chunk 标识符

该定义位于 semantic_chunker.py,metadata默认值为空字典。

Chunk metadata 字段

Metadata 键因方法而异,下表只列出实现中确实会写入的键:

FieldTypeSet byDescription
methodstr所有方法生成该 chunk 的切分方法
chunk_sizeint大多数方法该 chunk 的字符长度
sentence_countintsentencesemantic_transformer、spaCy 路径该 chunk 中的句子数
paragraph_countintparagraph该 chunk 中的段落数
word_countintword该 chunk 中的单词数
token_countinttoken;当 spaCy 可用时的sentence/semantic_transformertoken 数:并非始终存在
entity_countintentity_aware边界落在这个 chunk 内的实体数
entitieslistentity_aware边界落在这个 chunk 内的实体对象
relation_countintrelation_aware该 chunk 中的关系三元组数
relationshipslistrelation_aware该 chunk 中的关系对象
tripletslistrelation_aware该 chunk 中的三元组对象(与relationships相同内容)
element_countintstructural分组到该 chunk 的结构元素数
element_typeslist[str]structural元素类型:"heading""paragraph""list"
structure_preservedboolstructural结构是否被完整保留
chunk_index/has_overlapint/boolsliding_window滑窗序号与是否携带重叠

Token 方法的 Tokenizer 选项

token方法接受tokenizer=关键字参数,该参数会传给tiktoken.encoding_for_model()。值应为 tiktoken 模型名,无法识别的名称会自动回退到cl100k_base

ValueEncoding used
"gpt-4"(默认)cl100k_base
"gpt-3.5-turbo"cl100k_base
"text-embedding-ada-002"cl100k_base
任何无法识别的字符串回退到cl100k_base

错误的 tokenizer 警告。token方法会把tokenizer=值传给tiktoken.encoding_for_model()。若 tiktoken 不识别该模型名,会静默回退到cl100k_base。要获得确定性的行为,请传入有效的 tiktoken 模型名(如"gpt-4""gpt-3.5-turbo")。

源码佐证:split_by_tokens(methods.py)优先使用 tiktoken,异常时回退cl100k_base;tiktoken 不可用则尝试 HuggingFaceAutoTokenizertransformers包);两者都不可用时按空白分词近似,并把chunk_size乘以 4 以接近真实边界——这也解释了文档中"token 尺寸约等于字符数除以 4"的经验法则。

配置管理:从环境变量到 YAML/JSON/TOML

semantica.split提供集中的配置管理,支持三种配置来源(见 config.py):

  • 环境变量SPLIT_CHUNK_SIZESPLIT_CHUNK_OVERLAPSPLIT_DEFAULT_METHODSPLIT_MAX_CHUNK_SIZESPLIT_MIN_CHUNK_SIZE等;
  • 配置文件:YAML、JSON、TOML 格式,读取顶层split段作为全局配置、split_methods段作为方法专有配置;
  • 编程式 APIsplit_config.set("chunk_size", 2000)split_config.get("chunk_size", default=1000)split_config.set_method_config("recursive", ...)split_config.get_method_config("recursive")

解析优先级遵循"配置文件 → 环境变量 → 默认值"的回退链,环境变量值会自动做类型转换(int等)。TextSplitter构造时会通过split_config.get("chunk_size", 1000)读取默认值,并把get_method_config(method)返回的方法专有配置合并进self.options,因此你可以在不改动调用代码的情况下,通过 YAML 配置或环境变量全局调整切分参数。

回退链与自定义方法注册

TextSplittermethod参数支持传入方法列表构成回退链(见 splitter.py):逐个尝试列表中的方法,某个方法未找到、返回空结果或抛异常时,记录 warning 并继续尝试下一个;全部失败才抛出ProcessingError,错误信息包含失败方法列表与最后一个异常。

同时,registry.py 提供了MethodRegistry注册表,支持运行时注册自定义切分方法:

from semantica.split.registry import method_registry def my_splitter(text, chunk_size=1000, **kwargs): # 自定义切分逻辑,返回 List[Chunk] ... method_registry.register("split", "my_custom_method", my_splitter) available = method_registry.list_all("split") # 查看已注册方法

get_split_method在解析方法名时优先查注册表,其次查内置_SPLIT_METHODS字典(methods.py),因此注册的自定义方法可以像内置方法一样通过TextSplitter(method="my_custom_method")使用;list_available_methods()会合并内置与注册方法并去重排序。

与解析、抽取、流水线的集成

TextSplitter既可以独立使用,也可以与其他 Semantica 模块手动组合。下面是顺序模式示例:解析文件 → 切分文本 → 从每个 chunk 抽取实体:

from semantica.parse import DocumentParser from semantica.split import TextSplitter from semantica.semantic_extract import NERExtractor # 解析 parser = DocumentParser() parsed = parser.parse("data/report.pdf") # 返回带 "full_text" 键的 dict # 切分 splitter = TextSplitter(method="semantic_transformer", chunk_size=512) chunks = splitter.split(parsed["full_text"]) # 逐 chunk 抽取 ner = NERExtractor(method="ml") for chunk in chunks: entities = ner.extract(chunk.text) print(f" {len(entities)} entities in chunk starting at {chunk.start_index}")

需要完整的流水线编排 API 时,可参考 Pipeline 参考文档,将切分作为命名流水线步骤集成。

模块在 Semantica 全家桶中的定位:

  • Parse — 切分前解析文档:产出章节与元数据;
  • Embeddings — 为向量检索与语义切分嵌入 chunk;
  • Semantic Extract — 从单个 chunk 中抽取实体与关系;
  • Pipeline — 将切分集成到命名流水线步骤。

更详细的模块使用说明(含TextSplitter、各 chunker 与配置示例)还可以参考仓库内的 split_usage.md 使用文档,以及 tests/split 目录下的切分测试用例。

小结

semantica.split的核心设计理念是"切分必须保真":recursive提供无 NLP 开销的通用默认值,semantic_transformer在主题真正变化时才切分,entity_awarerelation_aware分别保证实体与三元组不被拦腰切断,structural尊重标题层级,sliding_window为密集检索提供可控重叠,hierarchical支持多粒度检索。统一的TextSplitter入口配合方法回退链、注册表扩展、配置管理与逐级降级机制,让你可以在不改变下游代码的前提下,为 RAG 检索、知识图谱构建和 LLM 上下文准备灵活切换切分策略——这正是保证后续嵌入质量与实体抽取质量的第一道关口。

【免费下载链接】semanticaGraph-Native Infrastructure for Context and Accountable AI Systems项目地址: https://gitcode.com/GitHub_Trending/sema/semantica

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询