LangGraph에 자주 등장하는 파이썬 문법

김지우·2025년 1월 18일

목차
1. 정리 배경
2. TypedDict
3. Annotated
4. Reducer: add_message
5. 결론


1. 정리 배경

LangGraph를 더 능숙하게 써야 한다면 당연히 LangGraph에서 자주 쓰는 파이썬 문법은 능숙하게 다룰 수 있어야 한다. 해당 게시물은 테디 노트님의 유료 강의를 보고 내가 이해하기 좋게 작성한 글이다.

설명이 부족하다고 느끼신다면 테디노트님 유료 강의를 참고할 것을 추천한다.

2. TypedDict

TypedDict는 LangGraph에서 Dict 대신 사용하는 경우가 많으며, State 만들때 Dict 대신 자주 사용된다.

# TypedDict와 Dict의 차이점 예시
from typing import TypedDict


# TypedDict 사용
class Person(TypedDict):
    name: str
    age: int  # 정수형으로 명시
    job: str


typed_dict: Person = {"name": "지우", "age": 30, "job": "킹"}

print(typed_dict)
print(type(typed_dict))

==== 결과 ======
{'name': '지우', 'age': 30, 'job': '킹'}
<class 'dict'>

사실 TypeDict와 Dict가 생긴 것 자체는 크게 다르지 않다.
Class에 명시한대로 데이터를 입력하지 않아도 오류가 발생하지도 않는다. 다만 IDE를 통해 적절한 타입의 데이터를 넣었는지 재차 확인하게 되는 장점을 가지고 있고, class에 상속 받아 쓰게 되니, 개발하는 사람 입장에서 한 번 더 확인할 수 있게 되는 장점이 있다.

이게 필요하지 않을거라고 생각한 적이 사실 회사에서 개발하면서 있기는 한데, 실제 서비스단에서 LangGraph를 사용하게 될 때, 더 많은 양의 데이터를 입력하고 저장해야 했던 기억이 떠오르니 필수 !! 까지는 아니어도 그래도 꽤 유용하게 사용될 수 있으니 사용하는 습관을 들이면 좋겠다~ 정도의 생각이 든다.

3. Annotated

Annotated는 타입 힌트에 메타데이터(주석)을 추가하게 하는 기능이다. 다만 개인적으로는 시스템 전체를 개발하는 과정에서 Annotated가 매우 유용하게 사용될 수 있있는 부분이라고 생각한다.

우선 기본 사용은 보통 이렇게 한다.

from typing import Annotated

name: Annotated[str, "이름"]
age: Annotated[int, "나이"]

Pydantic과 사용하면 메타데이터를 더 제공하는 것을 뛰어 넘어 더 데이터 관련 오류를 집어 낼 수 있다.

from typing import Annotated, List
from pydantic import Field, BaseModel, ValidationError


class Employee(BaseModel):
    id: Annotated[int, Field(..., description="직원 ID")]
    name: Annotated[str, Field(..., min_length=3, max_length=50, description="이름")]
    age: Annotated[int, Field(gt=18, lt=65, description="나이 (19-64세)")]
    salary: Annotated[
        float, Field(gt=0, lt=10000, description="연봉 (단위: 만원, 최대 10억)")
    ]
    skills: Annotated[
        List[str], Field(min_items=1, max_items=10, description="보유 기술 (1-10개)")
    ]


# 유효한 데이터로 인스턴스 생성
try:
    valid_employee = Employee(
        id=1, name="지우님", age=30, salary=1000, skills=["Python", "LangGraph"]
    )
    print("유효한 직원 데이터:", valid_employee)
except ValidationError as e:
    print("유효성 검사 오류:", e)

# 유효하지 않은 데이터로 인스턴스 생성 시도
try:
    invalid_employee = Employee(
        name="지우상",  # 이름이 너무 짧음
        age=17,  # 나이가 범위를 벗어남
        salary=20000,  # 급여가 범위를 벗어남
        skills="JavaScript",  # 리스트가 아님
    )
except ValidationError as e:
    print("유효성 검사 오류:")
    for error in e.errors():
        print(f"- {error['loc'][0]}: {error['msg']}")

여기서 추가적으로 알아볼 수 있는 부분은 Field에 들어가는 값들에 대해 알아 볼 수 있을 것 같다.
1. ... => 무조건 들어가야 하는 필드라는 것
2. gt 들어가야 하는 최소 값
3. lt 들어가야 하는 최대 값

결과를 확인 해보면 Field를 활용해 지정한 값을 넣지 않았을 때 오류가 발생하는 것을 확인할 수 있다.

4. Reducer: add_message

LangGraph에서는 State를 이용해 노드 간 데이터를 전달하고, 전달한 데이터는 보통 이전에 있던 데이터에 덮어씌워진다.

그러나 덮어씌워지는 것이 아니라 리스트에 추가되어야 하는 경우가 있을 수 있다.(대화 내용이라거나) 그런 경우에 Reducer를 사용하고, LangGraph에서는 Reducer임을 알리기 위해 add_message를 사용한다.

보통은 이런식으로 사용되는 것으로 보이고

from typing import Annotated, TypedDict
from langgraph.graph import add_messages


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

add_message는 보통 이런 식으로 사용된다.

from langchain_core.messages import AIMessage, HumanMessage
from langgraph.graph import add_messages

# 기본 사용 예시
msgs1 = [HumanMessage(content="안녕하세요?", id="1")]
msgs2 = [AIMessage(content="반갑습니다~", id="2")]

result1 = add_messages(msgs1, msgs2)
print(result1)

===== 결과 =======
[HumanMessage(content='안녕하세요?', additional_kwargs={}, response_metadata={}, id='1'), AIMessage(content='반갑습니다~', additional_kwargs={}, response_metadata={}, id='2')]

다만 id의 값이 같다면 리스트에 데이터를 추가하는 것이 아니라 덮어쓰기를 하게 된다.

5. 결론

해당 문법들을 실행시켜 보고 정리 해보니 서비스 출시를 위한 개발에 데이터와 관련된 에러를 빠르게 찾아낼 수 있는데 도움이 되는 문법들로 보인다. 능숙하게 사용할 수 있어야 한다.

profile
프로그래밍 기록 + 공부 기록

0개의 댓글