LangGraph tutorial: build cyclic agents with state

Blog 11 min read

LangChain Inc. Released LangGraph in 2024. It solves a specific problem: standard linear chains cannot handle stateful, cyclic agent workflows. This framework replaces simple request-response patterns with a graph architecture where State objects preserve conversation history across complex node interactions.

Instead of a straight line, the library uses graph data structures. Agents loop back for self-correction or branch conditionally based on real-time data, and the add_messages reducer appends each turn to shared state instead of overwriting it. What follows walks the architecture of one worked example rather than its code, a resume and job matching agent that pairs stored resume data with job listings fetched by a dedicated Tools node, and shows where conditional routing logic breaks when a node returns only part of the state.

LangGraph as a Stateful Orchestration Framework for Cyclic AI Workflows

LangGraph as a Cyclic Directed Graph Framework

Linear execution chains hit a wall when an agent needs to think, check its work, and try again. LangGraph replaces that linear constraint with a cyclic directed graph. This shift enables iterative processing loops, allowing agents to perform self-correction and multi-step tool use that standard chains forbid.

The core is the State object, typically defined via Python's TypedDict. It carries context as it passes between nodes. Unlike transient query models, this design ensures conversation history and variables persist. A checkpointer mechanism saves state after every step. This prevents data loss during long-running agents or human-in-the-loop interrupts.

Feature Linear Chains LangGraph Cycles
Flow Control Unidirectional Bidirectional / Loops
State Scope Transient Persistent via TypedDict
Execution Sequential Conditional / Iterative

Explicit state definition creates a constraint. Developers gain granular control over execution flow but must manually define the schema for every data exchange. This increases initial setup complexity compared to higher-level abstractions that hide state management. Omitting this rigor leads to agents that lose context or repeat work in production. The industry is pivoting toward these resilient, stateful agents. Builders must weigh the engineering overhead of strict schemas against the operational stability of persistent, checkpointed workflows.

Substantial enterprises including Klarna, Uber, and J.P. Morgan trust this approach for building production-grade agents that handle complex workflows. These organizations use the shared state model to coordinate multi-agent systems where multiple specialized nodes read and write to a single source of truth.

Relying on shared state introduces coupling. If one node corrupts the State object, downstream nodes fail unpredictably. Builders must define strict schemas using TypedDict to prevent type errors during runtime. The cost of this durability is increased storage usage, as every step writes to disk rather than holding data in memory. For AI Agents News readers, understanding this limitation is vital when designing systems that require both durability and strict data consistency.

Low-Level Orchestration Control vs Higher-Level Abstractions

LangGraph operates as a low-level orchestration framework. It distinguishes itself from higher-level abstractions by providing direct control over state and execution flow. Think of it as a "full control panel" compared to simpler flowchart capabilities. It enables complex feedback loops that linear chains cannot support. Developers must decide if their use case requires this granular management or if a standard linear workflow suffices.

Feature Low-Level Orchestration Higher-Level Abstraction
Control Direct state manipulation Implicit context handling
Flow Cyclic directed graphs Linear chains
Complexity High engineering overhead Rapid prototyping
Use Case Custom multi-agent loops Simple Q&A tasks

Flexibility demands increased engineering labor for initial setup and maintenance. Higher-level tools accelerate generic tasks yet often fail when bespoke logic dictates the information flow. Organizations requiring precise tool usage coordination benefit from the explicit graph structure. Simple query-response bots may not justify the complexity. The decision rests on whether the application demands iterative refinement or straightforward execution.

Architectural Mechanics of Nodes Edges and Conditional Routing Logic

Python Units as Graph Nodes in LangGraph

A LangGraph node operates as a discrete Python unit accepting the global State and returning specific updates. The State object holds application context, storing conversation messages alongside necessary variables.

These atomic execution blocks change raw functions into graph-aware components. They adhere to a strict input-output contract where the State object passes between them. Static pipeline steps simply process data. These units modify shared context, such as appending conversation history via the add_messages reducer.

Flexible Routing via Conditional Edges and State Changes

Conditional edges evaluate the current State object to determine the next execution path. They act as decision points within the workflow. Unlike Normal Edges that connect nodes sequentially, these flexible pathways inspect message content or variables to route logic branches. This mechanism prevents rigid linear chains by allowing the graph to loop back for refinement or jump to specialized tools based on real-time data.

The routing function receives the full State dictionary and returns a string identifier for the target node. This approach addresses specific architectural gaps found in previous components like LCEL and AgentExecutor, enabling more complex agent behaviors.

Feature Normal Edge Conditional Edge
Logic Static sequence Flexible evaluation
Input None Current State
Output Fixed next node Variable node ID

Nodes sometimes return partial state updates. This leaves the routing function without the necessary context to make a valid decision. The framework requires that every possible return value from the conditional function maps to an existing node or the END terminal. This strict binding ensures the agent lifecycle remains predictable even with complex branching. Designing state schemas with explicit routing flags simplifies these conditional checks.

State Persistence Failures and Checkpointing Risks

LangGraph is architected specifically for long-running agents that maintain context over extended periods and multiple interaction turns. When a Conditional Edge routes execution based on stale data, the graph may enter invalid states if the State object was not correctly updated by the preceding node. This failure mode can break human-in-the-loop interrupts, as the system cannot reconstruct the context required to resume paused tasks.

By enabling long-running agents, the framework shifts cost considerations from simple token usage to sustained state management and memory storage, which impacts cloud storage and database costs.

Failure Mode Root Cause Operational Impact
Context Loss Checkpoint write lag Agent repeats prior steps
Routing Error Stale state evaluation Infinite loops or dead ends
Interrupt Break Missing persistence Human feedback ignored

The promise of persistent state remains theoretical during load spikes without proper configuration. Implementing strong error handling within node functions helps guarantee durability and predictable behavior.

Constructing a Resume and Job Matching Agent with Integrated Data Sources

Defining Pydantic Models for Resume and Job Data Structures

Creating Pydantic models inside a dedicated modules folder sets the rigid schema needed for stateful agent orchestration. Developers create a resume.py file to hold classes like WorkExperience, Education, and Resume, which standardize input validation before data enters the graph. The Resume class includes a mock method returning sample data for a user named Jeff, featuring roles at Company X and Company Y alongside specific language proficiencies.

Parallel definitions in job.py structure employment opportunities. Salary fields might span from $100,000 to $120,000 to test numeric filtering logic. This approach converts unstructured text into typed objects so the shared state schema remains consistent across nodes. Static mock objects limit the agent to predefined scenarios until flexible parsing for PDFs or web scrapers replaces these fixtures. Conditional edges may fail to route correctly when encountering malformed job requirements or missing education fields without this strict typing. Such structural rigor prevents downstream hallucination by constraining the large language model to verified data points in production systems.

Integrating Expert and Tools Nodes with LangGraph MessagesState

Connecting the Expert node to OpenAI's GPT-4o and the Tools node requires explicit state definitions to preserve context across cycles. Builders define a State schema using MessagesState, which mandates that every function returns an updated dictionary containing the full conversation history. This structure enforces a strict contract where the Expert node processes user queries against retrieved resume data for Jeff, while the Tools node fetches job listings without losing prior interaction details.

This TypedDict-defined state ensures that context flows reliably between the LLM and external data sources, unlike implicit passing in earlier frameworks. The graph executes by passing this shared state object sequentially, allowing the model to reason over specific fields like professional summary or skills. Rigid state schemas increase initial boilerplate code and require precise type alignment between Pydantic models and graph inputs. Graph execution will fail on validation errors if developers do not map tool outputs exactly to state keys. Stability wins over rapid prototyping speed in this trade-off for production systems. The resulting architecture supports complex multi-turn reasoning where the agent iteratively refines job matches based on accumulated context.

Validating State Schemas and Node File Organization

Verify the Pydantic schema in modules/resume.py matches the graph's State definition before execution. Misaligned field names between the Resume model and the shared state cause silent data loss during node transitions. Developers must confirm that the mock method returns typed objects rather than raw dictionaries.

Component Validation Check Failure Mode
State Schema Fields match node outputs Silent attribute errors
File Structure modules/resume.py exists Import paths break
Data Types Lists vs single strings Parsing exceptions

A single type error loops indefinitely in the cyclic nature of LangGraph, consuming resources until the process crashes. Strict initial contracts matter because state persists across iterations in this cyclic flow. Treat the state definition as a binding interface; any deviation breaks the TypedDict-defined state contract required for reliable orchestration. AI Agents News recommends validating these schemas statically to prevent runtime failures in production graphs.

Verifying State Synchronization Before Deployment

Routing failures reduce to one cause: a preceding node returned a partial dictionary or skipped the add_messages reducer, so the conditional function evaluated data that never arrived. The schema itself is what the StateGraph builder receives, which is why every registered node reads and writes the same object; LangGraph documents that contract in its low-level concepts guide. Three checks make it testable before deployment.

  1. Define explicit return structures in every Python function to guarantee full state synchronization.
  2. Verify that nodes write to the shared context correctly before subsequent nodes evaluate the path.
  3. Inspect the intermediate state payload to confirm list appends rather than overwrites during execution.

Graphs re-traverse steps or fail to reach the intended termination point without these checks.

About

Diego Alvarez serves as Developer Advocate at AI Agents News, where he specializes in hands-on build guides and framework comparisons for autonomous systems. His daily work involves constructing and benchmarking multi-agent architectures using libraries like LangGraph, CrewAI, and AutoGen, giving him direct insight into the complexities of state management and orchestration. This practical experience makes him uniquely qualified to explain LangGraph's graph-based approach to controlling agent interactions and information flow. Unlike theoretical overviews, Diego's analysis stems from deploying these tools in real-world scenarios, focusing on reliability, cost implications, and failure modes. At AI Agents News, the team is dedicated to providing engineers with factual, vendor-neutral evaluations of the frameworks powering the agentic web. By connecting deep technical implementation details with clear architectural guidance, Diego helps software engineers and technical founders navigate the evolving environment of AI agent development without the hype.

Conclusion

Structural rigidity in graph definitions often breaks when iterative reasoning demands complex, non-linear loops. Ignoring strict state contracts leads to silent corruption of long-term memory. Context overwrites rather than appends, rendering the agent incapable of multi-step problem solving. As the industry shifts toward production-grade systems, builders must recognize that conditional edges function only as reliable crossroads when preceding nodes guarantee full state synchronization. Implement rigorous validation of intermediate state payloads before deploying any agent that relies on flexible routing logic.

Frequently Asked Questions

The routing function reads the shared State, so a partial dictionary or a skipped add_messages reducer leaves it deciding on stale data. The graph then re-traverses steps or never reaches the END terminal.

A linear chain hands the next step a transient payload; LangGraph hands it a TypedDict State that the checkpointer writes after every step, so the same object survives loops and human-in-the-loop pauses. The cost is storage: durability is paid on disk rather than held in memory.

Yes, the checkpointer mechanism enables pauses for human-in-the-loop interrupts. Agents resume exactly where they stopped, ensuring no data is lost during long tasks.

A normal edge fixes the next node in advance. A conditional edge receives the full State dictionary and returns a string identifier, so the successor is chosen at runtime, and every value that function can return has to map to an existing node or the END terminal.

No, the shared State object carries variables between nodes internally. Storage enters through the checkpointer, which writes each step to disk, so persistence costs storage rather than a lookup on every step.

References