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 common application of subgraphs is to build multi-agent systems.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 common case is for the parent graph and subgraph to communicate over a shared state key (channel) in the schema. 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 (subgraphBuilder in the example below) and compile it
Pass compiled subgraph to the .addNode method when defining the parent graph workflow
Copy
import { StateGraph, START } from "@langchain/langgraph";import * as z from "zod";const State = z.object({ foo: z.string(),});// Subgraphconst subgraphBuilder = new StateGraph(State) .addNode("subgraphNode1", (state) => { return { foo: "hi! " + state.foo }; }) .addEdge(START, "subgraphNode1");const subgraph = subgraphBuilder.compile();// Parent graphconst builder = new StateGraph(State) .addNode("node1", subgraph) .addEdge(START, "node1");const graph = builder.compile();
Full example: shared state schemas
Copy
import { StateGraph, START } from "@langchain/langgraph";import * as z from "zod";// Define subgraphconst SubgraphState = z.object({ foo: z.string(), // (1)! bar: z.string(), // (2)!});const subgraphBuilder = new StateGraph(SubgraphState) .addNode("subgraphNode1", (state) => { return { bar: "bar" }; }) .addNode("subgraphNode2", (state) => { // 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 }; }) .addEdge(START, "subgraphNode1") .addEdge("subgraphNode1", "subgraphNode2");const subgraph = subgraphBuilder.compile();// Define parent graphconst ParentState = z.object({ foo: z.string(),});const builder = new StateGraph(ParentState) .addNode("node1", (state) => { return { foo: "hi! " + state.foo }; }) .addNode("node2", subgraph) .addEdge(START, "node1") .addEdge("node1", "node2");const graph = builder.compile();for await (const chunk of await graph.stream({ foo: "foo" })) { console.log(chunk);}
This key is shared with the parent graph state
This key is private to the SubgraphState and is not visible to the parent graph
For more complex systems you might want to define subgraphs that have a completely different schema 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
import { StateGraph, START } from "@langchain/langgraph";import * as z from "zod";const SubgraphState = z.object({ bar: z.string(),});// Subgraphconst subgraphBuilder = new StateGraph(SubgraphState) .addNode("subgraphNode1", (state) => { return { bar: "hi! " + state.bar }; }) .addEdge(START, "subgraphNode1");const subgraph = subgraphBuilder.compile();// Parent graphconst State = z.object({ foo: z.string(),});const builder = new StateGraph(State) .addNode("node1", async (state) => { const subgraphOutput = await subgraph.invoke({ bar: state.foo }); // (1)! return { foo: subgraphOutput.bar }; // (2)! }) .addEdge(START, "node1");const graph = builder.compile();
Transform the state to the subgraph state
Transform response back to the parent state
Full example: different state schemas
Copy
import { StateGraph, START } from "@langchain/langgraph";import * as z from "zod";// Define subgraphconst SubgraphState = z.object({ // note that none of these keys are shared with the parent graph state bar: z.string(), baz: z.string(),});const subgraphBuilder = new StateGraph(SubgraphState) .addNode("subgraphNode1", (state) => { return { baz: "baz" }; }) .addNode("subgraphNode2", (state) => { return { bar: state.bar + state.baz }; }) .addEdge(START, "subgraphNode1") .addEdge("subgraphNode1", "subgraphNode2");const subgraph = subgraphBuilder.compile();// Define parent graphconst ParentState = z.object({ foo: z.string(),});const builder = new StateGraph(ParentState) .addNode("node1", (state) => { return { foo: "hi! " + state.foo }; }) .addNode("node2", async (state) => { const response = await subgraph.invoke({ bar: state.foo }); // (1)! return { foo: response.bar }; // (2)! }) .addEdge(START, "node1") .addEdge("node1", "node2");const graph = builder.compile();for await (const chunk of await graph.stream( { foo: "foo" }, { subgraphs: true })) { console.log(chunk);}
You only need to provide the checkpointer when compiling the parent graph. LangGraph will automatically propagate the checkpointer to the child subgraphs.
Copy
import { StateGraph, START, MemorySaver } from "@langchain/langgraph";import * as z from "zod";const State = z.object({ foo: z.string(),});// Subgraphconst subgraphBuilder = new StateGraph(State) .addNode("subgraphNode1", (state) => { return { foo: state.foo + "bar" }; }) .addEdge(START, "subgraphNode1");const subgraph = subgraphBuilder.compile();// Parent graphconst builder = new StateGraph(State) .addNode("node1", subgraph) .addEdge(START, "node1");const checkpointer = new MemorySaver();const graph = builder.compile({ checkpointer });
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.getState(config). To view the subgraph state, you can use graph.getState(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
import { StateGraph, START, MemorySaver, interrupt, Command } from "@langchain/langgraph";import * as z from "zod";const State = z.object({ foo: z.string(),});// Subgraphconst subgraphBuilder = new StateGraph(State) .addNode("subgraphNode1", (state) => { const value = interrupt("Provide value:"); return { foo: state.foo + value }; }) .addEdge(START, "subgraphNode1");const subgraph = subgraphBuilder.compile();// Parent graphconst builder = new StateGraph(State) .addNode("node1", subgraph) .addEdge(START, "node1");const checkpointer = new MemorySaver();const graph = builder.compile({ checkpointer });const config = { configurable: { thread_id: "1" } };await graph.invoke({ foo: "" }, config);const parentState = await graph.getState(config);const subgraphState = (await graph.getState(config, { subgraphs: true })).tasks[0].state; // (1)!// resume the subgraphawait graph.invoke(new 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.