
LangGraph를 학습하면서 가장 중요하게 이해해야 할 개념들을 정리했습니다. 특히 Tool Binding된 LLM 노드를 추가하는 방법과 Super-Step의 개념, 그리고 이것이 메모리 관리와 어떻게 연결되는지를 중심으로 기록했습니다.
LangChain은 일반 함수를 Tool로 변환할 수 있는 래퍼 클래스를 제공합니다. 예를 들어 검색 기능을 Tool로 만드는 과정은 다음과 같습니다.
from langchain.agents import Tool
from langchain_community.utilities import GoogleSerperAPIWrapper
serper = GoogleSerperAPIWrapper()
tool_search = Tool(
name="search",
func=serper.run,
description="Useful for when you need more information from an online search"
)
이렇게 생성된 Tool은 독립적으로 호출할 수 있습니다.
tool_search.invoke("What is the capital of France?")
Tool을 구현할 때는 항상 2가지 변경사항이 필요합니다:
finish_reason=="tool_calls"를 확인하고 함수를 실행한 후 결과 제공LangGraph에서는 bind_tools() 메서드를 사용하여 LLM에 Tool을 바인딩합니다.
from langchain_openai import ChatOpenAI
tools = [tool_search]
llm = ChatOpenAI(model="gpt-4o-mini")
llm_with_tools = llm.bind_tools(tools)
💡
llm.bind_tools()는 LLM이 필요할 때 특정 도구를 호출할 수 있도록 연결해주는 핵심 메서드입니다. 이를 통해 LLM은 단순히 텍스트를 생성하는 것을 넘어 실제 기능을 수행할 수 있게 됩니다.
from typing import Annotated, TypedDict
from langgraph.graph.message import add_messages
class State(TypedDict):
messages: Annotated[list, add_messages]
TypedDict를 사용하여 State 객체를 정의했습니다. add_messages는 메시지 리스트를 관리하는 reducer입니다.
from langgraph.graph import StateGraph, START
from langgraph.prebuilt import ToolNode, tools_condition
# Step 1-2: Graph Builder 초기화
graph_builder = StateGraph(State)
# Step 3: Chatbot Node 추가
def chatbot(state: State):
return {"messages": [llm_with_tools.invoke(state["messages"])]}
graph_builder.add_node("chatbot", chatbot)
graph_builder.add_node("tools", ToolNode(tools=tools))
# Step 4: Edge 연결
graph_builder.add_conditional_edges("chatbot", tools_condition, "tools")
graph_builder.add_edge("tools", "chatbot")
graph_builder.add_edge(START, "chatbot")
# Step 5: Compile
graph = graph_builder.compile()
💡
ToolNode는 LangGraph에서 제공하는 prebuilt 노드로, Tool 실행을 자동으로 처리합니다.tools_condition은 LLM이 Tool을 호출해야 하는지 판단하는 조건부 분기 입니다.
Graph가 State를 유지하고 append하고 있는데, 왜 이를 통해 메모리를 자동으로 관리하지 않을까요? 이것이 LangGraph를 이해하는 핵심 포인트입니다.
💡
Super-Step은 그래프 노드들의 단일 반복(iteration)으로 간주됩니다.
병렬로 실행되는 노드들은 같은 Super-Step에 속하고, 순차적으로 실행되는 노드들은 서로 다른 Super-Step에 속합니다.
관용적인 LangGraph에서는 각 Super-Step마다, 즉 각 상호작용마다 invoke를 호출합니다.
바로 이것이 체크포인팅(Checkpointing)이 달성하는 목표입니다.
💡
Super-Step의 개념을 이해하지 못하면 LangGraph의 메모리 관리와 State 전파 메커니즘을 제대로 활용할 수 없습니다. 각invoke호출이 하나의 Super-Step을 의미한다는 점을 명심해야 합니다.
from langgraph.checkpoint.memory import MemorySaver
memory = MemorySaver()
# Graph 컴파일 시 checkpointer 추가
graph = graph_builder.compile(checkpointer=memory)
config = {"configurable": {"thread_id": "1"}}
def chat(user_input: str, history):
result = graph.invoke(
{"messages": [{"role": "user", "content": user_input}]},
config=config
)
return result["messages"][-1].content
# 현재 State 확인
graph.get_state(config)
# State 히스토리 조회 (최신순)
list(graph.get_state_history(config))
💡
LangGraph는 특정 시점으로 되돌아가거나 분기할 수 있는 도구를 제공합니다:config = {"configurable": {"thread_id": "1", "checkpoint_id": ...}} graph.invoke(None, config=config)이를 통해 이전 체크포인트에서 복구하고 재실행할 수 있는 안정적인 시스템을 구축할 수 있습니다.
인메모리 저장소 대신 SQLite 데이터베이스를 사용하면 영구적인 메모리 관리가 가능합니다.
import sqlite3
from langgraph.checkpoint.sqlite import SqliteSaver
db_path = "memory.db"
conn = sqlite3.connect(db_path, check_same_thread=False)
sql_memory = SqliteSaver(conn)
# Graph에 SQL 메모리 적용
graph = graph_builder.compile(checkpointer=sql_memory)
config = {"configurable": {"thread_id": "3"}}
def chat(user_input: str, history):
result = graph.invoke(
{"messages": [{"role": "user", "content": user_input}]},
config=config
)
return result["messages"][-1].content
💡
단순한 인메모리 상태 관리를 넘어, 데이터베이스 기반의 영구적이고 반복 가능하며 견고한 시스템을 구축할 수 있습니다. 이를 통해 에이전트 시스템에 자연스러운 기억력을 심어줄 수 있습니다.
State - 현재 앱의 스냅샷을 표현하는 불변 객체
Nodes - 실제 작업을 수행하는 함수 단위
Edges - 노드 간 흐름 제어
llm.bind_tools(tools)로 LLM에 도구 연결ToolNode와 tools_condition으로 자동 처리invoke 호출LangGraph는 단순한 상태 관리 라이브러리가 아니라, 복잡한 에이전트 시스템을 구축하기 위한 강력한 프레임워크입니다. Tool Binding을 통해 LLM에 실제 기능을 부여하고, Super-Step 개념으로 상태 전파를 제어하며, Checkpointing으로 견고하고 반복 가능한 시스템을 만들 수 있습니다.
특히 Super-Step과 Checkpointing의 관계를 정확히 이해하는 것이 LangGraph를 제대로 활용하는 핵심입니다. Reducer가 자동으로 모든 것을 처리해줄 것이라는 착각에서 벗어나, 명시적인 체크포인팅을 통해 상태를 관리해야 합니다.
이러한 개념들을 바탕으로 실제 프로덕션 환경에서 사용 가능한 에이전트 시스템을 구축할 수 있을 것입니다.