Почему SRE идеально подходит для AI-агента
Работа SRE по своей сути сводится к корреляции информации из множества систем и принятию решений в условиях неопределённости. Во время инцидента инженер действует примерно так:
-
Смотрит на алерт. Что это за сигнал?
-
Анализирует метрики. Это выброс, тренд или плато?
-
Проверяет последние деплои. Что выходило в прод?
-
Ищет в рунбуках. Такое уже случалось?
-
Читает логи. Что конкретно ломается?
-
Принимает решение. Откатить? Масштабировать? Позвонить кому-то?
Это именно то, в чём силён агент на основе большой языковой модели (LLM-based agent): структурированные рассуждения над разнородными источниками данных с вызовом инструментов для получения дополнительной информации.
Разница между чат-ботом и агентом в том, что агент не ждёт, пока вы принесёте ему данные. Он идёт и добывает их сам.
Архитектура
Вот что я построил. В основе — цикл агента в стиле ReAct (Reason → Act → Observe → Repeat, то есть «рассуждай → действуй → наблюдай → повтори»), работающий поверх LLM (я использовал GPT-4o, но Claude 3.5 тоже хорошо справляется).
Агент имеет доступ к набору инструментов:
tools = [
splunk_search, # Выполнить поисковый запрос в Splunk, вернуть топ результатов
appdynamics_get_health, # Получить статус здоровья уровня приложения в AppDynamics
k8s_get_pods, # Вывести поды в неймспейсе с их статусом
k8s_describe_pod, # Описать конкретный под
confluence_search, # Найти страницу в Confluence по запросу
gitlab_recent_commits, # Получить последние коммиты/деплои сервиса
pagerduty_create_alert, # Эскалировать на человека
]
Каждый инструмент — это Python-функция с докстрингом, который становится частью схемы инструмента. LLM видит название инструмента, его описание и спецификацию параметров. Модель сама решает, какой инструмент вызвать, с какими аргументами, а затем получает результат и решает, что делать дальше.
Цикл выглядит так:
while not agent.done:
response = llm.chat(messages, tools=tools)
if response.tool_call:
result = execute_tool(response.tool_call)
messages.append(tool_result(result))
else:
agent.done = True
final_answer = response.content
Вот и всё. Это и есть ядро системы.
Как сделать инструменты рабочими
Самое сложное — не цикл агента. Самое сложное — это инструменты.
Splunk
REST API Splunk достаточно прост, но нужно обрабатывать асинхронное создание задач: отправить запрос, ждать завершения, затем получить результаты.
def splunk_search(query: str, earliest: str = "-1h", latest: str = "now") -> list[dict]:
"""
Выполнить поисковый запрос в Splunk. Возвращает до 20 результатов.
Используйте синтаксис SPL. Например: 'index=prod sourcetype=app_logs level=ERROR'
"""
job = splunk_client.jobs.create(query, earliest_time=earliest, latest_time=latest)
while not job.is_done():
time.sleep(1)
return [dict(r) for r in job.results()][:20]
Ключевой принцип: возвращайте меньше, а не больше. LLM не нужно 500 строк логов. Дайте ей 20. Если нужно — она сама запросит более конкретный запрос.
AppDynamics
У AppDynamics есть REST API, возвращающий правила работоспособности (health rules), нарушения и метрики уровней. Сложность в том, что API аутентифицируется токеном уровня контроллера, а структура эндпоинтов различается для SaaS и on-prem версий.
def appdynamics_get_health(app_name: str, tier_name: str = None) -> dict:
"""
Получить текущий статус работоспособности для приложения в AppDynamics.
Опционально фильтровать по конкретному уровню. Возвращает активные нарушения и общий статус.
"""
violations = appdyn_client.get_health_rule_violations(app_name, duration_in_mins=60)
tier_summary = appdyn_client.get_tier_summary(app_name, tier_name) if tier_name else None
return {
"violations": violations,
"tier": tier_summary
}
Kubernetes
Используйте официальный Python-клиент. Главное — грамотно структурировать возвращаемые данные: сырые объекты Kubernetes очень многословны.
def k8s_get_pods(namespace: str) -> list[dict]:
"""
Вывести все поды в неймспейсе Kubernetes. Возвращает имя пода, статус, количество рестартов и возраст.
Используйте для проверки крашащихся или зависших подов.
"""
v1 = client.CoreV1Api()
pods = v1.list_namespaced_pod(namespace)
return [
{
"name": p.metadata.name,
"status": p.status.phase,
"restarts": sum(cs.restart_count for cs in (p.status.container_statuses or [])),
"ready": all(cs.ready for cs in (p.status.container_statuses or [])),
}
for p in pods.items
]
Confluence
В REST API Confluence есть эндпоинт поиска по контенту. Вы передаёте CQL-запрос и получаете заголовки и тела страниц. Сложность в том, что тела страниц хранятся в формате Confluence Storage Format (нечто вроде XML). Его нужно зачищать:
def confluence_search(query: str) -> list[dict]:
"""
Найти рунбуки или документацию в Confluence по запросу.
Возвращает заголовок страницы и отрывок содержимого в виде простого текста.
"""
results = confluence_client.cql(f'text ~ "{query}" AND space = "RUNBOOKS"', limit=3)
pages = []
for r in results["results"]:
body = r["body"]["storage"]["value"]
text = BeautifulSoup(body, "html.parser").get_text()[:2000]
pages.append({"title": r["title"], "content": text})
return pages
GitLab
API GitLab чистый и удобный. Нас интересуют последние коммиты или запуски пайплайнов для сервиса:
def gitlab_recent_commits(project_path: str, branch: str = "main", limit: int = 5) -> list[dict]:
"""
Получить последние коммиты в проект GitLab. Используйте, чтобы проверить,
не вызвал ли недавний деплой регрессию.
"""
project = gl_client.projects.get(project_path)
commits = project.commits.list(ref_name=branch, per_page=limit)
return [
{
"sha": c.short_id,
"message": c.message,
"author": c.author_name,
"date": c.created_at,
}
for c in commits
]
Системный промпт
Системный промпт (system prompt) несёт на себе огромную нагрузку. Вот его суть в том виде, который я использую:
Ты — SRE-агент. Твоя задача — расследовать инциденты, диагностировать первопричины и предлагать действия.
Получив описание инцидента или алерт, ты должен:
1. Собрать релевантные данные с помощью доступных инструментов.
2. Рассуждать шаг за шагом о том, что говорят тебе данные.
3. Искать предыдущие инциденты или рунбуки, соответствующие паттерну.
4. Предложить конкретное действие: откат, масштабирование, рестарт, эскалация на человека или признание проблемы несущественной.
Правила:
- Всегда проверяй как минимум два независимых источника данных, прежде чем делать выводы.
- Если не уверен — скажи об этом и укажи, какая дополнительная информация помогла бы устранить неопределённость.
- Никогда не вызывай pagerduty_create_alert, пока не исчерпаны все другие диагностические варианты.
- Будь краток в финальном ответе. Включи: что ты обнаружил, что, по твоему мнению, стало причиной, и что нужно сделать.
Правило «как минимум два источника» важно. Без него агент порой цепляется за первый же найденный сигнал и останавливается.
Пример реального запуска
Вот как выглядит реальный запуск агента (в сокращении):
Входные данные:
"Всплеск задержки в сервисе checkout. P99 > 4s последние 10 минут."
Агент рассуждает: Нужно проверить AppDynamics для сервиса checkout, затем посмотреть на статус подов в Kubernetes и проверить последние деплои.
Вызов инструмента 1: appdynamics_get_health("checkout-service")
→ 2 активных нарушения: «Backend call time > 2s» на уровне payment-gateway
Вызов инструмента 2: k8s_get_pods("checkout")
→ Все поды запущены, 0 рестартов, все готовы
Вызов инструмента 3: gitlab_recent_commits("platform/checkout-service")
→ Последний коммит 3 часа назад, сообщение: «Increase payment gateway timeout to 10s»
Вызов инструмента 4: confluence_search("payment gateway timeout latency")
→ Найден рунбук: «Если задержка payment gateway > 2s, проверьте наличие rate limiting со стороны провайдера»
Вызов инструмента 5: splunk_search("index=prod service=payment-gateway ERROR timeout")
→ 847 ошибок connection timeout за последний час, все к api.paymentprovider.com
Финальный ответ: Первопричина: внешний платёжный провайдер, по всей видимости, применяет rate limiting или деградировал. Последний коммит увеличил внутренний порог таймаута, что маскирует поведение быстрого отказа (fast-fail). Рекомендации: (1) Проверьте страницу статуса платёжного провайдера. (2) Если провайдер деградировал — включите резервный режим оплаты согласно рунбуку P-14. (3) Рассмотрите откат изменения таймаута, чтобы восстановить fast-fail.
На это ушло около 40 секунд. Инженер-человек потратил бы 15 минут.
Что не сработало
Слишком много инструментов. Когда я добавил по инструменту на каждую возможную систему, агент начал делать лишние вызовы «просто чтобы проверить». Держите набор инструментов сфокусированным.
Неструктурированный вывод инструментов. Если инструмент возвращает сырой JSON или кусок текста, LLM тратит токены на его разбор. Структурируйте возвращаемые данные в виде аккуратных словарей с понятными ключами.
Разрешение агенту самостоятельно выполнять действия. Когда я позволил агенту вызывать kubectl rollout restart или запускать CI-пайплайны, он иногда действовал преждевременно. Теперь агент предлагает действия, но не выполняет их без подтверждения человека. Для production-систем это единственно правильный подход.
Слишком длинные системные промпты. У меня была версия с промптом на 2000 слов, охватывавшим все пограничные случаи. Модель начала воспринимать его как чеклист и выполнять лишнюю работу. Короткие, принципиальные промпты работают лучше.
Что работает неожиданно хорошо
Агент хорошо справляется с временно́й корреляцией. «Деплой три часа назад, всплеск ошибок три часа назад» — для человека это очевидно, но и для агента тоже. Такие связи он устанавливает стабильно.
Рунбуки он читает лучше большинства людей. Рунбуки часто плотные и плохо организованные. Агент читает их, извлекает нужное действие и применяет его, не теряя нить рассуждений.
Он знает, чего не знает. Когда я даю ему алерт без полезных результатов инструментов, он прямо говорит: «У меня недостаточно сигналов для диагностики. Рекомендую получить полный трейс ошибки из X и проверить Y.» Это ценно.
Задержка приемлемая. Полное расследование с пятью вызовами инструментов занимает 30–60 секунд. Для инцидента, на который у человека ушло бы 15 минут, это вполне нормально.
Соображения по развёртыванию
-
Сначала запускайте в режиме только чтения. Никаких инструментов записи. Только диагностика. Завоюйте доверие, прежде чем давать агенту возможность что-то менять.
-
Логируйте каждый вызов инструмента и его ответ. Вам нужна цепочка аудита. Когда агент ставит неверный диагноз, вы должны понять почему.
-
Контролируйте эскалацию в PagerDuty. Агент должен эскалировать только в том случае, если уверен, что проблема реальна и не может быть решена без человека.
-
Оборачивайте инструменты в таймауты. Внешние API зависают. Splunk-запрос, выполняющийся 3 минуты, заморозит цикл агента.
-
Стоимость токенов управляема. Одно полное расследование с GPT-4o стоит около $0.04–0.08. При реальном объёме инцидентов это несущественная сумма.
Более широкая перспектива
Цель — не заменить SRE-инженеров. Цель — взять на себя механическую часть реагирования на инциденты — ту, что связана с навигацией по дашбордам и перекрёстной проверкой систем, — чтобы инженеры могли сосредоточиться на том, что действительно требует суждения: архитектурных решениях, постмортемах, планировании ёмкости, построении систем, которые делают будущие инциденты менее вероятными.
SRE-агент, который автоматически правильно диагностирует 70% инцидентов и предоставляет полный диагностический отчёт для оставшихся 30%, — это колоссальный мультипликатор сил. Узкое место в реагировании на инциденты — не человеческий интеллект. Это человеческое внимание и стоимость переключения контекста.
Это решаемо уже сейчас: с помощью текущих моделей, текущих API и нескольких сотен строк на Python.
Если хотите это построить: начните с двух инструментов (Splunk + Kubernetes), напишите хороший системный промпт и прогоните агента по последним 10 инцидентам. Посмотрите, правильно ли он их разберёт. Добавляйте инструменты по мере необходимости.
Самое сложное — не AI. Самое сложное — написать чистые обёртки для инструментов.