LangChain 챗봇4장 · 대화형 챗봇과 메모리 전략9 / 22← 이전목차다음 →MetaContents

4장. 대화형 챗봇과 메모리 전략

손님마다 기록 상자 나누기

여행사에는 손님마다 기록철이 따로 있고, 철 겉면에는 회원번호가 적혀 있습니다. 손님이 오면 회원번호로 철을 찾아 펼치고, 상담이 끝나면 오늘의 장을 끼워 다시 꽂습니다. 처음 온 손님에게는 새 철을 만듭니다. LangChain 에서 이 철이 InMemoryChatMessageHistory, 회원번호가 세션 id, 철을 찾아 펼치고 다시 꽂는 비서가 RunnableWithMessageHistory 입니다.

세션 id 받기, 그 세션의 기록 꺼내기, 기록을 넣어 체인 실행하기, 질문과 답을 기록에 덧붙이기의 네 걸음을 이은 흐름도

네 상자는 bot.invoke(...) 한 번에 일어나는 일입니다. 우리가 세션 id 를 함께 넘기면, 그 id 의 기록 상자를 꺼내 프롬프트의 기록 자리에 넣고, 체인을 실행한 뒤, 질문과 답을 그 상자에 덧붙입니다. 앞 절의 chat() 함수가 손으로 하던 일을 부품 하나가 해 줍니다. 여기서 회원번호가 세션 id, 기록철이 InMemoryChatMessageHistory, 철을 다루는 비서가 RunnableWithMessageHistory 입니다. 비유와 다른 점은, 이 철은 컴퓨터 메모리에만 있어서 프로그램을 끄면 모두 사라진다는 것입니다.

RunnableWithMessageHistory 에 넘기는 것

인자뜻이 장의 값
첫째 인자감쌀 체인prompt | model
get_session_history세션 id 를 받아 기록 상자를 돌려주는 함수get_session_history
input_messages_key입력 사전에서 새 질문이 든 열쇠"question"
history_messages_key프롬프트의 기록 자리 이름"history"
config={"configurable": {"session_id": …}}부를 때 넘기는 세션 id. configurable 은 "설정할 수 있는"이라는 뜻"kim", "lee"

짧은 예시

가짜 모델로 손님 두 명(kim, lee)의 대화를 나눠 담습니다.

Python
import warnings

from langchain_core.chat_history import InMemoryChatMessageHistory
from langchain_core.language_models.fake_chat_models import FakeListChatModel
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
from langchain_core.runnables.history import RunnableWithMessageHistory

# 두 부품이 띄우는 "사용 중단 예정" 경고(아래 주의할 점)를 읽었으면 이 줄로 숨긴다
warnings.filterwarnings("ignore", category=DeprecationWarning)

store = {}   # {세션 id: 기록 상자}


def get_session_history(session_id):
    # 처음 보는 세션이면 빈 기록 상자를 새로 만든다
    if session_id not in store:
        store[session_id] = InMemoryChatMessageHistory()
    return store[session_id]


prompt = ChatPromptTemplate.from_messages([
    ("system", "너는 여행 비서다."), MessagesPlaceholder("history"), ("human", "{question}"),
])
model = FakeListChatModel(responses=["며칠인가요?", "어디로 가시나요?", "2박 3일 좋습니다."])
bot = RunnableWithMessageHistory(
    prompt | model,                       # 감쌀 체인
    get_session_history,                  # 세션 id → 기록 상자
    input_messages_key="question",        # 새 질문이 든 열쇠
    history_messages_key="history",       # 프롬프트의 기록 자리 이름
)

for sid, q in [("kim", "제주도 가요"), ("lee", "여행 가고 싶어요"), ("kim", "2박 3일이요")]:
    reply = bot.invoke({"question": q}, config={"configurable": {"session_id": sid}})
    print(sid, "→", reply.content)

# 세션마다 기록 상자에 쌓인 메시지 수
print({sid: len(h.messages) for sid, h in store.items()})
출력
kim → 며칠인가요?
lee → 어디로 가시나요?
kim → 2박 3일 좋습니다.
{'kim': 4, 'lee': 2}

kim 은 두 번 물어 질문·답 두 쌍(메시지 4개)이, lee 는 한 번 물어 한 쌍(2개)이 쌓였습니다. 두 손님의 기록이 섞이지 않았습니다. 기록 상자에는 시스템 지시가 들어가지 않습니다. 시스템 지시는 프롬프트 틀에 있어서 요청마다 새로 붙기 때문입니다.

코드가 의미하는 것

이 코드는 회원번호로 기록철을 찾아 펴고 다시 꽂는 비서를 부품으로 세운 것입니다. 철 보관함과 철을 찾는 함수를 두고, 체인을 비서로 감싸고, 두 손님과 번갈아 이야기합니다.

코드하는 일챗봇에서 뜻하는 것
store = {} 와 get_session_history세션 id 마다 기록 상자를 찾거나 만든다손님별 기록철 보관함입니다. 처음 온 손님에게만 새 철을 만듭니다
RunnableWithMessageHistory(prompt | model, …)체인을 기록 다루는 부품으로 감싼다앞 절의 chat() 이 손으로 하던 꺼내기와 덧붙이기를 대신합니다
input_messages_key, history_messages_key새 질문의 열쇠와 기록 자리 이름을 알려 준다무엇을 기록에 덧붙이고 기록을 어디에 넣을지 정하는 이름표입니다
config={"configurable": {"session_id": sid}}부를 때 어느 손님인지 넘긴다웹에서는 로그인한 사람의 번호처럼 손님마다 다른 값이 들어갈 자리입니다

결과에서 읽을 것은 두 가지입니다.

  • 번갈아 물어도 섞이지 않았다. {'kim': 4, 'lee': 2} 로 손님마다 자기 대화만 쌓였습니다.
  • 답의 내용은 기록과 상관없다. 가짜 모델은 목록의 답을 차례로 돌려주므로 "2박 3일 좋습니다."가 kim 에게 간 것은 적어 둔 순서 때문입니다. 이 예시에서 볼 것은 답이 아니라 기록의 수입니다.

주의할 점

  • 지금 판의 langchain-core(1.7)에서 이 두 부품은 "사용 중단 예정(deprecated)"으로 표시되어 있어서, 처음 쓸 때 경고 글이 나옵니다. RunnableWithMessageHistory 는 1.3.3 판부터, InMemoryChatMessageHistory 는 1.6.4 판부터이고, 둘 다 2.0 판에서 빠질 예정이라고 적혀 있습니다. 경고는 LangGraph 의 저장 기능(persistence)을 대신 쓰라고 권합니다. 이 과정은 1.x 판에서 그대로 돌아가고 원리를 보기 쉬운 이 두 부품으로 배우고, 실습 코드에서는 경고를 읽은 뒤 warnings.filterwarnings 로 숨깁니다. 판을 올릴 때는 공식 문서의 단기 기억(short-term memory) 안내를 확인합니다.
  • 세션 id 는 손님을 가리는 열쇠입니다. 웹에서는 로그인한 사람의 번호나 브라우저마다 만든 값으로 정합니다. 남의 세션 id 로 부르면 남의 대화가 보이므로 아무나 정할 수 있게 두지 않습니다.
  • 기록을 오래 두어야 하면 메모리 대신 파일이나 데이터베이스에 저장합니다. 실습 3에서는 기록을 json 파일로 남겨 봅니다.
← 이전 절4장 · MessagesPlaceholder 로 기록 자리 만들기다음 절 →4장 · 최근 대화만 남기는 Window 메모리