MODULE 9/LESSON 1
๐Ÿค– Autonomous Agents & Graph Orchestration

LangGraph StateGraph Workflows

Build cyclic, deterministic, multi-agent workflows with state persistence using LangGraph

โฑ 18 minโšก Interactive Tool๐Ÿ“Š Diagram
Standard linear chains (like LCEL or standard LangChain) fail on multi-step reasoning because they cannot loop backward. LangGraph models agent workflows as stateful graphs (`StateGraph`). Nodes are python functions that mutate state, edges route control dynamically, and checkpointers persist state across execution steps โ€” enabling true autonomous agents that can reflect and correct their own mistakes.

Key Concepts

TypedDict State

The entire graph shares a single `State` object (defined via Python TypedDict). Every node function receives this state, performs work, and returns a dict that updates the state.

Conditional Edges (Loops)

Unlike DAGs (Directed Acyclic Graphs), LangGraph supports cyclic routing. A conditional edge can evaluate the state (e.g., 'is code valid?') and route back to a previous node (e.g., 'Coder') to try again.

Checkpointers (Memory)

MemorySaver or Postgres checkpointers save the graph's state after every node. This allows 'Time Travel' (rewinding to a previous state) and Human-in-the-Loop (pausing for approval).

Multi-Agent Collaboration

Each node can be a completely independent LLM agent with its own system prompt and tools. They collaborate by mutating the shared State (e.g., a 'Researcher' agent passing data to a 'Writer' agent).

โšก Interactive Architecture Simulator

LangGraph StateGraph Workflow
agent
Planner Agent
waitingโ€ฆ
โ†’
tool
Tool: PostgreSQL
waitingโ€ฆ
โ†’
evaluator
Quality Evaluator
waitingโ€ฆ
โ†’
end
END / Checkpoint
waitingโ€ฆ
Step: 0 / 7Reflection loops: 0

LangGraph Architecture & Reflection Loop

LangGraph: Reflection & Multi-Agent Loop Shared AgentState messages: [ ... ] quality_score: 85 Updated by every node IN Planner Agent Generate Plan Tool Executor Run Search / DB Evaluator Agent Check Quality Conditional Edge Pass END Fail (Needs Reflection) Append critique to messages & try again PostgresSaver Persists State

LangChain vs LangGraph Architecture

FeatureLangChain (LCEL)LangGraph
Execution PathLinear / DAG (Directed Acyclic)Cyclic (Loops allowed)
Memory / StatePass-through dicts or external memoryCentralized `TypedDict` Graph State
Self-CorrectionDifficult (requires external loops)Native via Conditional Edges
Human-in-the-loopManual implementation requiredNative via Checkpointer pause/resume
Best ForRAG pipelines, ChatbotsMulti-Agent workflows, Autonomous Agents

Production Code 1: StateGraph Core Architecture

langgraph_agent_core.pypython
1import operator
2from typing import TypedDict, Annotated, Sequence
3from langgraph.graph import StateGraph, END
4from langchain_core.messages import BaseMessage, HumanMessage, AIMessage
5
6# โ”€โ”€ 1. Define Agent State Schema โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
7class AgentState(TypedDict):
8    # 'operator.add' means new messages append to the list instead of overwriting
9    messages: Annotated[Sequence[BaseMessage], operator.add]
10    loop_count: int
11    is_valid: bool
12
13# โ”€โ”€ 2. Define Node Functions โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
14def planner_node(state: AgentState):
15    """Generates the plan or code."""
16    # In production, call your LLM here
17    return {
18        "messages": [AIMessage(content="Generated SQL query plan.")], 
19        "loop_count": state.get("loop_count", 0) + 1
20    }
21
22def evaluator_node(state: AgentState):
23    """Evaluates the planner's output."""
24    # Mocking evaluation logic: passes only on the 2nd loop
25    is_valid = state["loop_count"] >= 2
26    return {"is_valid": is_valid}
27
28# โ”€โ”€ 3. Define Conditional Routing Logic โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
29def route_quality_check(state: AgentState) -> str:
30    if state["is_valid"]:
31        return END        # Output is good, finish graph
32    if state["loop_count"] > 3:
33        return END        # Break infinite loops
34    return "planner"      # Feedback loop: send back to planner to try again
35
36# โ”€โ”€ 4. Construct StateGraph โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
37workflow = StateGraph(AgentState)
38
39workflow.add_node("planner", planner_node)
40workflow.add_node("evaluator", evaluator_node)
41
42workflow.set_entry_point("planner")
43workflow.add_edge("planner", "evaluator")
44
45# Add Conditional Edge (Cyclic Reflection Loop)
46workflow.add_conditional_edges("evaluator", route_quality_check)
47
48app = workflow.compile()
49

Production Code 2: Human-in-the-Loop & Persistence

langgraph_hitl.pypython
1from langgraph.checkpoint.memory import MemorySaver
2# from langgraph.checkpoint.postgres import PostgresSaver # For Production DB
3from langgraph.graph import StateGraph
4# ... (Assuming 'workflow' from previous snippet is defined) ...
5
6# โ”€โ”€ 1. Compile with Checkpointer and Interrupts โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
7# MemorySaver stores state in RAM. PostgresSaver stores it in a DB table.
8checkpointer = MemorySaver()
9
10# 'interrupt_before' pauses execution BEFORE the evaluator node runs,
11# allowing a human to approve or modify the state.
12app = workflow.compile(
13    checkpointer=checkpointer,
14    interrupt_before=["evaluator"]
15)
16
17# โ”€โ”€ 2. First Run (Pauses at interrupt) โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
18thread_config = {"configurable": {"thread_id": "user-session-999"}}
19initial_input = {"messages": [HumanMessage(content="Write a React component")]}
20
21print("Running graph...")
22for event in app.stream(initial_input, thread_config):
23    print(event)
24
25# Graph execution pauses here! 
26print("Graph state:", app.get_state(thread_config).next) # Returns: ('evaluator',)
27
28# โ”€โ”€ 3. Human Approval (Resume Execution) โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
29user_approval = input("Approve the planner's output? (y/n): ")
30if user_approval.lower() == 'y':
31    # Resume graph execution with NO new input (passes None)
32    print("Resuming graph...")
33    for event in app.stream(None, thread_config):
34        print(event)
35
๐Ÿ’ก
Senior Architect Insight: To prevent autonomous agents from burning through your API budget in an infinite 'hallucination reflection' loop, ALWAYS add a `loop_count` to your `AgentState` and enforce a hard limit (e.g. max 3 retries) in your conditional edge router.