본문 바로가기
AI

랭그래프와 제미나이로 구현한 대화상태 기억하는 LLM 챗봇서버

by markbyun 2025. 5. 8.

이 튜토리얼에서는 Stateless 챗봇 서버LangGraph를 활용한 상태 기반 메모리 지원을 추가하여 업그레이드합니다. 이를 통해 모델이 이전 메시지를 기억하며 보다 사람다운 다중 턴 대화가 가능합니다.


이 업그레이드의 주요 특징

  • LangChain 통합을 통한 Gemini 2.5 Pro 기반 구동
  • 세션 메모리 유지를 위한 LangGraph의 MemorySaver 사용
  • Flask로 구현되었으며 CORS 지원 포함
  • thread_id를 사용하여 사용자별 대화 히스토리 유지

Stateless 버전과의 차이점

Stateless 버전과의 주요 차이점은 다음과 같습니다:

  • 상태 관리: messages를 통해 대화 히스토리를 추적하기 위해 TypedDict 기반의 State 클래스를 도입
  • LangGraph 통합: StateGraph를 사용한 상태 기반 워크플로우 정의 및 MemorySaver를 통한 메모리 유지
  • 세션 메모리: LangGraph의 configurable 입력을 통해 고유한 thread_id (예: user_124)로 챗 세션 연결

상태 기반 챗봇 전체 코드

import os
from dotenv import load_dotenv
from flask import Flask, request, jsonify
from flask_cors import CORS

# Typing 및 LangChain / LangGraph 관련 import
from typing import Sequence
from typing_extensions import Annotated, TypedDict
from langchain_google_genai import ChatGoogleGenerativeAI
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
from langchain_core.messages import HumanMessage, BaseMessage
from langgraph.graph.message import add_messages
from langgraph.checkpoint.memory import MemorySaver
from langgraph.graph import StateGraph, START, END

# .env 환경 변수 로드
load_dotenv(".env")
api_key = os.getenv("GOOGLE_API_KEY")

# Flask 서버 초기화
app = Flask(__name__)
CORS(app)

# Gemini 2.5 Pro LLM 설정
llm_pro = ChatGoogleGenerativeAI(
    model='gemini-2.5-pro-exp-03-25',
    temperature=0.5
)

# 시스템 역할 + 메모리 플레이스홀더를 포함한 프롬프트
prompt = ChatPromptTemplate.from_messages([
    ('system', '당신은 딥러닝 전문가입니다.'),
    MessagesPlaceholder(variable_name="messages"),
])

# 메모리 지원 상태 스키마 정의
class State(TypedDict):
    messages: Annotated[Sequence[BaseMessage], add_messages]

# 체인: prompt → model
runnable = prompt | llm_pro

# 그래프 노드 함수: 새로운 메시지 생성
def generator(state: State):
    response = runnable.invoke(state)
    return {"messages": [response]}  # 상태 내 메시지 업데이트

# LangGraph 워크플로우 생성
workflow = StateGraph(state_schema=State)
workflow.add_edge(START, "model")
workflow.add_node("model", generator)

# 대화 저장을 위한 MemorySaver 사용
memory = MemorySaver()
chat_app = workflow.compile(checkpointer=memory)

# 고유한 thread_id를 통한 사용자 세션 정의
config = {"configurable": {"thread_id": "user_124"}}

# API 엔드포인트
@app.route("/chat", methods=["POST"])
def chat():
    data = request.get_json()
    user_query = data.get('user_query')

    if not user_query:
        return jsonify({'response': "질문을 입력해주세요."})

    # LangGraph에 메시지 및 사용자 구성 전달
    output = chat_app.invoke(
        input={"messages": [HumanMessage(user_query)]},
        config=config
    )

    # 모델 응답 추출
    response = output["messages"][-1].content
    return jsonify({'response': response})

if __name__ == '__main__':
    app.run(debug=True, port=8000)

무엇이 상태 기반(Stateful)인가요?

Stateless 챗봇에서는 사용자 질문마다 독립적으로 처리되어 메모리를 유지하지 않습니다. 이 버전은 다음과 같은 방식으로 그 문제를 해결합니다:

  • State 스키마 내에서 messages 히스토리를 유지
  • MemorySaver를 사용하여 대화 상태를 저장 및 복원
  • configurable.thread_id를 통해 스레드 기반 메모리 유지

결론

이 업그레이드된 챗봇 서버는 이제 Gemini 2.5 Pro와 LangGraph를 활용하여 상태 기반 대화를 지원합니다. 이는 실제 서비스 환경에서 똑똑하고 문맥을 기억하는 어시스턴트를 만들기에 최적입니다.

직접 구현해보고 싶으신가요? thread_id를 사용자 세션이나 쿠키를 기반으로 동적으로 바꿔보세요!