ИИ на страже качества: как Авито автоматизировал Code review с помощью LLM
Проблема ручного обзора кода
В современных ИТ-компаниях с сотнями инженеров процесс проверки кода часто превращается в узкое горлышко. Разработчики вынуждены регулярно переключать внимание с написания собственных функций на чтение чужого кода. По историческим данным, еженедельно в репозиториях Авито создается около 1000 пул-реквестов (PR, pull request — запросов на объединение кода), а в пиковые часы нагрузка возрастает до 300 запросов в час.
Подобный объем работы затягивает выпуск функций (time-to-market) из-за очередей на проверку. Сказывается и человеческий фактор: из-за усталости ревьюеры пропускают дефекты и уязвимости, а стандарты проверки размываются из-за разного стиля комментирования. Автоматизация первичного код-ревью (совместной проверки кода перед его объединением) с привлечением больших языковых моделей (LLM) стала решением проблемы. Инструмент не позиционируется как автономная замена человеку: он берет на себя рутинную проверку стандартов форматирования и типовых ошибок (анализируя diff — разницу версий кода), в то время как финальная ответственность остается за разработчиками.
Архитектура системы автоматизации
Для интеграции ИИ создана распределенная система из трех компонентов: интерфейс разработки Stash (управление Git-репозиториями), Go-сервис (оркестратор и регистратор событий) и Python-сервис (ML Pipeline service) для взаимодействия с языковой моделью.
Взаимодействие между компонентами выстроено по асинхронной схеме:
- Разработчик запускает проверку простой текстовой командой
ai_reviewв комментариях к пул-реквесту. - Запрос перехватывается, событие регистрируется во внутренней шине данных (Data Bus) и считывается Go-сервисом.
- Go-сервис запрашивает diff и передает его через RPC (Remote Procedure Call — протокол удаленного вызова процедур для общения между сервисами) в Python-сервис.
- Python-сервис мгновенно возвращает ответ со статусом
200 OK, чтобы не блокировать Go-сервис на время генерации, которая для крупных пул-реквестов может занимать до минуты. - После завершения работы ML-пайплайна Python-сервис вызывает RPC у Go-сервиса, который переводит задачу в финальный статус и публикует сгенерированные комментарии в Stash через API.
Подготовка данных и контекст для модели
Обычного diff для качественного код-ревью недостаточно из-за отсутствия номеров строк и контекста. Поэтому изменения предварительно размечаются служебными скриптами.
Более того, для точной проверки модели необходим полный файл в его актуальном состоянии. Если нейросеть будет видеть только изолированный фрагмент кода, она совершит множество ложных замечаний, так как пропустит импортированные библиотеки, структуру файла или вызовы функций, объявленные выше или ниже по тексту. Go-сервис выгружает из репозитория весь измененный файл целиком и передает его в Python-сервис. Таким образом, модель видит как точечные изменения, так и всю логику программы вокруг них, что снижает риск некорректных рекомендаций.
ML-пайплайн: три шага к чистому результату
Основная проблема при использовании LLM для проверки кода — это ложные срабатывания (false positives), когда система выдает ошибочные замечания, засоряя пул-реквест. Чтобы решить эту проблему, процесс обработки в Python-сервисе разделили на три независимых этапа:
- RuleChecker (Генерация замечаний). Модель с высокой полнотой ищет любые потенциальные дефекты, ошибки безопасности, проблемы производительности и поддержки кода по внутренней таксономии Авито. Результат — массив JSON со строкой, категорией и рекомендацией.
- ReviewFilter (Валидация). Модель проверяет сгенерированные замечания по принципу Conclusion First (сначала однозначный ответ «да»/«нет», затем объяснение). Все замечания со значением «нет» отбрасываются. Это позволяет снизить ложные срабатывания (false positives) на 90%.
- CommentAggregator (Дедупликация). Комментарии векторизуются с помощью BAAI/bge-m3. Если косинусная близость (cosine similarity) векторов превышает 0.85, замечания объединяются, предотвращая дублирование.
- False positives — ложные срабатывания автоматических систем проверки.
- Эмбеддинг (Embedding) — числовое представление текста, по которому можно сравнивать смысл фраз.
- Косинусная близость (Cosine similarity) — метрика сходства двух векторов; чем выше значение, тем ближе смысл.
Выбор модели и преимущества локального хостинга (Self-hosting)
При выборе модели разработчики ориентировались на бенчмарк MERA-RuCodeReviewer — открытый русскоязычный тест для оценки качества код-ревью, содержащий примеры на Java, Python, Scala и Go. В лидерборде MERA на момент отбора лидировала модель Gemini 2.5 Flash, а второе место занимала Qwen3-Coder-30B-Instruct-FP8.
Команда выбрала модель Qwen3-Coder-30B-Instruct-FP8 из-за возможности её развертывания на собственных серверах компании (Self-hosting). Это критически важно по двум причинам:
- Безопасность. Код внутренних продуктов компании не должен отправляться во внешние облачные сервисы и API сторонних провайдеров во избежание утечек.
- Экономика. Проверка одного крупного пул-реквеста может требовать до 10 000 токенов контекста. При объемах в тысячи проверок использование внешних платных API становится экономически невыгодным, тогда как собственные вычислительные ресурсы позволяют обрабатывать запросы значительно дешевле.
Практическое руководство по развертыванию локального контура
Ниже приведена пошаговая инструкция по настройке базовых ML-компонентов в терминале Linux на сервере с GPU.
Предварительные требования:
- Сервер с установленной ОС Linux и поддержкой NVIDIA CUDA.
- Минимум 32 ГБ видеопамяти (VRAM) для запуска Qwen3-Coder-30B.
- Установленные Docker и Python 3.10+.
Шаг 1: Запуск сервера модели Qwen3-Coder-30B-Instruct-FP8
Вы можете запустить модель через vLLM или Docker с SGLang.
Запуск через vLLM:
pip install vllm
vllm serve "Qwen/Qwen3-Coder-30B-A3B-Instruct-FP8" --port 8000
Документация доступна на странице модели на Hugging Face.
Запуск через Docker с SGLang (рекомендуется для продакшена):
docker run --gpus all --shm-size 32g -p 30000:30000 \
-e HF_TOKEN="ваш_секретный_токен" \
lmsysorg/sglang:latest \
python3 -m sglang.launch_server --model-path "Qwen/Qwen3-Coder-30B-A3B-Instruct-FP8" --host 0.0.0.0 --port 30000
!WARNING Никогда не публикуйте ваш секретный токен Hugging Face (
HF_TOKEN) в публичных репозиториях кода или открытых чатах.
Шаг 2: Настройка модели векторного представления BAAI/bge-m3
Установите официальную библиотеку для работы с эмбеддингами:
pip install FlagEmbedding
Используйте следующий Python-код для вычисления смыслового сходства между комментариями:
from FlagEmbedding import BGEM3FlagModel
import numpy as np
model = BGEM3FlagModel('BAAI/bge-m3', use_fp16=True)
c1 = "Используйте константу вместо жестко прописанного таймаута."
c2 = "Таймаут зашит жестко, вынесите его в константы."
emb = model.encode([c1, c2], max_length=8192)['dense_vecs']
sim = np.dot(emb[0], emb[1]) / (np.linalg.norm(emb[0]) * np.linalg.norm(emb[1]))
print(f"Сходство: {sim:.4f}")
Документацию можно изучить в репозитории BAAI/bge-m3 на Hugging Face.
Шаг 3: Проверка работоспособности
Отправьте HTTP-запрос к запущенному инстансу с помощью curl:
curl http://localhost:8000/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{"model": "Qwen/Qwen3-Coder-30B-A3B-Instruct-FP8", "messages": [{"role": "user", "content": "Проверь: def f(x): return x*2"}]}'
Метрики эффективности и выводы для бизнеса
Интеграция ИИ в процесс код-ревью показала высокую практическую ценность. Для оценки результатов используются две ключевые метрики:
- Precision (точность): на основе лайков и дизлайков инженеров к комментариям ИИ точность составляет 85%.
- Outdated rate (влияние): доля комментариев, приведших к исправлениям. Достиг 32% (в Infra и DevOps — до 60%).
По скорости работы малые пул-реквесты (до 3 файлов) обрабатываются за 10–20 секунд, крупные изменения (10+ файлов) — около 60 секунд. Ограничение параллельных проверок установлено на уровне 650.
Главный вывод: автоматическое код-ревью на базе LLM разгружает разработчиков от рутины, позволяя им сосредоточиться на сложных архитектурных задачах. Для крупных компаний с собственной инфраструктурой self-hosting является оправданным решением. В то же время небольшим командам на старте проще и дешевле использовать готовые облачные API, так как поддержка собственного кластера GPU требует значительных вложений.

