Convert to markdown with chapter headings preserved
LangChain expects documents, not raw bytes. The cleanest way to integrate Docling is to wrap it in a BaseLoader. The loader parses the PDF, exports to markdown, and yields a single LCDocument with metadata attached. Why markdown? Because heading syntax gives the splitter natural boundaries.
from typing import Iterator
from langchain_core.documents import Document as LCDocument
from langchain_core.document_loaders import BaseLoader
class DoclingBookLoader(BaseLoader):
def __init__(self, file_path: str) -> None:
self.file_path = file_path
# converter setup from the previous lesson
self.converter = build_converter()
def lazy_load(self) -> Iterator[LCDocument]:
docling_doc = self.converter.convert(self.file_path).document
text = docling_doc.export_to_markdown()
metadata = {
'source': self.file_path,
'format': 'book',
}
yield LCDocument(page_content=text, metadata=metadata)lazy_load yields documents instead of returning a list. That keeps memory low for very long books. The single yield here is fine because we want the splitter to see the whole book as one continuous markdown stream.
Why markdown headings matter
The splitter uses blank lines and newlines as natural chunk boundaries. Headings give you that structure for free.
Good instinct, but the splitter already handles that. Passing one document lets RecursiveCharacterTextSplitter use markdown structure (blank lines, newlines) to cut on natural boundaries, while still producing chunks of controlled size. Splitting into one document per chapter too early locks you out of fine-grained chunking inside long chapters.
Quiz: Quiz
Loading practice…