Problema

En entornos de producción, la respuesta a una alerta suele iniciar con una fase de “recolección”. Los ingenieros despiertan, consultan dashboards, descargan logs, revisan despliegues recientes y, solo después de varios minutos, empiezan a razonar sobre la causa raíz. Ese tiempo de “busy‑work” es costoso y, a menudo, se repite en cada incidente.

Las soluciones SaaS que usan LLMs para correlacionar métricas y logs pueden reducir esa fricción, pero requieren enviar datos sensibles a la nube, algo inaceptable en muchas organizaciones por políticas de compliance o por miedo a fugas de información.

El desafío es crear una capa de diagnóstico que:

  1. Sea auto‑hosted: todo el procesamiento y la ingestión de datos permanecen dentro del perímetro de la empresa.
  2. Sea de solo lectura: el motor nunca modifica infraestructura, solo sugiere acciones.
  3. Proporcione evidencias: cada afirmación del modelo debe estar respaldada por logs, métricas o eventos concretos.
  4. Sea extensible: soportar diferentes fuentes de alertas, telemetría y canales de notificación sin acoplarse a una pila específica.

Causa

Los incidentes que consumen tiempo de diagnóstico comparten varios factores recurrentes:

  • Fragmentación de datos: logs en archivos, métricas en Prometheus, estados de pods en Kubernetes, despliegues en GitHub. Cada fuente necesita una extracción manual.
  • Falta de correlación automática: sin un motor que cruce eventos (por ejemplo, un despliegue reciente y un aumento de latencia), el ingeniero debe hacerlo mentalmente.
  • Políticas de seguridad restrictivas: la imposibilidad de enviar datos a la nube elimina la opción de usar servicios SaaS, dejando a los equipos sin herramientas de IA.
  • Modelos de LLM sin control de salida: los LLMs “generativos” pueden inventar información (hallucinations). Cuando el diagnóstico incluye datos falsos, el riesgo supera el beneficio.

Solución

Una arquitectura modular basada en plugins permite montar un pipeline de diagnóstico que cumple con los requisitos anteriores. Los componentes clave son:

  1. Agente de recolección

    • Se ejecuta como contenedor ligero (Docker) o como DaemonSet en Kubernetes.
    • Consume la alerta (webhook, Sentry, PagerDuty, etc.) y dispara una serie de plugins de “fuente”. Cada plugin sabe cómo extraer datos de una API concreta (K8s, Docker, GitHub, CloudWatch, etc.).
    • Los datos se normalizan a un formato JSON común: timestamp, source, type, payload.
  2. Motor de inferencia

    • Recibe el JSON consolidado y lo envía a un LLM.
    • Se envía solo el prompt y los datos relevantes; el modelo nunca accede a la infraestructura directamente.
    • El prompt incluye una instrucción estricta: “Responde solo con afirmaciones respaldadas por los datos provistos. Si no hay evidencia, indica ‘sin evidencia’.”
    • La respuesta se parsea y se valida contra un esquema que exige campos evidence y confidence.
  3. Validador de evidencia

    • Recorre cada afirmación y verifica que la referencia (evidence.id) exista en el JSON original.
    • Si falta, la afirmación se marca como “guess” y se envía a un canal de revisión humana.
  4. Publicador

    • Envía el diagnóstico a la herramienta de observabilidad elegida (Grafana dashboard, Slack, Teams).
    • Incluye enlaces a logs, métricas y commits relevantes, de modo que el operador pueda profundizar con un clic.
  5. Soporte para modelos locales

    • Cuando la política lo exige, el motor de inferencia se conecta a un servidor de modelos locales (Ollama, LM Studio).
    • La integración es opcional; si no hay modelo disponible, se recurre a una API externa (OpenAI, Anthropic).

Buenas prácticas para evitar hallucinations

  • Prompt estructurado: usar delimitadores claros (---DATA--- y ---END---) y solicitar una salida JSON.
  • Temperatura 0: garantiza respuestas determinísticas.
  • Post‑procesamiento: filtrar cualquier cadena que no sea JSON válido antes de pasar al validador.

Implementación mínima

  1. Define la interfaz del plugin (Python pseudo‑código). Cada plugin expone collect(alert) -> List[dict].
  2. Crea un contenedor Docker con el agente y los plugins instalados.
  3. Despliega como DaemonSet para que haya un agente en cada nodo y pueda leer logs locales sin montar volúmenes remotos.
  4. Configura el LLM: variable de entorno LLM_ENDPOINT (puede ser http://localhost:11434/v1/chat/completions para Ollama).
  5. Añade un webhook de salida a Slack o a un panel de Grafana.

Cuándo aplicar esta solución

  • Síntomas típicos: alertas que aparecen sin contexto útil, tiempo de diagnóstico > 5 min, equipos que no pueden usar SaaS por compliance.
  • Entornos con: Kubernetes, Docker, CI/CD en GitHub/GitLab, métricas en Prometheus o CloudWatch.
  • No aplica: infraestructuras extremadamente pequeñas donde el coste de mantener un agente supera el beneficio, o cuando la única fuente de datos es un único log estático que ya se procesa con scripts tradicionales.

Código

docker run -d \
  --name incident‑agent \
  --restart unless-stopped \
  -v /var/run/docker.sock:/var/run/docker.sock \
  -v /var/log:/var/log:ro \
  -e LLM_ENDPOINT=http://host.docker.internal:11434/v1/chat/completions \
  -e ALERT_WEBHOOK_URL=https://hooks.slack.com/services/XXX/YYY/ZZZ \
  ghcr.io/yourorg/incident‑agent:latest

Verificación

  1. Generar una alerta de prueba (por ejemplo, un webhook de Sentry que indique error_rate > 5%).
  2. Observar el log del contenedor: debe aparecer una línea Collected 3 sources, sending 1.2 KB to LLM.
  3. Revisar el canal de Slack: debería llegar un mensaje con título “Diagnóstico automático”, evidencias listadas y enlaces a los logs.
  4. Validar la estructura JSON del mensaje usando jq. Si alguna afirmación carece de evidence.id, el agente debe marcarla como guess y enviarla a un canal de revisión.

Notas adicionales

  • Seguridad de los plugins: ejecuta el agente con un usuario sin privilegios de escritura en el host. Usa seccomp y AppArmor para limitar accesos.
  • Rotación de credenciales: almacena tokens de APIs (GitHub, Slack) en un secret manager (Vault, Kubernetes Secrets) y recárgalos sin reiniciar el contenedor.
  • Escalabilidad: si el número de alertas supera la capacidad del LLM, introduce una cola (RabbitMQ o NATS) y procesa en lotes.
  • Monitoreo interno: expón métricas de Prometheus (agent_requests_total, agent_errors_total) para detectar cuellos de botella.
  • Pruebas de regresión: guarda ejemplos de alertas y sus diagnósticos esperados en un directorio fixtures/. Ejecuta una CI que compare la salida actual con la esperada para evitar que cambios en el prompt rompan la lógica.