테마
단기 메모리 구현
InMemorySaver를 활용한 대화 내역 저장과 활용
학습 목표
- InMemorySaver의 동작 원리와 특성(휘발성, 딕셔너리 기반)을 이해한다.
- thread_id를 활용하여 대화별 메모리를 분리하는 방법을 익힌다.
- State에서 정보를 읽고 쓰는 패턴을 구현할 수 있다.
- CreateReactAgent와 Graph Workflow 두 가지 구현 방식의 차이를 구분하고, 상황에 맞는 방식을 선택할 수 있다.
1. InMemorySaver 개요
LangGraph에서 Short-term Memory(단기 메모리) 를 구현하는 가장 간단한 방법은 InMemorySaver를 사용하는 것이다. 이 체크포인터는 순수 파이썬 딕셔너리를 기반으로 대화 상태를 메모리에 저장한다.
주요 특징
| 구분 | 설명 |
|---|---|
| 저장 방식 | 순수 파이썬 딕셔너리(dict) 기반 |
| 장점 | 설정이 간단하고, 빠른 실험과 테스트에 유용하다 |
| 단점 | 프로세스가 종료되면 데이터가 모두 소실된다 (휘발성) |
| 키 구분 | thread_id별로 대화 내역을 자동으로 구분하여 저장한다 |
동작 흐름
InMemorySaver는 graph.invoke() 호출 시 config에 지정된 thread_id를 기준으로 대화 내역을 자동으로 저장하고, 다음 호출 시 이전 대화를 자동으로 참조한다.
thread_id가 다르면 완전히 별개의 대화로 취급된다. 이를 통해 하나의 그래프 인스턴스로 여러 사용자의 대화를 동시에 관리할 수 있다.
2. 기본 구현 패턴
InMemorySaver를 사용하려면 세 단계를 거친다.
Step 1: InMemorySaver import 및 선언
langgraph.checkpoint.memory 모듈에서 MemorySaver를 가져와 인스턴스를 생성한다.
Step 2: 그래프 컴파일 시 checkpointer 인자로 설정
builder.compile() 호출 시 checkpointer 인자에 생성한 인스턴스를 전달한다.
Step 3: invoke 호출 시 configurable에 thread_id 설정
graph.invoke() 호출 시 config 딕셔너리의 configurable 안에 thread_id를 지정한다.
python
from langgraph.checkpoint.memory import MemorySaver
# Step 1: InMemorySaver 선언
checkpointer = MemorySaver()
# Step 2: 그래프 컴파일 시 checkpointer 설정
graph = builder.compile(checkpointer=checkpointer)
# Step 3: invoke 호출 시 thread_id 지정
config = {"configurable": {"thread_id": "user_1"}}
graph.invoke({"messages": [msg]}, config=config)
# 같은 thread_id로 다시 호출하면 이전 대화를 이어받는다
graph.invoke({"messages": [new_msg]}, config=config)구현 흐름 다이어그램
핵심 포인트: checkpointer를 설정하지 않으면 매 호출마다 상태가 초기화된다. thread_id를 지정하지 않으면 모든 호출이 같은 대화로 취급되어 의도치 않게 상태가 섞일 수 있다.
3. 메모리 읽기 패턴 (State에서 정보 활용)
단기 메모리의 핵심 활용 중 하나는 State에 저장된 정보를 Tool에서 읽어와 답변에 활용하는 것이다. 예를 들어 State에 userId가 저장되어 있으면, Tool이 이 값을 읽어 사용자 정보를 조회하고 LLM의 답변에 반영할 수 있다.
동작 원리
getUserInfo Tool이 State의 userId를 읽어 사용자 이름을 반환하는 예시다.
python
def getUserInfo(state):
"""State에서 userId를 읽어 사용자 정보를 반환한다."""
user_id = state.get("userId", None)
if user_id == "user_123":
return "John Smith"
else:
return "unknown user"읽기 패턴 시퀀스
이 패턴에서 중요한 점은 Tool이 State에 직접 접근할 수 있다는 것이다. LangGraph의 Tool은 State 객체를 인자로 받을 수 있으므로, 별도의 외부 저장소 없이도 State에 저장된 값을 활용하여 동적인 응답을 생성할 수 있다.
4. 메모리 쓰기 패턴 (State 정보 갱신)
읽기만으로는 부족한 경우가 있다. 실행 도중 State의 값을 갱신하고, 갱신된 값을 후속 Tool에서 읽어 활용하는 패턴도 필요하다.
동작 원리
updateUserInfoTool이 State의userName을 새로운 값으로 갱신한다.greetTool이 갱신된userName을 읽어와 인사 메시지를 생성한다.
python
def updateUserInfo(state, new_name):
"""State의 userName을 갱신한다."""
return {"userName": new_name}
def greet(state):
"""State에서 userName을 읽어 인사 메시지를 반환한다."""
name = state.get("userName", "Guest")
return f"안녕하세요, {name}님! 무엇을 도와드릴까요?"쓰기 + 읽기 조합 흐름
이처럼 읽기와 쓰기를 조합하면, 하나의 그래프 실행 안에서 Tool이 State를 동적으로 변경하고, 변경된 정보를 기반으로 후속 처리를 수행할 수 있다. 이것이 Dynamic Runtime Context의 핵심 장점이다.
주의사항: State 갱신은 Tool이 반환하는 딕셔너리를 통해 이루어진다. 직접 State 객체를 수정하는 것이 아니라, 갱신할 키-값 쌍을 반환하면 LangGraph가 State를 업데이트한다.
5. 두 가지 구현 방식 비교
LangGraph에서 단기 메모리를 활용하는 에이전트를 구현하는 방법은 크게 두 가지다.
CreateReactAgent 방식
create_react_agent() 함수를 사용하면 최소한의 코드로 에이전트를 생성할 수 있다. 내부적으로 ReAct 패턴(Reasoning + Acting)을 따르며, tools와 state_schema만 지정하면 된다.
python
from langgraph.prebuilt import create_react_agent
agent = create_react_agent(
model=llm,
tools=[getUserInfo, greet],
checkpointer=checkpointer,
state_schema=State
)Graph Workflow 방식
StateGraph를 사용하여 노드와 엣지를 직접 정의하는 방식이다. 그래프의 흐름을 세밀하게 제어할 수 있지만, 코드량이 많아진다.
python
from langgraph.graph import StateGraph, END
builder = StateGraph(State)
# 노드 추가
builder.add_node("llm_node", call_llm)
builder.add_node("tool_node", call_tool)
# 엣지 정의
builder.set_entry_point("llm_node")
builder.add_conditional_edges("llm_node", should_call_tool)
builder.add_edge("tool_node", "llm_node")
graph = builder.compile(checkpointer=checkpointer)비교 테이블
| 구분 | CreateReactAgent | Graph Workflow |
|---|---|---|
| 복잡도 | 낮음 | 높음 |
| 유연성 | 제한적 | 높음 |
| 적합한 경우 | 간단한 도구 호출 | 복잡한 워크플로 |
| 코드량 | 적음 | 많음 |
| 그래프 구조 | 자동 생성 | 직접 설계 |
| 조건부 분기 | 제한적 | 자유롭게 설정 가능 |
선택 기준: 단순히 LLM이 Tool을 호출하는 패턴이라면 CreateReactAgent로 충분하다. 하지만 노드 간 분기 로직, 병렬 처리, 커스텀 에러 핸들링 등 세밀한 제어가 필요하다면 Graph Workflow를 사용한다.
6. 핵심 정리
InMemorySaver는 LangGraph에서 단기 메모리를 구현하는 가장 간단한 방법이다. 파이썬 딕셔너리 기반으로 동작하며,
thread_id를 통해 대화별 상태를 자동으로 분리한다.
- 기본 패턴:
MemorySaver선언 -> 그래프 컴파일 시checkpointer설정 ->invoke시thread_id지정의 3단계로 구성된다.- 읽기 패턴: Tool이 State에 저장된 정보(userId 등)를 읽어와 동적인 응답을 생성한다.
- 쓰기 패턴: Tool이 State의 값을 갱신하고, 후속 Tool이 갱신된 값을 활용한다. 읽기와 쓰기의 조합으로 동적 메모리를 구현한다.
- 구현 방식 선택: 간단한 도구 호출에는
CreateReactAgent, 복잡한 워크플로에는Graph Workflow를 사용한다.InMemorySaver는 프로세스 종료 시 데이터가 소실되는 휘발성 메모리이므로, 프로덕션 환경에서는 영속성을 보장하는 별도의 체크포인터(예: PostgreSQL 기반)를 사용해야 한다.