Constraint decay: почему AI «забывает» твои ограничения в долгих сессиях — и что с этим делать
Я дал Claude Code задачу: рефакторинг PHP-модуля синхронизации Bitrix с 1С. Прописал в начале: строгая типизация, никаких mixed, никаких глобальных переменных, все методы через dependency injection.
Первые 4 метода вышли идеальными. Чистые сигнатуры, int|null там где надо, array вместо mixed.
Через час работы, на методе номер 11, смотрю в код и вижу mixed $data. Потом mixed $response. Потом функцию, которая принимает $config без типа вообще. Всё, что я просил в начале, растворилось.
Это не баг конкретной модели. Это феномен, который я наблюдаю уже два года. Я называю его constraint decay: деградация соблюдения ограничений по мере роста контекста сессии.
Что такое constraint decay — и почему это не про «контекстное окно кончилось»
Constraint decay — это не про длину контекста. Контекстное окно у современных моделей большое: Claude 4 держит 200k токенов. Проблема не в том, что модель «не видит» начало разговора. Она видит. Но по мере того как контекст наполняется кодом, файлами, промежуточными ответами — начальные ограничения начинают статистически «тонуть» в общей массе токенов. Модель продолжает генерировать код, опираясь всё больше на паттерны из середины сессии, а не на правила из начала.
Если в начале сессии ограничение занимает 200 токенов из 200, это 100% веса. Если к концу сессии контекст вырос до 80 000 токенов, те же 200 токенов с ограничением — это 0.25% веса. Attention механизм работает, но конкурирует с огромным объёмом другого контекста.
Ограничения не исчезают мгновенно. Они деградируют постепенно, и это опаснее внезапного отказа.
Как это выглядит в реальной PHP-сессии
В Bitrix-проектах я это наблюдаю конкретно вот как.
Начинаю с явного: «Все обработчики событий — через отдельные классы, реализующие интерфейс EventHandlerInterface. Никаких анонимных функций в AddEventHandler.»
Первые три класса — точно по паттерну. На пятом Claude начинает делать так:
\Bitrix\Main\EventManager::getInstance()->addEventHandler(
'sale', 'OnSaleOrderSaved',
function($event) {
// ... 40 строк кода
}
);
Ровно то, что я запретил. Я не менял задачу. Я не просил исключения. Ограничение просто выветрилось.
В другой сессии прописал: return types обязательны везде, PHP 8.1, строгая типизация через declare(strict_types=1) в каждом файле. К 12-му файлу два метода вернулись без return type вообще. Не неправильный тип — просто пропущен.
Эти примеры повторяются не случайно. Когда задача большая и контекст насыщенный, model drift — это закономерность, а не исключение.
Почему длинные free-form сессии — это production-риск
Риск не в том, что AI пишет откровенно плохой код. Он пишет код, который выглядит хорошо. Работает на тестах. Проходит первичный review.
Проблема в том, что он нарушает неочевидные инварианты. Тип mixed где должен быть int не ломает тест сразу. Он ломает логику через три вызова в рантайме. Анонимный обработчик вместо класса не падает в разработке. Он создаёт проблему при рефакторинге или тестировании через три месяца.
Я подсчитал: в сессиях длиннее 60 минут без явного re-anchoring — то есть без возврата к ограничениям — я нахожу нарушения исходных правил в среднем в 7 из 10 случаев. Не катастрофические, но требующие правки перед мержем.
В production PHP/Bitrix это означает: если ты даёшь AI долгую задачу и не контролируешь drift — ты получаешь код с технически корректным синтаксисом, но нарушенной архитектурой.
Техника первая: task isolation
Самый прямой ответ на constraint decay — не давать задачам становиться длинными.
Я разбиваю любую задачу, которая потенциально затронет больше 5 файлов или 500 строк нового кода, на подзадачи. Каждая подзадача — отдельная сессия с полными ограничениями в начале.
Вместо «рефактори весь модуль синхронизации» — пишу три задачи:
- Рефакторинг
SyncEventHandler(один класс, один файл) - Рефакторинг
ProductMapper(зависимости через конструктор) - Интеграционный тест для обоих
Каждая начинается с нуля.
Каждая начинается с нуля. Ограничения звучат полным голосом, потому что контекст ещё маленький.
Медленнее, чем один длинный промпт. Зато код требует намного меньше правок перед мержем. Для проекта на 28k SKU я перешёл на это примерно через 4 месяца работы с AI — количество «почему тут mixed вместо int» в review упало примерно втрое.
Техника вторая: re-anchoring
Иногда задача действительно неделима — сложный рефакторинг с контекстом, который нужен целиком. Для таких случаев я использую re-anchoring.
Re-anchoring — явное восстановление ограничений в середине сессии. В нужный момент я вставляю в чат блок напоминания:
Напоминание об ограничениях для этой задачи:
- Строгая типизация везде. PHP 8.1, declare(strict_types=1) в каждом файле.
- Никаких mixed. Все параметры — явные типы.
- Все обработчики событий — через классы с EventHandlerInterface.
- Никаких глобальных переменных.
Продолжай с учётом этих правил.
Это не объяснение. Это повторное включение ограничений в активную часть контекста.
Я делаю это каждые 20–30 минут долгой сессии, или после каждых 3–4 сгенерированных файлов — что наступит раньше. Занимает 30 секунд. Экономит 20 минут правок.
Re-anchoring работает не потому что «напоминает» модели. Он работает потому что возвращает ограничения в конец контекста, где они статистически влияют на следующий ответ сильнее всего.
Что это значит для команды
Если ты только начинаешь вводить AI-агентов в backend-разработку, вот практический вывод.
Не оценивай качество по первым двум-трём файлам. Они почти всегда хорошие. Смотри на 10-й, 12-й, 15-й. Там и видно, работает ли твой workflow.
Добавь в AI-ревью проверку на соответствие архитектурным правилам, а не только на синтаксис. Если у вас есть CLAUDE.md или аналог — проверяй, что агент действительно соблюдает то, что там написано, а не только первые два пункта.
И разделяй «AI пишет код» и «AI проектирует». Constraint decay — это аргумент в пользу того, что AI хорошо генерирует код внутри чётких ограничений. Но сами ограничения должны прийти от тебя. И ты должен следить, что они не растворились к середине задачи.
Это продолжает разговор, который я начал про сессионную гигиену и про то, что вообще можно давать AI в production. Контекст можно настроить правильно в начале — но без контроля во время работы этого недостаточно.
*Constraint decay не делает AI непригодным для production. Он делает его непригодным для бесконтрольного production. Разница принципиальная.*