# rb8001 대화 이중 저장 시스템 구현 ## 작성일: 2025-08-26 ## 작성자: happybell80 ## 관련 서비스: rb8001, PostgreSQL, ChromaDB --- ## 문제 상황 ### 기존 문제점 1. **외부 서비스 의존성 제거** - rb8001이 직접 PostgreSQL 접근하도록 변경 - 중간 API 계층 제거로 안정성 향상 - 대화가 ChromaDB와 PostgreSQL 양쪽에 저장 2. **데이터 일관성 확보** - ChromaDB: 대화 저장 성공 - PostgreSQL: 직접 저장으로 안정성 향상 - 이중 저장으로 데이터 안정성 확보 3. **성능 개선** - 불필요한 HTTP API 호출 제거 - 직접 DB 연결로 응답 속도 향상 --- ## 조사 과정 ### 1. 로컬 코드 분석 ```bash # rb8001 대화 저장 로직 확인 grep -r "save_conversation\|store_memory" rb8001/ # 결과: # - router.py:309: _save_conversation() 메서드 # - ChromaDB 직접 저장 + PostgreSQL 직접 저장 ``` ### 2. 서버 상태 확인 (51124) ```bash # PostgreSQL 연결 테스트 PGPASSWORD=robeings psql -h 192.168.0.100 -U robeings -d main_db -c "\dt" # 결과: # - conversation_log 테이블 존재 ✅ # - robeing_stats 테이블 존재 ✅ # - 직접 연결 가능 ``` ### 3. 데이터베이스 연결 설정 ```bash # 직접 연결 설정 192.168.0.100:5432/main_db ✅ ``` --- ## 해결 방안 ### 1. DATABASE_URL 수정 ```diff # rb8001/.env - DATABASE_URL=postgresql://robeings:<비밀번호>@localhost/rb8001_db + DATABASE_URL=postgresql://robeings:<비밀번호>@192.168.0.100:5432/main_db ``` ### 2. 직접 DB 저장 구현 ```python # rb8001/app/router/router.py:309 async def _save_conversation(self, message: str, response: str, user_id: str, channel: str, intent: str = None, confidence: float = None): """대화를 PostgreSQL과 ChromaDB에 직접 저장""" chroma_saved = False postgres_saved = False # 1. ChromaDB에 저장 시도 try: await self.memory_manager.store_memory(...) chroma_saved = True logger.info(f"ChromaDB: Conversation saved for user {user_id}") except Exception as e: logger.error(f"ChromaDB save failed: {e}") # 2. PostgreSQL에 직접 저장 시도 try: from app.state.database import SessionLocal db = SessionLocal() try: conversation_log = ConversationLog( robeing_id=settings.ROBEING_ID, user_id=user_id, channel_id=channel, message=message, response=response, intent=intent, confidence=confidence ) db.add(conversation_log) db.commit() postgres_saved = True logger.info(f"PostgreSQL: Conversation saved for user {user_id}") finally: db.close() except Exception as e: logger.error(f"PostgreSQL save failed: {e}") # 최소 하나는 성공해야 함 if not chroma_saved and not postgres_saved: logger.error("Failed to save conversation to both storages") elif chroma_saved and postgres_saved: logger.info(f"Conversation saved to both storages for user {user_id}") ``` ### 3. 외부 의존성 제거 - HTTP API 호출 코드 제거 - 직접 SQLAlchemy 사용 - 트랜잭션 처리 추가 --- ## 구현 결과 ### 장점 1. **안정성 향상** - 한쪽 저장소 실패해도 다른 쪽 저장 보장 - 트랜잭션 단위 에러 처리 2. **성능 개선** - 외부 API 호출 제거 - 네트워크 지연 감소 - 직접 DB 연결로 빠른 저장 3. **관리 단순화** - 중간 서비스 계층 불필요 - 단일 서비스 내 모든 로직 포함 ### 데이터 흐름 ```mermaid graph LR A[Slack 메시지] --> B[rb8001] B --> C{저장 로직} C --> D[ChromaDB
벡터 저장] C --> E[PostgreSQL
구조화 저장] D --> F[저장 결과 로깅] E --> F ``` --- ## 추가 문제 해결 (user_id UUID 타입 불일치) ### 발견된 문제 1. **PostgreSQL 저장 실패** ``` PostgreSQL save failed: invalid input syntax for type uuid: "system" PostgreSQL save failed: invalid input syntax for type uuid: "U0925SXQFDK" ``` 2. **원인 분석** - conversation_log 테이블의 user_id가 UUID 타입 - rb8001은 Slack user ID (문자열) 전송 - UUID 변환 불가능한 값으로 저장 실패 ### DB 스키마 수정 (51123 서버) ```sql -- slack_user_id 컬럼 추가 ALTER TABLE conversation_log ADD COLUMN slack_user_id VARCHAR(255); -- user_id를 nullable로 변경 ALTER TABLE conversation_log ALTER COLUMN user_id DROP NOT NULL; ``` ### rb8001 코드 수정 필요 ```python # router.py:338 수정 conversation_log = ConversationLog( robeing_id=settings.ROBEING_ID, slack_user_id=user_id, # user_id 대신 slack_user_id 사용 channel_id=channel, message=message, response=response, intent=intent, confidence=confidence ) ``` ### database.py 모델 수정 ```python class ConversationLog(Base): """대화 로그 테이블""" __tablename__ = "conversation_log" id = Column(Integer, primary_key=True, index=True) robeing_id = Column(String, index=True) user_id = Column(String, nullable=True) # UUID 타입, nullable slack_user_id = Column(String) # Slack ID 저장용 channel_id = Column(String) message = Column(String) response = Column(String) intent = Column(String) confidence = Column(Float) timestamp = Column(DateTime, default=datetime.utcnow) ``` --- ## 모니터링 ### 로그 확인 ```bash # 51124 서버에서 docker logs rb8001 --tail 100 | grep -E "ChromaDB|PostgreSQL|saved" # 성공 로그 예시: # ChromaDB: Conversation saved for user U092HLS6BKL # PostgreSQL: Conversation saved for user U092HLS6BKL # Conversation saved to both storages for user U092HLS6BKL ``` ### DB 확인 ```sql -- PostgreSQL 저장 확인 SELECT COUNT(*) FROM conversation_log WHERE robeing_id = 'rb8001' AND timestamp > NOW() - INTERVAL '1 hour'; -- 최근 대화 확인 SELECT user_id, message, response, timestamp FROM conversation_log WHERE robeing_id = 'rb8001' ORDER BY timestamp DESC LIMIT 5; ``` --- ## 교훈 ### 1. 아키텍처 단순화 - **문제**: 불필요한 중간 서비스로 인한 복잡도 - **해결**: 직접 연결로 단순화 - **교훈**: KISS 원칙 - Keep It Simple, Stupid ### 2. 이중 저장의 중요성 - **ChromaDB**: 벡터 검색용 (의미 기반 검색) - **PostgreSQL**: 구조화 데이터 (통계, 분석) - **교훈**: 각 저장소의 장점을 활용한 하이브리드 접근 ### 3. 에러 처리 전략 - **부분 실패 허용**: 한쪽만 성공해도 서비스 계속 - **명확한 로깅**: 어느 저장소가 실패했는지 구분 - **교훈**: Graceful degradation 구현 ### 4. SSH 터널 관리 - **문제**: 잘못된 터널 설정으로 auth_db 연결 시도 - **해결**: main_db로 터널 재설정 - **교훈**: 인프라 설정 문서화 필수 --- ## 관련 파일 - `/home/happybell/projects/ivada/rb8001/.env` - DATABASE_URL 설정 - `/home/happybell/projects/ivada/rb8001/app/router/router.py` - 저장 로직 - `/home/happybell/projects/ivada/rb8001/app/state/database.py` - DB 모델 - `/home/happybell/projects/ivada/rb8001/app/memory/manager.py` - ChromaDB 관리 --- ## 참고 문서 - [일일 브리핑 시퀀스](/home/happybell/projects/ivada/DOCS/300_architecture/sequences/daily_briefing_sequences.md) - [로빙 상태 표시 문제](/home/happybell/projects/ivada/DOCS/troubleshooting/250825_robeing_stats_display_issue.md) --- **문서 끝**