Skip to main content

LangGraph Workflows

Workflows in LangGraph define how state moves through nodes and edges to accomplish a task in a controlled, observable execution graph. Every agentic application—whether it’s a linear data pipeline, a branching support bot, or a multi‑step research loop—is built by composing these workflows. This handbook article is your implementation‑focused guide: you’ll learn to design, control, and execute agent workflows using LangGraph’s state‑driven execution model.

What Are Workflows in LangGraph

A workflow in LangGraph is the runtime execution path through a graph structure. It’s not a separate abstraction—it’s the living pattern that emerges when state flows from node to node, guided by edges and conditions. The graph provides the structure; the workflow is the behavior.

  • Workflow = execution logic and flow of control
  • Graph = structural definition (nodes + edges)
  • Nodes = processing units that update state
  • Edges = deterministic or conditional transitions between nodes

A simple example: a two‑step summarisation workflow.

from typing import TypedDict
from langgraph.graph import StateGraph, START, END

class State(TypedDict):
text: str
summary: str

def summarise_node(state: State):
# In production, call an LLM; here we mock
state["summary"] = state["text"][:100] + "..."
return state

builder = StateGraph(State)
builder.add_node("summarise", summarise_node)
builder.add_edge(START, "summarise")
builder.add_edge("summarise", END)

graph = builder.compile()
result = graph.invoke({"text": "Long article ..."})

Here the workflow is trivially linear: START → summarise → END. As soon as you add conditional edges, loops, or parallel branches, the workflow becomes dynamic and powerful.

Why Workflows Matter

Production AI agents are rarely single‑shot prompts. Workflows enable:

  • Complex task orchestration – Break a large goal into manageable steps (research → analyse → draft → review).
  • Multi‑step reasoning – Let the agent think, act, and observe repeatedly.
  • Tool execution coordination – Interleave LLM reasoning with API calls, database queries, and human approvals.
  • Human‑in‑the‑loop processes – Pause, wait for a reviewer, and resume exactly where you stopped.
  • Production reliability – Explicit control flow means you can reason about every possible path, handle errors, and guarantee completion.

A customer‑facing assistant that needs to verify identity, check inventory, and process a refund cannot be a single LLM call. It’s a workflow with branching, tool calls, and a human approval step. LangGraph gives you the primitives to build that confidently.

How LangGraph Workflows Work

At its core, a workflow is a repeated cycle: state → node → edge → next node → state update → … → completion. The graph runtime manages the loop until it reaches an END node.

  • State is a typed dictionary or Pydantic model that flows through every node.
  • Nodes receive the current state, execute business logic, and return an updated state (or a partial update).
  • Edges define the next node. Normal edges always point to a fixed node; conditional edges evaluate a function that returns the next node name (or END).
  • Runtime begins with the START node, invokes nodes in order, and stops when END is reached.

You don’t write a loop—LangGraph does. You define the possible paths, and the engine traverses them.

Types of Workflows in LangGraph

Workflows fall into a few composable patterns. You’ll mix and match these inside a single graph.

Sequential Workflows

The simplest pattern: a linear chain of nodes.

Use when every step must happen in a fixed order, with no branching.

Conditional Workflows

Branching based on state. A conditional edge inspects the state and returns the next node.

Implementation pattern:

def route_intent(state):
if state["intent"] == "refund":
return "refund_handler"
return "info_handler"

builder.add_conditional_edges("intent_classifier", route_intent, {
"refund_handler": "refund_handler",
"info_handler": "info_handler"
})

Parallel Workflows

Fan‑out: multiple nodes run concurrently, then their outputs are gathered before continuing.

LangGraph supports parallelism when you define multiple edges from the same node to different nodes; the runtime executes them concurrently and waits for all before proceeding. (In practice, you must use async graphs or manage thread pools.)

Looping Workflows

A graph can loop back to an earlier node to enable iterative refinement, retries, or continuous reasoning.

This is the classic “agent loop”. The conditional edge after the agent node checks if a tool was called or if the answer is final. Loops are bounded by a configurable recursion limit to prevent infinite execution.

Workflow Execution Model

The execution model is deterministic given the state. Key properties:

  • State passing: Each node receives the current state (or the relevant keys if using input/output schemas). The node returns an update, which is merged into the state.
  • Node execution order: For a single node, it runs to completion before the next. For concurrent nodes, order is not guaranteed.
  • Edge evaluation: After a node finishes, the outgoing edges are evaluated. Conditional edges call their routing function with the updated state.
  • Runtime control: The graph runs until it reaches END or the recursion limit is hit. You can also interrupt the graph programmatically (see human‑in‑the‑loop).

This model ensures that the entire execution is reproducible and checkpointable. After each super‑step, the state can be persisted, enabling resumption from any point.

Workflow Design Patterns

These are common structural templates you’ll use when building real applications.

PatternDescriptionTypical use
Linear PipelineNodes run one after another in a fixed sequenceETL, data processing
Decision TreeMultiple conditional branches based on stateIntent routing, business rule engine
Fan‑Out / Fan‑InParallel execution of independent tasks, then aggregationMulti‑source research, batch tool calls
Iterative RefinementLoop with a termination conditionSelf‑correction, draft‑review cycles
Human Review LoopLoop that pauses for human input before finalisingSensitive actions, content moderation

Linear Pipeline Pattern

builder.add_edge("extract", "transform")
builder.add_edge("transform", "load")

Decision Tree Pattern

Already shown above with conditional edges. Use multiple conditional edges or a single router that maps a large set of outcomes to handler nodes.

Fan‑Out / Fan‑In Pattern

Built by adding multiple edges from one node to several “worker” nodes, then connecting all workers to a single “collector” node.

Iterative Refinement Pattern

A loop where the agent produces a draft, then a critique node reviews it. If the critique finds issues, the graph loops back to the draft node; otherwise, it proceeds.

def should_revise(state):
return "draft" if state.get("needs_revision") else END

builder.add_conditional_edges("critique", should_revise, {
"draft": "draft",
END: END
})

Human Review Loop Pattern

Uses the interrupt function to pause the graph. The human supplies a resume value, and the graph continues.

Workflow vs Graph vs Node

These terms are often mixed. Let’s clarify their roles in LangGraph.

TermDefinitionExample
GraphThe structural blueprint: a set of nodes and edgesStateGraph(State)
WorkflowThe actual execution path through the graph given an input stateSTART → intent → tools → agent → END
NodeA single processing unit; a Python functiondef my_node(state): ...

You design a graph, and when invoked, it generates a workflow—the path taken. Two different inputs can produce different workflows from the same graph (through different conditional branches). This separation is key to building reusable, modular agents.

State Flow in Workflows

State is the lifeblood. Understanding how it flows through the workflow prevents bugs and enables advanced patterns.

  • Pass by reference, merge by update: Nodes receive the full state (or a subset). They return a dictionary containing only the keys they wish to update. LangGraph merges those updates into the existing state.
  • Accumulation: Using Annotated reducers (e.g., add_messages) allows state fields to accumulate across nodes, not just overwrite. This is essential for message lists and lists of tool results.
  • Visibility: Once a node updates the state, the next node (including ones on parallel branches) sees the change. The state is the single source of truth.
class AgentState(TypedDict):
messages: Annotated[list, add_messages] # accumulates
extracted_data: dict # overwritten per node
step_count: int # incremented manually

A workflow that extracts user information might have an extract node that sets extracted_data, and a verify node that reads that field. If the verify node fails, a loop can send the state back to extract for correction—all because state persists across the cycle.

Error Handling in Workflows

Production workflows must degrade gracefully. LangGraph provides several levers.

  • Node failure handling: If a node raises an exception, the graph stops by default. You can wrap node logic in try/except to catch errors and return an error state.
  • Retry strategies: For transient failures (network calls, API rate limits), implement retry loops. Use a counter in the state and a conditional edge that loops back to the same node if retries remain.
  • Fallback paths: Route to a “fallback” node if a primary node fails (e.g., using a conditional edge after the node that checks for an error flag in state).
  • Dead‑end prevention: Always ensure every conditional edge has a default case that leads to END or an error handler, so the graph never gets stuck.

Example of an error‑aware node:

def robust_tool_node(state):
try:
result = call_external_api(state["params"])
state["tool_result"] = result
state["error"] = None
except Exception as e:
state["tool_result"] = None
state["error"] = str(e)
return state

Then, a conditional edge after the node checks state["error"] and routes to a retry or fallback node.

Workflow Composition

As your application grows, you’ll want to reuse and compose workflows. LangGraph allows sub‑graphs: a compiled graph can be added as a node inside a parent graph.

sub_graph = sub_builder.compile()
builder.add_node("sub_workflow", sub_graph)

The sub‑graph receives the parent’s state and returns an update. This enables:

  • Modular workflows – Build a reusable “research” graph and embed it into a “report generation” graph.
  • Separation of concerns – Teams can own individual sub‑graphs and develop them independently.
  • State mapping – With input/output schemas, you can map keys between parent and child graphs, further decoupling them.

Composition is the key to scaling LangGraph projects beyond single‑file prototypes.

Dynamic Workflows

Sometimes the workflow itself must adapt at runtime. LangGraph supports dynamic routing decisions that go beyond static conditional edges.

  • Runtime routing: A node can return a Command object that explicitly overrides the next node, bypassing edge definitions.
  • Adaptive execution paths: The LLM can decide which node to go to next based on its reasoning. This is the basis of the “supervisor” pattern.
  • LLM‑driven decisions: You can ask the LLM to output the name of the next node, and use that as the routing key.

Example of a node returning a command:

from langgraph.types import Command

def dynamic_router(state):
next_node = llm_decide_next_step(state["messages"])
return Command(update=state, goto=next_node)

Be cautious: dynamic routing makes the workflow harder to statically analyse. Use it when static conditions become unwieldy.

Workflow Observability

Debugging a multi‑step agent requires visibility into the workflow’s execution path.

  • Execution tracing: LangGraph logs every node start/end and edge traversal. Enable logging to see the exact path.
  • Structured logging: Use Python’s logging module with distinct logger names per node. Include thread_id and run_id in all logs.
  • Debugging workflows: Run the graph step‑by‑step using graph.stream() to see intermediate outputs.
  • Performance monitoring: Track wall‑clock time per node, total runtime, and LLM token usage. Many teams integrate LangGraph with LangSmith for a visual dashboard.

Set up basic tracing:

import logging
logging.basicConfig(level=logging.INFO)

for event in graph.stream(input_state, config={"configurable": {"thread_id": "debug-1"}}):
logging.info(f"Event: {event}")

For production, forward logs to a central system and correlate with checkpoints.

Workflows in Production

When your workflow goes live, you’ll consider:

  • Scalability: Use async graphs for high concurrency. Choose a persistent checkpointer (Postgres) and consider session affinity for threads.
  • Reliability: Every workflow should have error handling and a timeout. Configure a global recursion limit (e.g., 25) to prevent infinite loops.
  • Cost control: Monitor LLM calls per workflow. Use caching for repeated, deterministic tool results.
  • Latency optimisation: Keep critical path nodes fast. Offload heavy processing to async tasks that update state later if needed.

A production‑ready graph compilation:

from langgraph.checkpoint.postgres import PostgresSaver

checkpointer = PostgresSaver.from_conn_string("...")
graph = builder.compile(checkpointer=checkpointer, interrupt_before=["human_review"])

Common Beginner Mistakes

Avoid these pitfalls when designing workflows.

  1. Overcomplicated graphs – Too many conditional branches make the graph hard to test. Start simple and compose sub‑graphs.
  2. Poor state design – Putting everything into a flat dictionary without clear schema leads to confusion. Use TypedDict or Pydantic.
  3. Missing fallback paths – A conditional edge that doesn’t handle all possible states will crash the graph. Always include an END or error route.
  4. Excessive branching – Deeply nested conditionals can be replaced with a router node that maps a key to a handler node name.
  5. No observability – Flying blind in production is dangerous. Add logging and tracing from day one.
  6. Ignoring recursion limits – Loops without a clear exit condition can exhaust resources. Always set a sensible recursion_limit.

Best Practices

  • Keep workflows modular – Compose sub‑graphs for reusable components (e.g., a “verify” sub‑graph used across workflows).
  • Minimise branching complexity – Use a single router node that maps intent → handler, rather than cascading conditions.
  • Use clear state schemas – Define state with explicit fields, and document how each node uses them.
  • Design explicit failure paths – Every node should either succeed or set an error field. Route on that field.
  • Enable tracing early – Even during development, stream events. It pays off when debugging.
  • Reuse sub‑graphs – Don’t copy‑paste node sequences; compile once, reuse as a node.

Practical Example: Customer Support Agent Workflow

We’ll build a support agent that:

  1. Receives user request.
  2. Classifies intent.
  3. Retrieves relevant knowledge base articles.
  4. Calls external tools if needed (e.g., check order status).
  5. Decides if human review is needed.
  6. Pauses for human approval (optional).
  7. Returns final response.

State definition

from typing import TypedDict, Annotated
from langgraph.graph.message import add_messages
from langchain_core.messages import BaseMessage

class SupportState(TypedDict):
messages: Annotated[list[BaseMessage], add_messages]
intent: str
order_data: dict
needs_approval: bool
approved: bool
final_response: str

Nodes

def intent_classifier(state: SupportState):
# Simulated; use an LLM node in production
last_msg = state["messages"][-1].content
state["intent"] = "refund" if "refund" in last_msg.lower() else "general"
return state

def retrieve_knowledge(state: SupportState):
# Mock: fetch article based on intent
state["messages"].append(("ai", f"Relevant help article for {state['intent']}..."))
return state

def tool_executor(state: SupportState):
if state["intent"] == "refund":
# Simulate order lookup
state["order_data"] = {"id": "123", "status": "delivered"}
state["needs_approval"] = True
return state

def decision_node(state: SupportState):
# In real implementation, LLM decides if human review needed
state["needs_approval"] = state.get("needs_approval", False)
return state

def human_review_node(state: SupportState):
from langgraph.types import interrupt
decision = interrupt({
"question": "Approve refund?",
"order": state.get("order_data", {})
})
state["approved"] = decision.get("approved", False)
return state

def final_response_node(state: SupportState):
if state.get("approved"):
state["final_response"] = "Refund has been approved and processed."
else:
state["final_response"] = "I have provided the relevant information. Do you need further assistance?"
return state

Graph assembly

from langgraph.graph import StateGraph, START, END

builder = StateGraph(SupportState)

# Add nodes
builder.add_node("intent", intent_classifier)
builder.add_node("retrieve", retrieve_knowledge)
builder.add_node("tools", tool_executor)
builder.add_node("decision", decision_node)
builder.add_node("human_review", human_review_node)
builder.add_node("final_response", final_response_node)

# Edges
builder.add_edge(START, "intent")
builder.add_edge("intent", "retrieve")
builder.add_edge("retrieve", "tools")
builder.add_edge("tools", "decision")

# Conditional after decision
def route_after_decision(state):
if state.get("needs_approval"):
return "human_review"
return "final_response"

builder.add_conditional_edges("decision", route_after_decision, {
"human_review": "human_review",
"final_response": "final_response"
})
builder.add_edge("human_review", "final_response")
builder.add_edge("final_response", END)

graph = builder.compile()

Execution

config = {"configurable": {"thread_id": "support-1001"}}
# First run – will pause at human_review if refund
graph.invoke({"messages": [("user", "I want a refund for order 123")]}, config)

# ... human reviews later ...
from langgraph.types import Command
graph.invoke(Command(resume={"approved": True}), config)

Workflow path taken:

  • If the intent is refund: START → intent → retrieve → tools → decision → human_review → final_response → END
  • The human review node pauses the workflow, waits indefinitely, then resumes.
  • The state at each step is checkpointed, enabling full traceability.

This single graph demonstrates sequential, conditional, and loop‑like patterns (via the human pause/resume) in a production‑realistic workflow.

Workflows and Other LangGraph Concepts

Workflows sit at the center of the LangGraph ecosystem. They directly interact with every other core component.

  • Core Concepts – The graph structure, state, nodes, and edges are the building blocks of any workflow.
  • Tool Calling – Workflows orchestrate when and how tools are called, and route based on tool outputs.
  • Memory – Checkpoints persist the workflow’s exact state at every step, enabling long‑running and resumable workflows.
  • Human‑in‑the‑Loop – The interrupt function is a workflow control mechanism that pauses and resumes, essential for approval flows.
  • Production – Production deployment is about making your workflows reliable, scalable, and observable.

Understanding workflows gives you the mental model to combine these capabilities into coherent, maintainable agents.

FAQ

1. What is a workflow in LangGraph?

A workflow is the runtime execution path through the nodes and edges of a LangGraph graph, defined by the state and the routing decisions made along the way.

2. How is a workflow different from a graph?

The graph is the static structure (the blueprint). The workflow is the dynamic instance—the actual sequence of nodes visited during a specific invocation.

3. Can workflows run in parallel?

Yes. By defining multiple outgoing edges from a node to different nodes, the runtime can execute them concurrently (especially in async mode).

4. How does routing work?

Routing is handled by conditional edges. A Python function inspects the current state and returns the name of the next node (or END).

5. Can workflows loop?

Yes. Edges that point back to an earlier node create loops, enabling iterative reasoning, retries, and refinement. A recursion limit prevents infinite loops.

6. How are failures handled in a workflow?

You can wrap node code in try/except, set an error flag in the state, and use conditional edges to route to a fallback or retry node.

7. Can workflows be dynamic?

Yes. Nodes can return a Command object to override the next node, or you can use LLM‑driven decisions to dynamically choose the next step.

8. What is a sub‑graph?

A compiled graph used as a node within another graph. It allows modular, reusable workflow components.

9. How do I debug a workflow?

Use graph.stream() to see each event, enable verbose logging, and integrate with tracing tools like LangSmith.

10. How does state flow through a workflow?

Nodes receive the current state, return an update dictionary, and the runtime merges it. Downstream nodes immediately see the merged state.

11. What is the difference between a workflow and a chain (like LangChain’s LCEL)?

Chains are linear or composable sequences of runnables. LangGraph workflows are stateful graphs with explicit control flow, branching, and loops—much more powerful for complex agents.

12. Can I pause a workflow and resume it later?

Yes, using the interrupt function and a persistent checkpointer. The workflow state is saved, and you can resume hours or days later.

13. How do I control the maximum number of steps?

Set recursion_limit in the config (e.g., config={"recursion_limit": 20}). The graph will terminate with an error if the limit is exceeded.

14. Are workflows suitable for production?

Absolutely. With persistent checkpointing, error handling, and observability, LangGraph workflows power many production agent systems.

15. How do I compose multiple workflows into one application?

Use sub‑graphs as nodes, or run multiple independent graphs and coordinate via external state (e.g., a database or message queue).

16. What’s the best way to organise a large workflow?

Break it into sub‑graphs by domain, keep the parent graph as a high‑level orchestrator, and use clear state interfaces.

Conclusion

LangGraph Workflows are the engine of your agentic applications. They transform static graph definitions into dynamic, stateful execution paths that can handle linear pipelines, complex decision trees, parallel processing, and iterative loops. By mastering the patterns—and pairing them with tool calling, memory, and human‑in‑the‑loop—you can build reliable, observable, and scalable AI agents.

Key takeaways:

  • Workflows are the actual execution paths through nodes and edges.
  • Patterns like linear pipelines, decision trees, fan‑out/fan‑in, and iterative refinement cover most use cases.
  • State flow and error handling are the backbone of robust workflow design.
  • Composition with sub‑graphs keeps complexity manageable.
  • Production readiness demands observability, error handling, and checkpointing from day one.

Continue your journey into production‑grade agents:

For a broader perspective, revisit the LangGraph Core Concepts and the framework overview. Now, go build workflows that think, act, and adapt.