Fun with Learning Technology
LearnCoursesQuestionsTracksToolsNewsExplorePractice
Fun with Learning Technology

A new problem, explained clearly, every day.

Subscribe
Learn
  • Lessons
  • Topics
  • News
  • Tools
  • Courses
  • Career tracks
  • Everything
Site
  • About
  • Contact
  • Support
  • Privacy
  • Terms
Get the daily one

One email per new problem. No spam.

Request a tutorial

Requests shape what gets made next.

© 2026 Fun with Learning TechnologyRSS
Home›Courses›LangChain›Nodes, Edges, and State

LangGraph

Nodes, Edges, and State

LangGraph conceptualizes agentic workflows as directed cyclic graphs where state serves as the single source of truth for all interactions. Understanding this structure allows you to move beyond simple linear chains into complex, iterative logic that can handle loops and decision-making. You reach for LangGraph when your application requires persistent memory, conditional routing, or multi-agent collaboration.

The Concept of State

In LangGraph, the State is the central conduit of information. Unlike traditional linear chains where data is passed directly between components, LangGraph uses a TypedDict or a Pydantic model as a persistent object that gets updated by every node in your graph. When you define your state, you are defining the schema of your entire application's memory. The reason this works so effectively is that each node can either overwrite existing keys or append to them using 'reducer' functions. This decoupled approach ensures that you always know the exact status of your application. By treating state as a mutable, versioned object, you enable the system to handle long-running processes that require constant context updates without losing track of previous interactions or intermediate reasoning steps, making debugging significantly more transparent than debugging a black-box chain.

from typing import TypedDict, Annotated
import operator

# Define the state as a container for messages
class AgentState(TypedDict):
    # The 'messages' key will append new items instead of overwriting
    messages: Annotated[list[str], operator.add]
    current_turn: int

Nodes as Functional Units

A node in LangGraph is simply a Python function that performs a specific unit of work. When the graph execution reaches a node, it passes the current State object into that function. The node processes this information, executes logic—such as calling an LLM or querying a database—and returns an update to the State. The beauty of this pattern is its modularity; nodes are entirely unaware of the graph's overall topology. Because they only care about receiving a specific structure and returning a partial update to that structure, you can unit-test your nodes in isolation without initializing the entire workflow. This functional approach encourages developers to write clean, predictable code, as each node is responsible for a well-defined outcome based solely on the current state provided to it at that specific moment in time.

def process_message(state: AgentState):
    # Perform work and return an update
    print(f"Processing turn: {state['current_turn']}")
    return {"messages": ["Processed input"], "current_turn": 1}

Building the Graph Structure

Once your nodes are defined, you use the StateGraph class to stitch them together into a coherent workflow. This is where you define the entry point and the finish line. The graph behaves as a state machine: you 'add_node' for every functional unit and 'add_edge' to define the sequence of execution. The 'START' node is a built-in convenience that signifies the initial trigger of your process, while the 'END' node signals completion. Why this structure is superior to simple chains is that it allows for explicit definition of flow. By mapping these connections, you create a visual and logical map of your application's logic. This eliminates implicit dependencies, as the flow is dictated by the graph structure rather than the execution order of your code, providing a rigid framework for complex agentic behaviors.

from langgraph.graph import StateGraph, START, END

# Initialize the graph with the state schema
builder = StateGraph(AgentState)
builder.add_node("worker", process_message)
builder.add_edge(START, "worker")
builder.add_edge("worker", END)
app = builder.compile()

Edges and Conditional Logic

Edges define the transitions between nodes. While linear edges simply route from node A to node B, conditional edges allow your application to make decisions dynamically based on the current state. This is where the true power of LangGraph lies; you can implement branching logic that checks for specific keywords, function results, or agent decisions before deciding which node to trigger next. Because the state is updated before the edge evaluation occurs, the routing logic has full access to the latest data produced by previous nodes. This enables self-correcting workflows where an agent might re-route itself back to a validation node if it detects an error in its previous output. This pattern effectively implements 'loops' which are impossible in standard sequential chains, giving your agent the capability to persist until a objective is met.

def router(state: AgentState):
    # Route based on the content of the last message
    if "error" in state['messages'][-1]:
        return "fix_node"
    return "finish_node"

# Use add_conditional_edges to handle dynamic flow
builder.add_conditional_edges("worker", router)

Compilation and Execution

The final step in the LangGraph workflow is the compilation process. When you call 'compile()', LangGraph validates your graph topology, checking for unreachable nodes or missing edges, and wraps your nodes into a single, executable object. This object acts as a runnable that manages the lifecycle of your agent. The runtime handles the heavy lifting, such as tracking the current step and serializing state transitions. By using the compiled graph's invoke method, you provide the initial state and trigger the cycle. The reason this compilation step is vital is that it creates a predictable, consistent execution environment that abstracts away the complexity of managing state updates and iteration counts, allowing you to treat your entire complex, cyclic agent as a single, modular unit that can be easily integrated into larger systems.

# Compile and run the workflow
app = builder.compile()
result = app.invoke({"messages": ["Start task"], "current_turn": 0})
print(result)

Key points

  • State in LangGraph acts as the centralized source of truth for all graph nodes.
  • Nodes represent discrete functional units that consume and update the global state.
  • Edges determine the flow of logic, allowing for both linear and branching execution paths.
  • Conditional edges enable dynamic decision-making based on the current data within the state.
  • The compilation process ensures the graph is logically sound before execution begins.
  • Cycles can be implemented, allowing agents to iterate until a specific condition is met.
  • Modularity is achieved because nodes operate independently of the graph's overall topology.
  • LangGraph provides persistent memory by maintaining state across multiple invocations of the graph.

Common mistakes

  • Mistake: Modifying the state directly within a node. Why it's wrong: LangGraph state must be treated as immutable to ensure predictability. Fix: Always return a dictionary update that the reducer will merge into the state.
  • Mistake: Assuming a node executes immediately upon state update. Why it's wrong: Nodes only execute when triggered by the graph's control flow or a conditional edge. Fix: Explicitly define edges that point to the node you want to execute next.
  • Mistake: Adding too much data to the state object. Why it's wrong: Large state objects increase token usage and latency in persistent systems. Fix: Store only the minimal necessary context and keep large documents in an external vector store.
  • Mistake: Creating loops without a terminal condition. Why it's wrong: The graph will run indefinitely, leading to high cost or timeouts. Fix: Always include a conditional edge that evaluates state to decide when to stop or transition to an END node.
  • Mistake: Forgetting to define a reducer for state keys. Why it's wrong: Without a reducer, new values will simply overwrite old ones instead of appending or merging. Fix: Use Annotated types with a reducer function like operator.add for list-based state accumulation.

Interview questions

In the context of LangGraph, what is the fundamental difference between a node and an edge?

In LangGraph, a node represents a fundamental unit of work, typically implemented as a Python function that performs a specific action, such as invoking a language model or retrieving data. An edge, by contrast, acts as the control flow mechanism. It defines the routing logic, determining which node should execute next based on the state returned by the current node. Essentially, nodes are the actors performing logic, while edges are the pathways directing the flow of control within your application's architecture.

How does the 'State' object function within a LangGraph workflow?

The State object serves as the single source of truth that is shared across all nodes in a graph. It is typically defined as a TypedDict or a Pydantic model. When a node executes, it receives the current state, performs its computation, and returns updates that are merged into the global state. This mechanism allows LangGraph to maintain context, keep track of message history, and ensure that every step of the agentic workflow has the necessary data to make informed decisions.

Why is it important to use 'StateSchema' in LangGraph when defining your agent?

Using a StateSchema is critical because it enforces strict data structures across the entire graph. By explicitly defining the fields in your state, you prevent bugs caused by unexpected data types or missing information as messages pass between nodes. It acts as a contract that every function must follow. For example, by defining 'messages: Annotated[Sequence[BaseMessage], operator.add]', you ensure that LangGraph automatically appends new messages to the existing list rather than overwriting the previous history, maintaining a clean conversation flow.

Can you explain the difference between a conditional edge and a normal edge in LangGraph?

A normal edge is a static routing rule; it unconditionally points from Node A to Node B every time Node A completes. A conditional edge, however, introduces dynamic branching. It uses a function to inspect the state—such as checking if a tool call exists or if a specific threshold is met—and returns a route based on that evaluation. This allows the graph to deviate from a linear path, enabling complex logic like retries, routing to different tools, or terminating the process entirely.

Compare the approach of using a simple LangChain Chain versus a LangGraph Agent for handling complex tasks.

A simple LangChain chain is typically a linear sequence of operations, like a prompt template followed by a model and an output parser; it is perfect for straightforward, predictable tasks. A LangGraph agent, conversely, utilizes a cyclic graph structure with state persistence. This allows the agent to loop back on its own outputs, re-run tools based on errors, or pause and wait for human-in-the-loop input. LangGraph is far superior for iterative reasoning tasks that cannot be mapped to a single linear path.

How would you implement a human-in-the-loop (HITL) interrupt in a LangGraph workflow and why is the State crucial here?

You implement a human-in-the-loop interrupt by defining a 'breakpoint' before a specific node in the graph, typically using the 'interrupt_before' parameter during execution. The State is crucial here because, when the graph pauses, the entire state is serialized and persisted. This allows the graph to stop execution, wait for external input, and then resume exactly where it left off, with the state fully intact, ensuring that the human intervention can modify the data before the agent proceeds.

All LangChain interview questions →

Check yourself

1. What is the primary role of a 'Node' in a LangGraph execution flow?

  • A.To define the schema of the data being passed
  • B.To encapsulate a unit of work that performs a transformation or task
  • C.To determine the sequence of transitions between steps
  • D.To persist the final output to a database
Show answer

B. To encapsulate a unit of work that performs a transformation or task
Nodes represent the functions or logic units. Option 0 is the job of the State schema, option 2 describes Edges, and option 3 is a side effect, not the primary structural role.

2. How should a developer handle state updates within a node to ensure reliability?

  • A.Overwrite the entire state object with a new dictionary
  • B.Use global variables to modify state outside the node
  • C.Return a partial update dictionary that the graph merges
  • D.Call the next node directly inside the current node function
Show answer

C. Return a partial update dictionary that the graph merges
LangGraph works by merging partial updates into the state. Option 0 destroys existing state, option 1 breaks encapsulation, and option 3 violates the graph's control flow structure.

3. When is it appropriate to use a Conditional Edge in a LangGraph flow?

  • A.When the next step is fixed and deterministic
  • B.When you want to run all nodes in parallel
  • C.When the next destination depends on the current state values
  • D.When you need to define the initial starting node
Show answer

C. When the next destination depends on the current state values
Conditional edges are designed for routing based on logic. Option 0 is a standard edge, option 1 is a graph feature not controlled by edges, and option 3 is defined at the graph compile level.

4. Why is the use of the Annotated type with a reducer function important in state definition?

  • A.It determines the starting node of the graph
  • B.It tells the graph how to handle conflicting updates for specific keys
  • C.It automatically optimizes the prompt for the LLM
  • D.It enables the graph to run on multiple hardware devices
Show answer

B. It tells the graph how to handle conflicting updates for specific keys
Reducers define how state is combined (e.g., adding to a list). Option 0 is unrelated, option 2 is a separate concern, and option 3 describes infrastructure rather than state management logic.

5. What happens if a graph execution reaches a node with no outgoing edges and is not explicitly directed to END?

  • A.The graph automatically restarts
  • B.The graph throws an execution error
  • C.The graph hangs indefinitely
  • D.The graph terminates silently
Show answer

B. The graph throws an execution error
LangGraph requires a clear termination path to the END constant. Without it, the graph engine cannot validate the graph structure, resulting in a runtime error.

Take the full LangChain quiz →

← PreviousLangGraph IntroductionNext →Conditional Routing

LangChain

24 lessons, free to read.

All lessons →

Track your progress

Sign in to mark lessons done, score quizzes and keep notes.

Open in the app