AegisOps: Автономный агент SRE с четырьмя уровнями памяти.
Как агент LangGraph ReAct решает инциденты в Kubernetes с использованием эпизодической памяти pgvector, уровней автономии, контролируемых политиками, шестнадцати инструментов и полной истории аудита.
Источники: I Built an Autonomous SRE Agent That Resolves Production Incidents While I Sleep
Сейчас 3:14 ночи, и в PagerDuty царит суматоха. Prometheus выдал критическое уведомление: уровень ошибок в платежном сервисе вырос до 34%, контейнеры гибнут из-за проблем с памятью, а время нарушения SLA истекает через шесть минут. Есть два варианта — встать с кровати и подключиться к кластеру через SSH или поручить решение проблемы агенту.
AegisOps — это автономный агент SRE, созданный именно для таких ситуаций. Он диагностирует, принимает меры и устраняет инциденты в производственной среде без участия человека, записывая каждое принятое решение и причину его принятия. Это не чат-бот, основанный на kubectl; у него есть реальная память, многоуровневые механизмы автономного управления и возможность извлекать уроки из уже решенных инцидентов. Задачей не было заменить инженера, находящегося в режиме дежурства, а предоставить ему неутомимого помощника, который уже знает руководства, помнит последние десять инцидентов и проверяет правила перед вмешательством в производственную среду.
Почему ReAct в LangGraph
ReAct (размышление плюс действие) идеально подходит для реагирования на инциденты. Простая цепочка действий выполняет шаги слепо, тогда как агент ReAct формирует мысль, выбирает действие, наблюдает за результатами и повторяет процесс. Именно такой подход необходим при сбоях, когда важны вопросы о том, что известно, что наблюдается и что следует попробовать дальше.
LangGraph обеспечивает выполнение графа с учетом состояния. Каждый узел имеет доступ к полному объекту AegisState — TypedDict, содержащему сообщения, память, детали инцидента, решения политик, вызовы инструментов и результаты проверки. Поскольку состояние сохраняется в PostgreSQL после каждого шага, выполнение процесса можно приостановить, изучить и возобновить.
В графе четыре узла, которые намеренно оставлены простыми, чтобы избежать чрезмерной сложности:
-
memory_nodeобрабатывается в первую очередь и загружает всё, что понадобится агенту, прежде чем начнётся процесс рассуждений.
agent_nodeпредставляет собой цикл рассуждений большой языковой модели.tools— это стандартныйToolNodeот LangGraph, который выполняет любые задания, заданные моделью.resolution_nodeзавершает обработку инцидента: он сохраняет информацию в памяти, рассчитывает время восстановления и отправляет сообщение в Slack.
Четыре уровня памяти
Большинство систем агентов игнорируют архитектуру памяти или ограничиваются двумя уровнями. Человек-SRE привлекает четыре вида памяти при решении инцидентов, а AegisOps воспроизводит их все.
- Эпизодическая память (pgvector, косинусное сходство) хранит информацию о ранее разрешённых инцидентах в виде эмбеддингов. При появлении нового инцидента извлекаются три наиболее похожих прошлых случая, которые вместе с их первопричиной, принятыми мерами и итогами включаются в системный запрос.
- Семантическая память (RAG над базой знаний в pgvector) хранит разбитые на части руководства, документацию по архитектуре и отчёты о последствиях инцидентов. Инструмент
retrieve_runbookвыполняет семантический поиск среди них, предоставляя агенту институциональные знания команды.
params.yml в Redis в начале обработки графа и оцениваются перед любым действием, изменяющим что-либо. Можно считать её тренированным рефлексом агента для безопасной работы.Эпизодическая память на практике
При входе 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 и ждет ответа. Это дает команде, находящейся в режиме дежурства, уверенность в том, что при выполнении наиболее рискованных операций человек постоянно в курсе ситуации.
Шестнадцать инструментов
AegisOps поставляется с шестнадцатью инструментами, разделенными на четыре категории. Шесть из них выделяются:
- get_logs предназначен для сбора доказательств. Он берет последние 500 строк из файла журнала службы, сохраняет указанные уровни информации и удаляет из каждой строки IP-адреса, UUID и простые числа. Следовательно, два отклонения от разных хостов считаются одним шаблоном. Класс
Counterв Python сортирует эти шаблоны и возвращает наиболее часто встречающиеся. Читатели терминала и цикл обработки данных агента получают один и тот же структурированный результат; при этом не требуется использование кластера Elasticsearch — достаточно файла журнала и нескольких строк кода на Python. - get_pod_status запрашивает у интерфейса Kubernetes CoreV1Api информацию о подах, имеющих метку
app={deployment}, и сообщает о их перезапусках, готовности к работе и состоянии контейнеров. Если к кластеру невозможно получить доступ, функция вежливо переходит на режим резервного функционирования и направляет агента на выполнение функцииget_logs.
kubectl.kubernetes.io/restartedAt запускает поэтапную перезагрузку, затем ожидается, пока все реплики станут готовы, после чего проводится проверка их работоспособности. Опциональный шаг отключения трафика предотвращает его поступление во время перезагрузки.True только тогда, когда все поды готовы и количество ответов типа 5xx не превышает 5%.Один инцидент — от начала до конца
Вот что сделал агент в связи с ситуацией 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. В чате #all-aegisops появилось сообщение в Slack, а информация об инциденте была сохранена в памяти.
Это всё же лучше, чем сбой из-за нехватки памяти в 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 или учетная запись сервиса внутри кластера настроены до запуска приложения.
Хроника аудита
Каждый вызов инструмента записывается в объект типа ToolInvocation из TypedDict: что было запущено, с какими параметрами, что вернулось, был ли результат успешным, сколько времени это заняло, когда это произошло и какое правило разрешило такой вызов. Записи накапливаются в 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 делают систему удобной для проверки, отладки и аудита в случае сбоев, когда возникает вопрос о том, почему агент принял те или иные решения.
Если вы разрабатываете что-то подобное, заранее инвестируйте в архитектуру памяти. Агент с хорошей памятью стоит в десять раз дороже агента с немного лучшим промптом. Трансформерам требовалась особая обработка, а агентам в реальном мире — контекст.
Связанные материалы
- Снимки памяти агента, навыки, процесс ухудшения качества данных и ограничение действия памяти по веткам — Четыре дополнения к встроенному слою памяти в виде векторной базы данных для программных агентов: портативные снимки с возможностью слияния, активация навыков, процесс ухудшения качества с проверкой на конфликты и ограничение действия памяти по конкретным веткам.
- Использование инструментов в производственных условиях для агентов: происхождение данных, повторные попытки выполнения операций и возможности мониторинга — Агент банка несколько раз вызывал платный API и выдумал номер телефона. Рассматриваются минимальные наборы инструментов, происхождение параметров, трехуровневая система повторных попыток и показатели эффективности каждого инструмента.