Компании, которые занимаются разработкой программного обеспечения, обычно разделяют разработчиков на две большие группы: на тех, кто создаёт новые продукты, и тех, кто занимается поддержкой созданных ранее решений. Первую группу называют отделом научно-исследовательских и опытно-конструкторских работ, НИОКР (англ. R&D — research and development). Вторую группу — инженеры поддержки (англ. Development Support [1]). Первая группа работает на поиск новых клиентов, вторая — на удержание клиентов. Первая — на будущее, вторая — на прошлое и настоящее. Но есть то, что объединяет эти две группы: поиск и исправление ошибок и неисправностей в программном коде.
В отделе НИОКР работа с ошибками — часть итерации над поиском оптимального решения задачи. Создание комплексных систем, сложных алгоритмов, взаимосвязанных компонентов — всё это сопровождается ошибками, которые не сразу могут быть обнаружены.
В отделе поддержки, как правило, ошибки обнаруживают клиенты, пользователи программного обеспечения. Эти ошибки не были обнаружены заранее, на этапах разработки и тестирования.
В обоих случаях — когда разработчики замечают ошибки на этапе решения задачи и когда пользователи обращаются с найденной проблемой — необходимо оперативно их устранить, потому что наличие ошибки равно потере средств компании-разработчика. В первом случае — потому что разработка останавливается из-за необходимости устранения ошибки; во втором — потому что компания может быть вынуждена покрывать убытки клиента, которые принесла эта ошибка.
Данная работа посвящена поиску решения, способного ускорить обнаружение и исправление ошибок в программном обеспечении. В работе будут представлены методы обнаружения и исправления ошибок на основе статического и динамического анализа — которые далее будут обозначаться как «классические» методы — и на основе больших языковых моделей, БЯМ (англ. LLM — large language model).
Цель данной работы: создание алгоритма для оптимального совмещения возможностей различных методов для поиска и исправления неисправностей в программном обеспечении.
Задачи работы: 1. Составление перечня классических методов и инструментов поиска неисправностей: статический и динамический анализ; 2. Составление перечня классических методов и инструментов исправления неисправностей; 3. Исследование принципов работы современных БЯМ; 4. Составление перечня инструментов поиска неисправностей на основе БЯМ; 5. Сравнительный анализ классических методов и методов на основе БЯМ в задаче с поиском неисправностей в ПО; 6. Составление перечня инструментов исправления неисправностей на основе БЯМ; 7. Сравнительный анализ классических методов и методов на основе БЯМ в задаче с исправлением неисправностей в ПО; 8. Создание решения с совместным использованием классических методов и методов на основе БЯМ.
В данной главе будут рассмотрены методы и инструменты поиска неисправностей на основе статического и динамического анализа — далее они будут называться «классическими» — и методы и инструменты исправления неисправностей.
Инструменты статического анализа не запускают программу, а работают с её исходным кодом или с его представлением, которое было получено после его обработки (например, после компиляции в байт-код в случае с Java). Статические анализаторы проверяют паттерны проектирования, некорректное использование языка и библиотек, сообщая о проблемах пользователю во время сборки программы или отображая ошибки в редакторах и интегрированных средах разработки [2].
Важно отметить, что ошибки компилятора не относятся к ошибкам, найденным в ходе статического анализа. Инструменты статического анализа — отдельные инструменты, обособленные от компиляторов.
Инструменты статического анализа принято называть «линтерами» (англ. linters) в честь первого инструмента — утилиты lint [3], созданной Стивеном Джонсоном для проверки кода на языке C. Утилита проверяла выражения, которые не могут быть перенесены между платформами, участки с неиспользуемым кодом, ошибки приведения типов.
Широкое распространение линтеры получили в языках программирования с динамической типизацией: JavaScript и Python. Наиболее популярные инструменты для этих языков — ESLint [4] и Pylint [5]. Существуют линтеры и для языков со статической типизацией: для C++ есть Clang-Tidy [6], для Java — PMD [7], SpotBugs [8]. Также существуют инструменты статического анализа, которые умеют работать с несколькими языками программирования: например, PVS-Studio [9], SonarCube [10], Infer [11][12].
Отдельно стоит упомянуть статический анализатор CodeQL от GitHub [13]. Данный инструмент состоит из двух частей: статического анализатора и языка запросов к исходному коду QL [14]. Это позволяет определить путь от ошибки в коде до места в пользовательском интерфейсе, которое приведёт к этой ошибке.
Динамический анализ как подход изучения ПО предполагает запуск программы с изучением её работы и влияния на окружение. Основа анализа — допущение, что программа представляет собой набор доступных снаружи входов и выходов и недоступных, расположенных внутри, алгоритмов. Изменяя входные данные, методы динамического анализа проверяют выходные данные и влияние на окружающую среду.
Тестирование [15] — самый распространенный метод динамического анализа. Данный метод заключается в создании обёртки вокруг программы (англ. test suite), который проверяет её работу. Тестирование нужно для проверки отдельных методов программы, компонентов и комплексной работы программы. Проверка производится на заранее известных данных: при создании теста описывают входные данные, ожидаемые выходные данные и проверяют фактические выходные данные — результат работы алгоритма. Существуют инструменты тестирования, которые позволяют проверять промежуточные вызовы, чтобы удостовериться в правильной последовательности операций.
Чтобы избавиться от предопределённости в тестировании и исследовать возможные нетривиальные случаи (так называемые “edge cases” или “corner cases” — пограничные ситуации), применяется «фаззинг». «Фаззинг» (англ. fuzzing, fuzz testing) — вид тестирования, при котором на вход в тестируемую систему подаётся случайный набор данных [16]. Данные могут быть как по-настоящему случайными, не имеющими нужного для входа формата — фаззинг такого типа называется мутационным, — так и форматированными, но с элементами случайности (например, JSON с определёнными полями, но случайными значениями в них) — это порождающий фаззинг [17].
Инструменты тестирования представляют собой целые фреймворки, потому что позволяют проверять программное обеспечение сразу на нескольких уровнях и заставляют разработчиков писать исходный код в таком стиле, чтобы его можно было «покрыть тестами» — то есть для каждого входа и выхода создать алгоритм их проверки. Среди фреймворков для тестирования стоит выделить JUnit [18] для Java, GTest [19] для C++, PyTest [20] для Python. Для фаззинг-тестирования — JBroFuzz [21] от OWASP, позволяющий проверять веб-приложения.
Фреймворки для тестирования устроены по одному принципу: есть ввод, есть исследуемый алгоритм, есть желаемый вывод, есть фактический вывод, который должен совпасть с ожидаемым. Такой подход используется в большинстве языков, когда нужно проверять именно алгоритм. Но также есть подходы и инструменты, которые позволяют проверять результат всей программы.
Тестированием всей программы является анализ производительности программы: так же, как при обычном тестировании, существует вход — входные данные, окружение, — алгоритм — исследуемая программа, — ожидаемый выход — ожидаемые потребление ресурсов системы, — фактический вывод — фактические потребление ресурсов. Инструменты для анализа производительности: JMeter [22] [23] и VisualVM [24] для Java, gprof [25], perf [26], Valgrind [27] для C++.
Если программа предоставляет интерфейс для взаимодействия, то он также может быть протестирован. Консольный интерфейс можно протестировать с использованием Judo [28], Web-API — с использованием тестов в Postman [29], графический интерфейс — Selenium [30], Puppeteer [31].
Выше были приведены инструменты, упрощающие поиск неисправностей в программном коде. Эти инструменты выводят список проблем, которые есть в программном коде, или указывают на места, где может находиться ошибка: ошибка логики, неправильное использование алгоритма. Со всеми этими проблемами предстоит разобраться разработчикам, если они хотят улучшить продукт. В помощь им существуют инструменты, исправляющие исходный код — так называемые Автоматические инструменты Исправления Программ (англ. Automatic Program Repair tool, APR tool) [32][33]. В этой главе будет рассказано об этих инструментах, а также будет приведена их классификация по методу работы.
Все описанные далее инструменты работают на основе тестов (англ. Test Suite based Program Repair): на вход инструментам подаётся программа и тесты, которые она должна проходить (подробнее об этом говорилось в разделе о динамическом анализе). Тесты здесь выступают как спецификация работы программы за неимением формального описания программы [34]. Это нужно, чтобы понять, до какой степени изменять программу, какое у неё должно быть задуманное поведение. В исследованной литературе такой подход называют «генерируй и проверяй» (англ. Generate-and-Validate). Все инструменты такого метода реализуют один и тот же алгоритм: локализация ошибки, создание исправления, проверка исправления. В результате работы инструменты создают так называемые «патчи», (англ. patch — заплата), применимость которых оценивается разработчиком.
Одним из ранних инструментов автоматического исправления программ является GenProg [35]. Он использует генетическое программирование для поиска исправлений дефектов. Итоговое исправление получается путём пошагового применения наиболее подходящих промежуточных исправлений поменьше [36]. Инструмент создавался для языка С, но также есть копия инструмента для языка Java — jGenProg [37]. Последователями инструмента являются: RSRepair [38] — используется не генетическое программирование, а случайный поиск для поиска исправлений [39]; ARJA [40] — предоставляют улучшения в области поиска исправлений, предлагает многоцелевую оптимизацию, работает с языком Java [41].
К этой категории относятся инструменты, которые в процессе работы создают промежуточное представление, которым оперируют при создании исправления. В разной литературе эту категорию называют по-разному: - «на основе синтеза» (англ. synthesis-based) — по итогу работы программа обратно синтезируется из модели; - «на основе семантики» (англ. semantics-based) — инструменты работают не с кодом, а со «значением» конструкций; - «на основе ограничений» (англ. constraint-based) — инструменты работают с конечным множеством решений и применяют соответствующие математические алгоритмы для создания исправлений.
SemFix [42] (Semantic-based Program Fixing) — инструмент для языка C, использующий динамическую символьную интерпретацию (англ. dynamic symbolic execution, DSE) для создания исправлений [43]. SemFix сканирует программу, находит в ней «подозрительные» места (подозрительные в контексте исследуемой проблемы конкретного теста), «решает» подозрительные места с учётом других тестов и переводит решение в исправление на языке исходного кода. Решение здесь происходит как раз за счёт DSE — при таком подходе программа с ветвлениями и переменными превращается в дерево состояний с переменными и переходами по ветвям с условиями, которое может быть представлено математически. Как признаются авторы, инструмент имеет свои ограничения, связанные с DSE: он показывает плохую производительность на сложных проектах с множеством связей.
Nopol [44] — инструмент для языка Java, специализирующийся на исправлении if-условий. В работе Nopol использует Задачу выполнимости формул в теориях (англ. SMT — Satisfiability Modulo Theory) [45]. Инструмент находит проблемное if-условие, из-за которого не проходит тест, собирает влияющие на это выражение переменные, переводит их в математическое представление в терминах SMT, решает и переводит решение обратно в исходный код.
Инструменты для исправления программ на основе шаблонов (англ. Template-based repair approaches) используют заранее собранные ошибки и их исправления как базу знаний для новых исправлений.
PAR [46] (Pattern-based Automatic program Repair) — инструмент для создания исправлений на основе заранее отобранных исправлений, созданных разработчиками при решении типовых проблем [47]. Авторы инструмента проанализировали свыше 60 000 исправлений, созданных разработчиками и создали на их основе 10 шаблонов, которые представляют собой скрипты исправления кода. Для оценки инструмента использовались 119 ошибок из больших и популярных проектов на Java — Apache Log4J, Rhino, AspectJ — и оценки 253 разработчиков. Инструмент представляет собой скопление опыта разработчиков при решении проблем (по крайней мере, тех, которые были найдены в исследуемых проектах). Однако в этом его слабость — он предлагает только те исправления, которые ему известны и только такие исправления, какие бы написали разработчики-люди [48].
AVATAR [49] — инструмент для создания исправлений на основе заранее отобранных исправлений. Отличительной чертой инструмента является упор на ошибки, найденные статическими анализаторами (в оригинальной статье говорится про FindBugs, SpotBugs, Infer, ErrorProne) [50], так как большинство из них пересекается с ошибками, которые могут быть найдены при методах, основанных на тестировании.
Инструменты из этой категории используют методы классического машинного обучения (англ. machine learning) и глубокого обучения (англ. deep learning) для нахождения и исправления ошибок. В данной работе к этой категории будут относиться инструменты без использования архитектуры трансформеров и больших языковых моделей — о них будет рассказано в следующей главе.
FixMiner [51] — инструмент, использующий представление кода в виде AST (Abstract Syntax Tree, Абстрактное Синтаксическое Дерево) для создания исправлений. База исправлений была собрана на основе проблем в облачном сервисе контроля версий GitHub в формате GNU diff (текстовый формат отображения изменения в коде), которые были переведены в формат, пригодный для использования в AST, и распределены по кластерам [52].
Liana (Learn It AgaiN for APR) — инструмент для исправления ошибок для языка Java, обучающийся на своих исправлениях, чтобы на каждой следующей итерации улучшать их [53]. Исправления анализируются и в итоге выбирается лучшее, которое может содержать черты других исправлений, за счёт обучения ранжированию (англ. learning to rank).
SynFix — инструмент для языка Python, использующий RNN (Recurrent Neural Network, Рекуррентная Нейронная Сеть) для исправления синтаксических ошибок, обученную на ошибках студентов онлайн-курсов, и методы на основе ограничений для обеспечения функциональной корректности алгоритма [54].
Инструменты автоматического исправления программ могут значительно упростить часть работы разработчиков, касающуюся исправления ошибок. Но только часть этой работы. Исследования на основе различных бенчмарков показывают [55][56], что не все инструменты могут исправить даже половину ошибок. Рабочими (с точностью выше 75%) являются только инструменты на основе машинного обучения. Связано это с тем, что инструменты создаются на основе существующих бенчмарков и страдают от переобучения [57][58]: инструменты выдают правдоподобные результаты, которые позволяют пройти тесты, но не результаты, которые были бы корректны в контексте всей программы. Более того, улучшение тестового покрытия не всегда приводит к лучшей работе программ для исправления ошибок — инструменты могут не выдавать никакое исправление в принципе.
В этой главе будет проведён анализ методов и инструментов для поиска и исправления неисправностей на основе больших языковых моделей. Однако, прежде чем приступить к изучению методов и инструментов, необходимо изучить, как устроены большие языковые модели, по каким принципам они работают.
Языковая модель — это нейросетевая модель для работы с текстом. Задача такой модели — генерировать текст или, более формально, выводить следующий текстовый токен (часть слова) на основе предыдущих [59]. Языковые модели используются для генерации текста, распознавания речи, перевода и других задач, связанных с текстом.
Большая языковая модель — подвид языковой модели, характеризующимся использованием архитектуры трансформеров, генеративного пред-обучения, до-обучения и большим количеством параметров [60].
Архитектура трансформеров была представлена Google [61] и ввела механизм внимания, позволяющий модели учитывать определённые места во входных данных больше остальных за счёт перераспределения весов. Это позволило обучать модели быстрее, чем RNN.
Скачок в развитии БЯМ произошёл с приходом генеративного пред-обучения. Этот подход позволил моделям обучаться без использования специально размеченных текстов, что значительно расширило область применения моделей. В статье от OpenAI [62] сказано, что тренировка модели GPT-1 проходила в 2 этапа: 1. (пред-)обучение на неразмеченном тексте; 2. точечное дополнительное обучение модели под конкретную задачу — генерацию текста.
Обучение на неразмеченном тексте происходило на большом наборе данных BookCorpus). Исследователи OpenAI отмечают: так как задача модели — предсказание следующего слова на основе предыдущих, — то большой корпус текста, написанного людьми, с длинными последовательными выражениями содержит достаточно слов, идущих друг за других, чтобы определить закономерности в языке.
Дальнейшим развитием для БЯМ стало наращивание данных, используемых для пред-обучения, параметров модели, которые «хранят закономерности языка» (модель GPT-2 имеет 1.5 млрд параметров против 117 млн у GPT-1 [63]), до-обучении с использованием наборов данных с диалогами (GPT-3 [64]), использование «промптов» (англ. prompt, подсказка, которая даётся модели для дальнейшей генерации, ChatGPT), пошаговое решение (начавшееся как практика в промптах [65], а после ставшая частью некоторых моделей, например, PaLM-540B [66], OpenAI GPT-o1 [67] и DeepSeek-R1 [68]).
Обучение модели GPT-3 на текстовых данных с диалогами и создание на его базе продукта с механизмом чата и промптами — ChatGPT — стало поворотным моментом в распространении БЯМ. Как считает мыслитель Джон Носта [69], успех обусловлен работе продукта на «операционной системе человечества» — языке и его письменному воплощению — тексте. Модели хорошо показывают себя в работе с естественным языком, а как они показывают себя в задачах программирования — об этом будет рассказано в следующем разделе.
В этом разделе будут рассмотрены методы и инструменты для поиска неисправностей в ПО с использованием машинного обучения и больших языковых моделей. Некоторые из инструментов для поиска неисправностей можно отнести к статическому или динамическому анализу, однако упор инструментов на использование нейронных сетей и больших языковых моделей не позволяет ставить их в один ряд с классическими методами.
Как уже было сказано, статический анализ — метод исследования программного кода без запуска программы. Метод работает с исходным кодом программы или его представлением — таким образом, в любом случае, работает с текстом. Данный метод выглядит хорошим кандидатом на получение выгоды от больших языковых моделей. Далее будет рассказано об инструментах, которые попытались получить эту выгоду.
SkipAnalyzer [70] — статический анализатор и средство исправления ошибок, использующий БЯМ во всех своих компонентах: в поиске ошибок, фильтре ложных срабатываний и генераторе исправлений. Инструмент выполнен только в виде исследовательской работы и представляет собой проверку гипотезы (англ. proof-of-concept) с использованием моделей ChatGPT. Создатели инструмента утверждают, что он показывает результаты лучше, чем статический анализатор Infer от Facebook, не использующий БЯМ.
IRIS [71] — инструмент, сочетающий в себе CodeQL, описанный ранее, и БЯМ [72]. В ходе своей работы IRIS собирает кандидаты для ошибок в коде самой программы и используемых библиотеках, используя CodeQL, маркирует входы и выходы данных, потенциально пригодные для исследуемой ошибки, используя БЯМ, анализирует граф вызовов для входов и выходов, снова используя CodeQL, фильтрует ложные срабатывания и показывает результат разработчику, используя БЯМ. Как заявляют авторы, ключевой идеей проекта была гипотеза, что «большие языковые модели видели достаточно использований различных библиотек и API и у них должно быть понимание о представленных там ошибках».
Semgrep [73] реализует поиск неисправностей и уязвимостей в исходном коде на основе правил, заданных пользователем-разработчиком. Бесплатная версия, работающая локально, позволяет искать ошибки внутри одного файла, в то время как платная версия позволяет отслеживать выполнение кода между файлами, даёт доступ к платформе для поиска неисправностей и доступ к цифровому ассистенту на основе БЯМ, понижающему число ложный срабатываний. Инструмент можно использовать как отдельную утилиту через интерфейс командной строки (англ. CLI — command line interface), как часть процесса сборки, получая комментарии к изменениям — коммитам и запросам на изменения (англ. commits и Pull Requests).
DeepCode [74] — статический анализатор от Snyk, основанный на машинном обучении. Инструмент обучался на исходном коде проектов, выложенных на GitHub, используя AST для лучшего понимания конструкций языка. DeepCode AI лежит в основе целой платформы от Snyk, направленной на анализ ошибок и уязвимостей в программах.
Нетрудно заметить, что среди инструментов статического анализа крайне мало тех, которые полагаются на большие языковые модели полностью. Связано это, прежде всего, с природой языковых моделей: их основная задача — создание нового текста, а не тщательная обработка существующего. Известно также, что БЯМ склонны «галлюцинировать» — создавать правдоподобный, но не правдивый ответ [75].
Большие языковые модели основаны на статистике и поэтому восприимчивы к данным, используемым для обучения. Несмотря на то, что современные большие языковые модели обучены на корпусах текста, которые содержат в себе большую часть интернета, качественного исходного кода там крайне мало — ещё меньше исходного кода, пригодного для обучения поиска неисправностей, так как нужен код с ошибкой и без неё.
Показательны исследования как обычных моделей, так и программных комплексов в задаче по поиску неисправностей [76][77][78][79]. Например, разработчики универсального статического анализатора PVS-Studio провели тестирование инструмента DeepCode от Sync на проекте PhysX [80] и пришли к выводу, что классические методы с тонко настроенными правилами нахождения ошибок показывают себя лучше инструментов с большими языковыми моделями. Авторы утверждают, что «обучение на коде будет ограничено этим самым кодом и, если что-то будет выбиваться из общего паттерна или проявляться лишь в редких случаях, ошибка или недочет смогут проскочить незамеченными», в то время как «для классического же статического анализа нет необходимости, чтобы ошибка встречалась много-много раз — достаточно, чтобы разработчик диагностики придумал общий принцип появления ошибки».
Динамический анализ в классическом виде, строго говоря, невыполним с использованием больших языковых моделей, так как анализ предполагает работу не с текстом — исходным кодом программы, — а с запущенной программой. Однако, БЯМ могут помочь в создании инструментов для классического динамического анализа — например, в генерации тестов различного уровня: юнит-тестов, интеграционных тестов [81][82][83][84]. Однако следует помнить о галлюцинациях моделей и оставлять последнее решение об имплементации проверок за разработчиками.
Ранее было сказано, что большие языковые модели плохо справляются с анализом кода, однако хорошо — с генерацией текста: исходного кода и его объяснением. Исправление неисправностей, по определению — создание или исправление существующего кода по определённым требованиям, что звучит подходящей задачей для языковых моделей.
Большие языковые модели в контексте инструментов автоматизированного исправления программ являются продолжением методов на основе машинного обучения. Представление архитектуры трансформеров и увеличение данных для обучения положительно сказались на способности инструментов создавать исправления, проходящие тесты. Однако, большие языковые модели общего назначения, например ChatGPT, показывают себя хуже специализированных средств для исправления программ [85]. Использование же более специализированных моделей, обученных на корпусах с упором на исходный код, например InCoder, Codex, DeepSeek-Coder-V2, позволяет достигать лучших результатов, чем у инструментов для исправления программ без использования машинного обучения [86][87][88][89].
Кроме обособленных моделей на рынке также представлены инструменты для исправления программ. Например, DeepCode AI Fix — инструмент от Snyk, который был уже упомянут ранее, умеет не только искать ошибки, но и исправлять их [90], Patchwork, который позволяет пошагово собирать исправления [91], Style-Analyzer для исправления ошибок форматирования [92], CodePatchLLM — фреймворк для БЯМ, получающий обратную связь от статического анализатора Svace [93].
Как и в случае с классическими методами исправления программ, исправления инструментов на основе БЯМ должны быть в итоге проверены разработчиками-людьми. Особенно это важно, если принять во внимание галлюцинации моделей. И хотя современные инструменты для программирования предлагают проверку кода на основе языковых моделей (GitHub Copilot [94], CodeRabbit [95]), решение о том, насколько представленные замечания валидны, существенны ли они в данном проекте, соответствуют ли исправления требованиям к программе — всё это до сих пор может быть оценено только непосредственным разработчиком решения.
В этой главе будет рассказано о практической стороне работы — разработке программного прототипа для поиска и исправления неисправностей в программном обеспечении. Будет рассказано, что представляет из себя прототип, почему он именно такой, кем и как он может быть использован и какие результаты показывает при реальном использовании.
Артефактом данной работы является плагин для средства сборки Maven, который используется в Java проектах — bugfix-suggester-maven-plugin. Плагин предлагает исправления в исходном коде на основе ошибок, найденных другими плагинами — анализаторами кода, в частности, SpotBugs — одним из самых популярных анализаторов для языка Java. Для создания исправлений плагин использует сервер Ollama [96], который позволяет запускать БЯМ на мощностях, контролируемых пользователем.
Паттерн плагина для средств сборки — основной паттерн для разработки инструментов для средств сборки Maven. Менеджер зависимостей, анализаторы кода, инструменты применения форматирования, упаковка приложения в дистрибутивы — всё это выполнено в виде плагинов [97].
В качестве компонента для поиска ошибок был выбран плагин SpotBugs с расширением FindSecBugs, так как этот инструмент превосходит остальные подобные инструменты в ряде исследований [98][99], а также просто и гибко настраивается, имеет удобный графический интерфейс для просмотра найденных ошибок, выводит ошибки в виде HTML-файла, совместимого с файлами, из которых состоит отчёт сборки Java-приложения.
Для генерации исправлений используется инструмент для запуска БЯМ локально — Ollama. Выбор в пользу больших языковых моделей был сделан аналогично решению авторов IRIS, о котором было рассказано выше: языковые модели «видели» достаточно правильного использования библиотек, чтобы предложить решения, достойные к рассмотрению конечным пользователем. Выбор же в пользу локального запуска моделей был сделан по двум причинам: позиционирование решения и в исследовательских целях.
Артефакт работы ориентирован на пользователей, которые разрабатывают программное обеспечение с закрытым исходным кодом и не могут допустить его утечки. К таким клиентам относятся компании-разработчики систем безопасности, банковский сектор, промышленные предприятия. Такие пользователи предпочитают иметь решения, работающие на своих мощностях со своими стандартами безопасности, и не могут допустить использования инструментов, которые прямо или косвенно отправляют данные вне контура компании.
Большинство поставщиков облачных сервисов с БЯМ заверяют, что не используют пользовательский ввод и вывод для обучения моделей, однако эти же поставщики уточняют, что могут использовать ввод и вывод для улучшения продукта, не раскрывая, в чём именно это выражается. Например, политика приватности OpenAI [100] утверждает, что может использовать пользовательские данные для тренировки моделей до тех пор, пока пользователь это разрешает. Политика приватности GitHub Copilot [101] заявляет, что не использует пользовательский ввод для обучения моделей на тарифных планах для Бизнесов и Корпораций, ничего не говоря про Бесплатный и Профессиональный планы.
С исследовательской точки зрения работа проверяет возможности современных больших языковых моделей с открытым исходным кодом и устанавливаемых локально для полноценного использования в качестве ассистентов по созданию исходного кода и исправлению ошибок. Работа является проверкой концепта, что современные архитектуры БЯМ не привязаны к мощностям облачных провайдеров.
Как уже было сказано, артефакт представляет собой плагин для средства сборки Maven и предполагает соответствующее использование. Плагин размещается в скрипте сборки проекта после плагинов, используемых для анализа кода и принимает их отчёты во входном параметре как источники ошибок, для которых нужно создать исправления. В качестве следующего параметра нужно указать модель, доступную на используемом сервере Ollama, которая будет использоваться для генерации исправлений, и промпт — подсказку для модели, каким ожидается исправление.
Пример использования плагина-анализатора и артефакта работы:
<reporting>
<plugins>
<!-- mvn com.github.spotbugs:spotbugs-maven-plugin:gui to show GUI -->
<plugin>
<groupId>com.github.spotbugs</groupId>
<artifactId>spotbugs-maven-plugin</artifactId>
<version>4.8.5.0</version>
<configuration>
<effort>Max</effort>
<threshold>low</threshold>
<failOnError>true</failOnError>
<includeFilterFile>${project.basedir}/spotbugs-include.xml</includeFilterFile>
<excludeFilterFile>${project.basedir}/spotbugs-exclude.xml</excludeFilterFile>
<plugins>
<plugin>
<groupId>com.h3xstream.findsecbugs</groupId>
<artifactId>findsecbugs-plugin</artifactId>
<version>1.13.0</version>
</plugin>
</plugins>
</configuration>
</plugin>
<!-- mvn com.salat:bugfix-suggester-maven-plugin:suggest -->
<plugin>
<groupId>com.salat.bugfix-suggester</groupId>
<artifactId>bugfix-suggester-maven-plugin</artifactId>
<configuration>
<inputFileWithBugs>${build.directory}/spotbugsXml.xml</inputFileWithBugs>
<modelName>deepseek-coder-v2:lite</modelName>
<prompt>SpotBugs after analysis gives this error. Suggest a fix. The error: %bugContent%. Source code: ```%sourceFile%```. Keep the answer small and precise, code mostly.</prompt>
<modelRequestTimeout>9000</modelRequestTimeout>
</configuration>
</plugin>
</plugins>
</reporting>
Артефакт используется на стадии сборки site средства Maven. Во время этой стадии создаётся документация проекта, запускаются анализаторы кода, артефакт работы, затем файлы-отчёты всех инструментов собираются в сайт-отчёт и становятся доступны вместе с артефактом программы. Такой подход был выбран как продолжение работы SpotBugs: его основное использование это создание файлов-отчётов, поэтому инструмент для исправления ошибок должен работать таким же образом.
Также артефакт может запущен отдельно через интерфейс командной строки для использования в решениях, не поддерживающих отчёты.
При разработке артефакта работы было стремление минимизировать усилия по настройке инструмента, чтобы уменьшить негативное влияние на процесс разработки программного обеспечения. Именно поэтому 1. артефакт выполнен в виде плагина для сборки, а не полноценного отдельного инструмента — всё, что требуется для использования, это добавление зависимости в проект программы; 2. артефакт выполнен в виде плагина при стадии создания отчёта — это вспомогательная стадия и не влияет на компиляцию проекта программы.
Основная сложность, которая остаётся на стороне пользователя — управление сервером Ollama, предоставляющему языковые модели. Частью этой сложности является управлением времени сборки: так как большие языковые модели склонны требовать большое количество ресурсов, а на большом проекте может быть найдено большое количество ошибок, требующих исправления, время создания отчётов и нагрузка на мощности, используемые при сборки, может существенно возрасти. Управление этой сложностью вынесено за рамки данной работы, так как предполагается, что у пользователя есть свои требования к установке этого сервера, и автор работы не может их учесть целиком. Со стороны артефакта предложены средства для оптимизации работы — для сокращения времени создания отчёта и снижения нагрузки на сервера, используемые при сборке: 1. возможность указания сетевого адреса сервера Ollama, если используется установка его на отдельную машину; 2. указание названия модели для генерации исправлений для использования более быстрых и оптимизированных моделей.
Распространенной практикой при разработке больших программ является концепция Непрерывная Интеграция и Непрерывная доставка (англ. CI/CD — Continuous Integration & Continuous Delivery) [102], частью которого является ежедневный запуск конвейеров сборки приложения. Частой реализаций этой практики является запуск сложных конвейеров сборки в нерабочие часы организации, тогда как в рабочее время при изменениях кода запускаются более простые конвейеры, чтобы разработчики получали результаты сборки, результаты своих экспериментов. Артефакт работы создавался с учётом именно этой практики, в том числе из-за тщательности и скорости работы языковых моделей.
Несмотря на описанные выше сложности в виде увеличения времени и нагрузки на сервера для сборки проектов, стоит отметить его успешную применимость.
Работа артефакта проверялась на личном проекте автора briene (3182 строки кода), на проекте самого артефакта (1022 строки кода) и на проектах с открытым исходным кодом: JUnit4 как проект среднего размера (32 232 строки кода) и Debezium — большой многомодульный проект (249 803 строки кода). Тестирование проводилось на машине со следующими характеристиками: AMD Ryzen 7 7840H w/ Radeon(TM) 780M Graphics 3.80 GHz, 32 Гб ОЗУ. Конфигурация плагина для SpotBugs и артефакта представлены на фрагменте исходного кода ниже. Результаты тестирования представлены в таблице 1.
Конфигурация плагинов для испытания:
<!-- mvn com.github.spotbugs:spotbugs-maven-plugin:4.8.5.0:spotbugs -->
<plugin>
<groupId>com.github.spotbugs</groupId>
<artifactId>spotbugs-maven-plugin</artifactId>
<version>4.8.5.0</version>
<executions>
<execution>
<id>default-cli</id>
<configuration>
<fork>false</fork>
<effort>Max</effort>
<threshold>Low</threshold>
<failOnError>true</failOnError>
<includeFilterFile>${project.basedir}/spotbugs-include.xml</includeFilterFile>
<excludeFilterFile>${project.basedir}/spotbugs-exclude.xml</excludeFilterFile>
<outputDirectory>${project.build.directory}</outputDirectory>
<plugins>
<plugin>
<groupId>com.h3xstream.findsecbugs</groupId>
<artifactId>findsecbugs-plugin</artifactId>
<version>1.13.0</version>
</plugin>
</plugins>
</configuration>
</execution>
</executions>
</plugin>
<!-- mvn com.salat:bugfix-suggester-maven-plugin:suggest -->
<plugin>
<groupId>com.salat.bugfix-suggester</groupId>
<artifactId>bugfix-suggester-maven-plugin</artifactId>
<executions>
<execution>
<id>default-cli</id>
<configuration>
<modelName>deepseek-coder-v2:lite</modelName>
<modelRequestTimeout>9000</modelRequestTimeout>
<prompt>SpotBugs after analysis gives this error. Suggest a fix. The error: %bugContent%. Source code: ```%sourceFile%```. Keep the answer small and precise, code mostly.</prompt>
</configuration>
</execution>
</executions>
</plugin>
Таблица 1. Результаты испытания артефакта работы
Проект | Строк кода | Модель | Найдено ошибок (SpotBugs) | Предложено исправлений (bugfix-suggester) | Затраченное время |
---|---|---|---|---|---|
bugfix-suggester-maven-plugin | 1022 | deepseek-coder-v2:lite | 85 | 85 | 32 минуты |
bugfix-suggester-maven-plugin | 1022 | codegemma | 85 | 85 | 54 минуты |
briene | 3182 | deepseek-coder-v2:lite | 56 | 56 | 38 минут |
briene | 3182 | codegemma | 56 | 56 | 46 минут |
JUnit4 | 32 232 | deepseek-coder-v2:lite | 123 | 122 | 2 часа |
JUnit | 32 232 | codegemma | 123 | 122 | 2 часа 11 минут |
Debezium | 249 803 | deepseek-coder-v2:lite | 1216 | 510 | 10 часов 30 минут* |
* Не удалось завершить испытание
По результатам тестирования видно, что артефакт сгенерировал исправления для большинства ошибок, которые были найдены инструментом SpotBugs, и для большинства проектов артефакт создал исправления меньше, чем за рабочий день (8 часов). Исключение — проект Debezium, в котором было найдено 1216 ошибок, а генерация исправлений только для половины из них заняла свыше 10 часов. Это является свидетельством того, что плагин для поиска неисправностей настроен на поиск всех возможных ошибок и не защищён от ложных срабатываний. Также на машине, которая выполняла генерацию исправлений, не использовалось аппаратное ускорение: время генерации исправления от больших языковых моделей можно сократить, используя GPU (graphics processing unit) от Nvidia с тензорными ядрами или NPU (neural processing unit) от разных производителей [103].
Решение, предлагаемое в рамках данной работы, предлагает автоматизацию процессов обнаружения и исправления ошибок в программном обеспечении и направлено на улучшение следующих показателей:
Среди имеющихся на момент написания работы подобных инструментов, артефакт работы занимает свою узкоспециализированную нишу. Сервисы для автоматизации поиска и исправления неисправностей в программном обеспечении либо очень ограниченно используют большие языковые модели (IRIS), либо полагаются на них во всем и работают с использованием облачных провайдеров (DeepCode, GitHub Copilot, Coderabbit). Отличается также и способ работы: сторонние инструменты работают при ручном запуске (IRIS, DeepCode), на этапе публикации изменений (GitHub Copilot, Coderabbit). Если рассматривать непосредственно большие языковые модели, установленные на мощностях компании, то, как уже было описано выше, они не справляются с анализом исходного кода на предмет ошибок. Артефакт работы же использует большие языковые модели при генерации исправлений, не зависит от облачных решений, применяется на этапе сборки проекта, а для поиска исправлений использует статический анализатор.
В ходе данной работы были проанализированы средства поиска неисправностей с использованием статического и динамического анализа, изучены инструменты исправления неисправностей, было рассмотрено применение больших языковых моделей в соответствующих задачах. Был исследовал подход для поиска неисправностей с использованием статического анализатора и исправления неисправностей с использованием больших языковых моделей. По итогам анализа было получено, что лучше всего с поиском неисправностей справляются классические методы — статический и динамический анализы, а с исправлением — инструменты на основе больших языковых моделей.
Практическим результатом работы стало расширение для средства сборки программного обеспечения, использующее большие языковые модели как основу для предложения исправлений неисправностей. В ходе испытаний артефакта была доказана его применимость и польза в процессах поиска и исправления неисправностей, был получен эффект ускорения процесса работы с ошибками в программном обеспечении — с исправлением неисправностей артефакт работы справляется меньше, чем за рабочий день. В рамках работы в расширении реализована вся запланированная функциональность.
Говоря о дальнейшем векторе исследованией, можно выделить обработку ложных срабатываний с использованием отдельного обращения к большим языковым моделям и использование необходимого аппаратного ускорения для уменьшения времени ожидания ответа от сервера с большими языковыми моделями.