AI 챗봇을 설계할 때 가장 중요한 아키텍처 결정 중 하나는 챗봇을 이전 대화 내용 유지 없는 상태(stateless)로 만들 것인지 이전 대화 내용 유지(stateful)로 만들 것인지입니다. 그 차이가 무엇이고 왜 중요한지 알아보겠습니다.
이전 대화 내용 유지 없는 챗봇 (Stateless Chatbots)
무상태 챗봇은 사용자 입력을 각각 독립적인 메시지로 처리합니다. 이전 대화 내용을 기억하지 않기 때문에 구현은 간단하지만, 대화의 맥락을 반영하기 어려워 복잡한 질의나 자연스러운 상호작용에 제약이 있습니다.
대화 내용 유지 챗봇 (Stateful Chatbots)
상태 유지 챗봇은 이전 대화의 맥락을 기억하며 응답할 수 있어, 더 개인화되고 일관성 있는 대화를 제공합니다. 긴 대화나 사용자 선호도 기억, 특정 작업 수행이 필요한 에이전트에 이상적입니다.
Gemini + LangGraph로 상태 유지 챗봇 만들기
아래는 Gemini 2.5 Pro, LangChain, LangGraph를 활용하여 상태 유지 챗봇을 구현하는 완전한 예시입니다. 이 챗봇은 메모리 기능을 통해 이전 메시지를 기억하고, 유연한 그래프 기반 워크플로우를 지원합니다.
# 필수 라이브러리 불러오기
import os
from dotenv import load_dotenv # .env 파일에서 환경 변수 불러오기
from typing import Sequence
from typing_extensions import Annotated, TypedDict # 타입 지정에 사용
# LangChain에서 제공하는 Gemini 모델
from langchain_google_genai import ChatGoogleGenerativeAI
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
from langchain_core.messages import HumanMessage, BaseMessage
# LangGraph 구성 요소 불러오기
from langgraph.graph.message import add_messages # 메시지를 상태에 추가
from langgraph.checkpoint.memory import MemorySaver # 메모리를 저장해 상태 유지
from langgraph.graph import StateGraph, MessagesState, START, END # 그래프 구성 요소
# 환경 변수 불러오기
load_dotenv(".env")
api_key = os.getenv("GOOGLE_API_KEY")
# Gemini 모델 초기화 (컨텍스트를 잘 이해하는 강력한 모델)
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]
# 프롬프트 → 모델 연결 실행 체인 생성
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) # 모델 노드 추가
# 대화 상태를 저장할 메모리 객체 생성
memory = MemorySaver()
app = workflow.compile(checkpointer=memory) # 메모리 기능과 함께 그래프 컴파일
# 사용자 세션을 식별할 thread_id 설정
config = {"configurable": {"thread_id": "user_124"}}
# Step 1: 사용자가 이름을 말함 → AI가 기억함
input_data = {"messages": [HumanMessage("안녕하세요, 저는 Mark입니다.")]}
output = app.invoke(input_data, config)
output["messages"][-1].pretty_print() # 응답 출력 (Mark 기억 여부 확인)
# Step 2: 새로운 질문을 던짐 → AI가 "Mark"를 기억하고 응답
input_data = {"messages": [HumanMessage("A* 알고리즘을 설명해주세요.")]}
output = app.invoke(input_data, config)
output["messages"][-1].pretty_print() # 기억을 활용한 맞춤 응답
출력 예시
================================== Ai Message ==================================
Hi Mark! It's nice to meet you.
As a deep learning expert agent, I'm ready to help with any questions or tasks you might have related to:
* Neural network architectures (CNNs, RNNs, Transformers, etc.)
* Frameworks like TensorFlow, PyTorch, Keras
* Training, optimization, and evaluation techniques
* Specific applications (computer vision, NLP, etc.)
* Theoretical concepts
* Or anything else in the realm of deep learning!
What can I help you with today?
================================== Ai Message ==================================
Okay, Mark! Let's break down the **A\* (pronounced "A-star") algorithm**.
In simple terms, A\* is a popular and widely used **pathfinding** and **graph traversal** algorithm. Its main goal is to efficiently find the **shortest path** (or least-cost path) between a starting node (point) and a target node (point) in a graph or grid.
Think of it like giving directions using GPS: it doesn't just explore randomly; it tries to find the *best* route considering both the distance already traveled and an estimate of the distance remaining.
Here's a more detailed breakdown:
**1. The Core Idea:**
...
* **Optimality:** If the heuristic `h(n)` is admissible, A\* is guaranteed to find the least-cost path. If the heuristic is also consistent, the algorithm is more efficient.
* **Efficiency:** Generally much more efficient than uninformed search algorithms like Breadth-First Search (BFS) or Dijkstra's algorithm (when finding a path to a *single* goal) because the heuristic guides the search towards the goal. However, its memory usage can be high as it needs to store the Open and Closed sets.
요약
- LangChain: 프롬프트와 메모리를 연결하는 Python 기반 LLM 프레임워크
- LangGraph: 상태 기반 그래프 워크플로우를 구축할 수 있는 확장 도구
- Gemini 2.5 Pro: Google의 고성능 생성형 AI 모델, LangChain을 통해 통합됨
- 상태 유지 챗봇:
MemorySaver
와StateGraph
를 통해 이전 대화를 기억함
'AI' 카테고리의 다른 글
벡터 데이터베이스(Vector DB)란? FAISS 예제와 함께 깊이 있게 살펴보기 (0) | 2025.05.08 |
---|---|
랭그래프와 제미나이로 구현한 대화상태 기억하는 LLM 챗봇서버 (0) | 2025.05.08 |
Google Gemini 2.5 Pro와 LangChain으로 간단한 LLM 챗봇 서버 만들기 (1) | 2025.05.08 |
쉽게 배우는 Google Gemini API 프로그램 (0) | 2025.05.08 |
트랜스포머 아키텍처 완벽 가이드 | Transformer Architecture Guide (0) | 2025.05.08 |