Retrieval-augmented generation has a part nobody wants to work on. You cut documents into
chunks, embed them, get some back, hand them to a model. The retriever and the model get the
attention. The cutting gets a chunk_size parameter and a shrug.
That parameter does more damage than it looks like it does. Cut a manual every 1,400 characters and you get passages that start mid-sentence, that no longer sit under any heading, and that cannot tell you which of your documents they came from. The model receives them as equals and reasons over them anyway. Nothing the retriever does well can exceed the quality of what you put in the index.
The prevailing answer has been more room — longer contexts, so you can afford to send more of the wrong thing and let the model sort it out. That does help. It is not the same as fixing it, and it is an expensive way to avoid the question.
pagespeak works at the other end. A retrieved unit should be a section: cut where the document’s own hierarchy says to cut, stamped with the document and chapter it belongs to and a hash of the file it came from, and carrying what the figures said — because a great deal of what a manual actually knows was never text.
What it does
Get the chapters back when the converter flattened them
Marker gives Chapter 1 and its 1.1 subsection the same #### level, so the whole document comes out as a flat pile of peers — no chapter above anything, and nothing for a breadcrumb to name. Docling has the same problem from the other end: it caps at two levels by design. --normalize-headings-mode llm_full sends the headings plus the body text to a model and re-levels them. One call per document, cached, and it will not drop a heading that owns children or owns its own prose.
Know which manual a retrieved chunk came from
Split into one file per section and every file opens with a > ↑ Doc / Chapter / Parent line and carries its own doc_id and source_sha256. So a chunk that comes back from a search can say which manual and which section it is, and you can check it still matches the PDF it was built from. In a 70-manual index, an answer cites the manual behind each step instead of blending three of them together.
Search text that exists only inside a figure
The vision pass writes a description into each image’s alt text, and a Mermaid block when the figure is a flow diagram. In the worked example, “which organ system includes the spleen?” comes back with Figure 1.5 as the top hit, 0.93 similarity — the word appears nowhere in the prose, only in the alt text the vision pass wrote. Descriptions are keyed by image hash, so re-converting the document never pays for them twice.
Find the conversions that came out broken
Marker can hand back a table crushed into a single <br>-joined mega-cell, or a section file that is an empty shell, and nothing about the run will have failed. pagespeak audit conversions/out reads the finished output for ten shapes like those — U+FFFD encoding damage, image links whose files are not there, duplicate headings — and exits 1 if it finds any. Read-only, no model calls, nothing to pay for. --summary-only gives per-check totals across a whole corpus.
Fix one mangled table without re-running the PDF
When Marker jams a whole grid into one cell, pagespeak repair-tables locates that PDF page, re-reads it with Docling, and splices the clean grid in. Prose, headings and the vision cache stay as they were, and it leaves the original alone if Docling’s grid would drop a value. --dry-run prints which tables it would touch.
Re-run one stage, not the whole 500-page PDF
Each of the seven phases writes its own checkpoint, so --from normalize --stop-after normalize re-runs exactly that stage against frozen upstream output. A parallel ingest that dies partway resumes from manifest.json and redoes only the unfinished chunks; convert refuses to build a document while any chunk is still failed.
What that looks like in practice
The clearest case is the one where the answer was never text to begin with. A washing machine puts a code on the display. The people who built it wrote down exactly what to check, in what order, with the voltage to measure and the resistance range that condemns the part — and then printed it as a flowchart, which is to say a picture, which is to say something no index could reach.
The harder test is a question with no single right source anywhere. I asked one I actually needed answered: how to keep cymbals out of a close tom mic, on the desk while tracking and again in the mix. Answering it properly meant a microphone, a console, a DAW and three plugin makers — sixteen sources, none of which is permitted to mention any of the others.
I ran it as a blind comparison: three agents answered from what they already knew, three answered from the corpus, and two judges graded both against 37 facts with sources attached. Unaided, 10 of 33. With the corpus, 25. The unaided answers were fluent and specific and named menu paths, in three different products, that do not exist — which is the failure mode that matters, because nothing in the writing warns you.
The parts of a manual you need when you are actually fixing or connecting something — diagnostic trees, routing, pinouts, front-panel layouts, menu trees — are the parts that were never text. In one corpus 15,539 figures were given a written description, adding 4,976,794 characters that did not exist in the extracted text, and 450 flow diagrams came back out as Mermaid.
Scale is the part that worries people, and it should. Ten questions naming a specific piece of gear, put to an index of 104,922 documents, returned the correct manual first every time — and the correct section within it about two thirds of the time. Landing in the right book on the first try is what makes the second search worth running: the section that comes back says which manual and which chapter it is, so an agent can tell it has the right equipment and the wrong page. A fixed-size chunk tells it neither.
The pass costs money once. Descriptions are cached against the image’s perceptual hash, so the same figure is never analysed twice: not on a re-ingest, not when the model changes, not when it turns up in another document. pagespring keeps the images on disk so the fetch does not repeat either.
None of this is knowledge a model can be trained into having, because it is not general knowledge. It is what your specific machine does, printed once, by the people who built it.