Advanced LangGraph Workflow Orchestration
Stateful Graph Foundations
Beyond Linear Chains
You're familiar with LangChain's sequential, linear chains. They're great for straightforward tasks where A leads to B, which leads to C. But what happens when you need a workflow that can loop, make decisions, and revisit previous steps? Simple chains fall short.
Chains are linear. LangGraph handles branching, looping, and multi‑turn coordination between specialized agents.
LangGraph shifts the paradigm from a straight line to a flexible graph. It's designed for building stateful, multi-actor applications where the flow of logic is not always predictable. This allows for cycles, conditional branches, and more sophisticated agentic behaviours.
The Heart of the Graph: State
The central concept that unlocks this power is state. Unlike stateless chains that pass outputs from one step to the next, a LangGraph application maintains a single, persistent state object that every part of the graph can read from and write to.
The State is the central concept in LangGraph.
This shared state acts as the single source of truth for the entire workflow. It accumulates information, tracks conversation history, and allows different parts of the graph to coordinate their actions. You define the structure of this state object using Python's TypedDict to create a clear schema. This enforces type safety and makes your workflow easier to understand and debug.
from typing import TypedDict, List
from langchain_core.messages import BaseMessage
# Define the state schema for our graph
class AgentState(TypedDict):
# The 'messages' field will hold the conversation history.
# It's a list of BaseMessage objects.
messages: List[BaseMessage]
In this example, our AgentState has one key, messages, which will store the history of the conversation. Every node in our graph will have access to this list.
Building the Workflow
The StateGraph is the canvas on which you build your application. It orchestrates the nodes (the steps in your workflow) and the edges (the connections between them). The process follows a clear lifecycle: define, add, compile, and invoke.
First, you define the StateGraph and pass it your state schema. This tells the graph what kind of data to expect.
from langgraph.graph import StateGraph
# 1. Define the graph
workflow = StateGraph(AgentState)
Next, you add nodes. A node is a Python function that takes the current state as input and returns a dictionary with updates for that state. LangGraph handles merging this new data into the main state object. You don't replace the state; you simply add to it.
def my_node_function(state: AgentState):
# This function would do some work, like calling an LLM
print("Executing my_node_function")
# It returns a dictionary with the updates to be merged into the state
return {"messages": ["A new message from my_node!"]}
# 2. Add nodes
workflow.add_node("my_node", my_node_function)
Edges define the path of execution. You can create simple connections or complex conditional logic. You also define an entry point. The special END constant signifies that the graph has finished its work for now.
# 3. Add edges
workflow.set_entry_point("my_node")
workflow.add_edge("my_node", END)
Finally, you compile the graph to create an executable object and then invoke it with an initial input.
# 4. Compile
app = workflow.compile()
# 5. Invoke
initial_input = {"messages": ["Initial message"]}
result = app.invoke(initial_input)
print(result)
# Expected output:
# {'messages': ['Initial message', 'A new message from my_node!']}
Merging State with Reducers
You might wonder how the output from my_node_function (["A new message from my_node!"]) was added to the existing ["Initial message"] instead of overwriting it. This is handled by a reducer.
When you define a state field that is a list, like our messages field, LangGraph's default reducer for that field is to append new items. For the messages: List[BaseMessage] field, this is often done explicitly using add_messages from langgraph.prebuilt. This function concatenates the lists, preserving the history.
This behavior is fundamental. Because state is updated additively rather than being replaced, the graph can loop back on itself, gather more information in each cycle, and build up a rich context over time—a capability at the core of building complex, reasoning agents.
What is the primary advantage of LangGraph compared to LangChain's sequential chains?
In LangGraph, what is the central concept that enables complex, cyclical agent behaviours?