diff --git a/journey/plans/250808_감정시스템_현실적용_5단계_로드맵.md b/journey/plans/250808_감정시스템_현실적용_5단계_로드맵.md index 6dc2f23..439344d 100644 --- a/journey/plans/250808_감정시스템_현실적용_5단계_로드맵.md +++ b/journey/plans/250808_감정시스템_현실적용_5단계_로드맵.md @@ -1,465 +1,73 @@ -# 로빙 감정 시스템 현실 적용 5단계 로드맵 +# 로빙 감정 시스템 5단계 로드맵 -**작성자**: happybell80 & Claude -**목적**: 감정 시스템 설계서를 현실적으로 구현 가능한 단계별 계획으로 구체화 - -## 핵심 원칙 -- **MVP 우선**: 복잡한 기능보다 작동하는 기본 기능 -- **측정 가능한 성과**: 각 단계마다 명확한 KPI -- **점진적 복잡도**: 단순 → 복잡으로 진화 -- **서비스 분리**: 단일 장애점 방지 -- **함수형 프로그래밍 100%**: 하드코딩 0% 목표. 근거없는 하드코딩 값 절대 사용 금지 - - 모든 값은 설정 파일이나 환경변수에서 로드 - - 순수 함수 체인으로 구성 - - 상태 변경 최소화, 불변성 유지 +**작성일**: 2025-08-08 +**목적**: 7개 감정 모델 → 32개 감정 + 온톨로지 점진 확장 --- -## Phase 1: 7개 감정 기본 구현 (모델 준비 완료, 통합 대기) +## Phase 1: 7개 기본 감정 (완료) + +**구현**: `troubleshooting/250815_emotion_model_training.md` 참조 +- 모델: klue/bert-base → ONNX (442MB, F1 56.3%) +- 감정: fear, surprise, anger, sadness, neutral, happiness, disgust +- 서비스: skill-embedding 통합 완료 + +--- + +## Phase 2: 엔트로피 기반 복잡도 (완료) + +**구현**: `troubleshooting/251016_emotion_entropy_integration.md` 참조 +- 엔트로피로 응답 복잡도 결정 +- 설정: confidence=0.35, entropy=2.0 + +--- + +## Phase 3: 감정 온톨로지 (부분 완료) + +**구현 완료**: `troubleshooting/251016_emotion_ontology_basic.md` 참조 +- emotion_likelihood_ontology.py (11개 규칙) +- OntologyReasoner.reason_with_emotion() + +**미구현**: +- 감정 기반 응답 톤 조정 (현재 LLM에만 의존) +- 감정 기록 및 패턴 분석 DB + +--- + +## Phase 4: 32개 세밀 감정 (미구현) ### 목표 -"이미 학습된 7개 한국어 감정 모델을 skill-embedding에 통합" +- Plutchik 감정 휠 기반 32개 감정 +- Primary(8) → Secondary(8) → Tertiary(16) -### 진행 현황 (2025-08-15 기준) -- ✅ 모델 학습 완료 (F1 56.3%) -- ✅ ONNX 변환 완료 (442MB) -- ⏳ skill-embedding 서비스 통합 대기 -- ⏳ /emotion 엔드포인트 구현 필요 +### 필요 작업 +1. AI Hub 데이터 재라벨링 (7 → 32개) +2. 모델 재학습 (다중 레이블 분류) +3. skill-embedding API 확장 -### 구현 범위 -```python -# AI Hub 데이터로 학습 완료된 7개 감정 -EMOTIONS = [ - "fear", # 공포 (기본정서) - "surprise", # 놀람 (기본정서) - "anger", # 분노 (기본정서) - "sadness", # 슬픔 - "neutral", # 중립 - "happiness", # 행복 - "disgust" # 혐오 -] -# 모델 성능: F1 56.3%, Temperature Scaling 1.232 - -# 엔트로피 계산 (7개 감정) -def calculate_entropy(probs: List[float]) -> float: - """7개 감정 확률값으로 엔트로피 계산""" - return -sum(p * log(p) for p in probs if p > 0) -``` - -### 기술 스택 -- **감정 모델**: klue/bert-base 기반 → ONNX 변환 완료 -- **서비스**: skill-embedding 확장 (포트 8515, /emotion 추가) -- **저장**: 기존 ChromaDB 활용 (메타데이터에 감정 추가) -- **의사결정**: 엔트로피 기반 복잡도 판단 -- **기존 코드**: rb10508_micro의 memory/storage.py 재사용 -- **설정값**: confidence=0.35, entropy=2.0, temperature=1.232 - -### 성능 목표 -- 응답시간: 500ms 이내 -- 정확도: 사용자 평가 3.5/5.0 -- 메모리: 200MB 이내 - -### 데이터 준비 (완료) -- AI Hub 한국어 대화 데이터셋 38,594개 샘플 -- 7개 감정 균형 분포 -- 학습/검증/테스트 분할 완료 -- 클래스 가중치 적용 - -### 검증 방법 -```bash -# 단위 테스트 -pytest tests/test_basic_emotions.py - -# 부하 테스트 -locust -f tests/load_test.py --users 10 - -# 응답시간 측정 -curl -w "@curl-format.txt" http://localhost:8503/analyze -``` - -### 산출물 -- [x] 7개 감정 모델 학습 완료 (training_emotion) ✅ 2025-08-08 -- [x] ONNX 변환 완료 (442MB) ✅ 2025-08-13 -- [x] 모델 파일 51124 서버 배치 완료 ✅ 2025-08-13 -- [ ] skill-embedding 서비스에 /emotion 엔드포인트 추가 ⏳ -- [ ] Temperature Scaling 적용 (1.232) ⏳ -- [ ] 엔트로피 계산기 구현 ⏳ -- [ ] ChromaDB 메타데이터 통합 ⏳ -- [ ] rb10508_micro 연동 ⏳ - -### 아키텍처 결정 사항 (2025-08-12) -**문제**: 감정 분석을 어디에 구현할 것인가? -- Option A: 별도 skill-emotion 서비스 (아키텍처 원칙) -- Option B: skill-embedding에 통합 (자원 효율성) - -**최종 결정**: skill-embedding에 통합 -- 이유: ONNX Runtime 공유, 메모리 200MB 절약, 레이턴시 감소 -- 트레이드오프: 서비스 역할 혼재 vs 실용성 -- 향후: 서비스명 변경 고려 (skill-embedding → skill-ai) - -### 현재 보유 모델 (2025-08-15) -1. **aihub-7emotions** (메인 모델) - - 크기: 442MB (ONNX) - - 감정: fear, surprise, anger, sadness, neutral, happiness, disgust - - 성능: F1 56.3%, ECE 0.090 - - 위치: /home/admin/ivada_project/onnx_models/aihub-7emotions/ - -2. **korean-sentiment-kcelectra** (보조 모델) - - 크기: 511MB (ONNX) - - 감정: 11개 한국어 세분화 감정 - - 성능: F1 70.72% - - 위치: /home/admin/ivada_project/onnx_models/korean-sentiment-kcelectra/ +**예상 기간**: 2-3개월 --- -## Phase 2: 서비스 통합 및 최적화 +## Phase 5: 감정 기억 시스템 (미구현) -### 목표 -"skill-embedding 서비스 통합, 캐싱 구현, rb10508_micro 연동" +### 설계 +- Neo4j에 감정 노드 저장 +- 시간별 감정 변화 추적 +- 패턴 분석 (우울증 조기 감지 등) -### 우선 작업 (로컬 개발자) -1. skill-embedding에 /emotion 엔드포인트 추가 -2. ONNX 모델 로딩 및 추론 코드 구현 -3. Temperature Scaling (T=1.232) 적용 -4. rb10508_micro에서 감정 API 호출 통합 - -### 최적화 전략 -```python -# LRU 캐시 적용 -from functools import lru_cache - -@lru_cache(maxsize=1000) -def get_emotion_embedding(text: str) -> np.ndarray: - """자주 사용되는 텍스트의 임베딩 캐싱""" - return model.encode(text) - -# 배치 처리 -async def batch_analyze(texts: List[str]): - """여러 텍스트 동시 처리""" - embeddings = model.encode(texts, batch_size=32) - return [analyze_emotion(emb) for emb in embeddings] +### 구조 +``` +User -feels→ Emotion -at→ Time + -talks_about→ Topic ``` -### ChromaDB 튜닝 -- 인덱스 최적화: HNSW 파라미터 조정 -- 쿼리 최적화: top-k를 10으로 제한 -- 연결 풀링: 커넥션 재사용 - -### 프로파일링 -```python -# 병목 지점 찾기 -import cProfile -import pstats - -cProfile.run('analyze_emotion(text)', 'profile_stats') -stats = pstats.Stats('profile_stats') -stats.sort_stats('cumulative').print_stats(10) -``` - -### 성능 목표 -- 응답시간: 200ms (60% 개선) -- 동시 처리: 50 req/s -- 캐시 적중률: 30% - -### 산출물 -- [x] ONNX 모델 변환 완료 (442MB) ✅ -- [ ] skill-embedding /emotion 엔드포인트 구현 ⏳ -- [ ] LRU 캐시 시스템 (5분 TTL) -- [ ] 배치 처리 API -- [ ] ChromaDB 감정 메타데이터 인덱싱 -- [ ] 성능 모니터링 (Grafana) -- [ ] rb10508_micro 감정 기반 응답 톤 조정 +**예상 기간**: 1개월 --- -## Phase 3: 감정 패턴 분석 및 개인화 +## 참고 -### 목표 -"장기 감정 패턴 추적, 사용자별 감정 프로파일 구축" - -### 감정 패턴 분석 -```python -# 시간별 감정 추적 -class EmotionTracker: - def __init__(self, user_id: str): - self.user_id = user_id - self.history = [] # 시계열 감정 데이터 - - def track(self, emotion_result: dict): - """감정 결과를 시계열로 저장""" - self.history.append({ - "timestamp": datetime.now(), - "emotions": emotion_result["emotions"], - "dominant": emotion_result["dominant"], - "entropy": emotion_result["entropy"] - }) - - def get_pattern(self, period: str = "day"): - """일/주/월 단위 감정 패턴 분석""" - # 시간대별 주요 감정 - # 감정 변화 추이 - # 엔트로피 패턴 - return analyze_temporal_pattern(self.history, period) -``` - -### 개인화 전략 -- 사용자별 감정 프로파일 생성 -- 감정 응답 히스토리 학습 -- 개인별 감정 임계값 조정 -- 엔트로피 특이점 활용 (창발적 응답) - -### 엔트로피 기반 의사결정 -```python -class EntropyBasedDecision: - def __init__(self): - self.entropy_threshold = 2.5 # 특이점 임계값 - - def should_be_creative(self, entropy: float) -> bool: - """높은 엔트로피일 때 창의적 응답""" - return entropy > self.entropy_threshold - - def adjust_response(self, response: str, emotion_result: dict): - """감정에 따른 응답 톤 조정""" - if emotion_result["dominant"] == "sadness": - return make_empathetic(response) - elif emotion_result["dominant"] == "anger": - return make_calm(response) - elif self.should_be_creative(emotion_result["entropy"]): - return make_creative(response) - return response -``` - -### 성능 목표 -- 패턴 분석: 일 1회 배치 처리 -- 프로파일 업데이트: 실시간 -- 감정 히스토리: 30일 보관 -- 개인화 정확도: 70% 이상 - -### 산출물 -- [ ] 감정 패턴 분석기 -- [ ] 사용자 감정 프로파일 DB -- [ ] 엔트로피 기반 의사결정 모듈 -- [ ] 시계열 감정 시각화 -- [ ] 개인화 응답 전략 - ---- - -## Phase 4: 베이지안 학습 시스템 - -### 목표 -"실시간 학습과 개인화된 감정 모델 구축" - -### 베이지안 파라미터 -```python -class BayesianEmotionModel: - def __init__(self): - # Dirichlet 사전분포 (9개 감정) - self.emotion_prior = np.ones(9) - - # Beta 분포 (저장 결정) - self.save_alpha = 1 - self.save_beta = 1 - - # Gamma 분포 (응답 길이) - self.length_k = 2 - self.length_theta = 50 - - def update_posterior(self, observation): - """관측값으로 사후분포 업데이트""" - self.emotion_prior += observation['emotion_counts'] - - if observation['saved']: - self.save_alpha += 1 - else: - self.save_beta += 1 - - # Gamma 업데이트 (moment matching) - self.length_k, self.length_theta = \ - self.update_gamma(observation['response_length']) -``` - -### 예측-평가 루프 -```python -async def prediction_evaluation_loop(user_input): - # 1. 예측 - prediction = model.predict_user_response(user_input) - - # 2. 실제 응답 생성 - actual_response = await generate_response(user_input) - - # 3. 사용자 반응 수집 - user_reaction = await collect_feedback() - - # 4. 오차 계산 (3종) - kl_div = calculate_kl(prediction, user_reaction) - brier = calculate_brier(prediction, user_reaction) - ece = calculate_ece(prediction, user_reaction) - - # 5. 모델 업데이트 - if max(kl_div, brier, ece) > threshold: - model.update_posterior(user_reaction) - - return actual_response -``` - -### 개인화 -- 사용자별 베이지안 파라미터 저장 -- 조직/팀/개인 3단계 계층 구조 -- Cold start: 조직 평균값 사용 - -### 성능 목표 -- ECE: ≤ 0.08 -- Brier Score: ≤ 0.20 -- 학습 수렴: 50회 상호작용 -- 개인화 효과: +15% 만족도 - -### 산출물 -- [ ] 베이지안 모델 클래스 -- [ ] 예측-평가 파이프라인 -- [ ] 3종 오차 메트릭 -- [ ] 사용자별 파라미터 저장소 -- [ ] 학습 곡선 분석 - ---- - -## Phase 5: 프로덕션 및 확장 - -### 목표 -"안정적인 프로덕션 배포와 고급 기능 추가" - -### 프라이버시 게이트 -```python -class PrivacyGate: - def __init__(self): - self.pii_patterns = load_pii_patterns() - self.sensitive_topics = load_sensitive_topics() - - def filter(self, text, metadata): - # PII 감지 - if self.detect_pii(text): - return self.anonymize(text) - - # 민감 주제 필터 - if self.is_sensitive(text): - return {"summary": self.summarize(text), - "original": None} - - # 24시간 옵트아웃 - if metadata.get('opt_out_requested'): - return None - - return text -``` - -### 모니터링 시스템 -```yaml -# prometheus metrics -metrics: - - emotion_analysis_duration_seconds - - emotion_cache_hit_ratio - - bayesian_update_count - - prediction_error_rate - - privacy_filter_triggers - -alerts: - - name: HighECE - expr: emotion_ece > 0.1 - for: 5m - - - name: SlowResponse - expr: emotion_p95_latency > 500 - for: 10m -``` - -### 고급 기능 -- HDBSCAN 클러스터링 도입 -- 감정 전환 패턴 학습 -- 멀티모달 확장 준비 (음성/표정) -- 설명가능 AI (LIME/SHAP) - -### 확장성 -```python -# 수평 확장 준비 -class EmotionAnalyzerCluster: - def __init__(self, workers=4): - self.workers = workers - self.load_balancer = ConsistentHash() - - async def analyze(self, text, user_id): - # 사용자별로 일관된 워커 할당 - worker = self.load_balancer.get_worker(user_id) - return await worker.analyze(text) -``` - -### 최종 KPI -- ECE: ≤ 0.05 -- Brier Score: ≤ 0.18 -- NDCG@10: ≥ 0.6 -- 응답시간 P95: ≤ 300ms -- 가용성: 99.9% - -### 산출물 -- [ ] 프라이버시 게이트 시스템 -- [ ] Prometheus/Grafana 대시보드 -- [ ] 수평 확장 아키텍처 -- [ ] HDBSCAN 클러스터링 -- [ ] 프로덕션 배포 (Docker/K8s) -- [ ] 운영 문서 및 Runbook - ---- - -## 리스크 및 완화 방안 - -### 기술적 리스크 -1. **ChromaDB 성능 한계** - - 완화: Redis 캐시 레이어 추가 - - 대안: Pinecone/Weaviate 검토 - -2. **모델 추론 속도** - - 완화: ONNX 변환, 양자화 - - 대안: DistilBERT 기반 경량 모델 - -3. **베이지안 계산 복잡도** - - 완화: 근사 알고리즘 사용 - - 대안: 단순 EMA로 대체 - -### 데이터 리스크 -1. **라벨 품질** - - 완화: 다중 라벨러, 합의 메커니즘 - - 대안: 약지도 학습 - -2. **개인정보 유출** - - 완화: 로컬 처리, 암호화 - - 대안: 연합 학습 - -### 운영 리스크 -1. **서비스 장애** - - 완화: Circuit breaker, 폴백 - - 대안: 기본 감정만 제공 - -2. **비용 증가** - - 완화: 사용량 기반 스케일링 - - 대안: 엣지 디바이스 처리 - ---- - -## 성공 기준 - -### Phase별 체크포인트 -- **Phase 1**: 5개 감정 인식 작동 확인 -- **Phase 2**: 200ms 응답시간 달성 -- **Phase 3**: 9개 감정 정확도 80% -- **Phase 4**: 개인화 효과 측정 가능 -- **Phase 5**: 프로덕션 안정성 99.9% - -### 전체 프로젝트 성공 지표 -1. **사용자 만족도**: NPS 40 이상 -2. **기술 성능**: 모든 KPI 목표치 달성 -3. **비즈니스 가치**: 사용자 이탈률 20% 감소 -4. **확장 가능성**: 일 100만 요청 처리 - ---- - ---- - -*이 로드맵은 이상적인 설계를 현실적으로 구현 가능한 단계로 나눈 실행 계획입니다. 각 Phase는 독립적으로 가치를 제공하며, 상황에 따라 중단하거나 방향을 전환할 수 있습니다.* \ No newline at end of file +- `book/200_core_design/225_온톨로지_기반_지식_표현.md` +- `troubleshooting/250815_emotion_model_training.md` +- `troubleshooting/251016_emotion_ontology_basic.md` diff --git a/journey/plans/251016_emotion_integration_plan.md b/journey/plans/251016_emotion_integration_plan.md index 18f65ef..31512ac 100644 --- a/journey/plans/251016_emotion_integration_plan.md +++ b/journey/plans/251016_emotion_integration_plan.md @@ -1,264 +1,49 @@ -# 감정 분류기 통합 계획 +# 감정 분류기 Router 통합 계획 **날짜**: 2025-10-16 -**작성자**: Claude -**목표**: Phase 3 감정 온톨로지를 실제 대화에 통합 +**목표**: 감정 분석을 대화 흐름에 통합 --- -## 현재 상태 +## 구현 완료 -### 구현 완료 -- ✅ EmotionClassifier (skill-embedding 연동) -- ✅ emotion_likelihood_ontology.py (11개 규칙) -- ✅ OntologyReasoner.reason_with_emotion() -- ✅ ethics_constraints_ontology.py (Router 통합) - -### 미구현 -- ❌ Router에 감정 분석 통합 -- ❌ 감정 정보를 LLM 컨텍스트에 전달 -- ❌ 실제 대화에서 감정 기반 응답 +**상세**: `troubleshooting/251016_emotion_router_integration.md` 참조 +- EmotionClassifier → skill-embedding 연동 +- Router에 감정 분석 추가 +- LLM 프롬프트에 감정 정보 전달 --- -## 통합 방식 결정 +## 미구현: 감정 기반 응답 톤 자동 조정 -### 방식 1: 증거 기반 우도 조정 (복잡) +### 현재 한계 +- 감정 분석 → LLM 프롬프트 전달만 +- 톤 조정은 LLM 자율 판단 (불안정) +- 명시적 규칙 없음 -**흐름**: -``` -사용자 메시지 → 감정 분석 → 증거 추출 → reason_with_emotion() → 조정된 증거 → LLM -``` +### 필요 작업 -**문제점**: -- "증거"를 어떻게 추출할 것인가? -- 일반 대화에서는 명확한 증거가 없음 -- Coldmail처럼 구조화된 데이터가 아님 - -**적용 가능한 경우**: -- 의사결정 지원 (투자, 구매 등) -- 정보 비교 (장단점 분석) - -### 방식 2: 감정 컨텍스트 전달 (실용적) ✅ 선택 - -**흐름**: -``` -사용자 메시지 → 감정 분석 → context에 감정 추가 → LLM (감정 고려 응답) -``` - -**장점**: -- 구현 간단 -- 모든 대화에 적용 가능 -- LLM이 감정을 자연스럽게 활용 -- 추후 확장 용이 - -**구현 방법**: +**감정-톤 매핑**: ```python -# 1. 감정 분석 -emotion_result = await emotion_classifier.predict_async(message) -user_emotion = emotion_result['top_label'] # fear, joy, sadness, etc. -emotion_confidence = emotion_result['top_p'] - -# 2. context에 추가 -context['user_emotion'] = user_emotion -context['emotion_confidence'] = emotion_confidence - -# 3. LLM에 전달 (프롬프트에서 활용) -llm_response = await llm_service.process_request(llm_request) -``` - ---- - -## 구현 계획 - -### 1단계: 환경변수 추가 - -**.env**: -```bash -# Emotion Analysis (Phase 3 Ontology) -USE_EMOTION_ANALYSIS=false # 기본값: 비활성화 -``` - -**config.py**: -```python -USE_EMOTION_ANALYSIS: bool = os.getenv("USE_EMOTION_ANALYSIS", "false").lower() == "true" -``` - -### 2단계: Router에 감정 분석 통합 - -**위치**: `router.py` → `_call_internal_llm()` 메서드 - -**추가 코드**: -```python -# Phase 3: 감정 분석 (옵션) -if settings.USE_EMOTION_ANALYSIS: - try: - from app.core.emotion.emotion_classifier import get_classifier - emotion_classifier = get_classifier() - - emotion_result = await emotion_classifier.predict_async(message) - user_emotion = emotion_result['top_label'] - emotion_confidence = emotion_result['top_p'] - - # context에 추가 - if context is None: - context = {} - context['user_emotion'] = user_emotion - context['emotion_confidence'] = emotion_confidence - - logger.info(f"Emotion detected: {user_emotion} (confidence: {emotion_confidence:.2f})") - - except Exception as e: - logger.error(f"Emotion analysis failed: {e}") -``` - -### 3단계: LLM 프롬프트에 감정 정보 활용 - -**위치**: `llm_service.py` 또는 프롬프트 생성 부분 - -**프롬프트 예시**: -```python -if context.get('user_emotion'): - emotion = context['user_emotion'] - emotion_map = { - 'fear': '불안', - 'joy': '기쁨', - 'sadness': '슬픔', - 'anger': '분노', - 'surprise': '놀람', - 'disgust': '혐오', - 'trust': '신뢰', - 'neutral': '평온' - } - emotion_kr = emotion_map.get(emotion, emotion) - - prompt += f"\n\n[참고] 사용자의 현재 감정 상태: {emotion_kr}" - prompt += f"\n이 감정을 고려하여 공감적이고 적절한 응답을 제공해주세요." -``` - -### 4단계: 응답에 감정 정보 포함 (디버깅용) - -**응답 형식**: -```python -result = { - "success": True, - "content": final_content, - "model_used": llm_response.model_used, - "emotion_detected": user_emotion, # 추가 - "emotion_confidence": emotion_confidence # 추가 +EMOTION_TONE_MAP = { + "fear": {"style": "reassuring", "emoji": False, "length": "short"}, + "anger": {"style": "calm", "emoji": False, "length": "medium"}, + "sadness": {"style": "empathetic", "emoji": True, "length": "medium"} } ``` ---- +**LLM 시스템 프롬프트 동적 생성**: +- `services/llm/gemini_handler.py` 수정 +- 감정별 톤 지시 자동 삽입 +- 예: "사용자가 불안(fear)을 느낍니다. 안심시키는 톤으로 짧게 답변하세요." -## 테스트 계획 - -### 테스트 케이스 - -1. **불안 (fear)**: - - 입력: "요즘 회사가 걱정돼요..." - - 기대: 위로와 안정감 있는 응답 - -2. **기쁨 (joy)**: - - 입력: "오늘 승진했어요!" - - 기대: 축하와 긍정적인 응답 - -3. **슬픔 (sadness)**: - - 입력: "실패해서 너무 속상해요" - - 기대: 공감과 위로의 응답 - -4. **중립 (neutral)**: - - 입력: "날씨가 어때요?" - - 기대: 일반적인 정보 제공 - -### 검증 방법 - -```bash -# 로그 확인 -docker logs rb8001 --tail 100 | grep -E "Emotion detected|user_emotion" - -# 응답 확인 -# Slack에서 대화 → 로그에서 감정 정보 확인 -``` +**A/B 테스트**: +- ON/OFF 비교 +- 사용자 만족도 측정 --- -## 향후 확장 (Phase 3.5) +## 참고 -### reason_with_emotion() 통합 - -**적용 시나리오**: 의사결정 지원 - -**예시**: -```python -# 사용자: "이 투자 어떻게 생각하세요?" -# 감정: fear (불안) - -# 1. LLM이 증거 생성 -evidences = [ - {"type": "위험", "content": "시장 변동성", "prior_likelihood": 0.6}, - {"type": "긍정", "content": "성장 가능성", "prior_likelihood": 0.5} -] - -# 2. 감정 기반 조정 -adjusted, explanation = reasoner.reason_with_emotion("fear", evidences) -# 위험 0.6 → 0.78 (+30%) - -# 3. 조정된 증거로 최종 응답 -``` - ---- - -## 성능 영향 - -### 추가 처리 시간 -- 감정 분석 API 호출: ~50-100ms -- skill-embedding 서비스 응답 시간 - -### 완화 방법 -- 비동기 호출 (await) -- 타임아웃 설정 (10초) -- 실패 시 graceful degradation - ---- - -## 롤백 방법 - -### 환경변수 비활성화 -```bash -USE_EMOTION_ANALYSIS=false -docker compose down && docker compose up -d -``` - ---- - -## 일정 - -### 즉시 (오늘) -- [x] 통합 계획 수립 -- [ ] 환경변수 추가 -- [ ] Router 코드 수정 -- [ ] 테스트 및 검증 - -### 내일 (2025-10-17) -- [ ] 실전 데이터 수집 -- [ ] 감정 분석 정확도 확인 - -### 향후 (Phase 3.5) -- [ ] reason_with_emotion() 통합 (의사결정 지원) -- [ ] 감정 기반 추천 시스템 -- [ ] 베이지안 학습 (피드백 기반) - ---- - -## 결론 - -**선택한 방식**: 감정 컨텍스트 전달 (방식 2) - -**이유**: -- 간단하고 실용적 -- 모든 대화에 적용 가능 -- LLM의 자연어 이해 능력 활용 -- 점진적 확장 가능 - -**다음 단계**: Router에 감정 분석 코드 추가 +- `troubleshooting/251016_emotion_router_integration.md` +- `311_FastAPI_구조_원칙.md` diff --git a/journey/plans/251016_ontology_coldmail_implementation.md b/journey/plans/251016_ontology_coldmail_implementation.md index bbd6078..b6037ec 100644 --- a/journey/plans/251016_ontology_coldmail_implementation.md +++ b/journey/plans/251016_ontology_coldmail_implementation.md @@ -1,22 +1,13 @@ -# 온톨로지 기반 Coldmail 필터 및 기억 시스템 구현 계획 +# 온톨로지 기반 Coldmail 필터 구현 계획 **날짜**: 2025-10-16 -**작성자**: Claude -**관련 문서**: 200_core_design/225_온톨로지_기반_지식_표현.md +**목표**: 임베딩 한계(파인티처 메일 누락)를 온톨로지 추론으로 해결 --- -## 목표 - -파인티처 메일 누락 같은 임베딩 한계(75% 정확도)를 온톨로지 추론으로 해결하고, 기억-감정-윤리 삼각형을 온톨로지로 구조화하여 로빙의 존재성 강화. - ---- - -## Phase 1: Coldmail 온톨로지 파일럿 (2주) - -### 개념 계층 구축 -**파일**: rb8001/app/ontology/coldmail_schema.owl +## Phase 1: Coldmail 온톨로지 (미착수) +### 개념 계층 ``` 메일 └── 외부메일 @@ -27,314 +18,75 @@ └── 사업계획서 ``` -### 관계 정의 -- `발신자 -속한다→ 회사` -- `회사 -투자단계→ {시드, 시리즈A, ...}` -- `메일 -포함한다→ 첨부파일` - ### 추론 규칙 (10개) -**파일**: rb8001/app/services/coldmail_ontology_reasoner.py -#### Coldmail 판정 규칙 (6개) -1. IF 제목 CONTAINS ["투자", "IR", "피칭", "사업계획"] AND 첨부 HAS PDF THEN coldmail (확률 0.9) -2. IF 첨부파일명 CONTAINS ["회사소개서", "IR_Deck", "Pitch", "사업계획서"] THEN coldmail (확률 0.85) -3. IF 제목 CONTAINS "검토요청" AND 첨부 EXISTS THEN coldmail (확률 0.8) -4. IF 발신자 NOT IN known_contacts AND 첨부 HAS PDF THEN coldmail (확률 0.7) -5. IF 본문 CONTAINS ["투자 유치", "펀딩", "시리즈", "밸류에이션"] THEN coldmail (확률 0.75) -6. IF 발신자.도메인 IN ["startup", "ventures", "capital"] THEN coldmail (확률 0.6) +**Coldmail 판정 (6개)**: +1. 제목 CONTAINS ["투자", "IR", "피칭"] + PDF 첨부 → 0.9 +2. 첨부파일명 CONTAINS ["회사소개서", "IR_Deck"] → 0.85 +3. 제목 "검토요청" + 첨부 → 0.8 +4. 미등록 발신자 + PDF → 0.7 +5. 본문 CONTAINS ["투자 유치", "펀딩", "밸류에이션"] → 0.75 +6. 발신자 도메인 IN ["startup", "ventures", "capital"] → 0.6 -#### Normal 판정 규칙 (4개) -7. IF 제목 CONTAINS ["행사", "초대", "안내", "세미나"] THEN normal (확률 0.9) -8. IF 제목 CONTAINS ["영수증", "발급", "세금계산서", "견적서"] THEN normal (확률 0.95) -9. IF 제목 CONTAINS ["회의", "공지", "보고", "업무"] THEN normal (확률 0.85) -10. IF 발신자 IN known_contacts AND NOT (규칙 1-6) THEN normal (확률 0.8) +**Normal 판정 (4개)**: +7. 제목 CONTAINS ["행사", "초대", "세미나"] → 0.9 +8. 제목 CONTAINS ["영수증", "세금계산서"] → 0.95 +9. 제목 CONTAINS ["회의", "공지", "보고"] → 0.85 +10. 등록 발신자 + 규칙 1-6 미해당 → 0.8 -#### 최종 판단 로직 -- Coldmail 규칙 매칭: 가장 높은 확률 선택 -- Normal 규칙 매칭: 가장 높은 확률 선택 -- Coldmail 확률 > Normal 확률 → Coldmail -- Threshold: 0.7 이상 시 확정, 0.4-0.7은 LLM fallback +### 구현 필요 -### 검증 -- 파인티처 메일: coldmail 확률 0.9+ (현재 0.28) +**파일**: `rb8001/app/services/coldmail_ontology_reasoner.py` +- 규칙 엔진 구현 +- 임베딩 분류(현재 75%) + 온톨로지 추론 → 90%+ 목표 +- Threshold: 0.7 이상 확정, 0.4-0.7은 LLM fallback + +**검증**: +- 파인티처 메일: coldmail 0.9+ (현재 0.28) - 기존 17건 재테스트: 정확도 90%+ --- -## Phase 2: 기억 시스템 온톨로지 통합 (1개월) +## Phase 2: Neo4j 기억 시스템 (미착수) -### Neo4j 도입 -**위치**: 51123 서버 (192.168.219.45) -**설치 상태**: ✅ 이미 설치됨 (시스템 직접 설치, Docker 아님) +### 인프라 -**설치 정보**: -- 버전: Neo4j 2025.06.2 Community Edition -- 서비스 상태: Active (running) since 2025-08-20 -- 가동 시간: 1개월 26일 -- HTTP 포트: 7474 (브라우저 접속) -- Bolt 포트: 7687 (드라이버 연결) -- 인증: 활성화 (비밀번호 변경됨) +**Neo4j 설치 완료** (51123 서버): +- 버전: 2025.06.2 Community +- Bolt: neo4j://192.168.219.45:7687 +- HTTP: http://192.168.219.45:7474 -**연결 정보**: -- Bolt URI: `neo4j://192.168.219.45:7687` -- HTTP API: `http://192.168.219.45:7474` -- 데이터베이스: neo4j (기본 DB), system (시스템 DB) +### 스키마 설계 -**스키마** (설계안): ``` -(사건)-[:발생시각]->(시간) -(사건)-[:관련감정]->(감정) -(사건)-[:참여자]->(사용자) -(사건)-[:결과]->(결과) +(:User)-[:SENDS]->(:Email)-[:CONTAINS]->(:Attachment) +(:Email)-[:CLASSIFIED_AS]->(:Category {name:"coldmail"}) +(:Email)-[:HAS_EMOTION]->(:Emotion {type:"fear", confidence:0.8}) ``` -### ChromaDB + Neo4j 하이브리드 알고리즘 -**rb8001/app/services/memory_hybrid_retrieval.py** - -#### 1단계: ChromaDB 벡터 검색 (빠른 필터링) -```python -# 입력: 사용자 쿼리 (예: "작년 프레젠테이션 때 어떻게 했지?") -# 1. 쿼리 임베딩: skill-embedding (8515) /embed 호출 -# 2. ChromaDB 유사도 검색: top_k=20 (충분한 후보) -# 3. 출력: 20개 후보 대화 (벡터 점수 포함) +### 쿼리 예시 +```cypher +// 투자 제안 이메일 중 긍정적 감정 메일 찾기 +MATCH (u:User)-[:SENDS]->(e:Email)-[:CLASSIFIED_AS]->(:Category {name:"coldmail"}) +WHERE e.emotion IN ["happiness", "neutral"] +RETURN e.subject, e.sender, e.timestamp ``` -#### 2단계: Neo4j 그래프 추론 (의미적 연결) -```python -# 입력: ChromaDB 후보 20개 -# Cypher 쿼리: -MATCH (event:사건)-[:관련감정]->(emotion) -MATCH (event)-[:결과]->(result) -WHERE event.id IN [후보 20개 ID] -RETURN event, emotion, result, event.시간 -ORDER BY event.시간 DESC - -# 우선순위: -# - [:결과]->(성공) 관계 있는 사건 우선 (가중치 1.5배) -# - [:관련감정]->(긴장) 매칭 시 가중치 1.3배 -# - 시간적 근접성: 1년 전 > 2년 전 (거리 역수) -``` - -#### 3단계: 점수 통합 및 순위 결정 -```python -최종점수 = (벡터점수 * 0.4) + (그래프점수 * 0.6) -# 그래프점수 = 관계가중치 * 시간가중치 -# 출력: 상위 5개 사건 반환 -``` - -### API 설계 -**rb8001/app/router/memory_ontology.py**: -- POST /memory/event: 사건 저장 (자동 관계 추론) -- GET /memory/recall: 쿼리 기반 회상 (3단계 하이브리드) - --- -## Phase 3: 감정-윤리 온톨로지 규칙화 (1개월) +## Phase 3: 감정-기억-윤리 삼각형 (미착수) -### 감정-우도 온톨로지 -**파일**: rb8001/app/ontology/emotion_likelihood.owl +### 통합 설계 +- 감정 분석 → 기억 검색 → 윤리 제약 → 응답 생성 +- Neo4j에 감정 이력 저장 +- 패턴 분석 (우울증 조기 감지) -``` -불안 -조정→ 위험관련증거 (가중치 1.3) -흥분 -조정→ 긍정관련증거 (가중치 1.2) -슬픔 -조정→ 위로관련증거 (가중치 1.5) -``` - -### 윤리 제약 온톨로지 -**파일**: rb8001/app/ontology/ethics_constraints.owl - -``` -정보수집 행동 -제약→ [개인정보보호, 투명성, 동의] -조언 행동 -제약→ [해악금지, 자율성존중] -``` - -### 추론 엔진 통합 -**rb8001/app/services/ontology_explainer.py** - -#### 추론 과정 추적 (Jena Rules) -```python -# 규칙 실행 시 추적 로그 생성 -trace = [] -for rule in matched_rules: - trace.append({ - "rule_id": rule.id, - "condition": rule.condition, # "사용자.감정 = 불안" - "action": rule.action, # "위험관련증거.우도 *= 1.3" - "matched_value": matched_value # "감정: 불안" - }) -``` - -#### 설명 템플릿 (자연어 생성) -```python -def generate_explanation(trace): - explanation = [] - for step in trace: - if step['rule_id'].startswith('emotion_'): - template = "사용자의 {emotion} 감정 때문에 {evidence} 관련 증거의 중요도를 {weight}배 조정했습니다." - explanation.append(template.format(...)) - elif step['rule_id'].startswith('ethics_'): - template = "{action} 행동은 {constraint} 원칙에 위배되어 거부했습니다. 대안: {alternative}" - explanation.append(template.format(...)) - return " ".join(explanation) -``` - -#### 윤리 충돌 해결 우선순위 (OWL Ontology) -```turtle -# ethics_constraints.owl -:해악금지 rdf:type :윤리원칙 ; :priority 1 . -:투명성 rdf:type :윤리원칙 ; :priority 2 . -:자율성존중 rdf:type :윤리원칙 ; :priority 3 . -:개인정보보호 rdf:type :윤리원칙 ; :priority 1 . - -# 충돌 시 priority 높은 것 우선 (1 > 2 > 3) -``` - -#### HermiT 일관성 검사 -- 규칙 간 모순 자동 탐지 (예: "불안 → 우도 증가" vs "불안 → 우도 감소") -- 배포 전 ontology validation 자동화 - ---- - -## 기술 스택 - -| 구분 | 도구 | 용도 | -|------|------|------| -| 온톨로지 편집 | Protégé | OWL 스키마 설계 | -| 그래프 DB | Neo4j | 사건-관계 저장 | -| 추론 엔진 | HermiT | 일관성 검사 | -| 벡터 DB | ChromaDB | 기존 유지 (하이브리드) | -| 표준 | RDF/OWL | 온톨로지 표현 | - ---- - -## 마일스톤 - -| 날짜 | 단계 | 목표 | -|------|------|------| -| Week 1-2 | Phase 1 | Coldmail 정확도 90%+ | -| Week 3-6 | Phase 2 | 기억 회상 의미적 연결 | -| Week 7-10 | Phase 3 | 감정-윤리 규칙 투명화 | - ---- - -## 체크리스트 - -Phase 1: ✅ 완료 (2025-10-16) -- [x] Coldmail 온톨로지 스키마 설계 (규칙 기반, Python 구현) -- [x] 추론 규칙 11개 구현 (coldmail_ontology_reasoner.py) -- [x] 파인티처 메일 재테스트 (0.90, 목표 0.9+ 달성) -- [x] Hybrid Filter 통합 (USE_ONTOLOGY_FILTER 환경변수, 롤백 가능) -- [ ] Slack 피드백 → 관계 가중치 베이지안 업데이트 (Phase 1.5) - -Phase 2: -- [x] Neo4j 설치 확인 (51123 서버) - ✅ 이미 설치됨 (2025.06.2) -- [ ] Neo4j Python 드라이버 연동 (neo4j 패키지) -- [ ] ChromaDB + Neo4j 하이브리드 쿼리 구현 -- [ ] "1년 전 비슷한 상황" 회상 테스트 - -Phase 3: -- [ ] 감정-우도 온톨로지 7가지 감정 -- [ ] 윤리 제약 온톨로지 사랑 기반 원칙 -- [ ] HermiT 일관성 검사 자동화 - ---- - ---- - -## Phase 1 구현 완료 (2025-10-16) - -### 구현 파일 -- **rb8001/app/services/coldmail_ontology_reasoner.py**: 11개 추론 규칙 -- **rb8001/app/services/coldmail_hybrid_filter.py**: 온톨로지 통합 (환경변수 제어) -- **rb8001/tests/test_coldmail_ontology.py**: 7개 테스트 케이스 (100% 통과) -- **rb8001/tests/test_hybrid_simple.py**: 5개 통합 테스트 (100% 통과) - -### 테스트 결과 -``` -파인티처 메일: 0.28 → 0.90 (4개 규칙 매칭) -- R1: 투자 키워드 + PDF (0.90) -- R2: 회사소개서 첨부명 (0.85) -- R3: 검토요청 + 첨부 (0.80) -- R3B: 투자검토 키워드 (0.65) -- R4: 신규 발신자 + PDF (0.70) -``` - -### 롤백 시나리오 - -#### 시나리오 1: 환경변수 롤백 (즉시, 권장) -**증상**: 온톨로지 오판, 성능 저하 -**방법**: -```bash -cd /home/admin/ivada_project/rb8001 - -# .env 파일 수정 -USE_ONTOLOGY_FILTER=false - -# Docker 재시작 (5초 소요) -docker compose down && docker compose up -d -``` -**결과**: 기존 임베딩 필터로 즉시 복귀, 코드 변경 없음 - -#### 시나리오 2: Git 부분 롤백 (온톨로지만 제거) -**증상**: 환경변수 롤백으로도 해결 안 될 때 -**방법**: -```bash -cd /home/admin/ivada_project/rb8001 - -# 온톨로지 파일만 제거 -git rm app/services/coldmail_ontology_reasoner.py -git rm tests/test_coldmail_ontology.py tests/test_hybrid_simple.py - -# hybrid_filter.py를 온톨로지 통합 이전 버전으로 복구 -git checkout 48aacfa^ -- app/services/coldmail_hybrid_filter.py - -# 커밋 및 배포 -git commit -m "Rollback: Remove ontology reasoner" -git push origin main -docker compose down && docker compose up -d --build -``` - -#### 시나리오 3: 전체 롤백 (Phase 1 이전) -**증상**: 심각한 오류, 전체 되돌리기 필요 -**방법**: -```bash -cd /home/admin/ivada_project/rb8001 - -# Phase 1 파일럿 직전 커밋으로 복귀 -git checkout 28ef36c # 48aacfa 이전 커밋 -git push origin main --force - -# Docker 재빌드 -docker compose down && docker compose up -d --build -``` -**주의**: force push는 최후 수단 - -#### 롤백 포인트 커밋 -- **28ef36c**: Phase 1 이전 (안전한 복귀 지점) -- **48aacfa**: Phase 1 파일럿 완료 (온톨로지 단독 동작) -- **7a122f4**: Hybrid Filter 통합 -- **88636cf**: UnboundLocalError 핫픽스 (현재) - -#### 롤백 검증 -```bash -# 롤백 후 확인 -docker logs rb8001 --tail 50 | grep -i "ontology\|embedding" -# "Embedding" 메시지만 보이면 롤백 성공 -``` - -### 실전 검증 계획 -- **일시**: 2025-10-17 09:05 Coldmail Daily Briefing -- **확인 사항**: - - [ ] 로그에서 "Stage 1 (Ontology)" 메시지 출력 - - [ ] 파인티처 유사 케이스 발생 시 온톨로지 판정 확인 - - [ ] 오류 없이 정상 동작 확인 - - [ ] 응답 속도 측정 (온톨로지 vs 기존 임베딩) +**예상 기간**: 3-4개월 --- ## 참고 -- 설계 원칙: 200_core_design/225_온톨로지_기반_지식_표현.md -- 문제 배경: troubleshooting/251014_claude_coldmail_filter_tokenization_issue.md -- 온톨로지 연구: research/ontology/ -- **구현 커밋**: rb8001 88636cf (hotfix), 7a122f4 (통합), 48aacfa (파일럿) +- `book/200_core_design/225_온톨로지_기반_지식_표현.md` +- `troubleshooting/250815_emotion_model_training.md` +- `troubleshooting/251016_emotion_ontology_basic.md` diff --git a/journey/plans/251123_rb8001_계층_분리_리팩토링_계획.md b/journey/plans/251123_rb8001_계층_분리_리팩토링_계획.md index b7b22aa..8068774 100644 --- a/journey/plans/251123_rb8001_계층_분리_리팩토링_계획.md +++ b/journey/plans/251123_rb8001_계층_분리_리팩토링_계획.md @@ -1,210 +1,85 @@ # rb8001 계층 분리 리팩토링 계획 **날짜**: 2025-11-23 -**작성자**: admin -**관련 파일**: `rb8001/main.py`, `rb8001/app/router/router.py` -**참고**: `DOCS/book/300_architecture/311_FastAPI_구조_원칙.md` +**참고**: `311_FastAPI_구조_원칙.md` --- ## 목적 -rb8001의 중복 대화 저장 문제를 해결하고, FastAPI 구조 원칙(311_FastAPI_구조_원칙.md)을 준수하는 계층 분리 구조로 리팩토링 - -### 즉시 해결 필요 문제 -- 대화가 DB에 2번 저장되는 중복 저장 버그 (1회 요청 → 2회 DB INSERT) - -### 장기 개선 목표 -- 계층 분리 원칙 위반 15건 해결 -- 유지보수성 및 확장성 향상 -- Conversation/Message 온톨로지 기반 통합 로그(actor, source_channel, channel_id, context_node 등)로 전환 + Celery/Redis 비동기 큐 도입 검토 (ChromaDB, intent_review_queue 백그라운드 처리) +중복 대화 저장 버그 해결 및 계층 분리 원칙 준수 --- -## 현재 문제 +## Phase 1: 중복 저장 해결 (완료) -### 1. 중복 저장 (긴급) - -**문제 상황:** -- router.py:876 - `_call_internal_llm`에서 1차 저장 (router → state 직접 호출) -- main.py:110 - `save_message_conversation`에서 2차 저장 (이미 제거됨) -- 결과: conversation_log 테이블에 동일 대화 2번 삽입 - -**원칙 위반:** -- 311_FastAPI_구조_원칙.md:92 - 계층 건너뛰기 (router → state 직접 호출) -- DRY 원칙 위반 (같은 로직 2곳 실행) - -### 2. 계층 구조 위반 (15건) - -#### 계층 건너뛰기 -1. router.py:876 - router → state 직접 호출 (원칙:92) -2. router/feedback_handler.py:58 - SessionLocal() 직접 사용 (원칙:21, 164) -3. router/intent_review_endpoint.py:21 - SessionLocal() 직접 사용 -4. router/slack_handler.py:77,202,361 - SessionLocal() 직접 사용 -5. services/startup_valuation.py:475 - asyncpg.connect() 직접 사용 (원칙:164) -6. services/intent_bayes.py:57,117,160 - psycopg2.connect() 직접 사용 -7. services/coldmail_filter.py:182,208,255 - asyncpg.connect() 직접 사용 -8. main.py:694,711 - state/database 직접 호출 (원칙:92) - -#### main.py 원칙 위반 -9. main.py:84-176 - 엔드포인트 직접 정의 (원칙:30 - "앱 실행, 라우터 등록만") -10. main.py:118-137 - 비즈니스 로직 포함 (원칙:21) -11. main.py:211,620 - router 내부 메서드(_call_internal_llm) 직접 호출 - -#### 비즈니스 로직 배치 오류 -12. router.py:128-682 - route_message()는 비즈니스 로직, services/에 있어야 함 (원칙:21) -13. app/brain/, app/llm/, app/memory/ - services/brain/, services/llm/로 이동 필요 (원칙:28-42) - -#### 구조 누락 -14. schemas/ 폴더 없음 (원칙:49) -15. models와 state 미분리 - state/database.py에 ORM 모델 + DB 접근 혼재 (원칙:36,47) - -#### 파일 크기 초과 -- 24개 파일이 300줄 초과 (router.py 915줄, main.py 747줄 등) +**문제**: router.py + main.py 이중 저장 +**해결**: `troubleshooting/250924_대화저장_오류.md` 참조 --- -## 해결 방안 +## Phase 2: 계층 건너뛰기 해결 (부분 완료) -### Phase 1: 중복 저장 해결 (긴급, 완료) +**구현 완료**: `troubleshooting/251123_rb8001_endpoint_service_separation.md` 참조 +- main.py 엔드포인트 → routers/로 이동 +- router → services 호출 구조 일부 정리 -**수정 완료:** -- router.py:873-884 - `_save_conversation` 호출 제거 -- router.py:628-638, 670-680 - services 호출 추가 -- main.py:108-115 - `save_message_conversation` 호출 제거 +**미완료 위반 사항**: +1. router/feedback_handler.py:58 - SessionLocal() 직접 사용 (원칙:164) +2. router/intent_review_endpoint.py:21 - SessionLocal() 직접 사용 +3. router/slack_handler.py:77,202,361 - SessionLocal() 직접 사용 +4. services/startup_valuation.py:475 - asyncpg.connect() 직접 사용 +5. services/intent_bayes.py:57,117,160 - psycopg2.connect() 직접 사용 +6. services/coldmail_filter.py:182,208,255 - asyncpg.connect() 직접 사용 -**결과:** -- 대화 저장 1회로 축소 -- 하지만 router에서 services 호출 (임시) - -### Phase 2 Step 1-2: 엔드포인트 및 비즈니스 로직 분리 (완료) - -**수정 완료:** -- app/router/message_endpoint.py 생성 - 엔드포인트 분리 -- main.py:84-176 제거 → message_endpoint.py로 이동 -- main.py에 include_router 추가 -- app/services/message_service.py 생성 - route_message 이동 -- router.py:128-682 (route_message) → message_service.py로 이동 -- router.py는 message_service 호출만 (~10줄) - -**결과:** -- 계층 분리 원칙 준수 (router → services → state) -- 중복 저장 문제 해결 (services 레이어에서 한 번만 저장) -- router.py 크기 927줄 → 366줄로 축소 - -### Phase 2 Step 3: 폴더 구조 정리 (완료) - -**수정 완료:** -- app/brain/ → app/services/brain/ 이동 -- app/llm/ → app/services/llm/ 이동 -- app/memory/ → app/services/memory/ 이동 -- app/skills/ → app/services/skills/ 이동 -- 모든 import 경로 수정 (28개 파일) -- main.py의 skills import 경로 수정 - -**결과:** -- 서비스 레이어 통합 완료 -- import 경로 일관성 확보 - -### Phase 2 Step 4: DB 접근 정리 (완료) - -**수정 완료:** -- feedback_handler.py: SessionLocal 제거, repository 사용 - - handle_chat_feedback를 async로 변경 - - update_or_create_feedback를 async로 변경 (내부 Session 관리) - - get_conversation_by_id 추가 -- slack_handler.py: SessionLocal 제거, slack_repository 사용 - - get_team_uuid_by_slack_team_id 추가 - - 3곳의 SessionLocal 직접 호출 제거 -- intent_review_endpoint.py: FastAPI Depends 패턴 유지 (허용) - -**결과:** -- router/services에서 SessionLocal 직접 사용 제거 -- repository 패턴으로 통일 -- TDD 테스트 작성 및 통과 확인 - -### Phase 2: 계층 분리 리팩토링 (대규모) - -#### Step 1: 엔드포인트 분리 -**생성:** -- app/router/message_endpoint.py (~100줄) - -**수정:** -- main.py:84-176 제거 → message_endpoint.py로 이동 -- main.py에 include_router 추가 - -#### Step 2: 비즈니스 로직 분리 -**생성:** -- app/services/message_service.py (~550줄) - -**수정:** -- router.py:128-682 (route_message) → message_service.py로 이동 -- router.py는 message_service 호출만 (~10줄) -- slack_handler.py:424,437,448,893 - message_service 호출로 변경 - -#### Step 3: 폴더 구조 정리 -**이동:** -- app/brain/ → app/services/brain/ -- app/llm/ → app/services/llm/ -- app/memory/ → app/services/memory/ -- app/skills/ → app/services/skills/ -- app/notifications/ → app/services/notifications/ -- app/pipelines/ → app/services/pipelines/ - -**생성:** -- app/schemas/ - API 요청/응답 스키마 분리 - -#### Step 4: DB 접근 정리 -**생성:** -- app/models/{domain}_model.py - ORM 모델 분리 - -**수정:** -- state/database.py - models 제거, Repository만 남김 -- router/feedback_handler.py, intent_review_endpoint.py, slack_handler.py - state 통해서만 DB 접근 -- services 파일들 - asyncpg.connect() 제거, state 호출로 변경 +**필요 작업**: +- state/repositories/ 폴더 생성 +- DB 접근 로직을 repositories로 분리 +- services에서 repositories 호출 --- -## 기대효과 +## Phase 3: 폴더 구조 정리 (미착수) -### 즉시 효과 -- 중복 저장 버그 해결 → DB 용량 절약, 데이터 정합성 확보 +### 현재 구조 +``` +app/ +├── brain/ # services/brain/으로 이동 필요 +├── llm/ # services/llm/으로 이동 필요 +├── memory/ # services/memory/로 이동 필요 +├── router/ # routers/로 이름 변경 필요 +├── services/ +└── state/ +``` -### 장기 효과 -1. **유지보수성**: 계층별 책임 명확 → 수정 영향 범위 최소화 -2. **확장성**: 새 기능 추가 시 어느 계층에 넣을지 명확 -3. **테스트**: 계층별 단위 테스트 작성 용이 -4. **협업**: 원칙 준수로 코드 가독성 향상 -5. **기술부채 감소**: 15개 위반 사항 해결 +### 목표 구조 (311_FastAPI_구조_원칙.md:28-42) +``` +app/ +├── routers/ # 엔드포인트만 +├── services/ # 비즈니스 로직 +├── state/ +│ ├── models/ # ORM 모델 +│ └── repositories/ # DB 접근 +└── schemas/ # Pydantic 모델 +``` --- -## 구현 규모 +## Phase 4: 파일 크기 제한 (미착수) -### 최소 수정 (Phase 1만) -- 영향 파일: 2개 (router.py, main.py) -- 수정 라인: ~50줄 -- 소요 시간: 완료 +**초과 파일** (300줄 기준): +- router.py: 915줄 +- main.py: 747줄 +- calendar_handler.py: 800줄+ -### 전체 리팩토링 (Phase 2) -- 영향 파일: 최소 30개 -- 신규 생성: ~650줄 -- 수정/제거: ~1,300줄 -- 총 변경: ~2,000줄 -- 예상 소요: 1-2일 - ---- - -## 우선순위 - -1. **긴급 (완료)**: 중복 저장 버그 해결 -2. **높음**: 엔드포인트 분리 (main.py 원칙 준수) -3. **중간**: 비즈니스 로직 분리 (route_message → service) -4. **낮음**: 폴더 구조 정리, 파일 크기 축소 +**분리 계획**: +- 파일별 기능 단위로 분할 +- 단일 책임 원칙 적용 --- ## 참고 -- 311_FastAPI_구조_원칙.md - 계층 분리 원칙 -- 312_문서_작성_원칙.md - 문서 작성 규칙 +- `311_FastAPI_구조_원칙.md` +- `troubleshooting/250924_대화저장_오류.md` +- `troubleshooting/251123_rb8001_endpoint_service_separation.md`