Structured Outputs and Validation
Explore methods to improve AI workflow reliability by enforcing structured outputs and validating data in LangGraph. Learn to handle parsing errors, validate fields, and create fallback routes to maintain smooth agent operations, even when model outputs vary or fail. This lesson guides you through building robust extraction, validation, and routing nodes that safeguard your workflows from failure.
We'll cover the following...
In a chat application, a sloppy model response is a UX problem. The user sees something vague or oddly formatted and moves on. The app continues working.
In a graph workflow, a sloppy model response is a system failure. If a model node returns free-form text when a downstream node expects a JSON object with a category key, that downstream node raises a KeyError. If a routing function expects the category to be one of four known values and the model returns "Feature Request" instead of "feature_request", the routing function falls through to an unhandled case. If a tool node receives a malformed input it cannot parse, the tool call silently fails or throws an exception that stops execution entirely.
The failures cascade. One unpredictable model output can break every node that runs after it.
This is why structure matters more in workflows than in chat. In a workflow, model outputs are not final answers; they are inputs to other nodes. Every downstream node is a consumer of the model’s output, and every consumer expects a specific shape.
Why models drift
Language models are probabilistic. The same prompt with the same input does not always produce the same output. On most runs, a well-written prompt reliably produces the expected format. On some runs, the model adds an explanation before the JSON object. On others, it uses slightly different field names. On rare runs, it produces something entirely unexpected.
The longer the workflow and the more nodes depend on a particular output shape, the more consequential any single drift becomes. We cannot eliminate drift entirely, but we can build a validation layer that catches it before it propagates.
What we are building
We will build a feedback classifier: a workflow that takes raw user feedback text and extracts four structured fields, a category, a sentiment, a priority level, and a one-sentence summary. Those fields then drive routing to a category-specific handler.
The workflow has two layers of protection. First, the extraction node wraps the model call and JSON parsing in a try/except block, writing a parse_error if anything goes wrong. Second, the validation node checks that the extracted fields are present and contain one of the allowed values. If either layer detects a problem, execution routes to a graceful fallback instead of crashing.
State design
The state schema separates the raw input from the extracted fields, and uses parse_error as the signal that travels between the extraction, validation, and routing layers.
from typing import TypedDictclass FeedbackState(TypedDict):# Input — provided at invocation, never modifiedraw_feedback: str# Control — written by extraction and validation nodescategory: str # "bug", "feature_request", "general", or "billing"sentiment: str # "positive", "negative", or "neutral"priority: str # "high", "medium", or "low"parse_error: str # non-empty string if extraction or validation failed# Output — written by extraction and handler nodessummary: strresponse: str
Line 6: The only input field. All extracted fields start as empty strings at invocation.
Lines 9–12: Four control fields extracted from the raw text by the model. Each has a fixed set of allowed values.
Line 13: The error signal. An empty string means all is well. A non-empty string means ...