Если вы когда-нибудь открывали утром почту и видели несколько десятков писем от Sentry с одинаковыми stack trace, то наверняка сталкивались с Alert Fatigue — усталостью от алертов.
В какой‑то момент таких уведомлений становится настолько много, что мозг начинает автоматически их игнорировать. Поэтому становится легко пропустить действительно критичную проблему на продакшене.
В этой статье расскажу, как мы решили проблему Alert Fatigue в Sentry и превратили десятки ежедневных писем с ошибками в один понятный утренний дайджест. Покажу, почему для MVP выбрали Google Apps Script вместо полноценного сервиса, с какими ограничениями столкнулись при работе с Gemini, почему в итоге остановились на YandexGPT и как всего за один вечер собрали полностью бессерверную автоматизацию.
А еще поделюсь реальными цифрами по стоимости, производительности и расскажу, где такой подход помогает экономить время команды, а где пока остается лишь удобным компромиссом.
Если вы когда-нибудь открывали утром почту и видели несколько десятков писем от Sentry с одинаковыми stack trace, то наверняка сталкивались с классическим Alert Fatigue — усталостью от алертов.
Дано
Проект, на котором от Sentry приходит 10-20 писем ежедневно. На обработку уходит 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.