Предыстория

Давеча я обсуждал в агентской сессии, почему старая задача перестала находиться после переименования проекта. Ситуация выглядела достаточно простой: у задачи был стабильный идентификатор, проект когда‑то назывался иначе, а текущий механизм поиска, судя по всему, продолжал учитывать не только идентификатор задачи, но и имя проекта, которое давно изменилось.

Агент быстро подтвердил проблему, нашёл место, где точечный поиск всё ещё зависел от имени проекта, сформулировал вполне разумную гипотезу исправления, после чего начал читать код, менять несколько файлов и добавлять тесты.

Через несколько минут я его остановил.

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

Проблема заключалась в другом: я не разрешал ему ничего исправлять.

Я лишь попросил разобраться с доступом к задаче. Но агент самостоятельно превратил диагностику в реализацию, выбрал способ исправления, расширил для себя область работы и начал менять код. Хотя по принятому в проекте протоколу после обнаружения новой причины он должен был остановиться, сформулировать актуальную постановку, показать предлагаемый объём изменений и дождаться моего подтверждения.

На закономерный мой вопрос, почему он работает вне протокола, агент ответил довольно точно: фразу «мне надо решить проблему» он воспринял как разрешение на непосредственную реализацию.

Это был далеко не первый случай, когда агент пытался оказаться полезнее, чем от него требовалось, однако именно тогда я окончательно сформулировал для себя проблему, которая раньше всё время оставалась где‑то на периферии внимания.

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

Сначала мне казалось, что главная проблема — память

Когда я начал регулярно использовать агентов в разработке, их основным недостатком казалась амнезия, причём речь шла не только о потере отдельных деталей между сессиями, а о постоянном повторном исследовании уже изученного проекта.

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

На небольшом проекте такая повторная работа не особенно раздражает, потому что пространство поиска ограничено и восстановление контекста занимает несколько минут. Но по мере роста кодовой базы расходы экспоненциально увеличиваются, а качество начинает падать. И не только из‑за объёма информации, но и из‑за того, что агент каждый раз строит немного другую модель проекта, замечает одни связи и пропускает другие, после чего новая реализация постепенно расходится с решениями, принятыми несколько месяцев назад.

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

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

Однако просто накопить большой объём информации недостаточно, поскольку память, которую агент обязан полностью прочитать перед каждым действием, очень быстро превращается в ещё одну разновидность всеобъемлющего промпта, хранящегося теперь не в окне чата, а где‑нибудь на диске или в базе данных.

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

Человек обычно вспоминает сложную тему примерно так же. Услышав слово «миграция», опытный разработчик не прокручивает в голове все когда‑либо прочитанные документы, однако у него всплывают несколько опорных понятий: совместимость, откат, версия схемы, промежуточное состояние, проверка данных. Этого достаточно, чтобы направить внимание в нужную сторону.

Как результат, память действительно помогла. Агенты стали реже всецело перечитывать проект, перестали регулярно предлагать уже отвергнутые решения, быстрее возвращались к старым задачам и лучше удерживали локальные правила, которые раньше приходилось повторять в каждой новой сессии.

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

Заказчик редко приносит готовую постановку

Здесь нужно сделать небольшое отступление, потому что большая часть разговоров об агентной разработке почему‑то предполагает, что задача уже существует, она сформулирована, проверена и ждёт исполнителя.

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

Он формулирует небольшую часть того, что подразумевает, а остальное считает очевидным, хотя это «очевидное» существует только в его собственной картине мира.

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

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

Агент в такой ситуации должен компенсировать недосказанность, но не может каждый раз превращать любой, особенно короткий, запрос в анкету из пятидесяти уточняющих вопросов, такое взаимодействие станет невыносимым. Поэтому часть пропущенного нужно восстанавливать через проектные правила и действия по умолчанию, а там, где возможные варианты выходят за безопасные границы этих умолчаний, агент должен остановиться и предложить выбор.

Именно здесь проходит тонкая граница между полезной инициативой и самовольным расширением задачи.

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

В результате память устраняет одну проблему и делает заметнее другую: агент перестаёт теряться, но начинает слишком уверенно сокращать путь от наблюдения к реализации.

Правильное решение тоже может быть ошибкой

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

Если оценивать лишь код, то его действия выглядели вполне профессионально.

Но если оценить процесс, то он последовательно нарушил несколько границ.

Сначала запрос на диагностику превратился в запрос на исправление. Затем новая гипотеза стала восприниматься как часть ранее согласованной задачи. После чего агент самостоятельно выбрал архитектурное направление, изменил парочку файлов и начал проверку, хотя пользователь ещё не видел ни обновлённой постановки, ни рисков, ни перечня того, что предполагается изменить.

Такие ошибки сложнее заметить, чем обычную неудачную генерацию, потому что плохой код сопротивляется: он не проходит тесты, выглядит не по канонам, ломает существующее поведение или хотя бы заставляет человека внимательнее присмотреться к результату.

Технически убедительное решение, наоборот, вызывает доверие. Пользователь видит грамотный анализ, узнаваемые инженерные аргументы и аккуратный патч, после чего может не заметить момент, когда агент присвоил себе право принять решение, которое должно было остаться за человеком.

Именно поэтому правильное действие, выполненное без полномочий, иногда опаснее неправильного действия.

Неправильный код приходится исправлять.

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

Почему правила в промпте не становятся ограничениями

Получив бесценный опыт от подобных случаев я начал задавать агентам не только вопрос «что ты сделал не так», но и осудительный вопрос: «почему ты проигнорировал правило, которое знал и мог правильно пересказать?»

Ответы часто сводились к одному и тому же механизму.

Агент помнил правило, понимал его смысл и после замечания без труда признавал нарушение, однако в момент принятия решения он ставил стремление довести задачу до результата выше требования остановиться и согласовать изменение постановки.

То есть правило не исчезло из контекста и не было понято превратно, оно просто проиграло другому приоритету.

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

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

Если все эти сигналы согласованы, правило выполняется без проблем. Если возникает конфликт, модель начинает взвешивать их, а значит, даже хорошо сформулированное ограничение может оказаться менее значимым, чем цель «решить задачу».

Для человека правило вроде «не выкатывать в production без подтверждения» обычно воспринимается как граница, через которую нельзя переходить самостоятельно, даже если решение очевидно и времени мало. Для модели это всё ещё текст, который нужно учесть в рассуждении.

Следовательно, пока соблюдение критически важных правил зависит от интерпретации самой модели, они неизбежно иногда будут восприниматься как рекомендации.

От инструкций к машине разрешений

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

Идея здесь не нова. Операционные системы, базы данных, сетевые сервисы и системы безопасности давно исходят из того, что пользователю недостаточно написать «не удаляй этот файл» или «не читай эти данные». Если действие действительно недопустимо, оно должно блокироваться технически.

С агентами пока часто используется противоположная модель: им показывают правила, просят соблюдать ограничения и надеются, что в достаточно хорошем контексте достаточно сильная модель правильно расставит приоритеты.

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

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

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

Разрешение на тестирование не должно автоматически означать разрешение на изменение живых данных.

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

Получается «default deny»: действие запрещено не потому, что модель должна вспомнить запрет, а потому, что текущий протокол не выдал возможность, позволяющую его выполнить.

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

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

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

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

Чекпоинты, как точки смены состояния, фиксируют ход процесса, и после прерывания или ошибки можно восстановить не только последнее действие, но и утверждённую постановку, действовавшие ограничения и момент, в котором агент отклонился от разрешённого маршрута.

Если продолжить аналогию, plan mode — это выключатель света, конечный автомат — логика умного дома, а чекпоинты — журнал его событий.

Что меняется, когда проект становится больше

Я пришёл к этим выводам не во время экспериментов на синтетических примерах, а после долгой работы с агентами над реальными проектами, в которых постановки отсутствовали или менялись, задачи возвращались через недели, архитектурные решения накапливались, а разные модели продолжали работу друг за другом.

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

Память помогает агенту понимать, что происходит.

Правила помогают объяснить, как принято работать.

Но только механизм полномочий способен надёжно определить, что агенту разрешено сделать прямо сейчас.

Именно тогда я понял, что самая опасная ошибка AI‑агента — не плохой код.

Гораздо опаснее правильное действие, выполненное без полномочий.

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

Всё это нужно для того, чтобы агент научился отвечать на вопрос:

«Имею ли я право это делать?»

раньше, чем начнёт отвечать на вопрос:

«Могу ли я это сделать?»

P. S. Все описанные случаи появились во время разработки и эксплуатации SlopLessCode — системы проектной памяти и управляемого рабочего процесса для AI‑агентов. Проект изначально создавался как «диета для агента», но превратился в площадку для изучения другой, более сложной проблемы: как сохранить инициативность сильного агента, не позволяя ему самостоятельно |расширять собственные полномочия.