멀티턴, 대화 요약를 위한 DB 설계
멀티턴, summary를 지원할 수 있도록 DB 설계를 해보도록 하겠다.
DB 종류 선정
- SQLite : 간단하고 빠르며, 챗봇 애플리케이션의 대화 기록을 관리하는 데 적합.
설치 및 설정이 간단 : 파일 기반 DB이므로 별도의 서버 설정이 필요 없고, 파일 하나로 데이터베이스를 다룰 수 있어 빠르게 사용 가능.
경량화 된 구조 : 작은 규모의 데이터베이스에 적합하며, 챗봇 대화 로그와 같이 비교적 단순한 데이터 구조를 다룰 때 효과적
빠른 성능 : 파일 기반 데이터베이스로, 챗봇 애플리케이션의 빠른 응답에 적합.
호환성 : 대부분의 언어와 플랫폼에서 기본으로 지원
결론 : 챗봇 대화 로그나 요약을 저장하는 정도의 용도라면 SQLite가 효율적, 만약 데이터베이스의 확장성과 분산 처리가 필요하다면 PostgreSQL 또는 MySQL 같은 데이터베이스로 전환하는 것이 좋다.
09. SQLite 에 대화내용 저장
- Redis
고속 데이터 처리 : 메모리 기반, I/O 빠르다, 대화 데이터의 실시간 처리가 중요한 챗봇 애플리케이션에 적합.
TTL 기능 : 데이터 만료시간 설정 가능
경량 데이터 처리 : 데이터 구조가 간단하여 작은 규모의 데이터를 빠르게 저장하고 접근 가능.
데이터 영구성 부족 : 인메모리DB, 서버 재기동시 데이터 손실 → 스냅샷과 AOF 설정 추가 필요, but 완전한 영구 저장 솔루션보다는 다소 제한적.
대규모 데이터 저장에 적합하지 않음 : 인메모리DB, 메모리, 비용문제 발생 가능성
데이터 일관성 문제 : 데이터일관성 보다는 속도에 중점 → 상관 없을것 같기도 함..
- PostgreSQL
확장성 및 안정성 : 대규모 데이터를 안정적으로 처리할 수 있는 확장성이 뛰어난 데이터베이스.
트랜잭선 지원 : 트랜잭션 처리 및 동시성 제어에 강력한 기능 제공. 데이터의 일관성과 무결성 보장.
인덱싱및 고급 쿼리 지원 : 요약이나 특정 대화만 빠르게 조회해야 할 때 성능을 높이는 데 유리.
설치 및 관리의 복잡성 : 서버 설치가 필요하고 구성도 다소 복잡함.
메모리 및 디스크 요구사항 : 시스템 리소스를 더 많이 요구, 소규모 프로젝트나 단순한 대화 기록 저장 용도로는 다소 과함.
결론 : 챗봇 데이터가 많아지고 검색이나 통계 분석이 중요한 경우 매우 적합한 선택. 다만 프로젝트 초기 개발 단계이거나 간단한 챗봇 로그 용도라면 PostgreSQL의 장점 활용 못할수도 있음.
1안 : SQLite 사용
테이블 정의서
한 턴의 대화마다 요약을 한다고 가정 했을 때.
Table 명 : interactions
No | Column Name | Attribute Name | Type | Null | Keys | Description |
---|---|---|---|---|---|---|
1 | conversation_id | transaction_id | INTEGER | NN | PK | Sequence |
2 | user_uuid | user_id | TEXT | NN | user id | |
3 | user_input | message | TEXT | NN | user 발화 | |
4 | agent_response | response | TEXT | NN | agent 응답 | |
5 | summary | summary | TEXT | NN | 대화내용 요약 | |
6 | timestamp | timestamp | TIMESTAMP | NN | agent 응답 |
Redis + PostgreSQL?
- Redis로 실시간 대화 기록 처리
- 최신 대화 기록과 같은 실시간 데이터를 Redis에 저장하여 빠르게 읽고 쓰기 가능.
- 대화 중에는 Redis에서 데이터 관리.
- PostgreSQL로 영구 데이터 저장
- 주요 대화 기록이나 요약, 사용자 정보 등의 영구적인 데이터를 PostgreSQL에 저장해 장기적으로 유지
- Redis에서 PostgreSQL로 데이터마이그레이션
- Redis에 저장된 데이터를 주기적으로 PostgreSQL로 이전하는 스케줄러 사용. (배치 작업 등)
단점
- 시스템 복잡성 증가
- 각 데이터베이스의 설정, 관리, 모니터링이 필요하며, 장애 대응이나 성능 최적화도 각각 별도로 수행해야 한다.
- 데이터 동기화가 실패하거나 지연되면 Redis와 PostgreSQL 간의 데이터 불일치 문제가 발생할 수 있어, 이를 해결하기 위한 모니터링과 에러 핸들링이 필요
- 메모리 비용 증가
- Redis는 대량의 데이터를 저장할 경우 메모리 비용 높다.
- Redis의 메모리 사용량을 줄이기 위해 TTL을 설정 -> 필요한 경우 모든 데이터를 PostgreSQL에 영구 저장해야 하므로 Redis와 PostgreSQL의 데이터 저장 비용이 중복
- 두 시스템의 상이한 데이터 모델
- Redis와 PostgreSQL은 서로 다른 데이터 모델과 쿼리 방식을 사용 (키-값 vs SQL)
Langchain.memory의 ConversationSummaryBufferMemory 등이 Redis를 대체 가능한가?
ConversationSummaryMemory는?
- 대화 요약에 특화 : 요약을 기반으로 챗봇의 기억을 관리하는 것이 목적
- 메모리 기반 : 임시 메모리에 저장. 서버 재기동 하거나 메모리 초기화 시 데이터 소실 → Redis는 스냅샷과 AOF 설정으로 반 영구 저장 가능
- 속도 및 접근성 : conversationSummaryMemory는 단일 프로세스 내의 임시 메모리로 사용되기 때문에 Redis처럼 여러 클라이언트가 접근하는 다중 프로세스 환경에서 사용하기는 제한적.
→ ConversationSummaryBufferMemory는 대화 내용을 요약하고 간단히 참조하는 메모리 역할에는 유용하지만 Redis를 대체할 수 있는 완전한 데이터 저장 솔루션은 아님.
→ 영구적이고 구조화된 대화 데이터를 저장하려면 Redis와 같은 외부 영구 저장소가 필요하며, ConversationSummaryBufferMemory는 Redis를 보완하는 임시 요약 메모리로 활용 가능.
RunnableWithMessageHistory + Redis
RunnableWithMessageHistory : 대화 기록과 맥락을 효과적으로 관리하기 위해 LangChain에서 제공하는 클래스
- 이전 메시지의 맥락을 유지해야 할 때 유용.
- 메시지 기록이 시간 순으로 관리되므로, 대화의 특정 메시지를 빠르게 조회가능
- RunnableWithMessageHistory와 함께 대화 요약 모델(예: GPT-3, GPT-4 등)을 연결하여 요약 기능을 구현. 이 클래스는 요약이 필요한 메시지를 정리하여 전달하고, 요약된 응답을 받아 메시지 히스토리에 추가하는 방식으로 동작
1 | from langchain_community.chat_message_histories import ChatMessageHistory |
메시지 기록을 효과적으로 관리하려면 두 가지 요소 필요
- Runnable : 주로 Retriever, Chain과 같이 BaseChatMessageHistory와 상호작용하는 runnable 객체
- BaseChatMessageHistory의 인스턴스를 반환하는 호출 가능한 객체 (callable)
- 메시지 기록을 관리하기 위한 객체
- 메시지 기록을 저장, 검색, 업데이트하는 데 사용.
무엇을 이용해 요약을 하는가?
- 언어 모델을 사용한 요약
- RunnableWithMessageHistory를 특정 언어 모델(예: OpenAI의 GPT)과 함께 설정하면, 대화 내용을 요약하는 요청을 모델에 전달
- LangChain의 요약 도구
- LangChain에서는 특정 요약 알고리즘을 사용하거나, SummaryMemory 클래스를 추가하여 반복적인 요약을 통해 핵심 내용을 추려나가는 구조를 제공
요약할 시점 커스터마이징
RunnableWithMessageHistory와 ConversationSummaryMemory를 사용할 때, 특정 조건에 따라 요약을 트리거할 수 있도록 커스터마이징 가능
대화의 길이, 특정 이벤트 발생, 주기적인 타이밍 등 요약 시점을 유연하게 조정 가능
- 메시지 수에 따른 요약 트리거
1
2if len(runnable.message_history) >= 10:
summary_memory.update_summary(runnable.message_history) - 특정 키워드 또는 이벤트에 따라 요약
1
2
3user_input = "요약해줘"
if "요약" in user_input:
summary_memory.update_summary(runnable.message_history) - 주기적 요약 (타이머 기반)
1
2
3
4
5import time
def periodic_summary():
while True:
time.sleep(300) # 5분마다 요약
summary_memory.update_summary(runnable.message_history) - 대화의 특정 단계에서 요약
1
2def on_conversation_end():
summary_memory.update_summary(runnable.message_history)
저장소 옵션
- 인메모리 ChatMessageHistory 사용
- 메모리 내 메시지 기록 관리
- 빠른 I/O
- 재기동 시 기록 삭제
- RedisChatMessageHistory 사용
- Redis 사용, 메시지 기록 영구 저장 가능
장점
- 고속 데이터 처리와 실시간 응답성
- Redis는 인메모리 데이터 저장소, 대화 데이터 I/O 빠름.
- RunnableWithMessageHistory와 결합하여 사용자의 메시지를 실시간으로 Redis에 기록
- 대화 흐름 관리와 일관성 유지
- RunnableWithMessageHistory는 대화의 흐름을 자연스럽게 관리
- Redis에 기록된 이전 메시지들을 참조할 수 있어 대화의 일관성을 유지한다.
- TTL을 통한 효율적 메모리 관리
- Redis에서는 대화 데이터를 일정 시간 동안만 유지하는 TTL(Time to Live) 설정이 가능.
- 오래된 대화 데이터를 자동으로 삭제하고, 최신 대화에 필요한 정보만 유지할 수 있어 메모리 사용을 최적화.
- RunnableWithMessageHistory와 함께 활용하면 최신 대화 기록만을 기반으로 챗봇이 응답하도록 구성 가능.
- 주기적인 요약 저장으로 메모리 절약
- RunnableWithMessageHistory를 사용하여 대화 흐름이 길어지면 요약을 주기적으로 생성해 Redis에 저장
- 불필요한 세부 메시지를 줄이고 요약본을 중심으로 대화 맥락을 유지.
- 확장성과 유연성
- Redis는 분산 환경에서 확장이 용이하므로, 대규모 사용자와의 대화 기록을 효과적으로 처리.
- RunnableWithMessageHistory를 통해 각 대화의 맥락을 관리하면서, Redis에 저장된 대화 기록을 다른 인스턴스나 마이크로서비스에서도 참조할 수 있어 유연한 확장이 가능.
- 대화 기록과 요약에 대한 손쉬운 접근성
- Redis는 다양한 데이터 구조(List, Hash, Set 등)를 지원하므로, RunnableWithMessageHistory와 함께 대화 기록을 효율적으로 구조화하여 저장 가능.
2안: RunnableWithMessageHistory + Redis
- 대화 기록 저장: RunnableWithMessageHistory의 메시지를 받아서 Redis에 저장한다.
- 요약 저장: 주기적으로 요약을 업데이트하거나, 대화 종료 시 요약을 Redis에 저장한다.
Redis 데이터 구조 설계
- 대화별 메시지 리스트
- Key: conversation:{conversation_id}:messages
- Type: List
- 설명
- 특정 대화의 메시지를 순서대로 저장.
- 각 메시지는 JSON 형식으로 저장, sender, message, timestamp 정보 기록
- 예시 2.대화 요약
1
2
3RPUSH conversation:conv_001:messages
'{"sender": "user", "message": "Hello!", "timestamp": "2024-10-30T10:00:00"}'
'{"sender": "bot", "message": "Hi! How can I help you?", "timestamp": "2024-10-30T10:00:01"}'
- Key: conversation:{conversation_id}:summary
- Type: Hash
- 설명
- 대화의 요약을 저장합니다. 요약을 주기적으로 업데이트.
- 예시
1
2
3HMSET conversation:conv_001:summary
"summary" "User asked about product details"
"timestamp" "2024-10-30T10:15:00"
- 사용자별 대화 목록
- Key: user:{user_id}:conversations
- Type: List
- 설명
- 사용자의 대화 ID를 시간순으로 저장.
- 예시 - 사용자가 conv_001, conv_002, conv_003 대화를 순서대로 진행했다면
1
LPUSH user:123:conversations conv_003 conv_002 conv_001
- user:123:conversations 리스트를 조회하면 사용자가 진행한 대화 목록을 시간 순서대로 볼 수 있다.
- 이 리스트는 사용자가 이전에 진행했던 대화 기록을 관리하거나, 특정 사용자의 과거 대화 요약을 불러올 때 유용
전체 워크플로우 예시
- 사용자가 메시지를 보내면
- RunnableWithMessageHistory는 해당 메시지를 conversation:{conversation_id}:messages 리스트에 저장.
- 이때 각 메시지는 sender, message, timestamp와 함께 JSON 형식으로 저장.
- 대화가 일정 수준으로 진행되면 요약을 업데이트:
- 주기적으로 대화의 요약을 생성하여 conversation:{conversation_id}:summary 해시에 업데이트.
- 요약은 전체 대화의 주요 내용이나 주제를 간략하게 표현하며, 대화가 종료될 때, 일정수준으로 길어지면 업데이트
- 사용자별 대화 기록 관리
- 각 대화가 시작될 때마다 user:{user_id}:conversations 리스트에 해당 대화 ID를 추가.
- 이를 통해 사용자의 전체 대화이력을 관리, 특정 사용자의 과거 대화 조회
References
멀티턴, 대화 요약를 위한 DB 설계
install_url
to use ShareThis. Please set it in _config.yml
.