Self-healing боты: система самовосстановления AI агентов

Три часа ночи. Бали. WhatsApp-транслятор упал уже в четвёртый раз за месяц. Клиенты не получают сообщений, менеджеры паникуют, сделки зависают. Я перезапускаю контейнер вручную, прописываю в заметках «разобраться с этим завтра», ложусь спать. Завтра не разбираюсь — завтра прилетает другой пожар. Так прошло шесть месяцев, пока я не понял: проблема не в боте, а в том, что у меня нет системы самовосстановления. Сегодня расскажу, как мы построили self-healing архитектуру, которая за следующие шесть месяцев выполнила 247 автовосстановлений — и ни разу не разбудила меня среди ночи.

Почему боты падают и почему это неизбежно

Принято думать, что бот — это что-то очень надёжное. Запустил, настроил, забыл. В реальности AI агент — живой программный организм, который работает в постоянно меняющейся среде. Он зависит от десятков внешних сервисов: API мессенджеров, баз данных, сторонних провайдеров, файловых систем. Каждое из этих звеньев может сломаться в любой момент.

За полтора года работы с AI агентами я собрал личную классификацию причин падений:

  • Потеря соединения — сетевые прерывания, таймауты, смена IP. Бот пытается что-то сделать, получает Connection Refused и зависает в ожидании.
  • Ошибки API — внешний сервис вернул 429 (rate limit), 500 (серверная ошибка) или неожиданный формат ответа. Бот не знает что делать и останавливается.
  • Бесконечные циклы — логическая ошибка в коде или неожиданные данные загоняют бота в петлю. CPU упирается в 100%, полезной работы ноль.
  • Переполнение памяти — бот накапливает данные в памяти, не освобождает их, через несколько часов система убивает процесс по OOM killer.
  • Протухшие токены — OAuth токены, API ключи, сессии истекают. Бот начинает получать 401 Unauthorized и ничего не делает.
  • Конкурентные конфликты — два экземпляра одного бота запустились одновременно, пишут в одну базу, создают дубли или блокируют друг друга.
  • Дисковое переполнение — логи, временные файлы, кэши разрастаются. Бот пытается записать что-то на диск и получает No space left on device.

Ни одна из этих проблем не является признаком «плохого кода». Это нормальная эксплуатационная реальность. Вопрос только в том, кто будет её разруливать: дежурный DevOps в три часа ночи или автоматическая система самовосстановления.

Традиционный подход и его цена

До внедрения self-healing у нас была стандартная схема: Алиса — бот-мониторинг — следила за состоянием всех сервисов и писала мне в Telegram, когда что-то шло не так. Звучит разумно. На практике получалось вот что.

Алиса пишет: «eZee Scrape упал». Я вижу сообщение через час, когда просыпаюсь. Захожу на сервер, смотрю логи, перезапускаю контейнер. Ещё час данные были недоступны — значит, утренний отчёт по занятости вилл пришёл без обновлений. Менеджер не понял почему, позвонил гостю вручную. Цепочка ошибок из одной поломки.

Алиса — это метафора для сотрудника, который умеет только жаловаться, но не решать. Она диагностирует, информирует, ждёт. Прекрасный мониторинг и бесполезный партнёр. Стоимость такого подхода складывается из:

  • Время реакции: от момента падения до восстановления — в среднем 45 минут при ручном вмешательстве
  • Накопленный стресс: постоянные ночные и утренние алерты разрушают качество работы
  • Цепные потери: каждая минута простоя бота — пропущенные лиды, несинхронизированные данные, недовольные клиенты
  • Контекстные переключения: разработчик, которого выдёргивают на «перезапусти контейнер», теряет час глубокой работы

Когда я подсчитал реальные потери за квартал, цифра оказалась неприятной. WhatsApp-транслятор падал раз в три дня. За квартал — порядка 30 инцидентов. Умножаем на 45 минут реакции, добавляем косвенные потери. Это сотни тысяч рублей в год, которые съедала неавтоматизированная инфраструктура.

Архитектура self-healing системы

Self-healing — это не один инструмент, а многоуровневая система, где каждый уровень обрабатывает свой класс проблем. Представьте это как луковицу: внешние слои ловят простые транзиентные ошибки, внутренние — более серьёзные структурные проблемы.

Уровень 1: Health check endpoints

Каждый бот в нашей системе обязательно реализует HTTP endpoint на /health. Это простой веб-сервер, который возвращает JSON с текущим состоянием агента:

Пример ответа /health:

status: "ok" или "degraded" или "critical"

uptime: время работы в секундах

last_heartbeat: временная метка последнего успешного действия

queue_size: размер очереди задач

memory_mb: текущее потребление памяти

errors_last_hour: количество ошибок за последний час

Ключевая деталь — это не просто «жив или нет». Бот может технически работать, но находиться в деградированном состоянии: обрабатывать задачи в 10 раз медленнее, накапливать очередь, получать повторяющиеся ошибки. Health check позволяет детектировать такие состояния до того, как они превратятся в полный отказ.

Уровень 2: Watchdog процесс

Watchdog — это независимый Python-процесс, который каждые 30 секунд опрашивает все зарегистрированные боты через их /health endpoints. Он живёт в отдельном контейнере и намеренно изолирован от остальной системы: если упадёт любой из ботов, watchdog продолжит работать.

Логика watchdog простая, но точная:

  • Нет ответа на /health в течение 30 секунд — пометить как «не отвечает»
  • Не отвечает 2 минуты подряд — инициировать автоперезапуск
  • Статус «critical» — немедленно инициировать диагностику
  • Статус «degraded» три проверки подряд — создать задачу в очереди на мягкое восстановление
  • Ошибок за час больше порога — активировать circuit breaker для проблемного API

Watchdog ведёт историю состояний каждого бота. Это позволяет отличить транзиентную ошибку от системного сбоя. Один таймаут — не повод для паники. Пять подряд — уже паттерн.

Уровень 3: Circuit breaker

Circuit breaker — паттерн из мира распределённых систем, который предотвращает каскадные отказы. Принцип прост: если внешний API возвращает ошибки слишком часто, мы временно прекращаем к нему обращаться.

У каждого circuit breaker три состояния:

  • Closed (нормальная работа) — запросы проходят свободно, ошибки считаются
  • Open (отключено) — запросы блокируются, возвращается заранее заготовленный fallback ответ. Открывается автоматически после 5 ошибок подряд или если процент ошибок превысил 50% за последние 60 секунд
  • Half-open (тестирование) — через 30 секунд после открытия пропускается один тестовый запрос. Если успешный — breaker закрывается. Если нет — снова открывается

Это особенно важно для работы с внешними API вроде Telegram Bot API, Google Sheets API, eZee PMS. Когда у них происходит деградация сервиса, боты без circuit breaker начинают накапливать очереди неудавшихся запросов, загружают CPU, могут вызвать собственный OOM. С circuit breaker они просто «замирают» до восстановления внешнего сервиса.

Уровень 4: Exponential backoff

Когда бот получает временную ошибку (сеть мигнула, API вернул 503), он не должен немедленно повторять запрос. Немедленный повтор только нагружает уже перегруженный сервис. Exponential backoff — это стратегия нарастающих пауз между попытками.

Первая попытка неудачна — ждём 1 секунду. Вторая неудача — 2 секунды. Третья — 4. Четвёртая — 8. И так до максимума в 5 минут. Добавляем случайный jitter (±20% от паузы), чтобы множество ботов не синхронизировались в «шторм» одновременных повторов.

После шести неудачных попыток задача считается не восстановимой через backoff — передаётся на следующий уровень: полный перезапуск контейнера.

Инфраструктура: Docker, supervisord и systemd

Self-healing на уровне кода — это хорошо, но недостаточно. Нужна поддержка на уровне инфраструктуры. Мы используем трёхуровневый стек автоматического восстановления.

Docker restart policies

Первый и самый простой уровень — политики перезапуска Docker контейнеров. Каждый бот запускается с политикой --restart=unless-stopped. Это значит: если процесс в контейнере завершился с ненулевым кодом, Docker автоматически перезапускает контейнер через небольшую паузу (до 10 секунд).

Docker делает это быстро и без участия watchdog. Большинство «упал и встал» инцидентов решаются именно здесь — за 5-10 секунд, полностью незаметно.

Supervisord внутри контейнеров

Для ботов с несколькими внутренними процессами используем supervisord. Например, бот-транслятор WhatsApp запускает одновременно: основной процесс обработки сообщений, внутренний веб-сервер для /health, воркер очереди задач. Supervisord следит за всеми тремя, перезапускает упавший компонент независимо от остальных.

Это позволяет избежать «перегрузки с пушки»: если упал только веб-сервер /health, не нужно перезапускать весь контейнер и терять состояние основного процесса.

systemd на хост-машине

Третий уровень — systemd сервисы на хост-машине. Сам Docker daemon и watchdog-процесс управляются через systemd с автозапуском. Если хост-машина перезагрузилась (обновление ядра, аварийная остановка питания), systemd автоматически поднимет Docker и все контейнеры в правильном порядке, соблюдая зависимости между сервисами.

Кастомный Python watchdog

Поверх всего этого работает наш Python watchdog — он делает то, что не умеют стандартные инструменты: принимает решения на основе бизнес-логики. Стандартный Docker restart просто перезапустит упавший контейнер. Watchdog понимает: если eZee Scraper упал одновременно с PostgreSQL, возможно, проблема в базе данных — нужно сначала восстановить БД, а потом Scraper.

Watchdog хранит граф зависимостей между сервисами и применяет его при восстановлении. Это предотвращает ситуацию, когда сервис перезапускается успешно, но сразу падает снова из-за недоступной зависимости.

Как работает автоматическое восстановление: сценарии

Разберём конкретные сценарии, которые система обрабатывает автоматически.

Сценарий 1: бот завис, не отвечает на health check

Watchdog фиксирует отсутствие ответа на /health. Через 30 секунд — повторная проверка. Ещё раз через 30. После 4 неудачных проверок (2 минуты молчания) watchdog:

  • Снимает метрику: фиксирует в базе факт зависания с временной меткой и последним известным состоянием
  • Посылает SIGTERM процессу — корректное завершение. Ждёт 10 секунд
  • Если не завершился — SIGKILL. Жёсткое убийство
  • Docker restart policy поднимает контейнер заново
  • Watchdog ждёт 30 секунд и проверяет /health нового экземпляра
  • Если всё ок — инцидент закрыт, в Telegram приходит тихое уведомление: «eZee Scraper: автовосстановлен в 03:47»

Сценарий 2: протухший OAuth токен

Google Sheets API начинает возвращать 401 Unauthorized. Circuit breaker фиксирует серию ошибок. Watchdog определяет паттерн: ошибки аутентификации, не сетевые проблемы. Запускает специализированный recovery action:

  • Обращается к vault за свежими credentials
  • Запускает процедуру обновления токена через refresh token
  • Записывает новый токен в защищённое хранилище
  • Посылает боту сигнал перечитать конфигурацию (без полного перезапуска)
  • Проверяет что следующий запрос к Sheets прошёл успешно

Весь процесс занимает около 20 секунд. Пользователь не видит ничего — синхронизация просто пропускает один цикл и продолжает работать.

Сценарий 3: переполнение диска

Бот начинает писать ошибки OSError: [Errno 28] No space left on device. Health check возвращает статус «critical» с кодом причины «disk_full». Watchdog:

  • Запускает скрипт очистки: удаляет логи старше 7 дней, временные файлы, Docker images без тегов
  • Сжимает архивные логи (gzip)
  • Проверяет свободное место. Если стало достаточно — бот продолжает работу
  • Если после очистки всё равно мало места — эскалирует: отправляет алерт с полным отчётом по использованию диска

Сценарий 4: бесконечный цикл, CPU 100%

Watchdog мониторит не только HTTP endpoints, но и системные метрики контейнеров через Docker stats API. Если CPU контейнера держится выше 90% более 5 минут при нулевом прогрессе задач (очередь не уменьшается, нет новых heartbeat в логах) — это признак зависания в цикле.

В этом случае watchdog собирает stacktrace (py-spy для Python процессов), сохраняет его в базу для последующего анализа, затем перезапускает контейнер. Stacktrace позволяет разработчику потом разобраться, на каком месте в коде бот завис — без необходимости воспроизводить проблему вручную.

Система оповещений: умные алерты вместо спама

Классическая проблема мониторинга — alert fatigue. Когда алертов слишком много, люди перестают на них реагировать. Мы прошли через это: в один период система слала по 30-40 сообщений в день, и я начал их игнорировать.

Теперь система оповещений работает по принципу «молчи, пока не нужна помощь»:

Тихие восстановления

Если система сама справилась с проблемой менее чем за 3 минуты — никакого уведомления. Только запись в лог. Я могу посмотреть дайджест восстановлений раз в день, если интересно.

Мягкие уведомления

Если восстановление заняло от 3 до 15 минут, или потребовалось нестандартное действие (обновление токена, очистка диска) — приходит краткое сообщение в Telegram. Одна строка: что случилось, что сделано, сколько заняло.

Критические алерты с полным контекстом

Если система не смогла восстановиться за 15 минут, или проблема повторяется более трёх раз за час, или произошло что-то, чего watchdog не умеет чинить — приходит развёрнутый алерт:

[CRITICAL] WhatsApp Транслятор — требуется вмешательство

Инцидент начался: 03:47 UTC+8

Попыток восстановления: 3

Последняя ошибка: ConnectionResetError([Errno 104] Connection reset by peer)

Последний успешный heartbeat: 01:23 UTC+8

Stacktrace: [ссылка на файл]

Последние 50 строк лога: [вставлено прямо в сообщение]

Предполагаемая причина: смена IP Bali ISP (исторический паттерн)

Ключевая деталь последнего пункта: watchdog анализирует исторические данные и пытается угадать причину. Не всегда правильно, но часто помогает сократить время диагностики с 20 минут до 2.

Кейс: WhatsApp транслятор и путь от трёх дней до нуля падений

WhatsApp транслятор — бот, который принимает сообщения от клиентов на разных языках (русский, английский, индонезийский), переводит и маршрутизирует менеджерам. Критически важный сервис: если он падает, коммуникация с клиентами разрывается.

До внедрения self-healing бот падал примерно раз в три дня. Иногда через два дня, иногда через четыре — но паттерн был стабильный. Средний даунтайм до восстановления — 45 минут (от падения до момента, когда я замечал алерт, захожу на сервер, разбираюсь, перезапускаю).

Диагностика корневой причины

Первым делом с помощью watchdog мы собрали статистику за месяц. Оказалось:

  • 80% падений происходили между 01:00 и 04:00 местного времени
  • Ошибка всегда одна и та же: ConnectionResetError
  • После перезапуска бот работал нормально ровно до следующей ночи

Версия: балийские ISP проводят ночное обслуживание сети, из-за чего меняется внешний IP сервера. Сессия WhatsApp Web привязана к IP, и при его смене сбрасывается. Бот не умел переподключаться — просто падал с Connection Reset.

Два уровня защиты

Решение оказалось двухуровневым. Первое: добавили в бот логику автоматического переподключения с exponential backoff — при потере соединения пробуем снова через 1, 2, 4, 8, 16 секунд. Второе: добавили watchdog правило специально для этого бота — если видим ConnectionResetError три раза за час, принудительно пересоздаём WhatsApp сессию полностью.

Результат: за следующие шесть месяцев бот не требовал ни одного ручного вмешательства. Watchdog автоматически обрабатывает ночные reconnect в среднем два раза в неделю. Пользователи не замечают ничего — задержка обработки сообщений в момент reconnect составляет 8-12 секунд.

Что система НЕ умеет делать: границы автоматизации

Честный разговор о self-healing невозможен без обсуждения ограничений. Есть класс проблем, которые система намеренно эскалирует человеку, не пытаясь починить самостоятельно.

  • Ошибки бизнес-логики. Если бот неправильно считает доходы или неверно классифицирует лиды — это не техническая поломка, а содержательная ошибка. Автоматическая «починка» может усугубить ситуацию.
  • Глобальные отказы внешних сервисов. Если Telegram API лежит для всего мира — watchdog это видит (circuit breaker открывается для всех ботов одновременно) и просто ждёт, периодически проверяя доступность. Ничего починить здесь нельзя.
  • Аппаратные проблемы сервера. Если умер диск или сгорела сетевая карта — нужен физический доступ. Watchdog присылает критический алерт и останавливается.
  • Проблемы безопасности. Если в логах появляются признаки компрометации (неожиданные запросы, изменения файлов) — система не пытается «починить» это самостоятельно. Блокирует подозрительные процессы и ждёт человека.
  • Изменения в API провайдеров. Если Booking.com поменял структуру ответа и парсер сломался — нужна правка кода, а не перезапуск контейнера.

Это осознанные границы. Попытка автоматизировать всё без исключений рано или поздно приведёт к ситуации, когда система «чинит» то, что не сломано, или маскирует реальную проблему. Self-healing должен быть умным, а не агрессивным.

Результаты: шесть месяцев в цифрах

Система работает с октября 2025 года. По состоянию на апрель 2026:

  • 247 автоматических восстановлений — это все инциденты, которые система обработала без участия человека
  • 0 ручных вмешательств ночью — ни одного раза я не вставал ночью чинить упавшего бота за последние 6 месяцев
  • Среднее время восстановления: 47 секунд — против 45 минут при ручном подходе. Ускорение в 57 раз
  • 14 эскалаций к человеку — это проблемы, которые система правильно определила как «требует человека». Ни одной ложной тревоги
  • Экономия: ~120 часов разработчика — оценка времени, которое раньше тратилось на ручную диагностику и перезапуск сервисов
  • Uptime ботов: 99.6% — с учётом времени плановых обновлений

Самая неожиданная польза оказалась не в числах, а в психологии. Я перестал постоянно думать «а вдруг что-то упало прямо сейчас». Это освобождает ментальную нагрузку, которая раньше была фоновым шумом каждого дня.

Как начать внедрять self-healing: практические шаги

Если вы хотите внедрить что-то подобное, не нужно строить всё сразу. Вот последовательность, которую я бы рекомендовал:

Шаг 1: Health check endpoints (1-2 дня)

Начните с простого: добавьте /health endpoint в каждый бот. Минимальный вариант — просто возвращать HTTP 200, если процесс жив. Это уже позволяет внешнему инструменту понять, что бот работает. Постепенно обогащайте: добавляйте метрики памяти, времени последнего успешного действия, счётчик ошибок.

Шаг 2: Docker restart policies (1 час)

Убедитесь, что все ваши боты запущены с restart: unless-stopped в docker-compose.yml. Это самый дешёвый и эффективный первый уровень защиты. Большинство «упал и забыл» случаев решается именно этим.

Шаг 3: Простой watchdog (3-5 дней)

Напишите скрипт, который каждую минуту опрашивает /health всех ботов и при отсутствии ответа отправляет алерт в Telegram. Это даст вам видимость без сложной автоматики. Постепенно добавляйте автодействия: сначала только уведомления, потом — автоперезапуск для самых понятных случаев.

Шаг 4: Exponential backoff в коде ботов (2-3 дня)

Пройдитесь по коду каждого бота и добавьте retry логику с backoff для всех внешних вызовов: HTTP запросы, обращения к БД, файловые операции. В Python для этого есть библиотека tenacity, которая делает это элегантно через декораторы.

Шаг 5: Circuit breaker (1 неделя)

Добавьте circuit breaker для критически важных внешних API. Для Python есть библиотека pybreaker. Начните с самых нестабильных интеграций — там, где ошибки API происходят чаще всего.

Каждый шаг даёт ощутимый результат даже без следующего. Не нужно внедрять всё сразу — начните с малого и итерируйте.

Также важно учитывать, что self-healing система — это живой проект, а не разовая настройка. Когда у вас появляются новые боты или интеграции, нужно добавлять их в watchdog, настраивать для них специфические recovery actions, собирать исторические паттерны отказов. Мы поддерживаем «книгу восстановлений» — документ, где описан каждый тип инцидента и как система его обрабатывает. Это помогает при онбординге новых разработчиков и при добавлении новых сервисов.

Если вам интересно посмотреть, как система мониторинга помогает выявлять проблемы до того, как они становятся критическими — читайте о нашем опыте построения системы мониторинга AI агентов. А о том, как бороться со спамом уведомлений от множества ботов — в статье про debounce для AI ассистента.

Читайте также

Подписаться на блог в Telegram

Читайте свежие кейсы об AI-автоматизации, системной архитектуре и масштабировании бизнеса.

Подписаться