본문으로 건너뛰기

© 2026 Molayo

Dev.to헤드라인2026. 05. 28. 14:20

99. 메모리 기능이 있는 챗봇 만들기

요약

LLM의 Stateless 특성을 극복하고 대화 맥락을 유지하는 메모리 구현 방법을 다룹니다. 컨텍스트 윈도우 제한 문제를 해결하기 위한 슬라이딩 윈도우, 요약, 엔티티 메모리 등 다양한 전략을 소개합니다.

핵심 포인트

  • LLM은 기본적으로 상태가 없는(Stateless) 구조임
  • 대화 기록을 프롬프트에 포함하여 메모리를 흉내 냄
  • 컨텍스트 윈도우 제한을 고려한 메모리 관리 전략 필요
  • 슬라이딩 윈도우, 요약, 엔티티 메모리 등 다양한 패턴 활용

당신이 챗봇에게 질문합니다: "프랑스의 수도는 어디인가요?"

챗봇은 대답합니다: "파리입니다."

당신이 다시 묻습니다: "그곳의 인구는 얼마인가요?"

챗봇은 대답합니다: "어디 말씀이신가요?"

이것이 바로 상태가 없는 (Stateless) 챗봇입니다. 모든 메시지는 완전히 새로운 대화로 취급됩니다. 챗봇은 "그곳"이 무엇을 가리키는지 전혀 알지 못합니다. 메모리 (Memory)가 없는 것입니다.

실제 대화는 이렇게 작동하지 않습니다. 맥락 (Context)이 이어지며, 참조 (References)가 축적됩니다. 챗봇은 이전에 어떤 내용이 오갔는지 알아야 합니다.

이 포스트에서는 메모리 기능이 있는 챗봇을 구축합니다. 당신이 두 메시지 전에 무엇을 말했는지, 어떤 주제로 토론하고 있는지, 그리고 이전에 어떤 결정이 내려졌는지를 아는 챗봇을 만드는 법을 다룹니다.

여기서 배우게 될 내용

  • LLM (Large Language Models)이 왜 상태가 없는지(Stateless)와 메모리를 흉내 내는 방법
  • 대화 기록 패턴 (Conversation history pattern): 실제로 어떻게 작동하는가
  • 컨텍스트 윈도우 (Context window) 제한과 그것이 중요한 이유
  • 슬라이딩 윈도우 메모리 (Sliding window memory): 마지막 N개의 메시지 유지하기
  • 요약 메모리 (Summary memory): 오래된 대화 압축하기
  • 엔티티 메모리 (Entity memory): 사용자에 대한 특정 사실 기억하기
  • LangChain을 사용하여 완전한 멀티턴 (Multi-turn) 챗봇 구축하기
  • 세션 간 메모리 유지하기 (Persisting memory across sessions)

LLM이 상태가 없는 이유

LLM API를 호출할 때마다, 모델은 새로 시작합니다. 이전 호출에 대한 기억이 전혀 없습니다. 모델이 가질 수 있는 유일한 맥락 (Context)은 현재 프롬프트 (Prompt)에 당신이 넣은 내용뿐입니다.

챗봇을 작동하게 만드는 비결은 이것입니다: 모든 프롬프트에 전체 대화 기록을 포함하는 것입니다.

Turn 1:
  USER: What's the capital of France?
  → Send to LLM: "User: What's the capital of France?"
...

모든 메시지는 계속 늘어나는 리스트에 추가됩니다. 그 리스트는 이후의 모든 프롬프트에 포함됩니다. LLM은 해당 내용이 현재 맥락 (Context) 안에 있기 때문에 이를 다시 참조할 수 있습니다.

단순합니다. 하지만 명확한 한계가 있습니다: 바로 컨텍스트 윈도우 (Context window)입니다.

컨텍스트 윈도우 문제

모든 LLM은 한 번에 처리할 수 있는 최대 토큰 (Tokens) 수가 정해져 있습니다. GPT-3.5-turbo: 16k 토큰. GPT-4: 128k 토큰. LLaMA-7B: 4k 토큰.

긴 대화는 이 윈도우를 가득 채웁니다. 대화가 제한을 초과하면, 단순히 모든 것을 포함할 수는 없습니다. 전략이 필요합니다.

토큰 수 추정 (대략적: 영어 기준 1 토큰 ≈ 4자)

def estimate_tokens(text: str) -> int:
return len(text) // 4
...

Output:

Turn   New tokens     Total tokens   % of 4k limit
--------------------------------------------------
1      12             16             0.4%
...

복잡한 주제에 대한 긴 대화는 쉽게 2000-3000 토큰에 도달할 수 있습니다. 여기에 RAG (Retrieval-Augmented Generation) 컨텍스트와 시스템 프롬프트 (System Prompts)를 추가하면 금방 제한에 도달하게 됩니다.

전략 1: 슬라이딩 윈도우 메모리 (Sliding Window Memory)

마지막 N개의 메시지만 유지합니다. 단순하고 효과적입니다.

from collections import deque
from typing import List, Optional

...

Output:

User: 프랑스의 수도는 어디인가요?
Bot:  프랑스의 수도는 파리입니다.

...

챗봇은 문맥을 통해 "거기" (파리)와 "그 도시" (파리)를 이해합니다. 슬라이딩 윈도우는 마지막 6개의 메시지를 유지합니다.

전략 2: 요약 메모리 (Summary Memory)

대화 기록이 길어지면, 오래된 메시지는 요약하고 최근 메시지는 전체를 유지합니다.

class SummaryMemoryChatbot:
    def __init__(self, model_pipeline, summarizer_pipeline,
                 max_recent: int = 6, summary_threshold: int = 10,
...

전략 3: 엔티티 메모리 (Entity Memory)

사용자 또는 대화 엔티티 (Entities)에 관한 특정 사실을 추출하여 저장합니다.

import re
from typing import Dict

...

Output:

User: 안녕하세요, 제 이름은 Alex입니다.
Bot:  만나서 반가워요, Alex!

...

챗봇은 모든 턴 (Turns)에 걸쳐 사용자의 이름과 주제를 기억합니다.

OpenAI API를 사용한 전체 챗봇

import openai
import json
from datetime import datetime
...

LangChain 메모리: 쉬운 방법

from langchain.memory import ConversationBufferMemory, ConversationSummaryMemory
from langchain.chains import ConversationChain
from langchain_community.llms import HuggingFacePipeline
...

세션 간 메모리 유지 (Persisting Memory Across Sessions)

import json
import os

class PersistentChatbot:
def init(self, model_pipeline, session_id: str,
storage_dir: str = './chat_sessions',
max_history: int = 50):
self.model = model_pipeline
self.session_id = session_id
self.storage_dir = storage_dir
self.max_history = max_history
self.history = []
self.metadata = {}

    os.makedirs(storage_dir, exist_ok=True)
    self._load_session()

def _session_path(self) -> str:
    return os.path.join(self.storage_dir, f"{self.session_id}.json")

def _load_session(self):
    path = self._session_path()
    if os.path.exist

AI 자동 생성 콘텐츠

본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.

원문 바로가기
0

댓글

0