M06 · Document ingestion and chunking for RAG06-0122 min read
Lesson 37 of 106 · Module 7 of 14 · Week 3
Threads:The measurement threadThe infrastructure thread
Document parsing for RAG: PDFs, tables, and silent failures
Document parsing is the RAG stage that converts a file into plain text a machine can embed, and it is the only stage in the pipeline whose failures produce no error message. A parser that silently drops a scanned page, scrambles a two-column layout, or flattens a table into a row of orphaned numbers still returns a string, still embeds cleanly, and still gets indexed — so the corpus looks complete while the answers are wrong.
What document parsing for RAG is
Document parsing for RAG is the extraction of a machine-readable text representation from a source document, together with enough structure to make that text retrievable — reading order, headings, table boundaries, and page provenance. It is a lossy conversion by nature. The question is never whether you lose information but which information you lose and whether you noticed.
It helps to be precise about the three distinct things a parser is doing at once:
- Character recovery — getting the actual glyphs out. For a natively generated PDF this is a lookup in an embedded font map. For a scanned page it requires optical character recognition (OCR), because there are no characters in the file at all, only pixels.
- Reading-order reconstruction — deciding what sequence those characters belong in. A PDF stores text as positioned draw operations, not as a stream of sentences, so "the order the text appears in the file" and "the order a human reads it" are different orders that happen to coincide for simple single-column documents.
- Structure preservation — retaining the relationships that made the text meaningful: this number belongs to that column header, this paragraph sits under that section heading, this footnote attaches to that sentence.
Most parsing tutorials only do the first. Production RAG failures are overwhelmingly in the second and third.
The official NCA-GENL blueprint touches this directly. Objective 1.4 is "Curate and embed content datasets for RAGs" — curation is exactly this work, and it precedes embedding in the wording. Objective 2.3, "Conduct data analysis under the supervision of a senior team member," and the Data Analysis module's scope statement — "inspecting, cleansing, transforming, and modeling data" — put inspection and cleansing squarely in the tested territory. Objective 4.4, "Identify system data, hardware, or software components required to meet user needs," is where "do I need an OCR component in this pipeline?" lives. [OFFICIAL]
How document parsing works: text layers, layout analysis, and OCR
The mechanism is worth understanding in three tiers of depth, because the exam tests the first tier hard, the second tier occasionally, and the third tier almost never.
L1 — A PDF is a drawing, not a document
The single most useful intuition: a PDF is closer to a set of printing instructions than to a text file. Inside it are operators that say, in effect, set font F1 at 11 points, move to coordinate (72, 684), draw the glyphs "Quarterly", move to (128, 684), draw the glyphs "revenue". There is no paragraph object. There is often no space character — the space between two words can be a coordinate jump rather than a glyph. There is no notion of "this is a table"; a table is horizontal and vertical lines drawn near some text.
A parser therefore has to infer the document from the drawing. Every inference is a place where it can be wrong without knowing it. Word documents and HTML are structurally better off — they carry real paragraph, heading, and table elements — which is why format matters enormously to parsing quality and why "we support PDF" is a much weaker claim than it sounds.
L2 — The four-stage parsing mechanism
A capable document parser runs roughly these stages, and you should be able to name them:
| Stage | What it does | What breaks it |
|---|---|---|
| Text-layer extraction | Reads embedded glyphs and their coordinates directly from the file | Scanned pages have no text layer; broken font maps produce garbage characters |
| Layout analysis | Groups positioned glyphs into lines, lines into blocks, blocks into a reading order | Multi-column layouts, sidebars, headers, footnotes, rotated text |
| OCR fallback | Runs optical character recognition on page images where no text layer exists | Low-resolution scans, handwriting, skew, stamps over text, poor contrast |
| Structure extraction | Recovers tables, headings, lists, and figure captions as distinct objects | Borderless tables, merged cells, multi-page tables, nested headers |
Two things about this table matter more than the rest. First, OCR is a fallback, not a default — running OCR on a page that already has a clean text layer usually makes the text worse, not better, because OCR introduces recognition errors where none existed. Second, structure extraction is a separate capability from character recovery, and a parser can be excellent at one and useless at the other. A PDF library that gets every character right and returns a table as a single run-together line has recovered the characters and destroyed the meaning.
L3 — Reading order and the coordinate model
At the deepest tier: layout analysis is a geometry problem. The parser has a set of text blocks each with a bounding box, and it must produce a linear sequence. Common heuristics sort by vertical position then horizontal, which is correct for single-column prose and catastrophically wrong for two-column layouts — it produces "left column line 1, right column line 1, left column line 2, right column line 2", interleaving two unrelated arguments sentence by sentence. Better parsers detect column boundaries first (by finding vertical whitespace gutters that persist down the page) and then read each column fully before moving on.
You do not need to implement this for the exam. You need to recognise the symptom: retrieved chunks that read as grammatical fragments spliced together, where every other clause belongs to a different topic. That symptom is a reading-order failure, not an embedding failure, and no embedding model or reranker fixes it.
Native PDF vs scanned PDF vs HTML vs DOCX: what a parser can recover from each
This comparison is the highest-value asset in the lesson, because scenario questions describe a document type and ask what the pipeline needs.
| Source format | Character recovery | Reading order | Table structure | Component you need | Characteristic silent failure |
|---|---|---|---|---|---|
| Natively generated PDF (exported from a word processor) | Reliable — glyphs and font maps are embedded | Inferred; good for one column, fragile for two | Inferred from ruling lines and alignment | PDF text extractor | Two-column interleaving; header/footer text injected mid-paragraph |
| Scanned PDF (page images, no text layer) | Requires OCR; accuracy varies with scan quality | Inferred from OCR output geometry | Very fragile | OCR engine plus layout analysis | Returns an empty string. The page silently contributes nothing to the index |
| Hybrid PDF (some pages native, some scanned) | Mixed per page | Mixed per page | Mixed per page | Per-page detection, then conditional OCR | Partial corpus: chapters 1–4 index, chapter 5 vanishes |
| HTML web page | Reliable — text is text | Explicit in the DOM, but polluted by navigation, cookie banners, and boilerplate | Explicit table elements, usually clean | HTML parser plus boilerplate removal | Navigation menus and cookie notices become the most-repeated text in the corpus |
| DOCX / ODT | Reliable | Explicit paragraph order | Explicit table objects | Office-format reader | Tracked changes, comments, and hidden text extracted as if they were body text |
| Spreadsheets (XLSX / CSV) | Reliable | Cell order is explicit but semantically ambiguous | Native, but a sheet is not prose | Tabular reader plus a serialisation choice | A grid flattened into a sentence, losing which value belongs to which column |
| Slide decks (PPTX) | Reliable | Weak — slides are spatial, not linear | Sparse | Presentation reader | Text boxes emitted in creation order rather than visual order; speaker notes silently merged with slide text |
| Email (EML / MSG) | Reliable | Explicit | N/A | Mail parser plus quote stripping | Quoted reply chains duplicate the same text dozens of times across the corpus |
The row that decides architectures is the scanned PDF. Every other format degrades; a scanned PDF disappears. And because a corpus is usually assembled from whatever a business already has, hybrid corpora are the norm, not the exception — a policy archive spanning fifteen years will have native exports for recent documents and scans of the older ones.
Worked example: auditing a parser over a 400-page manual
Here is a constructed audit — the numbers below are invented for teaching and are not measured results — that shows the shape of the check you should actually run. Constructed scenario.
Suppose you ingest a single product manual: 400 pages, mixed content, exported to one PDF by a documentation team that scanned in three appendices from a 2009 printed edition.
Ingestion audit — constructed example, not measured
Source document
pages 400
pages with an embedded text layer 356
pages that are images only 44 (the three scanned appendices)
Parser output, run 1 (text extractor only, no OCR)
pages producing >50 characters 356
pages producing 0 characters 44
total extracted characters 812,000
estimated tokens (chars / 4) 203,000
→ 11% of pages contributed nothing.
→ No error was raised. Zero exceptions. Exit code 0.
Parser output, run 2 (text extractor + OCR fallback on empty pages)
pages producing >50 characters 398
pages producing 0 characters 2 (two blank separator pages — correct)
total extracted characters 903,000
estimated tokens 225,800
→ recovered ~91,000 characters, ~22,800 tokens
→ the two remaining zero-text pages are genuinely blank: verified by eye
The audit that found the problem is three numbers wide: pages in, pages producing text, characters per page. That is the entire diagnostic. You can compute it in a few lines and it catches the most expensive parsing failure there is.
Now the second half of the same audit, on structure rather than characters:
Structure audit — same constructed example
Tables in the source (counted by hand on a 20-page sample, extrapolated)
~68 tables across 400 pages
Tables in the parser output
recognised as table objects 19
flattened into undelimited text 41
partially captured (header lost) 8
→ 72% of tables lost their header-to-value binding.
→ Every one of them still produced text. Nothing errored.
A flattened table is the purest example of a silent parsing failure. Consider a specification table whose row reads Operating temperature | -10 °C | 45 °C. Flattened without delimiters it becomes Operating temperature -10 °C 45 °C, and with the column headers Minimum and Maximum stripped off the top of the table, nothing in the chunk records which number is the minimum. Ask "what is the maximum operating temperature?" and the retriever finds this chunk — it is genuinely the most relevant text in the corpus — and the model reads two numbers with no labels and picks one. The citation is correct. The provenance is correct. The answer is a coin flip.
Decision table: which parsing approach for which corpus
When to reach for each approach, and when not to:
| Corpus description | Approach | Why | When this is the wrong choice |
|---|---|---|---|
| Markdown, plain text, or clean HTML you control | Direct read, strip boilerplate | Structure is already explicit; a heavy parser adds failure modes | Never wrong, but does not generalise to a mixed corpus |
| Natively exported PDFs, single column, mostly prose | Text-layer extractor with a page-level character-count check | Cheap, fast, high fidelity; the check catches the surprises | Wrong if any page is scanned and you skip the check |
| Any corpus you did not personally produce | Per-page text-layer detection, conditional OCR | Hybrid corpora are the default case, and OCR-everything degrades good pages | Over-engineering only if you have verified every page has a text layer |
| Documents whose value is in tables (specs, financials, lab results) | A parser with explicit table-structure extraction; serialise each table as Markdown or as one row-per-record | The header-to-value binding is the information | Wrong to rely on general text extraction here, however good its characters are |
| Multi-column academic PDFs | Layout-aware parser with column detection; verify reading order on a sample | Vertical-then-horizontal sorting interleaves columns | Wrong to trust a naive extractor's clean-looking output |
| Scanned archives, decades old, low quality | OCR with a quality gate; route low-confidence pages to human review or exclude them | Bad OCR is worse than no text: it produces plausible wrong words that embed fine | Wrong to ingest silently at any confidence |
| Very large web-scale crawls | Batch curation tooling built for scale, with boilerplate removal and language filtering as pipeline stages | Per-document scripting does not survive millions of documents | Wrong for a 200-document internal corpus, where it is pure overhead |
That last row is where NVIDIA's named tool for this problem sits. NeMo Curator is NVIDIA's data-curation component for preparing datasets at scale, and its role in the stack map is exactly this: curation, as distinct from NeMo for build and customise, NeMo Retriever for retrieval, NeMo Guardrails for safety rails, Triton for serving, and NIM for packaged deployment. [NVIDIA-DOC] For the exam, the identity is what is tested: which tool curates data? NeMo Curator. Know it at that level, alongside the fact that the NVIDIA AI Blueprint for RAG is the reference workflow that shows the ingestion-to-answer path assembled end to end, and NeMo Retriever is the retrieval-accuracy component that consumes what your ingestion produced. [NVIDIA-DOC] Do not memorise configuration flags for any of them; the [FIELD] calibration on this exam is consistent that YAML-level depth does not appear and that high-level tool identity does.
Why document parsing is on the NCA-GENL exam
Document parsing serves four official objectives at once, which is unusual for a single ingestion topic and is why it earns a full lesson rather than a paragraph inside a RAG overview.
| Objective (verbatim) | How parsing serves it |
|---|---|
| 1.4 Curate and embed content datasets for RAGs | Parsing is the first act of curation. You cannot curate text you have not extracted, and you cannot embed what the parser dropped |
| 1.3 Build LLM use cases such as retrieval-augmented generation (RAG), chatbots, and summarizers | A RAG use case built on a corpus that is 11% empty is a RAG use case that fails for one question in nine, unpredictably |
| 2.3 Conduct data analysis under the supervision of a senior team member | The page-count and character-count audit in §4 is data analysis on your own corpus, and it is exactly the inspect-and-cleanse work the module's scope statement describes |
| 4.4 Identify system data, hardware, or software components required to meet user needs | "This corpus contains scans, therefore this pipeline requires an OCR component" is the canonical form of this objective applied to RAG |
[OFFICIAL] on the objective wording; the mapping is this course's reading of it.
How the questions get phrased. The exam is 50–60 multiple-choice questions in 60 minutes, which is roughly 60–70 seconds each, so questions are compact scenarios rather than long cases. [OFFICIAL] Expect forms like these:
- "A team's RAG chatbot cannot answer questions about the company's 2011–2015 policy documents, although those documents are in the corpus and the pipeline reported no errors. What is the most likely cause?" — the keyed answer names scanned pages with no text layer and no OCR step.
- "Which pipeline component is required to ingest a corpus of scanned contracts?" — OCR.
- "A retrieved chunk contains correct numbers but the model attributes them to the wrong quantities. Which ingestion stage most likely failed?" — table-structure extraction, not embedding, not retrieval.
- "Which NVIDIA component is intended for curating datasets at scale?" — NeMo Curator.
- "Before embedding a document corpus, which step comes first?" — parsing/extraction, ahead of chunking, ahead of embedding, ahead of indexing. Component order is a repeatedly reported question shape for RAG. [FIELD]
Distractor families. Four recur, and recognising the family is faster than reasoning through each option:
- Downstream-fix distractors. The option proposes a bigger embedding model, a reranker, or a larger context window to solve a parsing problem. Tempting because those are all real techniques. Wrong because none of them can retrieve text that was never indexed.
- Tool-confusion distractors. NeMo Curator swapped for NeMo Retriever, NeMo Guardrails, TAO, or Triton. The stack-map discipline in this course exists for this family; [FIELD] reports consistently that NVIDIA-branded options are favoured when technically defensible, so the trap is picking an NVIDIA tool rather than the right one.
- Over-processing distractors. The option proposes running OCR on the entire corpus, or applying an aggressive classical-NLP cleanup (lowercasing, stop-word removal, stemming) before embedding. Both degrade a modern pipeline. OCR on a clean text layer adds errors; stripping case and stop words removes signal a contextual embedding model uses. See
02-05for why that classical pipeline belongs to a different era of NLP than a transformer embedding model. - Error-assumption distractors. The option assumes the pipeline would have raised an exception, and therefore concludes parsing is fine. The whole point of this lesson is that it would not have.
Common mistakes with document parsing for RAG
| Mistake | Symptom you actually see | Underlying cause | Fix |
|---|---|---|---|
| Trusting a zero-exception ingestion run | "It ingested fine" and then unanswerable questions about specific documents | Parsers return empty strings rather than raising on unparseable pages | Assert on output: pages-in vs pages-with-text, characters per page, and fail the run when a page yields under a threshold |
| No OCR path for a hybrid corpus | An identifiable slice of the corpus — one date range, one department, one file source — is invisible to retrieval | Scanned pages have no text layer to extract | Detect text-layer presence per page and route image-only pages to OCR |
| OCR applied indiscriminately | Retrieved text has plausible but wrong words: 1 for l, rn for m, mangled units | OCR recognition error introduced on pages that never needed it | Make OCR conditional on an empty or near-empty text layer |
| Tables flattened to prose | Numerically correct chunks, confidently wrong attributions of number to quantity | Header-to-value binding is destroyed by generic text extraction | Extract tables as structured objects and serialise each with its headers — Markdown tables or one self-describing sentence per row |
| Reading order not verified | Retrieved chunks read as spliced fragments; every other clause is off-topic | Multi-column layout sorted vertically then horizontally | Use a layout-aware parser; eyeball extracted text against the rendered page for a sample of every distinct document template |
| Headers, footers, and page numbers left in | The same short string recurs thousands of times and starts winning retrieval | Running headers are drawn on every page and extracted on every page | Strip repeated per-page furniture at parse time — and see 06-04, where this becomes a corpus-level deduplication problem |
| Formulas, code, and figure text treated as prose | Chunks containing mathematical or code content are retrieved for unrelated queries | Symbol-heavy text tokenises into low-information fragments that sit oddly in embedding space | Detect and either preserve these regions verbatim as distinct chunk types or exclude them, and record which you did |
| No provenance recorded during parsing | You cannot tell which file or page a retrieved chunk came from, so you cannot cite it or audit it | Provenance is available at parse time and discarded before indexing | Capture source file, page number, and section heading at parse time — the subject of 06-03 |
| Parsing once, never again | Quality drifts as new document templates enter the corpus | The audit was run at build time and never re-run | Make the ingestion audit a recurring job with the same three counts, so a new template that parses badly is visible immediately |
Why do document parsing failures stay silent instead of raising errors?
Because a parser's contract is to return a string, and an empty string is a valid string. There is no exception to raise: the file opened, the pages were read, the text-extraction call succeeded and produced zero characters, which is a correct description of a page containing no characters. The parser is not wrong. The pipeline's assumption — that a page yields text — is the thing that is wrong, and assumptions do not throw.
The same logic explains every other silent failure in the list. Interleaved columns are a valid string. A flattened table is a valid string. A cookie banner extracted as body text is a valid string. Every downstream stage accepts strings and produces something: the chunker chunks, the embedding model embeds — it will embed nonsense into a perfectly well-formed vector — the index indexes, the retriever retrieves, the generator generates. Not one of those stages has any way to know that the string it received does not represent the document.
The practical consequence is a rule worth carrying into every ingestion job you build: parsing must be validated by assertion on its output, not by the absence of errors. If you have not counted something, you have not checked anything.
Do I need OCR for my RAG corpus, and how do I know?
You need OCR if and only if some pages in your corpus contain text as pixels rather than as characters. The test is mechanical and takes one pass:
- For each page, extract the text layer and count the characters.
- Compare against the page's visual density — or simply flag any page under a low threshold, say 50 characters, since real content pages almost never fall below that.
- Render a handful of the flagged pages as images and look at them. If they contain visible text, you have image-only pages and you need OCR. If they are genuinely blank separators or full-page figures, you do not.
Two cautions. First, the answer is per page, not per corpus — the hybrid case in §3 is the common one, and a corpus-level "yes" leads to OCR-everything, which is the over-processing mistake. Second, OCR output needs its own quality gate. An OCR engine returns text with a confidence signal, and low-confidence text is actively dangerous in a RAG corpus: a wrong-but-plausible word embeds into a plausible region of vector space and gets retrieved for queries it should not match. Text you cannot trust is worse than text you do not have, because absence is at least honest.
What does a good ingestion audit measure?
Six numbers, all cheap, all computable without a model:
| Measurement | What it catches |
|---|---|
| Pages in vs pages producing text | Missing scans, unparseable pages, whole documents lost |
| Characters per page, as a distribution | Outliers at both ends: near-empty pages and pages where a table collapsed into one enormous line |
| Tables counted by hand on a sample vs tables recognised | Structure loss, the most under-detected failure |
| Extracted tokens vs an independent estimate of document length | Systematic loss you would otherwise never notice |
| Count of the most frequently repeated strings | Headers, footers, boilerplate, navigation — the input to 06-04 |
| Reading-order spot check on one page per document template | Column interleaving |
Templates, not documents, are the right sampling unit for the last one. A corpus of 4,000 documents may contain only six distinct layouts, and one spot check per layout is a complete audit of reading order — where 4,000 random spot checks would be both unaffordable and less informative.
The measurement thread in this course keeps insisting on the same idea in different clothes: 03-04 had you test retrieval quality by hand, 01-08 had you build an evaluation set before you had anything to evaluate. This is the same instruction pushed one stage earlier. The corpus is the earliest thing you can measure and the last thing anyone thinks to.
Glossary recap: the terms this lesson introduced
| Term | Definition |
|---|---|
| Document parsing | Extraction of machine-readable text plus recoverable structure from a source document, the first stage of a RAG ingestion pipeline |
| Text layer | Embedded character data in a PDF, with font maps and coordinates, that can be extracted without recognition |
| OCR (optical character recognition) | Recognition of characters from page images, required when a page has no text layer, and a source of plausible-but-wrong text |
| Layout analysis | Grouping positioned glyphs into lines, blocks, and a reading order |
| Reading order | The linear sequence a human would read a page in, which a parser must infer from geometry |
| Structure extraction | Recovery of tables, headings, and lists as distinct objects rather than as undifferentiated text |
| Silent failure | A failure that produces valid-looking output and no error, so it is invisible to every downstream stage |
| Hybrid corpus | A document set mixing native and scanned files, the normal case for any archive you did not create |
| Boilerplate | Repeated non-content text — headers, footers, navigation, cookie notices — extracted alongside real content |
| Ingestion audit | The set of counts that verifies a parser's output against the source documents |
| NeMo Curator | NVIDIA's data-curation component for preparing datasets at scale [NVIDIA-DOC] |
Key takeaways on document parsing for RAG
- Parsing is the only RAG stage whose failures are silent. Every other stage errors, degrades measurably, or shows up in a metric. Parsing returns a valid string and moves on.
- A PDF is a drawing, not a document. Paragraphs, reading order, and tables are inferred, and every inference is a place to be confidently wrong.
- Scanned pages return an empty string, not an error. That is the single most expensive silent failure, and it hides in the age of a corpus rather than in its size.
- OCR is a conditional fallback, not a default. Running it on clean text layers adds recognition errors; skipping it on image-only pages deletes content.
- A flattened table keeps the numbers and destroys their meaning. The header-to-value binding is the information; preserve it as structure or serialise it into each row.
- Validate by assertion, never by absence of errors. Pages in vs pages with text, characters per page, tables found vs tables present, and a reading-order spot check per document template.
- Downstream tuning cannot recover upstream loss. No reranker, no larger context window, and no better embedding model retrieves text that was never indexed. This is the reasoning that kills the most common distractor family on this topic.
- NeMo Curator is the NVIDIA answer to curation at scale, distinct from NeMo Retriever (retrieval accuracy) and from the AI Blueprint for RAG (reference workflow). Identity depth, not configuration depth. [NVIDIA-DOC]
Next: how parsed text becomes retrievable chunks
You now have text. It is one long string per document — possibly hundreds of thousands of characters — and nothing about it is retrievable yet, because retrieval works over pieces small enough for a single vector to represent faithfully. Deciding how big those pieces are, where to cut them, and whether they should overlap is a granularity decision that is entirely separate from how much text your model can read at once, and conflating those two things is one of the most common confusions in this whole subject.
Next: 06-02 Chunking strategies for RAG: fixed, recursive, and semantic — where chunk size is settled as a granularity decision, and the context window from 04-06 is settled as a ceiling that has nothing to do with it.