Остановка, лимиты и зацикливание
Автономный цикл обязан уметь останавливаться — и нормально (нашёл ответ), и аварийно (застрял). Без этого агент превращается в бесконечный счётчик расходов.
Условие остановки — правило, по которому цикл агента завершается: либо модель выдала финальный ответ, либо сработал предохранитель (лимит шагов, обнаружение цикла).
Нормальная остановка
Штатный способ — модель сама решает, что задача решена, и выдаёт Final Answer. Цикл это распознаёт и завершается. Но полагаться только на это нельзя: модель может так и не сказать «готово».
Предохранитель 1: лимит шагов
Самая важная защита. Сколько бы агент ни «думал», после N шагов цикл останавливается принудительно. Это спасает и от зацикливания, и от роста стоимости.
def stubborn_llm(history):
# заглушка зациклилась: всё время просит один и тот же поиск
return "Action: search[одно и то же]"
MAX_STEPS = 4
answer = None
history = []
for step in range(1, 1000):
if step > MAX_STEPS:
print(f"Лимит {MAX_STEPS} шагов исчерпан — принудительная остановка.")
answer = "не удалось решить за отведённые шаги"
break
block = stubborn_llm(history)
history.append(block)
print(f"шаг {step}: {block}")
print("Итог:", answer)
Вывод:
шаг 1: Action: search[одно и то же] шаг 2: Action: search[одно и то же] шаг 3: Action: search[одно и то же] шаг 4: Action: search[одно и то же] Лимит 4 шагов исчерпан — принудительная остановка. Итог: не удалось решить за отведённые шаги
Без MAX_STEPS этот цикл крутился бы вечно. В реальном агенте каждый виток — это вызов LLM за деньги, так что лимит шагов (и лимит бюджета) обязателен.
Предохранитель 2: обнаружение зацикливания
Лимит шагов спасает «в худшем случае», но иногда видно раньше: агент повторяет одно и то же действие. Это явный признак, что он застрял, — лучше прервать сразу, а не ждать лимита.
def detect_loop(actions, window=2):
# цикл, если последние window+1 действий одинаковы
if len(actions) > window:
last = actions[-(window + 1):]
if len(set(last)) == 1:
return True
return False
actions = []
for a in ["search[x]", "search[y]", "search[y]", "search[y]"]:
actions.append(a)
looped = detect_loop(actions)
print(f"действие {a:12} | цикл: {looped}")
if looped:
print(" -> агент застрял, прерываем")
break
Вывод:
действие search[x] | цикл: False действие search[y] | цикл: False действие search[y] | цикл: False действие search[y] | цикл: True -> агент застрял, прерываем
Почему агенты зацикливаются
- Инструмент стабильно возвращает «не найдено», а модель упорно пробует тот же запрос.
- Модель не замечает в истории, что уже делала этот шаг.
- Задача в принципе нерешаема имеющимися инструментами, но модель этого не признаёт.
Что делать при срабатывании предохранителя
- Вернуть честное «не удалось», а не выдуманный ответ.
- Залогировать всю трассу — по ней видно, где агент застрял.
- При необходимости передать задачу человеку (человек-в-цикле).
Памятка по остановке
| Механизм | Когда срабатывает |
| Final Answer | модель сама закончила (норма) |
| Лимит шагов | превышено число итераций |
| Обнаружение цикла | действие повторяется |
| Лимит бюджета | исчерпан лимит токенов/денег |
Итог
- Штатная остановка — это
Final Answer, но полагаться только на неё нельзя. - Лимит шагов — обязательный предохранитель от зацикливания и роста стоимости.
- Обнаружение повторов прерывает застрявшего агента раньше лимита; при срабатывании — честное «не удалось» и лог.