Configure chunking
Set the size of the chunks that Agentic GraphRAG makes. See which settings made each chunk. Change the settings for documents you already added. See Ingest documents for the basics of Graph.add. Each code block on this page is complete. Blocks that add documents open their own graph and close it at the end.
See Chunking for how the chunker works.
See the default settings
A Graph holds one Chunker. Its settings are size, min_size, tokenizer, splitter and tables.
from agrag.chunking import Chunker
chunker = Chunker()
print(chunker.settings())
print(chunker.fingerprint)
| Setting | Default | Meaning |
|---|---|---|
size | 600 | The most tokens in a chunk. |
min_size | a quarter of size | A chunk with fewer tokens carries on into the next section. |
tokenizer | o200k_base | The tokenizer that counts tokens. |
splitter | chonkie-<installed version> | The version of the installed splitter package that cuts text. The value depends on your environment. |
tables | 1 | The version of the table rendering rules. |
size and min_size count the tokens of each unit plus the blank lines that join them. The chunker adds those counts, so a chunk can hold a few tokens more than size. min_size must be smaller than size, and min_size=0 turns merging off.
Set the size
Pass chunker to Graph.open. This example makes chunks of up to 400 tokens.
import asyncio
from agrag.chunking import Chunker
from agrag.common.data_models import DEFAULT_SCHEMA
from agrag.embedding import build_embedder
from agrag.graphdb import build_graph_store
from agrag.ingestion import Graph
from agrag.ingestion.extract import GlinerExtractor
async def main() -> None:
graph_store = build_graph_store("neo4j")
graph = await Graph.open(
schema=DEFAULT_SCHEMA,
graph_store=graph_store,
embedder=build_embedder("ibm-granite/granite-embedding-small-english-r2"),
extractor=GlinerExtractor(),
chunker=Chunker(size=400),
)
try:
print(graph.chunker.settings())
finally:
await graph_store.close()
asyncio.run(main())
A smaller size can improve search precision, but it can also separate related facts. A larger size needs fewer extraction calls. Check the embedding model and backend input limits before increasing the chunk size. The default Granite model supports up to 8,192 tokens; the effective limit also depends on the backend's tokenizer settings.
Check what the chunker made
Graph.add returns the counts in result.chunking. Pass return_chunks=True to get the chunks. Each chunk records the chunker name and the settings fingerprint.
import asyncio
from agrag.common.data_models import DEFAULT_SCHEMA
from agrag.embedding import build_embedder
from agrag.graphdb import build_graph_store
from agrag.ingestion import Graph
from agrag.ingestion.extract import GlinerExtractor
TEXT = "Ada wrote the first program.\n\nCharles built the engine.\n\n" * 3
async def main() -> None:
graph_store = build_graph_store("neo4j")
graph = await Graph.open(
schema=DEFAULT_SCHEMA,
graph_store=graph_store,
embedder=build_embedder("ibm-granite/granite-embedding-small-english-r2"),
extractor=GlinerExtractor(),
)
try:
result = await graph.add(text=TEXT, return_chunks=True)
print(result.chunking)
for chunk in result.chunks:
print(chunk.chunker, chunk.chunker_hash, chunk.heading_path)
finally:
await graph_store.close()
asyncio.run(main())
result.chunking counts the chunks, sections, tables and figures.
Chunk a document without a graph
Chunker.chunk works on a Document and needs no database. This is the quickest way to test a size.
import hashlib
from agrag.chunking import Chunker
from agrag.common.data_models.document import Document, DocumentFamily, SourceFormat
text = "First paragraph.\n\nSecond paragraph.\n\n" * 20
document = Document(
text=text,
title="sample",
uri="sample.txt",
source_format=SourceFormat.TXT,
family=DocumentFamily.PROSE,
content_hash=hashlib.sha256(text.encode("utf-8")).hexdigest(),
loader_name="text",
char_count=len(text),
)
for size in (50, 200):
chunks = Chunker(size=size).chunk(document).chunks
print(size, len(chunks))
Chunk again after you change the size
Graph.update compares the stored fingerprint with the current one. A new size chunks the document again, even when the text did not change. Chunks that the old settings made close, and the new chunks open.
Chat and record files
A chat file has one section for each message. Pass loader=ChatLoader() for a single chat file, because no extension selects the chat loader. Short neighbouring messages can share a chunk, and a long message is cut at sentence boundaries. A record row has no sections and is one chunk. A row that is over size is cut at sentence boundaries.
Tables
A table that fits in size tokens is one chunk. A larger table becomes row groups, and each group starts with the header row. A row that is over size on its own becomes its own group with the header row.