Haystack 文本预处理组件全解:DocumentCleaner、DocumentSplitter 与 CSV 处理器的实战指南

发布时间:2026/9/12 22:30:29
Haystack 文本预处理组件全解:DocumentCleaner、DocumentSplitter 与 CSV 处理器的实战指南 Haystack 文本预处理组件全解DocumentCleaner、DocumentSplitter 与 CSV 处理器的实战指南【免费下载链接】haystackOpen-source AI orchestration framework for building context-engineered, production-ready LLM applications. Design modular pipelines and agent workflows with explicit control over retrieval, routing, memory, and generation. Built for scalable agents, RAG, multimodal applications, semantic search, and conversational systems.项目地址: https://gitcode.com/GitHub_Trending/ha/haystackHaystack 的preprocessors模块位于 haystack/components/preprocessors负责在索引与检索阶段对Document和纯文本做清洗、切分与整理是构建 RAG、语义检索与 Agent 流水线的关键前置环节。本文以 v2.18 版本 API 文档docs-website/reference_versioned_docs/version-2.18/haystack-api/preprocessors_api.md为骨架结合源码逐组件讲解 8 个预处理组件的全部参数、运行流程与实战示例读完你将能够针对不同文档形态文本、CSV、多页 PDF 文本组合出正确的清洗与切分方案。一、组件总览预处理能解决什么问题预处理发生在文档进入 Document Store / Embedder 之前主要解决三类问题清洗噪声多余空白、空行、页眉页脚、指定子串与正则片段、Unicode 变体字符等会影响向量化质量与检索精度控制块大小过长文本会超出 LLM 上下文限制且语义向量质量下降需要按词、句、页、段落、token 等维度切块结构化表格处理CSV 文档需要去除空行空列、按空行/空列阈值或按行拆分出独立子表。preprocessors模块共包含 8 个面向不同场景的组件本文逐一展开组件输入核心用途DocumentCleanerlist[Document]清洗文档文本DocumentSplitterlist[Document]按词/句/页等切分长文档DocumentPreprocessorlist[Document]先切分再清洗的一体化 SuperComponentRecursiveDocumentSplitterlist[Document]按分隔符优先级递归切块HierarchicalDocumentSplitterlist[Document]构建多粒度分层块树CSVDocumentCleanerlist[Document]去除 CSV 空行空列CSVDocumentSplitterlist[Document]按空行/空列阈值或逐行拆分 CSVTextCleanerlist[str]清洗纯文本字符串所有组件都通过component装饰器注册为 Haystack 标准组件run()方法以字典形式返回结果可以直接接入Pipeline。二、DocumentCleaner文档文本的深度清洗2.1 功能与完整签名DocumentCleaner按顺序执行一系列清洗操作去除多余空白、空行、指定子串、正则匹配片段、跨页重复的页眉页脚见 document_cleaner.py。其完整构造签名如下def __init__(remove_empty_lines: bool True, remove_extra_whitespaces: bool True, remove_repeated_substrings: bool False, keep_id: bool False, remove_substrings: Optional[list[str]] None, remove_regex: Optional[str] None, unicode_normalization: Optional[Literal[NFC, NFKC, NFD, NFKD]] None, ascii_only: bool False)2.2 参数详解参数默认值说明remove_empty_linesTrue删除空行及仅含空白字符的行。源码实现会先按\f分页再过滤每页中line.strip()为空的行document_cleaner.pyremove_extra_whitespacesTrue用正则\s\s将连续空白压缩为单个空格并strip()两端document_cleaner.pyremove_repeated_substringsFalse移除每页重复出现的子串页眉页脚。要求页面以换页符\f分隔TextFileToDocument与AzureOCRDocumentConverter支持该分隔符remove_substringsNone要删除的子串列表按顺序做str.replace(substring, )document_cleaner.pyremove_regexNone正则模式所有匹配片段被替换为空串document_cleaner.pykeep_idFalse为True时保留原文档 ID否则输出文档生成新的空 IDunicode_normalizationNoneUnicode 规范化形式NFC/NFKC/NFD/NFKD在所有其他步骤之前执行源码在构造时会校验取值非法值抛ValueErrordocument_cleaner.pyascii_onlyFalse转为纯 ASCII先把文本 NFKD 规范化以分离变音符号再以ascii, ignore编码删除非 ASCII 字符document_cleaner.py。同样先于任何模式匹配/删除步骤执行2.3 运行流程与页眉页脚检测原理run(documents)要求输入为list[Document]否则抛TypeErrordocument_cleaner.pycontent为None的文档会打日志警告并原样保留。清洗顺序固定为Unicode 规范化 → ASCII 转换 → 多余空白 → 空行 → 子串 → 正则 → 重复子串页眉页脚→ strip。页眉页脚检测采用最长公共 n-gram 启发式document_cleaner.py忽略首尾各 1 页后取每页前 300 字符页眉候选与后 300 字符页脚候选求所有页面间的最长公共 n-gramn 取 330找到后从所有页面中删除。源码注释明确提示该启发式基于精确匹配适合 Copyright 2019 by XXX 这类固定页脚无法识别 Page 3 of 4 这类含页码的文本。2.4 官方示例from haystack import Document from haystack.components.preprocessors import DocumentCleaner doc Document(contentThis is a document to clean\n\n\nsubstring to remove) cleaner DocumentCleaner(remove_substrings [substring to remove]) result cleaner.run(documents[doc]) assert result[documents][0].content This is a document to clean 三、DocumentSplitter长文档的单元化切块3.1 完整签名def __init__(split_by: Literal[function, page, passage, period, word, line, sentence] word, split_length: int 200, split_overlap: int 0, split_threshold: int 0, splitting_function: Optional[Callable[[str], list[str]]] None, respect_sentence_boundary: bool False, language: Language en, use_split_rules: bool True, extend_abbreviations: bool True, *, skip_empty_documents: bool True)3.2 切分单元split_by取值分隔方式word按空格 切分默认period按句点.切分page按换页符\f切分passage按双换行\n\n切分line按换行\n切分sentence使用 NLTK 句子分词器切分function使用splitting_function自定义函数切分源码中通过映射表_CHARACTER_SPLIT_BY_MAPPING把字符型单元映射到具体分隔符document_splitter.py切分后除最后一块外都会把分隔符原样拼接回块尾保证内容不丢失document_splitter.py。3.3 其余参数split_length默认 200每个块的最大单元数必须大于 0否则构造时抛ValueErrordocument_splitter.py。split_overlap默认 0相邻块之间的重叠单元数。不能为负且必须小于split_lengthdocument_splitter.py。开启后每个文档的 meta 会写入_split_overlap字段记录与相邻块的doc_id和重叠字符区间document_splitter.py。split_threshold默认 0每块最小单元数。块单元数低于阈值时并入前一块避免产生过小的碎片document_splitter.py。splitting_functionsplit_byfunction时必填接受单个str返回list[str]缺失会抛ValueError。源码会通过content.find(split, ...)定位每个切块在原文中的起始位置并据此计算split_idx_start与页号document_splitter.py。respect_sentence_boundary默认False按word切分时尽量不截断句子内部用 NLTK 检测句界该选项仅对split_byword有效其他切分单元会打警告并强制置回Falsedocument_splitter.py。language默认ensplit_bysentence或respect_sentence_boundaryTrue时 NLTK 分词器使用的语言。use_split_rules默认True句子切分是否启用额外启发式规则。extend_abbreviations默认True是否用内置缩写表扩展 NLTK PunktTokenizer 的缩写识别目前支持英语en与德语de。skip_empty_documents默认True跳过空内容文档。设为False时空文档也会被保留便于下游组件如LLMDocumentContentExtractor从非纯文本文档中提取文本。3.4 输出元数据每个切分块是新的Document其 meta 包含source_id原文档 ID、page_number原页码、split_id块序号、split_idx_start在原文中的起始字符偏移其余 meta 从原文档深拷贝document_splitter.py。3.5 官方示例from haystack import Document from haystack.components.preprocessors import DocumentSplitter doc Document(contentMoonlight shimmered softly, wolves howled nearby, night enveloped everything.) splitter DocumentSplitter(split_byword, split_length3, split_overlap0) result splitter.run(documents[doc])run()对非list[Document]输入抛TypeError对contentNone的文档抛ValueErrordocument_splitter.py。3.6 DocumentStore 兼容性官方文档明确DocumentSplitter可配合以下 Document Store 使用Astra、Elasticsearch、OpenSearch、Pgvector、Qdrant、WeaviateChroma 与 Pinecone 为有限支持重叠信息不存储。相关文档可参考仓库内 docs-website/docs/document-stores 目录下的对应 Store 页面。四、DocumentPreprocessor先切分再清洗的一体化 SuperComponent4.1 设计思路DocumentPreprocessor是一个super_component内部用一个两节点 Pipeline 串联DocumentSplitter→DocumentCleanerdocument_preprocessor.py通过input_mapping/output_mapping把 Pipeline 输入输出暴露为统一的documents接口document_preprocessor.py。这样在 YAML 或 Pipeline 中只需注册一个组件即可完成切块 清洗两件事。4.2 完整签名def __init__(*, split_by: Literal[function, page, passage, period, word, line, sentence] word, split_length: int 250, split_overlap: int 0, split_threshold: int 0, splitting_function: Optional[Callable[[str], list[str]]] None, respect_sentence_boundary: bool False, language: Language en, use_split_rules: bool True, extend_abbreviations: bool True, remove_empty_lines: bool True, remove_extra_whitespaces: bool True, remove_repeated_substrings: bool False, keep_id: bool False, remove_substrings: Optional[list[str]] None, remove_regex: Optional[str] None, unicode_normalization: Optional[Literal[NFC, NFKC, NFD, NFKD]] None, ascii_only: bool False) - None注意两点差异一是默认split_length为250比单独使用DocumentSplitter的 200 更大二是分两段参数块——Splitter 参数split_by至extend_abbreviations含义与第三节一致与Cleaner 参数remove_empty_lines至ascii_only含义与第二节一致中间用注释块分隔以便阅读。4.3 序列化支持作为 SuperComponent它实现了to_dict()与from_dict()序列化时会把自定义的splitting_function通过serialize_callable转换反序列化时再通过deserialize_callable还原document_preprocessor.py因此可以在 YAML 配置中完整往返保存。4.4 官方示例from haystack import Document from haystack.components.preprocessors import DocumentPreprocessor doc Document(contentI love pizza!) preprocessor DocumentPreprocessor() result preprocessor.run(documents[doc]) print(result[documents])五、RecursiveDocumentSplitter按分隔符优先级递归切块5.1 工作原理与固定单元切分不同RecursiveDocumentSplitter维护一个按优先级排列的分隔符列表先用最粗的分隔符切对仍然超长的块换用更细的分隔符继续切直到所有块都不超过split_lengthrecursive_splitter.py。这正是 LangChain 式递归切分思想在 Haystack 中的实现。5.2 完整签名def __init__(*, split_length: int 200, split_overlap: int 0, split_unit: Literal[word, char, token] word, separators: Optional[list[str]] None, sentence_splitter_params: Optional[dict[str, Any]] None)参数默认值说明split_length200每块最大长度单位由split_unit决定词/字符/tokensplit_overlap0相邻块重叠的单元数为负或 ≥split_length时抛ValueErrorrecursive_splitter.pysplit_unitwordword/char/tokentoken时使用 tiktoken 的o200k_base编码需pip install tiktokenseparators[\n\n, sentence, \n, ]分隔符列表。除sentence外均按正则处理内部用re.escape转义后作为捕获组切分并把分隔符拼回块尾sentence使用基于 NLTK 的SentenceSplitter见 sentence_tokenizer.py。默认列表中\n\n最粗、 最细sentence_splitter_paramsNone传给句子分词器的参数字典默认{keep_white_spaces: True}所有分隔符必须是字符串否则构造抛ValueError。若某一层所有分隔符都无法再细分会回退到按split_unit的固定长度切分recursive_splitter.py。5.3 warm_up 与运行要求warm_up()会按需加载 NLTK 句子分词器与 tiktoken 编码器recursive_splitter.py。run()在需要句子切分或 token 切分但未warm_up时会自动触发若无法满足依赖则抛RuntimeError。空内容文档会被跳过并记录警告。5.4 官方示例from haystack import Document from haystack.components.preprocessors import RecursiveDocumentSplitter chunker RecursiveDocumentSplitter(split_length260, split_overlap0, separators[\n\n, \n, ., ]) text (Artificial intelligence (AI) - Introduction AI, in its broadest sense, is intelligence exhibited by machines, particularly computer systems. AI technology is widely used throughout industry, government, and science. Some high-profile applications include advanced web search engines; recommendation systems; interacting via human speech; autonomous vehicles; generative and creative tools; and superhuman play and analysis in strategy games.) chunker.warm_up() doc Document(contenttext) doc_chunks chunker.run([doc]) print(doc_chunks[documents])输出每个块的 meta 包含source_id、parent_id、split_id、split_idx_start、page_number以及_split_overlap开启重叠时为记录列表否则为Nonerecursive_splitter.py。六、HierarchicalDocumentSplitter多粒度分块树6.1 核心思想HierarchicalDocumentSplitter将文档按多个block_sizes切分构建一棵分块树根节点是原始文档叶子节点是最小块中间块互为父子父块是更大的块子块是切分后更小的块适合分层检索先召回粗粒度块再精读细粒度块。6.2 完整签名def __init__(block_sizes: set[int], split_overlap: int 0, split_by: Literal[word, sentence, page, passage] word)参数约束构造时校验违反抛ValueError见 hierarchical_document_splitter.pyblock_sizes不能为空split_overlap不能为负且必须小于block_sizes中的最小值内部为每个块大小创建独立的DocumentSplitter块大小按降序排列逐层切分hierarchical_document_splitter.py。6.3 输出元数据除DocumentSplitter常规字段source_id、page_number、split_id、split_idx_start外分层块额外带有block_size该块的切分粒度根为 0、parent_id父块 ID、children_ids子块 ID 列表、level层级根为 0。注意内部实现使用__block_size、__parent_id等带双下划线前缀的临时键完成建树后映射为输出字段hierarchical_document_splitter.py。6.4 官方示例from haystack import Document from haystack.components.preprocessors import HierarchicalDocumentSplitter doc Document(contentThis is a simple test document) splitter HierarchicalDocumentSplitter(block_sizes{3, 2}, split_overlap0, split_byword) splitter.run([doc])输出 7 个文档1 个根block_size: 0、level: 0children_ids指向两个 3 词块2 个 3 词块level: 14 个 2 词块level: 2叶子children_ids: []。块的父子关系与split_idx_start均可在输出中直接追踪。七、CSVDocumentCleaner清洗表格化文档7.1 功能CSVDocumentCleaner读取Document.content中的 CSV 文本删除完全为空的整行与整列并支持保留表头行/前导列。依赖 pandas初始化时即检查依赖pip install pandas。7.2 完整签名与参数def __init__(*, ignore_rows: int 0, ignore_columns: int 0, remove_empty_rows: bool True, remove_empty_columns: bool True, keep_id: bool False) - None参数默认值说明ignore_rows0处理前从表格顶部忽略的行数被忽略的行在输出中保留原位不参与空行判定ignore_columns0处理前从表格左侧忽略的列数同样保留原位remove_empty_rowsTrue删除完全为空的整行remove_empty_columnsTrue删除完全为空的整列keep_idFalse是否在输出文档中保留原文档 ID7.3 处理流程源码级run()的处理步骤csv_document_cleaner.py用pd.read_csv(StringIO(content), headerNone, dtypeobject)解析 CSV解析失败时打日志并原样保留该文档若ignore_rows/ignore_columns超过表格实际行/列数警告并保留整份文档切片出被忽略的行/列对剩余部分按dropna(axis0/1, howall)删除全空行/列用pd.concat把被忽略的行限列与列限行按原位重新拼回csv_document_cleaner.py以to_csv(indexFalse, headerFalse, lineterminator\n)序列化回文本生成新Document。八、CSVDocumentSplitter把大表格拆成子表8.1 两种切分模式CSVDocumentSplitter支持两种模式split_mode参数源码中定义为Literal[threshold, row-wise]见 csv_document_splitter.pythreshold默认识别连续空行/空列达到阈值即作为分隔边界把大表切成多个子表row-wise把每一行单独拆成一个子表每个子表一个Document。8.2 完整签名与参数def __init__(row_split_threshold: Optional[int] 2, column_split_threshold: Optional[int] 2, read_csv_kwargs: Optional[dict[str, Any]] None, split_mode: SplitMode threshold) - None参数默认值说明row_split_threshold2触发按行切分所需的最小连续空行数为None时不启用行切分column_split_threshold2触发按列切分所需的最小连续空列数为None时不启用列切分read_csv_kwargsNone透传给pandas.read_csv的额外关键字参数split_modethresholdthreshold或row-wise构造约束csv_document_splitter.py阈值必须 ≥ 1row_split_threshold与column_split_threshold至少指定一个否则抛ValueError。读取 CSV 时的默认选项为headerNone不把首行当表头、skip_blank_linesFalse保留空行、dtypeobject阻止类型推断例如避免数字被转成浮点用户传入的read_csv_kwargs会覆盖这些默认值csv_document_splitter.py。8.3 切分流程源码级解析 CSV失败则原样保留文档row-wise模式逐行itertuples转成单行 DataFramecsv_document_splitter.pythreshold模式先找连续全空行/列的下标区间df.isnull().all(axis...)定位csv_document_splitter.py再按下标切出子表若同时指定行列阈值采用递归切分先按行切再对每块按列切之后反复检查是否又出现达到行阈值的连续空行并继续递归直到无法再分csv_document_splitter.py按子表在原文中的位置排序先行索引后列索引输出为Document。8.4 输出元数据每个子表文档的 meta 包含source_id原文档 ID、row_idx_start子表在原表中的起始行号、col_idx_start起始列号、split_id切分序号其余 meta 从原文档复制。无法处理的文档原样返回meta始终保留。九、TextCleaner评估前的纯文本清洗9.1 功能定位TextCleaner面向纯文本字符串列表而非Document官方文档明确其典型场景是评估evaluation前的数据清洗去除匹配正则的子串、转小写、去标点、去数字让待评估文本与参考答案更可比。9.2 完整签名与参数def __init__(remove_regexps: Optional[list[str]] None, convert_to_lowercase: bool False, remove_punctuation: bool False, remove_numbers: bool False)参数默认值说明remove_regexpsNone正则模式列表源码用|.join(...)合并并编译re.IGNORECASE匹配片段全部删除text_cleaner.pyconvert_to_lowercaseFalse全部转为小写remove_punctuationFalse删除string.punctuation中的标点字符remove_numbersFalse删除string.digits中的数字字符去标点与去数字在构造时合并进同一个str.translate翻译表运行效率较高text_cleaner.py。run(texts)按 正则删除 → 小写 → 翻译表删除 的顺序处理返回{texts: [...]}。9.3 官方示例from haystack.components.preprocessors import TextCleaner text_to_clean 1Moonlight shimmered softly, 300 Wolves howled nearby, Night enveloped everything. cleaner TextCleaner(convert_to_lowercaseTrue, remove_punctuationFalse, remove_numbersTrue) result cleaner.run(texts[text_to_clean])十、组合实战搭建一个完整的预处理 Pipeline把上述组件串进 HaystackPipeline即可得到一条标准的清洗 → 切块 → 向量化 → 写入索引链路from haystack import Document, Pipeline from haystack.components.converters import TextFileToDocument from haystack.components.preprocessors import DocumentCleaner, DocumentSplitter from haystack.components.embedders import SentenceTransformersDocumentEmbedder from haystack.components.writers import DocumentWriter from haystack.document_stores.in_memory import InMemoryDocumentStore document_store InMemoryDocumentStore() pipeline Pipeline() pipeline.add_component(converter, TextFileToDocument()) pipeline.add_component(cleaner, DocumentCleaner(remove_extra_whitespacesTrue, remove_empty_linesTrue, remove_substrings[[unused]])) pipeline.add_component(splitter, DocumentSplitter(split_byword, split_length150, split_overlap20, split_threshold10)) pipeline.add_component(embedder, SentenceTransformersDocumentEmbedder()) pipeline.add_component(writer, DocumentWriter(document_storedocument_store)) pipeline.connect(converter.documents, cleaner.documents) pipeline.connect(cleaner.documents, splitter.documents) pipeline.connect(splitter.documents, embedder.documents) pipeline.connect(embedder.documents, writer.documents) pipeline.run({converter: {sources: [path/to/your/file.txt]}})设计建议纯文本优先用DocumentSplitterDocumentCleaner或直接用DocumentPreprocessor一步到位段落结构明显的长文用RecursiveDocumentSplitter默认分隔符[\n\n, sentence, \n, ]已覆盖常见情况需要分层检索如先粗后细时用HierarchicalDocumentSplitter含多个表格的 CSV 文件先用CSVDocumentSplitter拆子表再对每个子表用CSVDocumentCleaner去空行空列评估实验前用TextCleaner统一大小写、去除标点与数字。十一、验证与深入阅读每个组件的实现都有对应测试用例验证其行为可作行为规范参考DocumentCleaner见 test/components/preprocessors/test_document_cleaner.pyDocumentSplitter见 test/components/preprocessors/test_document_splitter.pyDocumentPreprocessor见 test/components/preprocessors/test_document_preprocessor.pyRecursiveDocumentSplitter见 test/components/preprocessors/test_recursive_splitter.pyHierarchicalDocumentSplitter见 test/components/preprocessors/test_hierarchical_doc_splitter.pyCSVDocumentCleaner/CSVDocumentSplitter见 test/components/preprocessors/test_csv_document_cleaner.py 与 test/components/preprocessors/test_csv_document_splitter.pyTextCleaner见 test/components/preprocessors/test_text_cleaner.py句子级切分依赖的SentenceSplitter实现位于 haystack/components/preprocessors/sentence_tokenizer.py。若需在 YAML 中序列化这些组件各组件均实现了to_dict()/from_dict()其中DocumentSplitter对自定义切分函数使用serialize_callable/deserialize_callable处理document_splitter.py。值得注意的是当前仓库源码中的DocumentSplitter已在 v2.18 文档签名的split_by基础上扩展了token选项使用 tiktoken 的o200k_base编码并按 token 数切块以及tokenizer_encoding参数升级版本后可按需使用。【免费下载链接】haystackOpen-source AI orchestration framework for building context-engineered, production-ready LLM applications. Design modular pipelines and agent workflows with explicit control over retrieval, routing, memory, and generation. Built for scalable agents, RAG, multimodal applications, semantic search, and conversational systems.项目地址: https://gitcode.com/GitHub_Trending/ha/haystack创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考