Alpha Notice: These docs cover the v1-alpha release. Content is incomplete and subject to change.For the latest stable version, see the current LangGraph Python or LangGraph JavaScript docs.
This guide explains the mechanics of using subgraphs. A subgraph is a graph that is used as a node in another graph.Subgraphs are useful for:
Distributing development: when you want different teams to work on different parts of the graph independently, you can define each part as a subgraph, and as long as the subgraph interface (the input and output schemas) is respected, the parent graph can be built without knowing any details of the subgraph
When adding subgraphs, you need to define how the parent graph and the subgraph communicate:
Set up LangSmith for LangGraph development
Sign up for LangSmith to quickly spot issues and improve the performance of your LangGraph projects. LangSmith lets you use trace data to debug, test, and monitor your LLM apps built with LangGraph — read more about how to get started here.
A simple way to implement a subgraph is to invoke a graph from inside the node of another graph. In this case subgraphs can have completely different schemas from the parent graph (no shared keys). For example, you might want to keep a private message history for each of the agents in a multi-agent system.If that’s the case for your application, you need to define a node function that invokes the subgraph. This function needs to transform the input (parent) state to the subgraph state before invoking the subgraph, and transform the results back to the parent state before returning the state update from the node.
Copy
from typing_extensions import TypedDictfrom langgraph.graph.state import StateGraph, STARTclass SubgraphState(TypedDict): bar: str# Subgraphdef subgraph_node_1(state: SubgraphState): return {"bar": "hi! " + state["bar"]}subgraph_builder = StateGraph(SubgraphState)subgraph_builder.add_node(subgraph_node_1)subgraph_builder.add_edge(START, "subgraph_node_1")subgraph = subgraph_builder.compile()# Parent graphclass State(TypedDict): foo: strdef call_subgraph(state: State): # Transform the state to the subgraph state subgraph_output = subgraph.invoke({"bar": state["foo"]}) # Transform response back to the parent state return {"foo": subgraph_output["bar"]}builder = StateGraph(State)builder.add_node("node_1", call_subgraph)builder.add_edge(START, "node_1")graph = builder.compile()
Full example: different state schemas
Copy
from typing_extensions import TypedDictfrom langgraph.graph.state import StateGraph, START# Define subgraphclass SubgraphState(TypedDict): # note that none of these keys are shared with the parent graph state bar: str baz: strdef subgraph_node_1(state: SubgraphState): return {"baz": "baz"}def subgraph_node_2(state: SubgraphState): return {"bar": state["bar"] + state["baz"]}subgraph_builder = StateGraph(SubgraphState)subgraph_builder.add_node(subgraph_node_1)subgraph_builder.add_node(subgraph_node_2)subgraph_builder.add_edge(START, "subgraph_node_1")subgraph_builder.add_edge("subgraph_node_1", "subgraph_node_2")subgraph = subgraph_builder.compile()# Define parent graphclass ParentState(TypedDict): foo: strdef node_1(state: ParentState): return {"foo": "hi! " + state["foo"]}def node_2(state: ParentState): # Transform the state to the subgraph state response = subgraph.invoke({"bar": state["foo"]}) # Transform response back to the parent state return {"foo": response["bar"]}builder = StateGraph(ParentState)builder.add_node("node_1", node_1)builder.add_node("node_2", node_2)builder.add_edge(START, "node_1")builder.add_edge("node_1", "node_2")graph = builder.compile()for chunk in graph.stream({"foo": "foo"}, subgraphs=True): print(chunk)
Copy
((), {'node_1': {'foo': 'hi! foo'}})(('node_2:9c36dd0f-151a-cb42-cbad-fa2f851f9ab7',), {'grandchild_1': {'my_grandchild_key': 'hi Bob, how are you'}})(('node_2:9c36dd0f-151a-cb42-cbad-fa2f851f9ab7',), {'grandchild_2': {'bar': 'hi! foobaz'}})((), {'node_2': {'foo': 'hi! foobaz'}})
Full example: different state schemas (two levels of subgraphs)
This is an example with two levels of subgraphs: parent -> child -> grandchild.
Copy
# Grandchild graphfrom typing_extensions import TypedDictfrom langgraph.graph.state import StateGraph, START, ENDclass GrandChildState(TypedDict): my_grandchild_key: strdef grandchild_1(state: GrandChildState) -> GrandChildState: # NOTE: child or parent keys will not be accessible here return {"my_grandchild_key": state["my_grandchild_key"] + ", how are you"}grandchild = StateGraph(GrandChildState)grandchild.add_node("grandchild_1", grandchild_1)grandchild.add_edge(START, "grandchild_1")grandchild.add_edge("grandchild_1", END)grandchild_graph = grandchild.compile()# Child graphclass ChildState(TypedDict): my_child_key: strdef call_grandchild_graph(state: ChildState) -> ChildState: # NOTE: parent or grandchild keys won't be accessible here grandchild_graph_input = {"my_grandchild_key": state["my_child_key"]} grandchild_graph_output = grandchild_graph.invoke(grandchild_graph_input) return {"my_child_key": grandchild_graph_output["my_grandchild_key"] + " today?"}child = StateGraph(ChildState)# We're passing a function here instead of just compiled graph (`grandchild_graph`)child.add_node("child_1", call_grandchild_graph)child.add_edge(START, "child_1")child.add_edge("child_1", END)child_graph = child.compile()# Parent graphclass ParentState(TypedDict): my_key: strdef parent_1(state: ParentState) -> ParentState: # NOTE: child or grandchild keys won't be accessible here return {"my_key": "hi " + state["my_key"]}def parent_2(state: ParentState) -> ParentState: return {"my_key": state["my_key"] + " bye!"}def call_child_graph(state: ParentState) -> ParentState: child_graph_input = {"my_child_key": state["my_key"]} child_graph_output = child_graph.invoke(child_graph_input) return {"my_key": child_graph_output["my_child_key"]}parent = StateGraph(ParentState)parent.add_node("parent_1", parent_1)# We're passing a function here instead of just a compiled graph (`child_graph`)parent.add_node("child", call_child_graph)parent.add_node("parent_2", parent_2)parent.add_edge(START, "parent_1")parent.add_edge("parent_1", "child")parent.add_edge("child", "parent_2")parent.add_edge("parent_2", END)parent_graph = parent.compile()for chunk in parent_graph.stream({"my_key": "Bob"}, subgraphs=True): print(chunk)
Copy
((), {'parent_1': {'my_key': 'hi Bob'}})(('child:2e26e9ce-602f-862c-aa66-1ea5a4655e3b', 'child_1:781bb3b1-3971-84ce-810b-acf819a03f9c'), {'grandchild_1': {'my_grandchild_key': 'hi Bob, how are you'}})(('child:2e26e9ce-602f-862c-aa66-1ea5a4655e3b',), {'child_1': {'my_child_key': 'hi Bob, how are you today?'}})((), {'child': {'my_key': 'hi Bob, how are you today?'}})((), {'parent_2': {'my_key': 'hi Bob, how are you today? bye!'}})
When the parent graph and subgraph can communicate over a shared state key (channel) in the schema, you can add a graph as a node in another graph. For example, in multi-agent systems, the agents often communicate over a shared messages key.If your subgraph shares state keys with the parent graph, you can follow these steps to add it to your graph:
Define the subgraph workflow (subgraph_builder in the example below) and compile it
Pass compiled subgraph to the .add_node method when defining the parent graph workflow
from typing_extensions import TypedDictfrom langgraph.graph.state import StateGraph, START# Define subgraphclass SubgraphState(TypedDict): foo: str # shared with parent graph state bar: str # private to SubgraphStatedef subgraph_node_1(state: SubgraphState): return {"bar": "bar"}def subgraph_node_2(state: SubgraphState): # note that this node is using a state key ('bar') that is only available in the subgraph # and is sending update on the shared state key ('foo') return {"foo": state["foo"] + state["bar"]}subgraph_builder = StateGraph(SubgraphState)subgraph_builder.add_node(subgraph_node_1)subgraph_builder.add_node(subgraph_node_2)subgraph_builder.add_edge(START, "subgraph_node_1")subgraph_builder.add_edge("subgraph_node_1", "subgraph_node_2")subgraph = subgraph_builder.compile()# Define parent graphclass ParentState(TypedDict): foo: strdef node_1(state: ParentState): return {"foo": "hi! " + state["foo"]}builder = StateGraph(ParentState)builder.add_node("node_1", node_1)builder.add_node("node_2", subgraph)builder.add_edge(START, "node_1")builder.add_edge("node_1", "node_2")graph = builder.compile()for chunk in graph.stream({"foo": "foo"}): print(chunk)
You only need to provide the checkpointer when compiling the parent graph. LangGraph will automatically propagate the checkpointer to the child subgraphs.
If you want the subgraph to have its own memory, you can compile it with the appropriate checkpointer option. This is useful in multi-agent systems, if you want agents to keep track of their internal message histories:
When you enable persistence, you can inspect the graph state (checkpoint) via the appropriate method. To view the subgraph state, you can use the subgraphs option.You can inspect the graph state via graph.get_state(config). To view the subgraph state, you can use graph.get_state(config, subgraphs=True).
Available only when interrupted
Subgraph state can only be viewed when the subgraph is interrupted. Once you resume the graph, you won’t be able to access the subgraph state.
View interrupted subgraph state
Copy
from langgraph.graph import START, StateGraphfrom langgraph.checkpoint.memory import MemorySaverfrom langgraph.types import interrupt, Commandfrom typing_extensions import TypedDictclass State(TypedDict): foo: str# Subgraphdef subgraph_node_1(state: State): value = interrupt("Provide value:") return {"foo": state["foo"] + value}subgraph_builder = StateGraph(State)subgraph_builder.add_node(subgraph_node_1)subgraph_builder.add_edge(START, "subgraph_node_1")subgraph = subgraph_builder.compile()# Parent graphbuilder = StateGraph(State)builder.add_node("node_1", subgraph)builder.add_edge(START, "node_1")checkpointer = MemorySaver()graph = builder.compile(checkpointer=checkpointer)config = {"configurable": {"thread_id": "1"}}graph.invoke({"foo": ""}, config)parent_state = graph.get_state(config)# This will be available only when the subgraph is interrupted.# Once you resume the graph, you won't be able to access the subgraph state.subgraph_state = graph.get_state(config, subgraphs=True).tasks[0].state# resume the subgraphgraph.invoke(Command(resume="bar"), config)
This will be available only when the subgraph is interrupted. Once you resume the graph, you won’t be able to access the subgraph state.
To include outputs from subgraphs in the streamed outputs, you can set the subgraphs option in the stream method of the parent graph. This will stream outputs from both the parent graph and any subgraphs.
Copy
for chunk in graph.stream( {"foo": "foo"}, subgraphs=True, stream_mode="updates",): print(chunk)
Stream from subgraphs
Copy
from typing_extensions import TypedDictfrom langgraph.graph.state import StateGraph, START# Define subgraphclass SubgraphState(TypedDict): foo: str bar: strdef subgraph_node_1(state: SubgraphState): return {"bar": "bar"}def subgraph_node_2(state: SubgraphState): # note that this node is using a state key ('bar') that is only available in the subgraph # and is sending update on the shared state key ('foo') return {"foo": state["foo"] + state["bar"]}subgraph_builder = StateGraph(SubgraphState)subgraph_builder.add_node(subgraph_node_1)subgraph_builder.add_node(subgraph_node_2)subgraph_builder.add_edge(START, "subgraph_node_1")subgraph_builder.add_edge("subgraph_node_1", "subgraph_node_2")subgraph = subgraph_builder.compile()# Define parent graphclass ParentState(TypedDict): foo: strdef node_1(state: ParentState): return {"foo": "hi! " + state["foo"]}builder = StateGraph(ParentState)builder.add_node("node_1", node_1)builder.add_node("node_2", subgraph)builder.add_edge(START, "node_1")builder.add_edge("node_1", "node_2")graph = builder.compile()for chunk in graph.stream( {"foo": "foo"}, stream_mode="updates", subgraphs=True, ): print(chunk)