Webrium
Подписаться

Промпт как код: почему он гниёт быстрее

Разбираем, почему промпт портится быстрее кода, какие привычные приёмы разработки его не спасают и при каком условии мы ошибаемся

Поле, разделённое вертикальным швом: слева код, которому мир сообщает об устаревших допущениях — падает сборка, краснеет тест; справа промпт, где расхождение с код: мир кричитпромпт: тишина

У промпта есть свойство, которого у кода нет: он портится, когда его никто не трогает. Репозиторий чистый, деплоев нет, а качество ответов медленно ползёт вниз. Первая реакция — искать баг в никуда: откатывать нечего, бисектить не по чему, а git blame показывает человека, который когда-то поправил запятую и ни при чём.

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

Почему он гниёт быстрее

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

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

Зависимость едет под вами. Модель — это зависимость без контракта. У библиотеки есть версия и сигнатуры: если поведение меняется, об этом объявляет смена мажора. Модель меняет поведение без объявления: тот же промпт, тот же вход — другое распределение выходов. Представьте библиотеку, у которой патч молча меняет семантику функции. Против такой зависимости гниёт любой код; промпты просто живут в этом мире по умолчанию.

Контракт растворён в формулировках. Внутри промпта зашиты допущения: какие входы бывают, какой у них формат, чего модель не должна делать никогда. Отдельно они нигде не записаны — размазаны по тексту. Когда входы меняются, допущения устаревают молча. git blame здесь врёт: он показывает, кто последним правил текст, а не кто изменил поведение. Поведение изменил вендор модели, а виноватым в репозитории выглядит человек, тронувший запятую.

Рост идёт в одну сторону. Каждый инцидент добавляет строчку «никогда не делай X». Удалить старую никто не решается: проверять удаление дорого, а несущая она или балласт — неизвестно. Промпт превращается в осадочную породу прошлых инцидентов, и слои начинают спорить: новое предписание противоречит старому запрету, а модель разрешает спор непредсказуемо. Чем длиннее текст, тем слабее держится каждый приказ — и тем охотнее добавляют ещё строку, потому что «а вдруг поможет». Петля закручена в обратную сторону.

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

Отсюда арифметика гниения. Стоимость проверки правки не зависит от размера правки. Значит, правки проверяют редко или на глаз. Каждая непроверенная правка оставляет систему в состоянии «мы не знаем, что она теперь делает», и накопленное незнание — это и есть гниение. У кода проверка локальна и дёшева, поэтому петля замыкается на каждый коммит. У промпта проверка глобальна и дорога, поэтому петля разомкнута. Он гниёт быстрее не по чьей-то вине — по арифметике.

Тупик: что пробуют первым

Первым делом переносят привычный цикл: промпт в git, ревью на диффы, эталонные тесты. Выглядит добросовестно. Не работает — и если не понять почему, следующая попытка унаследует ту же ошибку.

Git даёт историю без ответственности. Ценность истории кода в воспроизводимости: сбой воспроизводится, бисект находит виновный коммит. Поведение промпта ревизией не определяется — модель под ним уже другая. Откатываешь «сломавший» коммит — ничего не меняется, бисект водит по кругу. История есть, ответственности нет: красиво переплетённый дневник.

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

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

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

Что реально держит промпт

Если гниение — арифметика стоимости проверки, лечение тоже арифметическое: сделать проверку настолько дешёвой, чтобы она тикала сама. Всё остальное — следствия.

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

Контрольная выборка — и таймер. Набор фиксированных входов, настолько маленький, что его не жалко гонять часто. Идея та же, что у эталонных тестов, но сверяется не точный текст, а контракт из предыдущего абзаца. При каждой правке промпта выборка прогоняется целиком — локальной проверки не существует, мы это уже выяснили. Но главный сдвиг рамки в другом. CI для кода привязан к коммитам, потому что кроме вас код никто не меняет. Промпт меняется и без ваших коммитов: вендор модели делает свой «коммит» в вашем рантайме, а ваш CI об этом не узнает никогда. Значит, выборка должна гоняться по расписанию — без правок, без повода. Не ворота на входе, а канарейка в шахте. Это, по сути, единственное настоящее отличие дисциплины промптов от дисциплины кода.

Бюджет считается в одну строку: стоимость прогона = размер выборки × токены на прогон × цена токена. Подставьте свои. Правило одно: выборка должна быть такой, чтобы таймер не требовал ничьего разрешения. Если прогон надо обосновывать — выборка велика, а не дисциплина слаба. Как считать такой бюджет на реальной нагрузке — тема нашего материала про стоимость инференса.

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

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

Позиция редакции

Наш тезис: «промпт как код» — метафора, которая перевезла багаж наполовину. Хранение перевезли — git, ревью, история; жизненный цикл бросили на старой квартире. Код живёт не в git, а в петле, которая срабатывает сама, когда меняется мир. У промпта мир меняется чаще — под ним едет модель, — а петля дороже, потому что локальности нет. Поэтому храните промпт как код, а проверяйте как зависимость без контракта: по расписанию, а не только по коммиту. Пока проверка привязана к вашим правкам, гниение замечается по жалобам — то есть там, где чинить уже дороже, чем переписывать.

Мы неправны при понятном условии: модель под промптом неподвижна и воспроизводима. Зафиксированный контракт с вендором, локально поднятые веса, запертая версия, детерминированные параметры выборки — тогда у зависимости появляется версия, у поведения — воспроизводимость, у бисекта — смысл. Промпт честно становится кодом против зафиксированной библиотеки, и привычного цикла «коммит — тест — ревью» достаточно; статья не про ваш случай. Узкое второе условие: если выход промпта исчерпывается механически проверяемым форматом, эталонные тесты ловят всё, и гнить нечему.

Во всех остальных случаях — контракт, выборка и таймер: из механики следует именно это.

С чем это связано

Метка над заголовком говорит, зачем туда идти: продолжить тему, перейти к практике или увидеть возражение.

Два маршрута: короткий дорогой — погоня за одной цифрой точности, длинный дешёвый — набор метрик под разные режимы отказаодна цифранесколько метрик
Практика

Что мерить у модели кроме точности

Правите промпт — как понять, что стало лучше, а не хуже? Одна точность на этот вопрос не отвечает: меряйте калибровку и отдельные режимы отказа, из них собирается процедура проверки.

Вложенность: агент внутри плана работ, в глубине выделен; границу его ответственности проводят снаружи, а не изнутриГраница — снаружи
Углубление

Что агент не делает: список, который пишут заранее

Перечисление действий и запретов в инструкции не очерчивает зону ответственности агента. Границу проводят снаружи — списком из трёх колонок, который заодно служит планом проверок.

Счёт за инференс, разогнанный повторами вызовов, растёт и пробивает порог бюджета, зажигая сигнал тревогисчёт с повторамимесяц
Предыстория

Сколько стоит инференс: считаем до счёта, а не после

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

Письмо по вторникам

Один разбор недели и короткий список того, что изменилось. Без дайджестов на сорок ссылок.

Начните вводить — материалы появятся здесь.

выбрать · Enter открыть · Esc закрыть