LangChain 독학 가이드 7 - 대화 메모리
대화 메모리가 없으면 생기는 문제
AI 챗봇을 만들다 보면 꼭 겪는 답답한 상황이 있어요. 바로 AI가 방금 전 대화를 까먹는 거죠.
“내 이름은 민수야”라고 했는데, 다음 질문에서 “내 이름이 뭐였지?”라고 물으면 “죄송하지만 이전 대화 내용을 모르겠습니다”라고 답하는 식이에요. 실제로 사람과 대화하는 느낌이 전혀 안 나죠.
이런 일이 생기는 이유는 LLM(대규모 언어 모델) 자체는 상태를 저장하지 않기 때문이에요. 매번 새로운 요청으로 인식하죠. 그래서 LangChain에서는 메모리(Memory) 기능을 제공해서 이전 대화를 기억하게 만들 수 있습니다.
제 경험상 메모리 기능을 추가하는 순간 챗봇의 품질이 확 달라져요. 진짜 대화하는 느낌이 나기 시작합니다.
ConversationBufferMemory - 모든 대화 기억하기
가장 기본적인 메모리는 ConversationBufferMemory예요. 이름처럼 모든 대화 내용을 버퍼에 저장해서 기억합니다.
from langchain_openai import ChatOpenAI
from langchain.chains import ConversationChain
from langchain.memory import ConversationBufferMemory
# 모델 설정
model = ChatOpenAI(model="gpt-3.5-turbo", temperature=0.7)
# 메모리 생성
memory = ConversationBufferMemory()
# 대화 체인 생성
conversation = ConversationChain(
llm=model,
memory=memory,
verbose=True # 대화 과정을 보여줌
)
# 첫 번째 대화
response1 = conversation.predict(input="안녕! 내 이름은 지혜야.")
print(f"AI: {response1}\n")
# 두 번째 대화
response2 = conversation.predict(input="내 이름이 뭐였지?")
print(f"AI: {response2}\n")
# 세 번째 대화
response3 = conversation.predict(input="나는 파이썬을 공부하고 있어.")
print(f"AI: {response3}\n")
# 네 번째 대화
response4 = conversation.predict(input="내가 뭘 공부한다고 했지?")
print(f"AI: {response4}")
실행 결과:
보세요! AI가 이름도 기억하고, 파이썬 공부한다는 것도 기억하고 있어요. 이게 바로 메모리의 힘입니다.
메모리에 저장된 내용을 확인하려면 이렇게 해보세요:
# 메모리 내용 확인
print(memory.load_memory_variables({}))
실행 결과:
ConversationBufferWindowMemory - 최근 대화만 기억하기
모든 대화를 기억하면 좋긴 한데, 문제가 하나 있어요. 대화가 길어질수록 토큰을 너무 많이 사용하게 된다는 거죠. 비용이 점점 올라가는 문제가 생깁니다.
그럴 때는 최근 N개의 대화만 기억하는 ConversationBufferWindowMemory를 사용하면 돼요.
from langchain.memory import ConversationBufferWindowMemory
# 최근 2개의 대화만 기억하는 메모리
memory = ConversationBufferWindowMemory(k=2)
# 대화 체인 생성
conversation = ConversationChain(
llm=model,
memory=memory,
verbose=False
)
# 여러 대화 진행
conversation.predict(input="내 이름은 철수야.")
conversation.predict(input="나는 서울에 살아.")
conversation.predict(input="내 취미는 축구야.")
conversation.predict(input="나는 개발자야.")
# 이름을 물어보면?
response = conversation.predict(input="내 이름이 뭐야?")
print(f"AI: {response}")
# 메모리 확인
print("\n저장된 대화:")
print(memory.load_memory_variables({}))
실행 결과:
k=2로 설정했기 때문에 최근 2개의 대화쌍만 기억하고 있어요. 그래서 맨 처음에 했던 “내 이름은 철수야”는 잊어버린 거죠. 하지만 최근에 말한 취미와 직업은 기억하고 있을 거예요.
ConversationSummaryMemory - 요약해서 기억하기
긴 대화 내용을 전부 저장하기엔 부담스럽고, 그렇다고 최근 몇 개만 기억하긴 아쉬울 때가 있어요. 이럴 때는 대화 내용을 요약해서 저장하는 방법이 있습니다.
from langchain.memory import ConversationSummaryMemory
# 요약 메모리 생성 (LLM이 요약을 만듦)
memory = ConversationSummaryMemory(llm=model)
# 대화 체인 생성
conversation = ConversationChain(
llm=model,
memory=memory,
verbose=True
)
# 긴 대화 시뮬레이션
conversation.predict(input="안녕! 나는 서울에 사는 25살 대학생이야.")
conversation.predict(input="컴퓨터공학을 전공하고 있고, 졸업 후엔 AI 엔지니어가 되고 싶어.")
conversation.predict(input="요즘은 LangChain을 공부하면서 챗봇 프로젝트를 진행 중이야.")
# 메모리 확인
print("\n요약된 메모리:")
print(memory.load_memory_variables({}))
# 대화 내용을 물어봄
response = conversation.predict(input="내가 어떤 공부를 하고 있다고 했지?")
print(f"\nAI: {response}")
실행 결과:
대화 내용이 요약되어 저장되는 걸 볼 수 있죠? 이렇게 하면 토큰을 절약하면서도 중요한 정보는 잃지 않을 수 있어요.
실전 활용 - 개인 비서 챗봇 만들기
이제 배운 메모리 기능을 활용해서 실용적인 개인 비서 챗봇을 만들어볼게요. 사용자 정보를 기억하고 맥락에 맞는 답변을 하는 챗봇입니다.
from langchain.chains import ConversationChain
from langchain.memory import ConversationBufferWindowMemory
from langchain_core.prompts import PromptTemplate
# 개인 비서 스타일의 프롬프트
template = """당신은 친근하고 유능한 개인 비서입니다.
사용자의 이전 대화 내용을 기억하고, 맥락에 맞는 도움을 제공하세요.
현재까지의 대화:
{history}
사용자: {input}
비서:"""
prompt = PromptTemplate(
input_variables=["history", "input"],
template=template
)
# 최근 5개 대화를 기억하는 메모리
memory = ConversationBufferWindowMemory(k=5)
# 체인 생성
assistant = ConversationChain(
llm=model,
prompt=prompt,
memory=memory,
verbose=False
)
# 대화 시뮬레이션
print("=== 개인 비서 챗봇 ===\n")
conversations = [
"내일 오전 10시에 회의가 있어.",
"점심으로 뭐 먹을까?",
"내일 일정이 어떻게 되지?",
"회의 준비물로 뭐가 필요할까?"
]
for user_input in conversations:
print(f"나: {user_input}")
response = assistant.predict(input=user_input)
print(f"비서: {response}\n")
실행 결과:
봤죠? 챗봇이 “내일 10시 회의”를 계속 기억하고 있어요. 점심 메뉴 추천할 때도 회의를 고려해서 답변하고, 일정을 물어봤을 때도 제대로 알려주죠.
메모리 직접 조작하기
때로는 메모리를 직접 컨트롤해야 할 때도 있어요. 특정 정보를 미리 넣어두거나, 불필요한 내용을 지우고 싶을 수 있죠.
from langchain.memory import ConversationBufferMemory
# 메모리 생성
memory = ConversationBufferMemory()
# 대화 내용 직접 추가
memory.save_context(
{"input": "내 이름은 영희야."},
{"output": "안녕하세요 영희님!"}
)
memory.save_context(
{"input": "나는 디자이너야."},
{"output": "디자이너시군요! 어떤 디자인을 주로 하시나요?"}
)
# 저장된 내용 확인
print("저장된 메모리:")
print(memory.load_memory_variables({}))
# 메모리 초기화
memory.clear()
print("\n메모리 초기화 후:")
print(memory.load_memory_variables({}))
실행 결과:
메모리 선택 가이드
상황에 따라 어떤 메모리를 쓸지 고민될 수 있어요. 제가 실제로 사용하는 기준을 알려드릴게요:
ConversationBufferMemory: 짧은 대화나 중요한 정보를 절대 놓치면 안 될 때. 고객 상담 챗봇 같은 곳에 좋아요.
ConversationBufferWindowMemory: 일반적인 대화형 챗봇에 가장 많이 씁니다. 비용과 성능의 균형이 좋아요. k=5~10 정도가 적당해요.
ConversationSummaryMemory: 매우 긴 대화가 예상될 때. 고객 서비스나 상담 챗봇처럼 한 세션이 오래 지속되는 경우에 유용합니다.
다음 단계
대화 메모리까지 마스터했다면 이제 정말 강력한 기능을 배울 차례예요. 다음 글에서는 RAG(검색 증강 생성)를 다룰 건데요, 이걸 배우면 AI가 우리가 제공한 문서나 데이터를 기반으로 답변할 수 있게 됩니다.
실제로 회사 내부 문서로 QA 시스템을 만들거나, PDF 파일 내용을 기반으로 답변하는 챗봇을 만들 수 있어요. 진짜 실용적인 애플리케이션을 만드는 핵심 기술입니다!