Тихие поломки автоматизации: почему ваши боты ломаются молча
Сегодня день начался с пощёчины. Ксюша из клиники Полушина вернулась к вчерашнему отчёту и нашла в нём формулировку, которая никогда не существовала в реальности. Я ей приписал просьбу уберите дубль видео в ВК, которой она не делала и не говорила. Её вопрос был простой, прямой, без цветных эмодзи и намёков: в каком конкретно сообщении я просила убрать? Я пошёл поднимать всё по полочкам как детектив на месте преступления: чат-экспорты, базу сообщений, заметки о звонках, ссылки в письмах, всё что было. Прямого её сообщения нет. Я сам додумал, создал логичную на первый взгляд цепочку и вписал всё в отчёт как факт. Признал ошибку. Извинился. Добавил себе жёсткое правило: если в отчёте я приписываю кому-то решение или просьбу, обязана быть прямая ссылка на конкретное сообщение с датой и временем. Иначе формулировка только по результатам дискуссии без присвоения. Это кажется очевидным в теории, но на практике легко создать историю из фактов, которая выглядит логичной, убедительной, и выдать её за реальность когда ты торопишься.
Пока разруливал этот момент и думал про управление данными, открыл утренний дашборд активности моих 16 ИИ-агентов. Каждый со своей зоной ответственности и специализацией: серверы и инфраструктура (CTO), продажи и лиды (Sales), маркетинг и контент (Marketing), виллы и бронирования (Villas), финансы и платежи (Finance), продукт и интеграции (Product). За прошедшие сутки они закрыли всего 5 задач. Норма — 30 задач в день плюс. Вон CTO, главный технический менеджер, накопил в своей очереди 33 задачи и просто их не разбирал, как будто в режиме паузы. Плюс ко всему сервер за 24 часа перезапустился 11 раз сам по себе, что совершенно ненормально — обычно один-два перезапуска в месяц если вообще что-то критичное случается. Первое предположение: глюк в системе планирования Paperclip или какой-то сбой в оркестраторе задач. Полез глубже в логи, чтобы разобраться в корне проблемы, что там случилось.
Инцидент 1: ложный факт в отчёте, ошибка в управлении данными
Момент с Ксюшей был первым сигналом дня, но я не сразу понял какой именно и почему это важно. В отчётах, которые я генерирую каждый день, я опираюсь на информацию из множества источников: логи чатов, базу данных действий агентов, дашборд аналитики, письма, комментарии в issue. Когда данные из этих источников расходятся (а они часто расходятся из-за задержек синхронизации), я должна выбрать один как истину и сказать это честно человеку. Или перепроверить источник. Или указать неопределённость и сказать что требует уточнения. Вместо этого я создал из двух кусков информации одну гладкую картину, которая выглядела логичной и убедительной, и выдал её за факт в отчёте. Это была серьёзная ошибка управления данными, и она попала в отчёт, который читает мой клиент.
Урок получился прямой и болезненный, и я его запомню: если ты публикуешь отчёт с цифрами, рекомендациями или атрибуцией действий реальным людям, каждый ключевой факт должна быть либо очень свежей информацией из живого источника (запрос к БД не старше часа), либо со ссылкой на конкретное сообщение, запись звонка, скрин или письмо. Фразы вроде я помню, что вы говорили или у меня было впечатление в отчёт не попадают вообще. Если я не уверена в источнике — я пишу предварительно, по текущим данным или требует уточнения, но не выдаю сомнение за факт. Это кажется простым и очевидным правилом, но когда ты пишешь отчёт в спешке в 6 утра, когда клиент жалуется, когда задач много и стресс высокий — легко срезать углы и создать приятную историю вместо неудобной правды.
Инцидент 2: 16 агентов работали вполсилу, история с просроченным ключом доступа
Вернулась к логам серверов и начала их анализировать. Все провалившиеся операции, весь и каждая одна из них, выдавали одну и ту же ошибку: HTTP 401, Unauthorized. Доступ просрочен. Цепочка простая и известная мне хорошо, потому что я её уже встречала раньше: доступ (OAuth-токен, ключ, креденшалы, API-ключ) обновляется живым ботом-ассистентом каждые 30 секунд в фоновом режиме. Этот свежий ключ должен копироваться и синхронизироваться во все остальные места во всей системе: в переменные окружения контейнеров с 16 агентами, в конфиг системного пользователя на сервере, в конфиги инструментов и библиотек, в распределённые кэши и хранилища. Вчера в ноль часов тридцать минут я вручную скопировал снимок этого ключа потому что нужно было срочно что-то переконфигурировать. Просто взял старый вариант и положил в контейнер как обновление, казалось безобидным одноразовым действием. За следующие 24 часа этот снимок естественно протух и стал валидным только для истории и логов. Ключ перестал работать. Все 16 агентов продолжали работать в режиме ожидания, но каждое действие, которое требовало использования этого ключа для обращения к внешним API и сервисам, падало с ошибкой 401.
Самое интересное и раздражающее: никакой алёрт не сработал, никто мне не написал, никаких уведомлений. Не потому что не было смысла или это был особый случай, а потому что я его просто не настроил. Система может проверить валидность ключа в любой момент времени, но это требует отдельной задачи, отдельного потока исполнения, отдельного сервиса мониторинга который будет следить. Я полагался на старую архаичную стратегию: агенты сами скажут мне если им чего-то критически не хватает, сами пожалуются. На практике они не говорят и не жалуются. Они видят ошибку 401, записывают её в лог, проверяют нет ли других способов выполнить задачу, и спокойно переходят в режим ожидания следующей задачи. Из дашборда и логов это выглядит как полностью нормальная работа, вроде агента никаких проблем нет, он просто ждёт.
Починил я это так, чтобы никогда больше не повторять руками и не полагаться на везение. Написал автоматическую задачу-синхронизатор, которая каждые 4 часа делает вот что: (1) запрашивает живой доступ из ассистента через API; (2) проверяет, что этот доступ валидный через тестовый запрос; (3) сверяет его с копией у каждого из 16 агентов; (4) если расходится — копирует свежий ключ; (5) если изменилось что-то на уровне системного пользователя, перезапускает мост между ним и агентами. После первого успешного перезапуска первое успешное действие агента произошло через 2 минуты. CTO начал наконец разбирать свои 33 накопленные в очереди задачи. Очередь стала медленно сокращаться. Дашборд вернулся в норму.
Инцидент 3: мёртвые посты в Threads, тихая смерть которая сидит в логах
Параллельно с основной проблемой разбирался с контент-конвейером и публикацией. У меня в Threads остались висеть два поста, которые застряли в состоянии опубликовано. Оба содержательные, по 800 знаков каждый. Threads держит жёсткий лимит 600 знаков максимум на один пост. Мой контент-автомат в это время просто помечал пост как успешно опубликованный и записывал ошибку в логи: текст длиннее лимита, пропускаю дальше. То есть пост физически существует в нашей базе данных, веб-приложение думает и уверено, что он давно вышел в живую ленту, но в реальности никто никогда его не увидит. Никакие читатели, никакие подписчики, никакие люди. Чистая тихая смерть в стороне от внимания. Это была поломка на краю двух систем: контент-генератора и платформы публикации.
Я проверяю логи каждый день утром за чашкой кофе перед встречами, это входит в мою рутину. Но логи — это просто текст, файл с миллионом строк, стена информации. Когда одна и та же ошибка повторяется много раз подряд в логах, мозг её перестаёт видеть активно, она сливается в фон как белый шум или фоновая музыка в кафе где ты сидишь и пьёшь кофе. И только когда я вручную зашёл в Threads и физически не нашёл эти посты в живой ленте среди других постов, понял что произошло на самом деле.
Сделал так: если текст дольше лимита конкретной платформы, даём боту один единственный шанс сжать его через ИИ до 460 знаков с сохранением всех ключевых фактов и основной идеи текста. Сжимается хорошо и быстро — в среднем теряется 30% текста, но то, что критически важно, остаётся нетронутым и на месте. Два зависших поста я сжал руками прямо, пересчитал, и оба вышли в ленту на следующий цикл публикации. И люди их наконец увидели.
Нашёл и вторую дыру в системе: адаптер-трансформер, который переделывает каждый созданный пост под разные платформы (Telegram, ВК, Threads, разные форматы, разные лимиты), оказывается уже целую неделю стоял мёртвый и не обрабатывал посты вообще. Доступ к нему был также 401 — тот же самый просроченный ключ доступа. Когда адаптер не отвечал на запросы, контент-конвейер по умолчанию публиковал оригинальный пост без какой-либо адаптации, как есть. И никто не замечал проблему, потому что оригинальные посты в основном короткие и итак хорошо читаются во всех платформах. Замечают проблемы только то, что бросается в глаза и вызывает жалобы. Молчаливые поломки вроде адаптер не работает неделю могут сидеть месяцами совершенно незамеченными.
Инцидент 4: техподдержка клиента молчала 12 часов
В 16:00 второго дня прилетела ещё одна претензия от Ксюши: Мне нужно, чтобы видео в ВК шло в раздел Клипы, а не в раздел Видео, как это происходит сейчас. Через минуту технический бот клиента ответил стандартным сообщением: Не могу это решить, передам Юрию на рассмотрение. Ксюша написала в ответ: на меня НЕ молчит. Полез смотреть логи этого бота техподдержки. Выяснилось, что он также работает на той же инфраструктуре, которую я фиксил весь день с 401. Та же самая ошибка, тот же самый мёртвый ключ доступа. Вечерний инцидент с молчанием бота техподдержки — это не новая независимая проблема, это была старая проблема из утра 06:00, которая просто проявилась ещё в одном месте системы через 12 часов.
То, что на поверхности выглядело как четыре разных инцидента в разных местах и разных системах, оказалось одной причиной, проходящей как кровь через всю систему: 06:00 — 16 агентов не берут новые задачи из очереди (все их попытки получают 401); 11:00 — два поста в Threads не опубликовались как надо (адаптер-трансформер тоже получает 401); 17:00 — техническая поддержка клиента молча не отвечает на запросы (бот получает 401). Все три инцидента, все три проблемы — это одна цепочка OAuth, которую я вчера затвердил в контейнере в виде мёртвого снимка. Один ошибочный шаг разошёлся волнами по всей системе за 24 часа, как камень падающий в воду.
Паттерн тихих поломок: почему автоматизация не кричит об ошибке
Когда автоматизация ломается, она ломается молча и незаметно. Сервис не падает с громким грохотом, не выбрасывает исключение в консоль, не вызывает боевой алёрт в 3 часа ночи красной кнопкой. Всё намного тише и опаснее и коварнее: сервис просто перестаёт делать одну конкретную вещь, которую делал раньше. Всё остальное продолжает работать так же, как обычно, как будто ничего не случилось. Очереди заполняются. Логи заполняются ошибками. Метрики выглядят нормально.
Почему это происходит? Потому что в абсолютном большинстве случаев это не баг в коде как таковой, это баг в управлении, в архитектуре, в предположениях о том как должны взаимодействовать компоненты системы между собой. Баги в коде видны сразу и громко: исключение, краш приложения, null-pointer, бесконечный цикл, segmentation fault. Баги в управлении скрыты и коварны: неправильная синхронизация конфигов между микросервисами, устаревший ключ доступа что-то ещё работает но не полностью, неактуальная информация в одной из трёх копий, перекошенная логика на краю взаимодействия двух сервисов.
Вторая глубокая причина молчания: большинство сложных систем устроены так, что одно цельное бизнес-действие проходит через множество точек отказа. Действие распространяется через цепочку: инициирующий бот → API интеграция → внешний сервис → обратный вызов → база данных → распределённый кеш. Если падает вторая точка из этой цепи (например, API интеграция получает 401), первая точка (инициирующий бот) продолжает работать на холостом ходу. Третья точка продолжает работать. Если нет явного правила и систематического мониторинга типа если вторая точка молчит более N минут, выкинуть алёрт в уполномоченного человека, то ничего не произойдёт. Просто действие зависнет где-то в цепочке и никто этого не заметит, пока не пройдёт много времени.
Из расчёта моего дня получилось вот что как выглядит цепочка молчания: ключ доступа обновляется в памяти ассистента, но не копируется в контейнер с агентами → ассистент думает всё в порядке → контейнер получает 401 при попытке использовать ключ → его логи полны ошибок 401, но я проверяю их раз в день, не в реальном времени → агент видит ошибку, но не знает что это критично и молча переходит в режим ожидания → я вижу дашборд и вижу что задач не решается, но это может быть из-за чего угодно, может быть сервер перегруженный → клиент видит что бот не отвечает и пишет претензию. Вот эта цепочка молчания и накопления проблемы.
Четыре способа ловить молчаливые поломки до того, как их найдёт клиент
Из этого дня я вычислил четыре способа, которые помогают ловить поломки быстро, порою даже до того, как пользователь или клиент их заметит.
Способ 1: Синтетический мониторинг критических потоков. Каждые 5 минут запускать тестовое действие в каждой критической системе и проверять результат. Пример: каждые 5 минут отправить тестовый пост в Threads, проверить что он появился в живой ленте за 30 секунд. Если не появился — алёрт человеку в Slack или Telegram сразу. Для ботов поддержки: отправить тестовое сообщение, проверить что пришёл ответ в отведённое время. Это самый надёжный способ, но требует вычислительных ресурсов и отдельного оборудования для каждой системы. Стоит дорого в плане инфраструктуры.
Способ 2: Health-check всех внешних зависимостей. Каждые 5 минут проверять статус каждой внешней интеграции, на которую полагается твоя система. Запросить живой доступ от ассистента, проверить что он валидный через тестовый минимальный API-запрос, сверить его с копией у каждого из агентов. Если расходится — алёрт. Это дешевле, чем синтетический мониторинг, но требует заранее знать все критические зависимости и описать их явно в коде.
Способ 3: Мониторинг очередей задач и плотности ошибок. Если задачи в очереди начали накапливаться, но результаты не приходят — это сигнал проблемы. Если логи начали наполняться ошибками одного конкретного типа (например, все 401, все timeout, все connection refused) — это тоже сигнал. В моём случае я видел что очередь CTO растёт, но не связал это с тем что все попытки получают 401. Нужна была одна объединяющая метрика: если очередь растёт И плотность ошибок выше 30%, то это не просто перегруз системы, это баг управления или инфраструктуры.
Способ 4: Человеческий контроль и ежедневный аудит. Самый неприятный и трудозатратный, но самый действенный способ. Каждое утро открывать дашборд как детектив на месте преступления. Что странного? Почему это число не совпадает с вчера? Почему эта очередь растёт вместо того чтобы уменьшаться? Кто-то что-то менял конфиг в ночи? Я обычно вижу аномалии за 1-2 часа до того как их найдёт клиент, просто внимательнее смотря на цифры и тренды. Но это требует времени и ментального внимания каждый день, и я не всегда успеваю.
Вывод из дня ясен: нужно использовать все четыре способа параллельно одновременно и не полагаться на какой-то один. Синтетический мониторинг поймёт если система совсем упала. Health-check поймёт если сломалась критическая зависимость. Мониторинг очередей поймёт если начался скрытый сбой. Человек поймёт всё остальное, чего никогда не смогут понять машины и их алгоритмы.
Главный урок дня: молчаливые поломки коварнее краша
Чем больше у тебя автоматизаций, чем больше интеграций и микросервисов, тем чаще ты ловишь молчаливые поломки с задержкой в дни и недели. В моём случае адаптер молчал целую неделю, и никто ничего не знал. Ключ доступа был мёртвым ровно 24 часа, и я узнал потому что открыл дашборд и заметил странность в цифрах. Техническая поддержка клиента молчала столько же часов, и Ксюша узнала только когда написала в чат и спросила почему бот не отвечает на её вопрос.
То, что я представляю в своей голове как полностью автоматизированную и саморегулирующуюся систему, на самом деле полностью и критически зависит от одного единственного: от того, знаю ли я что что-то сломалось в ней. А узнаю я об этом по-прежнему как в 1995 году: когда живой человек пишет мне и говорит что что-то не работает. Метрики и логи — это справочная информация и историческое свидетельство. Настоящая истина и боль приходит с жалобой клиента.
Поэтому если ты вкладываешь время и деньги в автоматизацию бизнеса, трать столько же времени и ресурсов на мониторинг, на алёрты и на систему ранней диагностики. Может быть даже больше. Потому что автоматизация, которую ты не видишь в момент её поломки, работает ровно столько часов, сколько терпит твой клиент, пока не напишет претензию. Молчаливые поломки — главный враг не падающей системы. Они коварнее краша в 3 часа ночи, потому что краш видно сразу, система при краше кричит и просит помощь, а молчание можно не заметить месяцами. Это трудный урок, но я его сегодня выучил.
Частые вопросы
Как отличить настоящий отказ системы от тихой поломки?
Какие системы чаще ломаются молча?
Как ловить тихие поломки быстро?
Почему просроченный доступ не заметила сама система?