7 760 статей
arXiv:2611.05457 88 3 нояб. 2026 г. FREE

Hybrid LLM-Deterministic Pipelines: массовая миграция кода через жёсткое разделение скриптов и LLM

КЛЮЧЕВАЯ СУТЬ
Кодовые агенты часто тонут при обновлении больших проектов. Они путают тестовые библиотеки с боевыми и галлюцинируют в конфигах. Гибридный конвейер позволяет обновлять сотни репозиториев с трёхкратным ростом принятых пул-реквестов. Фишка метода: модель не пишет конфиг сама, а лишь извлекает JSON для шаблона по готовому пересечению кода и логов продакшена.
Адаптировать под запрос
⚡

TL;DR

Hybrid LLM-Deterministic Pipelines (Automated Uplift) — это архитектура конвейера для массового обновления кода и конфигураций, в которой детерминированные скрипты отвечают за классификацию задач, сбор контекста и проверку результата, а LLM зажата в рамки одного чётко очерченного шага трансформации. Вместо того чтобы пускать агента самостоятельно бродить по репозиторию и угадывать окружение, система заранее собирает зависимости из кода и логов реального выполнения (runtime telemetry), подставляет их в жёсткий шаблон промпта и проверяет результат автотестами до создания пул-реквеста.

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

Метод решает обе проблемы за 5 последовательных этапов: детерминированный сбор контекста и сверка импортов с логами продакшена, сборка промпта по шаблону архетипа, генерация изменений через LLM (либо извлечение JSON-параметров для кодогенератора), жёсткая валидация правилами и тестами с автоматическим возвратом ошибок модели (до 3 попыток) и отдельная read-only сессия для честного описания пул-реквеста со всеми оставшимися предупреждениями.


🔬

Схема метода

ШАГ 1 (Детерминированный): Классификация репозитория по реестру + сбор AST-импортов и логов продакшена → [Структурированный контекст]
ШАГ 2 (Детерминированный): Подстановка контекста в шаблон конкретного архетипа → [Готовый промпт]
ШАГ 3 (LLM — запись/извлечение): Правка файлов в песочнице ИЛИ извлечение параметров в JSON для шаблонизатора → [Кандидат изменений]
ШАГ 4 (Детерминированный цикл до 3 раз): Проверка правилами прошлых ошибок, сборкой и тестами → при сбое возврат лога ошибки на Шаг 3
ШАГ 5 (LLM — только чтение): Анализ логов миграции и оставшихся предупреждений → [Описание пул-реквеста для ревьюера]

Шаги выполняются в конвейере из отдельных вызовов: Шаг 3 (генерация с ретраями после проверок Шага 4) и Шаг 5 (описание PR в режиме чтения) — это разные обращения к модели.


🚀

Пример применения

Задача: В финтех-компании (например, в духе Точки или Т-Банка) 150 микросервисов на Python нужно перевести со старого самописного сборщика конфигураций legacy_deploy.yaml и разрозненных requirements.txt на единый стандарт pyproject.toml (Poetry/uv) без поломки воркеров в продакшене.

Промпт:


Ты — инженер миграции инфраструктуры. Твоя задача — перевести конфигурацию сборки микросервиса со старого формата на единый стандарт на основе предсобранного контекста.
Не меняй бизнес-логику приложения.



Односценарный фоновый сервис (worker) со старой конфигурацией деплоя без изолированных метаданных пакета.



1. Точка входа из логов продакшена: `src/payout_worker.py` (Python 3.11)
2. Подтверждённые боевые зависимости (найдены и в AST кода, и в трассировках запуска на кластере — зафиксируй эти версии):
   - `pydantic==2.6.4`
   - `aiohttp==3.9.3`
   - `PyYAML==6.0.1` (в коде импортируется как `yaml`, каноническое имя пакета — `PyYAML`)
   - `Pillow==10.2.0` (в коде импортируется как `PIL`, каноническое имя пакета — `Pillow`)
3. Зависимости только из исходного кода (не вызывались в продакшене — перенеси в группу `[tool.poetry.group.dev.dependencies]` без жёсткого пина версии):
   - `pytest`, `faker`, `locust`
4. Транзитивные модули только из рантайма (не добавляй в прямые зависимости):
   - `idna`, `multidict`, `yarl`



Вместо прямого написания файлов сборки извлеки параметры миграции строго в JSON по схеме ниже, чтобы детерминированный шаблонизатор сгенерировал `pyproject.toml` и обновил `Dockerfile`.



Проверь перед выводом, что не нарушены правила из прошлых сбоев:
1. Ни один импорт из группы 4 (транзитивные) не попал в список прямых зависимостей.
2. Для модулей `yaml` и `PIL` указаны канонические имена пакетов (`PyYAML`, `Pillow`), а не имена импортов.
3. Точка входа `[tool.poetry.scripts]` указывает ровно на `src/payout_worker.py`.
4. Старый файл `legacy_deploy.yaml` помечен к удалению, а не оставлен рядом с новым конфигом.



Верни только валидный JSON с полями:
- `package_name`: строка
- `python_version`: строка
- `entrypoint`: строка
- `prod_dependencies`: словарь `{каноническое_имя: точная_версия}`
- `dev_dependencies`: список строк
- `files_to_remove`: список путей
- `warnings`: список нестандартных мест в конфиге, требующих внимания владельца репозитория

Результат: Модель вернёт строго типизированный JSON-объект, где боевые зависимости (PyYAML, Pillow, pydantic, aiohttp) отделены от тестовых (pytest, faker, locust) и очищены от транзитивного мусора, имена модулей приведены к каноническим именам пакетов, а старый конфиг добавлен в список на удаление. На следующем шаге шаблонизатор соберёт из этого JSON безошибочный pyproject.toml, а список warnings пойдёт в честное описание пул-реквеста для команды-владельца.


🧠

Почему это работает

Когда LLM дают полную свободу в задаче миграции репозитория, она сталкивается с двумя слепыми зонами. Во-первых, в самом исходном коде нет данных о реальной среде исполнения: по файлам проекта невозможно понять, какой скрипт реально запускается на бою, а какой мёртвым грузом лежит пять лет, или почему условный import yaml требует пакет PyYAML конкретной версии с боевого кластера. Во-вторых, когда модель сама и классифицирует проект, и пишет конфиг с нуля, и проверяет себя, мелкие галлюцинации накапливаются и прорываются в пул-реквест.

Сильная сторона современных LLM — трансформация и извлечение данных в жёстких границах, когда весь недостающий контекст уже собран и разложен по полочкам. Если подать модели готовое пересечение статического анализа (AST) и логов продакшена, ей не нужно гадать: то, что есть и в коде, и в логах запуска — это боевая зависимость с точной версией; то, что только в коде — тестовая или условная ветка.

Главный архитектурный секрет метода — мелкий цикл обратной связи (shallow feedback loop) и правило: «расширяй роль LLM в генерации изменений, но никогда — в их проверке и классификации». Если формат целевого файла регулярен (как в CMake или стандартном конфиге), модель вообще не должна писать код напрямую: она только извлекает структуру в JSON, а код пишет детерминированный шаблон. Валидация (сборка, юнит-тесты, проверка --help и чек-лист прошлых ошибок) выполняется скриптами до открытия PR, поэтому ревьюеру приходит уже скомпилированный и проверенный результат.

Рычаги управления: - Роль LLM (прямая правка vs извлечение JSON под шаблон) → для разнородных репозиториев давай модели править файлы, а для однотипных конфигов заставь её возвращать JSON под шаблон (это исключает синтаксические баги и утроило долю принятых PR в исследовании). - Число итераций самоисправления (до 3 повторов на Шаге 4) → если сборка или правило проверки упали, возвращай модели лог ошибки как дополнительный контекст; больше 3 раз повторять бессмысленно — проще пометить кейс для человека. - Порог кастомного шаблона архетипа → создавай отдельные детальные шаблоны промптов только для массовых категорий (от 20 однотипных задач), а для редких исключений используй общий шаблон с пометкой «черновой PR для старта». - Разделение записи и описания (Шаг 3 vs Шаг 5) → генерируй описание PR отдельным вызовом в режиме «только чтение», чтобы агент честно выписал оставшиеся предупреждения, а не пытался скрыть недоделки.


📋

Шаблон промпта

Ниже — универсальный двухфазный шаблон для миграции или рефакторинга (Фаза А — трансформация/извлечение с самопроверкой по правилам прошлых сбоев, Фаза Б — честное read-only ревью для описания PR).


  
    Архетип задачи: {архетип_проекта}
    Боевое окружение и точки входа (из логов/конфигов): {данные_рантайма}
    Подтверждённые зависимости (код ∩ рантайм): {боевые_зависимости}
    Условные/тестовые зависимости (только код): {тестовые_зависимости}
  

  
    Твоя задача: выполнить миграцию {исходный_формат} → {целевой_формат} строго в рамках переданного контекста.
    Режим вывода: {режим_вывода: "JSON по схеме для шаблонизатора" или "Прямая правка файлов"}
    Схема / требования к результату:
    {схема_или_спецификация}
  

  
    Проверь свой результат по правилам частых сбоев (если есть ошибки с прошлой попытки — исправь их: {лог_ошибок_валидации}):
    1. Не оставлены ли старые конфигурационные файлы рядом с новыми.
    2. Совпадают ли имена импортов в коде с каноническими именами пакетов в реестре.
    3. Не попали ли условные или транзитивные зависимости в жёсткие боевые требования.
    4. {своё_правило_проверки}
  

  
    После формирования изменений переключись в режим Read-Only ревьюера и составь описание Pull Request для команды-владельца:
    - Что именно было изменено и на каких данных основан выбор версий.
    - Какие автоматические проверки пройдены.
    - Список предупреждений (warnings): какие места требуют ручной проверки перед деплоем.
  

Что подставлять в плейсхолдеры: - {архетип_проекта} — тип репозитория из вашей классификации (например, «CLI-утилита без метаданных пакета» или «Сервис со старым Makefile»). - {данные_рантайма} — какая версия языка, какие точки входа и флаги реально запускаются на сервере. - {боевые_зависимости} и {тестовые_зависимости} — результат сверки импортов в коде с реальными логами работы. - {исходный_формат} и {целевой_формат} — откуда и куда переезжаем (например, Webpack → Vite или pip → uv). - {лог_ошибок_валидации} — вывод компилятора, линтера или сравнения --help, если первая попытка упала (при первом запуске оставь пустым).

🚀 Быстрый старт — вставь в чат:

Вот шаблон Hybrid LLM-Deterministic Pipeline. Адаптируй под мою задачу: [опиши, какой код или конфиги нужно мигрировать]. 
Задавай вопросы, чтобы заполнить поля.

[вставить шаблон выше]

LLM спросит, из какого формата в какой вы переезжаете, какие данные об окружении у вас есть (логи, конфиги деплоя, список пакетов), можно ли задать выход через JSON-шаблон и на каких типичных ошибках обычно спотыкается сборка — потому что без разделения детерминированных проверок и границ генерации модель начнёт додумывать зависимости из головы. Она возьмёт паттерн из шаблона и адаптирует под задачу.


⚠️

Ограничения

⚠️ Узкое место — ревью людьми, а не генерация: Даже когда конвейер штампует сотни качественных пул-реквестов, большинство из них зависают без ответа от команд-владельцев, если миграция низкоприоритетная. Без автоматической компиляции и прогона тестов внутри самого пайплайна разработчики боятся брать на себя риск выкатки и откладывают PR в долгий ящик.

⚠️ Глубокий цикл обратной связи бьёт по принятию: В задачах, где успех миграции можно проверить только при реальном деплое приложения (например, пересборка пакетов со сложными динамическими зависимостями), доля влитых PR оказывается втрое ниже, чем там, где сборка и юнит-тесты полностью проходят до открытия PR.

⚠️ Нерентабельность для мелких уникальных групп: Разрабатывать отдельные шаблоны промптов, сборщики контекста и правила валидации имеет смысл только для типовых архетипов (от двух десятков репозиториев и больше). На штучных нестандартных репозиториях конвейер даёт лишь черновую заготовку.


🔍

Как исследовали

Команда инженеров Bloomberg и исследователей из University College London полгода проводила масштабный промышленный эксперимент Automated Uplift, охвативший около 8 600 репозиториев-кандидатов и сгенерировавший 2 333 реальных пул-реквеста в двух кампаниях: перевод разнородных Python-пакетов на изолированную систему упаковки (PyPackaging, 294 PR) и перевод однотипных C++-проектов со старого сборщика на CMake (CppBuild-to-CMake, 2 039 PR). В работе использовали Claude Sonnet 4.5 и Claude Opus 4.6 (в среднем агент делал 13 шагов за 48 секунд на C++ и 63 шага за 7.2 минуты на Python), а сам инструмент для Python состоял из 4 300 строк детерминированного кода против всего 230 строк интерфейса к LLM и 3 600 строк шаблонов и документации.

Самое поразительное открытие обнаружилось при подсчёте итогов через два месяца после заморозки генерации: всего было успешно влито 729 миграций (что сэкономило компании более 1 000 человеко-дней рутинного труда), но в CMake-кампании приняли 34.2% PR при всего 8.1% отказов, тогда как в Python-кампании влили лишь 10.5% PR при 21.8% отказов, а около 60–68% PR вообще остались без решения команд! Разгадка оказалась в глубине цикла проверки: в C++ LLM только извлекала JSON из Makefile, шаблон собирал CMakeLists.txt, а пайплайн тут же компилировал проект и гонял юнит-тесты — ревьюеру оставалось лишь нажать кнопку на заведомо рабочем коде. При этом даже «отклонённые» PR в Python принесли скрытую пользу: в 34 из 51 репозитория, где разработчики закрыли PR бота (часто просто потому, что не имели прав пушить в ветку бота), они следом сами завершили миграцию, а в 14 случаях напрямую скопировали изменения агента.


💡

Адаптации и экстраполяции

🔧 Техника: Схема «LLM извлекает JSON → Шаблон пишет код» → Нулевой риск синтаксических галлюцинаций

Если вам нужно массово переписать конфиги (Dockerfile, GitHub Actions, nginx.conf, pyproject.toml), запретите LLM генерировать сам файл. Попросите её вернуть строго валидируемый JSON с параметрами из старого конфига, а целевой файл соберите обычным Jinja2-шаблоном. Любая ошибка шаблона чинится один раз на весь флот проектов, а модель не сможет случайно сломать отступы или придумать несуществующую директиву.

Экстраполяция: Аудит мёртвых зависимостей и фич-флагов через матрицу «Код × Логи»

Принцип перекрёстной сверки статики и рантайма из исследования отлично переносится на очистку легаси-кода, настройку доступов или ревизию инструментов агента:

Сверь два списка для нашего проекта:
1. Статический список (все объявленные переменные окружения / зависимости / фич-флаги в коде): {список_из_кода}
2. Фактический след из логов продакшена за 30 дней: {список_из_логов}

Раздели их на 3 корзины:
- [Код ∩ Логи] → Боевое ядро: зафиксировать и покрыть строгой проверкой.
- [Только Код] → Спящие/тестовые/мёртвые элементы: пометить кандидатами на удаление или вынос в dev-контур.
- [Только Логи] → Неявные транзитивные зависимости: задокументировать, чтобы не сломать при обновлении окружения.

🔗

Ресурсы

Исследование: Hybrid LLM–Deterministic Pipelines for Enterprise-Scale Code Migration at Bloomberg (arXiv:2611.05457v1, ноябрь 2026)

Авторы: Mario Corchero, László Kiss Kollár, Haowen Liu, Shivam Bairoliya, Gala Malbasic, Serkan Kirbas (Bloomberg), David Williams, Federica Sarro (University College London)


📋 Дайджест исследования

Ключевая суть

Кодовые агенты часто тонут при обновлении больших проектов. Они путают тестовые библиотеки с боевыми и галлюцинируют в конфигах. Гибридный конвейер позволяет обновлять сотни репозиториев с трёхкратным ростом принятых пул-реквестов. Фишка метода: модель не пишет конфиг сама, а лишь извлекает JSON для шаблона по готовому пересечению кода и логов продакшена.

Принцип работы

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

Почему работает

В исходном коде нет данных о реальном сервере. По файлам не видно, какой скрипт крутится в бою, а какой мёртвым грузом лежит пять лет. Когда скрипты заранее сверяют импорты в коде с логами сервера, модели не нужно гадать. Замена прямой генерации кода на выдачу JSON под шаблон утроила долю принятых пул-реквестов. Цикл до 3 автопроверок компилятором и тестами отсекает поломки до ревью человеком, а отдельная сессия в режиме чтения честно выписывает предупреждения.

Когда применять

Массовое обновление кода и инфраструктуры → конкретно для перевода десятков микросервисов на новые сборщики, конфиги или версии пакетов, особенно когда разработчики боятся принимать автоматические пул-реквесты. НЕ подходит для штучных нестандартных репозиториев (шаблон окупается от 20 однотипных задач).

Мини-рецепт

1. Собери факты скриптом: пересеки импорты из кода с логами продакшена (что есть в обоих местах — боевое, что только в коде — тестовое).
2. Сожми задачу до JSON: попроси модель не писать конфиг с нуля, а извлечь параметры в строгую JSON-схему для шаблонизатора.
3. Включи фильтр прошлых граблей: добавь в промпт <правила>удалить старый конфиг, заменить имена импортов PIL/yaml на пакеты Pillow/PyYAML.
4. Крути цикл проверки до 3 раз: проверяй результат сборкой и тестами, при ошибке возвращай лог модели.
5. Сделай честное описание: отдельным запросом в режиме только чтения попроси выписать все оставшиеся риски для ревьюера.

Примеры

[ПЛОХО] : Переведи этот репозиторий со старого legacy_deploy.yaml и requirements.txt на новый pyproject.toml и ничего не сломай
[ХОРОШО] : Точка входа из логов сервера: src/payout_worker.py (Python 3.11). Боевые пакеты (код + логи): pydantic==2.6.4, PyYAML==6.0.1 (импорт yaml). Только в коде (перенеси в dev): pytest, faker. Извлеки параметры строго в JSON по схеме для шаблонизатора, пометь старый конфиг к удалению и выпиши warnings
Источник: Hybrid LLM-Deterministic Pipelines for Enterprise-Scale Code Migration at Bloomberg
ArXiv ID: 2611.05457 | Сгенерировано: 2026-11-09 11:08

Проблемы LLM

ПроблемаСутьКак обойти
Модель путает боевые и тестовые зависимости при миграции кодаПо исходному коду нельзя понять, какие модули реально работают на сервере. Модель смешивает рабочие пакеты, тестовые библиотеки и косвенные зависимости. Сборка падает при выкаткеСобери зависимости скриптом до вызова модели. Сверь импорты в коде с логами реального запуска. Передай в промпт готовые списки: отдельно боевые пакеты с версиями, отдельно тестовые
Агент скрывает недоделки в описании измененийКогда одна и та же сессия правит код и пишет отчёт для проверки, модель приукрашивает результат. Она замалчивает сомнительные места и обходные костыли. Ревьюер пропускает скрытый багРаздели генерацию правок и написание отчёта на два разных вызова. Второй запрос запускай в режиме только для чтения. Подай ему логи сборки и попроси честно выписать все предупреждения для человека

Методы

МетодСуть
Извлечение параметров в JSON вместо прямой генерации конфиговНе проси модель писать регулярный файл сборки или конфигурации с нуля. Попроси вернуть только переменные в строгом формате JSON по схеме . Сам файл собери детерминированным шаблоном. Почему работает: Модель не может нарушить синтаксис целевого формата или выдумать лишние секции. Она решает только узкую задачу разбора данных. Доля принятых изменений вырастает втрое. Когда применять: Массовая миграция однотипных конфигов, сборок, манифестов. Когда не работает: Уникальный код со сложной логикой, который нельзя уложить в шаблон
Короткий цикл самоисправления по логам проверкиПроверяй результат работы модели скриптами, компилятором и списком прошлых ошибок до отправки человеку. Если проверка упала, верни лог сбоя обратно в промпт. Делай не более 3 попыток. Почему работает: Модель видит точную причину падения и быстро правит типовой промах. Если за 3 итерации ошибка не ушла, задача слишком сложная. Дальнейшие повторы только портят код. Когда применять: Автоматический рефакторинг и обновление зависимостей при наличии быстрых автотестов. Когда не работает: Среды без автоматической сборки, где ошибку видно только после выкатки

Тезисы

ТезисКомментарий
Расширяй роль модели в генерации, но никогда — в классификации и проверкеМодель отлично заполняет пробелы и переводит данные из одного формата в другой в жёстких рамках. Но при свободной навигации она каждый раз классифицирует проект иначе и пропускает собственные ошибки. Применяй: Отдай отбор файлов, сбор контекста и финальные тесты жёстким скриптам. Модели оставь ровно один изолированный шаг трансформации
Быстрая проверка до ревью повышает доверие к автопатчам в 3 разаРазработчики боятся принимать автоматические изменения, если их нельзя проверить локально. Когда конвейер сам компилирует код и прогоняет тесты до создания задачи на ревью, доля влитых правок вырастает втрое. Применяй: Встраивай запуск сборки и проверку вызова справки --help прямо в конвейер перед отправкой кода человеку
📖 Простыми словами

HybridLLM-Deterministic Pipelines for Enterprise-Scale Code Migration at Bloomberg

arXiv: 2611.05457

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

Это как если бы вы делали ремонт и наняли дизайнера, но запретили ему самому покупать материалы и общаться с прорабами. Вы сами делаете замеры, приносите ему конкретные образцы плитки и говорите: «Просто нарисуй, как это разложить». Дизайнер (LLM) делает свою творческую часть, а потом вы проверяете чертеж по линейке. Если он нарисовал окно там, где несущая стена — чертеж летит в шредер, а не строителям. В этом подходе автоматизированный аплифт работает так же: скрипт готовит почву, LLM трансформирует код, а тесты рубят галлюцинации на корню.

Главная фишка здесь — использование runtime telemetry, то есть данных из реальной жизни сервиса. Обычное SEO-шное сканирование файлов не скажет вам, что старый пакет PyYAML тянет за собой хвост из зависимостей, которые важны для продакшена. Система Bloomberg сначала смотрит логи выполнения, понимает, что реально крутится в памяти, и только потом скармливает эти данные модели. Это убивает главную проблему — когда код вроде бы правильный, но в облаке он не заводится, потому что окружение настроено иначе.

Хотя метод обкатывали на суровом финтехе и переезде на pyproject.toml, принцип гибридного конвейера применим везде, где нужно массово перелопатить тысячи файлов. Будь то обновление API, смена библиотек логирования или миграция с одного фреймворка на другой — схема одна. Сначала жесткий алгоритм собирает факты, потом LLM делает «красиво», а в конце автотесты подтверждают, что ничего не взорвалось. Агентская автономность — это миф, в реальности работает только жесткая дисциплина и проверка каждого шага.

Короче, хватит надеяться, что нейронка сама разберется в вашем легаси-коде — она просто нафантазирует то, чего нет. Будущее за гибридными пайплайнами, где LLM — это просто функция для переписывания текста, а не «умный помощник». Либо ты строишь вокруг модели забор из проверок и телеметрии, либо получаешь тысячи пул-реквестов, которые формально выглядят ок, но ломают продакшен при первом же запуске. Выбирай: или ты контролируешь процесс, или гадаешь, почему все упало.

Работа с исследованием

Адаптируйте исследование под ваши задачи или создайте готовый промпт на основе техник из исследования.

0 / 2000
~0.5-2 N-токенов ~10-30с
~0.3-1 N-токенов ~5-15с