
Салют, Хабр!
У GitHub есть GitHub Copilot Code Review, y GitVerse — GigaCode Agent… но мы не можем их использовать, так как это внешние решения, которым не хочется открывать свой код. Когда речь идёт об AI-агентах в разработке, путь большинства крупных компаний — создавать собственный велосипед с нужным количеством колёс и педалей.
Меня зовут Аврора, я занимаюсь инфраструктурой фронтенда в SberDevices. Для упрощения работы мы реализовали систему агентов фронтенда, где скомбинировали два подхода: агентный цикл, где модель получает цель и инструменты, и детерминированный workflow, когда она выполняет отдельные ограниченные задачи.
Сегодня расскажу о работе наших агентов: об ошибках фронтендеров по статистике AI-ревьюера, парсере для сужения контекста модели и функции системы, которая оказалась (внезапно) самой востребованной.
Проверка кода
AI-ревью автоматически стартует для любого merge request (MR); вместе с тем разработчик может ещё до реквеста запустить его и проверить свой код.
Мы не дообучали модель для AI-ревьюера — это модель GigaChat-2-Max «из коробки». Промпт задаёт его базовую роль: ты фронтенд-разработчик, проведи ревью кода, если заметил ошибки — опиши их. AI-ревьюер ориентирован на фронтенд-разработку на typescript, хотя ограничений по языкам и фреймворкам нет, код можно проверить на любом языке, который понимает модель (но получится так себе; под другие языки нужно доработать промпт и правила). Для удобства работы ему задано несколько специальных условий:
комментировать только конкретную строку, которую изменил;
объяснять проблему и предлагать, как исправить в коде;
указывать точный диапазон строк, чтобы комментарий прикреплялся точно к строке, где найдена проблема, а не ко всему merge request.
Сам AI-reviewer написан на TypeScript и NestJS. Взаимодействие с ним полностью серверное:

Технически AI-ревьюер — это не AI-агент. Его workflow задан кодом и выглядит так:
Когда в merge request запускают ревью, GitLab отправляет боту webhook; он также получает идентификаторы проекта и самого MR. Бот через GitLab API запрашивает git diff изменённых файлов.
Бот исключает из git diff служебные файлы, например, конфигурации, создаёт для каждого файла краткое описание и определяет, нужен ли ему подробный анализ. В зависимости от содержимого файл классифицируется как NEEDS_REVIEW («новый контент») или APPROVED («изменения в старом контенте»). Подробного анализа требуют только файлы, где код меняет логику или функциональность фичи/проекта.
Бот отправляет diff в GigaChat. LLM-клиент создаётся через библиотеку gigachat:
import GigaChat from 'gigachat'; // Создаём клиент для обращения к модели const llmClient = new GigaChat({ model: 'GigaChat-2-Max', credentials: process.env.LLM_TOKEN, }); // Передаём модели промпт с git diff const response = await llmClient.chat({ temperature: 0, messages: inputMessages, });
Бот проверяет структурированный ответ модели, отфильтровывает LGTM и через GitLab A I публикует его в MR комментарием. Плюс ещё одна обязательная часть — краткое резюме изменений в описании MR: что конкретно он содержит? AI-агент снимает эту задачу с разработчика, а живой ревьюер может быстро понять суть merge request без просмотра изменений.
С января по июль этого года AI-ревьюер проверил более 4 000 MR в 90 проектах. Потенциальные проблемы обнаружены в 8% случаев; это примерно один MR из 13. Кажется, немного, но это связано с тем, что культура разработчиков в нашей команде уже предполагает отдавать на код-ревью проверенный локальными агентами код. Если проблема находилась, бот оставляет около двух замечаний, чаще — одно.
Чтобы определить, в каких конкретно случаях помогает AI-ревьюер, я проанализировала его комментарии в 2026 году (январь-июль). Сначала распределила их по трём категориям: конкретная проблема, общая рекомендация, комментарий опубликован ошибочно. Оказалось, что в более чем 90% случаев бот сигнализирует о конкретной проблеме. Более точно можно разделить замечания AI-ревьюера по темам:
20% — стиль, именование и поддерживаемость;
18% — ошибки логики и функционального поведения;
16% — работа с null/undefined и обработка ошибок (самое актуальное для фронтендеров);
12% — типы, API и контракты;
11% — синтаксис, сборка и конфигурация.
Оставшиеся 23% делят между собой проблемы производительности, UI, асинхронного кода, тестов и безопасности.


Ещё больше ошибок!


Тестирование AI-ревьюера проходило в репозитории-песочнице (example-micromonorepo): там собраны примеры фронтенд-сервисов и тестовых MR на разных технологиях, где можно проверять сценарии ревью. Мы подключили бета-версию бота к вебхуку песочницы. Тест на изолированном стенде и в песочнице — это в целом наш обычный подход к тестированию инфраструктурных изменений. Благодаря подключению в виде вебхука после релиза бота обновлённая версия моментально появилась у всех пользователей.
AI-ревью можно отключить на старте MR на уровне репозитория в GitLab, но этого почти никто не делает. Вместе с ботом разработчик получает опцию автоматически оповещать человеческих ревьюеров через корпоративный мессенджер: не нужно копировать информацию по своему MR в канал месседжера и просить коллег посмотреть.
Дополнительно мы реализовали небольшого бота Chuck Norris. Он просто напоминает ревьюерам о забытых MR, приходя в личку в корпоративном мессенджере раз в сутки и затем — спустя несколько дней. Это позволяет снизить количество забытых и потерянных MR. Проанализировала востребованность бота (данные за январь-август):
в общей сложности почти 4 тысячи напоминаний в 51 проекте;
медиана — 2 пинга на один MR. Среднее не репрезентативно из-за нескольких рекордсменов с 100+ пингами;
почти половине MR хватило одного напоминания.
Интересно было бы сравнить два проекта — с автопингом или без — для эксперимента, но автопинг у нас в GitLab включён по умолчанию. Проект может отключить его у себя в конфиге, если потребуется.
Разумеется, невлитой MR сам по себе не приводит к сложностям. Но задержка разработки — это уже проблема: сложнее восстановить контекст, поправить конфликты главной ветки и фича-ветки. Поэтому напоминалка нужна.
Состояние процесса ревью хранится в MongoDB: участники, голоса, кворум, состояние MR, время последнего пинга. А основным хранилищем результатов выступает сам GitLab.
Как проверить результат?
Механизм AI-ревьюера подразумевает, что после ревью код пройдёт аппрув двух разработчиков. Если кворум достигнут, изменения можно вливать в основную ветку. Как и любой AI, модель может допускать ошибки, поэтому все комментарии на ревью — рекомендательные. Разработчик-автор MR сам решает, согласен он с замечанием или нет, и I won’t fix it.
Определение ошибок
Ещё одна возможность системы агентов фронтенда — анализ логов. Разработчик отправляет в канал корпоративного мессенджера ссылку на job, упавшую на GitLab; агент анализирует логи и сообщает, что не так. Workflow здесь задан программно, а в самой фиче сочетаются код и AI-агент на основе GigaChat. Как работает анализ логов:
1. Бот находит в сообщении разработчика id job, проверяет через GitLab API, что этот job и вправду failed, и скачивает полный trace.
Формально trace может содержать вывод любых команд. Конкретно наш AI-агент работает со сценариями сборки и деплоя. Это сборка приложения, docker-сборка, установка зависимостей, линтеры и проверки типов, nginx и Ansible, ошибки сети и инфраструктуры.
2. Из trace самописный парсер выделяет лог последней упавшей команды и отправляет в GigaChat-2-Макс вместе с контекстом сервиса (название, репозиторий, окружение и т.д.).
Парсер — это отдельный внутренний пакет, написанный на TypeScript. Его задача — сузить контекст для модели, выделив наиболее полезный фрагмент. А цель — уменьшить количество токенов и, соответственно, стоимость запроса плюс сократить количество потенциальных ошибок.
function parseJobLog(trace: string): string[] { return trace // Убираем служебные маркеры и цвета терминала .replaceAll(/section_start|section_end/g, '') // eslint-disable-next-line no-control-regex .replace(/\x1B\[[0-9;]*[mK]/g, '') // Оставляем секцию script, исключая after_script .split('Running after_script')[0] // Разбиваем вывод на блоки выполненных команд .split('$ '); } const failedCommandLog = parseJobLog(trace).at(-1);
В парсере стоит:
разделить универсальный и предметный парсинг.
Добавлять контекст выборочно. Конфиги и артефакты — только для тех job, где они помогают объяснить ошибку. Например, если это ошибка деплоя, из артефактов отправляем nginx-конфиг и исходный Jinja2-шаблон из репозитория.
предусмотреть такой корнер-кейс, как падение до пользовательского script. Ошибка runner, registry или сети может произойти раньше первой команды. Если парсер не нашёл начало script и последнюю пользовательскую команду (с причиной падения), он обрабатывает весь доступный trace.
const commandLogs = parseJobLog(trace); const relevantLogs = commandLogs.at(-1) || extractRunnerFailure(trace);
Как можно увидеть, в коде парсер последовательно разбивает trace на строки, находит начало секции script и отбрасывает after_script — иначе его ошибки и служебные сообщения могут смешаться с основной командой. Затем он удаляет управляющие последовательности GitLab и ANSI-коды терминала, разделяет лог по приглашению shell $, возвращает массив блоков: команда и весь её вывод. Анализатор берёт последний блок:const failedCommandLogs = parseJobLog(trace).at(-1);Именно блок последней выполнявшейся команды с высокой вероятностью стала причиной падения job.
На практике после парсера передаём боту не более 10 тысяч символов. Если логи длиннее, стоит сохранить начало и конец. По опыту, в начале лога содержится полезная информация, в конце — ошибка, а посередине — установка миллиона пакетов.
CI-логи потенциально содержат токены, адреса и переменные окружения, поэтому стоит отдельно очищать чувствительные данные (мы очищаем).
3. GigaChat ищет ошибки в логе.
Согласно промпту, модель выступает помощником команды frontend-инфраструктуры. А пользователи модели — frontend-разработчики, которые могут знать, а могут и не знать устройство Ansible, nginx и процесса деплоя.
4. AI-агент возвращает в тот же тред корпоративного мессенджера полный результат анализа логов.
В него входят такие данные, как:
на чьей стороне проблема — разработка, инфраструктура, не определено;
какая стадия и причина падения указаны в GitLab;
лог упавшей команды;
нормальное человеческое объяснение, что именно произошло;
рекомендации для инфраструктурной команды и/или разработчика: например, последовательность действий или список, что нужно проверить.

Как проверить результат?
Используем программные проверки + пользовательские реакции: разработчик может поставить ответу лайк или дислайк. Для этой части системы нам важно сохранять данные, поэтому промпт и ответ модели храним в PostgreSQL gigachain с привязкой к postId треда корпоративного мессенджера. История взаимодействий позволяет разбирать неудачные ответы и анализировать реакции пользователей, а также сравнивать качество разных версий агента и модели.
Починка сборки
Когда бот нашёл и озвучил разработчикам причины падения job, он может предложить автоматически исправить ошибку (если та на стороне разработки, а не инфраструктуры). Сейчас автоисправление поддерживает несколько сценариев — сборка приложения, Docker-сборка, конфигурация nginx. По сути мы начали с тех сценариев, которые:
сравнительно часто вызывают проблемы, но эти проблемы предсказуемы;
позволяют собрать достаточный контекст и предложить локальное изменение = AI-агента легко научить их чинить;
дают объективно проверить результат без участия человека.
Автоисправлением занимается классический AI-агент. Он получает исходники сервиса и набор tools, которые позволяют:
читать файл;
просматривать структуры каталогов;
искать по коду;
создавать и редактировать файлы;
точечно заменять тексты;
запускать shell-команды;
получать результаты сборки, линтера, теста и проч.
Количество обращений к модели не ограничено кодом, как в предыдущих вариантах: она работает итеративно, пока не решит задачу. Цель агента — «почини сборку»; он сам решает, какой инструмент вызвать и с какими аргументами. Результат каждого вызова возвращается модели.
В целом мы имеем дело не с harness, но с инфраструктурой связанных агентов-сервисов, которые передают друг другу структурированные данные. Предыдущий этап — анализ логов — становится потенциальным входом в следующий. Но запускает переход человек: если проблема относится к одному из поддерживаемых сценариев, в треде появляется кнопка Try fix with AI. Её должен нажать создатель треда.
Когда автоисправление закончено, CI повторно собирает проект. Если сборка прошла — появляется отдельная ветка и MR с изменениями в default branch. Важно: этот MR по-прежнему проверяет и аппрувит человек! Но AI-ревьюер тоже.
Иногда AI-агент не может исправить сборку; разработчик просто получает сообщение, что автоматическая починка не удалась.

По нашим подсчётам к агенту обращаются от 10 до 30 раз за день, то есть на момент написания статьи — минимум 1800 раз.
Как проверить результат?
Здесь основная проверка — сама сборка. Агент получает развалившийся проект. Если после merge request агента проект успешно собирается, значит, агент справился.
Заключение
С развитием искусственного интеллекта генерация кода быстро дешевеет — быстрее, чем проверка. По ощущениям, вскоре компании будут производить больше кода, чем способны осмысленно отревьюить, протестировать, задеплоить и промониторить его востребованность в проде. Мне кажется, это определит ландшафт разработки в ближайшем горизонте: вкладываться стоит в то, что удешевит проверку кода. Это прежде всего тесты, типы, воспроизводимые сборки, способы максимально сократить радиус поражения при ошибке; инструменты, которые повышают наблюдаемость, и инструменты для проверки корнер-кейсов. Словом, всё, что обеспечивает стабильность и предсказуемость системы, а также упрощает жизнь живым разработчикам. Любым способом: самой востребованной функцией системы агентов оказался… автоматический призыв тестировщиков в проект после обновления кода. Разработчик никому не пишет и ни с кем не общается — просто даёт команду, а бот тэгает тестировщиков в треде релиза. Это самая неочевидная для нас функция, но насколько она оказалась нужна!