Document Data Extraction with Multimodal LLMs

Photo by jurvetson on flickr
Traditional OCR converts a page into a flat string and discards layout, so a total in a table cell looks the same as a stray page number. Multimodal LLMs reason over the rendered image directly, keeping column alignment, table borders, and font weight available as evidence. This makes them far more robust on rotated scans, multi-column layouts, and handwritten annotations that regularly break template-based OCR pipelines.
Define a strict JSON schema up front and force the model to populate it through a tool call rather than asking it to describe the document in prose. This turns extraction into a typed function call where the model either returns a value matching the schema or does not return at all, removing an entire category of downstream parsing bugs caused by inconsistent free-text formatting.
Grounding means the model returns a bounding box or page region alongside each extracted field, not just the raw value. A review interface can then highlight exactly where on the source page a total or vendor name came from, turning a multi-minute manual re-read of a whole document into a two-second glance at one highlighted region. It is essential for making extracted data auditable.
Only for a subset of records. Asking the model to self-report a per-field confidence score lets a pipeline auto-approve documents where every required field is high-confidence and passes basic validation, while routing anything uncertain to a human review queue. Teams that adopt this pattern typically auto-approve most well-formed documents while still catching genuinely ambiguous cases.
Track accuracy per field against a held-out set of documents a human has already labeled, rather than relying on a single aggregate accuracy number. A pipeline can be excellent on vendor names but weak on line-item totals, and each failure mode needs a different fix. Re-run this evaluation whenever the prompt, schema, or model version changes, since an upgrade that improves one field can regress another.

Photo by jurvetson on flickr
Key Takeaway
Multimodal LLMs replace brittle per-vendor OCR templates with a single schema-constrained tool call: force structured JSON output, ground every value to its location on the page, expose per-field confidence scores, and route only low-confidence fields to human reviewers so extraction pipelines scale without becoming a manual bottleneck.
For a decade, extracting structured data from documents meant training a specialized OCR pipeline, hand-tuning template matchers, and accepting that anything outside the training distribution would silently fail. Multimodal large language models changed the economics of this problem. A model that can read a page the way a person does, layout, tables, handwriting, stamps, and all, can go from an unseen invoice format to a validated JSON record in a single API call, with no per-vendor template to maintain.
This post walks through the practical side of building that pipeline: how to frame the extraction task as a schema-constrained tool call rather than free-form prose, how to ground every extracted value in a location on the page so a reviewer can verify it in seconds, how to expose model confidence rather than hide it, and how to design the human review step so it scales instead of becoming a bottleneck.
Traditional OCR converts pixels to a flat string of characters and discards the two-dimensional layout that gave those characters meaning. A total that sits in the bottom-right cell of a table looks identical, as a string, to a stray page number. Multimodal LLMs instead reason over the rendered image directly, so column alignment, cell borders, indentation, and font weight all remain available as evidence. That matters most on the documents that break classic pipelines: rotated scans, multi-column layouts, handwritten annotations over printed text, and stamps or signatures overlapping a field.
Start with the highest-resolution scan you can get. Multimodal LLMs process documents as images internally, so a blurry 150 dpi scan genuinely loses information the model cannot recover, no matter how good the prompt is.
The single biggest reliability gain in production extraction pipelines does not come from a clever prompt, it comes from refusing to accept free-form prose as the output format at all. Instead of asking the model to describe an invoice and then parsing that description with regular expressions, define a strict JSON schema up front and force the model to populate it through a tool call. This turns extraction into a typed function call: the model either returns a value that satisfies the schema or it does not return at all, which eliminates an entire category of downstream parsing bugs.
// invoice-extraction-tool.ts
const invoiceTool = {
name: "record_invoice_fields",
description: "Record extracted invoice fields with page-relative bounding boxes",
input_schema: {
type: "object",
properties: {
vendor_name: { type: "string" },
invoice_number: { type: "string" },
invoice_date: { type: "string", format: "date" },
total_amount: { type: "number" },
currency: { type: "string" },
line_items: {
type: "array",
items: {
type: "object",
properties: {
description: { type: "string" },
quantity: { type: "number" },
unit_price: { type: "number" },
bbox: {
type: "array",
items: { type: "number" },
minItems: 4,
maxItems: 4,
},
},
required: ["description", "unit_price"],
},
},
confidence: {
type: "object",
properties: {
vendor_name: { type: "number" },
total_amount: { type: "number" },
},
},
},
required: ["vendor_name", "invoice_number", "total_amount"],
},
}
// Force the model to call this tool instead of replying in prose
const response = await client.messages.create({
model: "claude-opus-4-8",
max_tokens: 2048,
tools: [invoiceTool],
tool_choice: { type: "tool", name: "record_invoice_fields" },
messages: [
{
role: "user",
content: [
{ type: "document", source: { type: "base64", media_type: "application/pdf", data: pdfBase64 } },
{ type: "text", text: "Extract every field defined in the tool schema. If a field is unreadable, omit it rather than guessing." },
],
},
],
})Two details make this reliable in practice. First, mark fields that are genuinely optional as optional in the schema, and instruct the model explicitly to omit a field rather than invent a plausible-looking value when it cannot read something clearly, since a null is far cheaper to catch than a hallucinated number. Second, keep the schema itself in plain business language, a currency field description that says three-letter ISO currency code reads more predictably to the model than a bare enum with no description at all.
A JSON object full of correct-looking numbers is worth very little to a reviewer who cannot verify where each number came from. Grounding closes that gap by having the model return a bounding box, or at minimum a page number and approximate region, alongside every extracted field. When a reviewer opens the record, the interface highlights the exact rectangle on the source image that produced the total amount or the vendor name, turning a five-minute manual re-read of the whole invoice into a two-second glance at one highlighted region.
| Approach | What it grounds | Reviewer effort |
|---|---|---|
| No grounding | Nothing beyond the raw value | Re-read the full document to verify |
| Page number only | Which page the value came from | Scan one page instead of the whole file |
| Bounding box per field | The exact rectangle on the page | Glance at a highlighted region |
| Bounding box plus confidence | Rectangle and how sure the model is | Only low-confidence highlights need a look |
Bounding boxes returned by a language model are approximate, not pixel-perfect coordinates from a layout detector. Treat them as a strong hint for where to look, not as a value you can safely feed into an automated cropping or redaction pipeline without a tolerance margin.
A model that returns a total amount with no indication of certainty forces every field into the same review lane, whether it was crisply printed or smudged under a coffee ring. Asking the model to self-report a confidence level per field, alongside the extracted value, lets a pipeline route records automatically instead of sending every single one to a human. In practice this looks like a short, staged flow.
Teams that adopt confidence-based routing typically see eighty to ninety percent of well-formed documents auto-approve, which means human reviewers spend their time only on the genuinely ambiguous cases, which is exactly where their judgment adds the most value.
The review interface is not an afterthought bolted onto the extraction pipeline, it is the component that determines whether the whole system is trustworthy enough to run unattended most of the time. A good review screen shows the source document image on one side and the editable extracted fields on the other, with the grounded region highlighted the moment a reviewer focuses a field. Corrections should feed back into evaluation, not disappear after the record is approved.
Extraction accuracy is not one number, it is a per-field accuracy rate measured against a held-out set of documents a human has already labeled by hand. Track it per field rather than as a single aggregate, because a pipeline that gets vendor names right ninety-nine percent of the time and line-item totals right eighty percent of the time has a very different failure mode than one where every field sits at ninety percent, and the fix for each is different. Re-run this evaluation whenever you change the prompt, the schema, or the model version, since a version upgrade that improves one field can quietly regress another.
Keep a small, fixed evaluation set of forty to sixty documents that covers your hardest edge cases, rotated scans, handwritten totals, multi-page invoices, and re-run it on every prompt or model change before shipping. It catches regressions a spot check would miss.
Multimodal LLMs do not remove the need for careful engineering around a document extraction pipeline, they change where that engineering effort goes. Instead of maintaining template matchers per vendor, the work shifts to schema design, grounding, confidence-based routing, and an evaluation set that keeps the whole system honest as documents and models both keep changing.