Я — Полина Ященко, старший инженер по разработке ПО в YADRO. Мы с командой тестируем гипотезы и активно применяем искусственный интеллект для улучшения процессов разработки. Недавно мы запустили AI-ревьюера — бота, который помогает находить проблемы в стиле и логике кода.
Мы создали бота, чтобы упростить ревью пулл-реквестов. В команде есть стажёры, которые допускают типичные ошибки, например, открывают слишком большие PR. Иногда просто не хватало времени, чтобы оперативно всё проверить. Расскажу, как мы выбирали модель и разрабатывали серверную часть. Бот не обладает высокой производительностью, но отлично справляется с основной задачей — помогает инженерам находить и исправлять повторяющиеся ошибки.
Что такое AI-ревьюер
AI-ревьюер — это бот для пулл-реквестов (Pull Request, PR), который анализирует добавленный и изменённый код и оставляет комментарии с предложениями по улучшению. Он выявляет проблемы, связанные со стилем, логикой и конструкциями кода. Ревьюеру остаётся только проверить запрос и проанализировать дополнительные аспекты: наличие pytest-маркеров, соответствие автотеста его описанию в TestY TMS.
Как исследовали модели
Мы планировали запустить бота для двух репозиториев с автотестами. Перед этим нужно было проанализировать доступные модели и выбрать подходящую, а также определиться со способом развёртывания сервера.
Выбор модели
Исследование проводилось от самых маленьких моделей к крупным — так мы искали оптимальный баланс между нагрузкой и аналитическими возможностями. Для CPU-сервера использовали модели в формате GGUF, оптимизированные для быстрой загрузки и работы.
Сжатые модели используют квантование — процесс уменьшения количества бит на параметр, что снижает размер модели и требования к памяти. Мы выбрали формат Q4_K_M — «золотая середина» квантования с хорошим качеством сжатия и производительностью.
Для тестирования всем моделям была передана одна и та же ошибка: в автотесте отсутствовала функция set_log_level в библиотеке. Промт был на английском, но результаты переведены на русский для удобства.
Ответы моделей:
- phi-2: придумывал несуществующие логи, давал бесполезные советы.
- Tinyllama: предлагал переустановить библиотеку, но не понимал суть ошибки.
- Mistral: предложил корректные причины и решения — установку через pip и сборку с C-Python.
- Qwen3: перечислил несколько возможных причин, включая несовместимость версий Python и отсутствие библиотеки в системе. Ответ был обрезан из-за ограничения по токенам, но полезность высокая.
Выводы по моделям:
- phi-2 и Tinyllama отвечали непоследовательно, иногда выдавали бессмыслицу.
- Mistral и Qwen3 хорошо анализировали ошибки и предлагали реальные решения.
- Qwen3 показала лучшую производительность на слабом железе без GPU, что соответствовало нашим условиям.
Какую модель выбрали — станет ясно дальше.
Выбор способа развёртывания сервера
Мы рассматривали несколько инструментов для запуска API-сервера с LLM:
- ctransformers: Python-библиотека для GGUF-моделей на CPU. Отклонена из-за устаревания и сложной документации.
- llama-cpp-python: интеграция с OpenAI API. Не выбрана, так как llamafile использует её под капотом.
- vLLM: удобный инструмент, но его нельзя установить через pip на CPU. Сборка из исходников усложняет миграцию, а производительность ниже, чем у альтернатив.
- llamafile: выбран. Позволяет запускать модель одним файлом, разворачивает OpenAI-совместимый сервер, легко настраивается и быстро работает.
В качестве модели выбрали Qwen3-Coder-4b — оптимизирована для анализа кода, требует минимум ресурсов и хорошо отвечает на промпты.
Размер модели — 4 миллиарда параметров. Чем больше параметров, тем сложнее задачи модель может решать, но для нашей задачи 30b избыточны. 4b — оптимальный выбор.
Анализ кода занимает от одной до восьми минут в зависимости от объёма. Мы ограничиваем ответ до 700 токенов, чтобы избежать слишком длинных комментариев.
Оптимальные параметры модели:
- Размер контекста — 48 000 токенов (хватает для файлов на 1000–1500 строк).
- Температура — 0.3 (умеренная креативность, акцент на точность).
- RoPE Scaling — Yarn, с параметрами rope-freq-base=1000000 и rope-freq-scale=0.1 для лучшей обработки длинного кода.
- 8 CPU-потоков для вычислений.
- Параллельная обработка отключена — все запросы в одном потоке.
- Пропускная способность — 512 токена в секунду.
- Включены mlock и no-mmap — модель блокируется в RAM, чтобы избежать замедлений из-за свопа.
Чтобы эффективно обрабатывать длинные контексты, мы масштабируем токены: rope-freq-scale заставляет модель воспринимать 100 000 токенов как 10 000. Это позволяет использовать параметры, обученные на коротких контекстах. Чтобы избежать путаницы в порядке слов, rope-freq-base повышает «разрешение» диапазона.
Разработка серверной и клиентской части
Изначально ключевыми критериями были простота развёртывания и контроль над процессом. Сервер запускался на виртуальной машине во внутреннем облаке как systemd-сервис. Это позволяло просматривать логи через Journald и автоматически перезапускать сервис при сбоях.
Модель запускалась одной командой:
root/Qwen_Qwen3-4B-Q4_K_M.llamafile --server --nobrowser --host 0.0.0.0 --port 9000 -c 48000 -t 8 --temp 0.3 --batch-size 512 --parallel 1 --mlock --no-mmap --rope-scaling yarn --rope-freq-base 1000000 --numa distribute --rope-freq-scale 0.1
Процесс ждал запросов. Это было удобно, но ограничено по размеру модели.
Клиентская часть состояла из функций:
- получение git diff;
- определение параметров запроса (длина ответа);
- отправка запроса на сервер;
- публикация ревью.
Алгоритм выбора кода был простым: если в diff от 10 до 20 строк — ответ до 300 токенов, больше — до 500. Маленькие изменения не отправлялись.
Со временем стало ясно, что Qwen3-4B недостаточно для глубокого анализа. Мы хотели использовать модель с большим количеством параметров, но без увеличения своих ресурсов. Поэтому перешли на платформу Dify.
Теперь используется модель Qwen3-30b-A3B-Instruct-2507. Вся логика — промпты и ИИ-обработка — собрана в Dify.
Ключевое изменение: решение о том, что и как анализировать, перешло с клиента на сервер. Раньше клиент сам определял, какой код отправлять. Теперь он просто передаёт git diff с контекстом, а ИИ решает, нужно ли ревью и какой длины.
Раньше модель часто отвечала «код корректен» даже на мелкие правки — это было бесполезно. Теперь, если ИИ считает, что ревью нужно, он генерирует содержательный комментарий. Мелкие изменения либо игнорируются, либо получают краткую обратную связь без ложного одобрения.
В итоге мы перешли от самописного сервера с 4B-моделью к облачному пайплайну с 30B-моделью. Клиент стал тоньше, сервер — умнее, а качество ревью выросло.
Запрос к серверу прост — это OpenAI-совместимый API:
В промте используется /nothink, чтобы отключить «размышления вслух». Эта функция тратит токены и не нужна для нашей задачи.
Алгоритм ревью теперь сложнее:
- Анализируем git-diff каждого файла (только .py).
- Разбиваем на сегменты добавленных строк.
- Выделяем контекст для каждого сегмента.
- Отправляем контекст ИИ.
- ИИ решает, нужно ли ревью и какой длины.
- Формируется комментарий нужного размера.
- Ответ публикуется в Bitbucket.
- Переходим к следующему сегменту, затем к следующему файлу.
Клиентская часть требует доработки:
- Бот отвечает на английском, но иногда переключается на русский, если видит его в коде. Нужно зафиксировать язык ответа.
- Поведение бота нужно стабилизировать, чтобы устранить непредсказуемость.
Proof of concept
Мы реализовали схему: Bitbucket → Albert → Jenkins → AI → Bitbucket. Такая же архитектура используется для других проверок в TATLIN.BACKUP.
Процесс:
- При открытии PR срабатывает webhook в Bitbucket.
- Запрос отправляется на сервис Albert.
- Albert фильтрует событие и вызывает Jenkins API с параметрами PR.
- Jenkins запускает job, который запускает Python-скрипт.
- Скрипт получает diff через Bitbucket API, парсит добавленные строки и выделяет контекст.
- Код отправляется на AI-сервер, приходит ревью.
- Скрипт публикует комментарии в PR по файлам и строкам.
На разработку ушёл месяц — это не основная задача команды. Планируем улучшать бота: добавим возможность диалога с ним и загрузим базу кода наших репозиториев. Это снизит универсальность, но повысит качество ревью. Команда разработчиков СХД уже создаёт похожий пайплайн для Rust-кода.