До этого «состояние агента» — это туманный массив messages. Сегодня вводим строгую структуру: агент как конечный автомат. Каждый шаг — переход из явного состояния в другое. Это разница между «модель сама поймёт» и «модель работает в управляемом каркасе».
Когда задача сложная и многоэтапная, простой цикл «reason → act → observe» начинает разваливаться: агент путается между этапами, забывает, что уже сделал, повторяет одни и те же шаги. Решение — представить агента как машину состояний: явный список фаз, явные условия переходов, явный список действий в каждой фазе. Модель остаётся «мозгом», но работает в каркасе, который ты задаёшь как инженер.
В Дне 6 ты познакомился с базовым циклом агента: думай → действуй → наблюдай → решай. Это работает для коротких задач: «найди новость, составь сводку», «ответь на письмо». 5-10 итераций, и готово.
Но как только задача становится структурно сложной — например, «помоги пользователю забронировать поездку: узнай даты → найди варианты → согласуй → оплати → подтверди» — свободный цикл начинает плыть:
Это не проблема плохой модели — это проблема отсутствия структуры. У задачи есть естественные фазы, между ними есть зависимости («нельзя оплачивать без выбранного рейса»), а агент работает без знания об этой структуре.
В system prompt: «помоги забронировать поездку, узнай детали, найди варианты, оформи».
Модель сама решает, на каком шаге она сейчас. Через 10 ходов теряется: спрашивает то, что уже спросила, или пытается сделать то, для чего нет данных.
В system prompt: «задача проходит через 5 фаз: collect_dates, search_options, confirm, pay, finalize. Сейчас фаза: collect_dates. Действия в этой фазе: ...».
Модель работает только в текущей фазе, переход — по явному условию. Не теряется.
Свободный агент полагается на «модель сама разберётся, где она в процессе». Машина состояний даёт ей это знание явно — и снимает огромное количество ошибок.
Конечный автомат (FSM, finite state machine) — классическая концепция из computer science. Это формальная модель системы, у которой:
Когда мы применяем это к агенту: состояния — это фазы решения задачи. Переходы — это условия для перехода в следующую фазу. Действия — какие инструменты доступны и что модель пытается достичь в текущей фазе.
Реализация — простая. В состоянии агента (не в массиве messages, а в твоём коде) есть переменная current_state. Перед каждым вызовом LLM ты:
current_state и подкладываешь новые инструкцииВ контекст модели на каждом шаге попадает описание только текущей фазы, а не «всех инструкций сразу». Это сильно фокусирует поведение модели и сокращает шум в промпте.
Точка входа в задачу. Обычно «понять, что нужно пользователю». Содержит первые уточняющие вопросы и условия для перехода в основную работу.
Фаза выполнения. Сбор данных, поиск, обработка, генерация. Обычно состояний этого типа несколько — это «основное тело» задачи.
Перед необратимыми действиями. Показать пользователю, что собираешься сделать, дождаться явного «да». Об этом — Дни 14-15.
Задача успешно выполнена. Финальный ответ пользователю, выход из автомата. Состояние без переходов наружу.
Что-то пошло не так: не получилось получить нужное, инструмент вернул ошибку, нарушен инвариант. Переход в это состояние — из любой рабочей фазы.
Ждём внешнего события: ответа пользователя, callback от внешней системы, истечения таймера. Агент не делает ничего, пока не пришло то, что ждём.
Возьмём ту самую задачу про бронирование поездки и распишем её как машину состояний. Это покажет, как абстракция выглядит в реальной задаче.
Что здесь происходит на каждом шаге?
На каждой фазе твой код собирает system prompt из двух частей:
Это даёт фокус: модель знает, что от неё ждут сейчас, не пытается забегать вперёд.
Каждое условие — это проверка, которую делает твой код после очередного ответа модели. Примеры:
dates, origin, destination, passengersПроверка может быть простой (if dates and origin and destination: → next) или сложной (распарсить ответ пользователя, понять, какой вариант выбран). Часто для последнего нужен отдельный LLM-вызов на маленькой модели.
Принцип: переход — это решение твоего кода, а не модели. Модель только генерирует ответы; код смотрит на состояние и решает, куда идти.
Кроме current_state, у тебя есть state data — структурированные данные задачи, которые накапливаются:
intent: "flight"dates: { start: "...", end: "..." }origin: "Moscow", destination: "Yerevan"passengers: 2options_offered: [...]chosen_option: {...}payment_status: "pending"Эти данные подмешиваются в system prompt каждой фазы — модель видит, что уже собрано, что ещё нет. Это лучше, чем «модель сама вычислит из истории сообщений», что часто ошибается.
confirm_booking явно завершено.Не для каждой задачи. Простое правило:
Тест: если ты можешь нарисовать процесс как блок-схему — он будет машиной состояний. Если процесс открыт, ход решений не предсказуем — это свободный агент.
Можно делать машину состояний разной степени строгости:
Для критичных операций (деньги, удаление, отправка) — жёсткая FSM. Для исследовательских задач (помощь в анализе) — мягкая.
«Раз это мощный паттерн — буду применять везде». Для задачи «помоги мне написать пост» машина состояний — это оверкилл и тормоз: процесс открытый, ход непредсказуем, любая «жёсткая» структура только мешает.
FSM — для процессов с устойчивыми фазами и явными зависимостями между ними. Если ты не можешь до начала работы нарисовать список фаз — значит свободный агент лучше.
Спроектировал «солнечный путь»: все фазы по порядку до успешного финала. Не подумал, что: платёж может сорваться, пользователь может отменить в середине, инструмент может вернуть ошибку. Когда что-то идёт не по плану — система не знает, в каком она состоянии.
Хорошая FSM всегда включает: error state с возможностью graceful fallback, cancel state с возможностью пользователю выйти на любой фазе, timeout / retry для нестабильных переходов.
«Каждое маленькое действие — своё состояние». Получаешь 30 состояний с переходами «каждое в каждое» — это не FSM, это лапша.
Правило: состояний должно быть 5-10 на типичную задачу. Если больше — скорее всего, есть скрытые подзадачи, которые надо вынести во вложенные машины. Иерархические FSM существуют именно для этого.
Пользователь начал бронирование, дошёл до выбора варианта, закрыл вкладку. Завтра вернулся — а агент не помнит, на какой фазе был, и начинает с нуля.
State machine агента нужно сериализовать — сохранять current_state и state_data в базу. При возобновлении сессии — поднимать. Это, кстати, проще, чем восстанавливать «по контексту разговора» — у тебя структурированные данные, а не текстовый лог.
Открой любой чат-бот, который ты использовал (поддержка банка, бот заказа еды, бот техподдержки сервиса). Веди диалог по сценарию — записывай каждое «состояние», в котором ты находишься, и условие перехода. У большинства серьёзных ботов ты увидишь явную FSM «в действии». Это даст ощущение, как абстракция выглядит в реальном продукте.
Возьми задачу из своей работы или жизни, которую делал бы AI-агент: «приём заказа на доставку», «онбординг нового сотрудника», «составление резюме». Распиши: какие фазы (5-10)? Что собирается в state data на каждой? Какое условие перехода в следующую? Где error state? Где cancel? Где human-in-the-loop (об этом завтра)? Этот рисунок и есть архитектура агента.
Возьми условную задачу-процесс: «помоги пользователю составить план на выходные: спроси про настроение → спроси про время → предложи 3 варианта → уточни выбор». Сделай два теста: А. Промпт «свободный»: всё это написано в одном system prompt одной фразой. Б. Промпт «по фазам»: ты эмулируешь FSM руками. После каждого ответа модели вручную меняешь system prompt на «фаза 2 из 4: спроси про время». Сравни — на «А» модель часто скатывается в монолог, на «Б» она фокусирована и ведёт диалог как положено.