""" 의도 감지 모듈 ────────────── 사용자 질문의 의도를 분류 (인사, 종료, 불만, 일반 등) """ from typing import Dict, List, Optional from dataclasses import dataclass @dataclass class Intent: """의도 분류 결과""" name: str # "greeting", "farewell", "general" confidence: float # 0.0 ~ 1.0 matched_keywords: List[str] # 매칭된 키워드 def is_special(self) -> bool: """특별 처리가 필요한 의도인지 (인사, 종료만)""" return self.name in ["greeting", "farewell"] class IntentDetector: """사용자 의도 감지기""" # 의도별 키워드 패턴 INTENT_PATTERNS = { "greeting": { "keywords": [ "안녕", "안녕하세요", "안녕하십니까", "처음", "반가", "반갑습니다", "hi", "hello", "hey", "처음 뵙겠습니다", "처음입니다" ], "priority": 1 # 우선순위 (낮을수록 높음) }, "farewell": { "keywords": [ "감사", "고마워", "고맙습니다", "감사합니다", "잘됐", "해결", "알겠", "알았", "끝", "종료", "그만", "bye", "goodbye", "끝내", "닫기", "도움 됐", "도움됐", "충분" ], "priority": 2 } # complaint 제거: 감정 분석(EmotionDetector)으로만 처리 } def detect(self, query: str, ts: Optional[str] = None) -> Intent: """의도 감지 Args: query: 사용자 질문 ts: 타임스탬프 (로깅용) Returns: Intent 객체 """ query_lower = query.lower().strip() # 빈 문자열 체크 if not query_lower: return Intent(name="general", confidence=1.0, matched_keywords=[]) # 각 의도별 매칭 점수 계산 intent_scores = {} for intent_name, config in self.INTENT_PATTERNS.items(): matched = [] for keyword in config["keywords"]: if keyword.lower() in query_lower: matched.append(keyword) if matched: # 매칭된 키워드 개수와 우선순위를 고려한 점수 score = len(matched) * (1.0 / config["priority"]) intent_scores[intent_name] = { "score": score, "matched": matched, "priority": config["priority"] } # 매칭된 의도가 없으면 일반 질문 if not intent_scores: return Intent(name="general", confidence=1.0, matched_keywords=[]) # 가장 높은 점수의 의도 선택 best_intent = max(intent_scores.items(), key=lambda x: x[1]["score"]) intent_name = best_intent[0] intent_data = best_intent[1] # 신뢰도 계산 (0.7 ~ 0.95) confidence = min(0.95, 0.7 + (intent_data["score"] * 0.1)) result = Intent( name=intent_name, confidence=confidence, matched_keywords=intent_data["matched"] ) if ts: print(f"[IntentDetector] {ts} 의도 감지: {result.name} (신뢰도: {result.confidence:.2f}, 키워드: {result.matched_keywords})") return result def is_greeting(self, query: str) -> bool: """인사 여부 판단 (간단한 헬퍼)""" intent = self.detect(query) return intent.name == "greeting" def is_farewell(self, query: str) -> bool: """종료 인사 여부 판단""" intent = self.detect(query) return intent.name == "farewell"