Open in Colab Open in LangChain Academy

멀티 스키마(Multiple Schemas)

복습

이전 시간에 상태 스키마와 리듀서에 대해 다뤘습니다.

일반적으로 모든 그래프 노드는 단일 스키마와 통신합니다.

또한 이 단일 스키마는 그래프의 입력 및 출력 키/채널을 포함합니다.

목표

하지만 이에 대해 좀 더 세밀한 제어가 필요한 경우들이 있습니다:

  • 내부 노드들이 그래프의 입력/출력에는 필요하지 않은 정보를 전달할 수 있습니다.

  • 또한 그래프에 대해 서로 다른 입력/출력 스키마를 사용하고 싶을 수 있습니다. 예를 들어 출력에는 단일한 관련 출력 키만 포함될 수 있습니다.

다중 스키마로 그래프를 커스터마이징하는 몇 가지 방법에 대해 논의하겠습니다.

%%capture --no-stderr
%pip install --quiet -U langgraph

Private State

먼저 노드 간에 private state를 전달하는 경우를 다루겠습니다.

이는 그래프의 중간 작업 로직의 일부로 필요하지만, 전체 그래프 입력이나 출력과는 관련이 없는 모든 것에 유용합니다.

OverallStatePrivateState를 정의하겠습니다.

node_2PrivateState를 입력으로 사용하지만, OverallState로 출력합니다.

from typing import TypedDict
from IPython.display import Image, display
from langgraph.graph import StateGraph, START, END
 
 
class OverallState(TypedDict):
    foo: int
 
 
class PrivateState(TypedDict):
    baz: int
 
 
def node_1(state: OverallState) -> PrivateState:
    print("---Node 1---")
    return {"baz": state["foo"] + 1}
 
 
def node_2(state: PrivateState) -> OverallState:
    print("---Node 2---")
    return {"foo": state["baz"] + 1}
 
 
# Build graph
builder = StateGraph(OverallState)
builder.add_node("node_1", node_1)
builder.add_node("node_2", node_2)
 
# Logic
builder.add_edge(START, "node_1")
builder.add_edge("node_1", "node_2")
builder.add_edge("node_2", END)
 
# Add
graph = builder.compile()
 
# View
display(Image(graph.get_graph().draw_mermaid_png()))

graph.invoke({"foo": 1})
---Node 1---
---Node 2---





{'foo': 3}

bazPrivateState에만 포함됩니다.

node_2PrivateState를 입력으로 사용하지만, OverallState로 출력합니다.

따라서 bazOverallState에 없기 때문에 그래프 출력에서 제외되는 것을 확인할 수 있습니다.

입력/출력 스키마

기본적으로 StateGraph는 단일 스키마를 받으며, 모든 노드는 해당 스키마와 통신할 것으로 예상됩니다.

그러나 그래프에 대한 명시적인 입력 및 출력 스키마를 정의하는 것도 가능합니다.

이러한 경우 그래프 작업과 관련된 모든 키를 포함하는 “내부” 스키마를 정의하는 경우가 많습니다.

하지만 입력과 출력을 제한하기 위해 특정 inputoutput 스키마를 사용합니다.

먼저 단일 스키마로 그래프를 실행해보겠습니다.

class OverallState(TypedDict):
    question: str
    answer: str
    notes: str
 
 
def thinking_node(state: OverallState):
    return {"answer": "bye", "notes": "... his name is Lance"}
 
 
def answer_node(state: OverallState):
    return {"answer": "bye Lance"}
 
 
graph = StateGraph(OverallState)
graph.add_node("answer_node", answer_node)
graph.add_node("thinking_node", thinking_node)
graph.add_edge(START, "thinking_node")
graph.add_edge("thinking_node", "answer_node")
graph.add_edge("answer_node", END)
 
graph = graph.compile()
 
# View
display(Image(graph.get_graph().draw_mermaid_png()))

invoke의 출력에 OverallState의 모든 키가 포함되어 있는 것을 확인할 수 있습니다.

graph.invoke({"question": "hi"})
{'question': 'hi', 'answer': 'bye Lance', 'notes': '... his name is Lance'}

이제 그래프에 특정 inputoutput 스키마를 사용해보겠습니다.

여기서 input / output 스키마는 그래프의 입력과 출력에서 허용되는 키를 필터링합니다.

또한 타입 힌트 state: InputState를 사용하여 각 노드의 입력 스키마를 지정할 수 있습니다.

이는 그래프가 다중 스키마를 사용할 때 중요합니다.

아래에서는 예를 들어 answer_node의 출력이 OutputState로 필터링될 것임을 보여주기 위해 타입 힌트를 사용합니다.

class InputState(TypedDict):
    question: str
 
 
class OutputState(TypedDict):
    answer: str
 
 
class OverallState(TypedDict):
    question: str
    answer: str
    notes: str
 
 
def thinking_node(state: InputState):
    return {"answer": "bye", "notes": "... his is name is Lance"}
 
 
def answer_node(state: OverallState) -> OutputState:
    return {"answer": "bye Lance"}
 
 
graph = StateGraph(OverallState, input_schema=InputState, output_schema=OutputState)
graph.add_node("answer_node", answer_node)
graph.add_node("thinking_node", thinking_node)
graph.add_edge(START, "thinking_node")
graph.add_edge("thinking_node", "answer_node")
graph.add_edge("answer_node", END)
 
graph = graph.compile()
 
# View
display(Image(graph.get_graph().draw_mermaid_png()))
 
graph.invoke({"question": "hi"})

{'question': 'hi', 'answer': 'bye Lance', 'notes': '... his is name is Lance'}

output 스키마가 출력을 answer 키만으로 제한하는 것을 확인할 수 있습니다.