Глава 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»Источники главы
- [ant] Building Effective Agents — Anthropic Engineering (определение agent vs workflow, when to use agents)
- [ant] Messages API — Anthropic Docs (семантика
stop_reason,tool_use,tool_result) - [oai] A Practical Guide to Building Agents — OpenAI (Foundations, Orchestration: single-agent loop)
- [cli] Конспект: Orchestrator Loop (конкретная имплементация:
QueryEngine,queryLoop, recovery-итерации)