Недавно релизнулся интересный open-source проект от Tencent — AutoCodeBenchmark. Это целый фреймворк, который автоматизирует создание, проверку и даже описание задач по программированию. По сути, это конвейер, где на входе — простейший кусок кода, а на выходе — полноценный бенчмарк с задачами, решениями и тестами на 20 языках.
Давайте заглянем под капот и разберем, как эта штука устроена. Тут вам и LLM, и куча Python-скриптов, и хитрая "песочница" для безопасного выполнения кода.
Если посмотреть на репозиторий, то вся система стоит на двух китах:
Взаимодействие этих двух компонентов и есть сердце AutoCodeBenchmark. AutoCodeGen
выступает в роли "креативного директора", который ставит задачи LLM, а MultiLanguageSandbox
— в роли "ОТК" (отдела технического контроля), который безжалостно отбраковывает нерабочий код. Этот цикл "генерация-проверка" и позволяет на выходе получать качественный контент.
А теперь разберем каждый компонент в деталях.
Это мозг всей операции, настоящий конвейер, который превращает простейшие идеи в полноценные учебные материалы. Процесс разбит на четкие, последовательные шаги, где каждый скрипт в директории AutoCodeGen/src
выполняет свою конкретную функцию. Давайте пройдемся по всему этому пайплайну.
Все начинается с "семян" — seeds
. В директории AutoCodeGen/data/seeds/
лежат jsonl
файлы для нескольких языков. Внутри каждого — до смешного простые примеры кода.
Например, python.jsonl
содержит:
{"text": "def add_two_numbers(a, b):\n return a + b", "language": "python"}
Это наша отправная точка. Идея в том, что даже из такой примитивной функции можно "вырастить" что-то более интересное.
Скрипт build_msg_for_solution.py
берет этот seed и, используя шаблон из templates/gen_code_solution_templates/
, формирует промпт для LLM.
[!INFO] Суть промпта: "Эй, LLM. Вот тебе простая функция. Преврати ее во что-то более сложное и осмысленное. А еще напиши для нее два набора тестов:
demo_testing()
с парой простых примеров иfull_testing()
с кучей кейсов, включая пограничные. Выдай все в трех отдельных блоках кода."
LLM (call_api.py
отправляет запрос) получает эту инструкцию и начинает творить. Например, из простого сложения двух чисел она может сделать функцию для суммирования элементов в сложных вложенных структурах данных или что-то в этом роде.
Затем extract_three_code_blocks.py
парсит ответ модели, извлекая из него три части:
canonical_solution
: Усложненная версия исходной функции.demo_test_func
: Простые демонстрационные тесты.full_test_func
: Полный набор тестов для проверки.Теперь в дело вступает MultiLanguageSandbox
. Скрипт call_sandbox.py
берет сгенерированные canonical_solution
и оба тестовых набора (demo_test_func
и full_test_func
) и отправляет их на выполнение.
[!NOTE] Важный момент: На этом этапе тесты еще не являются классическими unit-тестами с
assert
. Они просто вызывают функцию с разными входами и печатают результат в консоль. Цель этой проверки — убедиться, что сгенерированный код в принципе запускается, не падает с ошибкой и выдает какой-то предсказуемый вывод.
Результат этого запуска (то, что было напечатано в консоль) сохраняется. Если код упал или отработал некорректно, вся заготовка (задача + решение + тесты) отправляется в брак.
И вот тут начинается самое интересное. Скрипт build_msg_for_test.py
берет успешно прошедшие проверку данные и формирует новый промпт для LLM.
[!TIP] Суть промпта: "Смотри, LLM. Вот функция (
canonical_solution
). Вот тестовые вызовы (demo_test_input
) и вот реальный результат их выполнения из песочницы (demo_test_output
). Сделай из этого нормальные unit-тесты сassert
."
Мы не просто просим нейросеть написать тесты из головы. Мы даем ей фактические, верифицированные данные о поведении кода и просим обернуть их в формат assert
. Это на порядок повышает качество и корректность финальных тестов.
После ответа модели скрипт extract_two_code_blocks.py
снова парсит результат, но на этот раз извлекает уже финальные demo_test_func
и full_test_func
в виде полноценных unit-тестов.
Когда у нас есть верифицированное решение и верифицированные тесты, остается последний шаг — придумать для всего этого осмысленное задание для человека.
Скрипт build_msg_for_question.py
формирует финальный промпт.
[!INFO] Суть промпта: "Окей, LLM, последний рывок. Вот тебе готовый код решения и тесты к нему. Придумай и напиши полноценное условие задачи, которое бы соответствовало этому коду. Опиши, что нужно сделать, формат ввода-вывода и приведи примеры из
demo_test
."
extract_question.py
извлекает из ответа модели текст задачи в тегах <question>
, и на этом конвейер завершает свою работу.
На выходе из этого многоступенчатого процесса мы получаем полный и, что самое главное, проверенный пакет данных:
question
: Текстовое описание задачи.canonical_solution
: Эталонное решение на одном из исходных языков.demo_test_func
: Демонстрационные тесты (часто для примеров в условии).full_test_func
: Полный набор тестов для проверки решения.language
: Язык программирования.А дальше эти пакеты можно использовать как основу для перевода на другие языки (для этого есть build_msg_for_translation.py
) или для оценки моделей. Но прежде чем код куда-то пойдет, он должен пройти через "чистилище" — песочницу.
Если AutoCodeGen — это мозг, то MultiLanguageSandbox — это бронированная комната, где этот мозг проводит свои рискованные эксперименты. Главная задача этого компонента — выполнить потенциально небезопасный, сгенерированный нейросетью код так, чтобы он не навредил основной системе, и вернуть достоверный результат его работы.
Архитектура песочницы построена вокруг Flask-сервера (sandbox.py
), который принимает HTTP-запросы на выполнение кода. Внутри используется сложная система, чтобы обеспечить и безопасность, и поддержку множества языков.
code_config.yaml
: Это конфигурационный файл, который является сердцем мультиязычной поддержки. Для каждого языка здесь прописаны свои правила игры:
compile_cmd
и compile_flags
: Как компилировать код (если это компилируемый язык).execute_cmd
и execute_flags
: Как запускать код.file_extension
и file_name_template
: Как называть файлы.handler
: Указание на специальный обработчик для языков, требующих особого подхода (например, dotnet_handler
для C# или rust_handler
для Rust).error_check
: Список строк, наличие которых в выводе будет считаться ошибкой теста (например, "AssertionError").[!TIP] За счет этого файла добавление нового языка в песочницу сводится к описанию его правил в YAML, а не к переписыванию логики на Python. Очень гибко.
sandbox.py
(Flask App): Точка входа. Принимает JSON-запрос с кодом, языком и параметрами. Главный эндпоинт — /submit
. Он оркестрирует весь процесс внутри песочницы.
code_splicer.py
: Умный "сшиватель" кода. Перед выполнением часто нужно объединить код решения (func_code
) и код тестов (main_code
). Этот модуль делает это не тупым сложением строк, а с учетом синтаксиса языка:
using
/import
директивы.package main
и import (...)
.<?php ?>
.code.py
(CodeStore): Управляет временным рабочим окружением для каждого запроса. Он создает уникальную директорию, сохраняет туда исходный код, а для языков со сложной структурой проекта (вроде Rust с его Cargo.toml
или C# с .csproj
) разворачивает необходимый шаблон проекта.
safe_subprocess.py
: Самая важная часть с точки зрения безопасности. Это не просто обертка над стандартным subprocess
. Этот модуль запускает код от имени специального, максимально ограниченного в правах пользователя sandbox
(uid=1000
, gid=1000
).
root
.stdout
и stderr
.safe_subprocess
убивает всю группу процессов (os.killpg
), чтобы ни один "потомок" не остался висеть в системе.executor.py
: Непосредственный исполнитель. Он берет language_config
из YAML, формирует команды для компиляции и запуска и передает их в safe_subprocess
. После выполнения анализирует exit_code
, stdout
и stderr
, чтобы определить исход: PASSED
, COMPILATION_ERROR
, RUNTIME_ERROR
и т.д.
Запуск JVM — процесс довольно медленный и ресурсоемкий. Если для каждого Java-теста запускать новую виртуальную машину, производительность сильно упадет. Разработчики AutoCodeBenchmark решили эту проблему элегантно.
В jvm_pool_manager.py
реализован пул постоянно работающих JVM-процессов.
WorkerMain.java
), каждый из которых слушает свой порт.executor
не запускает новый процесс, а отправляет код по HTTP на свободный порт из пула.Ваша поддержка — это энергия для новых статей и проектов. Спасибо, что читаете!
AutoCodeBenchmark — это не просто очередной датасет. Это мощный и хорошо продуманный инструмент для генерации датасетов. Его архитектура показывает, как можно эффективно сочетать мощь генеративных моделей с надежностью и безопасностью изолированных сред выполнения.
Ключевые выводы, которые можно сделать из разбора этого проекта:
assert
'ы — ключевой элемент, обеспечивающий корректность тестов. Модель не фантазирует, а работает с фактами.