Глава 02 · Цикл

Один поворот цикла — это весь агент

Если вы прочитаете только одну главу — пусть это будет эта. Здесь живёт сердце harness: маленький while, который превращает один вызов модели в агента.

Что произошло между «чатом» и «агентом»

Чат — это один вызов модели: пользователь пишет сообщение, модель отвечает текстом, на этом разговор заканчивается. Агент — это та же модель, но обёрнутая в цикл, в котором она может попросить harness что-то сделать (прочитать файл, выполнить команду, сходить в API), получить результат и решить, что делать дальше. Anthropic формулирует разницу архитектурно: workflow — это «системы, где LLM и инструменты оркестрируются через предопределённые пути в коде», а agent — «система, где LLM динамически направляет собственный процесс и использование инструментов, сохраняя контроль над тем, как выполняется задача».

Ключевое слово здесь — dynamically. В workflow граф фиксирован: разработчик заранее знает, какой шаг идёт после какого. В агенте граф строится на лету самой моделью: она смотрит на текущее состояние messages и решает, нужен ли ещё один шаг, какой инструмент вызвать и с какими параметрами. OpenAI в «Practical Guide to Building Agents» описывает ту же идею короче: «every orchestration approach needs the concept of a run, typically implemented as a loop that lets agents operate until an exit condition is reached». Минимум, который делает агента агентом, — это LLM, цикл, инструменты и память (история сообщений).

Анатомия одного поворота

Один поворот цикла — это пять последовательных фаз, и в каждой из них что-то конкретное происходит с массивом messages, который мы отдаём в API.

1. Sample. Harness отправляет в Messages API текущий messages (плюс system, tools, max_tokens) и получает ответ — массив content-блоков. Среди них могут быть блоки text, thinking и tool_use. 2. Check stop_reason. Каждый ответ API сопровождается полем stop_reason: оно говорит, почему модель остановилась. Если значение — end_turn, поворот заканчивается. Если tool_use, в ответе есть хотя бы один блок tool_use, и его нужно выполнить.

3. Tool exec. Harness берёт каждый tool_use-блок (имя инструмента и JSON-аргументы), запускает соответствующую функцию и получает результат — строку или массив блоков. 4. Append. Ответ модели (целиком, с tool_use-блоками) добавляется в messages как сообщение с ролью assistant. Следом добавляется сообщение с ролью user, в котором содержится один блок tool_result на каждый tool_use — связанный по полю tool_use_id. 5. Next. Цикл возвращается к фазе 1: тот же массив messages, но уже выросший на два сообщения, снова летит в API. Модель видит, что она просила, и что получила, — и решает следующий шаг.

Это и есть весь цикл. Никакой второй модели, никакого «менеджера», никакого DAG. Просто while, который накапливает историю и заново спрашивает модель, что делать дальше.

stop_reason как контроллер цикла

В API нет отдельного флага «агент закончил». Состоянием цикла управляет одно поле в ответе — stop_reason. Документация Messages API перечисляет основные значения: end_turn — модель «естественно завершила свой ход» (нормальный финал), tool_use — модель попросила выполнить инструмент и ждёт результата, max_tokens — модель упёрлась в лимит токенов и оборвалась посреди мысли, stop_sequence — сработала пользовательская стоп-последовательность. Внутри цикла harness — это буквально switch по stop_reason: tool_use запускает фазы 3–5, end_turn возвращает результат наружу, max_tokens в зрелых имплементациях триггерит механизм восстановления (об этом ниже).

В claude-cli этот switch обёрнут в один-единственный while (true) внутри функции queryLoop: каждая итерация заново деструктурирует состояние, вызывает модель, выполняет инструменты и решает — продолжить (continue с новым State) или вернуть терминальную причину наружу. Подробности — в нашем конспекте по orchestrator loop, в частности раздел «Top of the inner loop».

Когда цикл прерывается

Терминальных условий несколько, и важно держать их в голове, потому что именно они отделяют «агент работает» от «агент завис». Anthropic формулирует это так: «задача чаще всего завершается по достижении цели, но обычно также включают условия остановки — например, максимальное число итераций — чтобы сохранять контроль». На практике harness обычно проверяет хотя бы четыре сигнала: end_turn от модели (нормальное завершение), лимит итераций или бюджета (защита от бесконечного цикла), ошибка или невосстановимое исключение со стороны API/инструмента, прерывание пользователем (abort).

В claude-cli, помимо этих четырёх, видно ещё одно семейство — recovery-итерации. На prompt_too_long цикл не падает, а пытается ужать контекст (context-collapse → reactive compact) и пойти на следующий заход. На max_output_tokens происходит escalating retry: лимит повышается, и в messages добавляется системный nudge «продолжи без извинений». Каждый такой recovery — это просто новый State и continue: для цикла нет разницы между «модель попросила инструмент» и «API вернул 413» — это одинаковые причины пойти на новую итерацию.

Почему так мало кода

Если выписать orchestrator loop на доске — это десяток строк: вечный while, вызов модели, switch на stop_reason, выполнение инструментов, дописывание messages, повтор. Anthropic подчёркивает это прямо: «их реализация чаще всего проста — это, как правило, просто LLM, использующие инструменты на основе обратной связи от окружения, в цикле». Большинство фреймворков добавляют слои абстракции поверх этой простой картинки и часто только мешают её увидеть.

Сложность живёт не в цикле, а в его аргументах. Что лежит в messages — это глава «Context Window». Какие tools объявлены и как они описаны для модели — глава «Tools». Кто и когда разрешает их вызвать — глава «Permissions». Куда сохраняется состояние между поворотами и сессиями — главы «Память и файлы» и «Sessions». Что вклинивается между шагами — глава «Hooks». Цикл сам по себе тривиален; всё, что вы прочитаете дальше, — это про то, чем его наполнить.

Workflow vs Agent — практический критерий

Anthropic даёт чёткий критерий выбора: workflow подходит, когда задача предсказуема и хорошо раскладывается на фиксированные подшаги; agent — когда «нужны гибкость и решения, управляемые моделью, на масштабе». Если вы можете заранее нарисовать граф «после шага A идёт шаг B, после B — либо C, либо D» — вам, скорее всего, не нужен агент: prompt chaining, routing или orchestrator-workers справятся дешевле и предсказуемее. Агент оправдан, когда «трудно или невозможно предсказать необходимое число шагов и нельзя зашить фиксированный путь».

Цена за гибкость — латентность, стоимость и риск составных ошибок: модель может уйти не туда, и каждая лишняя итерация — это лишний вызов API. Поэтому даже там, где агент уместен, его обычно запускают в песочнице, с лимитом итераций и с человеком в контуре на критичных действиях. Глава «Permissions» разбирает, как именно эти контуры устроены.

"

Agents, on the other hand, are systems where LLMs dynamically direct their own processes and tool usage, maintaining control over how they accomplish tasks.

— Erik S. и Barry Zhang · Anthropic Engineering, «Building Effective Agents»

Источники главы