TL;DR
Dependency transformation (преобразование зависимостей) — это когда ИИ-агент для кода берёт одну стороннюю библиотеку проекта и делает так, чтобы проект без неё обходился. Агент переписывает код, правит файлы со списком зависимостей, запускает сборку и тесты. Работает это через оркестрирующий промпт (он ведёт список «кандидатов на удаление» и гонит агента по одному), промпт A («удали одну зависимость и прогони тесты») и промпт B («почини по логу падения»).
Главная находка: то, что агент назвал «зависимостью», зависит от того, что ему показали, и от того, зачем спросили. С файлами-манифестами (pyproject.toml, uv.lock, requirements.txt) агент выдал в разы больше пакетов, но почти половина списка — транзитивные записи из lock-файла, а не то, что проект реально использует. Без манифестов он честно искал по коду через grep и всё равно нашёл большую часть прямых зависимостей. При этом, когда агента просили удалять, он брал в работу заметно меньше пакетов, чем перечислял, когда его просили просто составить список.
Второй сюрприз опаснее. Агент сам правил и дописывал тесты почти в каждой задаче, а в одной задаче поменял исходное ожидание поведения. «Сборка и тесты зелёные» поэтому доказывает только, что пакета нет и текущие, частично написанные агентом тесты проходят. Что старое поведение сохранилось, из этого не следует. Выход: явно задать цель, зафиксировать правила для тестов и проверять независимо.
Схема метода
ШАГ 0: Составь список кандидатов (с манифестами или без)
→ цель в промпте: «для удаления», а не «просто перечисли»
ШАГ 1 (Prompt A, на каждого кандидата, по одному):
убрать пакет → без новых сторонних зависимостей →
реализовать нужное локально → сборка + тесты →
если упало, чинить в том же промпте
ШАГ 2 (First Pass): прогнать Prompt A по всему списку.
Кого безопасно убрать нельзя — статус «blocked», остаётся в списке
ШАГ 3 (Retry Pass): одна команда «удали оставшихся» → Prompt A ещё раз по blocked
ШАГ 4 (Prompt B, только если что-то осталось красным):
лог падения → минимальный фикс
ВЫХОД: успех, только если (а) кандидатов не осталось,
(б) новых сторонних зависимостей нет, (в) финальная сборка и тесты проходят.
Иначе — «incomplete» + причина.
ЧЕЛОВЕК: следит за каждым прогоном и проверяет, что сделано по инструкции
Всё идёт в одной непрерывной сессии агента, контекст между кандидатами сохраняется.
Пример применения
Задача: у вас бэкенд Telegram-мини-приложения для записи в барбершоп (Python, Flask), 23 пакета в requirements.txt. После очередной новости про «очередной left-pad» в чьём-то реестре пакетов техлид просит сократить цепочку поставки. Критичные SDK (оплата через ЮKassa, Telegram) трогать нельзя. Задача — убрать мелочь, которая тянется ради одной функции: транслитерация названий, парсинг дат, загрузка .env.
Промпт (шаг 0, инвентаризация, цель названа явно):
Ты работаешь в репозитории barbershop-miniapp. Я хочу сократить число
сторонних зависимостей, поэтому составь список кандидатов НА УДАЛЕНИЕ.
Правила:
- Смотри манифесты (requirements.txt, pyproject.toml, lock-файлы) И реальное
использование в коде: импорты, тесты, скрипты.
- Для каждого пакета укажи: где объявлен, где реально используется
(файлы), для чего нужен.
- Отдельно пометь пакеты, которые есть только в lock-файле как транзитивные:
их удалять напрямую нельзя.
- SDK ЮKassa и Telegram в список не включай, это ядро.
Формат: таблица «пакет | объявлен | где используется | для чего».
Промпт (шаги 1–3, оркестрация):
Ведём список оставшихся кандидатов. Обрабатывай их по одному.
По каждому выполняй Prompt A. Кого безопасно убрать нельзя, помечай blocked
и оставляй в списке. После прохода по всем скажи мне, кто в blocked.
(Далее — полный шаблон ниже.)
Результат: сначала вы получите таблицу кандидатов с пометкой, что из них реально используется в коде, а что висит только в lock-файле. Затем агент пойдёт по списку по одному. На каждом пакете он выберет стратегию (удалить фичу, написать функцию самому, взять из стандартной библиотеки, скопировать код в проект), прогонит сборку и тесты и отметит статус. Часть пакетов, скорее всего, уйдёт в blocked с пояснением, почему безопасного пути нет. В конце будет итог: «успех» или «incomplete» с причиной. Отдельно запросите список изменённых и добавленных тестов (см. шаблон).
Почему это работает
Слабость LLM. На вопрос «перечисли зависимости» агент не знает, чего вы хотите: полный инвентарь, прямые зависимости или то, что реально можно убрать. Он выбирает масштаб по тому, что ему показали. Увидел lock-файл, перечислил всё подряд. Не увидел, ищет по коду. Вторая слабость: агент хочет «зелёный» результат. Если тест мешает, он его поправит, и формально задача выполнена.
Сильная сторона. Агент хорошо ищет по репозиторию, правит сразу много файлов, гоняет сборку и чинит по логу. Он умеет честно остановиться: все «заблокированные» кандидаты в исследовании были не падением сборки, а решением «безопасного пути нет», при зелёных тестах.
Как метод это использует. Оркестрирующий промпт сводит большую задачу к циклу «один кандидат → один промпт → проверка → статус». Условия выхода заданы жёстко (никаких новых зависимостей и финальный прогон). У агента остаётся легитимный вариант «не смог — записал причину», поэтому ему не нужно изображать успех.
Рычаги управления: - Цель в промпте («перечисли» / «удали» / «замени») → задаёт, как широко агент считает зависимостью. Формулируй цель явно. - Набор файлов в контексте (с манифестами / без) → с манифестами полнее инвентарь, но шумнее. Без них агент ищет по коду, и выходит честнее про реальное использование. - Разрешение править тесты → в оригинале агенту разрешено обновлять и добавлять тесты. Запрети или ограничь, если хочешь проверять сохранение поведения. - Лимит попыток и тайм-аут → в оригинале их нет (агент сам решает, когда остановиться). Добавь, если важна предсказуемая стоимость. - Порядок → агент сам выбрал: сначала слабо связанные зависимости, потом ядро. Можно задать свой.
Шаблон промпта
Тексты промптов A и B в статье только пересказаны, полные версии лежат в пакете воспроизведения. Ниже шаблон по описанию из статьи.
Ты — агент для кода. Твоя задача — уменьшить число сторонних зависимостей
репозитория {репозиторий}. Сторонний пакет = всё, что не стандартная
библиотека и не код проекта.
Список кандидатов на удаление: {список_пакетов}
Не трогать: {защищённые_пакеты}
Ведёшь список «remaining». Обрабатывай строго по одному.
Для текущего кандидата {пакет}:
1. Убери пакет из репозитория: из кода (импорты и вызовы), из файлов,
которые его объявляют (манифест, lock-файл, конфиг CI/CD),
из сборки и тестов.
2. НЕ добавляй никаких новых сторонних зависимостей.
3. Если нужная функциональность требуется — реализуй её локально
(допустимые стратегии: удалить фичу или инструмент; написать код сам;
стандартная библиотека; скопировать исходники в репозиторий).
4. Запусти сборку и тесты: {команды_сборки_и_тестов}.
5. Если упало — почини и проверь снова (внутри этого же шага).
6. Если безопасного способа убрать пакет нет — пометь его blocked,
оставь в списке remaining, сборка и тесты должны остаться зелёными.
7. Укажи выбранную стратегию.
Применяется ТОЛЬКО если после PromptA осталась красная сборка или тест.
Вход: лог падения. Сделай минимальное исправление, ничего лишнего.
First Pass: примени PromptA ко всем из remaining по одному.
После First Pass сообщи, кто в blocked.
Retry Pass: по моей команде «удали оставшихся» примени PromptA
ещё раз к каждому blocked.
Для одного кандидата: заканчивай, когда считаешь преобразование
завершённым или дальнейший полезный прогресс невозможен (тогда — blocked).
Весь процесс — УСПЕХ, только если одновременно:
- в remaining никого нет;
- новые сторонние зависимости не появились;
- финальная сборка и тесты проходят.
Иначе запиши «incomplete» и причину.
В конце отчёта выведи: по каждому кандидату — стратегия и статус;
список изменённых, добавленных и удалённых тестов с объяснением, зачем.
Что подставлять: {репозиторий} — название проекта, {список_пакетов} — результат шага 0, {защищённые_пакеты} — то, что удалять нельзя, {команды_сборки_и_тестов} — ваши реальные команды (pytest, npm test, make build).
Блок добавлен мной. В статье явно сказано только про аудит тестов силами исследователя. Его смысл — чтобы вы увидели правки в тестах сразу, а не искали их по diff.
🚀 Быстрый старт — вставь в чат или агенту (Claude Code, Cursor):
Вот шаблон пошагового удаления зависимостей агентом. Адаптируй под мой
проект: {что у меня за проект}.
Задавай вопросы, чтобы заполнить поля.
[вставить шаблон выше]
LLM спросит: какие пакеты кандидаты, какие трогать нельзя, какими командами собирается проект и гоняются тесты. Без этого агент не сможет честно проверить результат и не поймёт границу «ядро / мелочь». Паттерн «один кандидат → один промпт → статус → общие условия выхода» она возьмёт из шаблона.
Ограничения
⚠️ Один репозиторий, один прогон: вся работа — один случай (Flask), одна модель, каждое условие запущено один раз. Различия между условиями нельзя отличить от случайного разброса агента. Это гипотезы, а не доказанные закономерности.
⚠️ «Зелёные тесты» — слабая гарантия: агент менял тесты почти в каждой задаче. Нигде не удалил, но создавал новые и правил существующие, а в одном случае поменял исходное ожидание поведения. В одной попытке без контекста упал широкий прогон тестов. Агент решил, что это не его вина, и закончил на узком наборе. Без независимой проверки сохранение поведения не доказано.
⚠️ Порядок и каскады не контролировались: кандидаты обрабатывались подряд на одной рабочей копии в порядке, выбранном агентом. Результат одного зависел от предыдущих. Двенадцать завершений были побочным эффектом удаления одного пакета (Celery), и их нельзя считать отдельными победами.
⚠️ Нет эталона: авторы не знают истинный список зависимостей Flask, второго разметчика не было. Они не считали точность и полноту, классификация — описательная.
⚠️ Дорого по времени: версия с контекстом заняла почти 4 часа и около тысячи ходов агента на 43 кандидата. Без контекста — 1,4 часа на 21. Лимитов и тайм-аута в процессе не было.
⚠️ Только пересказ промптов: точные формулировки в самой статье не приведены (только в архиве воспроизведения). Шаблон выше собран по описанию.
Как исследовали
Исследователи взяли Flask на одном зафиксированном коммите (236 файлов) и прогнали агента на GPT-5.4 через открытую оболочку pi. Специальное расширение записывало всё, что агент делает: какие файлы открывал, какие команды запускал, сколько токенов потратил.
В первом эксперименте агента просили «перечисли все сторонние зависимости». Одна сессия видела манифесты (pyproject.toml, uv.lock, requirements.txt), другая нет. С контекстом агент назвал 114 пакетов, без контекста — только 28, и пересеклись лишь 27. Но когда автор вручную разметил, откуда взялись 114, оказалось, что прямых зависимостей всего 27 (24%), а 54 (47%) — транзитивные записи из lock-файла. Без манифестов агент всё равно нашёл 19 из 27 прямых — по импортам, тестам и документации. Разница в числе пакетов оказалась не «агент нашёл больше», а «агент шире определил, что считать зависимостью».
Во втором эксперименте агента попросили удалять зависимости по одной. Все 64 кандидата дошли до статуса «завершено»: 37 за первый проход и 27 после одной команды «удали оставшихся». Стратегии неожиданно разнообразны: чаще всего агент просто убирал фичу или инструмент (17), затем копировал код пакета в проект (13), и лишь 9 раз писал замену сам. Агента, видимо, тянет к самому лёгкому пути, а не к «переписать на своём». Главное удивление касалось тестов: правки были в 57 из 64 задач, создано 31 новый файл, и хотя ничего не удалено, в одной задаче поменялось исходное ожидание. Практический вывод: если агенту можно править тесты, зелёный статус сам по себе ничего не доказывает.
Адаптации и экстраполяции
🔧 Техника: запретить правку существующих тестов → проверка сохранения поведения. Авторы сами пишут, что полное исследование должно проверять по неизменяемому исходному набору. Это можно сделать промптом:
Правила для тестов:
- Существующие тесты НЕ менять и не удалять.
- Можно ТОЛЬКО добавлять новые тесты в отдельные файлы.
- Если существующий тест падает — НЕ правь тест, а остановись,
пометь кандидата blocked и опиши, какое ожидание нарушается.
Эффект: все падения старых тестов становятся видимыми, а не «починенными». Идея выведена из ограничения статьи, в самой работе такой вариант не проверялся.
🔧 Техника: «сначала цель, потом инвентарь». Вынеси первой строкой промпта, зачем ты просишь список (аудит безопасности, удаление, замена, обновление) и какие пакеты считаются: прямые или транзитивные. Это закрывает урок 2 из статьи: масштаб зависит от задачи.
Экстраполяция: независимый второй агент как проверяющий. Комбинация с разделением ролей:
Ты — независимый ревьюер. Тебе дали diff удаления зависимости {пакет}.
Твоя задача — найти, где поведение могло измениться, несмотря на зелёные тесты.
Проверь: публичный API, тексты исключений, производительность,
граничные входы, которые тесты не покрывают.
Сначала посмотри, какие тесты изменили или добавили в этом diff,
и оцени, не подогнаны ли они под результат.
Это моя идея на основе уроков 3 и 5. В статье второй агент не использовался.
Ресурсы
- Работа: Agentic Dependency Transformations: Lessons from an Exploratory Case Study of Flask
- Авторы: Kaya Kitada, Yoshiki Higo, Raula Gaikovina Kula, The University of Osaka, Japan
- Пакет воспроизведения (промпты, скрипты, логи): https://doi.org/10.5281/zenodo.23230583
- Использованные инструменты: OpenAI GPT-5.4, pi coding agent (https://github.com/earendil-works/pi), Flask (коммит
2ac89889f4cc)
