and costly. The quick parser misses the desk the reply lives in. Run the heavy one on each web page of a 200-page report and also you pay for 200 pages to rescue three. Run a budget one in every single place and the one flattened desk sinks the reply. Neither setting is correct for the entire doc, as a result of the doc is just not uniform: most pages are plain textual content a quick parser handles tremendous, and some carry the tables that want the costly one. The web page itself ought to determine how exhausting it will get parsed.
This text is the primary of two elements on adaptive parsing, in Half III of Enterprise Doc Intelligence, a sequence that builds an enterprise RAG system from 4 bricks: doc parsing, query parsing, retrieval, and era. This half builds the escalation cascade and a budget checks that determine when a parse is sweet sufficient; the second half, Loop engineering with adaptive parsing in motion: parsing flat tables with Azure and figures with a imaginative and prescient LLM (hyperlink to return), walks the deeper parsers in motion.

📓 The runnable companion runs a budget checks your self: you name pre_parse_signals on every web page of the Consideration paper, print the per-page flags (char_count, image_count, the flat-table fingerprint on line_df), and watch which pages path to a heavier parser earlier than a single greenback is spent on one. On GitHub: doc-intel/notebooks-vol1.

PyMuPDF parses a web page in 5 milliseconds at no cost. A imaginative and prescient LLM on the identical web page can price ten thousand occasions extra and take ten seconds. When the query lives in plain prose, a budget parser wins. When the reply hides inside a desk, a determine, a scanned area, or a flattened structure, a budget parser silently returns nothing helpful and the LLM confidently solutions the improper query.
Working the heaviest parser on each web page is wasteful. Working the most cost effective one in every single place is improper. The trick is to start out low cost and escalate solely when one thing says a budget parse missed the reply. That sign has to return from the pipeline itself: a sequence of checks towards the parse output, the query, and the LLM’s personal footprint when it tries to make use of what got here out.
The way in which round this can be a suggestions loop the pipeline builds for itself. Begin with the most cost effective parser. Consider its output at each verify of the pipeline. Escalate to a deeper parser solely when a minimum of one verify flags a budget parse as inadequate for the query.
The analysis is just not one verify at one place. It’s a cascade:
- pre-parsing metadata routes many of the corpus earlier than any extraction
- parsing-time outputs flag flattened tables and opaque figures
- retrieval scoring catches anchors that drift
- era flags what survived all the above
Every verify is cheaper than the following, extra dependable than the following, and asks its personal query: “did the parser produce sufficient to reply?”.
This half walks a budget finish of that cascade: at every verify, what indicator fires, what it prices to compute, what it buys. Collectively they determine, earlier than any costly parser runs, whether or not a web page wants one. Two recurring examples thread by means of each elements, each from the Consideration paper (Vaswani et al. 2017, below the arXiv non-exclusive distribution license): Desk 3 (web page 9, a flat-parsed grid the place each cell is its personal line) and Determine 1 (web page 3, a diagram physique that PyMuPDF returns empty). Right here we see how every is detected at no cost; the second half escalates them finish to finish.
1. When low cost parsing isn’t sufficient
Adaptive parsing runs in two phases. Section one is initialisation: choose a baseline parser per doc, primarily based on what the doc is (native PDF, scan, Phrase export). In a platform context this fires at ingestion: each new doc will get its baseline parse on arrival, so the system all the time has a primary textual layer to work with, even for scanned PDFs the place the baseline must be an OCR engine as a result of PyMuPDF would return empty. Section two is the cascade: run low cost deterministic checks on the baseline’s output and escalate the pages that fail to a deeper parser. Most pages keep on the baseline; the few that fail a verify get the richer therapy they really want.
The pipeline all the time begins low cost on part one. PyMuPDF on native PDFs, a free OCR engine on scans. Each produce the identical line_df form, so the remainder of the pipeline (query parsing, retrieval, era) doesn’t know which parser ran. That works more often than not. It fails on a small however predictable set of content material shapes, and part two has a number of locations to catch the failure, ordered from low cost deterministic checks on the parser’s outputs to a final-line-of-defence LLM name at era time.
1.1. Low cost parsing: PyMuPDF for native PDFs, free OCR for scans
Article 5A (what to learn in a PDF) launched PyMuPDF (imported as fitz) because the default for native-digital PDFs: 5 seconds per doc, no API calls, no price, and a clear line-level extraction with bounding containers. The Consideration paper, the NIST Cybersecurity Framework (US Authorities work, public area within the US, see NIST copyright assertion), most workplace exports, most arXiv papers, virtually each contract authored in Phrase or LaTeX: PyMuPDF is the proper start line.
The opposite half of the corpus is scans: photographed pages, faxed paperwork, image-only PDFs. PyMuPDF returns empty textual content on these. A free OCR engine like Tesseract or PaddleOCR fills the hole: identical line_df form out, however slower (minutes per doc) and weaker on construction. Free OCR reads paragraph textual content fairly effectively. It struggles on tables, multi-column layouts, and degraded scans. We point out the scanned case right here for completeness; the labored examples on this article each use a local PDF so we will isolate the parsing-failure mechanism with out OCR noise on high.
OCR has no cheap-good tier. The place native PDF extraction is near-lossless on the PyMuPDF tier, character recognition on a scan begins weak regardless of the engine. Free OCR (Tesseract, EasyOCR) will get readable textual content out of unpolluted trendy scans, then degrades quick on skewed pages, low DPI, blended languages, historic fonts, tight column gutters, something under 200 DPI. Docling makes use of EasyOCR by default for character recognition, so the OCR-level errors are the identical: what Docling provides is structure on high of these characters, not higher characters. Cloud document-AI companies (Azure Doc Intelligence, AWS Textract, Mistral Doc AI) recognise characters higher and add structure, at just a few cents per web page. Imaginative and prescient LLMs learn broken scans the others quit on, at just a few cents to a dime per web page relying on the mannequin.
The asymmetry issues for the initialisation part. On a local corpus a budget baseline is genuinely good. On a scan corpus a budget baseline is genuinely weak, and the cascade will hearth on most pages slightly than the few that PyMuPDF would miss. On a scan-heavy corpus, choosing a paid tier because the baseline upfront is commonly cheaper end-to-end than operating per-page escalations.
Docling as a stronger baseline, when the funds and the {hardware} permit it. Docling is open supply, free at runtime, layout-aware. It subsumes the PyMuPDF + Tesseract cut up: one name handles native PDFs and OCR’d scans, returns structured tables, sections, and picture captions out of the field. Tempting as a common baseline, however the price form is actual. Docling pulls just a few hundred MB of structure and TableFormer fashions on first set up. At runtime it’s one to 2 orders of magnitude slower than PyMuPDF on native PDFs (Article 5quinquies measured round twenty-seven seconds per web page on a 1974 scan), and that latency assumes a working GPU. On CPU it’s noticeably slower once more. On a 500-page corpus meaning minutes to an hour of parsing time per doc, not the 5 seconds PyMuPDF provides. The set up itself may also battle company SSL inspection.
The proper query is just not “can I exploit Docling because the baseline?” however “does the operational envelope (corpus dimension, GPU entry, ingestion latency funds) let me afford it?”. Three regimes present up:
- Small corpus, GPU obtainable, structure high quality issues: Docling as baseline pays off. The cascade under hardly ever fires as a result of the baseline already returned structured output.
- Massive corpus, no GPU, or strict ingestion latency: PyMuPDF + Tesseract stays the baseline. Docling enters the cascade as an escalation tier (Camelot / Docling row in the price gradient), invoked solely on pages that failed a verify, not on the entire doc.
- Blended corpus: baseline-per-document. Native PDFs undergo PyMuPDF, scans by means of Tesseract or Docling relying on availability. The initialisation part already selected the proper device per doc kind; the cascade catches the leftover failures.
The 2-phase structure absorbs all three. Decide the strongest baseline the operational envelope permits you to afford; let the cascade deal with regardless of the baseline nonetheless misses.
The explanation this issues is the price gradient between parser tiers. On a 500-page doc end-to-end:

The gradient makes parsing-on-demand repay. Hold most pages on PyMuPDF; pay for a deeper parser solely on the pages a query wants.
1.2. The failure circumstances low cost parsing can’t deal with
Low cost parsing is quick and free, and it leaves a recognisable set of content material invisible or scrambled. 5 shapes come up usually:
- Flattened tables: The most typical failure on native PDFs. PyMuPDF returns every cell as a separate line, in geometric order, with column construction erased. “Self-Consideration” and “O(n² · d)” sit on traces 9 and 10 of the web page with nothing connecting them: the LLM can’t pair the row label with its worth. Detected at parsing-time by the flat-table fingerprint (Part 3.2.2); walked end-to-end within the second half.
- Figures and diagrams: PyMuPDF extracts the web page textual content round a picture however the picture itself is opaque to it. “Determine 1 reveals the encoder-decoder structure” survives; the structure diagram doesn’t. Detected at parsing-time by the opaque-figure verify (Part 3.2.3); walked end-to-end within the second half.
- Multi-column layouts: A two-column web page the place the parser walks columns geometrically as a substitute of logically produces interleaved sentences from each columns. The textual content is technically there; the studying order is damaged.
- Degraded OCR: Scanned pages with low decision, skew, or compression artefacts produce character-level errors: l/I/1 confusion, fragmented phrases, lacking punctuation. The LLM can generally get well the which means, generally hallucinates across the noise.
- Embedded objects: Equations rendered as vector paths, signatures as photographs, checkboxes, watermarks: objects the parser might return as placeholders, garbled textual content, or nothing in any respect.
This half picks two of those (flattened tables, figures) for the labored walkthroughs the second half develops, as a result of they’ve the cleanest reproductions on a public doc. The mechanism is identical for the opposite three. The parsing-time deterministic checks (doc parsing brick, part 3) flag the failure form, and the pipeline routes the web page to the proper deeper parser. The LLM sign at era time (era brick, within the second half) is the security internet for the circumstances the deterministic checks didn’t catch upstream.
Another reason to parse lazily, in numbers. An enterprise corpus averages 30 to 100 pages per doc, 1 to three pages related to any given query, and fewer than 10 questions per doc over its lifetime. Multiply these out and roughly 90% of any web page parsed at ingestion isn’t consumed by any reply. Deep-parsing each web page upfront pays the invoice for pages nobody will ever learn.
Right here is the entire article in a single image. The remainder of the textual content walks each bit of this cascade, however it helps to have the total map first.

2. A cascade of analysis checks
Parsing high quality is a multi-check choice. The pipeline begins low cost and asks, at every verify, whether or not the parser produced sufficient for the query. Each verify has a price; each verify that flags an issue can route the affected web page to a deeper parser. The pipeline pays just for the standard the query wanted.
The pipeline’s 4 bricks (doc parsing, query parsing, retrieval, era, launched in Articles 5 to eight) every personal one to 3 of the 9 analysis checks. The grouping makes the cascade legible: each verify belongs to a brick, each brick has its personal price profile and its personal attribute verify. The cascade diagram in part 1 reveals the total mapping.
The 2 recurring examples make the cascade concrete.
- Desk 3 on web page 9 of the Consideration paper is a flat-parsed grid: each cell by itself line with no column anchor surviving. Textual content density alone misses it at verify 1; the deterministic check-2 fingerprint catches it cleanly (flat-table signature), and verify 7 (the LLM flag) confirms it, with the caveats developed within the second half.
- Determine 1 on web page 3 is a diagram physique that PyMuPDF returns empty. Catchable at verify 1 (lower-than-average textual content density mixed with an embedded picture), at verify 2 (opaque determine area with zero extracted chars), and at verify 7.
The most affordable verify that fires wins, and the pipeline by no means asks the slower checks a query the quicker checks already answered.
The info mannequin that helps it: No matter verify triggers escalation, the schema for the rerun is identical. Add one column, parsing_method, to the relational tables from Article 5. The escalation logic turns into: write new rows with a deeper methodology on the affected pages. Blended paperwork, audit path, and caching all fall out of the schema at no cost.
page_df is the canonical place as a result of parsing all the time occurs per web page. After a typical adaptive run on the Transformer paper, the place the cascade flagged web page 6 (Desk 1, Most path lengths, caught by the check-2 flat-table fingerprint) and web page 3 (Determine 1, caught by the char-density sign at verify 1 and the opaque-figure sign at verify 2), page_df appears to be like like this. This part makes use of Desk 1 to indicate the form of the escalated tables; the total end-to-end walkthrough within the second half takes a second flat desk, Desk 3 on web page 9 (Variations on the Transformer structure), so that you see the sample on two completely different actual tables slightly than one.

page_df after one adaptive run: flagged pages carry two rows, one per methodology – Picture by creatorTwo design choices are baked in. First, escalation provides new rows, it doesn’t exchange. The PyMuPDF row for web page 6 stays alongside the Azure row: “PyMuPDF tried, the cascade flagged the parse, Azure was referred to as and succeeded”. Second, the context_structured column on page_df lets downstream queries choose the proper row: SELECT * FROM page_df WHERE page_num=6 AND context_structured=True returns the trusted parse with none re-run.
line_df additionally carries parsing_method. Strains from PyMuPDF coexist with traces from the deeper parser on the identical web page. PyMuPDF produced the prose round Desk 1; Azure produced the markdown rows of the desk itself; each keep in line_df. The retrieval brick reads line_df and sees a uniform form, no matter which parser produced which row.
Pictures reside in line_df too, as rows with kind='picture' and a placeholder textual content. That is the bit that makes the determine case (within the second half) work mechanically the identical because the desk case. The opaque-figure sign at verify 2 already flags such pages from line_df alone (picture current, zero extractable chars inside its bbox). When the cascade flags web page 3, the pipeline picks the row from image_df, sees parsing_method='not_parsed', and calls a imaginative and prescient LLM. The imaginative and prescient output will get appended to line_df as new textual content rows with parsing_method='vision_gpt4o', and the image_df row is up to date.

line_df with textual content, desk, image-placeholder rows, every tagged by parsing_method – Picture by creatorFocused re-parsing is one perform name, method-agnostic, that returns new rows with parsing_method=methodology already populated:
def enrich(pdf_path, line_df, page_df, pages, methodology):
"""Add layer-2 rows for `pages` utilizing `methodology` ; maintain current rows for audit."""
if methodology == "azure_layout":
from docintel.parsing.pdf.azure_layout import azure_layout_pdf_to_line_df
new_lines = azure_layout_pdf_to_line_df(pdf_path, pages=pages)
elif methodology == "vision_gpt4o":
new_lines = vision_extract(pdf_path, pages=pages)
# new_lines already carries parsing_method=methodology
line_df = pd.concat([line_df, new_lines], ignore_index=True)
page_df = append_page_rows(page_df, pages, methodology)
return line_df, page_df
The line_df schema from Article 5 is unchanged for downstream bricks: retrieval and era learn line_df, group by page_num, and by no means have to know which parser produced which line. The one contract that modified is “the parser identification travels with the row”.
3. Doc parsing: free deterministic checks
Doc parsing owns three of the 9 checks: pre-parsing (verify 1, earlier than any extraction runs), parsing-time (verify 2, on PyMuPDF’s personal outputs), and post-parsing (verify 3, on chunks downstream of line_df). All three are deterministic, free, and produce a verdict in milliseconds with none LLM name. Collectively they catch many of the failure shapes the article cares about, together with each Case A (the flat desk, Desk 3 on web page 9) and Case B (the opaque determine, Determine 1 on web page 3), the 2 circumstances the second half walks finish to finish.
3.1. Test 1: pre-parsing
The most affordable analysis level sits earlier than any text-extraction name runs. PyMuPDF can already report per-page metadata in milliseconds: what number of characters it might extract, what number of embedded photographs every web page carries, what producer string the PDF declares. These indicators route many of the corpus earlier than the parsing choice turns into attention-grabbing.
3.1.1. Per-page textual content density and picture depend
A local-PDF web page usually extracts 2000-4000 characters of textual content. Pages that are available effectively under that, paired with a number of embedded photographs, are signalling that “the web page is generally a determine” with none deeper inspection.

For Determine 1 on web page 3, this sign alone is sufficient to flag: 1827 chars vs the doc’s 2633-char imply, plus one embedded picture. The pipeline may route web page 3 to a imaginative and prescient LLM with out ever calling Azure or operating a era cross. For Desk 3 on web page 9, the identical sign doesn’t hearth: 2973 chars (above imply), zero embedded photographs. Test 1 catches one of many two examples at no cost; verify 2 will catch the opposite.
3.1.2. PDF metadata, producer string, and a tier trace
The PDF metadata provides a second low cost classifier. The Consideration paper’s producer is pdfTeX-1.40.25, which says LaTeX-authored, native, no OCR wanted. A scanner-produced PDF would say Adobe Scan, Canon, OmniPage. That single string routes 80% of an enterprise corpus to the proper tier earlier than any textual content extraction runs. The creation date, the embedded font checklist, and the presence of an XMP block add coarse however helpful classifiers.
pre = pre_parse_signals("knowledge/paper/1706.03762v7.pdf")
pre["producer"] # -> "pdfTeX-1.40.25"
pre["is_scanner_output"] # -> False
pre["pages"].head()
# page_num char_count image_count image_bboxes
# 1 2858 0 []
# 2 4256 0 []
# 3 1827 1 [(196.6, 72.0, 415.4, 394.4)] <-- Determine 1
# 4 2508 2 [(175.0, 94.0, 239.0, 221.3),
# (346.8, 82.7, 467.0, 267.3)] <-- Determine 2
# 5 3193 0 []
The results of this verify is just not the reply to a query. It’s a per-page judgment: “this web page is regular”, “this web page has a lacking picture physique”, “this entire doc is scanned and desires OCR upfront”. The judgment is free and it ships with the parsing_method column on page_df.
3.2. Test 2: parsing-time
PyMuPDF runs subsequent. Its outputs already carry deterministic indicators in regards to the structure it simply walked. Three checks hearth from line_df alone, none of them needing an LLM name.
3.2.1. Per-page column depend and reading-order threat
PyMuPDF extracts each line with bounding-box coordinates. Clustering line x-coordinates per web page produces a column depend. A well-structured doc has a secure column depend from web page to web page. Variance is the routing sign.

The anomaly on web page 10 is itself a parsing-quality sign. The detector didn’t produce a improper depend accidentally, it produced it as a result of the desk broke the column mannequin. That’s info the pipeline can use to route web page 10 to a layout-aware parser.
3.2.2. Flat-table fingerprint, caught on Desk 3
When PyMuPDF flattens a desk, the cells fall on their very own traces with slim bboxes instantly following the “Desk N: …” caption.

The fingerprint is concrete: on web page 9, traces 5-20 maintain the desk headers cut up throughout two bodily rows of bboxes, and features 21-33 maintain the 13 cells of the base row every by itself line. The cells are brief (1-4 chars), slim (below 30 PDF factors vast), and clustered vertically in a decent band proper after a “Desk 3: …” caption. A verify that counts these properties on line_df[page_num=9] and journeys a boolean flag runs in O(n_lines) with none LLM name. Desk 3 is caught at this verify.
3.2.3. Opaque determine areas, caught on Determine 1
PyMuPDF experiences picture bboxes at extraction time by way of web page.get_image_info(). If a web page has a picture and the textual content extracted inside its bbox is empty, the determine physique didn’t survive the parse.

The verify is identical because the pre-parsing density verify however extra exact: as a substitute of noticing the web page has fewer characters, it pinpoints a particular rectangle with zero characters. Determine 1 is caught at this verify. (Pre-parsing additionally catches it, extra cheaply; verify 2 confirms it and provides the bbox the following parser will want.)
3.2.4. Multi-column studying order, caught on BERT paper
The Consideration paper is single-column, so it can’t illustrate this verify. We swap in BERT (1810.04805) which is the usual ACL two-column format. assign_column_positions clusters line x-coordinates per web page and labels every line “left” / “proper” / “single” / “multi”. No hardcoded midpoint, the cut up is computed from the precise line distribution. When PyMuPDF walks such a web page geometrically, it might interleave sentences throughout the column boundary; the column labels let the pipeline (or a deeper parser) restore the proper studying order.

x0 values – Picture by creator3.2.5. Placing it collectively: one perform, three flags
pre = pre_parse_signals("knowledge/paper/1706.03762v7.pdf")
line_df = fitz_pdf_to_line_df("knowledge/paper/1706.03762v7.pdf")
# Three pages, three indicators, no LLM name
page_level_parsing_signals(line_df, page_num=3, pre_signals=pre)
# {'page_num': 3, 'flat_table': False, 'opaque_figure': True,
# 'col_anomaly': False, 'causes': ['opaque_figure'], 'escalate': True}
page_level_parsing_signals(line_df, page_num=9, pre_signals=pre)
# {'page_num': 9, 'flat_table': True, 'opaque_figure': False,
# 'col_anomaly': False, 'causes': ['flat_table'], 'escalate': True}
page_level_parsing_signals(line_df, page_num=10, pre_signals=pre)
# {'page_num': 10, 'flat_table': True, 'opaque_figure': False,
# 'col_anomaly': True, 'causes': ['flat_table', 'col_anomaly_3'],
# 'escalate': True}
On the finish of verify 2, each Desk 3 and Determine 1 are flagged for escalation with no single LLM name. The pipeline may enrich each pages with the proper deeper parser now, earlier than retrieval even begins. The remaining checks of the pipeline (retrieval, era, post-gen) grow to be last-resort security nets, not the first detection mechanism.
3.3. Test 3: post-parsing (chunk integrity, studying order)
After line_df is constructed, the pipeline chunks it for retrieval. A piece that cuts mid-sentence or mid-table-row carries a low chunk_integrity_score that downstream prompts can learn. Surya and PaddleStructure expose this instantly; with line_df alone we will compute it from line numbers and bboxes (sentence-end punctuation within the final line, table-row alignment inside the chunk).
The rating is deterministic, low cost, and fires earlier than any retrieval name. The article doesn’t develop a labored instance for verify 3 as a result of each Case A and Case B are already caught at checks 1 and a couple of; we point out it right here so the panorama is full.
4. Query parsing: routing by intent
Query parsing turns the query right into a typed parsed_question (Article 6) and makes use of that to route the cascade. Two checks reside right here: verify 4 (intent routing, one small LLM name on the query) and verify 9 (person suggestions, a gradual human-driven loop that refines verify 4 over time).
4.1. Test 4: intent routing
“In Desk 3”, “what share”, “in keeping with article 5” sign that the reply lives in a structured area. Route on to table-aware retrieval, skip prose embeddings. Article 6 made the case for parsing the query right into a typed object; routing by intent is certainly one of its payoffs. The price of this verify is one small LLM name on the query (just a few cents per thousand questions). The standard acquire: the pipeline routes Desk 3 inquiries to a table-aware parser with out ever needing the LLM at verify 7 to note the desk was flattened.
4.2. Test 9: person suggestions and golden set
Test 9 is the long-loop verify: a query that the person re-asks with barely completely different wording, a golden set drift over time, an knowledgeable flagging a improper reply. None of those hearth inside a single pipeline run, however they refine the query parser (and, not directly, each verify that follows). Concretely: a recurring rewording captured in expert_keywords_df (Article 6) will get added to the query router, and the following query that makes use of that phrasing is appropriately categorized with out the person having to retype it. The price of this verify is human time spent on golden-set curation; the standard acquire compounds.
5. Retrieval: rating hole and meeting checks
Retrieval takes the parsed query and the parsed corpus, and returns a small set of candidate chunks. Two checks hearth right here, each deterministic and each operating on portions the retrieval brick already computed.
5.1. Test 5: rating hole and supply range
High-1 cosine of 0.45 is a poor anchor. High-1 of 0.85 with top-2 at 0.83 is ambiguity. Each deterministic, each hearth earlier than era. Reranker disagreement with the bi-encoder is itself a second-order sign. The price is the cosine computation already completed by retrieval; the standard acquire is catching questions that landed nowhere clear.
5.2. Test 6: context meeting (gaps and contradictions)
As soon as the candidate chunks are picked, a final deterministic verify compares them to the query’s parsed scope. Query covers 2020-2024, chunks cowl 2020-2022: the hole is detectable earlier than the LLM name. Identical for chunks that contradict one another on a key truth. These checks run in milliseconds and are skipped solely at the price of fabrication-prone solutions downstream.
5.3. Adaptive embedding granularity: page-default, line-on-demand
The identical cheap-then-rich logic the cascade applies to parsing additionally applies to embedding. Parsing produced page_df and line_df (Article 5 Part 3.2 mentioned “line_df retains the door open” on this); retrieval now decides which granularity to embed, and when.
The default is page-level. One vector per page_df row, cosined towards the query vector, top-Ok pages picked. Low cost, persistable, retrievable in milliseconds on corpora as much as tens of 1000’s of pages. A typical enterprise contract is twenty to 2 hundred pages; a 500-document corpus carries forty to 1 hundred thousand web page vectors, effectively inside an in-memory cosine. That is what the minimal pipeline of Article 1 (minimal RAG) makes use of, and what most manufacturing retrieval nonetheless makes use of when the reply is clearly page-shaped.
Line-level on the entire corpus is the improper default. A web page averages 30 to 80 line_df rows in a contract, 50 to 120 in a analysis paper, generally 200 in a flattened table-heavy PDF. Embedding each line on the total corpus multiplies the vector depend by that issue: forty thousand web page vectors grow to be two to 10 million line vectors. The price reveals on two axes: compute at index time (one API name or one mannequin cross per line, towards a funds that was sized for pages) and storage at question time (the in-memory cosine now not suits and the retrieval drops to a vector database with non-trivial latency). For corpora that match page-level on a laptop computer, line-level pushes deployment right into a devoted vector retailer and adjustments the operations envelope.
The escalation sample: page-then-line, just for the top-Ok pages. When the page-level Test 5 fires (rating hole too small, candidates ambiguous, or high web page is lengthy and the reply is one line deep inside it), the retrieval brick falls again to a second cross. For the top-Ok pages solely, embed each line on these pages (just a few hundred vectors, not thousands and thousands), run cosine towards the query once more, and choose the highest traces. The result’s a line-level anchor inside a page-level scope. Article 7 develops the page-then-line drill-down with labored examples; the identical operation right here turns into a verify within the cascade: page-level handed however didn’t converge, so escalate to line-level on the surviving candidates.
The price form of this escalation is bounded. If page-level retrieval returns 5 candidate pages and every web page has fifty traces, the drill-down embeds 250 traces per query. At charges round twenty thousand line embeddings per second on a neighborhood mannequin, the added latency is just a few hundred milliseconds. The vectors are computed on-the-fly and never continued: that is the important thing distinction with keen line-level indexing. The escalation pays just for the candidate pages, just for the questions that triggered it. Most questions by no means attain this department.
When to additionally persist line-level vectors. Two circumstances earn the storage price. First, corpora the place virtually each query is line-shaped (audit checklists towards lengthy compliance paperwork, declare matching towards case recordsdata, contract clause lookup): the keen line index amortises throughout queries and the cascade collapses to a one-shot retrieval. Second, corpora the place the page-level cosine systematically fails the Test 5 hole (questions whose reply is buried in lengthy blended pages the place the page-level sign averages out): the keen line index avoids the per-question re-embed.
The sample is the parsing cascade transposed to embeddings. Default to a budget granularity (page-level vectors from page_df), drill all the way down to the wealthy one solely when a verify fires (line-level vectors over the surviving candidates from line_df). Identical knowledge mannequin, identical continued artefacts, identical operational property: nothing pays the wealthy price until a budget cross mentioned it ought to.
6. Conclusion
The cascade turns parsing high quality right into a sequence of low cost questions. Pre-parsing metadata routes many of the corpus earlier than any extraction runs. Parsing-time fingerprints flag flat tables and opaque figures from PyMuPDF’s personal output. Intent routing and the retrieval score-gap catch circumstances the parser’s output alone can’t. Each verify is deterministic, free, and runs in milliseconds, and each one that fires can route a single web page to a deeper parser.
One column, parsing_method, makes the entire thing auditable: escalation provides rows, it by no means overwrites, so a web page can carry its PyMuPDF parse and its Azure parse facet by facet. What none of those low cost checks can catch is the failure that solely reveals up when the LLM tries to make use of the textual content. That final line of defence, and the 2 escalations walked finish to finish (a flat desk to Azure, a determine to a imaginative and prescient LLM), are the second half: Loop engineering with adaptive parsing in motion: parsing flat tables with Azure and figures with a imaginative and prescient LLM (hyperlink to return).
7. Sources and additional studying
The reference for what superior parsing does and its per-page price is Docling (Auer et al., Docling Technical Report, 2024). Desk extraction as a separate drawback from textual content extraction is grounded by Smock et al. (PubTables-1M / Desk Transformer, CVPR 2022). The vision-LLM-per-page price tier (~5-30 seconds on a GPU) is from Blecher et al. (Nougat, Meta 2023). The LLM-as-feedback-signal sample the article makes use of to drive escalation is in the identical household as Asai et al. (Self-RAG, ICLR 2024). The article experiences a concrete unfavourable outcome from an 18-run stress take a look at: LLM self-evaluation because the binary parser-quality verdict is unreliable by itself; a separate-LLM groundedness verify on the produced reply catches what the in-loop sign misses.
Earlier within the sequence:
What works, what breaks
- The minimal Enterprise RAG that by no means lies about its supply: PDF in, highlighted reply out. The four-brick pipeline finish to finish: PDF in, highlighted reply out.
- Embeddings Aren’t Magic: The Predictable Failure Modes of RAG Retrieval. The place embedding similarity wins (synonyms, typos, paraphrase), the place it predictably breaks (unknown phrases, negation, term-vs-answer relevance), and methods to use it anyway.
- RAG is just not machine studying, and the ML toolkit solves the improper drawback. Why chunk-size sweeps and finetuning optimize the improper factor; route by query kind as a substitute.
- From regex to imaginative and prescient fashions: which RAG approach suits which drawback. Two axes, doc complexity and query management, that choose the approach for every case.
Doc parsing
- Past extract_text: the 2 layers of a PDF that drive RAG high quality. The primary half of the parsing brick: the doc’s nature, indicators, and abstract.
- Cease returning flat textual content from a PDF: the relational tables RAG wants. The second half of the parsing brick: the relational tables each downstream brick reads.
- When PyMuPDF can’t see the desk: parse PDFs for RAG with Azure Format. The identical tables from Azure Format: native desk cells, OCR, paragraph roles.
- Parse PDFs for RAG regionally with Docling: wealthy tables, no cloud add. The identical tables computed regionally with Docling: TableFormer cells, nothing leaves the machine.
- Imaginative and prescient LLMs are PDF parsers too: studying charts and diagrams for RAG. Imaginative and prescient as a parser: the photographs grow to be searchable textual content.
- Parse scanned PDFs for RAG with EasyOCR: free OCR provides you phrases, not a doc. The place conventional OCR stops: textual content recovered, construction misplaced.
- Making a PDF’s photographs searchable for RAG, with out paying to learn all of them. The picture cascade: filter low cost, classify, describe solely what’s value studying.
- Reconstructing the desk of contents a PDF forgot to ship, so RAG can scope by part. Rebuilding toc_df when the PDF prints a contents web page however ships no define.
Query parsing
- RAG questions want parsing too: flip the person’s string into briefs for retrieval and era. The thesis of query parsing: why a person string wants the identical parsing as a doc, and the way it splits right into a retrieval transient and a era transient.
- What the query parser extracts from a person string: key phrases, scope, form, decomposition, clarification. The 5 households of columns the parser reads straight from the person’s query, with the code that fills each.
- Dispatching the parsed RAG query: chunk technique, mannequin tier, activations, audit. The selections the parser makes on high of the person string, utilizing the doc’s profile: dispatch, activations, full schema, the audit path (pipeline_trace.json), and a broker-corpus walkthrough.
Retrieval
Technology
- Make RAG era return a typed contract: citations, typed values, and self-checks (hyperlink to return). The reply schema because the contract: typed values, gadgets with proof spans, self-assessment fields, and the completeness sign the pipeline computes itself.
- Assemble every RAG era immediate from a base immediate plus the principles every query wants (hyperlink to return). The dispatcher: a set BASE immediate plus the principles every query wants, the schema picked from the registry, and the total hint stored on each name.
- Validating the RAG reply earlier than the person sees it: spans, quotes, and the suggestions loop (hyperlink to return). The post-generation validator (spans, verbatim quotes, codecs), not-found as a first-class reply, and the suggestions loops that shut the pipeline.
One-document pipelines
- A manufacturing RAG pipeline for PDFs: relational parsing, TOC retrieval, typed solutions (hyperlink to return). Every of the 4 bricks upgraded one contract at a time: relational parsing, corpus-aware questions, TOC-routed retrieval, typed solutions.
- Cease RAG hallucinations with context engineering: one pipeline, 4 very completely different PDFs (hyperlink to return). The 4 upgraded bricks wired into one name, run finish to finish on a paper, a compliance doc, and a broken-TOC doc.
Identical course because the article:
- Auer et al., Docling Technical Report, IBM Analysis 2024 (arXiv:2408.09869). What superior parsing does and what it prices.
- Smock, Pesala, Abraham, PubTables-1M / Desk Transformer (TATR), CVPR 2022 (arXiv:2110.00061). Desk extraction is a separate drawback from textual content extraction. Helps the case for escalating selectively on tables.
- Blecher et al., Nougat: Neural Optical Understanding for Tutorial Paperwork, Meta 2023 (arXiv:2308.13418). Reference level for the vision-LLM price tier: about 5 to 30 seconds per web page on a GPU.
- Asai et al., Self-RAG: Studying to Retrieve, Generate, and Critique by means of Self-Reflection, ICLR 2024 (arXiv:2310.11511). The LLM-as-feedback-signal sample the article makes use of to drive escalation is in the identical household.
Completely different angle, completely different context:
- Faysse et al., ColPali: Environment friendly Doc Retrieval with Imaginative and prescient Language Fashions, 2024 (arXiv:2407.01449). Imaginative and prescient-language mannequin over the web page picture. The context is retrieval the place the web page picture itself is the artefact, making the parsing-text-vs-tables distinction much less related. Completely different from the per-page deterministic-first cascade defended right here.
- Wang et al., DocLLM: A Format-Conscious Generative Language Mannequin for Multimodal Doc Understanding, JPMorgan 2024 (arXiv:2401.00908). Format-aware LLM that processes the entire doc with out an upstream parser tier. Identical household as ColPali.
- Kim et al., OCR-free Doc Understanding Transformer (Donut), ECCV 2022 (arXiv:2111.15664). Finish-to-end OCR-free doc understanding; helpful distinction with the OCR-quality-scoring escalation tier the article describes.
















