# Subgraph A pattern for building hierarchical graph structures and modularizing complex systems. ## Overview Subgraph is a pattern for hierarchically organizing complex systems by **embedding graphs as nodes in other graphs**. ## Use Cases - Modularizing large-scale agent systems - Integrating multiple specialized agents - Reusable workflow components - Multi-level hierarchical structures ## Two Implementation Approaches ### Approach 1: Add Graph as Node Use when **sharing state keys**. ```python # Subgraph definition class SubState(TypedDict): messages: Annotated[list, add_messages] sub_result: str def sub_node_a(state: SubState): return {"messages": [{"role": "assistant", "content": "Sub A"}]} def sub_node_b(state: SubState): return {"sub_result": "Sub B completed"} # Build subgraph sub_builder = StateGraph(SubState) sub_builder.add_node("sub_a", sub_node_a) sub_builder.add_node("sub_b", sub_node_b) sub_builder.add_edge(START, "sub_a") sub_builder.add_edge("sub_a", "sub_b") sub_builder.add_edge("sub_b", END) sub_graph = sub_builder.compile() # Use subgraph as node in parent graph class ParentState(TypedDict): messages: Annotated[list, add_messages] # Shared key sub_result: str # Shared key parent_data: str parent_builder = StateGraph(ParentState) # Add subgraph directly as node parent_builder.add_node("subgraph", sub_graph) parent_builder.add_edge(START, "subgraph") parent_builder.add_edge("subgraph", END) parent_graph = parent_builder.compile() ``` ### Approach 2: Call Graph from Within Node Use when having **different state schemas**. ```python # Subgraph (own state) class SubGraphState(TypedDict): input_text: str output_text: str def process_node(state: SubGraphState): return {"output_text": process(state["input_text"])} sub_builder = StateGraph(SubGraphState) sub_builder.add_node("process", process_node) sub_builder.add_edge(START, "process") sub_builder.add_edge("process", END) sub_graph = sub_builder.compile() # Parent graph (different state) class ParentState(TypedDict): user_query: str result: str def invoke_subgraph_node(state: ParentState): """Call subgraph within node""" # Convert parent state to subgraph state sub_input = {"input_text": state["user_query"]} # Execute subgraph sub_output = sub_graph.invoke(sub_input) # Convert subgraph output to parent state return {"result": sub_output["output_text"]} parent_builder = StateGraph(ParentState) parent_builder.add_node("call_subgraph", invoke_subgraph_node) parent_builder.add_edge(START, "call_subgraph") parent_builder.add_edge("call_subgraph", END) parent_graph = parent_builder.compile() ``` ## Multi-Level Subgraphs Multiple levels of subgraphs (parent → child → grandchild) are also possible: ```python # Grandchild graph class GrandchildState(TypedDict): data: str grandchild_builder = StateGraph(GrandchildState) grandchild_builder.add_node("process", lambda s: {"data": f"Processed: {s['data']}"}) grandchild_builder.add_edge(START, "process") grandchild_builder.add_edge("process", END) grandchild_graph = grandchild_builder.compile() # Child graph (includes grandchild graph) class ChildState(TypedDict): data: str child_builder = StateGraph(ChildState) child_builder.add_node("grandchild", grandchild_graph) # Add grandchild graph child_builder.add_edge(START, "grandchild") child_builder.add_edge("grandchild", END) child_graph = child_builder.compile() # Parent graph (includes child graph) class ParentState(TypedDict): data: str parent_builder = StateGraph(ParentState) parent_builder.add_node("child", child_graph) # Add child graph parent_builder.add_edge(START, "child") parent_builder.add_edge("child", END) parent_graph = parent_builder.compile() ``` ## Navigation Between Subgraphs Transition from subgraph to another node in parent graph: ```python from langgraph.types import Command def sub_node_with_navigation(state: SubState): """Navigate from subgraph node to parent graph""" result = process(state["data"]) if need_parent_intervention(result): # Transition to another node in parent graph return Command( update={"result": result}, goto="parent_handler", graph=Command.PARENT ) return {"result": result} ``` ## Persistence and Debugging ### Automatic Checkpointer Propagation ```python from langgraph.checkpoint.memory import MemorySaver # Set checkpointer only on parent graph checkpointer = MemorySaver() parent_graph = parent_builder.compile( checkpointer=checkpointer # Automatically propagates to child graphs ) ``` ### Streaming Including Subgraph Output ```python # Stream including subgraph details for chunk in parent_graph.stream( inputs, stream_mode="values", subgraphs=True # Include subgraph output ): print(chunk) ``` ## Practical Example: Multi-Agent System ```python # Research agent (subgraph) class ResearchState(TypedDict): messages: Annotated[list, add_messages] research_result: str research_builder = StateGraph(ResearchState) research_builder.add_node("search", search_node) research_builder.add_node("analyze", analyze_node) research_builder.add_edge(START, "search") research_builder.add_edge("search", "analyze") research_builder.add_edge("analyze", END) research_graph = research_builder.compile() # Coding agent (subgraph) class CodingState(TypedDict): messages: Annotated[list, add_messages] code: str coding_builder = StateGraph(CodingState) coding_builder.add_node("generate", generate_code_node) coding_builder.add_node("test", test_code_node) coding_builder.add_edge(START, "generate") coding_builder.add_edge("generate", "test") coding_builder.add_edge("test", END) coding_graph = coding_builder.compile() # Integrated system (parent graph) class SystemState(TypedDict): messages: Annotated[list, add_messages] research_result: str code: str task_type: str def router(state: SystemState): if "research" in state["messages"][-1].content: return "research" return "coding" system_builder = StateGraph(SystemState) # Add subgraphs system_builder.add_node("research_agent", research_graph) system_builder.add_node("coding_agent", coding_graph) # Routing system_builder.add_conditional_edges( START, router, { "research": "research_agent", "coding": "coding_agent" } ) system_builder.add_edge("research_agent", END) system_builder.add_edge("coding_agent", END) system_graph = system_builder.compile() ``` ## Benefits ✅ **Modularization**: Divide complex systems into smaller parts ✅ **Reusability**: Use subgraphs in multiple parent graphs ✅ **Maintainability**: Improve each subgraph independently ✅ **Testability**: Test subgraphs individually ## Considerations ⚠️ **State Sharing**: Carefully design which keys to share ⚠️ **Debugging Complexity**: Deep hierarchies are hard to track ⚠️ **Performance**: Multi-level increases overhead ⚠️ **Circular References**: Watch for circular dependencies between subgraphs ## Best Practices 1. **Shallow Hierarchy**: Keep hierarchy as shallow as possible (2-3 levels) 2. **Clear Responsibilities**: Clearly define role of each subgraph 3. **Minimize State**: Share only necessary state keys 4. **Independence**: Subgraphs should operate as independently as possible ## Summary Subgraph is optimal for **hierarchical organization of complex systems**. Choose between two approaches depending on state sharing method. ## Related Pages - [02_graph_architecture_agent.md](02_graph_architecture_agent.md) - Combination with multi-agent - [01_core_concepts_state.md](01_core_concepts_state.md) - State design - [03_memory_management_persistence.md](03_memory_management_persistence.md) - Checkpointer propagation