Course Content
LangGraph Agents
7 sections · 49 lessons
How do you handle node output formats and enforce structured state updates?
What you need to know
Python
1from typing import Literal2from pydantic import BaseModel, Field, ValidationError3from langchain_core.exceptions import OutputParserException45class ClaimFields(BaseModel):6 policy_number: str = Field(pattern=r"^POL-\d{6}$")7 amount_inr: int = Field(gt=0, le=5_000_000)8 category: Literal["hospital", "pharmacy", "diagnostics"]910def extract(state: dict) -> dict:11 try:12 f = llm.with_structured_output(ClaimFields).invoke(state["claim_text"])13 return {"fields": f.model_dump(), "error": None}14 except (ValidationError, OutputParserException) as e:15 return {"error": str(e), "attempts": state["attempts"] + 1}1617def after_extract(state: dict) -> str:18 if state["error"] is None:19 return "score"20 return "repair" if state["attempts"] < 2 else "human_review"What happens at each layer
- The model —
with_structured_outputsends the Pydantic schema to the provider (tool calling or native JSON schema mode) and parses the reply into aClaimFieldsobject. - The node — validation errors become data in state, not a crashed run.
- The edge — reads
errorandattemptsand picksscore,repairor a human.
Three facts about node returns
- Only returned keys change. Everything else keeps its value.
- Keys that are not in the schema are silently ignored — a typo loses data without an error. Unit-test node outputs.
- With a Pydantic state schema, a wrong type written by one node fails validation when the next node reads it.
Keep types apart
messages— for the model to read.fields,documents— artifacts for other nodes.route,error,attempts— decisions for edges.
A real-life example
A claim-intake graph extracts fields from photos of hospital bills. About 6% of bills have a blurred policy number, so the model returns "POL-12345" (five digits). The Pydantic pattern rejects it; the node writes the error; the edge sends it to repair, which re-prompts with the exact error and a crop of the header area. Half of those are fixed on the second try; the rest go to human_review after two attempts. Before this design, bad policy numbers reached the database and failed a day later at payout.
Follow-up questions to expect
- "Structured output or JSON mode?" — Structured output with a schema. JSON mode only guarantees valid JSON, not your fields and types.
- "Why not just raise and let retry handle it?" — A retry policy repeats the same call. A validation failure needs a different prompt with the error in it, so it is a route, not a retry.
- "How does
create_agentdo this?" — Passresponse_format=YourModel; the final structured result appears understructured_responsein the output.