Привет, Хабр! Меня зовут Евгений Зорин, я ведущий разработчик в центре инноваций Future Crew. У моего проекта достаточно компактная команда. Нам постоянно нужно проверять критически важную функциональность, и часто это может сделать только сам разработчик. С появлением современных LLM, таких как ChatGPT, возникла идея об их внедрении для ревью кода. В качестве подопытного кролика был выбран Swift. В этом материале я расскажу, чего мы добились, какие инструменты использовали и как LLM справляется с поставленной задачей.

Зачем это нужно

Ручное ревью отнимает много времени и сил, а ошибки все равно проскакивают. С помощью LLM можно создавать системы, которые будут автоматически анализировать код, предлагать оптимизации и контролировать стандарты написания кода.

При этом важным условием внедрения такого ревью является его локальное выполнение. Так код не передается в облачные сервисы или сторонние платформы, а команды контролируют все этапы обработки данных. Это особенно важно при разработке систем, работающих с конфиденциальной информацией. Также есть возможность дообучения и конфигурирования LLM под конкретные требования команды и компании.

Тест гипотезы с помощью готовых инструментов

На старте проекта мы проанализировали, что уже есть на рынке. Часть сервисов мы отсеяли сразу: например, CodeRabbit просил дать админский доступ ко всему gitlab-репозиторию, что явно не входило в наши планы и не соответствовало элементарным правилам безопасности. А вот остальные протестили руками.

Начали мы с отправки пробного кода, не содержащего чувствительной информации, в сервис bito.ai. Получили такой ответ:

Удобно, когда предлагаются конкретные изменения для рефакторинга и исправления кода прямо в MR. При правильной настройке можно еще сделать кнопку «применить исправления», позволяющую одним нажатием изменить код
Удобно, когда предлагаются конкретные изменения для рефакторинга и исправления кода прямо в MR. При правильной настройке можно еще сделать кнопку «применить исправления», позволяющую одним нажатием изменить код

Bito.ai дал ценные замечания, и мы поняли, что LLM могут быть полезными для автоматического ревью. К сожалению, большинство инструментов, в том числе и bito.ai, выступают в роли прокси для ChatGPT. В их основе лежит взаимодействие с облачными нейросетевыми моделями, которые обеспечивают обработку и анализ кода, выводя автоматизированные комментарии и рекомендации. Для наших задач они не подходили, но мы поняли, как осуществлять ревью с помощью LLM, и познакомились с основными алгоритмами для оценки качества кода.

Мы должны были добиться высокой пользы от комментариев. Смотрели число исправлений кода по рекомендациям к общему числу комментариев, оставленных LLM.

Создание собственного инструмента

Для работы с локальными моделями мы выбрали n8n — open-source-конструктор для создания различных workflow. Он помогает в выполнении рутинных рабочих процессов, интегрируя в единый оркестр множество сервисов и сущностей. Именно в нем задается вся логика предварительной и последовательной обработки кода, который отправляется в модель на ревью:

Типовая блок-схема анализа кода в n8n, отображающая flow работы всего инструмента
Типовая блок-схема анализа кода в n8n, отображающая flow работы всего инструмента

На схеме есть flow, по которому проходят изменения, сделанные в рамках merge request (MR). Каждый блок отвечает за свою функцию:

  • Webhook слушает вебхук от Gitlab, который отправляется при создании MR или добавлении в него нового коммита и комментария в MR.

  • Need review запускает процесс ревью по заложенному условию, когда видит в комментарии нужное слово-триггер. Если его нет, то весь flow пропускается.

  • Get changes парcит ответ от API Gitlab и берет только поле changes из JSON-ответа.

  • Split Out1 делает из поля changes массив, чтобы каждое изменение анализировать отдельно. При этом из JSON-ответа берется только поле changes, остальные поля пропускаются.

  • Split Out2 также использует поле changes, но уже вместе с другими полями (различной служебной информацией: именем файла, исходной и целевой ветками, номером MR и так далее).

  • Parse Last Diff Line1 и Code находят начало и конец строки каждого сделанного изменения, чтобы затем оставить под ними комментарий в коде.

  • HTTP Request получает на вход полный JSON-ответ от API Gitlab'a со всей служебной информацией. Она используется, чтобы передавать модели и сравнивать не только изменения, но и контекст кода.

  • Merge объединяет информацию для отправки на анализ в нейросеть: он сопоставляет изменения конкретного файла и его содержимое.

  • Basic LLM chain 1 является интерфейсом, где мы указываем промпт, для отправки.

  • Ollama Chat Model — интерфейс, в котором мы выбираем модель.

  • Post discussion публикует комментарии к коду в Gitlab через его API.

Модель с локального сервера мы подключили к n8n, указав ее в Olama chat model:

Блок Ollama Chat Model позволяет подключить локальную модель по HTTPS. Если развернуто несколько моделей, то в этом блоке можно их выбирать и быстро переключаться на требуемую нейросетевую модель
Блок Ollama Chat Model позволяет подключить локальную модель по HTTPS. Если развернуто несколько моделей, то в этом блоке можно их выбирать и быстро переключаться на требуемую нейросетевую модель

В процессе внедрения механизма автоматического ревью мы доработали схему, чтобы комментарии к коду оставались в нужном месте и мог идти анализ нескольких файлов сразу:

На старте работы мы использовали ответы ChatGPT, чтобы понимать, как самая продвинутая LLM анализирует код. Затем мы делали тонкую настройку специализированных моделей (Codeqwen:7b, Llama3 и т. д.) и оценивали, как их советы помогают обнаружить явные ошибки в коде и выдают необходимые рекомендации по их устранению. В целом, каждая модель дала полезные замечания и предупреждения. Да, ChatGPT дает более информативный и развернутый ответ, но и локальная модель хорошо справилась и предлагала изменения для улучшения кода.

Для запуска ревью мы использовали такой промпт:

Так нам ответил ChatGPT:

А так — развернутая локально LLama:

Очевидно, что ChatGPT дает более информативный и развернутый ответ. Тем не менее локальная модель предлагает даже изменения для улучшения кода, что весьма ценно.

Сравнительный анализ моделей

Очевидно, что перед началом активного применения LLM нужно обратиться к чужому опыту и посмотреть, что с ними делали другие разработчики. Сначала мы взяли с huggingface.co список самых популярных моделей для языка Swift. Он составлялся на основе данных Open LLM Leaderboard и Open LLM-Perf Leaderboard, где у базовых мультиязычных LLM оценивалась производительность генерации кода по бенчмаркам HumanEval и MultiPL-E.

В сравнении присутствуют только открытые LLM, которые могут использоваться для дальнейшего обучения:

Для теста мы сравнили ответы трех из пяти топовых моделей: Codeqwen: 7b, Llama3:latest и Deepseek-coder:33b.

В этих сравнениях можно увидеть, что ответы на один и тот же промпт бывают разные и могут включать информацию о функциональности кода, что не всегда оказывается полезным для анализа. Путем настройки промпта подбор параметров модели и ее дообучение под конкретные требования команды добиваются нужного поведения при анализе кода
В этих сравнениях можно увидеть, что ответы на один и тот же промпт бывают разные и могут включать информацию о функциональности кода, что не всегда оказывается полезным для анализа. Путем настройки промпта подбор параметров модели и ее дообучение под конкретные требования команды добиваются нужного поведения при анализе кода

С помощью настройки промпта и изменения температуры мы пытались получить у этих LLM похожие ответы, но они все равно выдавали разные, причем то на русском, то на английском языке. Требовалось постоянно уточнять промпт и снижать температуру для получения более стабильной работы. Вдобавок мы были вынуждены внедрить дополнительные модули для фильтрации ошибочных и пустых ответов.

В итоге базовой моделью для дальнейших экспериментов мы выбрали Codeqwen:7b, так как ее советы выглядели самыми адекватными. Сейчас у нас полным ходом идет калибровка ответов, которые бы удовлетворяли нашим потребностям.

Что могут увидеть LLM в коде

LLM хорошо оптимизируют код, выявляя и исправляя потенциальные ошибки, такие как deadlock, деление на ноль или присвоение переменным самих себя и так далее. Их сложно найти, а вреда они могут принести очень и очень много. Например, так LLM ищет в коде на Swift deadlock и присвоение переменных самой себе:

Да, некоторые статические анализаторы обнаруживают подобные ошибки, но, к сожалению, далеко не все. Искать такие баги — задача нетривиальная. В итоге внедрение LLM упрощает и делает проверку кода более гибкой.

Не все модели предоставляют однозначные ответы и фиксируют очевидные ошибки. Для этого нужно использовать соответствующие промпты и дообучать на конкретных примерах, создавая необходимые шаблоны.

Ресурсоемкость

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

Запускать огромную модель ChatGPT на локальном сервере непрактично, если нужен анализ конкретного языка программирования или требования сильно ограничены чем-то еще. Специализированные модели не только занимают меньше места и быстрее отвечают, но и дают более качественные ответы.

Чтобы узнать, какая из них будет в итоге потреблять больше ресурсов, мы провели анализ кода нескольких небольших файлов. Смотрели производительность каждой из моделей, чтобы оценить потенциальную применимость и возможность запуска на доступном оборудовании. Использовали сервер с такой конфигурацией:

Cores per socket: 1
Number of sockets: 32
Memory: 160 GB
GPU: NVIDIA A100 80Gb
NVIDIA: SMI 525.147.05
Driver Version: 525.147.05
CUDA Version: 12.0

И получили такое время анализа от отправки кода на ревью до получения результата: 

Codeqwen:7b — 5 минут
Deepseek-coder:33b — 8,5 минуты
Llama3:latest — 1,5 минуты

Понятно, что даже девять минут — это нормально для получения действительно полезных комментариев, но для больших команд с ограниченным доступом к вычислительным ресурсам рациональнее будет дообучать более простые и легкие модели.

Настройка инструмента ревью

Особое внимание пришлось уделить правильной конфигурации, чтобы инструмент мог эффективно генерировать комментарии и вносить актуальные правки в ревью. 

В процессе внедрения локальной LLM мы поставили себе следующие задачи:

  • Исключать файлы по расширениям. Это оказалось довольно легко сделать путем добавления файла конфигурации в n8n.

  • Получать ответы только на русском языке. Для этого достаточно было явно прописать требование в промпте. 

  • Иметь возможность дообучения LLM. Тут нам пришлось создать дополнительные модули на JavaScript и повторно настраивать систему.

  • Отправлять в LLM контекст кода. Эту задачу, увы, пока реализовать не удалось.

Для обеспечения приемлемого ответа модели в местах изменения кода мы столкнулись с множеством проблем. Например:

  • Настройка n8n осуществляется на JavaScript и требует достаточных навыков в написании конфигураций. Так, для обработки нескольких файлов и комментирования только изменений в коде требовалось по частям отправлять их на анализ, а потом корректно вставлять комментарий в нужную строку.

  • Ответы некоторых моделей не всегда соответствовали анализируемому коду. Вот примеры неправильных:

    • {"oldLine": -, "newLine": -,... — не валидный json возникал всего пару раз, скорее всего, модель галлюцинировала.

    •   {"oldLine": -1, "newLine": -1,...
      {"oldLine": null, "newLine": null,...
      {"oldLine": 123, "newLine": 123,...

Такая ошибка появлялась, когда LLM отвечала на код, выходящий за рамки изменений. Путем подбора соответствующего промпта решили эту задачу.

  • Пайплайн в n8n изначально не поддерживает работу с большим количеством файлов и изменений, поэтому его нужно настраивать и добавлять новые пайпы.

  • Требуется настройка промпта и дополнительная обработка некоторых изменений в коде. Например, если правится UI, то в промпте нужно прописывать анализ только UI и так далее.

В целом, это технические трудности настройки в n8n дополнительных модулей, исключающих неправильные ответы. Эти модули фильтровали и обеспечивали необходимым контекстом изменения в коде. Путем регулирования температуры стабилизировалась вариативность, иначе было сложно контролировать качество ответов модели. С помощью подбора правильного промпта под конкретный MR получалось добиться более релевантных и полезных комментариев к изменениям в коде. 

К чему мы пришли

Инструмент автоматического ревью пока используется в ограниченном режиме, так как требует настройки промптов и параметров самой модели. Тем не менее уже сейчас помогает поймать явные ошибки в Swift-коде. Важно отметить, что данный пример сосредоточен на языке Swift, однако сам подход может быть адаптирован для работы с кодом на других языках программирования (Go, Python, Java, C/C++ и т. д.). Путем выбора наиболее подходящей модели с настройкой параметров и адаптирования соответствующего промпта.

Пока еще есть много проблем в настройке и точности ответов, однако мы ждем, что при запуске полноценного обучения на базе других MR комментарии станут корректнее и полезнее.

Следующим этапом мы планируем дообучить конкретные модели под требования определенных продуктов. В результате:

  • Будут учитываться специфические стандарты и процессы.

  • Оптимизируются ресурсы, потому что специализированные модели не такие «прожорливые».

  • Повысим точность анализа и снизим число ошибок.

  • Инструменты автоматического ревью будут больше соответствовать потребностям команды.

  • Станет быстрее и проще внедрять их в рабочие процессы.

В будущем такой флоу мы проверим и для других языков программирования, например Кotlin.

Рентабельность автоматического код-ревью

Внедрение такого инструмента зависит от объема проекта и может обойтись от 200 до 500 тысяч рублей. Внедрить его можно за 2–4 недели. Эксплуатация потребует от 50 до 100 тысяч на поддержку и обновление и еще от 10 до 50 тысяч в зависимости от нагрузки и инфраструктуры на вычислительные ресурсы.

При этом время на ревью уменьшается на 20–40%, а число ошибок уменьшается на 15–30%. В целом, по моим расчетам, подобная система может окупиться за полгода-год.

Немного о рисках

Одним из ключевых рисков является генерация ложных срабатываний (false positives) — ситуаций, когда инструмент отмечает код как ошибочный или требует исправлений, хотя в реальности таких проблем нет. Из приведенных выше примеров можно увидеть, что в примерах ответов, выдаваемых нейронкой, очень много не только «мусора», но и отчасти вредных комментариев. Такие ошибки могут привести к неприятным результатам:

  • Снижение доверия команды. Разработчики могут игнорировать рекомендации инструмента, если слишком много из них оказываются ошибочными.

  • Потеря времени. Проверка ложных предупреждений увеличивает время ревью вместо его сокращения.

  • Конфликты со стилевыми или архитектурными решениями. LLM может некорректно интерпретировать нестандартные, но оправданные подходы, что добавляет ненужные исправления.

Минимизировать ложные срабатывания можно с помощью:

  • Настройки правил и контекстов. Для этого нужно адаптировать модель под конкретные стандарты и стиль команды, исключать из анализа специфичные участки кода, где заранее понятно, что может быть ошибка.

  • Итеративного улучшения. Может помочь постоянный сбор обратной связи от разработчиков и обучение модели на реальных примерах из проекта.

  • Ограничение автоматизации. Автоматическое ревью стоит использовать только для проверки стандартов, стиля или типовых ошибок. Сложные логические проверки надо оставить за людьми.

Чем же мне так понравились LLM

Мы пока еще в процессе внедрения, но планируем, что автоматическое ревью сэкономит нам время при анализе кода, будет находить ошибки, которые пропустили люди, а самое главное — будет адаптироваться к новым паттернам и постепенно становиться все точнее и точнее.

Описанный в этом материале подход может применяться не только к анализу кода, но и для любых документов. Например, проверке юридических актов, автоматизированному анализу контрактов и финансовых отчетов. На этом у меня все, готов ответить на ваши вопросы в комментариях.

P. S. Огромное спасибо моим коллегам Булату Низамутдинову, Артему Нагорному и Алексею Доронину за помощь в развертывании и настройке инструмента.

Комментарии (13)


  1. StrawberryPie
    28.01.2025 10:42

    Слышали что нибудь про модель ChatGPT? И я нет, но у автора она есть. Вроде не много цифр нужно, одна-две, чтобы написать конкретную модель.


    1. evgzor Автор
      28.01.2025 10:42

      ChatGPT локально не запустишь. Мы ориентировались на модели, которые можно запустить локально. Для сравнения использовалась ChatGPT 4.


  1. cross_join
    28.01.2025 10:42

    Каковы комплексные результаты сравнения со статическими анализаторами кода? Или цель была заменить классический алгоритм нейросетевым и посмотреть, что получится?


    1. evgzor Автор
      28.01.2025 10:42

      Серьезного сравнения с топовыми статическими анализаторами, такими как SwiftLint или SonarQube, не проводилось. Однако субъективно, они уже уступают анализу с использованием нейронной сети. Даже при грубой настройке удавалось находить больше критических ошибок, при этом ошибки, которые выявляют статические анализаторы, также не оставались незамеченными. Мы проводили проверки на Swift, но до анализа на Java или Go пока не дошли. Тем не менее, возможно, на других языках ситуация обстоит иначе.
      Спасибо, за интересный вопрос.


      1. cross_join
        28.01.2025 10:42

        Насчет Swift ничего сказать не могу, но для Си++ SonarQube никоим образом не тянет на топовой анализатор, несмотря на генерацию массы подсказок. PVS Studio в наших тестах оказался на голову выше.


        1. evgzor Автор
          28.01.2025 10:42

          Интересно. А какие модели использовали?


          1. cross_join
            28.01.2025 10:42

            Всё "из коробки" без дополнительных настроек, кодовая база небольшая около 100К строк, тесты делали 4 года назад. Цена разнилась, соответственно: 100 долл/год за лицензию у Сонара и до 10К у PVS


            1. evgzor Автор
              28.01.2025 10:42

              4 года назад все-таки давненько... сейчас меняется чуть ли каждый месяц модели... у меня опыт со стат анализаторами небольшой есть и они не так быстро эволюционируют... Пока статью писал, появились новые версии доступных моделей не говоря о платных... ИМХО все скатывается в использование нейронок. Статические анализаторы останутся, но их будет меньше... Но посмотрим. Да, и опять же никто не запрещает использовать оба подхода на одном проекте, если все работает локально.


              1. cross_join
                28.01.2025 10:42

                Если нужно решить проблему одноразово, то очередная модель сможет помочь, видимо. Если анализ кода включен в цепочку разработки, то проблема недетерминированности встанет в полный рост. С тем же успехом можно пытаться заменять компилятор и получать всякий раз разные результаты даже на одном и том же наборе исходников. А ведь исходники активно меняются. Также ключевым показателем будет процент ложных срабатываний, требующий участия оператора


                1. evgzor Автор
                  28.01.2025 10:42

                  Эффективность моделей зависит от их правильного встраивания в процесс, наличия механизмов контроля качества и прозрачных метрик оценки их работы. При хорошей настройке они могут стать инструментом, дополняющим существующие этапы разработки. Соглашусь, что проблем масса их все предстоит решить и от проекта к проекту они будут проявляться по разному.


  1. evgzor Автор
    28.01.2025 10:42

    .


  1. enabokov
    28.01.2025 10:42

    Используем авторевью около года. Полезная штука. Редко, когда срабатывает неправильно. Предусмотрен фидбек. Ещё пишет summary по изменения. Прямо чудеса.


    1. evgzor Автор
      28.01.2025 10:42

      А какая модель в нейронке используется, если не секрет?