CreaRack-SL

Servicio insight_service — clasificación y generación de insights de IA

Servicio insight_service

Archivo: monitoring/services/insight_service.py | Versión: Etapa 3 (cb4fe5e+) | Status: Activo

Propósito

Servicio principal de inteligencia artificial del CNS (Correlation aNalysis System). Transforma anomalías detectadas (SNMP, SSH, syslog) en insights estructurados vía el Tutor (LLM). Incluye clasificación automática de categorías (CONNECTIVITY, SECURITY, PERFORMANCE, OTHER) y resolución autónoma de insights de conectividad.

Constantes de clasificación

Pools de keywords (Etapa 3 B18)

# CONNECTIVITY: anomalías de disponibilidad/alcance
_CONNECTIVITY_KEYWORDS = [
    "total loss", "unreachable", "100%", "device down", 
    "link_down", "link down"
]

# SECURITY: amenazas y ataques
_SECURITY_KEYWORDS = [
    "rogue", "deauth", "evil twin", "arp spoof", "mac flood",
    "dhcp starvation", "vlan hopping", "attack", "intrusion", "spoofing"
]

# PERFORMANCE: degradación de rendimiento
_PERFORMANCE_KEYWORDS = [
    "latency", "packet loss", "saturation", "bandwidth",
    "congestion", "jitter", "crc", "utilization", "duplex"
]

Nota: Idénticos a los hardcodeados en la migration 0020 para determinismo histórico.

Rate limits

RATE_LIMIT_PER_DEVICE = 5      # máximo 5 insights por hora y dispositivo
RATE_LIMIT_PER_TENANT = 100    # máximo 100 insights por hora y org

API Pública

classify_insight_category(text: str) -> str

Clasifica un texto en una categoría de insight usando keyword matching.

Parámetros:

  • text: Concatenación típicamente de anomaly_description + summary (texto a clasificar).

Devuelve: String ("connectivity", "security", "performance", "other").

Lógica:

  1. Convierte text a lowercase.
  2. Si algún keyword en _CONNECTIVITY_KEYWORDS → devuelve "connectivity".
  3. Si algún keyword en _SECURITY_KEYWORDS → devuelve "security".
  4. Si algún keyword en _PERFORMANCE_KEYWORDS → devuelve "performance".
  5. Si ninguno → devuelve "other".

Uso:

category = classify_insight_category(f"{anomaly_description} {result.diagnosis.summary}")
# p.ej. "Interface eth0 down - 100% packet loss" → "connectivity"

# Almacenar en AIInsight
insight = AIInsight.objects.create(
    ...,
    category=category,  # ← Etapa 3
)

async def create_insight(...) -> AIInsight

Genera un insight nuevo usando el Tutor (LLM).

Parámetros (principales):

  • target: Instancia de MonitoringTarget.
  • anomaly_description: Descripción breve del evento (p.ej. “Interface eth0 down”).
  • snmp_data: Dict de valores SNMP brutos ({“sysDescr”: ”…”, “ifName”: ”…”}).
  • ssh_logs: Logs de SSH/syslog brutos (string o list).
  • provider: Proveedor LLM (“anthropic”, “google”, “openai”). Default: “anthropic”.

Flujo:

  1. Rate limit check: _rate_limited(target.organization, target) → raise RateLimitExceeded si alcanzado.
  2. Prompt constructor: Arma prompt para el Tutor con contexto (SNMP, logs, MIBs, comandos).
  3. Tutor call: await call_tutor(prompt, provider=...) → struct con diagnosis, recommendation, commands.
  4. Create insight: Instancia AIInsight con:
    • anomaly_trigger = anomaly_description
    • diagnosis_text = result.diagnosis.text
    • summary = result.diagnosis.summary
    • confidence_score = result.diagnosis.confidence
    • category = classify_insight_category(f"{anomaly_description} {result.diagnosis.summary}") ← Etapa 3
    • status = AIInsight.Status.PENDING
    • raw_snmp_data, raw_ssh_logs (para auditabilidad)
  5. Audit log: AIInsightAuditLog.objects.create(action="created", ...).
  6. Return: Instancia guardada de AIInsight.

Errores:

  • RateLimitExceeded: Límites alcanzados.
  • TutorCallError: Fallo en LLM.
  • ValidationError: Datos de entrada inválidos.

async def auto_resolve_connectivity_insights(target_id: int) -> int

Resuelve automáticamente insights de conectividad cuando un dispositivo se recupera (Agent reporta target UP).

Parámetro:

  • target_id: ID de MonitoringTarget.

Devuelve: Número de insights resueltos.

Flujo (Etapa 3):

  1. Query: AIInsight.objects.filter(target_id=target_id, status=PENDING, category=CONNECTIVITY).
    • Antes (B18): Iteraba todos los PENDING y hacía regex-match en summary + anomaly_trigger.
    • Después (Etapa 3): Filtra directamente en DB por category=CONNECTIVITY (más eficiente, indexed).
  2. Para cada insight:
    • Marcar como status = ACKNOWLEDGED.
    • acknowledged_at = timezone.now().
    • acknowledged_notes = "Auto-resolved: device connectivity recovered".
  3. Audit log: AIInsightAuditLog.objects.create(action="auto_resolved", reason="connectivity_recovered").
  4. Return: Contador de insights resueltos.

Guardias:

  • Solo CONNECTIVITY se auto-resuelve (otros requieren revisión manual o lógica específica).
  • SECURITY/PERFORMANCE insights quedan PENDING hasta ack manual.

async def fetch_historical_insights(target_id: int, hours: int = 24) -> list[AIInsight]

Obtiene insights históricos para reporting.

Parámetros:

  • target_id: ID de target.
  • hours: Rango temporal (default 24 horas).

Devuelve: Lista de AIInsight ordenadas por timestamp descendente.

Rate limiting

_rate_limited(organization, target) -> bool

def _rate_limited(organization, target) -> bool:
    one_hour_ago = timezone.now() - timedelta(hours=1)
    
    # Check tenant limit
    tenant_count = AIInsight.objects.filter(
        organization=organization,
        created_at__gte=one_hour_ago
    ).count()
    if tenant_count >= RATE_LIMIT_PER_TENANT:
        return True
    
    # Check per-device limit
    device_count = AIInsight.objects.filter(
        target=target,
        created_at__gte=one_hour_ago
    ).count()
    if device_count >= RATE_LIMIT_PER_DEVICE:
        return True
    
    return False

Previene DOS por generación masiva de insights (p.ej. un agente defectuoso reportando 10k anomalías/hora).

Flujo de un típico evento

Agent (probe.py) detecta interface down
  ↓
Emite: MonitoringAlert(target=router-01, type="interface_down", severity="high", ...)
  ↓
CNS triggers: await create_insight(
    target=router-01,
    anomaly_description="Interface eth0 down",
    snmp_data={...},
    ssh_logs="..."
)
  ↓
[Rate limit check] → OK, procede
  ↓
[Tutor call] → LLM diagnostica: "Link down, probable cable disconnect"
  ↓
[Classify] → classify_insight_category("Interface eth0 down Link down...") → "connectivity"
  ↓
[Create AIInsight]
  insight = AIInsight(
    target=router-01,
    anomaly_trigger="Interface eth0 down",
    summary="Link down, probable cable disconnect",
    category="connectivity",  ← Etapa 3
    status=PENDING,
    ...
  )
  ↓
[Notify] → send_notification(channels, insight, "created")
  ↓
[Operator reviews] → ACK manually o espera a que device recupere
  ↓
[Device recovers] → Agent reporta target UP
  ↓
[Auto-resolve] → await auto_resolve_connectivity_insights(target_id)
  ↓
[Query] → SELECT * FROM monitoring_aiinsight WHERE target_id=1 AND status=PENDING AND category='connectivity'
  ↓
[Mark ACKNOWLEDGED] → insight.status = ACKNOWLEDGED, save
  ↓
[Notify] → send_notification(..., "auto_resolved")

Integración con otros servicios

ServicioRelación
notification_servicecreate_insight() notifica canales al crear
http_serviceAnomalía HTTP (probe down) → trigger para create_insight
models_insight.AIInsightPersistence layer
models_insight.AIInsightAuditLogAudit trail

Véase también

  • [[feature—monitoring—cierre-deudas-sa3]]
  • [[entity—monitoring—model—ai-insight]]
  • [[entity—monitoring—service—notification-service]]
  • [[entity—monitoring—service—http-service]]
  • [[entity—monitoring—model—monitoring-target]]