The 10-line version#
from docling.chunking import HybridChunker
from docling.document_converter import DocumentConverter
doc = DocumentConverter().convert("https://arxiv.org/pdf/2408.09869").document
chunker = HybridChunker(max_tokens=256) # match your embedding model
for chunk in chunker.chunk(doc):
text = chunker.contextualize(chunk) # headings + text: embed this
pages = {p.page_no for item in chunk.meta.doc_items for p in item.prov}
print(len(text), chunk.meta.headings, sorted(pages))Each chunk carries meta.headings (its section path) and meta.doc_items (the source items with page numbers and bounding boxes) — enough to show citations that link back to the exact spot on the page.
See real chunks#
We converted the 9-page Docling technical report and chunked it three ways with docling-core 2.106.0. Browse the actual output: colour marks the element type, the bar shows each chunk's size against the limit, and red means over the limit.
What we measured#
Chunked by docling.org with docling-core 2.106.0. The last bin holds everything ≥ 512.
Show data table
| Tokens | Hierarchical | Hybrid · 256 tokens | Hybrid · 512 tokens |
|---|---|---|---|
| 0–63 | 13 | 2 | 0 |
| 64–127 | 27 | 10 | 4 |
| 128–191 | 10 | 10 | 6 |
| 192–255 | 5 | 24 | 3 |
| 256–319 | 2 | 2 | 5 |
| 320–383 | 0 | 0 | 3 |
| 384–447 | 0 | 0 | 4 |
| 448–511 | 2 | 0 | 4 |
| 512+ | 2 | 0 | 1 |
| Chunker | Chunks | Average tokens | Largest | Over 256 tokens |
|---|---|---|---|---|
| HierarchicalChunker | 61 | 144 | 1,102 (the reference list) | 5 of 61 (2 over 512) |
HybridChunker, max_tokens=256 (default) | 48 | 182 | 256 | none |
HybridChunker, max_tokens=512 | 30 | 290 | 512 | 16 of 30 (by design: limit is 512) |
How the chunkers differ#
HierarchicalChunker
One chunk per document element (paragraph, list, table), with its heading path attached. No token awareness: a long table or list stays one chunk, however large. Good for inspecting structure, risky for embeddings.
HybridChunker
Starts from the hierarchical chunks, splits any chunk over max_tokens using your tokenizer, then merges undersized neighbours that share headings (merge_peers=True). Tables that must split repeat their header (repeat_table_header=True).
HybridChunker settings
| Parameter | Default | Meaning |
|---|---|---|
tokenizer | all-MiniLM-L6-v2 tokenizer | Counts tokens; use the tokenizer of your embedding model |
max_tokens | the tokenizer's own limit (256 here) | Hard ceiling per chunk, headings included |
merge_peers | True | Merge small sibling chunks with the same headings |
repeat_table_header | True | Repeat the header row when a table is split |
omit_header_on_overflow | False | Drop the heading context if it alone would overflow |
always_emit_headings | False | Emit heading-only chunks for empty sections |
Defaults read from the HybridChunker model fields in docling-core 2.106.0.
Use your embedding model's tokenizer
from docling.chunking import HybridChunker
from docling_core.transforms.chunker.tokenizer.huggingface import HuggingFaceTokenizer
from transformers import AutoTokenizer
EMBED_MODEL = "BAAI/bge-small-en-v1.5" # whatever you embed with
tokenizer = HuggingFaceTokenizer(
tokenizer=AutoTokenizer.from_pretrained(EMBED_MODEL),
max_tokens=512,
)
chunker = HybridChunker(tokenizer=tokenizer)For OpenAI embedding models, docling-core also provides an OpenAITokenizer wrapper around tiktoken (install docling-core[chunking-openai]).
Chunks from the command line#
docling convert report.pdf --to chunks --chunks-max-tokens 512 --output chunks/
docling convert report.pdf --to chunks --chunks-type hierarchical
docling convert report.pdf --to chunks --chunks-tokenizer BAAI/bge-small-en-v1.5Framework loaders#
LangChain
Since langchain-docling 3.0, the base package is a lightweight client for docling-serve; local conversion needs the local extra.
pip install "langchain-docling[local]"from langchain_docling import DoclingLoader
from langchain_docling.loader import ExportType
loader = DoclingLoader(
file_path=["https://arxiv.org/pdf/2408.09869"],
export_type=ExportType.DOC_CHUNKS, # default; or ExportType.MARKDOWN
)
docs = loader.load() # one LangChain Document per chunk
print(docs[0].metadata.keys())LlamaIndex
pip install llama-index-readers-docling llama-index-node-parser-doclingfrom llama_index.node_parser.docling import DoclingNodeParser
from llama_index.readers.docling import DoclingReader
reader = DoclingReader(export_type=DoclingReader.ExportType.JSON)
docs = reader.load_data(file_path="https://arxiv.org/pdf/2408.09869")
nodes = DoclingNodeParser().get_nodes_from_documents(documents=docs)Use the JSON export with DoclingNodeParser so nodes keep headings and page provenance in their metadata.
Haystack and others
docling-haystack provides a Haystack converter component. Docling's README also lists Crew AI among its integrations, and docling-mcp exposes conversion as tools for MCP-capable agents.
Practical advice#
- Store
doc_itemsprovenance with each vector so answers can cite page and position. - Embed contextualized text, display raw text. Headings help retrieval; users want the passage.
- Keep the DoclingDocument JSON. Re-chunking with a new tokenizer is instant from JSON, slow from PDF.
- Check tables. Tables are serialised row by row (“Caption, human human = 84-89. …” in our test). If your answers depend on numbers, test retrieval on table questions specifically.
Frequently asked questions
What is the best Docling chunker for RAG?
HybridChunker in most cases. It starts from the document's structure (like HierarchicalChunker), splits anything over the token limit and merges undersized neighbours with the same headings. On our test paper it kept every chunk within the limit, while HierarchicalChunker produced chunks of up to 1,102 tokens.
What max_tokens should I use with Docling's HybridChunker?
Use the maximum input length of your embedding model, and pass that model's tokenizer. By default HybridChunker uses the sentence-transformers/all-MiniLM-L6-v2 tokenizer and its 256-token limit. For larger models (512 or 8,192 tokens) raise max_tokens accordingly.
What does contextualize() do in Docling?
It returns the chunk text with its heading path prepended (for example '1 Introduction' before the paragraph). Embedding the contextualized text usually improves retrieval because the vector knows which section the text belongs to.
How are tables chunked?
Tables are serialised row by row and kept together where they fit. If a table exceeds the token limit, HybridChunker splits it and, by default, repeats the table header in each piece (repeat_table_header=True).
Can I get chunks from the Docling CLI?
Yes: docling convert file.pdf --to chunks --chunks-max-tokens 512. Use --chunks-type hierarchical for structure-only chunks and --chunks-tokenizer to set the tokenizer.
Sources
- docling-core 2.106.0 chunkers (docling_core/transforms/chunker)
- Official docs: chunking concept
- langchain-docling 3.0.0 on PyPI
- llama-index-readers-docling and llama-index-node-parser-docling on PyPI
- docling-haystack on PyPI