Inicio / Artículos

AegisOps: Un agente SRE autónomo con cuatro capas de memoria

Cómo un agente LangGraph ReAct resuelve incidentes en Kubernetes, con memoria episódica pgvector, niveles de autonomía controlados por políticas, dieciséis herramientas y un registro de auditoría completo.

Editor: REACTAPP.TOP12 min de lectura2723 palabras

Fuentes: I Built an Autonomous SRE Agent That Resolves Production Incidents While I Sleep

Son las 3:14 de la madrugada y PagerDuty está muy activo. Prometheus ha emitido una alerta crítica: la tasa de errores del servicio de pagos ha subido al 34%, los pods se están cerrando debido a errores de memoria insuficiente y la ventana de incumplimiento del SLA se abre en seis minutos. Las opciones son levantarse de la cama e ingresar vía SSH a un clúster, o dejar que un agente se encargue del asunto.

AegisOps es un agente SRE autónomo creado precisamente para momentos como este. Diagnostica, actúa y resuelve incidentes en producción sin la intervención humana, además de registrar cada decisión que toma y el motivo de ella. No se trata de un chatbot basado en kubectl. Cuenta con memoria real, controles de autonomía en capas y la capacidad de aprender de los incidentes que ya ha gestionado. El objetivo declarado nunca fue reemplazar al ingeniero de guardia, sino proporcionarle un respondedor incansable que ya conozca los procedimientos, recuerde los últimos diez incidentes y verifique las políticas antes de intervenir en producción.

Por qué ReAct en LangGraph

ReAct (Razonamiento más Acción) se adapta perfectamente a la respuesta a incidentes. Una cadena simple ejecuta los pasos de forma ciega, mientras que un agente ReAct genera un pensamiento, elige una acción, observa lo que ocurre y vuelve a repetir el proceso. Esa es la actitud adecuada ante una interrupción, ya que las preguntas útiles son qué se sabe, qué se está observando y qué probar a continuación.

LangGraph ofrece una ejecución de gráfico con estado. Cada nodo tiene acceso al completo AegisState, un TypedDict que almacena mensajes, memoria, detalles del incidente, decisiones de política, llamadas a herramientas y resultados de verificación. Dado que el estado se guarda en PostgreSQL después de cada paso, es posible pausar, examinar y reanudar una ejecución.

El gráfico consta de cuatro nodos, diseñados deliberadamente de forma simple para evitar una sobreingeniería:

    • memory_node se ejecuta primero y carga todo lo que el agente necesitará antes de que comience cualquier razonamiento.
  • agent_node corresponde al bucle de razonamiento del LLM.
  • tools es el ToolNode estándar de LangGraph, el cual lleva a cabo lo que el modelo solicite.
  • resolution_node cierra el incidente: guarda la memoria, calcula el MTTR y envía el mensaje a Slack.

Cuatro capas de memoria

La mayoría de los sistemas de agentes omiten la arquitectura de memoria o se conforman con dos capas. Un SRE humano aporta cuatro tipos de memoria a un incidente, y AegisOps reproduce todas ellas.

  1. Memoria episódica (pgvector, similitud coseno) almacena los incidentes resueltos en el pasado como embeddings. Cuando llega uno nuevo, se recuperan los tres casos anteriores más similares e se insertan en el prompt del sistema junto con su causa raíz, las acciones tomadas y el resultado.
  2. Memoria semántica (RAG sobre una base de conocimientos en pgvector) guarda manuales de procedimientos divididos en fragmentos, documentos de arquitectura e informes de análisis postincidente. La herramienta retrieve_runbook los busca de forma semántica, proporcionando al agente el conocimiento institucional del equipo.
  • Memoria procedural (políticas YAML, un motor de reglas y Redis) abarca las reglas estrictas sobre lo que el agente puede hacer en cada nivel de autonomía. Estas se cargan desde params.yml a Redis al inicio del grafo y se evalúan antes de cualquier acción que modifique algo. Considérela el reflejo entrenado del agente para operaciones seguras.
  • Memoria de trabajo (el estado de AegisOps y el punto de control de LangGraph) cubre el estado en curso: mensajes, pensamientos actuales, llamadas a herramientas, resultados de las verificaciones de estado y conteos de intentos. El hecho de hacer puntos de control en cada nodo garantiza que el contexto se mantenga tras los reinicios.
  • Memoria episódica en la práctica

    Al ingresar, memory_node llama a retrieve_similar_incidents con la lista de síntomas del incidente actual. Un codificador de oraciones de 768 dimensiones incrusta esos síntomas, y la distancia de coseno de pgvector los compara con la tabla IncidentMemory. Los incidentes anteriores que superan el umbral de similitud de 0.72 se formatean y se colocan en el prompt del sistema, antes del primer pensamiento del agente.

    Después de una resolución, resolution_node llama a save_incident_memory, de modo que el agente grabe sus propios recuerdos. La incrustación proviene de los síntomas combinados con la hipótesis de causa raíz, por lo que los incidentes posteriores con síntomas similares encontrarán este uno.

    Una base de datos vectorial dedicada como Pinecone o Weaviate era una opción. Mantener todo en PostgreSQL eliminaba una dependencia de infraestructura y garantizaba consistencia transaccional entre los registros de incidentes y sus embeddings. A esta escala, pgvector es más que suficiente.

    Niveles de autonomía L0 a L3

    Esta fue la pregunta de diseño más difícil: ¿cómo se le puede dar a un agente un verdadero poder sobre el entorno de producción sin arriesgarse a una catástrofe? La respuesta es un modelo de autonomía en capas en el que cada acción de modificación pasa por una verificación de políticas antes de ejecutarse.

    Cada herramienta de acción (restart_deployment, scale_deployment, update_deployment_image) comienza con una llamada a retrieve_policy. El motor de políticas evalúa la acción, el servicio, la gravedad y el nivel actual de autonomía en relación con las reglas de params.yml. Si la respuesta es negativa, la herramienta devuelve un rechazo de inmediato y el agente debe buscar otra vía, generalmente la escalada.

    El campo requires_approval merece atención. Incluso en el nivel L3, algunas acciones destructivas, como revertir la imagen en un servicio de pagos crítico, siguen necesitando una aprobación humana explícita. El agente publica un mensaje en Slack y espera. Esto brinda confianza al equipo de guardia de que las operaciones más riesgosas mantienen a un humano involucrado.

    Las dieciséis herramientas

    AegisOps incluye dieciséis herramientas divididas en cuatro categorías. Seis destacan:

    1. get_logs es el recolector de pruebas. Toma las últimas 500 líneas del archivo de registro de un servicio, mantiene los niveles solicitados y elimina de cada línea las direcciones IP, UUID y números simples. Por lo tanto, dos rechazos provenientes de hosts diferentes se consideran un único patrón. Un Counter de Python clasifica los patrones y devuelve los más frecuentes. Tanto los lectores de terminal como el bucle de razonamiento del agente reciben el mismo resultado estructurado, y no se necesita ningún clúster de Elasticsearch, solo un archivo de registro y unas pocas líneas de Python.
    2. get_pod_status solicita a Kubernetes CoreV1Api la información de los pods con la etiqueta app={deployment} y reporta los reinicios, el estado de disponibilidad y el estado del contenedor. Cuando el clúster no es accesible, funciona de forma reducida y envía al agente a get_logs en su lugar.
    3. restart_deployment se encarga de la mayor parte del trabajo. Al aplicar parches a la anotación kubectl.kubernetes.io/restartedAt se inicia un reinicio progresivo; luego espera a que todas las réplicas estén listas y realiza una verificación de estado. Un paso opcional de desconexión evita que llegue tráfico durante el reinicio.
    4. update_deployment_image sirve como solución de emergencia para revertir cambios. Reescribe la imagen del contenedor en la especificación, espera a que finalice el despliegue y verifica posteriormente el estado de los pods. Al ser la herramienta más potente del conjunto, los incidentes críticos en producción requieren autonomía de nivel L3 para poder utilizarla.
    5. check_service_health combina la disponibilidad de los pods según Kubernetes con la tasa de errores en tiempo real de Prometheus. Solo devuelve True cuando todos los pods están listos y las respuestas de tipo 5xx permanecen por debajo del 5%.
    6. retrieve_runbook busca runbooks, análisis posteriores a incidentes y notas de arquitectura según su contenido y devuelve los cuatro fragmentos más relevantes con una similitud de 0.75 o superior, los cuales se incluyen en el prompt del sistema bajo el encabezado “RUNBOOK CONTEXT”.

    Un incidente, de principio a fin

    Esto es lo que hizo el agente respecto al problema OOMKill en el servicio de pagos a las 3:14 a.m.:

    [MEMORY] Loading episodic context for payment-service...
    [MEMORY] Found 2 similar past incidents (similarity: 0.89, 0.76)
    [MEMORY] Loaded 3 procedural policies for payment-service/critical
    
    [THOUGHT] Incident: payment-service OOMKill, 3 pods CrashLooping.
    Past incident INC-2801 had identical symptoms — root cause was
    a memory leak in the gRPC connection pool after a deploy.
    Starting with get_logs to confirm before acting.
    
    [ACTION] Calling tool: get_logs with {'service_name': 'payment-service', 'log_level': 'error'}
    → top_errors: [{"pattern": "out of memory: kill process", "count": 47},
    {"pattern": "grpc connection pool exhausted", "count": 23}]
    
    [THOUGHT] OOM confirmed. gRPC pool exhaustion matches INC-2801 pattern.
    Retrieve runbook before acting — want the fix procedure.
    
    [ACTION] Calling tool: retrieve_runbook with {'query': 'payment service grpc oom memory leak fix'}
    → [runbooks / payment-service-oom.md] Recommended action: rolling restart
    resets connection pool. If recurs within 1h, rollback to previous image.
    
    [THOUGHT] Runbook confirms rolling restart. Checking policy for autonomy L2...
    
    [ACTION] Calling tool: restart_deployment with {'service': 'payment-service',
    'deployment': 'payment-worker', 'namespace': 'production',
    'severity': 'critical', 'autonomy_level': 'L2'}
    → policy: ALLOWED (L2 ≥ min_level L2 for restart_deployment)
    → draining pods... restarting... polling rollout...
    → health_check: healthy=True, pods_ready=3/3, error_rate=0.008
    
    [THOUGHT] Restart succeeded. Error rate 0.8% — below 5% threshold.
    Verifying with check_service_health before closing.
    
    [ACTION] Calling tool: check_service_health with {'service': 'payment-service',
    'deployment': 'payment-worker', 'namespace': 'production'}
    → healthy=True, pods_total=3, pods_ready=3, error_rate=0.007
    
    [THOUGHT] Service fully recovered. Writing audit log and resolving.
    
    [ACTION] Calling tool: write_audit_log ...
    → audit_log written: restart_deployment for incident INC-2847
    
    RESOLVED: payment-service recovered after rolling restart. Root cause: gRPC
    connection pool OOM — matched pattern from INC-2801. All 3 pods healthy,
    error rate 0.7%. MTTR: 187 seconds.
    
    [MEMORY] Saving incident INC-2847 to episodic memory...
    [NOTIFY] Sending resolution to Slack channel: #all-aegisops ✅
    

    Todo el proceso duró 187 segundos, desde la alerta hasta la resolución en poco más de tres minutos, sin que nadie se despertara. Ese es el tiempo que ahora se logra habitualmente en incidentes similares a los anteriores. El agente comenzó por analizar qué funcionó la última vez, como haría un ingeniero senior, lo cual es el resultado del funcionamiento de la memoria episódica.

    Ese rastro se reconstruyó para mayor claridad. Una ejecución real en un entorno local sencillo, con un único clúster Kind y sin Prometheus, se ve diferente. El incidente fue desencadenado por una simple llamada de webhook de PowerShell:

    Invoke-RestMethod -Uri "http://localhost:8000/webhook/incident" `
      -Method Post `
      -ContentType "application/json" `
      -Body '{
        "source": "webhook",
        "severity": "critical",
        "service": "payment-service",
        "symptoms": ["High 500 errors", "DB connection timeouts"],
        "autonomy_level": "L2"
      }'
    

    El agente lo ejecutó de principio a fin. Recuperó incidentes similares y formuló la hipótesis de que un aumento repentino del tráfico estaba sobrecargando la capacidad de los pods, algo similar a un caso anterior en la memoria. Llamó a get_pod_status y vio que 2 de los 5 pods estaban listos; escalió la implementación a 4 réplicas, lo confirmó con check_service_health y cerró el incidente como RESOLVIDO. Un mensaje de Slack llegó al canal #all-aegisops y el incidente fue guardado nuevamente en la memoria episódica.

    Eso es menos problemático que un fallo por falta de memoria en gRPC a las 3 de la mañana, pero el ciclo permanece igual: el mismo ciclo de Pensamiento, Acción y Observación, la misma verificación de políticas antes de escalar y la misma escritura en la memoria al final. Solo el entorno es más sencillo. El agente no puede distinguir entre un clúster de laptops y una flota de 50 nodos. Cambia el kubeconfig, dirige Prometheus hacia métricas reales y el mismo código funcionará en el entorno real.

    Slack como canal de salida

    Las acciones importantes envían un mensaje a Slack. Cuando un incidente se cierra, el nodo de resolución escribe en #all-aegisops con el color que indica la gravedad, el MTTR y un resumen completo de la resolución, lo que significa que el ingeniero de turno se despierta al ver una marca de verificación verde y no una alerta por pager.

    La tecnología utilizada

    Todo funciona con una tecnología sencilla. PostgreSQL con pgvector gestiona la memoria episódica y los puntos de control de LangGraph. Redis almacena las políticas y está reservado para futuras limitaciones de tasa. Prometheus proporciona las tasas reales de errores a check_service_health, mientras que Grafana ofrece paneles de control.

    version: "3.9"
    
    services:
      postgres:
        image: ankane/pgvector:latest
        ports:
          - "5445:5432"
        environment:
          POSTGRES_USER: postgres
          POSTGRES_PASSWORD: admin123
          POSTGRES_DB: aegisops
        volumes:
          - postgres_data:/var/lib/postgresql/data
    
      redis:
        image: redis:alpine
        ports:
          - "6380:6379"
    
      app:
        build: .
        ports:
          - "8000:8000"
        env_file: .env
        depends_on:
          - postgres
          - redis
    
      prometheus:
        image: prom/prometheus:latest
        ports:
          - "9090:9090"
        volumes:
          - ./infra/prometheus.yml:/etc/prometheus/prometheus.yml
    
      grafana:
        image: grafana/grafana:latest
        ports:
          - "3000:3000"
    
    volumes:
      postgres_data:
    

    La aplicación es FastAPI ejecutada mediante uvicorn. Los incidentes llegan a un endpoint de webhook que crea un IncidentEvent y llama a run_agent(). La ejecución es asíncrona, por lo que varios incidentes pueden procesarse en paralelo sin bloqueos. El cliente de Kubernetes es kubernetes_asyncio, lo que mantiene las llamadas al clúster sin bloqueos.

    Un detalle importante: get_pod_status y check_service_health cargan la configuración de Kubernetes al importarse el módulo, no dentro de la función asíncrona. Si el clúster no es accesible al iniciar la aplicación, estas funciones fallan en silencio y devuelven mensajes de advertencia; por lo tanto, asegúrese de que KUBECONFIG o la cuenta de servicio dentro del clúster esté configurada antes de que arranque la aplicación.

    El registro de auditoría

    Cada llamada a una herramienta se registra en un ToolInvocation TypedDict: qué se ejecutó, con qué parámetros, qué se devolvió, si tuvo éxito, cuánto tiempo tomó, cuándo ocurrió y qué decisión de política lo permitió. Los registros se acumulan en state["tool_invocations"] durante la ejecución y se guardan en la memoria episódica al finalizar. Una herramienta separada llamada write_audit_log emite una línea JSON estructurada para cada acción relevante.

    El resultado es que cualquier incidente puede reproducirse posteriormente: la secuencia de pasos, los permisos en vigor y la razón detrás de cada uno. Los equipos de cumplimiento solicitan precisamente este tipo de registros.

    Una elección de diseño determina el resto. Cada acción de mutación informa sobre la decisión de política junto con el resultado de la verificación de estado, de modo que el agente aprende tres cosas al mismo tiempo: si la llamada funcionó, si fue permitida y si el servicio está operativo en ese momento. Estos tres factores influyen en su siguiente decisión.

    Qué falta aún

    Todo lo anterior funciona de forma integral. Avanzar más se vio limitado únicamente por una máquina local que tenía dificultades para ejecutar Ollama junto con todo el sistema. Con más recursos, surgen tres aspectos que aún faltan resolver.

    El flujo de aprobación es la deficiencia más urgente. approval_status ya existe en el estado y requires_approval ya está marcado, pero en realidad nada se detiene. La idea es enviar un botón de Slack, detener el gráfico y reanudarlo solo después de que alguien haga clic, antes de que se ejecute cualquier acción de nivel 3. LangGraph puede suspender un gráfico durante su ejecución mediante sus puntos de control, por lo que lo que queda es solucionar los problemas técnicos, no rediseñarlo.

    La comparación de métricas antes y después es la segunda deficiencia. check_service_health proporciona una captura instantánea después de la corrección. Lo que necesita el agente es un valor delta de Prometheus, es decir, la tasa de errores cinco minutos antes frente a cinco minutos después, para estar seguro de que realmente se solucionó algo y no fue solo cuestión de coincidencia temporal.

    Autoconstrucción de manuales de procedimientos es el objetivo a largo plazo. Actualmente, los dos repositorios de conocimiento, los incidentes anteriores y los manuales de procedimientos, nunca se alimentan mutuamente. Después de una solución inusual, el agente debería escribir un nuevo fragmento del manual y añadirlo a la base de conocimientos, de modo que retrieve_runbook lo devuelva la próxima vez. Eso uniría los dos niveles de memoria.

    Qué enseñó el proyecto

    El problema real con los agentes de infraestructura autónomos resulta no ser el razonamiento del modelo, sino la estructura de seguridad que lo rodea. Los niveles de memoria, un motor de políticas, un registro de auditoría y controles de aprobación son lo que hacen que un equipo esté dispuesto a dejar una herramienta funcionando sola a las 3 de la mañana.

    AegisOps adopta una postura clara: una máquina de estados estructurada con límites de permisos definidos es superior a un agente flexible que improvisa. Esa postura se ha mantenido hasta ahora, gracias a un registro de incidentes más completo y a un agente que mejora con cada caso nuevo. La posibilidad de hacer puntos de control en LangGraph, la separación clara entre herramientas de diagnóstico y de acción, y el estado definido por TypedDict facilitan la inspección, depuración y auditoría del sistema cuando ocurre un problema y surge la pregunta de por qué el agente eligió actuar de esa manera.

    Si estás desarrollando algo similar, invierte temprano en una arquitectura de memoria adecuada. Un agente con buena memoria vale diez veces más que uno con un prompt ligeramente mejor. Los Transformers requerían atención, mientras que los agentes en el mundo real necesitan contexto.

    Leer más

  • Elegir una arquitectura RAG: Naive, Advanced, CRAG, GraphRAG, Agentic — Una comparación práctica de seis arquitecturas RAG con ventajas, desventajas y casos de uso, además de un proceso de toma de decisiones en cuatro pasos y los casos en los que RAG no es la herramienta adecuada.
  • Notas prácticas: Le di a un agente IA una URL JSON. Construyó una empresa functional — Guía paso a paso de Notas prácticas: Le di a un agente IA una URL JSON. Construyó una empresa functional: contratos, verificaciones y espacios de código listos para usar para los equipos que implementan este patrón.
  • Pasando a Claude Opus 5.5: Cinco cambios significativos detrás del ID del modelo — Una puerta de entrada para cambiar a claude-opus-5-5: pensamiento continuo, elección forzada de herramientas, bloques de pensamiento vinculados al modelo, herramientas para el uso de la computadora y un flujo de progreso silencioso.
  • De un enfoque básico a un pipeline RAG en producción para declaraciones SEC 10-K: fragmentación de datos, búsqueda híbrida — Por qué un pipeline RAG convencional falló con declaraciones de 10-K de 300 páginas, y las soluciones: fragmentación según secciones, recuperación híbrida, reclasificación con codificadores cruzados, validación y citas de metadatos.
  • Ingeniería de Agent Harness: Presupuestos, permisos y evaluaciones para agentes en producción — La mayoría de los agentes fallan en producción debido a una estructura deficiente de harness, no al modelo en sí. Conozca el ciclo de operaciones, los niveles de riesgo, los presupuestos, la capa de contexto y las evaluaciones de seguridad que mantienen seguros a los agentes.
  • Cómo un agente AI para arreglar documentos se engañó a sí mismo: falsos positivos en un entorno de pruebas Docker — Un agente LangGraph diseñado para reparar códigos dañados en README primero publicó una solución inútil y luego falló al tratar con paquetes src-layout. Aquí se muestra el error del entorno de pruebas y la solución real para arreglar los documentos.
  • Construye un servidor MCP para un diario de trabajo buscable en 190 líneas de TypeScript — Un pequeño servidor MCP basado en archivos Markdown: cuatro herramientas basadas en intenciones, descripciones al estilo de prompt, un cliente de demostración de stdio que también sirve como prueba, además de problemas reales que pueden surgir.
  • Cómo funciona RAG en el interior: fragmentación, embeddings, búsqueda vectorial y reclasificación — Una explicación paso a paso de la generación aumentada por recuperación de información: fragmentación, embeddings, bases de datos vectoriales, montaje de prompts, reclasificación, límites de las alucinaciones y una breve nota sobre KAG.