Почему ИИ начинает ошибаться в длинной разработке — и что с этим делать
Есть неприятный эффект, который хорошо знаком тем, кто пытался вести с ИИ не небольшой скрипт на вечер, а настоящий проект.
В начале всё выглядит почти идеально.
Вы ставите задачу. Модель быстро разбирается в коде, предлагает архитектуру, находит ошибки, пишет изменения. Несколько итераций проходят хорошо.
Потом появляются исправления.
Потом исправления исправлений.
Меняется архитектура. Какие-то решения отменяются. Появляются новые ограничения. Один баг оказывается следствием другого. Проект разрастается.
И в какой-то момент происходит странная вещь.
ИИ всё ещё уверенно отвечает. Он по-прежнему пишет технически убедительный текст. Но начинает ссылаться на уже устаревшее решение, возвращать закрытую проблему, использовать старое состояние репозитория или уверенно утверждать то, чего фактически никто не проверял.
Причём это не обязательно выглядит как очевидная «галлюцинация».
Чаще ошибка намного опаснее.
Ответ выглядит логично.
Просто он основан не на текущем состоянии проекта, а на одной из его прошлых версий.
Проблема не только в размере контекстного окна
Обычно такую ситуацию объясняют просто: закончился контекст.
Но на практике всё интереснее.
Мы заметили, что очень длинный диалог может продолжать работать стабильно гораздо дольше, чем ожидалось, если состояние проекта представлено правильно.
И наоборот: сравнительно короткая разработка может довольно быстро превратиться в хаос, если проект существует только в форме истории переписки.
Представим обычную работу с ИИ:
задача ↓ ответ модели ↓ исправление ↓ ещё одно исправление ↓ новое архитектурное решение ↓ отмена предыдущего решения ↓ новый баг ↓ объяснение бага ↓ патч ↓ ещё один патч
Через некоторое время чат содержит одновременно старую архитектуру, новую архитектуру, ошибочные гипотезы, исправленные гипотезы, предыдущие версии файлов, новые версии файлов и десятки промежуточных решений.
Человеку ещё относительно легко сказать:
Это было три часа назад. Мы уже выяснили, что причина другая.
Для модели история выглядит иначе. Перед ней находится огромное количество семантически похожей информации.
И ей нужно определить, какая из этих версий сейчас является истиной.
Именно здесь начинается context drift — дрейф контекста.
Самая опасная галлюцинация — не выдуманный API
Когда говорят о галлюцинациях ИИ в программировании, обычно приводят очевидные примеры: модель придумала функцию, библиотеку или параметр.
Но в длинной разработке гораздо опаснее другой класс ошибок.
Например:
старый HEAD принимается за текущий HEAD старый acceptance criterion принимается за действующий закрытый дефект снова считается открытым гипотеза из предыдущего расследования воспринимается как установленный факт слово PASS из отчёта исполнителя воспринимается как доказательство отменённое архитектурное решение внезапно возвращается в проект
Каждый такой вывод сам по себе может выглядеть совершенно разумно.
Проблема в provenance — происхождении утверждения.
Откуда мы знаем, что это действительно правда сейчас?
Что изменилось у нас
Во время разработки нескольких проектов мы постепенно ушли от схемы:
человек → промт → модель → код
к схеме, где между намерением человека и изменением системы появилась инженерная структура.
Проект получил явное состояние:
CURRENT HEAD CURRENT TREE ACTIVE CONTRACT FROZEN DECISIONS OPEN BLOCKERS REJECTED HYPOTHESES RAW EVIDENCE TEST RESULT HUMAN OBSERVATION NEXT EXACT OPERATION
И произошло неожиданное.
Мы строили эту систему прежде всего для контроля разработки.
Но обнаружили второй эффект: модель стала намного стабильнее работать в очень длинных диалогах.
Не потому, что она стала лучше запоминать.
А потому, что ей стало значительно меньше нужно помнить.
Чат перестал быть базой данных проекта
Это, вероятно, ключевой момент.
В обычной разработке через ИИ сам разговор постепенно превращается в единственный источник состояния.
Чтобы понять, что происходит сейчас, нужно мысленно восстановить историю:
Сначала сделали A. Потом из-за B переделали A. После этого оказалось C. Потом C признали ложной гипотезой. Поэтому вернулись к части A, но с другим ограничением…
Чем длиннее такая история, тем выше вероятность ошибки.
Мы заменили её другим принципом:
Чат не является source of truth.
Источник истины — состояние проекта и доказательства.
Разговор нужен для принятия решений и управления операциями.
Но если между словами в переписке и текущим репозиторием есть конфликт, репозиторий выигрывает.
Если исполнитель написал PASS, но evidence не подтверждает результат, PASS не становится частью состояния проекта.
Если старое решение было отменено, оно не должно продолжать конкурировать с новым только потому, что находится выше в чате.
Это очень сильно меняет сам характер работы модели.
Evidence outranks claims
Один из принципов, который оказался особенно важным:
EVIDENCE > CLAIM
Фраза:
Все тесты прошли.
сама по себе ничего не доказывает.
Нужен результат теста.
Фраза:
Исправление находится в текущей версии.
не доказывает, что мы действительно проверяем правильный commit.
Нужен HEAD.
Фраза:
Дефект устранён.
не равна наблюдаемому результату.
Нужен reproducible test, лог, diff, hash или human observation — в зависимости от характера проблемы.
Почему это влияет именно на стабильность ИИ?
Потому что ошибочный вывод перестаёт автоматически наследоваться следующей итерацией.
В обычном чате одна неправильная предпосылка может жить десятки сообщений:
ошибка ↓ модель принимает её за факт ↓ строит следующее решение ↓ следующее решение уже содержит эту предпосылку ↓ через несколько итераций никто не помнит, откуда она вообще появилась
Когда между утверждением и принятием его в состояние существует evidence gate, этот эффект сильно уменьшается.
Замороженные решения уменьшают архитектурный дрейф
Есть ещё одна проблема длинных разговоров.
Модель умеет находить альтернативы.
Это плюс, пока архитектура обсуждается.
Но после принятия решения это может стать минусом.
Через несколько десятков итераций модель снова видит старую проблему и предлагает красивое решение, не замечая, что этот путь уже обсуждался и был сознательно отвергнут.
Поэтому у нас появились frozen decisions.
Условно:
FROZEN решение принято; основание зафиксировано; без нового evidence не переоткрывать.
Это не означает, что архитектуру больше нельзя менять.
Можно.
Но её изменение становится отдельным инженерным событием, а не побочным эффектом очередного ответа модели.
Декомпозиция ограничивает пространство галлюцинации
Очень сильно изменилась и постановка задач.
Есть огромная разница между:
Исправь приложение.
и:
Вот конкретный дефект. Вот наблюдаемое поведение. Вот ожидаемое поведение. Вот граница изменения. Эти компоненты менять запрещено. Вот критерий завершения. Вот evidence, который должен подтвердить исправление.
Чем меньше пространство допустимых решений, тем меньше пространства остаётся для догадок.
ИИ всё ещё может ошибиться.
Но ошибка становится локальной и, что гораздо важнее, определяемой.
Для нас это стало важнее попытки создать систему, в которой модель якобы никогда не ошибается.
Такой системы не существует.
Полезнее сделать так, чтобы ошибка не могла незаметно превратиться в архитектуру.
Разделение исполнения и проверки
Есть ещё один принцип, который кажется очевидным в обычной инженерии, но часто исчезает при работе с ИИ.
Модель пишет изменение — и эта же модель говорит:
Готово. Всё работает.
Это очень удобная схема.
И очень слабая.
Мы начали разделять роли.
Один контекст формирует задачу.
Другой выполняет изменение.
Отдельный проход проверяет результат относительно контракта и evidence.
Это не гарантирует отсутствие ошибок. Независимый аудитор тоже может ошибиться.
Но возникает важное свойство: ошибка одной модели больше не получает автоматического права объявить саму себя правильным результатом.
И тут обнаружился ещё один эффект
Самое интересное началось позже.
Мы ожидали, что по мере роста чата качество будет постепенно снижаться:
100% 95% 90% 80% ...
Но визуально происходило нечто другое.
Проект мог очень долго идти практически с одинаковым качеством.
А затем ближе к пределу разговора появлялись первые заметные ошибки состояния.
То есть поведение больше напоминало:
стабильно → стабильно → стабильно → стабильно → порог → заметный рост риска ошибки
Это пока именно практическое наблюдение, а не научно измеренная закономерность.
Но у него есть логичное объяснение.
Пока актуальное состояние проекта компактно и хорошо отделено от истории, модели достаточно нескольких опорных точек.
Но в огромном диалоге постепенно накапливается слишком много конкурирующих сущностей:
старые HEAD новые HEAD старые критерии новые критерии закрытые дефекты новые дефекты ложные гипотезы текущие гипотезы старые контракты новые контракты
Формально информация никуда не исчезла.
Проблема в другом: отношение signal-to-noise начинает ухудшаться.
Нужное правило всё ещё существует, но находится среди сотен других технически похожих правил.
Поэтому появился Context Rotation
Из этого наблюдения следует довольно практичное правило.
Не нужно ждать, пока чат начнёт ошибаться.
Разговор необходимо менять до деградации.
Мы называем это Context Rotation.
В логической точке проекта формируется canonical handoff:
CURRENT STATE CURRENT HEAD / TREE ACTIVE CONTRACT FROZEN DECISIONS OPEN BLOCKERS REJECTED HYPOTHESES KNOWN RISKS LAST VERIFIED EVIDENCE NEXT EXACT OPERATION
После этого начинается новый рабочий контекст.
Важно: туда переносится не полный пересказ предыдущего разговора.
Иначе мы просто переносим проблему из одного чата в другой.
Передаётся состояние, необходимое для продолжения работы.
История остаётся историей.
Фактически проект начинает работать как конечный автомат
Мне кажется, именно здесь находится главное различие.
Обычная работа с ИИ строится как рассказ:
что произошло → что потом произошло → что мы после этого решили
Инженерная работа всё больше становится похожа на последовательность проверяемых состояний:
S0 ↓ evidence S1 ↓ evidence S2 ↓ evidence S3
Переход между состояниями происходит только при выполнении определённых условий.
Поэтому модели не требуется каждый раз реконструировать проект из всей истории взаимодействия.
Именно это, похоже, значительно увеличивает стабильность на длинной дистанции.
Главный вывод
Мы начинали строить инженерный метод вокруг простой проблемы:
как использовать ИИ в разработке и при этом не потерять контроль над результатом.
Но постепенно обнаружили более фундаментальную вещь.
Проблема длинной работы с ИИ заключается не только в качестве самой модели и не только в размере её контекстного окна.
Очень многое зависит от того, как представлено состояние задачи.
Если состояние существует только внутри диалога, модель вынуждена постоянно реконструировать настоящее из истории.
Если же текущее состояние вынесено наружу, формализовано, проверяемо и очищено от уже отвергнутых предпосылок, зависимость от памяти разговора резко уменьшается.
Поэтому я бы сформулировал главный принцип так:
Стабильность ИИ на длинной дистанции определяется не только объёмом доступного контекста, но и качеством представления текущего состояния.
И второй:
Не заставляйте модель помнить проект. Дайте ей возможность каждый раз доказуемо определить, в каком состоянии проект находится сейчас.
Это не делает ИИ безошибочным.
Но меняет характер ошибок.
Вместо бесконтрольного накопления неверных предпосылок появляется система, в которой ошибку можно локализовать, проверить и остановить до того, как она станет фундаментом следующего решения.
И, возможно, именно это является одной из необходимых границ между обычным vibe coding и инженерной разработкой с ИИ.
больше про инженерию в нашем сообществе - перейти