zioinfo-mail/itsm/core/auto_rca.py
DESKTOP-TKLFCPR\ython e228faabf5 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

147 lines
4.7 KiB
Python

"""자동 RCA 분석 — Ollama LLM을 활용한 근본 원인 초안 생성."""
from __future__ import annotations
import json
import logging
from datetime import datetime
from typing import Optional
logger = logging.getLogger(__name__)
RCA_PROMPT = """당신은 IT 인프라 전문가입니다. 다음 장애 정보를 분석하여 RCA(근본 원인 분석)를 JSON으로 제공하세요.
장애 정보:
{incident_info}
변경 이력:
{change_history}
다음 필드를 포함하는 JSON만 출력하세요 (한국어):
{{
"root_cause": "추정 근본 원인",
"contributing_factors": ["기여 요인 1", "기여 요인 2"],
"timeline": "발생부터 발견까지 타임라인",
"prevention": ["재발 방지 조치 1", "재발 방지 조치 2"],
"confidence": 0.8
}}"""
async def analyze_rca(incident_id: int, db) -> dict:
"""장애 ID로 RCA 초안 자동 생성."""
from models import Incident
from sqlalchemy import select, text
incident = await db.get(Incident, incident_id)
if not incident:
raise ValueError(f"장애 ID {incident_id}를 찾을 수 없습니다.")
incident_info = (
f"장애번호: {incident.incident_id}\n"
f"제목: {incident.title}\n"
f"등급: {incident.grade}\n"
f"설명: {incident.description or '없음'}\n"
f"영향 서비스: {incident.affected_service or '없음'}\n"
f"발생시각: {incident.occurred_at}\n"
f"복구시각: {incident.resolved_at or '미복구'}"
)
# 최근 변경 이력 조회 (CI 변경 로그)
try:
from models import CIChangeLog
logs = (await db.execute(
select(CIChangeLog)
.order_by(CIChangeLog.changed_at.desc())
.limit(5)
)).scalars().all()
change_history = "\n".join(
f"- [{l.changed_at}] {l.change_type}: {l.summary or ''}"
for l in logs
) or "최근 변경 이력 없음"
except Exception:
change_history = "변경 이력 조회 불가"
prompt = RCA_PROMPT.format(
incident_info=incident_info,
change_history=change_history,
)
try:
from core.llm_client import get_llm_client
client = get_llm_client()
resp = await client.chat(prompt)
raw = resp.content.strip()
# JSON 블록 추출
if "```" in raw:
raw = raw.split("```")[1]
if raw.startswith("json"):
raw = raw[4:]
rca = json.loads(raw)
except Exception as e:
logger.warning("LLM RCA 분석 실패 — 기본 템플릿 사용: %s", e)
rca = {
"root_cause": f"자동 분석 실패 — 수동 분석 필요. ({str(e)[:100]})",
"contributing_factors": ["분석 데이터 부족"],
"timeline": incident_info,
"prevention": ["장애 재발 방지 조치를 수동으로 기록하세요."],
"confidence": 0.0,
}
return {
"incident_id": incident.incident_id,
"rca": rca,
"auto_generated": True,
"generated_at": datetime.utcnow().isoformat(),
}
async def analyze_problem_rca(problem_id: int, db) -> dict:
"""Problem 레코드 ID로 RCA 초안 자동 생성."""
from models import ProblemRecord
from sqlalchemy import select
prb = await db.get(ProblemRecord, problem_id)
if not prb:
raise ValueError(f"Problem ID {problem_id}를 찾을 수 없습니다.")
incident_info = (
f"문제번호: {prb.problem_id}\n"
f"제목: {prb.title}\n"
f"설명: {prb.description or '없음'}\n"
f"카테고리: {prb.category}\n"
f"영향 사용자: {prb.affected_users}\n"
f"관련 인시던트 수: {prb.incident_count}\n"
f"누적 다운타임: {prb.total_downtime_min}"
)
prompt = RCA_PROMPT.format(
incident_info=incident_info,
change_history="문제 레코드 기반 분석",
)
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:]
rca = json.loads(raw)
except Exception as e:
logger.warning("LLM Problem RCA 분석 실패: %s", e)
rca = {
"root_cause": "자동 분석 실패 — 수동 분석 필요.",
"contributing_factors": [],
"timeline": incident_info,
"prevention": [],
"confidence": 0.0,
}
return {
"problem_id": prb.problem_id,
"rca": rca,
"auto_generated": True,
"generated_at": datetime.utcnow().isoformat(),
}