Loop engineering: как спроектировать цикл агента, который знает, когда остановиться
Как устроен цикл ИИ-агента: от циклов «наблюдай — действуй» и состояния до условий остановки, лимитов ретраев, контроля затрат и метрик, плюс сравнение неуправляемого и управляемого дизайна.
Вокруг любой ИИ-модели есть набор вспомогательных частей, и одна из важнейших среди них — цикл агента. Здесь мы присмотримся именно к нему и к тому, как его спроектировать, чтобы агент вёл себя предсказуемо, не разорял и понимал, когда пора заканчивать.
От одного вызова к циклу
Обычный чат-бот, как правило, идёт по одному прямому пути:
User → LLM → Response
У агента задача сложнее. Чтобы закончить одно задание, может понадобиться несколько шагов, и после каждого модель смотрит, что получилось:
User
↓
Understand the task
↓
Decide what to do
↓
Use a tool
↓
Observe the result
↓
Decide what to do next
↓
Use another tool
↓
Evaluate the result
↓
Finish
Вот этим повторением и занимается loop engineering.
Цикл агента — это круг, в котором ИИ-агент раз за разом делает следующее:
- Получает информацию.
- Выбирает, что делать.
- Выполняет действие.
- Смотрит на результат.
- Решает, нужно ли ещё одно действие.
- В какой-то момент останавливается.
В упрощённом виде цикл выглядит так:
┌───────────┐
│ Observe │
└─────┬─────┘
↓
┌───────────┐
│ Decide │
└─────┬─────┘
↓
┌───────────┐
│ Act │
└─────┬─────┘
↓
┌───────────┐
│ Observe │
│ Result │
└─────┬─────┘
↓
Finished?
/ \
No Yes
↓ ↓
Decide Stop
Главное здесь то, что один вызов модели не обязан сразу выдать финальный ответ. Агент может действовать, учиться на том, что вернулось, и продолжать.
Небольшой пример: погода в Бристоле
Допустим, пользователь спрашивает: «Какая погода в Бристоле и брать ли зонт?» Агент может справиться за несколько ходов. Он получает вопрос и понимает, что нужны текущие данные. Затем вызывает погодный API, и тот возвращает вот что:
Temperature: 16°C
Condition: Heavy rain
Теперь у агента есть всё нужное, и он отвечает, что в Бристоле дождь, так что зонт лучше взять. Даже такой крошечный запрос потребовал больше одной операции.
Бывают и простые задачи. Вопросу вроде «Сколько будет 15 × 20?» хватает одной операции. Запрос потяжелее: «Найди три британских университета с магистратурой по Data Science и сравни стоимость обучения и требования к поступлению». Тут может понадобиться примерно такая последовательность:
Search University A
↓
Read information
↓
Search University B
↓
Read information
↓
Search University C
↓
Read information
↓
Compare results
↓
Generate answer
Ни один отдельный ответ языковой модели надёжно всё это не покроет. Цикл позволяет агенту разбить большую задачу на маленькие действия.
Агент как конечный автомат
Ситуацию агента можно описать как состояние. Пусть s_t — состояние в момент t. На каждом такте агент получает наблюдение o_t, выбирает действие a_t, и мир переходит в следующее состояние:
В этом выражении s_t — текущее состояние, o_t — текущее наблюдение, a_t — действие, f — функция перехода состояний, а s_{t+1} — следующее состояние. Выбор действия тоже можно записать компактно:
Здесь греческая буква пи обозначает политику принятия решений. В агенте на базе LLM модель — часть этой политики. Снимок состояния может выглядеть так:
State:
"We need the current weather."
Observation:
"Weather tool is available."
Action:
"Call weather(location='Bristol')"
А после ответа инструмента:
New observation:
"16°C, heavy rain"
Next action:
"Generate final response"
Смотреть на агента так, как на систему, меняющую состояние, полезнее, чем считать его чат-ботом с лишними шагами.
Когда остановиться
Пожалуй, самый трудный вопрос в проектировании цикла — как агент понимает, что закончил. Представьте агента, который застрял вот так:
Call search
↓
Get result
↓
Call search again
↓
Get result
↓
Call search again
↓
...
Без правила выхода это может продолжаться вечно. Поэтому продакшен-агенту нужны условия завершения. Распространены несколько.
Первое — счастливый путь, когда задача выполнена:
Task completed → STOP
Второе — бюджет шагов, например максимум пять:
Maximum steps = 5
Если агент дошёл до пяти шагов и не закончил, он останавливается:
STOP
Третье — повторяющиеся сбои инструмента, после которых прогон нужно завершить или передать кому-то другому:
Tool failed repeatedly → STOP / ESCALATE
Четвёртое — рискованное действие, когда человек должен одобрить его до того, как что-либо произойдёт:
High-risk action → STOP → Ask human
Стоимость, задержка и надёжность
Считайте каждый шаг чем-то, что стоит денег. Допустим, цена одного шага такая:
Тогда обычный прогон из 10 шагов стоит примерно столько:
Баг, из-за которого шагов становится 1000, резко меняет итог:
Для одного запроса это уже может быть неприятно.
Умножьте на тысячи пользователей, и проблема очевидна. Поэтому loop engineering касается стоимости, задержки и надёжности не меньше, чем функциональности.
Обработка сбоев
Реальные системы ломаются. Представьте, что погодный вызов вернул вот это:
Error: API unavailable
Плохо спроектированный агент просто падает. Цикл получше реагирует на ошибку:
Tool call
↓
Error
↓
Retry?
┌─┴─┐
Yes No
↓ ↓
Retry Escalate
Ретраям тоже нужны лимиты. Например, после двух сбоев система может остановиться, а не слать один и тот же обречённый запрос снова и снова.
Где встречаются циклы
Циклы агентов есть во многих областях. Вот пять типичных.
Исследования:
Search → Read → Compare → Search again → Summarise
Программирование:
Read code → Modify code → Run tests → Observe errors → Fix → Test again
Анализ данных:
Inspect data → Run analysis → Observe result → Create visualisation → Explain
Поддержка клиентов:
Understand issue → Search knowledge base → Check customer data → Respond
Управление компьютером:
Observe screen → Decide action → Click/type → Observe new screen → Continue
Общая нить простая: действуй, смотри на результат, потом решай, что дальше.
Неуправляемые и управляемые циклы
Сравним два варианта. Первый даёт модели инструмент и отпускает её:
LLM
↓
Tool
↓
LLM
↓
Tool
↓
LLM
↓
Tool
↓
...
Здесь ничто ничего не контролирует. Второй вариант добавляет контрольные точки вокруг каждого шага:
Task
↓
Observe
↓
Decide
↓
Validate action
↓
Execute
↓
Observe result
↓
Evaluate
↓
┌───────────────┐
│ Goal complete?│
└───────┬───────┘
Yes│ No
↓ ↓
Stop Continue
Второй вариант вводит контроль, и в этом суть loop engineering.
Как измерять цикл
Об успехе нельзя судить только по тому, появился ли в итоге ответ. Вот несколько полезных метрик:
Ещё стоит отслеживать:
- сколько длится задача (задержка)
- сколько потрачено токенов
- во что обходится каждая завершённая задача
- как часто вызовы повторяются
- долю завершённых задач
- как часто приходится вмешиваться человеку
Вместе эти показатели говорят, становится ли агент действительно лучше.
Компромисс по числу шагов
Дополнительные шаги позволяют агенту браться за задачи посложнее. Но они же приносят рост затрат, долгое ожидание, больше шансов ошибиться, больше сбоев инструментов и менее предсказуемое поведение. Так что цель не в том, чтобы агент крутился как можно дольше. Цель — выполнить задачу за минимальное число шагов, при котором всё ещё работает надёжно.
Что нужно надёжному циклу
Loop engineering — это проектирование и контроль повторяющегося цикла выполнения ИИ-агента. Базовый шаблон такой:
Он повторяется, пока задача не выполнена или система не решит остановиться. Надёжному агенту нужны:
- чётко определённые действия
- итерации, которые кто-то контролирует
- явные условия остановки
- обработка ошибок
- лимиты на ретраи
- управляемое состояние
- ограничения на расходы
- постоянная оценка
Автономность не означает бесконечного выполнения. Хороший агент знает не только, что делать дальше, но и когда остановиться.
Сильная модель и продуманный цикл всё равно могут ошибиться, если агенту подают не ту информацию. Отсюда следующий вопрос: что агент должен получать на каждом шаге? Ответ — в области context engineering, которая занимается тем, чтобы давать агентам нужную информацию в нужный момент.