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.
이 가이드에서는 서브그래프를 사용하는 메커니즘을 설명합니다. 서브그래프는 다른 그래프에서 노드로 사용되는 그래프입니다.서브그래프는 다음과 같은 경우에 유용합니다:
LangGraph 개발을 위한 LangSmith 설정LangSmith에 가입하여 LangGraph 프로젝트의 문제를 신속하게 파악하고 성능을 개선하세요. LangSmith를 사용하면 트레이스 데이터를 활용하여 LangGraph로 구축한 LLM 앱을 디버깅, 테스트 및 모니터링할 수 있습니다 — 시작하는 방법에 대한 자세한 내용은 여기에서 확인하세요.
서브그래프를 구현하는 간단한 방법은 다른 그래프의 노드 내부에서 그래프를 호출하는 것입니다. 이 경우 서브그래프는 부모 그래프와 완전히 다른 스키마를 가질 수 있습니다(공유 키 없음). 예를 들어, 멀티 에이전트 시스템의 각 에이전트에 대해 비공개 메시지 히스토리를 유지하고 싶을 수 있습니다.애플리케이션에서 이러한 경우에 해당한다면, 서브그래프를 호출하는 노드 함수를 정의해야 합니다. 이 함수는 서브그래프를 호출하기 전에 입력(부모) 상태를 서브그래프 상태로 변환하고, 결과를 다시 부모 상태로 변환한 후 노드에서 상태 업데이트를 반환해야 합니다.
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()
전체 예제: 다른 상태 스키마
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'}})
전체 예제: 다른 상태 스키마 (두 단계의 서브그래프)
다음은 두 단계의 서브그래프를 사용하는 예제입니다: 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!'}})
부모 그래프와 서브그래프가 스키마의 공유 상태 키(채널)를 통해 통신할 수 있는 경우, 그래프를 다른 그래프의 노드로 추가할 수 있습니다. 예를 들어, 멀티 에이전트 시스템에서는 에이전트들이 종종 공유된 메시지 키를 통해 통신합니다.서브그래프가 부모 그래프와 상태 키를 공유하는 경우, 다음 단계에 따라 그래프에 추가할 수 있습니다:
서브그래프 워크플로우(아래 예제의 subgraph_builder)를 정의하고 컴파일합니다
부모 그래프 워크플로우를 정의할 때 .add_node 메서드에 컴파일된 서브그래프를 전달합니다
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)
영속성을 활성화하면, 적절한 메서드를 통해 그래프 상태를 검사(체크포인트)할 수 있습니다. 서브그래프 상태를 보려면 subgraphs 옵션을 사용할 수 있습니다.graph.get_state(config)를 통해 그래프 상태를 검사할 수 있습니다. 서브그래프 상태를 보려면 graph.get_state(config, subgraphs=True)를 사용할 수 있습니다.
중단되었을 때만 사용 가능
서브그래프 상태는 서브그래프가 중단되었을 때만 볼 수 있습니다. 그래프를 재개하면 서브그래프 상태에 액세스할 수 없습니다.
중단된 서브그래프 상태 보기
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)
이는 서브그래프가 중단되었을 때만 사용 가능합니다. 그래프를 재개하면 서브그래프 상태에 액세스할 수 없습니다.
스트리밍 출력에 서브그래프의 출력을 포함하려면, 부모 그래프의 stream 메서드에서 subgraphs 옵션을 설정할 수 있습니다. 이렇게 하면 부모 그래프와 모든 서브그래프의 출력이 스트리밍됩니다.
Copy
for chunk in graph.stream( {"foo": "foo"}, subgraphs=True, stream_mode="updates",): print(chunk)
서브그래프에서 스트리밍
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)