guardia-itsm/core/ticket_classifier.py
DESKTOP-TKLFCPRython 64c27c3509 feat(itsm): G-1~G-12 확장 기능 + 하네스/봇/설치스크립트 구현
G-1: 메신저 Webhook Relay + _send_to_room 실제 httpx 호출 구현
G-2: POST /api/tasks/bulk SR 대량작업 엔드포인트 (최대 100건)
G-3: 라이선스 만료 알림 스케줄러 (매일 09:00 KST)
G-4: 체험판 upgrade_banner 필드 + license.py 배너 로직
G-5: core/auto_rca.py + incidents/problem auto-rca 엔드포인트
G-6: core/deploy_impact.py + vibe impact-analysis 엔드포인트
G-7: core/ticket_classifier.py + SR 생성 시 AI 분류 + ai-suggestion API
G-8: VulnPatchRecord 모델 + vuln_scan 패치추적 4개 엔드포인트
G-9: core/jira_sync.py + gateway Jira/Confluence 연동 엔드포인트
G-10: core/push_notify.py + routers/push.py + PushSubscription 모델
G-11: approvals 다중승인 (위임/서명/기한초과/마감연장)
G-12: alembic.ini + migrations/ + cicd/migrate_to_postgres.sh

하네스: guardia-orchestrator 확장기능 Phase 반영
봇명령어: /sr /status /license /bulk 슬래시 명령어 추가
설치스크립트: setup/ (Ubuntu, CentOS, RHEL, Windows) --test 옵션 포함

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-29 18:18:52 +09:00

91 lines
3.3 KiB
Python

"""AI 자동 티켓 분류 — SR 타이틀/설명에서 우선순위, 카테고리, 담당팀 제안."""
from __future__ import annotations
import json
import logging
logger = logging.getLogger(__name__)
CLASSIFY_PROMPT = """다음 IT 서비스 요청을 분석하여 JSON으로 분류하세요.
제목: {title}
설명: {description}
다음 필드를 포함하는 JSON만 출력하세요:
{{
"priority": "CRITICAL|HIGH|MEDIUM|LOW",
"category": "HARDWARE|SOFTWARE|NETWORK|DATABASE|SECURITY|DEPLOYMENT|OTHER",
"team": "INFRA|DEV|DBA|SECURITY|HELPDESK",
"urgency_reason": "우선순위 판단 이유",
"keywords": ["핵심키워드1", "핵심키워드2", "핵심키워드3"],
"confidence": 0.85
}}"""
async def classify_ticket(title: str, description: str) -> dict:
"""SR 제목과 설명으로 AI 분류 제안 생성."""
prompt = CLASSIFY_PROMPT.format(
title=title,
description=(description or "")[:500],
)
try:
from core.llm_client import get_llm_client
client = get_llm_client()
resp = await client.chat(prompt)
raw = resp.content.strip()
if "```" in raw:
raw = raw.split("```")[1]
if raw.startswith("json"):
raw = raw[4:]
result = json.loads(raw)
# 필수 필드 보정
result.setdefault("priority", "MEDIUM")
result.setdefault("category", "OTHER")
result.setdefault("team", "HELPDESK")
result.setdefault("urgency_reason", "")
result.setdefault("keywords", [])
result.setdefault("confidence", 0.5)
return result
except Exception as e:
logger.warning("AI 티켓 분류 실패 (Fail-Safe): %s", e)
# 키워드 기반 규칙 폴백
return _rule_based_classify(title, description)
def _rule_based_classify(title: str, description: str) -> dict:
"""LLM 실패 시 키워드 규칙 기반 분류."""
text = f"{title} {description}".lower()
priority = "MEDIUM"
category = "OTHER"
team = "HELPDESK"
if any(k in text for k in ["긴급", "critical", "서비스 중단", "장애", "접속 불가"]):
priority = "CRITICAL"
elif any(k in text for k in ["오류", "error", "실패", "배포", "deploy"]):
priority = "HIGH"
elif any(k in text for k in ["설치", "변경", "업그레이드"]):
priority = "MEDIUM"
if any(k in text for k in ["서버", "cpu", "메모리", "디스크", "하드웨어"]):
category, team = "HARDWARE", "INFRA"
elif any(k in text for k in ["db", "데이터베이스", "oracle", "mysql", "postgresql"]):
category, team = "DATABASE", "DBA"
elif any(k in text for k in ["네트워크", "방화벽", "포트", "ip", "dns"]):
category, team = "NETWORK", "INFRA"
elif any(k in text for k in ["보안", "취약점", "cve", "패치"]):
category, team = "SECURITY", "SECURITY"
elif any(k in text for k in ["배포", "deploy", "jenkins", "빌드"]):
category, team = "DEPLOYMENT", "DEV"
elif any(k in text for k in ["소프트웨어", "애플리케이션", "app", "버그"]):
category, team = "SOFTWARE", "DEV"
return {
"priority": priority,
"category": category,
"team": team,
"urgency_reason": "키워드 규칙 기반 분류 (LLM 미사용)",
"keywords": [],
"confidence": 0.3,
}