Головна / Статті

AegisOps: Автономний агент SRE з чотирма рівнями пам’яті

Як агент LangGraph ReAct вирішує інциденти в Kubernetes за допомогою епізодичної пам’яті pgvector, рівнів автономії, контрольованих політиками, шістнадцяти інструментів та повного журналу аудиту.

Видавець: REACTAPP.TOP12 хв читання2723 слів

Джерела: I Built an Autonomous SRE Agent That Resolves Production Incidents While I Sleep

Зараз 3:14 ранку, і у PagerDuty постійні сигнали тривоги. Prometheus відправив критичне сповіщення: рівень помилок у сервісі оплати зріс до 34%, контейнери зникають через помилки типу OOMKills, а термін порушення умов SLA закінчується через шість хвилин. Є два варіанти — піднятися з ліжка та підключитися через SSH до кластера або доручити це агенту.

AegisOps — це автономний агент SRE, створений саме для таких ситуацій. Він діагностує, вживає заходів та усуває проблеми в промисловому середовищі без участі людини, записуючи кожне прийняте рішення та причину його прийняття. Це не чат-бот, обгорнутий навколо kubectl. У нього є справжня пам’ять, багатошарові механізми автономного керування та можливість навчатися на прикладах вже вирішених проблем. Основною метою було не замінити інженера, який працює у режимі чергування, а надати йому невтомного помічника, який вже знає інструкції, пам’ятає останні десять інцидентів та перевіряє правила перед тим, як втручатися в роботу системи.

Чому ReAct у LangGraph

ReAct (міркування плюс дія) ідеально підходить для реагування на інциденти. Проста ланцюгова структура виконує кроки сліпо, тоді як агент ReAct формулює думку, обирає дію, спостерігає за тим, що сталося, та повторює цей процес. Саме така підхід є правильним під час збоїв, адже корисними є запитання про те, що відомо, що спостерігається та що потрібно спробувати далі.

LangGraph забезпечує виконання графа з урахуванням стану. Кожен вузол має доступ до повного об’єкта AegisState — TypedDict, який містить повідомлення, пам’ять, деталі інциденту, рішення політик, виклики інструментів та результати перевірки. Оскільки стан зберігається у PostgreSQL після кожного кроку, виконання можна призупинити, перевірити та продовжити.

У графі є чотири вузли, які навмисно залишені простими, щоб уникнути надмірної складності:

    • memory_node працює першим та завантажує все, що знадобиться агенту, ще до початку міркувань.
  • agent_node — це цикл міркувань LLM.
  • tools — це стандартний ToolNode від LangGraph, який виконує те, про що просить модель.
  • resolution_node завершує обробку інциденту: він зберігає інформацію з пам’яті, розраховує MTTR та надсилає повідомлення в Slack.

Чотири рівні пам’яті

Більшість систем агентів ігнорують архітектуру пам’яті або обмежуються двома рівнями. Спеціаліст з обслуговування систем людина використовує чотири види пам’яті під час обробки інцидентів, а AegisOps відтворює їх усі.

  1. Епізодична пам’ять (pgvector, косинусна схожість) зберігає минулі вирішені інциденти у вигляді ембеддингів. Коли з’являється новий інцидент, знаходяться три найбільш схожі попередні випадки, які потім вставляються до системного запиту разом із їхньою первинною причиною, вжитими заходами та результатом.
  2. Семантична пам’ять (RAG над базою знань у pgvector) зберігає розділені на частини посібники, документацію до архітектури та звіти після аналізу інцидентів. Інструмент retrieve_runbook шукає серед них семантично, надаючи агенту інституційні знання команди.
  • Процедурна пам’ять (політики YAML, двигун правил та Redis) визначає жорсткі правила щодо того, що агент може робити на кожному рівні автономії. Вони завантажуються з params.yml до Redis на початку обробки графу та оцінюються перед будь-якою дією, яка щось змінює. Це можна вважати навченим рефлексом агента для безпечної роботи.
  • Робоча пам’ять (стан AegisOps та чекпоїнт LangGraph) відображає поточний стан: повідомлення, поточні думки, виклики інструментів, результати перевірки стану та кількість спроб повторної обробки. Зберігання чекпоїнтів на кожному вузлі забезпечує збереження контексту після перезапуску.
  • Епізодична пам’ять на практиці

    Під час входу memory_node викликає функцію retrieve_similar_incidents із списком симптомів поточної події. 768-вимірний кодер речень інкорпорує ці симптоми, а функція косинусової відстані з pgvector порівнює їх із даними з таблиці IncidentMemory. Попередні події, які мають рівень схожості вище порогового значення 0,72, форматуються та додаються до системного запиту перед першою думкою агента.

    Після вирішення проблеми resolution_node викликає функцію save_incident_memory, щоб агент міг зберегти власні спогади. Інкорпорація симптомів здійснюється разом із гіпотезою первинної причини, тому подальші події з схожими симптомами знайдуть цю конкретну подію.

    Варіантом була спеціалізована векторна база даних, така як Pinecone чи Weaviate. Зберігання всього в PostgreSQL усувало одну залежність від інфраструктури та забезпечувало транзакційну послідовність між записами про інциденти та їхніми векторними представленнями. На такому масштабі pgvector цілком достатній.

    Рівні автономії від L0 до L3

    Це було найскладніше питання дизайну: як надати агенту справжні повноваження у робочому середовищі, не ризикуючи катастрофою? Відповіддю є модель багатошарової автономії, у якій кожна дія, що змінює стан, проходить перевірку за правилами перед виконанням.

    Кожен інструмент дії (restart_deployment, scale_deployment, update_deployment_image) починається з виклику retrieve_policy. Механізм політик оцінює дію, сервіс, ступінь серйозності та поточний рівень автономії з урахуванням правил, вказаних у params.yml. Якщо відповідь «ні», інструмент негайно повертає відмову, і агенту доводиться шукати інший шлях, зазвичай через ескалацію.

    Слід звернути увагу на поле requires_approval. Навіть на рівні L3 деякі руйнівні дії, такі як скасування оновлення образу в критичному сервісі оплат, все ще потребують прямої схвалення людини. Агент публікує повідомлення у Slack та чекає. Це дає команді, яка працює у режимі 24/7, впевненість у тому, що найбільш ризиковані операції контролюються людиною.

    Шістнадцять інструментів

    AegisOps постачає шістнадцять інструментів у чотирьох категоріях. Шість з них виділяються:

    1. get_logs є збирачем доказів. Він бере останні 500 рядків файлу журналів сервісу, зберігає необхідні рівні деталей та видаляє з кожного рядка IP-адреси, UUID та звичайні числа. Таким чином два випадки відсутності певних елементів з різних хостів вважаються одним шаблоном. Клас Counter у Python ранжує ці шаблони та повертає найчастіше зустрічаючіся. Зчитувачі терміналу та цикл обробки даних агента отримують однаковий структурований результат; для цього не потрібен кластер Elasticsearch — достатньо файлу журналів та кількох рядків коду на Python.
    2. get_pod_status запитує Kubernetes CoreV1Api щодо подів, позначених міткою app={deployment}, та повідомляє про їх перезапуски, готовність та стан контейнерів. Якщо до кластера неможливо дістатися, він коректно переходить на альтернативний режим та направляє агента до функції get_logs.
  • restart_deployment виконує більшу частину роботи. Оновлення анотації kubectl.kubernetes.io/restartedAt запускає поступове перезавантаження, після чого очікується, поки всі репліки стануть готовими, і виконується перевірка їхнього стану. Опційний крок відключення дозволяє утримати трафік поза системою під час перезавантаження.
  • update_deployment_image є засобом екстреного скасування змін. Він перезаписує зображення контейнера в специфікації, чекає, поки процес розгортання завершиться, а потім перевіряє стан подів. Оскільки це найсильніший інструмент у наборі засобів, для його використання під час критичних інцидентів у продакшені потрібна автономія рівня L3.
  • check_service_health поєднує інформацію про готовність подів від Kubernetes з даними про частоту помилок від Prometheus. Він повертає значення True лише тоді, коли всі поди готові та кількість відповідей типу 5xx залишається нижче 5%.
  • retrieve_runbook шукає посібники з роботи з інцидентами, звіти після їх вирішення та примітки щодо архітектури за змістом та повертає чотири найкращі фрагменти зі схожістю 0,75 або вище, які розміщуються у системному запиті під заголовком "RUNBOOK CONTEXT".
  • Один інцидент — від початку до кінця

    Ось що зробив агент у випадку перевантаження сервісу оплати OOMKill о 3:14 ранку:

    [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 ✅
    

    Усе це зайняло 187 секунд – від отримання сповіщення до вирішення проблеми пройшло трохи більше трьох хвилин, причому нікого не потрібно було будити. Саме такий час тепер зазвичай спостерігається під час інцидентів, схожих на попередні. Співробітник почав з аналізу того, що спрацювало минулого разу, як це робить старший інженер – це є проявом епізодичної пам’яті, яка виконує свою функцію.

    Цей слід було відновлено для кращої зрозумілості. Реальна робота на простій локальній інфраструктурі, з одним кластером Kind без Prometheus, виглядає інакше. Інцидент було спричинено простим викликом webhook у 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"
      }'
    

    Агент пройшов усю процедуру від початку до кінця. Він знайшов схожі інциденти та сформулював гіпотезу про те, що сплеск трафіку перевантажує ємність подів, що збігається з попереднім випадком у пам’яті. Він викликав функцію get_pod_status та побачив, що 2 з 5 подів готові, тоді збільшив кількість реплік до 4, підтвердив стан за допомогою check_service_health та закрив інцидент як RESOLVED. У чаті Slack у каналі #all-aegisops з’явилось повідомлення, а інцидент було занесено назад у епізодичну пам’ять.

    Це набагато краще, ніж збій через нестачу пам’яті у gRPC о 3 години ранку, проте цикл залишається незмінним: той самий цикл „Мислення – Дія – Спостереження“, та сама перевірка політики перед масштабуванням та той самий запис у пам’ять наприкінці. Лише умови роботи простіші. Агент не може відрізнити кластер з ноутбуків від флоту з 50 вузлів. Якщо змінити kubeconfig та налаштувати Prometheus на реальні метрики, той самий код буде працювати на реальних системах.

    Slack як канал виведення інформації

    Важливі дії відправляють повідомлення в Slack. Коли інцидент закривається, вузол з рішенням записує інформацію у #all-aegisops із вказанням рівня серйозності, часу усунення проблеми та повним описом рішення, що означає, що інженер, який знаходиться у режимі чергування, отримує повідомлення з зеленим галочком, а не сповіщення через пейджер.

    Технологічна стек

    Усе це працює на простій технологічній стеку. PostgreSQL з модулем pgvector використовується для зберігання епізодичної інформації та чекпоїнтів LangGraph. Redis зберігає політики та призначений для майбутнього обмеження швидкості запитів. Prometheus надсилає реальні показники кількості помилок у функцію check_service_health, а Grafana створює панелі керування.

    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:
    

    Додаток — це FastAPI під керуванням uvicorn. Інциденти надходять на кінцеву точку webhook, яка створює об’єкт IncidentEvent та викликає функцію run_agent(). Виконання є асинхронним, тому кілька інцидентів можуть оброблятися паралельно без блокування. Клієнт Kubernetes — це kubernetes_asyncio, який забезпечує неблокуючі виклики до кластера.

    Одна проблема: функції get_pod_status та check_service_health завантажують конфігурацію Kubernetes під час імпорту модуля, а не всередині асинхронної функції. Якщо кластер недоступний під час запуску, ці функції працюють без помилок, але повертають попереджувальні повідомлення, тому переконайтеся, що KUBECONFIG або обліковий запис сервісу в кластері налаштовані перед запуском додатку.

    Історія аудиту

    Кожен виклик інструменту записується у TypedDict ToolInvocation: що було виконано, з якими параметрами, що повернулося, чи було це успішним, скільки часу це зайняло, коли це сталось та яке рішення політики це дозволило. Ці записи накопичуються у state["tool_invocations"] під час виконання та зберігаються у епізодичній пам’яті після завершення. Окремий інструмент write_audit_log генерує структурований рядок JSON для кожної значущої дії.

    У результаті будь-яку подію можна переглянути пізніше: послідовність кроків, діючі дозволи та причина кожного з них. Саме такі записи потребують команди з контролю дотримання правил.

    Одне рішення щодо дизайну впливає на все інше. Кожна дія зі змінами повідомляє про рішення політики разом із результатом перевірки стану, тож агент одночасно дізнається три речі: чи вдалося здійснити виклик, чи було це дозволено та чи є сервіс зараз у стабільному стані. Усі ці фактори впливають на його наступні рішення.

    Що ще бракує

    Усе вищезазначене працює безперервно. Продовження роботи обмежувалося лише можливостями локальної машини, яка намагалась запустити Ollama поруч із повною системою. З більшими ресурсами спочатку проявляються три прогалини.

    Процес схвалення є найбільшою проблемою. У стані вже існує approval_status, і requires_approval вже перевіряється, проте ніщо насправді не зупиняє процес. План полягає у тому, щоб надіслати кнопку в Slack, зупинити граф та продовжити його роботу лише після того, як хтось натисне на кнопку, ще до того, як буде виконана будь-яка дія рівня L3. LangGraph може призупинити граф під час його виконання за допомогою своїх точок перевірки, тож залишається лише налаштування логіки, а не повна переробка.

    Порівняння показників до та після змін — це друга проблема. check_service_health дає лише моментальний знімок стану після виправлення. Агенту потрібен дельта-показник від Prometheus — кількість помилок за п’ять хвилин до та після змін, щоб бути впевненим, що проблема справді вирішена, а не що це було лише наслідком збігу часу.

    Самостійне створення посібників з роботи є довгостроковою метою. Наразі два джерела знань — минулі інциденти та посібники — ніколи не взаємодіють між собою. Після незвичайного рішення проблеми агент має написати новий фрагмент посібника та додати його до бази знань, щоб функція retrieve_runbook могла його повернути наступного разу. Це об’єднало б обидві шари пам’яті.

    Що навчив проект

    Виявляється, справжньою проблемою автономних агентів інфраструктури є не механізм міркувань моделі, а структура безпеки, яка її оточує. Шари пам’яті, двигун політик, журнал аудиту та механізми схвалення — ось що змушує команду довіряти інструменту навіть о 3 годині ранку.

    AegisOps займає чітку позицію: структурована машина станів із чіткими межами дозволів краща за агента, який діє імпровізовано. Ця позиція досі виявилася ефективною завдяки більш детальному журналу інцидентів та агенту, який вдосконалюється після кожного нового випадку. Функція створення контрольних точок у LangGraph, чітке розділення діагностичних та дійових інструментів, а також використання TypedDict для опису станів роблять систему простою для перевірки, виправлення помилок та аудиту у разі проблем, коли виникає питання, чому агент обрав саме такі дії.

    Якщо ви створюєте щось подібне, рано інвестуйте у архітектуру пам’яті. Агент із хорошою пам’яттю вартий у десять разів більше, ніж агент із трохи кращим запитом. Трансформерам потрібна увага, а агентам у реальному світі — контекст.

    Пов’язана література

  • Вибір архітектури RAG: Naive, Advanced, CRAG, GraphRAG, Agentic — практичне порівняння шести архітектур RAG із перевагами, недоліками та сценаріями використання, а також чотирикроковий алгоритм прийняття рішень та випадки, коли RAG є непідходящим інструментом.
  • Практичні нотатки: Я дав штучному інтелекту URL у форматі JSON. Він створив функціональну корпорацію — детальний огляд Практичних нотаток: Я дав штучному інтелекту URL у форматі JSON. Він створив функціональну корпорацію: контракти, перевірки та готові блоки коду для команд, які впроваджують цю модель.
  • Перехід на Claude Opus 5.5: п’ять кардинальних змін, пов’язаних із ідентифікатором моделі — інструмент для переходу на claude-opus-5-5: постійне мислення, примусовий вибір інструментів, блокування мислення через обмеження моделі, інструменти для роботи з комп’ютером та безшумний потік прогресу.
  • Від простого підходу до робочої системи RAG для юридичних фінансових документів типу SEC 10-K: розділення на частини, гібридний пошук — чому стандартна система RAG зазнала невдачі при обробці 300-сторінкових документів 10-K та як це виправити: частини з урахуванням структури розділів, гібридний метод пошуку, переранжування за допомогою крос-енкодерів, перевірка та посилання на метадані.
  • Інженерія систем керування агентами: бюджети, дозволи та оцінки для агентів у продакшені — Більшість агентів зазнають невдач у продакшені через слабку систему керування, а не через саму модель. Дізнайтеся про механізми роботи, рівні ризиків, бюджети, структурурацію контексту та оцінки безпеки, які забезпечують безпеку агентів.
  • Як AI-агент для виправлення документів обманув себе: хибні позитивні результати в сандбоксі Docker — Агент LangGraph, який виправляє пошкоджений код README, спочатку оприлюднив неефективне рішення, а потім зазнав невдач із пакетами src-layout. Ось опис помилки в сандбоксі та справжнє рішення для виправлення документів.
  • Створення сервера MCP для щоденника роботи з пошуком за допомогою 190 рядків TypeScript — невеликий сервер MCP для файлів Markdown: чотири інструменти на основі інтентів, описи у стилі запитів, демонстраційний клієнт stdio, який виконує функцію тесту, а також реальні проблеми під час роботи.
  • Як працює RAG у продакшн-системах: чанкінг, ембеддинги, векторний пошук та переранкінг — поетапний огляд технології Retrieval-Augmented Generation: чанкінг, ембеддинги, векторні бази даних, формування запитів, переранкінг, обмеження щодо галюцинацій та короткий опис KAG.