Проблема
Запит виглядає простим: знайти статус замовлення і повернути коротку відповідь.
У логах видно, що агент постійно повторює один і той самий цикл:
plan → call_tool → analyze → plan → call_tool → analyze
Тиждень тому така задача закривалась за 3-4 кроки.
Тепер той самий тип запиту може крутитись 20+ кроків і завершуватись timeout.
За 15 хвилин агент може зробити 60+ кроків і витратити близько $12 на задачу, яка зазвичай коштує ~$0.08.
Система не падає одразу.
Вона просто повільно спалює час, токени і гроші.
Аналогія: уяви навігатор, який на кожному перехресті каже "розверніться", навіть коли ти вже розвернувся. Машина їде, але ти не наближаєшся до цілі. Нескінченний цикл в агентах працює так само: дії є, прогресу немає.
Чому це стається
LLM-агенти — це стохастичні системи. Навіть невелика зміна prompt, tool output або контексту може змістити порядок кроків. Якщо runtime не перевіряє реальний прогрес, цикл легко застрягає.
У production зазвичай так:
- LLM пропонує наступну дію;
- агент викликає
tool; - отримує спостереження, але без нового сигналу;
- знову повертається в той самий reasoning loop.
Infinite loop виникає не тоді, коли агент "занадто довго думає", а тоді, коли runtime не відрізняє корисну роботу від повторення без прогресу.
Які збої трапляються найчастіше
Щоб не ускладнювати, у сценарії infinite loop найчастіше бачать чотири патерни.
Жорсткий цикл (Hard loop)
Агент викликає той самий tool з тими самими аргументами багато разів.
Типова причина: немає dedupe за tool+args або повтори дозволені без ліміту.
М'який цикл (Soft loop)
Агент виконує ту саму дію з мінімальними змінами аргументів: наприклад, додає одне слово в пошук і пробує знову.
Типова причина: немає перевірки "чи з'явилось щось нове".
Цикл ретраїв (Retry storm)
Інструмент падає, і одночасно ретраї роблять і gateway, і сам агент. У результаті кількість викликів множиться.
Типова причина: retry-логіка розкидана по кількох шарах без єдиної policy.
Семантичний цикл (Semantic loop)
Агент виглядає активним, але реально не рухається: переформульовує план, пересумовує ті самі дані або знову запитує те, що вже відомо.
Типова причина: немає чіткого критерію прогресу в runtime.
Як виявляти ці проблеми
Нескінченний цикл краще видно не по одній метриці, а по комбінації сигналів.
| Метрика | Сигнал зациклення | Що робити |
|---|---|---|
steps_per_task | різкий ріст кроків без завершення | додати жорсткий max_steps і stop reason |
repeated_tool_signature_rate | повтори tool+args у межах одного run | увімкнути dedupe і ліміт повторів |
no_progress_steps | кілька кроків без нових фактів/артефактів | зупиняти run за правилом no-progress window |
stop_reason_distribution | багато timeout і max_steps_reached | перевірити retry policy і runtime-gates |
tokens_per_task | витрати ростуть, а якість стоїть | обмежити context/tool output і ввести progress check |
Як відрізнити збій від справді складної задачі
Довгий run не завжди означає loop. Ключове питання: чи з'являється новий корисний сигнал.
Нормально, якщо:
- кожен 1-2 крок додає нові факти або артефакти;
toolвиклики змінюються змістовно, а не косметично;- агент поступово наближається до
final_answer.
Небезпечно, якщо:
- 3-5 кроків поспіль не додають нічого нового;
- повторюється той самий
tool(або той самий намір); - витрати ростуть, а якість відповіді не покращується.
Як зупиняти такі збої
Мета проста: не продовжувати run за будь-яку ціну, а завершувати його керовано.
Практично це виглядає так:
- ставиш жорсткі runtime-ліміти:
max_steps,timeout,max_tool_calls,max_tokens; - додаєш dedupe за
tool+argsі ліміт повторів; - зупиняєш run, якщо немає прогресу впродовж N кроків;
- повертаєш керований stop reason і частковий результат, а не "тиху" помилку.
Мінімальний loop-guard у runtime:
class LoopGuard:
def __init__(self):
self.max_steps = 12
self.max_repeat = 3
self.max_flat_steps = 4
self.steps = 0
self.flat_steps = 0
self.seen = {}
def on_step(self):
self.steps += 1
if self.steps > self.max_steps:
return "max_steps_reached"
return None
def on_tool_call(self, signature: str):
self.seen[signature] = self.seen.get(signature, 0) + 1
if self.seen[signature] >= self.max_repeat:
return "loop_detected:repeated_tool_signature"
return None
def on_progress(self, has_new_signal: bool):
self.flat_steps = 0 if has_new_signal else self.flat_steps + 1
if self.flat_steps >= self.max_flat_steps:
return "loop_detected:no_progress"
return None
Важливо: у кожній ітерації спочатку викликай on_step(), потім on_tool_call(...), а після аналізу результату — on_progress(...).
Цей guard не "лікує" агента. Він не дає циклу стати production-інцидентом.
Де це реалізується в архітектурі
У production системах контроль loop зазвичай знаходиться не в самому агенті, а в окремих архітектурних шарах.
Agent Runtime відповідає за execution loop агента: ліміти (max_steps, timeout, max_tokens), stop reasons і примусове завершення run. Саме тут зазвичай реалізують LoopGuard і перевірку прогресу.
Tool Execution Layer відповідає за безпечне виконання tool_call: dedupe викликів, retry policy і нормалізацію помилок. Багато циклів — retry storm, repeated tool calls і tool spam — виникають саме тут, коли немає єдиної retry policy або дедуплікації.
Самоперевірка
Швидка перевірка перед релізом: відмічайте пункти і дивіться статус нижче.
Це короткий sanity-check, а не формальний аудит.
Прогрес: 0/8
⚠ Є сигнали ризику
Бракує базових контролів. Закрийте ключові пункти цього чекліста перед релізом.
FAQ
Q: Чи вирішує проблему infinite loop перехід на сильнішу модель?
A: Частково інколи допомагає, але не вирішує корінь проблеми. Без runtime-gates навіть сильна модель може зациклитись.
Q: Як підбирати max_steps на старті?
A: Стартуй з малого консервативного ліміту і піднімай його лише там, де бачиш підтверджений приріст якості.
Q: Чи потрібно завжди робити retries?
A: Ні. Для 401/403 і стабільних валідаційних помилок retries зазвичай лише погіршують loop.
Q: Що показувати користувачу, коли run зупинено?
A: Причину зупинки, що вже спробували, і частковий результат. Це знижує повторні запуски без змін.
Infinite loop майже ніколи не виглядає як велика аварія. Це повільна деградація, яка з'їдає бюджет і час. Тому production-агенту потрібна не лише "розумна" модель, а й жорсткий runtime-контроль.
Пов'язані сторінки
Щоб глибше закрити цю проблему, подивись:
- Чому AI агенти ламаються — загальна карта збоїв у production.
- Tool spam — як обмежувати дубльовані виклики інструментів.
- Budget explosion — як loop перетворюється на неконтрольовані витрати.
- Agent Runtime — де реалізовувати loop guards і stop reasons.
- Tool Execution Layer — де тримати retries, timeout і валідацію викликів.