Технологии

Агент зациклился: причины и предохранители

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

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

Механизмов четыре. Они выглядят одинаково снаружи — повторяющиеся действия — и требуют разных исправлений.

#Быстрая проверка

Откройте лог и посмотрите на повторяющиеся вызовы. Два вопроса определяют механизм за полминуты:

  1. Аргументы у повторов одинаковые или разные?

    • Одинаковые → механизм 1 или 2.
    • Разные, но по кругу → механизм 3.
    • Разные и не повторяются, но прогресса нет → механизм 4.
  2. Результат повтора отличается от предыдущего?

    • Нет → модель не поняла результат либо его не видит.
    • Да → цепочка реагирует на изменение, но не сходится.

#Механизм 1. Повтор идентичного вызова

Как выглядит. Один и тот же вызов с точно теми же аргументами два-три раза подряд.

Почему. Модель видит свой предыдущий вызов в контексте и продолжает текст по образцу. Это не «забыла» — это ровно то, чем занимается языковая модель: продолжает похожее похожим.

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

Чего не делать. Просить модель в промпте «не повторяйся». Это пожелание, конкурирующее с механикой продолжения текста, и оно проигрывает.

#Механизм 2. Неинформативный результат

Как выглядит. Повтор того же вызова, но с искренним намерением: модель не поняла, что уже получила ответ.

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

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

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

#Механизм 3. Цикл между двумя состояниями

Как выглядит. A, B, A, B. Прочитать файл, изменить, прочитать, изменить — или переход между двумя каталогами.

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

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

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

#Механизм 4. Блуждание без прогресса

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

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

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

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

#Таблица «механизм — предохранитель»

Механизм Признак в логе Предохранитель Общий лимит помогает
Повтор идентичного вызова Те же аргументы подряд Дедупликация по тексту вызова Только как последняя преграда
Неинформативный результат Повтор после пустого ответа Различимый текст на все исходы Нет
Цикл между двумя состояниями Чередование A, B, A, B Признак завершения + счётчик пар Частично
Блуждание без прогресса Разные вызовы, нет результата Проверяемая цель + счётчик застоя Да, но поздно

Из таблицы видно главное: общий лимит шагов полноценно решает только четвёртый случай, и то с опозданием — вы платите за все шаги до предела.

#Значения лимитов по умолчанию

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

Раундов цикла за один ход           6
  (в режиме углублённого ответа     8)

Вызовов инструментов за ход         16

Символов на один результат          4 000
Символов суммарно за ход           24 000

Повторов одного вызова              0 — дедупликация по точному тексту

Шагов в фоновой цепочке
  размышлений                       5 по умолчанию
  аварийный предел, если модель
  сама решает, когда закончить     50

Таймаут одного внешнего вызова     от десятков секунд;
  внешний инструментальный сервер   60 с
  выполнение команды                30 с

Два замечания к этим числам.

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

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

#Что должно происходить при срабатывании

Половина проблем с лимитами — не в самих лимитах, а в том, что происходит после.

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

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

#Если ничего не помогло

Проверьте, видит ли модель результаты вообще. Бывает, что результат инструмента не дописывается в контекст из-за ошибки в сборке промпта. Симптом — модель повторяет вызовы, как будто ничего не получала, потому что действительно ничего не получала.

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

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

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

Разбор трассировки по шагам, включая поиск точки, где цепочка перестала сходиться, — в материале про чтение трассировки агента.

#Частые вопросы

#Почему агент повторяет один и тот же вызов инструмента?

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

#Какой лимит шагов ставить?

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

#Что делать, если агент чередует два действия?

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

#Помогает ли просто увеличить лимит?

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

#Как отличить зацикливание от долгой работы?

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

#Может ли зацикливание стоить денег?

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

#Вывод

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

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

Хотите ИИ, который помнит вас?

Persona держит контекст между чатами и работает на вашем железе — если развернуть её у себя и подключить локальную модель.

Создать аккаунт