**Adaptive Parsing for Enterprise RAG: A Cheap‑First Cascade**
Modern Retrieval‑Augmented Generation (RAG) pipelines for enterprise documents must handle heterogeneous content: long stretches of plain text, scanned pages, multi‑column layouts, flattened tables, and diagrams. Treating every page identically—either parsing everything eagerly with a heavyweight vision‑LLM or flattening everything with a cheap extractor—leads to either high costs or irrecoverable information loss. This article presents **adaptive parsing**, a two‑phase strategy that starts with a cheap baseline parser and escalates only the pages and regions that truly need deeper understanding.
—
### 1. When Cheap Parsing Isn’t Enough
Adaptive parsing operates in two phases:
1. **Initialization**: Choose a baseline parser per document type (native PDFs → PyMuPDF; scans → free OCR).
2. **Cascade evaluation**: Run cheap, deterministic checks on the parser output. Escalate a page only when a check signals that the cheap parse missed critical content.
The goal is to keep the majority of pages on the cheap baseline while surgically applying heavier parsers (Azure Layout, Docling, or vision LLMs) only where needed. Two recurring failure cases on the Attention paper illustrate the problem:
– **Flattened tables** (e.g., Table 3 on page 9): PyMuPDF outputs each cell as an independent line, destroying column structure. An LLM cannot reliably pair row and column labels.
– **Opaque figures** (e.g., Figure 1 on page 3): The figure body is rendered as an image with no embedded text. The parser returns empty for the diagram content.
Instead of parsing every page deeply, we detect these patterns early and route only affected pages to more powerful parsers.
—
### 2. A Cascade of Evaluation Checks
The cascade is organized across four RAG bricks:
– Document parsing
– Question parsing
– Retrieval
– Generation
Each brick owns one or more **evaluation checks** that are deterministic, low‑cost, and capable of triggering escalation. The earlier a check fires, the cheaper the outcome.
#### 2.1 Document Parsing Checks (Free Deterministic)
These operate directly on PyMuPDF or OCR outputs and require no LLM calls.
1. **Pre‑parsing metadata (check 1)**: per‑page character count and embedded image count. Pages with low text density plus images likely contain diagrams or figures.
2. **Parsing‑time fingerprints (check 2)**: flat‑table signatures and empty figure bounding boxes derived from `line_df`.
3. **Post‑parsing chunk integrity (check 3)**: coarse-grained checks on sentence boundaries and table‑row alignment across chunks.
#### 2.2 Question Parsing Checks
– **Intent routing (check 4)**: a small LLM call on the question classifies whether the intent is table‑oriented, figure‑oriented, or prose. This routes downstream processing early.
– **User feedback loop (check 9)**: human corrections refine the classifier over time.
#### 2.3 Retrieval Checks
– **Score gap and source diversity (check 5)**: measures ambiguity in vector similarity.
– **Context assembly (check 6)**: detects gaps or contradictions across retrieved chunks before generation.
#### 2.4 Generation Checks
– **LLM self‑evaluation and groundedness**: a final safety net that catches failures missed earlier.
—
### 3. Concrete Detection Examples
### 3.1 Check 1: Pre‑Parsing Signals
Using only metadata from PyMuPDF:
“`python
pre = pre_parse_signals(“paper.pdf”)
pre[“pages”].head()
# page_num char_count image_count image_bboxes
# 1 2858 0 []
# 3 1827 1 [(196.6, 72.0, 415.4, 394.4)] # Figure 1
“`
Page 3 is flagged immediately because its character count is below average and it contains an image.
### 3.2 Check 2: Flat‑Table Fingerprint (Table 3)
On pages with tables, PyMuPDF often outputs cells as isolated lines:
“`python
line_df.loc[page == 9]
# Each cell on its own line with narrow bounding boxes
# after a “Table 3: …” caption
“`
A lightweight aggregation counts narrow, short lines following a caption. When the fingerprint triggers, the page is routed to a layout‑aware parser (e.g., Azure or Docling).
### 3.3 Check 2: Opaque Figure (Figure 1)
PyMuPDF can report image bounding boxes:
“`python
images = page.get_image_info()
# Bbox exists, but text inside that bbox is empty
“`
If text extraction inside the image bounding box returns near‑zero characters, the page is flagged for vision‑LLM treatment.
—
### 4. Data Model and Escalation Mechanics
A single new column, `parsing_method`, ties everything together:
– `line_df` and `page_df` carry `parsing_method` per row.
– Escalation **adds new rows** rather than overwriting, preserving auditability.
– Downstream bricks read `line_df` and see a uniform schema regardless of origin.
Example enrichment function:
“`python
def enrich(pdf_path, line_df, page_df, pages, method):
new_lines = parse_with_method(pdf_path, pages, method)
line_df = pd.concat([line_df, new_lines], ignore_index=True)
page_df = append_page_rows(page_df, pages, method)
return line_df, page_df
“`
—
### 5. Embedding Granularity: Page‑Level vs Line‑Level
Adaptive parsing extends to vectorization:
– **Default (page‑level)**: One vector per page; cheap, scalable, sufficient for most contracts.
– **Escalation (line‑level)**: Only for top‑K ambiguous pages; denser vectors but bounded cost.
The cascade decides *when* to escalate granularity based on retrieval confidence and score gaps, avoiding a full line‑level index for the entire corpus.
—
### 6. Conclusion
Adaptive parsing transforms document understanding from a one‑size‑fits‑all problem into a **cascade of cheap checks** that progressively refine parsing only where necessary:
– Start with a fast baseline parser.
– Use deterministic, low‑cost checks to flag problematic pages.
– Escalate selectively—by page and by region—to heavier parsers.
– Maintain an auditable `parsing_method` trail for transparency.
The most expensive parsers are invoked only when cheaper methods have already proven insufficient, dramatically reducing cost and latency while preserving answer quality. The next article in this series will walk these escalations end‑to‑end with real tables and diagrams.



