Skip to course content
Free LangChain course

LangChain for LLM Applications and RAG

Unit 03.02: Validating before you trust

Parsing gets you a dict. Validation gets you a dict you can rely on, and the gap between the two is where most structured-output bugs live.

Types, allowed values, and patterns

A Pydantic model with Literal types and a field pattern rejects three things a parser accepts happily.

The code validates four candidates.

from pydantic import BaseModel, Field, ValidationError
from typing import Literal


class Triage(BaseModel):
    category: Literal["billing", "technical", "account", "other"]
    urgency: Literal["low", "medium", "high"]
    account_id: str = Field(pattern=r"^ACC-\d+$")


CANDIDATES = [
    {"category": "billing", "urgency": "low", "account_id": "ACC-1187"},
    {"category": "Billing Dept", "urgency": "low", "account_id": "ACC-1187"},
    {"category": "billing", "urgency": "low", "account_id": "1187"},
    {"category": "billing", "account_id": "ACC-1187"},
]
for raw in CANDIDATES:
    try:
        print(f"OK    {Triage(**raw)}")
    except ValidationError as exc:
        first = exc.errors()[0]
        print(f"FAIL  {str(raw)[:52]:54} {first['loc'][0]}: {first['type']}")

# Parsing gets you a dict. Validation gets you a dict you can rely on, and the
# three failures above -- a value outside the allowed set, a malformed id, a
# missing field -- are all things a parser accepts happily.

Each failure is a different kind. "Billing Dept" is a plausible label outside the allowed set - exactly what model drift looks like. "1187" is a well-formed string in the wrong format. The fourth is simply missing a required field.

A parser accepts all three, and the error surfaces later, somewhere else, as a lookup that found nothing or a branch that took the default.

The mistake this prevents

Two details about schema design earn their keep here. Field descriptions are shown to the model, so Field(description="the support category this ticket belongs to") produces more consistent content than a bare field named category - a vague description is a vague instruction. And make fields optional only when the value genuinely may not exist: an optional field silently accepts an omission, so marking something optional for convenience removes the check that would have caught the model leaving it out.

The mistake is validating with if "category" in result. It catches the missing field and neither of the others, and it grows into a page of hand-written checks that drift out of sync with what downstream code expects. Declare the schema once.

Takeaway

Validate with a schema that constrains types, allowed values and formats. Parsing succeeding tells you the shape was JSON, not that the content is usable.