07-agent-systems

Agent Frameworks: LangGraph & PydanticAI

When a hand-rolled ReAct loop stops being enough, and what graph-based orchestration and type-safe agent frameworks actually buy you over it.

August 14, 2026
langgraphpydanticaiorchestrationagentsgen-ai

Where the Hand-Rolled Loop Starts to Strain

The refund agent's hand-rolled ReAct loop — a while-loop calling the model, parsing the requested action, executing it, feeding the result back — works fine for the eligibility-check-then-refund task from the last guide. Then requirements grow: refunds over $500 need a human approval step before issue_refund actually runs, the workflow needs to survive a server restart mid-approval without losing its place, and the branching logic (eligible / ineligible / needs-exception-review / needs-approval) is turning the loop's body into a tangle of conditionals that's getting harder to reason about with every new case. None of this is impossible to hand-roll — it's that hand-rolling it well means slowly reinventing pieces of two separate concerns: state management and control flow.

LangGraph: Agents as Explicit State Machines

LangGraph models an agent as a graph: nodes are steps (an LLM call, a tool execution, a human-approval gate), and edges define transitions between them — including conditional edges, where the next node depends on the state so far. The refund workflow becomes an explicit graph instead of implicit if/else branching buried in a loop body:

The two things this buys over the hand-rolled version are the ones that were actually getting painful: built-in state persistence (the graph's state checkpoints automatically, so await_human_approval can genuinely pause — surviving a restart — and resume exactly where it left off once a human responds, which the next phase covers in more depth), and explicit, inspectable control flow (the branching logic lives in the graph's edges, not scattered through conditional statements, which makes the eligible/ineligible/needs-approval paths something you can look at rather than trace through code).

PydanticAI: Agents That Take Type Safety Seriously

PydanticAI starts from a different problem: Phase 2 covered getting structured, schema-validated output from a single model call, but a multi-step agent has many such calls chained together, each one a place where an unvalidated or malformed value can silently corrupt everything downstream. PydanticAI extends the structured-output discipline through the entire agent — tool inputs, tool outputs, and the agent's final result are all defined and validated as Pydantic models end to end, not just the last response. For the refund agent, that means the eligibility check's output, the refund amount calculation, and the payment API call's parameters are each independently type-checked, so a malformed amount can't silently slip from one step into the next and only surface once money has actually moved.

python
class RefundDecision(BaseModel):
    eligible: bool
    amount_cents: int = Field(ge=0)
    reason: str
 
class RefundResult(BaseModel):
    order_id: str
    decision: RefundDecision
    confirmation_id: str | None

Two Different Philosophies, Not Competing Products

LangGraphPydanticAI
Core abstractionGraph of nodes and edgesType-validated agent loop
Strongest forComplex branching, persistence, human-in-the-loop pausesCorrectness of data flowing between steps
Mental modelState machineStrongly-typed function pipeline

These aren't mutually exclusive concerns — a production refund agent benefits from both explicit control flow and validated data at every step, and it's common to see LangGraph's orchestration used alongside Pydantic models defining the shape of data at each node, rather than treating the two frameworks as an either/or choice.

Check yourself

The refund agent's hand-rolled loop needs to pause for human approval on refunds over $500 and reliably resume — even across a server restart — once approved. What does a framework like LangGraph provide here that a hand-rolled while-loop doesn't have by default?


What's Next

A framework handles orchestration for one agent well. The refund agent is about to become one piece of something larger — a broader customer-operations system that also handles shipping and account questions — and cramming all of that into a single agent's toolset and system prompt creates its own new problems.

Frequently asked questions

Do I need a framework for every agent, even a simple one?

No — a single tool call, or even a short, bounded ReAct loop with two or three steps and no need to pause or persist state, is often simpler to hand-roll than to wire into a framework's abstractions. Frameworks earn their complexity specifically once you need persistence, non-trivial branching, or human-in-the-loop pauses, which is exactly the point where the refund agent in this guide needed one.

Can LangGraph and PydanticAI be used together in the same system?

Yes — they solve different layers of the same problem (control flow versus data validation), and using LangGraph's graph orchestration with Pydantic models defining each node's inputs and outputs is a common, complementary combination rather than a conflict between competing frameworks.

Does using an agent framework eliminate the need to understand ReAct from the last guide?

No — frameworks like LangGraph implement orchestration patterns that often include a ReAct-style reasoning loop internally, or make it straightforward to build one as part of a graph node. Understanding the underlying pattern is what lets you use the framework's abstractions deliberately instead of treating them as a black box.