opncrafter

Tutorial: Building Cyclic Agents with LangGraph

Dec 30, 2025 • 25 min read

What is LangGraph? A library for building stateful, multi-actor applications with LLMs. Unlike "Chains" (DAGs), LangGraph supports Cycles.

1. The Mental Model: State Machines

Most agent frameworks (AutoGPT, BabyAGI) are just `while` loops. LangGraph formalizes this as a Graph.

  • State: A shared schema (Python Dictionary) that evolves over time.
  • Nodes: Functions that modify the state.
  • Edges: Control flow rules (If X, go to Node A. Else, go to Node B).

2. Step 1: Define the State

We use Python's TypedDict to enforce type safety.

from typing import TypedDict, Annotated, List, Union
from langchain_core.messages import BaseMessage
import operator

class AgentState(TypedDict):
    # 'Annotated' allows us to define reducer functions.
    # operator.add means "append to list" instead of overwriting.
    messages: Annotated[List[BaseMessage], operator.add]
    next_step: str

3. Step 2: Define Nodes

Nodes are just Python functions that take the current state and return a state update.

async function call_model(state: AgentState):
    messages = state['messages']
    response = await model.ainvoke(messages)
    return {"messages": [response]}

async function run_tool(state: AgentState):
    last_message = state['messages'][-1]
    tool_input = last_message.tool_calls[0]
    result = await tool_executor.invoke(tool_input)
    return {"messages": [result]}

4. Step 3: The Graph (Edges & Conditional Logic)

This is where the magic happens. We wire the nodes together.

from langgraph.graph import StateGraph, END

workflow = StateGraph(AgentState)

# 1. Add Nodes
workflow.add_node("agent", call_model)
workflow.add_node("action", run_tool)

# 2. Set Entry Point
workflow.set_entry_point("agent")

# 3. Add Conditional Edge (The "Router")
def should_continue(state):
    last_message = state['messages'][-1]
    if "tool_calls" in last_message:
        return "continue"
    return "end"

workflow.add_conditional_edges(
    "agent",
    should_continue,
    {
        "continue": "action",
        "end": END
    }
)

# 4. Add Normal Edge
workflow.add_edge("action", "agent") # Loop back!

# 5. Compile
app = workflow.compile()

5. Persistence (Time Travel)

Agents need memory. LangGraph has built-in Checkpointers (Postgres, SQLite, Redis).

This allows you to:

  1. Pause execution (Human-in-the-loop).
  2. "Time Travel" (rewind the state to fix a mistake).
  3. Resume a conversation days later.
from langgraph.checkpoint.sqlite import SqliteSaver

memory = SqliteSaver.from_conn_string(":memory:")
app = workflow.compile(checkpointer=memory)

# Run with a thread_id
config = {"configurable": {"thread_id": "123"}}
app.invoke({"messages": [("user", "Hi")]}, config=config)

Conclusion

Status: Essential Skill. Linear Chains are dead. If you are building reliable agents, you need Cycles, and LangGraph is the best way to manage them.