Недавно Марсело Эммерих написал пост, в котором предложил заменить менеджеры пакетов «реестром промптов». По мысли автора, разработчики библиотек станут публиковать промпты для ИИ. Разработчик вставляет промпт в свой ИИ-инструмент, который прямо на месте генерирует самодостаточную реализацию. Никаких транзитивных зависимостей, никаких атак на цепочку поставок, никаких конфликтов версий. Каждый раз генерируется свежий код, подогнанный под ваш язык и проект.
Это наивное представление, которое, однако, указывает на реальные проблемы. Атаки на цепочку поставок — серьёзная опасность. В самом деле сложно судить о деревьях, в которых содержатся транзитивные зависимости. Очевидно, что привлекательна перспектива сгенерировать именно то, что вам нужно — и ничего лишнего.
В сгенерированном коде всё равно ещё придётся реализовать TLS, разобрать JSON, обработать Unicode. Если вы перестали называть что-то «зависимостью», сложность от этого никуда не исчезнет, а просто переместиться. Но мне нравится углубляться в кроличьи норы — давайте же посмотрим, куда ведёт эта.
Вниз по кроличьей норе
Допустим, вы пишете промпт для библиотеки, обслуживающей HTTP-клиент. В промпте её поведение должно быть описано достаточно точно, чтобы ИИ мог сгенерировать что-то корректное. Не только благополучный путь. Нужно указать, как будет работать пул соединений, обрабатываться переадресация, описать поведение при задержках, логику повторных попыток, верификацию сертификата TLS, поддержку прокси. У каждой из этих задач есть пограничные случаи, варьирующиеся в разных операционных системах, архитектурах и версиях среды выполнения.
Таким образом, вы пишете тесты. Промпт должен сопровождаться набором тестов или хотя бы промптом для генерации набора тестов, поскольку в противном случае вы никак не сможете узнать, работает ли сгенерированный код на самом деле. Эти тесты должны покрывать целую матрицу платформ и сред выполнения. HTTP-клиент, работающий под Linux x86 с Python 3.12, но незаметно отбрасывающий заголовки под macOS ARM с Python 3.10 никуда не годится. А сама модель превращается в ещё одну ось матрицы. Так, GPT-4o может генерировать корректный код для обработки TLS, а Claude в таком же случае давать трудноуловимый баг — и наоборот. В то же время, привычная модель может начать выдавать уже другой код после того, как провайдер её обновит. То есть, весь набор тестов нужно прогонять заново при каждом обновлении версии модели. К тому моменту, как вы напишете промпт, в котором в достаточной степени детализированы все эти варианты поведения, плюс промпт для генерации достаточного тестового покрытия, позволяющего всё это проверить, у вас получится нечто гораздо более крупное, чем библиотека, которую вы пытались заменить.
Далее автор промпта его улучшает. Может быть, добавляет поддержку HTTP/2 или исправляет спецификацию пула соединений. Дальнейшие пользователи данного промпта должны об этом знать. У них должен быть выбор, когда переходить на новую версию. Поэтому вы начинаете версионировать промпты. Должна быть возможность сообщить: «я использую промпт версии 2.3 для HTTP-клиентов», так, чтобы было понятно, что речь о чём-то стабильном. Соответственно, понадобится журнал изменений. Требуется возможность закрепить хорошо известную версию, пока коллеги заняты тестированием новой.
Далее вы замечаете, что тестовые случаи для проверки HTTP-клиента очень похожи, независимо от того, на каком языке он написан. Пограничные случаи при верификации TLS не изменятся только лишь из-за того, что вы сгенерируете код на Python, а не на Go. Таким образом, вы извлекаете эти разделяемые спецификации тестов и упаковываете их в модули, рассчитанные на многоразовое использование. Другие авторы промптов также захотят пользоваться этими модулями. Теперь для модулей со спецификацией тестирования нужны собственные версии, поскольку недопустимо, чтобы при изменении совместно используемого набора тестов для TLS не происходило незаметного нарушения промпта для HTTP-клиента или промпта для WebSocket, который также от него зависит.
В данный момент в промпте для HTTP-клиента заявляется, что он работает с версиями модуля TLS от v1.2 до v1.x и с версиями v2.0 или выше модуля, который содержит спецификацию пула соединений. Это объявления зависимостей. Нужен механизм их разрешения, позволяющий определять, какие версии этих модулей взаимно совместимы. Вам понадобится lockfile (Promptfile.lock, если угодно), так, чтобы все коллеги по команде генерировали код на основе одних и тех же версий промптов.
Сами промпты становятся длинными и наполняются повторами. Вы замечаете, что снова и снова пишете шаблонные формулировки «обрабатывать задержки при…», «проверять сертификаты при…», «прослеживать переадресацию вплоть до N переходов, сохраняя заголовки кроме…». Начинаете прибегать к сокращениям. «Реализует HTTP-REDIRECT-SPEC-v2», а не прописываете её каждый раз полностью. Другие авторы промптов начинают перенимать ваши сокращения. Кто-то напишет документ, точно разъясняющий, что именно означает HTTP-REDIRECT-SPEC-v2 — и вот у вас есть язык спецификаций.
Со временем язык спецификаций уточняется, поскольку естественному языку присуща неоднозначность, и разные модели могут по-разному интерпретировать один и тот же промпт. Вы дополнительно его структурируете. Определяете точные сигнатуры функций. Указываете типы возвращаемых значений. Далее требуется конкретизировать поведение при ошибках настолько, чтобы две разные модели давали в таких случаях взаимозаменяемый вывод. Спецификация всё меньше походит на прозу, сближаясь с языком программирования. Становится формальным детерминированным описанием поведений, которые может надёжно воспроизводить машина.
Вот вы и написали менеджер пакетов. Просто стараетесь его так не называть. У вас версионируются модули с промптами, есть механизм разрешения зависимостей, lockfile, язык спецификаций, и вы всё чётче осознаёте, что вам на самом деле нужен детерминированное, формализованное описание вариантов поведения, которое каждый раз даёт одинаковый вывод. Или, по мотивам десятого правила Гринспуна:
Любая достаточно сложная прогр��мма содержит заново написанную, неспецифицированную, глючную и медленную реализацию половины менеджера пакетов.
Всё это требует достаточно высокой пропускной способности. Кроме того, реестр промптов также будет подвергаться атакам на цепочку поставок, направленным непосредственно на LLM и ИИ-агенты, от инъекции промпта через метаданные пакета до слопсквоттинга в сочетании с запутыванием зависимостей.
Что дают пакеты
Первоочередная ценность пакета заключается не в коде его реализации. Кто угодно может за выходные переписать curl на Rust, о чём много раз слышал Дэниэл Стенберг. Но вот нельзя переписать накопившиеся за двадцать лет багрепорты, странные пограничные случаи, на которые кто-то натыкался в продакшне и находил время, чтобы их исправить, аргументацию в тредах по проблемам, на основе которой, в конце концов, удавалось договориться о правильном поведении. Эти знания рассыпаны по истории пакетов и наросли естественным образом. Ни в каком промпте их не заключить.
Имена пакетов и номера версий подобны флажкам на горнолыжном треке. Это точки координации, к которым разработчики возвращаются снова и снова, чтобы проверить, не внёс ли кто-то новых улучшений. Когда специалист по поддержке широко используемой библиотеки фиксит в ней баг, далее эта правка распространяется по графу зависимостей. Тысячи проектов подхватывают внесённое улучшение, выкатив обновление версии. Никто это не координирует. Поддержка библиотеки даже не знает большинства её пользователей, работающих «ниже по течению». В свою очередь, этим пользователям не требуется понимать, каково внутреннее устройство сделанного обновления. Благодаря семантическому версионированию появился протокол, позволяющий выразить «это можно спокойно брать», не требующий явной координации во всех парах «производитель-потребитель».
Михил Буддинг написал об изолирующем цикле обратной связи в ИИ: по мере того, как разработчики покидают публичные форумы, предпочитая работать с приватными ИИ-ассистентами, коллективное знание дробится из-за огораживания, и общественное достояние деградирует. В экосистемах пакетов складывается противоположная динамика. Знания просачиваются наружу благодаря совместному использованию кода, а улучшения со временем накапливаются. Каждая независимо поддерживаемая библиотека со временем становится лучше благодаря багрепортам, пропатчиванию небезопасных мест, работе над производительностью и тем возможностям, которые добавляют пользователи, использующие библиотеку в реальной практике. Эти улучшения распределены неравномерно и иногда запутаны, но они накапливаются. Проект с 200 зависимостями тихо улучшается благодаря поддержке со стороны сотен человек, не имеющих к нему прямого отношения.
В мире с регистром промптов этот цикл будет разорван. Вы генерируете код для вашего HTTP-клиента на основе промпта. Через полгода кто-то обнаруживает неочевидный баг с верификацией TLS именно в том паттерне, который обычно производит этот промпт. В мире с менеджером пакетов мейнтейнер библиотеки внёс бы правку, вырезал релиз, а вы бы обновились. В мире промптов ваш код уже сгенерирован, лежит у вас в репозитории и, возможно, уже менялся. Сам промпт теперь может выдавать несколько другой код, поскольку базово изменилась сама модель. У вас нет стабильного «портрета» того кода, который вы выполняете, и нет никакой возможности сравнить его с тем, что промпт сгенерирует сегодня. Никто кроме вас больше не пользуется именно вашим выводом модели, соответственно, нет и сообщества, которое искало бы баги в общем коде. Все поколения изолированы.
В реестре промптов делается ещё одно допущение: автор промпта полностью прописал желаемое поведение кода. Но половина ценности зрелой библиотеки заключается в том поведении, которое автор и не думал явно прописывать. Это её корректность, которая сама собой формируется на материале многолетних отчетов о багах. Кто-то собрал в 2019 году странную конфигурацию прокси и заявил о проблеме. Кто-то ещё в 2021 году обнаружил, что при высокой конкурентности возникают условия гонки. Кто-то третий заметил, что конкретная комбинация заголовков отказывает на старых TLS-стеках. Теперь все эти исправления запечены в библиотеке. Невозможно учесть в промпте путь длиной в десять лет сбора отчётов о багах.
Кто управляет промптами
Реестры пакетов — это не просто хранилища файлов. В них решается, кто владеет именами, как решаются споры, что удаляется, и как поступать со скомпрометированными аккаунтами. Это органы управления, на уровне которых принимаются окончательные решения и поддерживается здоровье экосистем, а именно: удаляется вредоносное ПО, передаются заброшенные пакеты, обеспечивается соблюдение политик именования. Когда в npm восстановили пакет left-pad после того, как Азер Кочулу убрал его, это было управленческое решение, позволившее спасти работоспособность тысяч сборок. В реестре промптов ничего подобного нет. Кто решает, что конкретный промпт вредоносный? Кто выступает арбитром при конфликте, если два автора промптов претендуют на одно и то же имя при спецификации? Кто вмешается, если широко используемый промпт начнёт производить уязвимый код? Всё это управленческие вопросы, которые должны решаться институционально.
Да, это сотрудничество в значительной степени удалось автоматизировать — может быть, даже слишком, если вы когда-нибудь заглядывали в папку node_modules. Но в основе автоматизации всё равно лежат процессы, управляемые человеком: люди опираются на работу коллег без необходимости общаться друг с другом, и такую координацию невозможно прописать в промпте.
ИИ-агенты не положат конец использованию пакетов. Пакеты массово фигурируют в данных для обучения моделей, и любой агент уже первым делом прибегает к npm install или pip install. Интересная работа происходит в противоположном направлении: сейчас агентов учат лучше работать с менеджерами пакетов, а не заменять их. Я нарабатываю нав��к, позволяющий агентам скептически оценивать пакеты , прежде, чем их предлагать. Агент должен убедиться, что пакет существует, поддерживается, что данный вариант использования ещё не покрыт вариантами, содержащимися в стандартной библиотеке. Существует и более широкая возможность предоставить агентам общий протокол для управления пакетами, общий словарь для разрешения конфликтов, публикации и управления, работающий в разных экосистемах. Управление пакетами — это больной вопрос, и искусственный интеллект, глубоко понимающий менеджеры пакетов, представляется полезнее ИИ, который стремится их искоренить.
