LangChain Memory, 어떤 종류가 있을까? 📚
LangChain은 다양한 유형의 Memory를 제공하여 사용 시나리오에 맞춰 유연하게 선택할 수 있습니다. 각 Memory의 특징과 사용법을 살펴볼까요?
(본 내용은 LangChain Academy의 memoryschema_collection.ipynb 자료를 기반으로 작성되었습니다. 원본 자료는 여기에서 확인하실 수 있습니다.)
1. ConversationBufferMemory: 모든 대화를 통째로 기억! 📝
ConversationBufferMemory는 가장 기본적인 형태의 메모리입니다. 이름처럼 모든 대화 내용을 버퍼(buffer)에 저장하고, 필요할 때 전체 대화 기록을 LLM에 전달합니다.
✅ 장점: 모든 맥락이 보존되어 정확도가 높습니다. ❌ 단점: 대화가 길어지면 토큰 비용이 증가하고, LLM의 컨텍스트 윈도우를 초과할 수 있습니다.
from langchain.memory import ConversationBufferMemory
from langchain.llms import OpenAI
from langchain.chains import ConversationChain
from langchain.prompts import PromptTemplate
import os
# OpenAI API 키 설정 (실제 사용 시에는 환경 변수 등으로 관리)
# os.environ["OPENAI_API_KEY"] = "YOUR_OPENAI_API_KEY"
llm = OpenAI(temperature=0.7)
# 프롬프트 템플릿 정의
template = """다음은 인간과 AI의 친밀한 대화입니다. AI는 상세하고 맥락에 맞는 답변을 제공합니다.
{history}
Human: {input}
AI:"""
prompt = PromptTemplate(input_variables=["history", "input"], template=template)
# ConversationBufferMemory 초기화
memory = ConversationBufferMemory(memory_key="history")
# ConversationChain 설정
conversation = ConversationChain(
llm=llm,
prompt=prompt,
memory=memory,
verbose=True # 내부 작동 방식 확인을 위해 True 설정
)
# 대화 시작
print(conversation.predict(input="안녕, 내 이름은 제인이야. 너는 누구니?"))
# AI: 안녕하세요 제인! 저는 당신을 돕기 위해 LangChain으로 만들어진 AI입니다.
print(conversation.predict(input="나는 서울에 살고 있어. 너는 어디에 살아?"))
# AI: 제인, 저는 물리적인 장소에 살지 않습니다. 저는 클라우드에 존재합니다.
print(conversation.predict(input="내가 어디에 산다고 말했지?"))
# AI: 제인, 당신은 서울에 산다고 말씀하셨습니다.
설명: verbose=True를 통해 LLM에 전달되는 프롬프트(이전 대화 포함)를 확인할 수 있습니다. 대화가 진행될수록 history 변수에 이전 대화가 누적되는 것을 볼 수 있습니다.
2. ConversationBufferWindowMemory: 최근 대화만 똑똑하게! 🪟
ConversationBufferWindowMemory는 ConversationBufferMemory와 유사하지만, k라는 파라미터를 통해 최근 k개의 대화만 기억하도록 제한할 수 있습니다. 대화의 길이가 예측하기 어렵지만 너무 길어지는 것을 방지하고 싶을 때 유용합니다.
✅ 장점: 토큰 비용을 관리하고 컨텍스트 윈도우 오버플로우를 방지합니다. ❌ 단점: k가 너무 작으면 중요한 초기 맥락을 잃을 수 있습니다.
from langchain.memory import ConversationBufferWindowMemory
# 최근 2개의 대화만 기억하도록 설정
memory = ConversationBufferWindowMemory(k=2, memory_key="history")
conversation = ConversationChain(
llm=llm,
prompt=prompt,
memory=memory,
verbose=True
)
print(conversation.predict(input="안녕, 내 이름은 앨리스야."))
print(conversation.predict(input="나는 강아지를 키우고 있어."))
print(conversation.predict(input="그 강아지는 너무 귀여워!"))
print(conversation.predict(input="내 이름이 뭐였지?"))
# AI: 앨리스, 당신의 이름은 앨리스입니다. (이전 대화 중 '이름'이 k=2 범위 안에 있었기 때문)
print(conversation.predict(input="나는 고양이도 좋아해.")) # 새로운 대화 추가
print(conversation.predict(input="내가 강아지를 키우는지 기억나?")) # k=2 범위를 벗어나 기억하지 못함
# AI: 저는 당신이 강아지를 키우는지 기억하지 못합니다.
설명: k=2로 설정했기 때문에, 세 번째 대화 이후에는 첫 번째 대화('안녕, 내 이름은 앨리스야.')가 메모리에서 사라져 LLM에 전달되지 않습니다.
3. ConversationSummaryMemory: 대화 요약으로 핵심만 쏙쏙! 📝✨
ConversationSummaryMemory는 대화 내용을 버퍼에 저장하는 대신, LLM을 사용하여 이전 대화를 요약합니다. 이 요약본을 다음 대화의 컨텍스트로 사용하죠. 대화가 매우 길어질 것으로 예상될 때 토큰 효율성을 극대화할 수 있습니다.
✅ 장점: 매우 긴 대화에서도 핵심 맥락을 유지하며 토큰 비용을 크게 절감합니다. ❌ 단점: 요약 과정에서 미묘한 뉘앙스가 손실될 수 있으며, 요약을 위해 LLM을 한 번 더 호출해야 하므로 약간의 지연이 발생할 수 있습니다.
from langchain.memory import ConversationSummaryMemory
# ConversationSummaryMemory 초기화
memory = ConversationSummaryMemory(llm=llm, memory_key="history")
conversation = ConversationChain(
llm=llm,
prompt=prompt,
memory=memory,
verbose=True
)
print(conversation.predict(input="저는 마케팅 전문가이고, 새로운 캠페인을 기획 중입니다."))
print(conversation.predict(input="특히 Z세대를 타겟으로 한 소셜 미디어 전략에 관심이 많아요."))
print(conversation.predict(input="어떤 플랫폼이 효과적일까요?"))
# LLM은 위 대화들을 '마케팅 전문가가 Z세대를 위한 소셜 미디어 캠페인 플랫폼을 문의함' 등으로 요약하여 저장합니다.
print(conversation.predict(input="그럼 숏폼 콘텐츠는 어떨까요?"))
# AI: 숏폼 콘텐츠는 Z세대에게 매우 효과적일 수 있습니다. 틱톡, 인스타그램 릴스, 유튜브 쇼츠 등 다양한 플랫폼에서 활용할 수 있습니다.
설명: verbose=True를 통해 LLM에 전달되는 history를 보면, 원문 대화 대신 LLM이 생성한 요약 텍스트가 들어있는 것을 확인할 수 있습니다.
4. ConversationSummaryBufferMemory: 요약과 버퍼의 완벽 조화! 🧠💾
ConversationSummaryBufferMemory는 ConversationBufferWindowMemory와 ConversationSummaryMemory의 장점을 결합한 형태입니다. 최근 k개의 대화는 원문 그대로 버퍼에 저장하고, 오래된 대화는 LLM을 이용해 요약하여 저장합니다. 이를 통해 최신 대화의 정확도를 유지하면서도 전체적인 맥락을 잃지 않고 토큰 효율성을 관리할 수 있습니다.
✅ 장점: 최신 대화의 세부 정보를 유지하면서도 긴 대화의 토큰 효율성을 확보합니다. ❌ 단점: k 값 설정이 중요하며, 요약 과정에서 약간의 지연이 발생할 수 있습니다.
from langchain.memory import ConversationSummaryBufferMemory
# max_token_limit을 설정하여 메모리 크기 제한
# 이 값은 버퍼와 요약본을 합친 전체 메모리의 토큰 길이를 제한합니다.
memory = ConversationSummaryBufferMemory(llm=llm, max_token_limit=100, memory_key="history")
conversation = ConversationChain(
llm=llm,
prompt=prompt,
memory=memory,
verbose=True
)
print(conversation.predict(input="안녕하세요! 저는 책을 정말 좋아하는 사람이에요."))
print(conversation.predict(input="특히 판타지 소설에 푹 빠져 살죠."))
print(conversation.predict(input="최근에 읽은 건 '반지의 제왕' 시리즈인데, 정말 감명 깊었어요."))
# 여기까지는 버퍼에 저장될 수 있음
print(conversation.predict(input="혹시 '반지의 제왕'과 비슷한 느낌의 다른 판타지 소설을 추천해 주실 수 있나요?"))
# 여기서 메모리가 max_token_limit을 넘으면 오래된 대화는 요약되고, 최신 대화는 유지됩니다.
# AI: 네, '반지의 제왕'과 유사한 느낌의 판타지 소설로는 '어스시의 마법사', '나니아 연대기', '왕좌의 게임' 시리즈 등이 있습니다.
print(conversation.predict(input="제가 어떤 종류의 책을 좋아한다고 했죠?"))
# AI는 요약된 정보와 최신 버퍼를 통해 판타지 소설을 좋아한다는 것을 기억합니다.
# AI: 당신은 판타지 소설을 좋아한다고 말씀하셨습니다. 특히 '반지의 제왕'을 감명 깊게 읽으셨다고요.
설명: max_token_limit에 도달하면 가장 오래된 버퍼 내용이 요약으로 대체됩니다. 이 과정을 통해 중요한 최신 대화는 그대로 유지하고, 오래된 정보는 압축된 형태로 보존됩니다.
5. ConversationKGMemory: 지식 그래프로 사실 관계 기억하기! 💡🕸️
ConversationKGMemory는 다른 메모리와는 차별화된 방식으로 작동합니다. 단순히 대화 내용을 저장하거나 요약하는 것을 넘어, 대화에서 얻은 '사실(facts)'들을 추출하여 지식 그래프(Knowledge Graph) 형태로 저장합니다. 복잡한 관계와 정보를 기억하고 추론해야 할 때 매우 강력합니다.
✅ 장점: 대화 속에서 사실을 추출하고 관계를 추론하여 저장하므로, 특정 정보에 대한 질의에 정확하게 응답할 수 있습니다. 정보의 구조화에 유리합니다. ❌ 단점: 지식 그래프 구축 및 질의에 추가적인 LLM 호출이 필요하여 비용과 지연이 발생할 수 있습니다.
from langchain.memory import ConversationKGMemory
# ConversationKGMemory 초기화
# KGMemory는 지식 그래프를 생성하기 위해 LLM을 사용합니다.
memory = ConversationKGMemory(llm=llm, memory_key="history")
conversation = ConversationChain(
llm=llm,
prompt=prompt,
memory=memory,
verbose=True
)
print(conversation.predict(input="제 이름은 김민수입니다. 저는 대한민국 서울에 있는 IT 회사에서 일해요."))
print(conversation.predict(input="저희 회사는 '테크플로우'라고 불리며, 주로 인공지능 솔루션을 개발합니다."))
# AI는 '김민수'가 '테크플로우'에서 일하고, '테크플로우'가 '서울'에 있으며 '인공지능 솔루션'을 개발한다는 사실을 지식 그래프로 저장합니다.
print(conversation.predict(input="제가 일하는 회사의 주요 개발 분야는 무엇이었죠?"))
# AI: 당신이 일하는 회사 '테크플로우'는 인공지능 솔루션을 개발한다고 말씀하셨습니다.
설명: verbose=True로 확인해 보면, KGMemory가 대화에서 (김민수, work_at, 테크플로우), (테크플로우, located_in, 서울), (테크플로우, develops, 인공지능 솔루션)과 같은 트리플(triple) 형태의 지식을 추출하여 관리하는 것을 볼 수 있습니다. 이 덕분에 복잡한 질의에도 정확하게 답변할 수 있습니다.
"개인 맞춤형 여행 도우미 봇" 봇 제작하기! ✈️🗺️
이제 저만의 독창적인 사례를 통해 LangChain Memory의 강력함을 보여드릴 시간입니다! 저는 ConversationSummaryBufferMemory를 활용하여 사용자의 여행 취향과 이전 대화를 기억하고, 이를 바탕으로 맞춤형 여행지를 추천해 주는 **"개인 맞춤형 여행 도우미 봇"**을 만들어 보겠습니다.
이 봇은 최근 대화는 상세히 기억하고, 오래된 취향 정보(예: 자연 경관 선호, 예산 등)는 요약하여 효율적으로 관리할 것입니다.
from langchain.memory import ConversationSummaryBufferMemory
from langchain.llms import OpenAI
from langchain.chains import ConversationChain
from langchain.prompts import PromptTemplate
import os
# OpenAI API 키 설정 (실제 사용 시 환경 변수 등으로 관리)
# os.environ["OPENAI_API_KEY"] = "YOUR_OPENAI_API_KEY"
llm = OpenAI(temperature=0.7)
# 개인 맞춤형 여행 도우미 프롬프트 템플릿
travel_template = """당신은 친절하고 유능한 개인 맞춤형 여행 도우미 봇입니다. 사용자의 이전 대화 내용과 선호도를 참고하여 맞춤형 여행 정보 및 추천을 제공합니다. 사용자의 질문에 항상 친절하게 응대하고, 흥미로운 여행지를 제안해주세요.
{history}
사용자: {input}
도우미:"""
travel_prompt = PromptTemplate(input_variables=["history", "input"], template=travel_template)
# ConversationSummaryBufferMemory 초기화
# 최대 200토큰까지는 버퍼에 저장하고, 그 이상은 요약하여 관리
travel_memory = ConversationSummaryBufferMemory(llm=llm, max_token_limit=200, memory_key="history")
# ConversationChain 설정
travel_assistant = ConversationChain(
llm=llm,
prompt=travel_prompt,
memory=travel_memory,
verbose=True
)
print("🌟 개인 맞춤형 여행 도우미 봇입니다. 무엇을 도와드릴까요? 🌟")
# 봇과의 대화 시작
print(travel_assistant.predict(input="안녕하세요! 다음 휴가 계획 좀 도와주실 수 있나요?"))
# 도우미: 네, 물론이죠! 어디로 여행을 가고 싶으신가요? ✈️
print(travel_assistant.predict(input="아직 정하지 않았지만, 저는 자연 풍경과 활동적인 것을 좋아해요. 예산은 중간 정도입니다."))
# 도우미: 자연 풍경과 활동적인 것을 좋아하시고, 중간 예산을 고려하시는군요! 혹시 어떤 활동에 관심이 있으신가요? (예: 하이킹, 스노클링 등)
print(travel_assistant.predict(input="하이킹을 좋아하고, 바다도 좋습니다. 지난번에는 제주도에 다녀왔어요."))
# 도우미: 지난번에 제주도에 다녀오셨군요! 이번에는 강원도 설악산이나 남해 한려해상국립공원 어떠세요? 하이킹과 아름다운 바다를 동시에 즐길 수 있습니다. ⛰️🌊
print(travel_assistant.predict(input="설악산 좋은데요! 그럼 설악산 근처에서 가볼 만한 맛집이나 숙소도 추천해 주실 수 있나요?"))
# 도우미: 네, 설악산 근처에는 속초 중앙시장이나 아바이 마을 등 맛집이 많습니다. 숙소는 설악 워터피아 근처나 대포항 근처의 중급 호텔을 추천드립니다. 이전에 말씀해주신 중간 예산을 고려했습니다. 😊
print(travel_assistant.predict(input="제가 자연 풍경 말고 또 어떤 걸 좋아한다고 했죠?"))
# 도우미: 당신은 활동적인 것을 좋아하고, 특히 하이킹과 바다를 선호한다고 말씀하셨습니다.
print(travel_assistant.predict(input="그럼 제가 제주도에 다녀왔다는 것도 기억하시나요?"))
# 도우미: 네, 지난번에 제주도에 다녀오셨다고 말씀해주셨습니다. 🏝️
코드 설명 및 결과 분석:
- 프롬프트 템플릿 (travel_template): 봇의 역할(친절한 여행 도우미)과 지침(이전 대화와 선호도 참고, 흥미로운 여행지 제안)을 명확히 정의했습니다. {history}와 {input} 변수를 통해 이전 대화 맥락과 현재 사용자 입력이 LLM에 전달됩니다.
- ConversationSummaryBufferMemory 활용: max_token_limit=200을 설정하여, 200토큰까지는 최신 대화 내용을 그대로 기억하고, 그 이상이 되면 오래된 대화는 요약하도록 했습니다. 이를 통해 "자연 풍경, 활동적인 것 선호, 중간 예산" 같은 사용자의 핵심 선호도는 요약된 형태로 유지하면서, "설악산 추천"과 같은 최근의 구체적인 대화 맥락은 버퍼에 상세하게 남아있게 됩니다.
- 대화 흐름:
- 봇은 사용자의 "자연 풍경과 활동적인 것, 중간 예산"이라는 선호도를 기억합니다.
- 이를 바탕으로 "설악산"을 추천합니다.
- 이후 "맛집과 숙소 추천" 요청 시, 봇은 "설악산"이라는 현재 목적지와 이전에 언급된 "중간 예산"을 모두 고려하여 답변합니다.
- "제가 어떤 종류의 책을 좋아한다고 했죠?"와 같은 질문에도, 요약된 메모리와 버퍼 메모리에 있는 정보를 통해 정확히 답변하는 것을 볼 수 있습니다. "제주도에 다녀왔다는 것도 기억하시나요?" 질문에도 정확히 답변합니다.
이처럼 ConversationSummaryBufferMemory는 장기적인 사용자 선호도(요약)와 단기적인 대화 맥락(버퍼)을 효율적으로 관리하여, 봇이 사용자에게 더욱 개인화되고 일관된 경험을 제공할 수 있도록 돕습니다.
'LLM' 카테고리의 다른 글
| LangChain 커스텀 툴 만들기 (0) | 2025.11.21 |
|---|---|
| LangChain Memory (0) | 2025.11.21 |
| LangChain과 Pydantic으로 사용자 프로필 자동 업데이트 (0) | 2025.11.21 |
| 나만의 AI 리서치 에이전트 구축 가이드🤖 (0) | 2025.11.21 |
| LangChain 챗봇 메모리 (0) | 2025.11.21 |