LangGraph is a framework built on top of LangChain for creating stateful, multi-step workflows as directed graphs, where each step is a node and the connections between steps are edges.
StateGraph ClassThe StateGraph class is used to create graph workflows in LangGraph. You initialize it with a state schema (a Python TypedDict) that specifies the names and types of all shared fields. Each node in the graph reads from and writes to this shared state.
from langgraph.graph import StateGraphfrom typing_extensions import TypedDict# Define the state schema with all shared fieldsclass SupportTicketState(TypedDict):issue: stranalysis: strresolution: str# Initialize the graph with the state schemabuilder = StateGraph(SupportTicketState)
START and END ConstantsIn LangGraph, START and END are special constants representing the entry and exit points of a graph. START links to the first node to process, whereas END signifies that execution is finished and no further nodes will run.
from langgraph.graph import START, ENDbuilder.add_edge(START, "analyze") # Entry pointbuilder.add_edge("respond", END) # Exit point
add_node()A node is a Python function registered with add_node("name", function). Each node receives the current state as input and returns a dictionary containing only the fields it wants to update. LangGraph then automatically merges these updates into the shared state.
def analyze_issue(state: SupportTicketState):"""Analyze the support issue and return findings."""result = llm.invoke(f"Analyze this support issue: {state['issue']}")# Return only the field being updatedreturn {"analysis": result.content}builder.add_node("analyze", analyze_issue)
add_edge()In LangGraph, the add_edge(source, target) method establishes a fixed connection between two nodes, called an edge, so that when the source node finishes, the target node executes next.
# Define the execution orderbuilder.add_edge(START, "analyze")builder.add_edge("analyze", "respond")builder.add_edge("respond", END)
The compile() method in LangGraph validates the graph structure and returns an executable object that can be run with an initial state dictionary using the .invoke() method.
# Compile the graph into a runnablesupport_pipeline = builder.compile()# Run the graph with an initial stateresult = support_pipeline.invoke({"issue": "Unable to reset password after multiple attempts","analysis": "","resolution": ""})
A reducer determines how a state field updates when a node provides a value: by default, values overwrite the field, but using Annotated[list, operator.add] changes this behavior to append new items instead of replacing them.
from typing import Annotatedimport operatorclass OrderState(TypedDict):order_id: strstatus: str# This field accumulates entries from all nodesaudit_trail: Annotated[list, operator.add]
The add_conditional_edges() method links a source node to a routing function that evaluates the current state and returns the name of the next node to execute. This allows the graph to follow different paths during runtime based on the data.
from typing import Literal# Define the routing functiondef route_by_priority(state) -> Literal["escalate", "standard_response"]:"""Choose the next node based on ticket priority."""if state["priority"] == "urgent":return "escalate"return "standard_response"# Add conditional edgesbuilder.add_conditional_edges("analyze", route_by_priority)
A cycle in LangGraph happens when an edge links a downstream node back to an upstream node, forming a loop. This allows for iterative execution, where results are improved repeatedly until a condition is satisfied, such as a quality threshold or maximum number of iterations.
# Conditional edge decides whether to continue loopingbuilder.add_conditional_edges("review", should_refine,{"refine": "refine", "done": END})# This edge creates the cycle back to reviewbuilder.add_edge("refine", "review")
recursion_limit in LangGraphIn LangGraph, recursion_limit is a configuration parameter passed to .invoke() that sets the maximum number of node executions, preventing infinite loops by raising an error when the limit is exceeded.
# Set a safety limit of 10 node executionsresult = support_pipeline.invoke(initial_state,config={"recursion_limit": 10})
MemorySaverIn LangGraph, MemorySaver is an in-memory checkpointer that preserves the graph’s state during execution. Passing a checkpointer to compile() enables the graph to be paused and resumed later.
from langgraph.checkpoint.memory import MemorySavercheckpointer = MemorySaver()graph = builder.compile(checkpointer=checkpointer)
In LangGraph, interrupt() pauses execution within a node and provides information for human review. When the human responds, Command(resume=...) sends their input back to the paused node, while Command(goto=...) directs execution to a specific next node based on that decision.
from langgraph.types import interrupt, Commanddef manager_approval(state) -> Command[Literal["process", "reject"]]:"""Pause for manager review before proceeding."""# Pause and show the request to a humandecision = interrupt({"request": state["request"],"instruction": "Reply: 'approve' or 'reject: <reason>'"})# Route based on the human's decisionif decision.startswith("approve"):return Command(goto="process")reason = decision.replace("reject:", "").strip()return Command(goto="reject", update={"reason": reason})
MessagesState in LangGraphIn LangGraph, MessagesState is a built-in state schema that includes a messages list, managed by the add_messages reducer. It automatically collects the entire conversation history across turns, storing message objects such as HumanMessage and AIMessage.
from langgraph.graph import StateGraph, MessagesStatefrom langchain_core.messages import HumanMessagedef chatbot_node(state: MessagesState):"""Generate a response based on conversation history."""response = llm_with_tools.invoke(state["messages"])# Returning a list appends to the conversationreturn {"messages": [response]}builder = StateGraph(MessagesState)
In LangGraph, ToolNode is a built-in node that automatically executes tool calls from a language model with tools attached via bind_tools(), then sends the results back to the state as messages.
from langgraph.prebuilt import ToolNodefrom langchain_core.tools import tool@tooldef get_order_status(order_id: str) -> str:"""Look up the status of an order."""return f"Order {order_id} is out for delivery."tools = [get_order_status]# Attach tools to the modelllm_with_tools = llm.bind_tools(tools)# ToolNode executes any tool calls from the modelbuilder.add_node("tools", ToolNode(tools))