Tried to create a minimal example similar to your original query
class State(TypedDict):
messages: Annotated[Sequence[BaseMessage], add_messages]
class Subgraph1State(TypedDict):
messages: Annotated[Sequence[BaseMessage], add_messages]
subgraph1_messages: Annotated[Sequence[BaseMessage], add_messages]
# Graph nodes
def subgraph_1_entry_node(state: State) -> Subgraph1State:
print(f"state in subgraph 1 entry node: {state}")
# Update the state to use subgraph 1 state for internal processing
state["messages"].append(HumanMessage(content="Inside subgraph 1 entry node"))
return {"subgraph1_messages": state["messages"]}
def subgraph_1_node2(state: Subgraph1State):
print(f"state in subgraph 1 node 2: {state}")
state["subgraph1_messages"].append(HumanMessage(content="Inside subgraph 1 node 2"))
# Now update the state to return main graph state as returning access to main graph
return {"messages": state["subgraph1_messages"]}
# Subgraph 1 compilation
subgraph_1_builder = StateGraph(Subgraph1State)
subgraph_1_builder.add_node("subgraph_1_entry_node", subgraph_1_entry_node)
subgraph_1_builder.add_node("subgraph_1_node2", subgraph_1_node2)
subgraph_1_builder.add_edge(START, "subgraph_1_entry_node")
subgraph_1_builder.add_edge("subgraph_1_entry_node", "subgraph_1_node2")
subgraph_1_builder.add_edge("subgraph_1_node2", END)
subgraph_1 = subgraph_1_builder.compile(name="subgraph_1")
class Subgraph2State(TypedDict):
messages: Annotated[Sequence[BaseMessage], add_messages]
subgraph2_messages: Annotated[Sequence[BaseMessage], add_messages]
# Graph nodes
def subgraph_2_entry_node(state: State) -> Subgraph2State:
print(f"state in subgraph 2 entry node: {state}")
# Update the state to use subgraph 2 state for internal processing
state["messages"].append(HumanMessage(content="Inside subgraph 2 entry node"))
return {"subgraph2_messages": state["messages"]}
def subgraph_2_node2(state: Subgraph2State):
print(f"state in subgraph 2 node 2: {state}")
state["subgraph2_messages"].append(HumanMessage(content="Inside subgraph 2 node 2"))
# Now update the state to return main graph state as returning access to main graph
return {"messages": state["subgraph2_messages"]}
# Subgraph 2 compilation
subgraph_2_builder = StateGraph(Subgraph2State)
subgraph_2_builder.add_node("subgraph_2_entry_node", subgraph_2_entry_node)
subgraph_2_builder.add_node("subgraph_2_node2", subgraph_2_node2)
subgraph_2_builder.add_edge(START, "subgraph_2_entry_node")
subgraph_2_builder.add_edge("subgraph_2_entry_node", "subgraph_2_node2")
subgraph_2_builder.add_edge("subgraph_2_node2", END)
subgraph_2 = subgraph_2_builder.compile(name="subgraph_2")
# Graph nodes - It will use same state as main graph
def subgraph_3_entry_node(state: State):
print(f"state in subgraph 3 entry node: {state}")
state["messages"].append(HumanMessage(content="Inside subgraph 3 entry node"))
return state
def subgraph_3_node2(state: State):
print(f"state in subgraph 3 node 2: {state}")
state["messages"].append(HumanMessage(content="Inside subgraph 3 node 2"))
return state
# Subgraph 3 compilation
subgraph_3_builder = StateGraph(State)
subgraph_3_builder.add_node("subgraph_3_entry_node", subgraph_3_entry_node)
subgraph_3_builder.add_node("subgraph_3_node2", subgraph_3_node2)
subgraph_3_builder.add_edge(START, "subgraph_3_entry_node")
subgraph_3_builder.add_edge("subgraph_3_entry_node", "subgraph_3_node2")
subgraph_3_builder.add_edge("subgraph_3_node2", END)
subgraph_3 = subgraph_3_builder.compile(name="subgraph_3")
def entry_node(state: State):
print(f"state in main graph entry node: {state}")
state["messages"].append(HumanMessage(content="Inside main graph entry node"))
return state
def node2(state: State):
print(f"state in main graph node 2: {state}")
state["messages"].append(HumanMessage(content="Inside main graph node 2"))
return state
# Main graph compilation
builder = StateGraph(State)
builder.add_node("entry_node", entry_node)
builder.add_node("subgraph_1", subgraph_1)
builder.add_node("subgraph_2", subgraph_2)
builder.add_node("subgraph_3", subgraph_3)
builder.add_node("node2", node2)
builder.add_edge(START, "entry_node")
builder.add_edge("entry_node", "subgraph_1")
builder.add_edge("entry_node", "subgraph_2")
builder.add_edge(["subgraph_1", "subgraph_2"], "subgraph_3")
builder.add_edge("subgraph_3", "node2")
builder.add_edge("node2", END)
main_graph = builder.compile(name="main_graph")
print(main_graph.get_graph(xray=1).draw_mermaid())
Graph is compiled like below:
Now invoked graph like below:
main_graph.invoke({"messages": [HumanMessage(content="Start the process with Hello in main graph entry node")]})
Response is bit verbose but lets focus on print statements with “state in subgraph1 entry node” and “state in subgraph 2 entry node”.
Subgraph 1 and Subgraph2 are at same level, so as per my understanding they will be executed in same superstep hence a copy of state will be provided as input to both and processing in each subgraph does not impact the state of other subgraph
After execution of main graph entry node state would look like below
{'messages': [HumanMessage(content='Start the process with Hello in main graph entry node', additional_kwargs={}, response_metadata={}, id='8895c3bd-b051-47ab-8945-71b0cbc64abf'), HumanMessage(content='Inside main graph entry node', additional_kwargs={}, response_metadata={}, id='18d8b08c-15a0-4ace-b0e0-1097a6ecac3a')
State in subgraph 1 and subgraph 2 is same as state object passed from main graph entry node, even though as per complete response subgraph1 was executed first but it didn’t changed the state for subgraph2 execution.
state in subgraph 1 entry node: {'messages': [HumanMessage(content='Start the process with Hello in main graph entry node', additional_kwargs={}, response_metadata={}, id='8895c3bd-b051-47ab-8945-71b0cbc64abf'), HumanMessage(content='Inside main graph entry node', additional_kwargs={}, response_metadata={}, id='18d8b08c-15a0-4ace-b0e0-1097a6ecac3a')]}
state in subgraph 2 entry node: {'messages': [HumanMessage(content='Start the process with Hello in main graph entry node', additional_kwargs={}, response_metadata={}, id='8895c3bd-b051-47ab-8945-71b0cbc64abf'), HumanMessage(content='Inside main graph entry node', additional_kwargs={}, response_metadata={}, id='18d8b08c-15a0-4ace-b0e0-1097a6ecac3a')]}
Complete response
state in main graph entry node: {'messages': [HumanMessage(content='Hello', additional_kwargs={}, response_metadata={}, id='0c9190c0-791e-4a07-86c6-bcdcef0a2cd6')]}
--- Node: entry_node ---
================================ Human Message =================================
Hello
================================ Human Message =================================
Inside main graph entry node
..
state in main graph entry node: {'messages': [HumanMessage(content='Start the process with Hello in main graph entry node', additional_kwargs={}, response_metadata={}, id='8895c3bd-b051-47ab-8945-71b0cbc64abf')]}
state in subgraph 1 entry node: {'messages': [HumanMessage(content='Start the process with Hello in main graph entry node', additional_kwargs={}, response_metadata={}, id='8895c3bd-b051-47ab-8945-71b0cbc64abf'), HumanMessage(content='Inside main graph entry node', additional_kwargs={}, response_metadata={}, id='18d8b08c-15a0-4ace-b0e0-1097a6ecac3a')]}
state in subgraph 1 node 2: {'messages': [HumanMessage(content='Start the process with Hello in main graph entry node', additional_kwargs={}, response_metadata={}, id='8895c3bd-b051-47ab-8945-71b0cbc64abf'), HumanMessage(content='Inside main graph entry node', additional_kwargs={}, response_metadata={}, id='18d8b08c-15a0-4ace-b0e0-1097a6ecac3a'), HumanMessage(content='Inside subgraph 1 entry node', additional_kwargs={}, response_metadata={}, id='6de6cc34-5104-499d-a97e-cd94e238ec60')], 'subgraph1_messages': [HumanMessage(content='Start the process with Hello in main graph entry node', additional_kwargs={}, response_metadata={}, id='8895c3bd-b051-47ab-8945-71b0cbc64abf'), HumanMessage(content='Inside main graph entry node', additional_kwargs={}, response_metadata={}, id='18d8b08c-15a0-4ace-b0e0-1097a6ecac3a'), HumanMessage(content='Inside subgraph 1 entry node', additional_kwargs={}, response_metadata={}, id='6de6cc34-5104-499d-a97e-cd94e238ec60')]}
state in subgraph 2 entry node: {'messages': [HumanMessage(content='Start the process with Hello in main graph entry node', additional_kwargs={}, response_metadata={}, id='8895c3bd-b051-47ab-8945-71b0cbc64abf'), HumanMessage(content='Inside main graph entry node', additional_kwargs={}, response_metadata={}, id='18d8b08c-15a0-4ace-b0e0-1097a6ecac3a')]}
state in subgraph 2 node 2: {'messages': [HumanMessage(content='Start the process with Hello in main graph entry node', additional_kwargs={}, response_metadata={}, id='8895c3bd-b051-47ab-8945-71b0cbc64abf'), HumanMessage(content='Inside main graph entry node', additional_kwargs={}, response_metadata={}, id='18d8b08c-15a0-4ace-b0e0-1097a6ecac3a'), HumanMessage(content='Inside subgraph 2 entry node', additional_kwargs={}, response_metadata={}, id='9e95196c-cb2a-450b-b895-96f7ae53edb1')], 'subgraph2_messages': [HumanMessage(content='Start the process with Hello in main graph entry node', additional_kwargs={}, response_metadata={}, id='8895c3bd-b051-47ab-8945-71b0cbc64abf'), HumanMessage(content='Inside main graph entry node', additional_kwargs={}, response_metadata={}, id='18d8b08c-15a0-4ace-b0e0-1097a6ecac3a'), HumanMessage(content='Inside subgraph 2 entry node', additional_kwargs={}, response_metadata={}, id='9e95196c-cb2a-450b-b895-96f7ae53edb1')]}
state in subgraph 3 entry node: {'messages': [HumanMessage(content='Start the process with Hello in main graph entry node', additional_kwargs={}, response_metadata={}, id='8895c3bd-b051-47ab-8945-71b0cbc64abf'), HumanMessage(content='Inside main graph entry node', additional_kwargs={}, response_metadata={}, id='18d8b08c-15a0-4ace-b0e0-1097a6ecac3a'), HumanMessage(content='Inside subgraph 1 entry node', additional_kwargs={}, response_metadata={}, id='6de6cc34-5104-499d-a97e-cd94e238ec60'), HumanMessage(content='Inside subgraph 1 node 2', additional_kwargs={}, response_metadata={}, id='f52650dc-e2d3-4e88-b98b-f6870b6e325c'), HumanMessage(content='Inside subgraph 2 entry node', additional_kwargs={}, response_metadata={}, id='9e95196c-cb2a-450b-b895-96f7ae53edb1'), HumanMessage(content='Inside subgraph 2 node 2', additional_kwargs={}, response_metadata={}, id='e3b0ee81-ca75-47de-8cbe-d583d8a0836d')]}
state in subgraph 3 node 2: {'messages': [HumanMessage(content='Start the process with Hello in main graph entry node', additional_kwargs={}, response_metadata={}, id='8895c3bd-b051-47ab-8945-71b0cbc64abf'), HumanMessage(content='Inside main graph entry node', additional_kwargs={}, response_metadata={}, id='18d8b08c-15a0-4ace-b0e0-1097a6ecac3a'), HumanMessage(content='Inside subgraph 1 entry node', additional_kwargs={}, response_metadata={}, id='6de6cc34-5104-499d-a97e-cd94e238ec60'), HumanMessage(content='Inside subgraph 1 node 2', additional_kwargs={}, response_metadata={}, id='f52650dc-e2d3-4e88-b98b-f6870b6e325c'), HumanMessage(content='Inside subgraph 2 entry node', additional_kwargs={}, response_metadata={}, id='9e95196c-cb2a-450b-b895-96f7ae53edb1'), HumanMessage(content='Inside subgraph 2 node 2', additional_kwargs={}, response_metadata={}, id='e3b0ee81-ca75-47de-8cbe-d583d8a0836d'), HumanMessage(content='Inside subgraph 3 entry node', additional_kwargs={}, response_metadata={}, id='bd4fd0cc-9aab-4cee-86c8-5470e5fb6498')]}
state in main graph node 2: {'messages': [HumanMessage(content='Start the process with Hello in main graph entry node', additional_kwargs={}, response_metadata={}, id='8895c3bd-b051-47ab-8945-71b0cbc64abf'), HumanMessage(content='Inside main graph entry node', additional_kwargs={}, response_metadata={}, id='18d8b08c-15a0-4ace-b0e0-1097a6ecac3a'), HumanMessage(content='Inside subgraph 1 entry node', additional_kwargs={}, response_metadata={}, id='6de6cc34-5104-499d-a97e-cd94e238ec60'), HumanMessage(content='Inside subgraph 1 node 2', additional_kwargs={}, response_metadata={}, id='f52650dc-e2d3-4e88-b98b-f6870b6e325c'), HumanMessage(content='Inside subgraph 2 entry node', additional_kwargs={}, response_metadata={}, id='9e95196c-cb2a-450b-b895-96f7ae53edb1'), HumanMessage(content='Inside subgraph 2 node 2', additional_kwargs={}, response_metadata={}, id='e3b0ee81-ca75-47de-8cbe-d583d8a0836d'), HumanMessage(content='Inside subgraph 3 entry node', additional_kwargs={}, response_metadata={}, id='bd4fd0cc-9aab-4cee-86c8-5470e5fb6498'), HumanMessage(content='Inside subgraph 3 node 2', additional_kwargs={}, response_metadata={}, id='e92dca0f-2236-4f3f-b336-e1d6d2f50e09')]}
Parallel execution: Use the Graph API is something that talks about this behavior.
Now for the question does subgraphs have isolated state?
I believe it depends on the implementation, if you have implemented like subgraph1 and subgraph2 above yes they are isolated but for subgraph3 that is not the case because in execution it comes after subgraph1 and subgraph2 execution so both of those updated the state and subgraph3 received the updated state
state in subgraph 3 entry node: {'messages': [HumanMessage(content='Start the process with Hello in main graph entry node', additional_kwargs={}, response_metadata={}, id='8895c3bd-b051-47ab-8945-71b0cbc64abf'), HumanMessage(content='Inside main graph entry node', additional_kwargs={}, response_metadata={}, id='18d8b08c-15a0-4ace-b0e0-1097a6ecac3a'), HumanMessage(content='Inside subgraph 1 entry node', additional_kwargs={}, response_metadata={}, id='6de6cc34-5104-499d-a97e-cd94e238ec60'), HumanMessage(content='Inside subgraph 1 node 2', additional_kwargs={}, response_metadata={}, id='f52650dc-e2d3-4e88-b98b-f6870b6e325c'), HumanMessage(content='Inside subgraph 2 entry node', additional_kwargs={}, response_metadata={}, id='9e95196c-cb2a-450b-b895-96f7ae53edb1'), HumanMessage(content='Inside subgraph 2 node 2', additional_kwargs={}, response_metadata={}, id='e3b0ee81-ca75-47de-8cbe-d583d8a0836d')]}
Hope this answers your question…