4b86b2a660
- server-dev start/stop/deploy 및 Gitea push 자동 배포 - local-dev 로컬 개발 환경 Co-authored-by: Cursor <cursoragent@cursor.com>
116 lines
3.8 KiB
Python
116 lines
3.8 KiB
Python
"""
|
|
의도 감지 모듈
|
|
──────────────
|
|
사용자 질문의 의도를 분류 (인사, 종료, 불만, 일반 등)
|
|
"""
|
|
|
|
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"
|