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 deanomaly_description + summary(texto a clasificar).
Devuelve: String ("connectivity", "security", "performance", "other").
Lógica:
- Convierte
texta lowercase. - Si algún keyword en
_CONNECTIVITY_KEYWORDS→ devuelve"connectivity". - Si algún keyword en
_SECURITY_KEYWORDS→ devuelve"security". - Si algún keyword en
_PERFORMANCE_KEYWORDS→ devuelve"performance". - 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 deMonitoringTarget.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:
- Rate limit check:
_rate_limited(target.organization, target)→ raiseRateLimitExceededsi alcanzado. - Prompt constructor: Arma prompt para el Tutor con contexto (SNMP, logs, MIBs, comandos).
- Tutor call:
await call_tutor(prompt, provider=...)→ struct con diagnosis, recommendation, commands. - Create insight: Instancia
AIInsightcon:anomaly_trigger = anomaly_descriptiondiagnosis_text = result.diagnosis.textsummary = result.diagnosis.summaryconfidence_score = result.diagnosis.confidencecategory = classify_insight_category(f"{anomaly_description} {result.diagnosis.summary}")← Etapa 3status = AIInsight.Status.PENDINGraw_snmp_data, raw_ssh_logs(para auditabilidad)
- Audit log:
AIInsightAuditLog.objects.create(action="created", ...). - 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 deMonitoringTarget.
Devuelve: Número de insights resueltos.
Flujo (Etapa 3):
- 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).
- Antes (B18): Iteraba todos los PENDING y hacía regex-match en
- Para cada insight:
- Marcar como
status = ACKNOWLEDGED. acknowledged_at = timezone.now().acknowledged_notes = "Auto-resolved: device connectivity recovered".
- Marcar como
- Audit log:
AIInsightAuditLog.objects.create(action="auto_resolved", reason="connectivity_recovered"). - 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
| Servicio | Relación |
|---|---|
| notification_service | create_insight() notifica canales al crear |
| http_service | Anomalía HTTP (probe down) → trigger para create_insight |
| models_insight.AIInsight | Persistence layer |
| models_insight.AIInsightAuditLog | Audit 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]]
Referenciado desde
- Auditoria Suprema · Monitoring SA2 (Protocolos/Probes) + SA3 (CNS/IA)
- Cierre de deudas Etapa 3 SA3 — DNS-rebinding + clasificación de insights
- Cola de auditoría B (task #286): CNS e insights de IA — WS sin gate de permiso, apply/rollback sin Agente, prompts sin sanear
- Modelo AIInsight — insights de inteligencia artificial con clasificación estructurada