Несмотря на впечатляющие успехи LLM в генерации кода, их применение в крупномасштабных проектах остаётся сложной задачей. Традиционные подходы, основанные на простых текстовых запросах (промптах), оказываются недостаточными для управления сложностью, взаимозависимостями и обеспечением консистентности кода в таких проектах.
Исследование, проведённое Вуянгом Чжаном, Янсонгом Ли, Зею Донгом, Ю Ву, Инъяо Чжоу, Доулеем Вангом, Сонгсироу Сингом и Да Шеном, предлагает новый подход Interoperable Literate Programming (ILP), который использует принципы грамотного программирования (Literate Programming — LP) для решения этих проблем. Давайте разберёмся, что такое ILP, как и зачем он был разработан, и рассмотрим практические решения, предложенные в исследовании, с акцентом на доказательную базу и пользу для IT-специалистов.
Проблемы традиционной генерации кода LLM в крупных проектах
Современные LLM демонстрируют впечатляющие возможности в генерации функциональных фрагментов кода, добавлении комментариев и выполнении задач, таких как перевод кода, исправление ошибок и генерация тестов. Однако, при масштабировании на крупные проекты, традиционные методы генерации кода на основе промптов сталкиваются с рядом ограничений:
-
Сложность API и взаимозависимости. Крупные проекты характеризуются сложными API и множеством взаимозависимых компонентов. LLM, обученные на огромных объёмах данных, часто не могут эффективно учитывать специфические API проекта и логические связи между его частями, полагаясь на свои общие знания и внешние библиотеки. Даже попытка подгрузить документацию внутрь LLM не всегда спасает ситуацию, так как модель периодически забывает ранее полученные данные.
-
Внешние предубеждения (External Bias). LLM склонны использовать знания, полученные в процессе обучения, и внешние ресурсы, игнорируя API и логику, специфичные для конкретного проекта. Это может привести к генерации кода, который функционально похож на запрошенный, но не соответствует архитектуре и стандартам проекта.
-
Непоследовательность решений. Незначительные изменения в промпте или контексте могут приводить к совершенно разным вариантам кода, что затрудняет получение стабильных и предсказуемых результатов, особенно в задачах, требующих строгой логики и консистентности.
-
Ограниченность контекста. LLM имеют ограниченное окно контекста, что может быть недостаточно для понимания логики и взаимосвязей в крупных проектах. При работе со сложными API или бизнес-логикой модель может упускать важную информацию, приводя к неполному или некорректному коду.
Концепция Interoperable Literate Programming (ILP)
Для преодоления этих ограничений исследование предлагает Interoperable Literate Programming (ILP) – подход, который структурирует процесс генерации кода LLM, опираясь на принципы грамотного программирования. ILP стремится предоставить структурированную документацию, не только описывающую код, но и направляющую LLM в процессе генерации, обеспечивая консистентность, точность и соответствие логике проекта.
Ключевые элементы концепции ILP:
-
Грамотное программирование (Literate Programming) в основе. ILP наследует идею LP о сочетании кода и естественного языка для создания целостного повествования, объясняющего логику и намерения разработчика. В отличие от традиционных комментариев, документация в стиле LP является неотъемлемой частью кода и организует его в логическом порядке, удобном для чтения и понимания человеком и машиной.
-
Язык Scheme для описания логики API. В ILP используется язык Scheme для формального описания логики API в виде направленного ациклического графа (DAG). Выбор Scheme обусловлен его минималистичным синтаксисом, λ-исчислением в основе и меньшей распространённостью в обучающих данных LLM, что снижает вероятность внешних предубеждений. DAG позволяет явно представить взаимосвязи между компонентами API и логический поток данных.
-
Пошаговая методология (Step-wise Methodology). Для упрощения сложных задач для LLM, ILP использует пошаговую методологию, вдохновлённую математической индукцией. Логика API разбивается на последовательность zero-step (базовый шаг) и successor-step (шаг преемника), что позволяет LLM обрабатывать информацию инкрементно и обеспечивает логическую консистентность.
-
Слой взаимодействия с LLM (LLM Interaction Layer). ILP включает в себя слой взаимодействия с LLM, который представляет собой структурированные аннотации (например, с использованием макроса define-with-docs в Scheme). Эти аннотации предоставляют LLM машиночитаемые поля, описывающие ключевые аспекты функций и компонентов API, такие как алгоритмические паттерны, сложность, стабильность и примеры использования.
Практические решения и инструменты ILP
Исследование предлагает практические инструменты для реализации концепции ILP:
-
Редактор Mogan — это UTF-8-совместимый редактор грамотного программирования, разработанный для упрощения рабочего процесса в крупных проектах. Mogan позволяет редактировать и экспортировать все файлы проекта в рамках единого документа (*.tmu), в отличие от разрозненных файлов в традиционных IDE или Jupyter Notebook. Это обеспечивает целостное представление проекта и облегчает навигацию и понимание взаимосвязей.
Сравнение грамотного программирования с Mogan и Jupyter Notebook. Как видно на рисунке, Mogan позволяет объединить в одном документе части кода из разных файлов, а также редактировать один и тот же файл в разных местах документа, создавая «паутину» взаимосвязанных идей. Mogan также поддерживает функцию “BUILD BUFFER”, которая позволяет экспортировать проект в компилируемый исходный код, сохраняя структуру и логику, описанную в ILP-документе.
Экспорт проекта из Mogan -
Интерпретатор Goldfish Scheme — это интерпретатор Scheme, разработанный специально для работы с LLM и поддержки ILP. Он включает в себя Python-подобную стандартную библиотеку для облегчения переноса знаний из Python, а также слой взаимодействия с LLM, который позволяет использовать структурированные аннотации и пошаговую методологию.
Пример использования макроса define-with-docs для описания функции quicksort в Goldfish Scheme:
(define-with-docs quicksort
#:pattern "divide-and-conquer"
#:complexity "O(n log n)"
#:stability "unstable"
#:examples
'((quicksort '(3 1 4 1 5 9 2 6 5 3))
=> (1 1 2 3 3 4 5 5 6 9))
(lambda (lst)
;; Implementation follows...
))
В этом примере аннотации #pattern, #complexity, #stability и #examples предоставляют LLM структурированную информацию о функции, помогая ей лучше понять ее логику и характеристики.
Детальный обзор экспериментов, подтверждающих эффективность ILP
Для всесторонней оценки ILP, исследователи провели серию экспериментов, охватывающих различные аспекты генерации кода LLM. Эксперименты были структурированы для детального изучения влияния ILP на качество, консистентность и эффективность генерируемого кода.
Экспериментальная установка и модели включали в себя широкий спектр LLM для обеспечения репрезентативности результатов. Использовались как удалённые коммерческие модели (GPT-4o и GPT-4 от OpenAI, Claude Sonnet от Anthropic), так и локально развёрнутые open-source модели (LLAMA 3.1 (версии 8B и 33B), Qwen-14B, Mistral-7B). Локальные модели были развёрнуты на высокопроизводительной рабочей станции, оснащённой AMD 7950X, 128GB RAM и двумя NVIDIA RTX 4090 GPUs.
Для оценки производительности применялся бенчмарк RepoBench, адаптированный для языка Scheme. RepoBench оценивает генерацию кода в контексте реальных репозиториев, учитывая взаимосвязи между файлами, документацию и тесты, что отличает его от изолированных бенчмарков вроде HumanEval или MBPP. Это делает его особенно подходящим для оценки ILP в крупномасштабных проектах. Эксперименты включали задачи генерации кода Scheme для различных функций, охватывающих широкий спектр операций: от базовых (make-list, null-list) и операций средней сложности (drop-right, take-right, split-at) до сложных (delete-duplicates, iota, append, reverse, for-each, remove, find)
Методология оценки
-
Сравнение с и без ILP: Для каждой функции RepoBench проводилось сравнение генерации кода в двух режимах:
-
С ILP-документацией: LLM предоставлялась подробная ILP-документация, включающая описание логики на естественном языке, формальное описание на Scheme (с использованием define-with-docs), пошаговую методологию (zero-step, successor-step) и примеры использования.
-
Без ILP-документации: LLM предоставлялся только текстовый запрос на генерацию функции, без какой-либо дополнительной документации или структурированных инструкций.
-
-
Метрики качества кода: Для объективной оценки качества сгенерированного кода использовались следующие метрики:
-
Функциональная корректность. Оценивалась по результатам прохождения автоматических тестов RepoBench. Метрика “Success” в таблице показывает, прошла ли функция все тесты (“S” — Success) или нет (“F” — Failure).
-
Структурная консистентность. Оценивалась визуально и путем анализа кода на предмет следования предписанным шаблонам, использования вспомогательных функций, обработки крайних случаев и проверок ошибок.
-
Стиль программирования. Оценивалось соответствие кода функциональному стилю программирования на Scheme, включая использование рекурсии, функций высшего порядка и избегание побочных эффектов.
-

Таблица демонстрирует значительное преимущество ILP в большинстве задач. Для GPT-4o и GPT-4, использование ILP-документации (столбцы “GPT-4o (R. D.)” и “GPT-4 (R. D.)”) привело к 100% успеху в реализации всех протестированных функций (обозначено “S”). В то же время при использовании только R7RS-описаний (столбец “GPT-4o (R.)”), наблюдались неудачи в реализации функций iota, split-at и delete-duplicates (обозначено “F”). Это наглядно демонстрирует, что ILP-документация существенно повышает надёжность и качество генерации кода LLM.
Интересно отметить, что небольшая модель LLAMA 3.1 2B, при использовании ILP-документации, показала результаты, сравнимые с GPT-4o и GPT-4. Это подтверждает, что ILP позволяет малым моделям компенсировать недостаток размера и достигать уровня производительности больших моделей благодаря структурированному руководству и явному описанию логики.
Для более глубокого изучения влияния именования функций, исследователи провели отдельный эксперимент, заменяя стандартные английские имена функций на произвольные имена на пиньине (китайская транскрипция латиницей). Например, вместо map использовалось ditu hanshu liebiao. Результаты показали, что при использовании нестандартных имён, LLM значительно повысили зависимость от ILP-документации, практически полностью игнорируя свои предустановленные знания о стандартных функциях Scheme. Это является сильным доказательством того, что ILP эффективно направляет LLM и снижает влияние внешних предубеждений.
Визуальный анализ сгенерированного кода подтвердил, что ILP способствует генерации более структурированного, консистентного и качественного кода. Код, сгенерированный с ILP, чаще включал обработку крайних случаев, проверки ошибок, вспомогательные функции и в целом лучше соответствовал функциональному стилю программирования на Scheme.
К итогам
ILP не просто предлагает способ «не облажаться» при использовании LLM в крупных проектах, а открывает путь к более эффективному, надежному и контролируемому процессу разработки, где LLM выступают в роли мощных, но управляемых помощников, а не непредсказуемых «черных ящиков». ILP имеет потенциал изменить подход к разработке ПО, делая ее более доступной, быстрой и качественной.
Оригинал исследования можно найти тут.
Автор: LesnoyChelovek