멀티 스키마(Multiple Schemas)
복습
이전 시간에 상태 스키마와 리듀서에 대해 다뤘습니다.
일반적으로 모든 그래프 노드는 단일 스키마와 통신합니다.
또한 이 단일 스키마는 그래프의 입력 및 출력 키/채널을 포함합니다.
목표
하지만 이에 대해 좀 더 세밀한 제어가 필요한 경우들이 있습니다:
-
내부 노드들이 그래프의 입력/출력에는 필요하지 않은 정보를 전달할 수 있습니다.
-
또한 그래프에 대해 서로 다른 입력/출력 스키마를 사용하고 싶을 수 있습니다. 예를 들어 출력에는 단일한 관련 출력 키만 포함될 수 있습니다.
다중 스키마로 그래프를 커스터마이징하는 몇 가지 방법에 대해 논의하겠습니다.
%%capture --no-stderr
%pip install --quiet -U langgraphPrivate State
먼저 노드 간에 private state를 전달하는 경우를 다루겠습니다.
이는 그래프의 중간 작업 로직의 일부로 필요하지만, 전체 그래프 입력이나 출력과는 관련이 없는 모든 것에 유용합니다.
OverallState와 PrivateState를 정의하겠습니다.
node_2는 PrivateState를 입력으로 사용하지만, 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}
baz는 PrivateState에만 포함됩니다.
node_2는 PrivateState를 입력으로 사용하지만, OverallState로 출력합니다.
따라서 baz는 OverallState에 없기 때문에 그래프 출력에서 제외되는 것을 확인할 수 있습니다.
입력/출력 스키마
기본적으로 StateGraph는 단일 스키마를 받으며, 모든 노드는 해당 스키마와 통신할 것으로 예상됩니다.
그러나 그래프에 대한 명시적인 입력 및 출력 스키마를 정의하는 것도 가능합니다.
이러한 경우 그래프 작업과 관련된 모든 키를 포함하는 “내부” 스키마를 정의하는 경우가 많습니다.
하지만 입력과 출력을 제한하기 위해 특정 input 및 output 스키마를 사용합니다.
먼저 단일 스키마로 그래프를 실행해보겠습니다.
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'}
이제 그래프에 특정 input 및 output 스키마를 사용해보겠습니다.
여기서 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 키만으로 제한하는 것을 확인할 수 있습니다.