首页
/ Docling 文档分块(Chunking)完全指南:BaseChunker 抽象与 Hybrid / Hierarchical / Line-Based 分块器实战

Docling 文档分块(Chunking)完全指南:BaseChunker 抽象与 Hybrid / Hierarchical / Line-Based 分块器实战

2026-09-06 12:19:35作者:董灵辛Dennis

Docling 提供了一组"原生分块器"(native chunkers),让你可以直接在结构化文档模型 DoclingDocument 上完成面向 RAG 与检索增强生成(gen AI)的文本切分,而无需先导出 Markdown 再自行切分。本文围绕 Docling 的 chunking 概念页展开,系统讲解 BaseChunker 抽象接口、HybridChunkerHierarchicalChunkerLineBasedTokenChunker 三类分块器的原理与参数,并结合仓库中的 CLI 实现、服务端配置模型与官方示例 Notebook,给出可直接复制运行的实战代码。读完后,你将能够:为嵌入模型正确选择 tokenizer 与 max_tokens、控制表格表头在跨块场景下的重复行为,以及通过自定义序列化器扩展 chunk 的文本呈现形式。

两种分块思路:原生 chunker vs 导出后处理

从一份 DoclingDocument 出发,原则上有两种分块路线:

  1. 导出 Markdown(或类似格式)后做用户自定义分块:先把文档导出为 Markdown,再在下游用任意方式切分。仓库中 LangChain RAG 示例 中就展示了这种 Markdown 导出模式的做法;
  2. 使用 Docling 原生 chunker:直接操作 DoclingDocument 对象本身,利用其内部结构信息(标题层级、表格、图片、列表等)生成带元数据的 chunk。

本文聚焦第二种方式。chunker 是 Docling 的一个抽象层:给定一个 DoclingDocument,它返回一个 chunk 流(stream),每个 chunk 都以字符串形式捕获文档的某一部分,并附带相应的元数据(标题、说明文字、来源文档项引用等)。

为了兼顾下游应用的灵活性与开箱即用的便捷性,Docling 定义了一个 chunker 类层级:基类 BaseChunker 加若干具体子类。Docling 与 LlamaIndex 等 gen AI 框架的集成正是基于 BaseChunker 接口完成的,因此用户可以方便地接入任何内置、自定义或第三方的 BaseChunker 实现。

docling 包中,这些分块器通过 docling/chunking/init.py 统一再导出:

from docling_core.transforms.chunker.base import BaseChunk, BaseChunker, BaseMeta
from docling_core.transforms.chunker.hierarchical_chunker import (
    DocChunk,
    DocMeta,
    HierarchicalChunker,
)
from docling_core.transforms.chunker.hybrid_chunker import HybridChunker

可以看到,真正的实现位于 docling_core.transforms.chunker 命名空间,docling.chunking 只是包一层再导出的便捷入口。

BaseChunker:分块器的最小接口约定

BaseChunker 基类 API 规定,任何 chunker 都应提供两个方法:

  • def chunk(self, dl_doc: DoclingDocument, **kwargs) -> Iterator[BaseChunk]: 返回所提供文档的 chunk 流(惰性迭代,适合处理大文档);
  • def contextualize(self, chunk: BaseChunk) -> str: 返回 chunk 的"元数据增强后"序列化文本,通常用于喂给嵌入模型或生成模型——即在纯文本之前拼接上该 chunk 所属的标题、说明文字等上下文信息,让下游模型知道这段文本"来自文档的哪个部分"。

这两个方法构成了 Docling 与 gen AI 框架的集成契约:只要实现该接口,就可以被任意下游框架消费。

HybridChunker:token 感知的"结构 + 长度"混合分块

引入方式

  • 如果你使用的是完整的 docling 包:

    from docling.chunking import HybridChunker
    
  • 如果你只使用 docling-core 包,则需按需安装 extra 后从核心路径导入:

    # 使用 HuggingFace tokenizer 时安装 chunking extra
    pip install 'docling-core[chunking]'
    # 或者使用 OpenAI tokenizer(tiktoken)时
    pip install 'docling-core[chunking-openai]'
    
    from docling_core.transforms.chunker.hybrid_chunker import HybridChunker
    

工作原理:两遍 pass 的 token 感知精化

HybridChunker 采用混合策略:在基于文档结构的层级式(hierarchical)分块结果之上,应用 tokenization 感知的精化。具体来说:

  • 它以层级分块器的输出为起点,基于用户提供的 tokenizer(通常应与嵌入模型的 tokenizer 对齐)执行两遍处理:
    • 第一遍:只在必要时拆分(split)——即当某个 chunk 的 token 数超过上限时把它切小;
    • 第二遍:只在可能时合并(merge)——即把 token 偏小的相邻 chunk 合并起来,前提是它们具有相同的标题与说明文字(headings & captions)。用户可通过参数 merge_peers 关闭这一步(默认 True)。

这种"先保结构、再控长度"的设计,使得 chunk 既保留了文档语义边界(不会把两段不同主题的文字硬拼到一起),又能稳定控制在嵌入模型可接受的 token 范围内。

表头重复:Table Chunking with Repeated Headers

当用 HybridChunker 切分表格时,可以控制表头(table header)的处理方式:

  • repeat_table_header(默认 True):启用后,当一张表横跨多个 chunk 时,表头会在每个 chunk 的开头重复出现,保证每个 chunk 都保有表格结构上下文,检索回来的行不会因为缺少列名而无法被模型理解;
  • omit_header_on_overflow(默认 False):与 repeat_table_header=True 联合使用,为宽表提供灵活性——如果某一行在不带表头时能装进 token 上限、但带上表头会超限,则该行所在的 chunk 会省略表头。这能在保留行完整性的同时最大化 token 利用率,特别适合表头非常宽、或 token 预算非常紧张的场景。

Hybrid 分块示例 Notebook 中给出了完整的表头重复用法(以 CSV 宽表为例):

from docling_core.transforms.chunker.hierarchical_chunker import (
    ChunkingDocSerializer,
    ChunkingSerializerProvider,
)
from docling_core.transforms.serializer.markdown import (
    MarkdownParams,
    MarkdownTableSerializer,
)

# 自定义序列化器:让表格以 Markdown 形式呈现
class MDTableSerializerProvider(ChunkingSerializerProvider):
    def get_serializer(self, doc):
        return ChunkingDocSerializer(
            doc=doc,
            table_serializer=MarkdownTableSerializer(),
            params=MarkdownParams(compact_tables=True),
        )

small_tokenizer = HuggingFaceTokenizer(
    tokenizer=AutoTokenizer.from_pretrained(EMBED_MODEL_ID),
    max_tokens=200,
)

chunker_with_headers = HybridChunker(
    tokenizer=small_tokenizer,
    repeat_table_header=True,  # 每个 chunk 重复表头
    serializer_provider=MDTableSerializerProvider(),  # 使用 Markdown 表格格式
)

csv_chunks = list(chunker_with_headers.chunk(csv_doc))
for i, chunk in enumerate(csv_chunks[:3], 1):
    print(f"Chunk {i}: {chunk.text[:300]}...")
    print(f"Tokens: {small_tokenizer.count_tokens(chunk.text)}")

Line-Based Token Chunker:保行完整的 token 分块器

引入方式

  • 使用 docling 包时:

    from docling.chunking import LineBasedTokenChunker
    
  • 只使用 docling-core 时,先安装 chunking extra,再:

    from docling_core.transforms.chunker.line_chunker import LineBasedTokenChunker
    

定位与能力

LineBasedTokenChunker 是一个 tokenization 感知、保持行边界的分块器,特别适合表格、代码、日志、列表等结构化内容。它优先保证整行完整地落在单个 chunk 内,只有当某一行自身就超过最大 token 限制时才被迫拆行。

核心能力:

  • 优先让整行保留在同一个 chunk 内;
  • 支持为每个 chunk 附加一个重复前缀(例如表格表头),为每行提供结构上下文;
  • 通过 omit_prefix_on_overflow 参数处理溢出:设为 True 时,如果某行"带前缀"会超 token 上限但"不带前缀"能放下,则该行省略前缀,从而保持行完整。

Line-Based 分块示例 Notebook 演示了两种行为的对比,以及直接对 DoclingDocument 分块:

from docling_core.transforms.chunker.line_chunker import LineBasedTokenChunker
from docling_core.transforms.chunker.tokenizer.huggingface import HuggingFaceTokenizer
from transformers import AutoTokenizer

tokenizer = HuggingFaceTokenizer(
    tokenizer=AutoTokenizer.from_pretrained("sentence-transformers/all-MiniLM-L6-v2"),
    max_tokens=50,  # 演示用的小上限
)

# 带表头前缀的分块器
chunker = LineBasedTokenChunker(
    tokenizer=tokenizer,
    prefix="| Name | Age | Department |\n|------|-----|------------|\n",
    omit_prefix_on_overflow=False,  # 默认:总是包含前缀
)

lines = [
    "| Alice | 30 | Engineering |\n",
    "| Bob | 25 | Marketing |\n",
    "| Charlie | 35 | Sales |\n",
]

chunks = chunker.chunk_text(lines)  # 对行列表直接分块
for i, chunk in enumerate(chunks, 1):
    print(f"=== Chunk {i} ===")
    print(chunk)
    print(f"Tokens: {tokenizer.count_tokens(chunk)}\n")

同样的 chunker 也可以直接作用于转换后的文档:

from docling.document_converter import DocumentConverter

result = DocumentConverter().convert("docs/examples/data/2408.09869v3_enriched.json")
doc = result.document

chunker = LineBasedTokenChunker(
    tokenizer=tokenizer,
    prefix="",  # 普通文档不需要前缀
)
chunks = list(chunker.chunk(doc))

示例中还展示了边缘行为:当前缀本身超过 max_tokens 时,构造器会发出警告并把前缀自行切分成 prefix_chunksomit_prefix_on_overflow=TrueFalse 的对比输出则直观体现了"保行完整"与"保前缀上下文"之间的取舍。

HierarchicalChunker:基于文档结构分块

HierarchicalChunker 直接利用 DoclingDocument 的结构信息,为每一个检测到的文档元素(段落、表格、图片等)创建一个 chunk,默认会把列表项合并到一起(可通过参数 merge_list_items 关闭)。它会负责挂接所有相关的文档元数据,包括标题(headings)和说明文字(captions)。

它是整个 chunker 层级的结构基准——HybridChunker 的输入正是它的输出。由于它不做 token 长度控制,适合"一个元素一个 chunk"的粗粒度检索策略,或作为其他自定义分块逻辑的起点。

Tokenizer 的选择与 max_tokens 设置

HybridChunkerLineBasedTokenChunker 都要求传入一个 tokenizer,这是把"文档结构"与"模型上下文窗口"对齐的关键一环。仓库示例(hybrid_chunking.ipynb)展示了两种主流配置:

# HuggingFace tokenizer(默认路径)
from docling_core.transforms.chunker.tokenizer.huggingface import HuggingFaceTokenizer
from transformers import AutoTokenizer

EMBED_MODEL_ID = "sentence-transformers/all-MiniLM-L6-v2"
MAX_TOKENS = 64  # 示例中故意设小;实际应不超过嵌入模型上下文长度

tokenizer = HuggingFaceTokenizer(
    tokenizer=AutoTokenizer.from_pretrained(EMBED_MODEL_ID),
    max_tokens=MAX_TOKENS,  # 可选,HF 情况下默认从 tokenizer 上下文长度推导
)

# OpenAI tokenizer(tiktoken)
# import tiktoken
# from docling_core.transforms.chunker.tokenizer.openai import OpenAITokenizer
# tokenizer = OpenAITokenizer(
#     tokenizer=tiktoken.encoding_for_model("gpt-4o"),
#     max_tokens=128 * 1024,  # OpenAI 系模型需要按其上下文窗口显式给出
# )

实践要点:

  • max_tokens与你最终使用的嵌入(或生成)模型的 tokenizer 与上下文上限对齐——示例中默认使用的 sentence-transformers/all-MiniLM-L6-v2 即是一个常见默认值;
  • 若不给 max_tokens,HuggingFace 路径下会从 tokenizer 自身的上下文长度自动推导;
  • 分块后用 tokenizer.count_tokens(chunk.text) 可校验每个 chunk 是否确实落在预算内,这也是示例 Notebook 中每轮打印 token 数的原因。

CLI 中的原生分块:--to chunks

Docling 命令行直接把原生分块做成了导出格式。从 docling/cli/main.py 的源码可以看到:

chunker_type: ChunkerType = ChunkerType.HYBRID,
chunk_max_tokens: int | None = None,
chunk_tokenizer: str = "sentence-transformers/all-MiniLM-L6-v2",
if export_chunks:
    ...
    if chunker_type == ChunkerType.HIERARCHICAL:
        chunker_obj = HierarchicalChunker()
    else:  # 默认:hybrid
        hf_tok = HuggingFaceTokenizer.from_pretrained(
            model_name=chunk_tokenizer,
            max_tokens=chunk_max_tokens,
        )
        chunker_obj = HybridChunker(tokenizer=hf_tok)

对应的相关 CLI 选项(同文件约 L750-L760):

  • --to chunks:以 JSONL 形式导出 chunk(每个文档生成 .chunks.jsonl);
  • --chunks-type:选择 hierarchicalhybrid(默认 hybrid);
  • --chunks-max-tokens:每个 chunk 的最大 token 数,缺省使用 tokenizer 自身的上限;
  • --chunks-tokenizer:HuggingFace tokenizer 模型名,默认 sentence-transformers/all-MiniLM-L6-v2

从源码看(docling/cli/main.py),导出的每条 chunk 记录包含 raw_text(chunk 原文)、textcontextualize 后的元数据增强文本)、headingscaptionsdoc_items(组成该 chunk 的文档项 self_ref 列表)与 origin(来源页信息),并在 hybrid 模式下额外记录 num_tokens。这意味着 CLI 输出的 chunk 可直接用于构建索引,同时保留了回溯到原文档结构的能力。

服务端配置:Chunker 选项模型

如果你通过 Docling 服务(service_client)远程调用分块,配置由 docling/datamodel/service/chunking.py 中的 Pydantic 模型描述:

class BaseChunkerOptions(BaseModel):
    chunker: ChunkerType                      # "hierarchical" | "hybrid"
    use_markdown_tables: bool = False          # 表格用 Markdown 格式序列化
    use_markdown_images: bool = False         # chunk 中序列化图片引用,并增加 has_image 元数据
    image_placeholder: str = "![IMAGE]"      # 关闭 markdown 图片时使用的占位符
    include_raw_text: bool = False            # 响应中同时给出 raw_text 与 text

HybridChunkerOptions 在此之上增加三个 hybrid 专属参数:

class HybridChunkerOptions(BaseChunkerOptions):
    chunker: Literal[ChunkerType.HYBRID] = ChunkerType.HYBRID
    max_tokens: Optional[int] = None  # 为 None 时从 tokenizer 自动提取
    tokenizer: str = "sentence-transformers/all-MiniLM-L6-v2"  # HF 模型名
    merge_peers: bool = True         # 合并具有相同标题的偏小相邻 chunk

注意与 API 层的区别:Python 本地调用时 max_tokens 直接传给 HuggingFaceTokenizer 构造,而服务端选项中 tokenizer 是字符串模型名、由服务端实例化。service_client/client.py_async_client.pychunking_options 参数的默认值即 HybridChunkerOptions() / HierarchicalChunkerOptions()

进阶:自定义序列化与 Chunk 扩展

Advanced chunking & serialization 示例 展示了 HybridChunker 的两个高级扩展点,都是基于"序列化器提供器"(ChunkingSerializerProvider)机制:

  1. 更换表格序列化器:如上文 MDTableSerializerProvider,让表格 chunk 以 Markdown 呈现;
  2. 自定义图片占位与序列化:通过 MarkdownParams(image_placeholder="<!-- image -->") 修改图片占位符,或继承 MarkdownPictureSerializer 把图片的分类结果、SMILES、描述文本等元数据直接写进 chunk 文本;
  3. Chunk 扩展器(chunk expander)TreeChunkExpander 可把被 token 上限截断的表格 chunk 反向扩展到其完整的所属文档项(完整表格);PageChunkExpander 则扩展到整个所属页面。当嵌入/生成阶段需要"完整上下文"而检索阶段需要"小粒度"时,这种双向能力很有价值:
from docling_core.transforms.chunker.chunk_expander import (
    PageChunkExpander,
    TreeChunkExpander,
)

tree_expander = TreeChunkExpander()
expanded_chunk = tree_expander.expand(
    chunk=table_chunk, dl_doc=doc, serializer=serializer
)

此外,该示例还演示了通过 MarkdownParams(traverse_pictures=True) 控制是否遍历图片生成对应 chunk,以及用 chunker.chunk(doc) 直接处理经 OCR 转换的扫描 PDF 文档。

参考示例与延伸阅读

总结来说,Docling 的 chunking 体系以 BaseChunker 接口为契约,以 HierarchicalChunker 为结构基础,以 HybridChunker 为默认的 token 感知生产选择,并辅以 LineBasedTokenChunker 应对表格/代码/日志等行敏感内容;再通过 CLI 的 --to chunks、服务端 ChunkerOptions 与自定义序列化器/扩展器,覆盖了从本地脚本到远程服务、从默认行为到深度定制的完整链路。

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.14 K
2.75 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
857
1.35 K
docsdocs
暂无描述
Markdown
898
5.82 K
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
921
1.84 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.8 K
1.02 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
531
596
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
1.02 K
519
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.36 K
1.46 K
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
548
391