Structured Extraction
Turn a pile of unstructured documents into validated records — structured output run in a batch loop, with a place to put the ones that don't validate.
from pydantic import BaseModel, Field, ValidationError
from langchain.chat_models import init_chat_model
class Invoice(BaseModel):
"""Key fields extracted from an invoice."""
vendor: str = Field(description="Name of the vendor issuing the invoice")
invoice_number: str = Field(description="The invoice's unique identifier")
total_amount: float = Field(description="Total amount due, in USD")
due_date: str = Field(description="Due date, ISO 8601 format")
model = init_chat_model("gpt-4o-mini", model_provider="openai")
extractor = model.with_structured_output(Invoice, include_raw=True)
documents = [
"Invoice #INV-2024-001 from Acme Supplies, due 2024-03-15, total $1,240.00",
"Invoice #INV-2024-002 from Widget Co, due 2024-03-20, total $890.50",
]
extracted: list[Invoice] = []
failed: list[dict] = []
for doc in documents:
result = extractor.invoke(f"Extract the invoice fields from:\n\n{doc}")
if result["parsing_error"] is not None:
failed.append({"document": doc, "error": str(result["parsing_error"])})
continue
extracted.append(result["parsed"])
print(f"Extracted {len(extracted)}, failed {len(failed)}")
include_raw=True is what makes the failure path possible — without it, a parsing failure raises instead of giving you a parsing_error field to branch on. Route failures to a dead-letter list (or queue) rather than letting one bad document stop the batch.
A record that validates isn't necessarily correct — the schema enforces shape, not truth. total_amount: 0.0 and vendor: "Unknown" both satisfy the schema while being useless. For a real pipeline, add a validator (Pydantic field_validator) that rejects obviously-placeholder values, or spot-check a sample against the source documents before trusting the batch.
See also
- Structured Output — the
with_structured_outputmechanics this batch loop wraps. - Async and Batching — running this loop with
abatchinstead of a sequentialfor.