$ init portfolio
R
Back to Blog
LangGraphAI AgentsLangChain

LangGraph: Building Stateful AI Workflows

How to build complex, stateful AI applications using LangGraph's graph-based approach.

Raj Tiwari
Raj Tiwari
2026-07-085 min read
LangGraph: Building Stateful AI Workflows

Why LangGraph?

LangChain gives you chains. LangGraph gives you graphs. This means you can build:

  • Loops (agent retries)
  • Branching (conditional logic)
  • Human-in-the-loop (approval steps)
  • State management (persistent memory)

Basic Graph: A Simple Agent

python
class="text-purple-400 font-medium">from langgraph.graph class="text-purple-400 font-medium">import StateGraph, MessagesState, START, END
class="text-purple-400 font-medium">from langchain_openai class="text-purple-400 font-medium">import ChatOpenAI

llm = ChatOpenAI(model=class="text-emerald-class="text-amber-300">400">"gpt-class="text-amber-300">4")

class=class="text-emerald-class="text-amber-300">400">"text-gray-class="text-amber-300">500 italic"># Define a node
class="text-purple-400 font-medium">def chatbot(state: MessagesState):
    return {class="text-emerald-class="text-amber-300">400">"messages": [llm.invoke(state[class="text-emerald-class="text-amber-300">400">"messages"])]}

class=class="text-emerald-class="text-amber-300">400">"text-gray-class="text-amber-300">500 italic"># Build the graph
graph = StateGraph(MessagesState)
graph.add_node(class="text-emerald-class="text-amber-300">400">"chatbot", chatbot)
graph.add_edge(START, class="text-emerald-class="text-amber-300">400">"chatbot")
graph.add_edge(class="text-emerald-class="text-amber-300">400">"chatbot", END)

app = graph.compile()

class=class="text-emerald-class="text-amber-300">400">"text-gray-class="text-amber-300">500 italic"># Run it
result = app.invoke({class="text-emerald-class="text-amber-300">400">"messages": [(class="text-emerald-class="text-amber-300">400">"user", class="text-emerald-class="text-amber-300">400">"What is AI?")]})
print(result[class="text-emerald-class="text-amber-300">400">"messages"][-class="text-amber-300">1].content)

Adding Tools

python
class="text-purple-400 font-medium">from langgraph.prebuilt class="text-purple-400 font-medium">import ToolNode
class="text-purple-400 font-medium">from langchain_core.tools class="text-purple-400 font-medium">import tool

class="text-amber-class="text-amber-300">400">@tool
class="text-purple-400 font-medium">def get_weather(city: str) -> str:
    class="text-emerald-class="text-amber-300">400">""class="text-emerald-class="text-amber-300">400">"Get current weather for a city."class="text-emerald-class="text-amber-300">400">""
    return class="text-emerald-class="text-amber-300">400">f"Weather in {city}: class="text-amber-300">25°C, Sunny"

tools = [get_weather]
llm_with_tools = llm.bind_tools(tools)

class="text-purple-400 font-medium">def agent(state: MessagesState):
    response = llm_with_tools.invoke(state[class="text-emerald-class="text-amber-300">400">"messages"])
    return {class="text-emerald-class="text-amber-300">400">"messages": [response]}

class="text-purple-400 font-medium">def should_use_tool(state: MessagesState):
    last_message = state[class="text-emerald-class="text-amber-300">400">"messages"][-class="text-amber-300">1]
    if hasattr(last_message, class="text-emerald-class="text-amber-300">400">"tool_calls") and last_message.tool_calls:
        return class="text-emerald-class="text-amber-300">400">"tools"
    return END

class=class="text-emerald-class="text-amber-300">400">"text-gray-class="text-amber-300">500 italic"># Build graph with tool routing
graph = StateGraph(MessagesState)
graph.add_node(class="text-emerald-class="text-amber-300">400">"agent", agent)
graph.add_node(class="text-emerald-class="text-amber-300">400">"tools", ToolNode(tools))
graph.add_edge(START, class="text-emerald-class="text-amber-300">400">"agent")
graph.add_conditional_edges(class="text-emerald-class="text-amber-300">400">"agent", should_use_tool)
graph.add_edge(class="text-emerald-class="text-amber-300">400">"tools", class="text-emerald-class="text-amber-300">400">"agent")

app = graph.compile()

Adding Memory with Checkpointer

python
class="text-purple-400 font-medium">from langgraph.checkpoint.memory class="text-purple-400 font-medium">import MemorySaver

memory = MemorySaver()
app = graph.compile(checkpointer=memory)

class=class="text-emerald-class="text-amber-300">400">"text-gray-class="text-amber-300">500 italic"># Thread maintains conversation state
config = {class="text-emerald-class="text-amber-300">400">"configurable": {class="text-emerald-class="text-amber-300">400">"thread_id": class="text-emerald-class="text-amber-300">400">"user-class="text-amber-300">123"}}

result1 = app.invoke(
    {class="text-emerald-class="text-amber-300">400">"messages": [(class="text-emerald-class="text-amber-300">400">"user", class="text-emerald-class="text-amber-300">400">"Hi, Iclass="text-emerald-class="text-amber-300">400">'m Raj")]},
    config=config
)

result2 = app.invoke(
    {class="text-emerald-class="text-amber-300">400">"messages": [(class="text-emerald-class="text-amber-300">400">"user", class="text-emerald-class="text-amber-300">400">"What's my name?")]},
    config=config
)
class=class="text-emerald-class="text-amber-300">400">"text-gray-class="text-amber-300">500 italic"># The model remembers the user's name

Key Takeaways

  • LangGraph enables complex, stateful AI workflows
  • Use conditional edges for routing logic
  • Checkpointers add memory persistence across interactions
  • Perfect for building agents that need tool use and conversation state
0