diff --git a/plans/250808_감정시스템_현실적용_5단계_로드맵.md b/plans/250808_감정시스템_현실적용_5단계_로드맵.md index 2213234..796dc57 100644 --- a/plans/250808_감정시스템_현실적용_5단계_로드맵.md +++ b/plans/250808_감정시스템_현실적용_5단계_로드맵.md @@ -8,7 +8,10 @@ - **측정 가능한 성과**: 각 단계마다 명확한 KPI - **점진적 복잡도**: 단순 → 복잡으로 진화 - **서비스 분리**: 단일 장애점 방지 -- **함수형 100%**: 하드코딩 0%, 순수 함수 체인 +- **함수형 프로그래밍 100%**: 하드코딩 0% 목표. 근거없는 하드코딩 값 절대 사용 금지 + - 모든 값은 설정 파일이나 환경변수에서 로드 + - 순수 함수 체인으로 구성 + - 상태 변경 최소화, 불변성 유지 --- diff --git a/plans/250812_감정시스템_Phase1_실행계획.md b/plans/250812_감정시스템_Phase1_실행계획.md index fb367b2..28850b5 100644 --- a/plans/250812_감정시스템_Phase1_실행계획.md +++ b/plans/250812_감정시스템_Phase1_실행계획.md @@ -4,6 +4,32 @@ 작성자: Claude (51123 서버) 상태: 실행 준비 완료 +## 0. 개발 철학 (절대 준수) + +### 함수형 프로그래밍 100% 원칙 +- **하드코딩 0% 목표**: 근거없는 하드코딩 값 절대 사용 금지 +- **모든 설정값은 외부화**: + - 환경변수 (.env) + - 설정 파일 (config.yaml) + - DB에서 동적 로드 +- **순수 함수 체인**: 부작용 없는 함수 조합 +- **불변성 유지**: 상태 변경 대신 새 객체 생성 + +### 예시 +```python +# ❌ 잘못된 예 (하드코딩) +def analyze_emotion(text): + if len(text) > 100: # 하드코딩된 값 + return "neutral" + threshold = 0.3 # 하드코딩된 임계값 + +# ✅ 올바른 예 (함수형) +def analyze_emotion(text: str, config: Dict) -> Dict: + max_length = config.get("max_text_length", 512) + threshold = config.get("confidence_threshold", 0.3) + # 순수 함수로 처리 +``` + ## 1. 현황 분석 결과 ### 1.1 기존 자산 @@ -100,12 +126,13 @@ import torch import numpy as np class EmotionAnalyzer: - def __init__(self, model_path="/opt/models/emotion"): - self.model = AutoModelForSequenceClassification.from_pretrained(model_path) - self.tokenizer = AutoTokenizer.from_pretrained(model_path) - self.temperature = 1.232 # from calibration - self.emotions = ['fear', 'surprise', 'anger', 'sadness', - 'neutral', 'happiness', 'disgust'] + def __init__(self, config: Dict): + # 하드코딩 없음 - 모든 값은 config에서 + self.model_path = config["model_path"] + self.model = AutoModelForSequenceClassification.from_pretrained(self.model_path) + self.tokenizer = AutoTokenizer.from_pretrained(self.model_path) + self.temperature = config["temperature_scaling"] # from calibration.json + self.emotions = config["emotion_labels"] # from config async def analyze(self, text: str) -> dict: inputs = self.tokenizer(text, return_tensors="pt", @@ -158,13 +185,18 @@ python convert_to_onnx.py \ --optimize ``` -#### 캐싱 구현 +#### 캐싱 구현 (설정 기반) ```python from functools import lru_cache import hashlib class CachedEmotionAnalyzer(EmotionAnalyzer): - @lru_cache(maxsize=1000) + def __init__(self, config: Dict): + super().__init__(config) + self.cache_size = config.get("cache_size", 1000) + self.cache_ttl = config.get("cache_ttl_seconds", 300) + + @lru_cache(maxsize=None) # maxsize는 동적으로 관리 def _analyze_cached(self, text_hash: str): # 실제 분석 로직 pass @@ -229,9 +261,17 @@ locust -f tests/load_test.py --users 10 --spawn-rate 2 | 낮은 정확도 (56.3%) | 낮 | confidence 임계값 설정 | | Cold start | 낮 | 서비스 시작 시 프리로드 | -### 6.2 폴백 전략 -- confidence < 0.3일 때 neutral로 분류 -- 엔트로피 > 2.8일 때 "복잡한 감정" 표시 +### 6.2 폴백 전략 (설정 기반) +```yaml +# config.yaml +fallback: + confidence_threshold: 0.3 # DB나 실험으로 결정된 값 + entropy_threshold: 2.8 # 데이터 분석으로 도출된 값 + default_emotion: "neutral" # 기본값 + skip_on_error: true # 오류 시 동작 +``` +- confidence < threshold일 때 neutral로 분류 +- 엔트로피 > threshold일 때 "복잡한 감정" 표시 - 오류 시 감정 분석 스킵하고 진행 ## 7. 작업 분담