LlamaIndex Node Parser 完全指南:从文档到 Node 的分块原理与六大类解析器实战
【免费下载链接】llama_indexLlamaIndex is the document processing platform for AI项目地址: https://gitcode.com/GitHub_Trending/ll/llama_index
Node Parser(节点解析器)是 LlamaIndex 文档处理链路中的核心抽象:它接收一组Document,将每个文档切分成更小的Node(节点),使每个 Node 成为父文档的一个特定片段,供后续索引构建、向量检索与 LLM 生成使用。本文以官方文档 Node Parser Usage Pattern 及其 Modules 指南 为主体,结合llama-index-core源码深入讲解 Node Parser 的三种接入方式、底层接口设计与参数细节,并逐一剖析文件类、文本类、关系类共十余种解析器的使用场景与配置项。读完本文,你将掌握如何为 RAG 应用选择与调优合适的分块策略,并能在 IngestionPipeline 或VectorStoreIndex.from_documents()中正确落地。
Node Parser 是什么
Node Parser 是一个简洁的抽象:它接收一个文档列表,并将它们切分成Node对象,使得每个 Node 都是父文档的一个具体片段。当文档被切分为 Node 时,其所有属性(metadata、文本模板与元数据模板等)都会继承给子 Node。关于Node与Document的详细属性说明,可参阅 Documents and Nodes 指南。
从源码角度看,所有解析器都继承自 interface.py 中定义的NodeParser(本身是TransformComponent的子类),并通过统一的get_nodes_from_documents()入口对外服务。核心抽象方法只有_parse_nodes(),因此新增一种解析器只需实现"如何把一个节点拆成多个"这一件事:
NodeParser:所有节点解析器的基类,包含include_metadata、include_prev_next_rel、id_func等公共配置字段,并内置回调(CBEventType.NODE_PARSING)与前后处理逻辑;TextSplitter:文本分块基类,要求实现split_text(text) -> List[str],_parse_nodes会调用build_nodes_from_splits()将每个文本块包装为TextNode;MetadataAwareTextSplitter:元数据感知的分块器,要求实现split_text_metadata_aware(text, metadata_str),在切分时会先扣减元数据占用的 token 额度(取 EMBED 与 LLM 两种元数据模式中较长者),保证拼接元数据后不超 chunk 上限。
Node 构建的细节在 node_utils.py 的build_nodes_from_splits()中:每个子节点会继承文档的 embedding、excluded_embed_metadata_keys、metadata_template、text_template等属性,并建立指向父文档的SOURCE关系。默认的id_func使用uuid4()生成节点 ID(见 node_utils.py)。
三种接入方式
独立使用(Standalone Usage)
Node Parser 可以脱离索引单独使用,直接对Document列表调用:
from llama_index.core import Document from llama_index.core.node_parser import SentenceSplitter node_parser = SentenceSplitter(chunk_size=1024, chunk_overlap=20) nodes = node_parser.get_nodes_from_documents( [Document(text="long text")], show_progress=False )这里SentenceSplitter是默认也最常用的解析器。源码中其chunk_size默认值为DEFAULT_CHUNK_SIZE(定义于 constants.py),chunk_overlap默认值为 200(见 sentence.py),并校验"overlap 不得大于 chunk_size",否则抛出ValueError。
摄取管道中使用(Transformation Usage)
Node Parser 可作为任意变换序列的一员,放入摄取管道(Ingestion Pipeline),与嵌入、存储等步骤编排执行:
from llama_index.core import SimpleDirectoryReader from llama_index.core.ingestion import IngestionPipeline from llama_index.core.node_parser import TokenTextSplitter documents = SimpleDirectoryReader("./data").load_data() pipeline = IngestionPipeline(transformations=[TokenTextSplitter(), ...]) nodes = pipeline.run(documents=documents)TokenTextSplitter按原始 token 数进行一致大小的切分,其chunk_overlap默认值为DEFAULT_CHUNK_OVERLAP。从 token.py 的_split实现看,切分优先级依次为:主分隔符separator(默认空格)→ 备用分隔符backup_separators(默认["\n"])→ 逐字符切分;_merge阶段则不断向当前块追加 split,超限后回退(pop)前部元素形成 overlap。
索引构建中使用(Index Usage)
也可以在transformations参数或全局Settings中设置,使索引通过.from_documents()构建时自动应用分块:
from llama_index.core import SimpleDirectoryReader, VectorStoreIndex from llama_index.core.node_parser import SentenceSplitter documents = SimpleDirectoryReader("./data").load_data() # 全局设置:影响所有索引 from llama_index.core import Settings Settings.text_splitter = SentenceSplitter(chunk_size=1024, chunk_overlap=20) # 按索引设置:仅影响当前索引 index = VectorStoreIndex.from_documents( documents, transformations=[SentenceSplitter(chunk_size=1024, chunk_overlap=20)], )Settings.text_splitter是全局默认分块器;transformations则提供索引级别的覆盖。从NodeParser的接口设计看,二者最终都通过__call__调用get_nodes_from_documents()(见 interface.py),因此解析器可以无缝嵌入任何TransformComponent组合中。
文件类 Node Parser(File-Based Node Parsers)
文件类解析器根据待解析内容的类型(JSON、Markdown、HTML 等)创建节点。最简单的流程是:先用FlatReader(旧称FlatFileReader)读取文件,再用SimpleFileNodeParser自动为每种内容类型选用最佳解析器;随后可将文件解析器与文本解析器串联,以进一步按实际文本长度二次切分。
SimpleFileNodeParser
SimpleFileNodeParser依据文件扩展名自动分派解析器。源码中映射关系位于 simple_file.py:.md→MarkdownNodeParser,.html→HTMLNodeParser,.json→JSONNodeParser。使用时需要配合llama-index-readers-file包中的FlatReader:
from llama_index.core.node_parser import SimpleFileNodeParser from llama_index.readers.file import FlatReader from pathlib import Path md_docs = FlatReader().load_data(Path("./test.md")) parser = SimpleFileNodeParser() md_nodes = parser.get_nodes_from_documents(md_docs)HTMLNodeParser
HTMLNodeParser使用beautifulsoup解析原始 HTML。默认只解析一组选定的 HTML 标签,可通过tags参数覆盖。默认标签集合定义于 html.py:["p", "h1", "h2", "h3", "h4", "h5", "h6", "li", "b", "i", "u", "section"]。
from llama_index.core.node_parser import HTMLNodeParser parser = HTMLNodeParser(tags=["p", "h1"]) # 可选:自定义标签列表 nodes = parser.get_nodes_from_documents(html_docs)注意:HTMLNodeParser依赖bs4,若环境中未安装会在运行时抛出ImportError("bs4 is required to read HTML files.")。
JSONNodeParser
JSONNodeParser解析原始 JSON。其get_nodes_from_node()先对 JSON 字符串做合法性解析,再按结构拆分为节点,解析失败时对文本进行兜底处理(见 json.py)。
from llama_index.core.node_parser import JSONNodeParser parser = JSONNodeParser() nodes = parser.get_nodes_from_documents(json_docs)MarkdownNodeParser
MarkdownNodeParser解析原始 Markdown 文本,采用基于标题(Header)的切分逻辑:每个节点包含其文本内容以及通向它的标题路径,header_path_separator参数(默认"/")用于拼接该路径元数据(见 markdown.py)。
from llama_index.core.node_parser import MarkdownNodeParser parser = MarkdownNodeParser() nodes = parser.get_nodes_from_documents(markdown_docs)上述三个文件解析器的行为均由 tests/node_parser/test_file.py、test_markdown.py、test_html.py、test_json.py 等测试覆盖验证。
文本分块器(Text-Splitters)
CodeSplitter
CodeSplitter基于 tree-sitter 语法树(AST)按编程语言切分代码文本,避免在字符串或语法结构中拦腰截断。参数定义于 code.py,默认值包括:chunk_lines=40、chunk_lines_overlap=15、max_chars=1500、count_mode="char"、max_tokens=512。
from llama_index.core.node_parser import CodeSplitter splitter = CodeSplitter( language="python", chunk_lines=40, # 每个块的行数 chunk_lines_overlap=15, # 块与块之间的行重叠数 max_chars=1500, # 每个块的最大字符数 ) nodes = splitter.get_nodes_from_documents(documents)源码中count_mode支持"char"与"token"两种计数模式:字符模式按max_chars限制,token 模式按max_tokens限制,可配合自定义 tokenizer 精确控制块大小。支持的语言列表取决于 tree-sitter 语言包(py-tree-sitter-languages)。
LangchainNodeParser
如果你已经熟悉 LangChain 的文本分割器,可以直接将其包装为 Node Parser 使用:
from langchain.text_splitter import RecursiveCharacterTextSplitter from llama_index.core.node_parser import LangchainNodeParser parser = LangchainNodeParser(RecursiveCharacterTextSplitter()) nodes = parser.get_nodes_from_documents(documents)实现位于 langchain.py,它在_parse_nodes中调用 LangChain splitter 的split_text,再交由 LlamaIndex 的build_nodes_from_splits构建节点。
Chunker(Chonkie 包装器)
Chunker是一个多用途 Node Parser,包装了 chonkie 库的多种分块器。可以通过别名初始化(合法别名列表可通过Chunker.valid_chunker_types属性查看),也可以直接传入 chonkie 分块器实例:
from llama_index.node_parser.chonkie import Chunker parser = Chunker("recursive", chunk_size=2048) nodes = parser.get_nodes_from_documents(documents)from chonkie import RecursiveChunker from llama_index.node_parser.chonkie import Chunker chonkie_chunker = RecursiveChunker() parser = Chunker(chonkie_chunker) nodes = parser.get_nodes_from_documents(documents)该集成位于独立的llama-index-node-parser-chonkie包中(源码见 chunkers.py)。其实现基于 chonkie 的ComponentRegistry动态获取分块器类,因此新增的 chonkie 分块策略无需改动即可接入;传入不合法别名时会抛出ValueError并列出全部合法别名。它继承自MetadataAwareTextSplitter,同样具备元数据感知切分能力。
SentenceSplitter
SentenceSplitter是 LlamaIndex 最常用的解析器,切分时尽量尊重句子边界,避免在句子或段落中间硬切。从 sentence.py 的_split实现看,其切分顺序为:段落分隔符(默认\n\n\n)→ 句子 tokenizer(默认 nltk 句子切分器)→ 次级正则[^,.;。?!]+[,.;。?!]?|[,.;。?!]→ 空格 → 逐字符;_merge阶段优先合并完整句子,只有需要凑满 chunk 或处理超长句时才使用次级切分结果,从而尽可能避免块尾悬空句。
from llama_index.core.node_parser import SentenceSplitter splitter = SentenceSplitter( chunk_size=1024, chunk_overlap=20, ) nodes = splitter.get_nodes_from_documents(documents)关键参数说明(依据 sentence.py):
chunk_size:每个块的目标 token 数,默认DEFAULT_CHUNK_SIZE,必须大于 0;chunk_overlap:相邻块的 token 重叠量,默认 200,必须大于等于 0 且小于chunk_size;separator:单词级切分的默认分隔符,默认空格;paragraph_separator:段落分隔符,默认\n\n\n;secondary_chunking_regex:次级句子切分正则,默认[^,.;。?!]+[,.;。?!]?|[,.;。?!](已兼容中文句读);tokenizer:自定义 tokenizer,默认使用get_tokenizer()(优先 tiktoken)。
作为MetadataAwareTextSplitter,SentenceSplitter.split_text_metadata_aware()会用chunk_size - metadata_len计算有效块大小;若元数据长度超过chunk_size会抛出ValueError,接近时打印警告(见 sentence.py)。
SentenceWindowNodeParser
SentenceWindowNodeParser与普通解析器不同:它将文档拆成单个句子,并把每个句子周围的一圈"窗口"句子放入节点元数据。注意:这些窗口元数据不会暴露给 LLM 或嵌入模型。它最适合生成作用域非常精准的 embedding,再配合MetadataReplacementNodePostProcessor,在把节点交给 LLM 前用其上下文窗口替换原句子。
from llama_index.core.node_parser import SentenceWindowNodeParser node_parser = SentenceWindowNodeParser.from_defaults( # 每侧捕获的句子数量 window_size=3, # 保存周围句子窗口的元数据键 window_metadata_key="window", # 保存原始句子的元数据键 original_text_metadata_key="original_sentence", )从 sentence_window.py 的实现看,每个节点除了自身句子文本外,window_metadata_key(默认"window")记录window_size前后共2*window_size+1句的拼接文本,original_text_metadata_key(默认"original_text")记录原句子;同时这两个键会被加入excluded_embed_metadata_keys与excluded_llm_metadata_keys,从而保证它们不参与嵌入计算也不进入 LLM 上下文。与MetadataReplacementNodePostProcessor配合的完整示例见 node_postprocessor 示例。
SemanticSplitterNodeParser
"语义分块"(Semantic chunking)由 Greg Kamradt 在其嵌入分块五层级视频教程中提出。与固定块大小的切分不同,语义分块器使用 embedding 相似度自适应地在句子之间选取断点,确保每个"块"内包含的句子在语义上彼此相关。LlamaIndex 将其实现为SemanticSplitterNodeParser。
from llama_index.core.node_parser import SemanticSplitterNodeParser from llama_index.embeddings.openai import OpenAIEmbedding embed_model = OpenAIEmbedding() splitter = SemanticSplitterNodeParser( buffer_size=1, breakpoint_percentile_threshold=95, embed_model=embed_model )注意事项:
- 默认的句子切分正则主要适用于英文句子;
- 可能需要根据数据调节断点百分位阈值。
从源码看其算法流程(semantic_splitter.py):
_build_sentence_groups:将每句与前后各buffer_size句合并为"组合句";- 对全部组合句批量计算 embedding(
get_text_embedding_batch,异步版本走aget_text_embedding_batch); _calculate_distances_between_sentence_groups:计算相邻组合句的余弦相似度并转为距离1 - similarity;_build_node_chunks:对距离序列取breakpoint_percentile_threshold百分位作为断点阈值,距离超过阈值的相邻位置即产生新块。
参数含义:buffer_size表示评估语义相似度时归并的句子数(1 表示逐句独立评估,>1 表示分组评估);breakpoint_percentile_threshold是必须被超过才成块的余弦不相似度百分位,值越小生成的节点越多。若文档极短(无距离可算),整个文档会作为一个节点。完整示例见 semantic_chunking.ipynb,其行为由 test_semantic_splitter.py 验证。
TokenTextSplitter
TokenTextSplitter按原始 token 数切分,力求每个块大小一致:
from llama_index.core.node_parser import TokenTextSplitter splitter = TokenTextSplitter( chunk_size=1024, chunk_overlap=20, separator=" ", ) nodes = splitter.get_nodes_from_documents(documents)关键参数(依据 token.py):
chunk_size:每块目标 token 数,默认DEFAULT_CHUNK_SIZE;chunk_overlap:块间 token 重叠量,默认DEFAULT_CHUNK_OVERLAP;separator:主分隔符,默认空格;backup_separators:备用分隔符列表,默认["\n"];keep_whitespaces:是否保留块首尾空白,默认False(切分后自动strip())。
与SentenceSplitter相比,TokenTextSplitter是更朴素的"按 token 硬切"方案:切分优先级为分隔符 → 备用分隔符 → 逐字符,块尾容易出现悬空句。
关系类 Node Parser(Relation-Based Node Parsers)
HierarchicalNodeParser
HierarchicalNodeParser将节点切分为层次化节点:单个输入会被切成多级不同块大小,每个节点都保存指向其父节点的引用。与AutoMergingRetriever组合使用时,当某父节点的大多数子节点被检索到时,系统会自动用父节点替换子节点,从而为响应合成提供更完整的上下文。
from llama_index.core.node_parser import HierarchicalNodeParser node_parser = HierarchicalNodeParser.from_defaults( chunk_sizes=[2048, 512, 128] )从 hierarchical.py 的实现看,from_defaults会为每个 chunk_size 创建一个对应的SentenceSplitter(chunk_overlap默认 20),并按层级顺序编号为chunk_size_2048、chunk_size_512、chunk_size_128等;你也可以通过node_parser_ids+node_parser_map为每一层指定完全自定义的解析器(但二者不能同时指定)。切分过程在_recursively_get_nodes_from_nodes中逐层递归:每一层子节点通过_add_parent_child_relationship与父节点互建PARENT/CHILD关系,最终返回一个扁平的节点列表(见 hierarchical.py)。
模块还导出了四个辅助函数(见init.py):
get_leaf_nodes(nodes):返回没有子节点的叶子节点;get_root_nodes(nodes):返回没有父节点的根节点;get_child_nodes(nodes, all_nodes):返回给定节点的直接子节点;get_deeper_nodes(nodes, depth):返回距根节点指定深度的节点(深度为负时抛ValueError)。
与AutoMergingRetriever配合的完整示例见 auto_merging_retriever 示例,层次化行为由 test_hierarchical.py 验证。
解析器选型建议
综合官方文档与源码实现,可以按以下思路选型:
| 场景 | 推荐解析器 | 理由 |
|---|---|---|
| 通用长文(新闻、文章、报告) | SentenceSplitter | 尊重句子/段落边界,块尾悬空句最少 |
| 需要严格一致的 token 预算 | TokenTextSplitter | 按原始 token 数硬切,行为可预期 |
| 代码文件 | CodeSplitter | 基于语法树切分,不破坏语法结构 |
| 语义关联性强的文档 | SemanticSplitterNodeParser | 按 embedding 相似度自适应断点 |
| 先粗粒度嵌入、再细粒度送入 LLM | SentenceWindowNodeParser+MetadataReplacementNodePostProcessor | 嵌入用单句、生成用上下文窗口 |
| 检索后需要父级上下文补全 | HierarchicalNodeParser+AutoMergingRetriever | 命中多数子节点时自动合并为父节点 |
| Markdown / HTML / JSON 文件 | MarkdownNodeParser/HTMLNodeParser/JSONNodeParser,或SimpleFileNodeParser自动分派 | 按文件结构切分,保留标题路径等结构信息 |
| 已有 LangChain 分块器 | LangchainNodeParser | 复用既有逻辑 |
| 想使用 chonkie 分块策略 | Chunker | 一个入口接入全部 chonkie 分块器 |
总结
Node Parser 是 LlamaIndex 从文档到可检索 Node 的第一道工序,本文覆盖的解析器全部可以从 node_parser 包 顶层直接导入。在实际项目中,建议先通过独立使用方式在样本数据上对比各解析器的输出(注意SentenceSplitter与TokenTextSplitter的默认chunk_overlap不同),再通过Settings.text_splitter或transformations将选定的解析器固化到索引构建链路中;对于语义分块与句子窗口等高级策略,还需结合对应的 embedding 模型与后处理器整体调优。
【免费下载链接】llama_indexLlamaIndex is the document processing platform for AI项目地址: https://gitcode.com/GitHub_Trending/ll/llama_index
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考