배경 및 목적
이번 13기 AI 스터디에 앞서, 1DAY 1랭그래프, 즉 하루에 1개의 랭그래프 개념을 설명하는 부분을 기획하게 됐습니다.
-> 랭그래프 스터디에 관심이 있다면 신청해주세요!
스터디 신청링크
이 프로젝트의 목표는 랭그래프를 쉽게 입문하실 수 있게 상세한 개념부터 코드까지 함께 공부해보실 수 있게 정리해서 드릴 예정입니다.
저는 랭그래프를 처음들어봐요!, 1일차 글을 보고 싶다면!?
-> 링크로 접속하기!
참고 자료
활용 툴
파이썬, 코랩, 랭그래프, 랭체인
핵심 요약: LangGraph를 활용한 상태 스키마 정의
0. 핵심 내용 3줄 요약
다양한 상태 스키마 정의 방법: LangGraph에서
TypedDict
,Dataclass
,Pydantic
을 사용하여 그래프의 상태 구조와 데이터 타입을 유연하게 정의할 수 있습니다.유효성 검사 및 타입 안전성: Pydantic을 활용하면 런타임 시 데이터의 유효성을 검증하여 안정적이고 신뢰할 수 있는 상태 관리를 구현할 수 있습니다.
상태 스키마의 중요성: 명확한 상태 스키마 정의를 통해 LangGraph 내에서 데이터 흐름을 일관되게 관리하고, 복잡한 AI 에이전트를 효과적으로 구축할 수 있습니다.
복잡한 로직의 AI 에이전트를 만들기 위해서는 state를 잘 정의해야 하는데, 그 부분에 대해 실수하거나, 명확하게 하기 위해 3가지 방법 ( TypedDict
, Dataclass
, Pydantic
) 중에 하나를 택할 수 있고,
특히 Pydantic은 틀에 벗어나면 바로 Error를 Raise 할 수 있게 설계 돼있습니다.
이번 그래프 예시는 다음과 같습니다.
1. 소개 및 검토
- 목표: 모듈 1에서 구축한 에이전트의 기능을 바탕으로, 이번 모듈에서는 상태(State)와 메모리(Memory)에 대한 깊은 이해를 바탕으로 상태 스키마(State Schema)를 정의하는 다양한 방법을 학습합니다.
- 기능 검토:
- act
: 모델이 특정 도구를 호출하도록 함
- observe
: 도구 출력을 모델에 전달
- reason
: 도구 출력에 기반하여 다음 행동 결정
- persist state
: 메모리 내 체크포인터를 사용하여 장기 대화 지원
2. 환경 설정
- 라이브러리 설치: langgraph
라이브러리를 설치합니다.
%%capture --no-stderr
%pip install --quiet -U langgraph
3. 상태 스키마 정의 방법
LangGraph에서 StateGraph
를 정의할 때, 다양한 방식으로 상태 스키마를 설정할 수 있습니다. 주요 방법으로는 TypedDict
, Dataclass
, Pydantic
이 있습니다.
3.1. TypedDict 사용
- 개념: Python의 TypedDict
를 사용하여 상태의 키와 값 유형을 정의합니다.
- 특징: 정적 타입 검사기나 IDE에서 타입 오류를 사전에 잡을 수 있으나, 런타임에는 타입이 강제되지 않습니다.
- 예시:
from typing_extensions import TypedDict
class TypedDictState(TypedDict):
foo: str
bar: str
from typing import Literal
class TypedDictState(TypedDict):
name: str
mood: Literal["happy","sad"]
3.2. Dataclass 사용
- 개념: Python의 dataclasses
를 사용하여 구조화된 데이터를 정의합니다.
- 특징: 간결한 구문으로 데이터 저장을 위한 클래스를 생성할 수 있습니다.
- 예시:
from dataclasses import dataclass
from typing import Literal
@dataclass
class DataclassState:
name: str
mood: Literal["happy","sad"]
3.3. Pydantic 사용
- 개념: Pydantic을 사용하여 데이터 유효성 검사와 설정 관리를 수행합니다.
- 특징: 런타임에 데이터가 지정된 타입과 제약 조건을 준수하는지 검증할 수 있습니다.
- 예시:
from pydantic import BaseModel, field_validator, ValidationError
from typing import Literal
class PydanticState(BaseModel):
name: str
mood: Literal["happy","sad"]
@field_validator('mood')
@classmethod
def validate_mood(cls, value):
if value not in ["happy", "sad"]:
raise ValueError("Each mood must be either 'happy' or 'sad'")
return value
try:
state = PydanticState(name="John Doe", mood="sad")
except ValidationError as e:
print("Validation Error:", e)
4. 그래프 구축 예시
- 상태 정의: TypedDict
또는 Dataclass
를 사용하여 상태 스키마를 정의합니다.
- 노드 정의: 각 노드는 상태를 받아 새로운 상태를 반환하는 함수로 정의됩니다.
- 에지 정의: 노드 간의 연결을 설정하여 그래프의 흐름을 제어합니다.
- 예시 코드 (TypedDict 사용):
import random
from IPython.display import Image, display
from langgraph.graph import StateGraph, START, END
from typing import Literal
def node_1(state):
print("---Node 1---")
return {"name": state['name'] + " is ... "}
def node_2(state):
print("---Node 2---")
return {"mood": "happy"}
def node_3(state):
print("---Node 3---")
return {"mood": "sad"}
def decide_mood(state) -> Literal["node_2", "node_3"]:
if random.random() < 0.5:
return "node_2"
return "node_3"
# Build graph
builder = StateGraph(TypedDictState)
builder.add_node("node_1", node_1)
builder.add_node("node_2", node_2)
builder.add_node("node_3", node_3)
# Logic
builder.add_edge(START, "node_1")
builder.add_conditional_edges("node_1", decide_mood)
builder.add_edge("node_2", END)
builder.add_edge("node_3", END)
# Add
graph = builder.compile()
# View
display(Image(graph.get_graph().draw_mermaid_png()))
- 예시 코드 (Dataclass 사용):
from dataclasses import dataclass
from typing import Literal
from langgraph.graph import StateGraph, START, END
from IPython.display import Image, display
@dataclass
class DataclassState:
name: str
mood: Literal["happy","sad"]
def node_1(state):
print("---Node 1---")
return {"name": state.name + " is ... "}
# Build graph
builder = StateGraph(DataclassState)
builder.add_node("node_1", node_1)
builder.add_node("node_2", node_2)
builder.add_node("node_3", node_3)
# Logic
builder.add_edge(START, "node_1")
builder.add_conditional_edges("node_1", decide_mood)
builder.add_edge("node_2", END)
builder.add_edge("node_3", END)
# Add
graph = builder.compile()
# View
display(Image(graph.get_graph().draw_mermaid_png()))
- 예시 코드 (Pydantic 사용):
from pydantic import BaseModel, field_validator, ValidationError
from typing import Literal
from langgraph.graph import StateGraph, START, END
from IPython.display import Image, display
class PydanticState(BaseModel):
name: str
mood: Literal["happy","sad"]
@field_validator('mood')
@classmethod
def validate_mood(cls, value):
if value not in ["happy", "sad"]:
raise ValueError("Each mood must be either 'happy' or 'sad'")
return value
def node_1(state):
print("---Node 1---")
return {"name": state.name + " is ... "}
# Build graph
builder = StateGraph(PydanticState)
builder.add_node("node_1", node_1)
builder.add_node("node_2", node_2)
builder.add_node("node_3", node_3)
# Logic
builder.add_edge(START, "node_1")
builder.add_conditional_edges("node_1", decide_mood)
builder.add_edge("node_2", END)
builder.add_edge("node_3", END)
# Add
graph = builder.compile()
# View
display(Image(graph.get_graph().draw_mermaid_png()))
# Invoke
graph.invoke(PydanticState(name="Lance", mood="sad"))
5. 그래프 실행
- 초기 상태 설정: 정의한 상태 스키마에 맞는 초기 값을 설정하여 그래프를 호출합니다.
- TypedDict 예시:
graph.invoke({"name":"Lance"})
- Dataclass 예시:
graph.invoke(DataclassState(name="Lance", mood="sad"))
- Pydantic 예시:
state = PydanticState(name="John Doe", mood="sad")
graph.invoke(state)
6. 결론
- 상태 스키마의 중요성: 상태 스키마를 명확히 정의함으로써 그래프 내에서 데이터의 구조와 타입을 일관되게 관리할 수 있습니다.
- 유연한 정의 방법: TypedDict
, Dataclass
, Pydantic
등 다양한 방법을 통해 상황에 맞는 상태 스키마를 선택할 수 있습니다.
- 타입 검증: 특히 Pydantic을 사용하면 런타임에 데이터의 유효성을 검증할 수 있어 안정적인 그래프 구축이 가능합니다.
랭그래프, AI 에이전트 구현에 관심이 생겼나요?
->지금바로 랭그래프 스터디에 관심이 있다면 신청해주세요!
지피터스 13기 스터디 신청링크