Get Documents Ready for AI
Extract a document into clean, structured data that preserves reading order, headings, tables, and images, with positional bounding box data available for each object. The results are verifiable evidence objects ready to index, retrieve, cite and pass to your LLM or RAG pipeline.
- Source exactly where the answers come from in the document.
- Ensure transparency and verifiability in your AI workflows.
Keep context small without losing structure
Chunking breaks a long Markdown document into focused passages that can be embedded and retrieved independently. PyMuPDF keeps the extracted text tied to its source page and position, so retrieved answers can retain useful provenance.
Start with a budget
Choose a maximum token count that fits your embedding model and retrieval window. Smaller chunks are more precise; larger chunks carry more context.
Keep provenance
Store each chunk's text with its page and position metadata from GNN layout analysis, so answers can link back to the original document.
Tune, then reassemble
Test retrieval quality at your target size. Reassemble adjacent chunks into a larger context window when a question needs more surrounding detail.
Chunk the document in both small & large sets
Use smaller chunks for precise retrieval, then reassemble them when the LLM needs a broader section to answer confidently.
The following code sample demonstrates this "small-to-big" retrieval pattern.
import pymupdf
cd = pymupdf.to_chunks("input.pdf", max_tokens=200) # 10 chunks
big = cd.reassemble_chunks(max_tokens=1200) # 2 chunks: c0 pages 1-3, c1 pages 4-6
# --- your code: replace with your retriever and LLM ---
def search(query):
return "c3" # a small-chunk id is returned from your index
def llm(query, context, bboxes):
return f"answer grounded in {len(context)} characters"
# --- end of your code ---
query = "Can you tell me about the construction process here?"
# from cd, the 200 token index with many small chunks
small = cd.get(search(query))
# "context" becomes the first large chunk that contains at least one source element
also present in small.
context = next(
(b for b in big
if set(small.metadata.element_ids)
& set(b.metadata.element_ids)),
None
)
# then send the large contextual chunk to the LLM
answer = llm(query, context.text, context.metadata.bboxes)
print("Answer:", answer)
# compare token counts for both small & big
print(small.id, small.metadata.token_count,
small.metadata.page_start, small.metadata.page_end,
"->",
context.id, context.metadata.token_count,
context.metadata.page_start, context.metadata.page_end)
# -> c3 117 1 1 -> c0 1093 1 3
Preserve the layout, not just the text
See how PyMuPDF captures the spatial structure behind tables, columns, and positioned content.
One extraction step, usable everywhere
Generate a Markdown file your AI tooling can work with directly. Because the output remains human-readable, it is also easy to inspect, version, and debug.
import pymupdf
doc = pymupdf.open("annual-report.pdf")
markdown = doc.to_markdown()
with open("annual-report.md", "w", encoding="utf-8") as output:
output.write(markdown)