VLM Parsing & Modern OCR

9 min read Module 2 of 9 Topic 5 of 25

What you'll learn

  • Decide which documents justify VLM parsing versus fast local parsing
  • Prompt a VLM to emit structured markdown with tables, not freeform prose
  • Build a confidence-based routing tier so OCR spend concentrates where it pays
  • Recognize and mitigate VLM hallucination risk on numeric and financial data
Building this at your company? For enterprise and company teams taking this to production: book a 30-minute session with our AI engineers for architecture guidance, code review, and a rollout plan for your use case.
Book a Team Session

Lesson 4 handled the 90-something percent of documents with a real embedded text layer, where a fast structural parser does the whole job in milliseconds. This lesson handles the rest: scans, faxes (yes, still, especially in healthcare and legal), photographed whiteboards, stamped invoices, handwritten margins, and PDFs whose layout defeats even a good structural parser. In 2023 this was Tesseract territory, and results on anything but clean printed text were genuinely rough. By 2025, vision-language models rewrote the category almost entirely.

What VLMs changed

Classical OCR answers a narrow question: “what characters are on this page and where are they positioned.” Everything else, columns, tables, reading order, distinguishing a signature from a stamp from a handwritten note, was left as your problem to solve downstream. Vision-language models answer a fundamentally better question: “what does this page say, as a structured document.” They read the page holistically, the way a person would, and can emit markdown with intact tables directly, without a separate structure-reconstruction step. Handwriting, rotation, poor contrast, coffee stains, and mixed languages on the same page degrade them far less than they degraded classical OCR engines, because the model is reasoning about the whole image rather than classifying isolated character glyphs one at a time.

The 2025 wave made this practical at real corpus scale, not just as a demo. Mistral OCR shipped as a dedicated document-understanding API priced per page specifically for this workload. The general frontier models, the Gemini and GPT families and Claude, all became good enough at document images that many teams simply route hard pages to whichever model provider they already have a relationship with, rather than standing up a separate specialized service. Open-weight options, the Qwen-VL derivatives and purpose-built small document models like the DeepSeek-OCR line, cover the case where document text genuinely cannot leave your infrastructure, running on modest local GPU hardware.

Prompt for structure, not prose

The biggest practical lever, and the one teams underuse, is the prompt itself. Ask a VLM “what does this document say?” and you get a chatty summary that loses exactly the structural detail your pipeline needs. Ask it for a transcription contract instead, with explicit rules, and you get pipeline-grade output that slots directly into the same document model from Lesson 4:

# VLM parsing call: the prompt is a transcription CONTRACT,
# not an open question. Works with any multimodal API.

PARSE_PROMPT = """Transcribe this document page to markdown.

Rules:
- Preserve the reading order a careful human would use.
- Render every table as a markdown table. Never summarize
  or omit rows. Keep cell values exactly as printed.
- Mark headings with #, ##, ### matching their visual level.
- Transcribe handwriting where legible. If a word is not
  legible, write [illegible], never a guess.
- Do not add commentary, descriptions, or summaries.
- If part of the page is a photo or diagram, insert
  [figure: one-line description] at its position."""

async def vlm_parse_page(page_image: bytes) -> str:
    response = await client.messages.create(
        model="claude-sonnet-5",
        max_tokens=4096,
        messages=[{
            "role": "user",
            "content": [
                {"type": "image", "source": {
                    "type": "base64",
                    "media_type": "image/png",
                    "data": b64(page_image),
                }},
                {"type": "text", "text": PARSE_PROMPT},
            ],
        }],
    )
    return response.content[0].text

The [illegible] rule is doing quiet, important work that’s easy to overlook. VLMs are fundamentally generative models: faced with ambiguous pixels, they emit the most plausible text rather than admitting uncertainty, exactly as a language model completes a sentence with the most likely next words. A plausible wrong invoice total is more dangerous to your downstream users than an obviously broken OCR artifact, because a broken artifact gets caught and questioned while a plausible wrong number gets trusted and acted on. Giving the model explicit, prompted permission to say “I don’t know” for a specific word measurably reduces this failure mode in practice.

Route by cost tier

A frontier VLM pass costs real money and real seconds per page, both of which add up fast at corpus scale. Docling’s structural parse from Lesson 4 costs milliseconds and effectively nothing. The production pattern is a routing tier that spends the expensive path only where it actually pays for itself, keeping the vast majority of your corpus on the cheap path:

flowchart TD
    IN["Incoming document"] --> T{"Has embedded\ntext layer?"}
    T -- "Yes" --> DL["Docling structural parse\n(fast, local, free)"]
    DL --> C{"Layout confidence OK?\nTables plausible?"}
    C -- "Yes" --> OUT["Normalized ParsedDoc"]
    C -- "No" --> VLM["VLM parse\n(per-page, paid)"]
    T -- "No (scan/photo)" --> VLM
    VLM --> HS{"High-stakes doc with\nlow-confidence fields?"}
    HS -- "Yes" --> REV["Human review queue"]
    HS -- "No" --> OUT
    REV --> OUT

    style IN fill:#EEF0F7,stroke:#6366F1,color:#0F172A
    style T fill:#fff7ed,stroke:#f59e0b,color:#0F172A
    style DL fill:#f0fdf9,stroke:#0D9488,color:#0F172A
    style C fill:#fff7ed,stroke:#f59e0b,color:#0F172A
    style VLM fill:#EEF0F7,stroke:#6366F1,color:#0F172A
    style HS fill:#fff7ed,stroke:#f59e0b,color:#0F172A
    style REV fill:#fff7ed,stroke:#f59e0b,color:#0F172A
    style OUT fill:#f0fdf9,stroke:#0D9488,color:#0F172A

Both branches emit the same ParsedDoc schema defined in Lesson 4. This is a deliberate architectural choice, not a coincidence: downstream stages, chunking, embedding, indexing, never know or care which parsing path a given page took, which means you can change the routing thresholds or swap VLM providers without touching a single line of Module 3’s code. In a typical corporate corpus, somewhere between 4 and 10 percent of pages end up on the VLM branch, which keeps per-corpus parsing cost in the hundreds of dollars rather than the tens of thousands a naive “VLM everything” approach would run up.

Two practical notes from the trenches

Resolution is a real cost dial, not a free quality knob. Render pages at roughly 150 to 200 DPI for VLM input. Below that range, small fonts start to smear and even a strong VLM begins to misread digits and punctuation. Above it, you pay meaningfully more in image tokens for detail the model’s vision encoder generally cannot use any better, since transcription accuracy on real documents tends to plateau well before you reach print-quality resolutions. Test this on a sample of your own hardest documents (dense tables, small print) rather than trusting a single blanket setting.

Batch APIs fit ingestion perfectly, and using the synchronous endpoint here is leaving money on the table. Most providers discount asynchronous batch processing by roughly half versus their synchronous per-token pricing, and nothing on the ingestion plane, as established in Lesson 2, has a latency deadline. Submitting a batch job for a backlog of scanned documents and collecting results over the following hours costs meaningfully less than looping through the same documents with synchronous calls, for identical output quality.

With clean, structured, correctly routed documents now flowing out of parsing in both its fast and expensive forms, the next problem is keeping that pipeline fed with current data as source documents keep changing underneath you, which is exactly where Lesson 6 picks up.

Knowledge Check

4 questions to test your understanding

1 What changed between classical OCR (Tesseract-era) and VLM-based parsing?

2 Your VLM parser occasionally 'fixes' a blurry total from 1,432 to 1,482 with full confidence. What is the standard mitigation?

3 A batch of 50,000 scanned invoices needs VLM parsing. What is the correct API usage pattern?

4 Why does rendering scanned pages at roughly 150-200 DPI for VLM input outperform both lower and much higher resolutions?

Discussion

Questions and notes from learners on this topic

Loading discussion…

Go further with expert guidance

Ready to build production AI?
Talk to our R&D team.

These courses give you the foundation. Our embedded AI teams take you from prototype to production in 30–90 days, with your team, your codebase, your goals. Book a free strategy call to see how we can accelerate your AI initiative.

30 minutes · No obligation · Expert AI engineers, not sales reps

AI Architecture Review

Audit your current stack and identify high-impact improvements

Project Review

Get expert feedback on your AI implementation and codebase

Team Mentoring

Upskill your engineers with hands-on AI coaching sessions

AI Strategy

Define your AI roadmap, prioritization, and implementation plan