Привет, коллеги! Меня зовут Василь Хамидуллин, и я тестировщик в компании fuse8.

В этой статье расскажу, как мы решили проблему Alert Fatigue в Sentry и превратили десятки ежедневных писем с ошибками в один понятный утренний дайджест. Покажу, почему для MVP выбрали Google Apps Script вместо полноценного сервиса, с какими ограничениями столкнулись при работе с Gemini, почему в итоге остановились на YandexGPT и как всего за один вечер собрали полностью бессерверную автоматизацию.
А еще поделюсь реальными цифрами по стоимости, производительности и расскажу, где такой подход помогает экономить время команды, а где пока остается лишь удобным компромиссом.
Если вы когда-нибудь открывали утром почту и видели несколько десятков писем от Sentry с одинаковыми stack trace, то наверняка сталкивались с классическим Alert Fatigue — усталостью от алертов.
В какой-то момент таких уведомлений становится настолько много, что мозг начинает автоматически их игнорировать. Поэтому становится легко пропустить действительно критичную проблему на продакшене.
У нас ситуация выглядела примерно так:
-
10–20 писем от Sentry ежедневно;
-
15–20 минут ручного просмотра каждое утро;
-
дублирующиеся ошибки, разбросанные по разным проектам.
Хотелось получать не поток уведомлений, а короткую человеческую сводку:
-
что сломалось;
-
насколько это критично;
-
сколько раз повторилось;
-
где смотреть подробнее.
В итоге за один вечер появился небольшой сервис на Google Apps Script и YandexGPT, который каждое утро присылает единый дайджест по всем ошибкам за последние сутки.
Сейчас чтение такого отчета занимает около двух минут.
Архитектура решения
Схема получилась максимально простой и полностью бессерверной:

Google Apps Script по расписанию собирает письма от Sentry за последние 24 часа, извлекает из них полезную информацию, отправляет её в YandexGPT и формирует итоговый отчет.
Никаких серверов, Docker, Kubernetes и отдельных баз данных.
Почему не Sentry API?
Это первый вопрос, который возникает при взгляде на архитектуру.
Конечно, у Sentry есть API, и через него можно получать гораздо больше информации.
Но для MVP почта оказалась самым быстрым вариантом:
-
алерты уже приходили в Gmail;
-
фильтры были настроены;
-
не требовалось отдельно хранить токены Sentry;
-
интеграцию удалось собрать буквально за вечер.
Если бы проект развивался дальше, логичным следующим шагом был бы переход на API.
Почему вообще LLM?
Действительно, отчасти можно было бы обойтись обычным кодом, не прибегая к использованию нейронки. Например, количество ошибок, ссылки на инциденты и статистику повторений легко собрать обычными скриптами.
Проблема здесь в том, что ошибки разных сервисов выглядят по-разному:
-
Java stack trace;
-
Node.js исключения;
-
SQL ошибки;
-
сетевые таймауты;
-
ошибки сторонних API.
Чтобы привести всё это к единому формату, пришлось бы писать множество отдельных правил обработки для каждого типа ошибок и постоянно поддерживать их в актуальном состоянии.
LLM помогает привести все к общему знаменателю:
-
читает сырой лог;
-
выделяет суть проблемы;
-
группирует похожие ошибки;
-
формирует единый формат отчета.
Фактически нейросеть превращает технический шум в читаемый утренний обзор.
Попытка №1: Gemini
Поскольку код запускался внутри Google Apps Script, логичным выбором выглядел Gemini. В ней можно было использовать бесплатный тариф, быстро настроить интеграцию и нет необходимости поднимать дополнительную инфраструктуру.
Но на практике появились проблемы:
-
Сначала API начал возвращать 404 для моделей, которые фигурировали в документации. Оказалось, что привычные модели вроде gemini-1.5-flash в нашем регионе недоступны или переименованы. Пришлось писать отдельный скрипт-чекер, который вытащил список реально доступных моделей (в нашем случае сработала gemini-2.0-flash).
-
Затем начали появляться ошибки 503 в периоды высокой нагрузки. Только мы обрадовались, как Google ответил: «This model is currently experiencing high demand». Бесплатный тариф означает, что ваши запросы обрабатываются по остаточному принципу. Для ежедневного дайджеста, который должен работать как часы, это не годится.
-
А финальным аргументом стали ошибки 429 и фактически недоступные квоты для нашего аккаунта. Как выяснилось, для многих аккаунтов (особенно без привязанной иностранной карты и в определенных гео) Google просто обнуляет бесплатные квоты.
Поэтому бесплатные API для надежной автоматизации — это утопия. Нужно платное API, которое можно легально и без костылей оплатить российской картой.
Переезд на YandexGPT
В итоге выбор пал на YandexGPT через Yandex Cloud. Тут и оплата российскими картами, и стартовый грант на 4000₽, и понятный API.
Настройка сводится к трём шагам:
-
Создаем сервисный аккаунт.
-
Выдаем ему роль ai.languageModels.user.
-
Генерируем API-ключ.
После этого можно вызывать модель напрямую из Google Apps Script через UrlFetchApp.
Инженерия: как мы формируем контекст
Самая важная часть решения — подготовка данных. Скармливать модели десятки HTML-писем напрямую нельзя, потому что окно контекста быстро закончится, запросы станут дороже, а ответы — хуже.
Поэтому для каждого письма мы извлекаем только полезную информацию:
-
тему письма;
-
ссылку на Issue в Sentry;
-
текст ошибки;
-
время получения.
Логи дополнительно обрезаются до 4000 символов.
Фрагмент сбора данных выглядит так:
function collectEmailTexts_(threads) { const emails = []; threads.forEach(thread => { thread.getMessages().forEach(message => { const rawHtml = message.getBody(); emails.push({ subject: message.getSubject(), link: extractSentryLink_(rawHtml), body: truncate_(message.getPlainBody(), 4000), }); }); }); return emails;}
После этого все письма агрегируются в единый пакет и отправляются в модель одним запросом.
Интеграция в Google Apps Script сводится к одному UrlFetchApp.fetch. Самое важное здесь — правильный системный промпт. Чтобы ИИ не лил воду, а давал четкую структуру, мы использовали такой шаблон:
const systemPrompt = [ 'Ты опытный техлид. Проанализируй письма об ошибках из Sentry и составь структурированную сводку на русском языке.', 'ВАЖНО: Пиши обычным текстом, строго без использования Markdown-разметки (не используй символы ** для выделения жирным).', 'Для каждой уникальной ошибки выведи только следующие пункты в формате "Ключ: Значение":', 'Название: (ОБЯЗАТЕЛЬНО скопируй оригинальный заголовок из темы письма)', 'Ссылка: (ОБЯЗАТЕЛЬНО выведи URL из поля Link)', 'Источник: (Определи сервис и окружение на основе названия проекта, например: Бэкенд Прод, Фронтенд Стейдж)', 'Суть: (краткое описание проблемы простыми словами, 1-2 предложения)', 'Критичность: CRITICAL / HIGH / MEDIUM / LOW с кратким обоснованием', 'Опирайся только на предоставленные тексты писем. Никаких выводов и рекомендаций в конце писать не нужно.' ].join('\n');
Теперь каждое утро в 09:00 Google Apps Script по триггеру идет в Gmail, собирает все письма от getsentry.com за последние 24 часа, упаковывает их в один JSON и отправляет в YandexGPT.
Почему один запрос лучше пятидесяти
Когда проектируешь подобную автоматизацию, велик соблазн сделать архитектуру «в лоб»: пришло письмо ➔ триггернулся скрипт ➔ отправили запрос в LLM. Это инженерное преступление по трем причинам:
1. Экономия токенов
Системный промпт занимает определенное количество токенов. Если отправить 50 писем отдельно, мы оплатим этот промпт 50 раз. При пакетной обработке — только один.
2. Борьба с дубликатами
Очень часто одна и та же ошибка повторяется десятки раз. Когда модель видит весь массив целиком, она может написать: «Ошибка SequelizeUniqueConstraintError повторилась 5 раз». А не будет генерировать 5 одинаковых отчетов.
3. Меньше запросов
Меньше вероятность столкнуться с лимитами API и таймаутами выполнения Google Apps Script.
Экономика и производительность: реальные цифры
Заглянем в реальный биллинг Yandex AI Studio нашей работающей системы и в логи выполнения Apps Script.
Скорость работы. Судя по логам выполнения Google Apps Script, полный цикл (парсинг писем, отправка запроса в YandexGPT, получение ответа и отправка финального email) занимает от 1 до 13 секунд. Это значит, что у нас колоссальный запас до 6-минутного хард-лимита Google Apps Script.

Стоимость. В день тестирования (15 июня) скрипт обработал пакет из 8 писем. Весь этот объем обошелся суммарно в 8,51 ₽. Из них 6,80 ₽ ушло на огромный входящий контекст, а 1,71 ₽ — на генерацию ответа. В спокойные дни, когда прилетает 1–2 варнинга, цена падает до 1,5–2 ₽ за сутки.

Итоговый прогноз на месяц: Реальный месяц ежедневного мониторинга бэкенда, фронтенда и других сервисов будет обходиться всего в 150–240 рублей.
Если сравнить это с временем разработчиков и тестировщиков, которые ежедневно просматривают десятки уведомлений вручную, экономика выглядит вполне разумной.
Результат
Раньше утро выглядело примерно так:

Теперь приходит одно письмо.

Чтобы запустить процесс, достаточно настроить ежедневный триггер:
function setupDailyTrigger() { ScriptApp.newTrigger('runSentryDigest') .timeBased() .everyDays(1) .atHour(9) .create();}
После этого система работает полностью автономно.
Что осталось нерешенным
Конечно, решение не идеальное. Во-первых, лимиты контекста все еще ограничены: очень длинные stack trace при массовом падении (например, 200 уникальных писем за ночь) могут съесть все окно контекста LLM. Во-вторых, модель нет-нет, да и галлюцинирует: иногда ИИ может выдать неверную оценку критичности, если текст ошибки звучит страшно, но по факту в вашей бизнес-логике это обычный warning.
Дальнейшее развитие
Следующими шагами могут стать:
-
переход с почты на Sentry API;
-
автоматическая группировка по проектам;
-
публикация дайджеста в Telegram или Slack;
-
автоматическое создание задач в Jira.
Итоги
Получилось решение, которое:
-
уменьшило количество уведомлений с десятков до одного письма;
-
экономит время на ежедневном просмотре алертов;
-
работает полностью без серверов;
-
обходится в считанные рубли в день.
Самое приятное, что для запуска не понадобилась сложная инфраструктура: хватило Gmail, Google Apps Script и нескольких вызовов YandexGPT.
Если у вас есть свои способы борьбы с Alert Fatigue или опыт автоматизации Sentry, будет интересно обсудить их в комментариях.
ссылка на оригинал статьи https://habr.com/ru/articles/1052262/