번역 자료 / / 2025. 5. 23. 07:55

[langgraph] 메모리(Memory)

[langgraph] 메모리

원문 출처: https://langchain-ai.github.io/langgraph/agents/memory/

LangGraph는 대화형 에이전트 구축에 필수적인 두 가지 메모리 유형을 지원합니다:

  • 단기 메모리(Short-term memory): 세션 내에서 메시지 히스토리를 유지하며, 진행 중인 대화를 추적합니다.
  • 장기 메모리(Long-term memory): 여러 세션에 걸쳐 사용자별 또는 애플리케이션 레벨의 데이터를 저장합니다.

이 가이드에서는 LangGraph에서 두 가지 메모리 유형을 사용하는 방법을 설명합니다. 메모리 개념에 대한 더 깊은 이해는 LangGraph 메모리 문서를 참고하세요.

단기 메모리장기 메모리 모두 LLM 상호작용 간 연속성을 유지하려면 영속적 저장소가 필요합니다. 실제 서비스 환경에서는 데이터베이스에 저장하는 것이 일반적입니다.

용어 정리

  • 단기 메모리 = 스레드 레벨 메모리(thread-level memory)
  • 장기 메모리 = 크로스 스레드 메모리(cross-thread memory)
  • 스레드(thread)는 동일한 thread_id로 그룹화된 일련의 관련 실행(run)입니다.

단기 메모리(Short-term memory)

단기 메모리를 사용하면 에이전트가 다중 턴 대화를 추적할 수 있습니다. 사용 방법:

  1. 에이전트 생성 시 checkpointer를 제공합니다. checkpointer는 에이전트 상태의 영속화를 담당합니다.
  2. 에이전트 실행 시 config에 고유한 thread_id를 전달합니다. 이 값은 대화 세션을 식별하는 고유 문자열입니다.

API Reference: create_react_agent | InMemorySaver

from langgraph.prebuilt import create_react_agent
from langgraph.checkpoint.memory import InMemorySaver

checkpointer = InMemorySaver() # (1)!

def get_weather(city: str) -> str:
    """주어진 도시의 날씨를 조회합니다."""
    return f"{city}의 날씨는 항상 맑음!"

agent = create_react_agent(
    model="anthropic:claude-3-7-sonnet-latest",
    tools=[get_weather],
    checkpointer=checkpointer # (2)!
)

# 에이전트 실행
config = {
    "configurable": {
        "thread_id": "1"  # (3)!
    }
}

sf_response = agent.invoke(
    {"messages": [{"role": "user", "content": "what is the weather in sf"}]},
    config
)

# 같은 thread_id로 대화 이어가기
ny_response = agent.invoke(
    {"messages": [{"role": "user", "content": "what about new york?"}]},
    config # (4)!
)
  1. InMemorySaver는 메모리에 상태를 저장하는 checkpointer입니다. 실제 서비스에서는 데이터베이스 등 영속 저장소를 사용하세요. LangGraph Platform을 사용하면 프로덕션용 checkpointer가 자동 제공됩니다.
  2. 에이전트에 checkpointer를 전달합니다. 이를 통해 여러 호출 간 상태가 유지됩니다.
  3. config에 고유한 thread_id를 전달합니다. 이 값은 대화 세션을 식별합니다.
  4. 같은 thread_id로 호출하면 이전 메시지 히스토리가 자동으로 포함되어, 예를 들어 뉴욕 날씨를 물을 때도 대화 맥락이 유지됩니다.

메시지 히스토리 관리(Manage message history)

긴 대화는 LLM의 컨텍스트 윈도우를 초과할 수 있습니다. 대표적인 해결책은:

  • 요약(summarization): 대화 내용을 요약해 유지
  • 트리밍(trimming): 히스토리에서 앞/뒤 N개 메시지 삭제

이렇게 하면 LLM의 컨텍스트 한계를 넘지 않으면서 대화 맥락을 유지할 수 있습니다.

메시지 히스토리 관리를 위해 pre_model_hook(모델 호출 전 항상 실행되는 함수/노드)을 지정할 수 있습니다.

메시지 히스토리 요약(Summarize message history)

긴 대화는 LLM의 컨텍스트 윈도우를 초과할 수 있습니다. 대표적 해결책은 대화 내용을 요약해 유지하는 것입니다.

pre_model_hook에 SummarizationNode를 사용하면 됩니다.

API Reference: ChatAnthropic | count_tokens_approximately | create_react_agent | AgentState | InMemorySaver

from langchain_anthropic import ChatAnthropic
from langmem.short_term import SummarizationNode
from langchain_core.messages.utils import count_tokens_approximately
from langgraph.prebuilt import create_react_agent
from langgraph.prebuilt.chat_agent_executor import AgentState
from langgraph.checkpoint.memory import InMemorySaver
from typing import Any

model = ChatAnthropic(model="claude-3-7-sonnet-latest")

summarization_node = SummarizationNode(
    token_counter=count_tokens_approximately,
    model=model,
    max_tokens=384,
    max_summary_tokens=128,
    output_messages_key="llm_input_messages",
)

class State(AgentState):
    context: dict[str, Any]  # 요약 정보 관리용

checkpointer = InMemorySaver()

agent = create_react_agent(
    model=model,
    tools=tools,
    pre_model_hook=summarization_node,
    state_schema=State,
    checkpointer=checkpointer,
)
  • context 키는 이전 요약 정보를 관리합니다. 매번 LLM 호출마다 요약하지 않도록 효율적으로 관리합니다.

메시지 히스토리 트리밍(Trim message history)

트리밍은 히스토리에서 앞/뒤 N개 메시지를 삭제하는 방식입니다. 자세한 구현은 별도 가이드를 참고하세요.

도구에서 단기 메모리 읽기(Read in tools)

에이전트는 도구 내부에서 단기 메모리(state)에 접근할 수 있습니다.

API Reference: InjectedState | create_react_agent

from typing import Annotated
from langgraph.prebuilt import InjectedState, create_react_agent

class CustomState(AgentState):
    user_id: str

def get_user_info(
    state: Annotated[CustomState, InjectedState]
) -> str:
    """사용자 정보 조회"""
    user_id = state["user_id"]
    return "User is John Smith" if user_id == "user_123" else "Unknown user"

agent = create_react_agent(
    model="anthropic:claude-3-7-sonnet-latest",
    tools=[get_user_info],
    state_schema=CustomState,
)

agent.invoke({
    "messages": "look up user information",
    "user_id": "user_123"
})

자세한 내용은 컨텍스트 가이드를 참고하세요.

도구에서 단기 메모리 쓰기(Write from tools)

도구 실행 중 단기 메모리(state)를 업데이트하려면, 도구에서 state 업데이트를 반환하면 됩니다. 이는 중간 결과를 저장하거나 이후 도구/프롬프트에서 활용할 정보를 남길 때 유용합니다.

API Reference: InjectedToolCallId | RunnableConfig | ToolMessage | InjectedState | create_react_agent | AgentState | Command

from typing import Annotated
from langchain_core.tools import InjectedToolCallId
from langchain_core.runnables import RunnableConfig
from langchain_core.messages import ToolMessage
from langgraph.prebuilt import InjectedState, create_react_agent
from langgraph.prebuilt.chat_agent_executor import AgentState
from langgraph.types import Command

class CustomState(AgentState):
    user_name: str

def update_user_info(
    tool_call_id: Annotated[str, InjectedToolCallId],
    config: RunnableConfig
) -> Command:
    """사용자 정보 조회 및 업데이트"""
    user_id = config["configurable"].get("user_id")
    name = "John Smith" if user_id == "user_123" else "Unknown user"
    return Command(update={
        "user_name": name,
        "messages": [
            ToolMessage(
                "Successfully looked up user information",
                tool_call_id=tool_call_id
            )
        ]
    })

def greet(
    state: Annotated[CustomState, InjectedState]
) -> str:
    """사용자 정보 조회 후 인사"""
    user_name = state["user_name"]
    return f"Hello {user_name}!"

agent = create_react_agent(
    model="anthropic:claude-3-7-sonnet-latest",
    tools=[update_user_info, greet],
    state_schema=CustomState
)

agent.invoke(
    {"messages": [{"role": "user", "content": "greet the user"}]},
    config={"configurable": {"user_id": "user_123"}}
)

자세한 내용은 도구에서 state 업데이트 가이드를 참고하세요.

장기 메모리(Long-term memory)

장기 메모리는 여러 대화에 걸쳐 사용자별/애플리케이션별 데이터를 저장하는 데 사용합니다. 예를 들어 챗봇에서 사용자 선호도나 정보를 기억할 수 있습니다.

장기 메모리 사용 방법:

  1. store를 설정해 데이터 영속화
  2. 도구/프롬프트 내부에서 get_store 함수로 store에 접근

읽기(Read)

사용자 정보를 조회하는 도구 예시

from langchain_core.runnables import RunnableConfig
from langgraph.config import get_store
from langgraph.prebuilt import create_react_agent
from langgraph.store.memory import InMemoryStore

store = InMemoryStore()

store.put(
    ("users",),
    "user_123",
    {
        "name": "John Smith",
        "language": "English",
    }
)

def get_user_info(config: RunnableConfig) -> str:
    """사용자 정보 조회"""
    store = get_store()
    user_id = config["configurable"].get("user_id")
    user_info = store.get(("users",), user_id)
    return str(user_info.value) if user_info else "Unknown user"

agent = create_react_agent(
    model="anthropic:claude-3-7-sonnet-latest",
    tools=[get_user_info],
    store=store
)

agent.invoke(
    {"messages": [{"role": "user", "content": "look up user information"}]},
    config={"configurable": {"user_id": "user_123"}}
)

쓰기(Write)

사용자 정보를 업데이트하는 도구 예시

from typing_extensions import TypedDict
from langgraph.config import get_store
from langgraph.prebuilt import create_react_agent
from langgraph.store.memory import InMemoryStore

store = InMemoryStore()

class UserInfo(TypedDict):
    name: str

def save_user_info(user_info: UserInfo, config: RunnableConfig) -> str:
    """사용자 정보 저장"""
    store = get_store()
    user_id = config["configurable"].get("user_id")
    store.put(("users",), user_id, user_info)
    return "Successfully saved user info."

agent = create_react_agent(
    model="anthropic:claude-3-7-sonnet-latest",
    tools=[save_user_info],
    store=store
)

agent.invoke(
    {"messages": [{"role": "user", "content": "My name is John Smith"}]},
    config={"configurable": {"user_id": "user_123"}}
)

# store에서 직접 값 확인
store.get(("users",), "user_123").value

시맨틱 검색(Semantic search)

LangGraph는 장기 메모리 내 항목을 시맨틱 유사도로 검색할 수도 있습니다.

사전 구축 메모리 도구(Prebuilt memory tools)

LangMem은 장기 메모리 관리를 위한 LangChain 공식 라이브러리입니다. 자세한 사용법은 LangMem 문서를 참고하세요.

추가 자료

반응형
  • 네이버 블로그 공유
  • 네이버 밴드 공유
  • 페이스북 공유
  • 카카오스토리 공유