Message Passing Pattern
정의
Message Passing Pattern은 LangChain에서 멀티턴 대화를 구현하는 가장 기본적인 방식. 이전 대화 내용을 리스트에 수동으로 저장하고, 프롬프트에 직접 전달하는 패턴이다.
핵심 개념
대화 리스트 = [메시지1, 메시지2, 메시지3, ...]
↓
프롬프트에 직접 삽입
↓
"이전 대화: [메시지들...]"
"새 질문: [현재 입력]"
기본 구현
from langchain.schema import HumanMessage, AIMessage
from langchain.chat_models import ChatOpenAI
from langchain.prompts import ChatPromptTemplate
# 1. 대화 리스트 초기화
chat_history = []
# 2. 프롬프트 구성 (messages 플레이스홀더)
prompt = ChatPromptTemplate.from_messages([
("system", "당신은 도움을 주는 어시스턴트입니다."),
("placeholder", "{messages}"),
("human", "{input}")
])
# 3. 모델과 체인
model = ChatOpenAI(model="gpt-4o")
chain = prompt | model
# 4. 첫 번째 대화
user_input = "안녕하세요, 저는 말차를 좋아해요"
chat_history.append(HumanMessage(content=user_input))
response = chain.invoke({
"messages": chat_history,
"input": user_input
})
chat_history.append(AIMessage(content=response.content))
# 5. 두 번째 대화 (이전 메시지 포함)
user_input = "디저트 추천해줄래?"
chat_history.append(HumanMessage(content=user_input))
response = chain.invoke({
"messages": chat_history, # ← 모든 이전 메시지 포함
"input": user_input
})
chat_history.append(AIMessage(content=response.content))메시지 타입
HumanMessage vs. AIMessage
from langchain.schema import HumanMessage, AIMessage
# 사용자 메시지
human_msg = HumanMessage(content="일본어로 '잘 먹겠습니다' 말해줄래?")
# AI 메시지
ai_msg = AIMessage(content="いただきます (이타다키마스)")
# 메시지 리스트
chat_history = [human_msg, ai_msg]
print(chat_history)
# Output:
# [
# HumanMessage(content="일본어로 '잘 먹겠습니다' 말해줄래?"),
# AIMessage(content="いただきます (이타다키마스)")
# ]실제 예제: 기본 대화
from langchain.schema import HumanMessage, AIMessage
from langchain.chat_models import ChatOpenAI
from langchain.prompts import ChatPromptTemplate
# 설정
chat_history = []
model = ChatOpenAI(model="gpt-4o")
prompt = ChatPromptTemplate.from_messages([
("system", "당신은 친절한 어시스턴트입니다."),
("placeholder", "{messages}"),
("human", "{input}")
])
chain = prompt | model
# 대화 1
print("=== 대화 1 ===")
user_input = "일본어로 '잘 먹겠습니다' 말해줄래?"
chat_history.append(HumanMessage(content=user_input))
response = chain.invoke({
"messages": chat_history,
"input": user_input
})
print(f"사용자: {user_input}")
print(f"AI: {response.content}")
chat_history.append(AIMessage(content=response.content))
# 대화 2 (이전 컨텍스트 활용)
print("\n=== 대화 2 ===")
user_input = "내가 방금 뭐라고 했지?"
chat_history.append(HumanMessage(content=user_input))
response = chain.invoke({
"messages": chat_history, # ← 이전 메시지 포함!
"input": user_input
})
print(f"사용자: {user_input}")
print(f"AI: {response.content}")
chat_history.append(AIMessage(content=response.content))
# AI: "당신은 일본어로 '잘 먹겠습니다'를 어떻게 말하는지 물어봤습니다."수동 루프 예제
from langchain.schema import HumanMessage, AIMessage
from langchain.chat_models import ChatOpenAI
from langchain.prompts import ChatPromptTemplate
chat_history = []
model = ChatOpenAI(model="gpt-4o")
prompt = ChatPromptTemplate.from_messages([
("system", "당신은 친절한 어시스턴트입니다."),
("placeholder", "{messages}"),
("human", "{input}")
])
chain = prompt | model
# 대화 루프
while True:
user_input = input("사용자: ")
if user_input.lower() == "quit":
break
# 1. 사용자 입력 저장
chat_history.append(HumanMessage(content=user_input))
# 2. 모델에 invoke (전체 히스토리 포함)
response = chain.invoke({
"messages": chat_history,
"input": user_input
})
# 3. AI 응답 출력 및 저장
print(f"AI: {response.content}")
chat_history.append(AIMessage(content=response.content))프롬프트 보이기 (Debug용)
# 실제 프롬프트에 무엇이 전달되는지 보기
from langchain.schema import HumanMessage, AIMessage
chat_history = [
HumanMessage(content="일본어로 '잘 먹겠습니다' 말해줄래?"),
AIMessage(content="いただきます (이타다키마스)"),
HumanMessage(content="내가 방금 뭐라고 했지?")
]
# 프롬프트에 전달될 메시지들
print("=== 프롬프트에 전달될 메시지 ===")
for msg in chat_history:
print(f"{msg.__class__.__name__}: {msg.content}")
# 출력:
# HumanMessage: 일본어로 '잘 먹겠습니다' 말해줄래?
# AIMessage: いただきます (이타다키마스)
# HumanMessage: 내가 방금 뭐라고 했지?장점과 단점
✅ 장점
- 가장 기본적 — LangChain의 핵심 개념만 사용
- 완전한 제어 — 메시지 추가/삭제/조작 자유도 높음
- 단순한 구조 — 이해하기 쉬움
- 플렉시블 — 도메인별 커스터마이제이션 용이
❌ 단점
- 수동 관리 — 매번 append 필요
- 토큰 낭비 — 모든 이전 메시지를 프롬프트에 포함
- 스케일 문제 — 대화가 길수록 비용 ↑
- 세션 관리 없음 — 다중 사용자 지원 복잡
- 메모리 저장 불편 — 파일/DB 저장 시 별도 로직 필요
vs. ChatMessageHistory
| 항목 | Message Passing | ChatMessageHistory |
|---|---|---|
| 저장 방식 | 수동 append | add_user_message() 메서드 |
| 메시지 타입 | HumanMessage, AIMessage | 동일 |
| 코드 길이 | 짧음 | 중간 |
| 구조화 | 낮음 | 높음 |
| invoke 시 저장 | 수동 | 수동 (Method 2) |
vs. RunnableWithMessageHistory
| 항목 | Message Passing | RunnableWithMessageHistory |
|---|---|---|
| 자동화 | ❌ | ✅ |
| 세션 관리 | ❌ | ✅ |
| invoke 저장 | 수동 | 자동 |
| 다중 사용자 | 복잡 | 간단 |
토큰 효율성 문제
대화 1: 100 tokens
대화 2: 100 + 전체 히스토리 = 300 tokens ⬆️
대화 3: 100 + 전체 히스토리 = 500 tokens ⬆️⬆️
대화 10: 100 + 전체 히스토리 = 1500+ tokens 🔥
해결책: RunnableWithMessageHistory + Message Summarization
심화 예제: 음악 추천 대화
from langchain.schema import HumanMessage, AIMessage
from langchain.chat_models import ChatOpenAI
from langchain.prompts import ChatPromptTemplate
chat_history = []
model = ChatOpenAI(model="gpt-4o")
prompt = ChatPromptTemplate.from_messages([
("system", "당신은 음악 추천 전문가입니다."),
("placeholder", "{messages}"),
("human", "{input}")
])
chain = prompt | model
# 대화 흐름
messages_and_inputs = [
"안녕",
"나는 말차를 좋아해",
"디저트 추천해줄래?", # ← AI가 "말차"를 기억해야 함
]
for user_input in messages_and_inputs:
chat_history.append(HumanMessage(content=user_input))
response = chain.invoke({
"messages": chat_history,
"input": user_input
})
print(f"사용자: {user_input}")
print(f"AI: {response.content}\n")
chat_history.append(AIMessage(content=response.content))학습 경로
- Message Passing Pattern (본 문서) — 기초 이해
- ChatMessageHistory — 구조화
- RunnableWithMessageHistory — 자동화
- Message Summarization — 토큰 최적화
관련 소스: word-chain-chatbot (Method 1 예제)