메인 콘텐츠로 건너뛰기
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는 에이전트 워크플로를 그래프로 모델링합니다. 에이전트의 동작을 세 가지 핵심 구성 요소로 정의할 수 있습니다:
  1. State: 애플리케이션의 현재 스냅샷을 나타내는 공유 데이터 구조입니다. 어떤 데이터 타입이든 가능하지만, 일반적으로 공유 상태 스키마를 사용하여 정의합니다.
  2. Nodes: 에이전트의 로직을 인코딩하는 함수입니다. 현재 상태를 입력으로 받아 계산이나 부수 효과를 수행한 후 업데이트된 상태를 반환합니다.
  3. Edges: 현재 상태를 기반으로 다음에 실행할 Node를 결정하는 함수입니다. 조건부 분기 또는 고정 전환이 될 수 있습니다.
NodesEdges를 조합하면 시간이 지남에 따라 상태를 발전시키는 복잡하고 반복적인 워크플로를 만들 수 있습니다. 하지만 진정한 강력함은 LangGraph가 상태를 관리하는 방식에서 나옵니다. 강조하자면: NodesEdges는 함수에 불과합니다. LLM이 포함될 수도 있고 일반 코드만 있을 수도 있습니다. 요약하자면: 노드는 작업을 수행하고, 엣지는 다음에 무엇을 할지 알려줍니다. LangGraph의 기본 그래프 알고리즘은 메시지 전달을 사용하여 일반적인 프로그램을 정의합니다. Node가 작업을 완료하면 하나 이상의 엣지를 따라 다른 노드에 메시지를 보냅니다. 메시지를 받은 노드는 자신의 함수를 실행하고, 결과 메시지를 다음 노드 세트에 전달하며, 이 과정이 계속됩니다. Google의 Pregel 시스템에서 영감을 받아, 프로그램은 개별적인 “super-step”으로 진행됩니다. super-step은 그래프 노드에 대한 단일 반복으로 간주할 수 있습니다. 병렬로 실행되는 노드는 동일한 super-step의 일부이며, 순차적으로 실행되는 노드는 별도의 super-step에 속합니다. 그래프 실행이 시작될 때 모든 노드는 inactive 상태로 시작합니다. 노드는 들어오는 엣지(또는 “채널”) 중 하나에서 새 메시지(상태)를 받으면 active 상태가 됩니다. 활성 노드는 자신의 함수를 실행하고 업데이트로 응답합니다. 각 super-step이 끝날 때 들어오는 메시지가 없는 노드는 자신을 inactive로 표시하여 halt에 투표합니다. 모든 노드가 inactive이고 전송 중인 메시지가 없으면 그래프 실행이 종료됩니다.

StateGraph

StateGraph 클래스는 사용할 주요 그래프 클래스입니다. 이 클래스는 사용자 정의 State 객체로 매개변수화됩니다.

그래프 컴파일

그래프를 빌드하려면 먼저 상태를 정의하고, 노드엣지를 추가한 다음 컴파일합니다. 그래프 컴파일은 정확히 무엇이며 왜 필요할까요? 컴파일은 매우 간단한 단계입니다. 그래프 구조에 대한 몇 가지 기본 검사(고립된 노드가 없는지 등)를 제공합니다. 또한 checkpointers 및 중단점과 같은 런타임 인자를 지정할 수 있는 곳이기도 합니다. .compile 메서드를 호출하여 그래프를 컴파일합니다:
graph = graph_builder.compile(...)
그래프를 사용하기 전에 반드시 컴파일해야 합니다.

State

그래프를 정의할 때 가장 먼저 하는 일은 그래프의 State를 정의하는 것입니다. State그래프의 스키마와 상태에 업데이트를 적용하는 방법을 지정하는 reducer 함수로 구성됩니다. State의 스키마는 그래프의 모든 NodesEdges에 대한 입력 스키마가 되며, TypedDict 또는 Pydantic 모델이 될 수 있습니다. 모든 NodesState에 대한 업데이트를 발행하며, 이는 지정된 reducer 함수를 사용하여 적용됩니다.

Schema

그래프의 스키마를 지정하는 주요 방법은 TypedDict를 사용하는 것입니다. 상태에 기본값을 제공하려면 dataclass를 사용하세요. 재귀적 데이터 유효성 검사를 원한다면 Pydantic BaseModel을 그래프 상태로 사용할 수도 있습니다(다만 pydantic은 TypedDictdataclass보다 성능이 떨어집니다). 기본적으로 그래프는 동일한 입력 및 출력 스키마를 갖습니다. 이를 변경하려면 명시적 입력 및 출력 스키마를 직접 지정할 수도 있습니다. 이는 많은 키가 있고 일부는 명시적으로 입력용이고 다른 일부는 출력용인 경우에 유용합니다. 사용 방법은 여기 가이드를 참조하세요.

다중 스키마

일반적으로 모든 그래프 노드는 단일 스키마로 통신합니다. 즉, 동일한 상태 채널을 읽고 쓴다는 의미입니다. 하지만 이를 더 세밀하게 제어하려는 경우가 있습니다:
  • 내부 노드는 그래프의 입력/출력에 필요하지 않은 정보를 전달할 수 있습니다.
  • 그래프에 대해 서로 다른 입력/출력 스키마를 사용하고 싶을 수도 있습니다. 예를 들어 출력은 단일 관련 출력 키만 포함할 수 있습니다.
내부 노드 통신을 위해 그래프 내부의 비공개 상태 채널에 노드가 쓰도록 할 수 있습니다. 비공개 스키마인 PrivateState를 정의하기만 하면 됩니다. 그래프에 대한 명시적 입력 및 출력 스키마를 정의할 수도 있습니다. 이 경우 그래프 작업과 관련된 모든 키를 포함하는 “내부” 스키마를 정의합니다. 하지만 그래프의 입력 및 출력을 제한하기 위해 “내부” 스키마의 하위 집합인 inputoutput 스키마도 정의합니다. 자세한 내용은 이 가이드를 참조하세요. 예시를 살펴보겠습니다:
class InputState(TypedDict):
    user_input: str

class OutputState(TypedDict):
    graph_output: str

class OverallState(TypedDict):
    foo: str
    user_input: str
    graph_output: str

class PrivateState(TypedDict):
    bar: str

def node_1(state: InputState) -> OverallState:
    # OverallState에 쓰기
    return {"foo": state["user_input"] + " name"}

def node_2(state: OverallState) -> PrivateState:
    # OverallState에서 읽고 PrivateState에 쓰기
    return {"bar": state["foo"] + " is"}

def node_3(state: PrivateState) -> OutputState:
    # PrivateState에서 읽고 OutputState에 쓰기
    return {"graph_output": state["bar"] + " Lance"}

builder = StateGraph(OverallState,input_schema=InputState,output_schema=OutputState)
builder.add_node("node_1", node_1)
builder.add_node("node_2", node_2)
builder.add_node("node_3", node_3)
builder.add_edge(START, "node_1")
builder.add_edge("node_1", "node_2")
builder.add_edge("node_2", "node_3")
builder.add_edge("node_3", END)

graph = builder.compile()
graph.invoke({"user_input":"My"})
# {'graph_output': 'My name is Lance'}
여기서 주목해야 할 두 가지 미묘하고 중요한 사항이 있습니다:
  1. node_1의 입력 스키마로 state: InputState를 전달합니다. 하지만 OverallState의 채널인 foo에 씁니다. 입력 스키마에 포함되지 않은 상태 채널에 어떻게 쓸 수 있을까요? 이는 노드가 그래프 상태의 모든 상태 채널에 쓸 수 있기 때문입니다. 그래프 상태는 초기화 시 정의된 상태 채널의 합집합이며, 여기에는 OverallState와 필터 InputStateOutputState가 포함됩니다.
  2. StateGraph(OverallState,input_schema=InputState,output_schema=OutputState)로 그래프를 초기화합니다. 그렇다면 node_2에서 PrivateState에 어떻게 쓸 수 있을까요? StateGraph 초기화에 전달되지 않은 스키마에 그래프가 어떻게 액세스할 수 있을까요? 이는 노드가 상태 스키마 정의가 존재하는 한 추가 상태 채널을 선언할 수도 있기 때문입니다. 이 경우 PrivateState 스키마가 정의되어 있으므로 그래프에 bar를 새 상태 채널로 추가하고 여기에 쓸 수 있습니다.

Reducers

Reducer는 노드의 업데이트가 State에 적용되는 방식을 이해하는 핵심입니다. State의 각 키에는 고유한 독립적인 reducer 함수가 있습니다. reducer 함수가 명시적으로 지정되지 않으면 해당 키에 대한 모든 업데이트가 이를 재정의한다고 가정합니다. 기본 타입의 reducer부터 시작하여 몇 가지 다른 타입의 reducer가 있습니다:

기본 Reducer

다음 두 예시는 기본 reducer를 사용하는 방법을 보여줍니다: 예시 A:
from typing_extensions import TypedDict

class State(TypedDict):
    foo: int
    bar: list[str]
이 예시에서는 어떤 키에도 reducer 함수가 지정되지 않았습니다. 그래프에 대한 입력이 다음과 같다고 가정해 봅시다: {"foo": 1, "bar": ["hi"]}. 그런 다음 첫 번째 Node{"foo": 2}를 반환한다고 가정합니다. 이는 상태에 대한 업데이트로 처리됩니다. Node는 전체 State 스키마를 반환할 필요가 없으며 업데이트만 반환하면 됩니다. 이 업데이트를 적용하면 State{"foo": 2, "bar": ["hi"]}가 됩니다. 두 번째 노드가 {"bar": ["bye"]}를 반환하면 State{"foo": 2, "bar": ["bye"]}가 됩니다. 예시 B:
from typing import Annotated
from typing_extensions import TypedDict
from operator import add

class State(TypedDict):
    foo: int
    bar: Annotated[list[str], add]
이 예시에서는 Annotated 타입을 사용하여 두 번째 키(bar)에 대한 reducer 함수(operator.add)를 지정했습니다. 첫 번째 키는 변경되지 않습니다. 그래프에 대한 입력이 {"foo": 1, "bar": ["hi"]}라고 가정해 봅시다. 그런 다음 첫 번째 Node{"foo": 2}를 반환한다고 가정합니다. 이는 상태에 대한 업데이트로 처리됩니다. Node는 전체 State 스키마를 반환할 필요가 없으며 업데이트만 반환하면 됩니다. 이 업데이트를 적용하면 State{"foo": 2, "bar": ["hi"]}가 됩니다. 두 번째 노드가 {"bar": ["bye"]}를 반환하면 State{"foo": 2, "bar": ["hi", "bye"]}가 됩니다. 여기서 bar 키는 두 리스트를 함께 더하는 방식으로 업데이트됩니다.

그래프 상태에서 메시지 작업하기

메시지를 사용하는 이유는?

대부분의 최신 LLM 제공업체는 메시지 리스트를 입력으로 받는 채팅 모델 인터페이스를 제공합니다. 특히 LangChain의 ChatModelMessage 객체의 리스트를 입력으로 받습니다. 이러한 메시지는 HumanMessage(사용자 입력) 또는 AIMessage(LLM 응답)와 같은 다양한 형태로 제공됩니다. 메시지 객체가 무엇인지 자세히 알아보려면 이 개념 가이드를 참조하세요.

그래프에서 메시지 사용하기

많은 경우, 이전 대화 기록을 메시지 리스트로 그래프 상태에 저장하는 것이 유용합니다. 이렇게 하려면 그래프 상태에 Message 객체 리스트를 저장하는 키(채널)를 추가하고 reducer 함수로 주석을 달 수 있습니다(아래 예시의 messages 키 참조). reducer 함수는 각 상태 업데이트마다 상태의 Message 객체 리스트를 업데이트하는 방법을 그래프에 알려주는 데 필수적입니다(예: 노드가 업데이트를 보낼 때). reducer를 지정하지 않으면 모든 상태 업데이트가 가장 최근에 제공된 값으로 메시지 리스트를 덮어씁니다. 단순히 기존 리스트에 메시지를 추가하려면 reducer로 operator.add를 사용할 수 있습니다. 하지만 그래프 상태에서 메시지를 수동으로 업데이트하고 싶을 수도 있습니다(예: human-in-the-loop). operator.add를 사용하면 그래프에 보내는 수동 상태 업데이트가 기존 메시지를 업데이트하는 대신 기존 메시지 리스트에 추가됩니다. 이를 방지하려면 메시지 ID를 추적하고 업데이트된 경우 기존 메시지를 덮어쓸 수 있는 reducer가 필요합니다. 이를 위해 사전 빌드된 add_messages 함수를 사용할 수 있습니다. 새로운 메시지의 경우 기존 리스트에 단순히 추가하지만, 기존 메시지에 대한 업데이트도 올바르게 처리합니다.

직렬화

메시지 ID를 추적하는 것 외에도, add_messages 함수는 messages 채널에서 상태 업데이트를 받을 때마다 메시지를 LangChain Message 객체로 역직렬화하려고 시도합니다. LangChain 직렬화/역직렬화에 대한 자세한 정보는 여기를 참조하세요. 이를 통해 다음 형식으로 그래프 입력/상태 업데이트를 보낼 수 있습니다:
# 이것이 지원됩니다
{"messages": [HumanMessage(content="message")]}

# 이것도 지원됩니다
{"messages": [{"type": "human", "content": "message"}]}
add_messages를 사용할 때 상태 업데이트는 항상 LangChain Messages로 역직렬화되므로 state["messages"][-1].content와 같이 점 표기법을 사용하여 메시지 속성에 액세스해야 합니다. 다음은 add_messages를 reducer 함수로 사용하는 그래프의 예입니다.
from langchain.messages import AnyMessage
from langgraph.graph.message import add_messages
from typing import Annotated
from typing_extensions import TypedDict

class GraphState(TypedDict):
    messages: Annotated[list[AnyMessage], add_messages]

MessagesState

상태에 메시지 리스트를 포함하는 것이 매우 일반적이므로 메시지를 쉽게 사용할 수 있도록 MessagesState라는 사전 빌드된 상태가 있습니다. MessagesStateAnyMessage 객체의 리스트인 단일 messages 키로 정의되며 add_messages reducer를 사용합니다. 일반적으로 메시지만이 아닌 더 많은 상태를 추적해야 하므로 다음과 같이 이 상태를 서브클래싱하고 더 많은 필드를 추가하는 것을 볼 수 있습니다:
from langgraph.graph import MessagesState

class State(MessagesState):
    documents: list[str]

Nodes

LangGraph에서 노드는 다음 인자를 받는 Python 함수(동기 또는 비동기)입니다:
  1. state: 그래프의 상태
  2. config: thread_id와 같은 구성 정보 및 tags와 같은 추적 정보를 포함하는 RunnableConfig 객체
  3. runtime: 런타임 contextstore, stream_writer와 같은 기타 정보를 포함하는 Runtime 객체
NetworkX와 유사하게, add_node 메서드를 사용하여 그래프에 이러한 노드를 추가합니다:
from dataclasses import dataclass
from typing_extensions import TypedDict

from langchain_core.runnables import RunnableConfig
from langgraph.graph import StateGraph
from langgraph.runtime import Runtime

class State(TypedDict):
    input: str
    results: str

@dataclass
class Context:
    user_id: str

builder = StateGraph(State)

def plain_node(state: State):
    return state

def node_with_runtime(state: State, runtime: Runtime[Context]):
    print("In node: ", runtime.context.user_id)
    return {"results": f"Hello, {state['input']}!"}

def node_with_config(state: State, config: RunnableConfig):
    print("In node with thread_id: ", config["configurable"]["thread_id"])
    return {"results": f"Hello, {state['input']}!"}


builder.add_node("plain_node", plain_node)
builder.add_node("node_with_runtime", node_with_runtime)
builder.add_node("node_with_config", node_with_config)
...
내부적으로 함수는 RunnableLambda로 변환되어 함수에 배치 및 비동기 지원과 함께 네이티브 추적 및 디버깅 기능이 추가됩니다. 이름을 지정하지 않고 그래프에 노드를 추가하면 함수 이름과 동일한 기본 이름이 지정됩니다.
builder.add_node(my_node)
# 그런 다음 이 노드를 "my_node"로 참조하여 엣지를 생성할 수 있습니다

START 노드

START 노드는 사용자 입력을 그래프에 보내는 노드를 나타내는 특수 노드입니다. 이 노드를 참조하는 주요 목적은 어떤 노드를 먼저 호출해야 하는지 결정하는 것입니다.
from langgraph.graph import START

graph.add_edge(START, "node_a")

END 노드

END 노드는 종료 노드를 나타내는 특수 노드입니다. 이 노드는 완료된 후 작업이 없는 엣지를 나타낼 때 참조됩니다.
from langgraph.graph import END

graph.add_edge("node_a", END)

노드 캐싱

LangGraph는 노드에 대한 입력을 기반으로 작업/노드 캐싱을 지원합니다. 캐싱을 사용하려면:
  • 그래프를 컴파일할 때(또는 진입점을 지정할 때) 캐시를 지정합니다
  • 노드에 대한 캐시 정책을 지정합니다. 각 캐시 정책은 다음을 지원합니다:
    • key_func: 노드에 대한 입력을 기반으로 캐시 키를 생성하는 데 사용되며, 기본적으로 pickle을 사용한 입력의 hash입니다.
    • ttl: 캐시의 수명(초)입니다. 지정하지 않으면 캐시가 만료되지 않습니다.
예를 들어:
import time
from typing_extensions import TypedDict
from langgraph.graph import StateGraph
from langgraph.cache.memory import InMemoryCache
from langgraph.types import CachePolicy


class State(TypedDict):
    x: int
    result: int


builder = StateGraph(State)


def expensive_node(state: State) -> dict[str, int]:
    # 비용이 많이 드는 계산
    time.sleep(2)
    return {"result": state["x"] * 2}


builder.add_node("expensive_node", expensive_node, cache_policy=CachePolicy(ttl=3))
builder.set_entry_point("expensive_node")
builder.set_finish_point("expensive_node")

graph = builder.compile(cache=InMemoryCache())

print(graph.invoke({"x": 5}, stream_mode='updates'))  # (1)!
[{'expensive_node': {'result': 10}}]
print(graph.invoke({"x": 5}, stream_mode='updates'))  # (2)!
[{'expensive_node': {'result': 10}, '__metadata__': {'cached': True}}]
  1. 첫 번째 실행은 (모의 비용이 많이 드는 계산으로 인해) 2초가 걸립니다.
  2. 두 번째 실행은 캐시를 활용하여 빠르게 반환됩니다.

Edges

엣지는 로직이 라우팅되는 방식과 그래프가 중지를 결정하는 방식을 정의합니다. 이는 에이전트가 작동하는 방식과 서로 다른 노드가 통신하는 방식의 큰 부분입니다. 몇 가지 주요 유형의 엣지가 있습니다:
  • Normal Edges: 한 노드에서 다음 노드로 직접 이동합니다.
  • Conditional Edges: 함수를 호출하여 다음에 이동할 노드를 결정합니다.
  • Entry Point: 사용자 입력이 도착할 때 먼저 호출할 노드입니다.
  • Conditional Entry Point: 함수를 호출하여 사용자 입력이 도착할 때 먼저 호출할 노드를 결정합니다.
노드는 여러 개의 나가는 엣지를 가질 수 있습니다. 노드에 여러 개의 나가는 엣지가 있으면 해당 대상 노드 모두가 다음 superstep의 일부로 병렬로 실행됩니다.

Normal Edges

노드 A에서 노드 B로 항상 이동하려면 add_edge 메서드를 직접 사용할 수 있습니다.
graph.add_edge("node_a", "node_b")

Conditional Edges

선택적으로 하나 이상의 엣지로 라우팅하거나(또는 선택적으로 종료하려면) add_conditional_edges 메서드를 사용할 수 있습니다. 이 메서드는 노드의 이름과 해당 노드가 실행된 후 호출할 “라우팅 함수”를 받습니다:
graph.add_conditional_edges("node_a", routing_function)
노드와 유사하게, routing_function은 그래프의 현재 state를 받아 값을 반환합니다. 기본적으로 routing_function의 반환 값은 다음에 상태를 보낼 노드(또는 노드 리스트)의 이름으로 사용됩니다. 이러한 모든 노드는 다음 superstep의 일부로 병렬로 실행됩니다. 선택적으로 routing_function의 출력을 다음 노드의 이름에 매핑하는 딕셔너리를 제공할 수 있습니다.
graph.add_conditional_edges("node_a", routing_function, {True: "node_b", False: "node_c"})
상태 업데이트와 라우팅을 단일 함수로 결합하려면 조건부 엣지 대신 Command를 사용하세요.

Entry Point

진입점은 그래프가 시작될 때 실행되는 첫 번째 노드입니다. 가상 START 노드에서 실행할 첫 번째 노드로 add_edge 메서드를 사용하여 그래프에 진입할 위치를 지정할 수 있습니다.
from langgraph.graph import START

graph.add_edge(START, "node_a")

Conditional Entry Point

조건부 진입점을 사용하면 커스텀 로직에 따라 서로 다른 노드에서 시작할 수 있습니다. 가상 START 노드에서 add_conditional_edges를 사용하여 이를 수행할 수 있습니다.
from langgraph.graph import START

graph.add_conditional_edges(START, routing_function)
선택적으로 routing_function의 출력을 다음 노드의 이름에 매핑하는 딕셔너리를 제공할 수 있습니다.
graph.add_conditional_edges(START, routing_function, {True: "node_b", False: "node_c"})

Send

기본적으로 NodesEdges는 사전에 정의되며 동일한 공유 상태에서 작동합니다. 하지만 정확한 엣지를 미리 알 수 없거나 동시에 서로 다른 버전의 State가 존재하기를 원할 수 있습니다. 이에 대한 일반적인 예는 map-reduce 디자인 패턴입니다. 이 디자인 패턴에서 첫 번째 노드는 객체 리스트를 생성할 수 있으며, 이러한 모든 객체에 다른 노드를 적용하고 싶을 수 있습니다. 객체의 수를 미리 알 수 없을 수 있으며(즉, 엣지의 수를 알 수 없을 수 있음) 다운스트림 Node에 대한 입력 State는 달라야 합니다(생성된 각 객체에 대해 하나씩). 이 디자인 패턴을 지원하기 위해 LangGraph는 조건부 엣지에서 Send 객체를 반환하는 것을 지원합니다. Send는 두 개의 인자를 받습니다: 첫 번째는 노드의 이름이고 두 번째는 해당 노드에 전달할 상태입니다.
def continue_to_jokes(state: OverallState):
    return [Send("generate_joke", {"subject": s}) for s in state['subjects']]

graph.add_conditional_edges("node_a", continue_to_jokes)

Command

제어 흐름(엣지)과 상태 업데이트(노드)를 결합하는 것이 유용할 수 있습니다. 예를 들어, 동일한 노드에서 상태 업데이트를 수행하고 다음에 갈 노드를 결정하고 싶을 수 있습니다. LangGraph는 노드 함수에서 Command 객체를 반환하여 이를 수행할 수 있는 방법을 제공합니다:
def my_node(state: State) -> Command[Literal["my_other_node"]]:
    return Command(
        # 상태 업데이트
        update={"foo": "bar"},
        # 제어 흐름
        goto="my_other_node"
    )
Command를 사용하면 동적 제어 흐름 동작(조건부 엣지와 동일)도 달성할 수 있습니다:
def my_node(state: State) -> Command[Literal["my_other_node"]]:
    if state["foo"] == "bar":
        return Command(update={"foo": "baz"}, goto="my_other_node")
노드 함수에서 Command를 반환할 때 노드가 라우팅하는 노드 이름 리스트와 함께 반환 타입 주석을 추가해야 합니다. 예: Command[Literal["my_other_node"]]. 이는 그래프 렌더링에 필요하며 my_nodemy_other_node로 이동할 수 있음을 LangGraph에 알립니다.
Command 사용 방법에 대한 엔드투엔드 예시는 이 가이드를 확인하세요.

언제 조건부 엣지 대신 Command를 사용해야 할까요?

  • 그래프 상태를 업데이트하고 다른 노드로 라우팅해야 할 때 Command를 사용하세요. 예를 들어, 다른 에이전트로 라우팅하고 해당 에이전트에 일부 정보를 전달하는 것이 중요한 멀티 에이전트 핸드오프를 구현할 때입니다.
  • 상태를 업데이트하지 않고 조건부로 노드 간을 라우팅하려면 조건부 엣지를 사용하세요.

부모 그래프의 노드로 이동하기

서브그래프를 사용하는 경우, 서브그래프 내의 노드에서 다른 서브그래프(즉, 부모 그래프의 다른 노드)로 이동하고 싶을 수 있습니다. 이렇게 하려면 Command에서 graph=Command.PARENT를 지정할 수 있습니다:
def my_node(state: State) -> Command[Literal["other_subgraph"]]:
    return Command(
        update={"foo": "bar"},
        goto="other_subgraph",  # 여기서 `other_subgraph`는 부모 그래프의 노드입니다
        graph=Command.PARENT
    )
graphCommand.PARENT로 설정하면 가장 가까운 부모 그래프로 이동합니다.서브그래프 노드에서 부모 그래프 노드로 업데이트를 보낼 때 부모와 서브그래프 상태 스키마 모두에서 공유하는 키에 대해 부모 그래프 상태에서 업데이트하는 키에 대한 reducer반드시 정의해야 합니다. 예시를 참조하세요.
이는 멀티 에이전트 핸드오프를 구현할 때 특히 유용합니다. 자세한 내용은 이 가이드를 확인하세요.

도구 내부에서 사용하기

일반적인 사용 사례는 도구 내부에서 그래프 상태를 업데이트하는 것입니다. 예를 들어, 고객 지원 애플리케이션에서 대화 시작 부분에 계정 번호 또는 ID를 기반으로 고객 정보를 조회하고 싶을 수 있습니다. 자세한 내용은 이 가이드를 참조하세요.

Human-in-the-loop

Command는 human-in-the-loop 워크플로의 중요한 부분입니다: interrupt()를 사용하여 사용자 입력을 수집할 때 CommandCommand(resume="User input")을 통해 입력을 제공하고 실행을 재개하는 데 사용됩니다. 자세한 정보는 이 개념 가이드를 확인하세요.

그래프 마이그레이션

LangGraph는 checkpointer를 사용하여 상태를 추적하는 경우에도 그래프 정의(노드, 엣지 및 상태)의 마이그레이션을 쉽게 처리할 수 있습니다.
  • 그래프 끝에 있는 스레드(즉, 중단되지 않음)의 경우 그래프의 전체 토폴로지를 변경할 수 있습니다(즉, 모든 노드와 엣지를 제거, 추가, 이름 변경 등).
  • 현재 중단된 스레드의 경우 노드 이름 변경/제거 이외의 모든 토폴로지 변경을 지원합니다(해당 스레드가 더 이상 존재하지 않는 노드에 진입하려고 할 수 있으므로). 이것이 문제가 된다면 연락 주시면 솔루션의 우선순위를 정할 수 있습니다.
  • 상태를 수정하는 경우 키 추가 및 제거에 대한 완전한 하위 및 상위 호환성이 있습니다.
  • 이름이 변경된 상태 키는 기존 스레드에서 저장된 상태를 잃습니다.
  • 타입이 호환되지 않는 방식으로 변경된 상태 키는 현재 변경 전 상태가 있는 스레드에서 문제를 일으킬 수 있습니다. 이것이 문제가 된다면 연락 주시면 솔루션의 우선순위를 정할 수 있습니다.

런타임 컨텍스트

그래프를 생성할 때 노드에 전달되는 런타임 컨텍스트에 대한 context_schema를 지정할 수 있습니다. 이는 그래프 상태의 일부가 아닌 정보를 노드에 전달하는 데 유용합니다. 예를 들어, 모델 이름이나 데이터베이스 연결과 같은 종속성을 전달하고 싶을 수 있습니다.
@dataclass
class ContextSchema:
    llm_provider: str = "openai"

graph = StateGraph(State, context_schema=ContextSchema)
그런 다음 invoke 메서드의 context 매개변수를 사용하여 이 컨텍스트를 그래프에 전달할 수 있습니다.
graph.invoke(inputs, context={"llm_provider": "anthropic"})
그런 다음 노드 또는 조건부 엣지 내부에서 이 컨텍스트에 액세스하고 사용할 수 있습니다:
from langgraph.runtime import Runtime

def node_a(state: State, runtime: Runtime[ContextSchema]):
    llm = get_llm(runtime.context.llm_provider)
    # ...
구성에 대한 전체 분석은 이 가이드를 참조하세요.

재귀 제한

재귀 제한은 단일 실행 중에 그래프가 실행할 수 있는 최대 super-step 수를 설정합니다. 제한에 도달하면 LangGraph는 GraphRecursionError를 발생시킵니다. 기본적으로 이 값은 25 단계로 설정됩니다. 재귀 제한은 런타임 시 모든 그래프에서 설정할 수 있으며, config 딕셔너리를 통해 .invoke/.stream에 전달됩니다. 중요한 점은 recursion_limit가 독립적인 config 키이며 다른 모든 사용자 정의 구성처럼 configurable 키 내부에 전달되어서는 안 된다는 것입니다. 아래 예시를 참조하세요:
graph.invoke(inputs, config={"recursion_limit": 5}, context={"llm": "anthropic"})
재귀 제한이 작동하는 방식에 대한 자세한 내용은 이 가이드를 읽어보세요.

시각화

그래프가 복잡해짐에 따라 그래프를 시각화할 수 있는 것이 종종 유용합니다. LangGraph는 그래프를 시각화하는 몇 가지 내장 방법을 제공합니다. 자세한 정보는 이 가이드를 참조하세요.
I