
# Конечные автоматы

Конечные автоматы и рабочие процессы решают разные задачи.

Рабочий процесс отвечает на вопрос: «Какая задача выполняется следующей?» Конечный автомат отвечает на вопрос: «В каком состоянии находится объект и какие события допустимы в этом состоянии?» Это различие важно, когда вы создаёте не просто последовательность шагов, а долгоживущую сущность, которая со временем реагирует на внешние события, например сеанс агента, чат, цикл проверки, тикет или процесс согласования.

## Быстрый пример

```php
use Queuety\Enums\StateMachineStatus;
use Queuety\Queuety;

$machine_id = Queuety::machine( 'agent_session' )
    ->version( 'agent-session.v1' )
    ->state( 'awaiting_user' )
    ->on( 'user_message', 'planning' )
    ->state( 'planning' )
    ->action( PlanSessionAction::class )
    ->on( 'planned', 'awaiting_review' )
    ->state( 'awaiting_review' )
    ->on( 'approve', 'completed', ReviewApprovedGuard::class )
    ->on( 'reject', 'awaiting_user' )
    ->state( 'completed', StateMachineStatus::Completed )
    ->dispatch( [ 'thread_id' => 42 ] );

Queuety::machine_event(
    $machine_id,
    'user_message',
    [ 'message' => 'Find competitors for this product' ]
);

Queuety::machine_event(
    $machine_id,
    'approve',
    [ 'approved' => true, 'reviewer' => 'editor@example.com' ]
);
```

Этот автомат начинает в состоянии `awaiting_user`, переходит в `planning` при получении сообщения пользователя, позволяет поставленному в очередь действию отправить `planned`, а затем ожидает в `awaiting_review`, пока внешнее согласование не завершит сеанс. Важно не само по себе оформление кода. Важно, что жизненный цикл задан явно, доступен для проверки и управляется событиями.

## Как работает автомат

При отправке экземпляра автомата на выполнение Queuety сохраняет имя текущего состояния, публичную полезную нагрузку состояния, версию и хеш определения, допустимые входящие события для текущего состояния и долговечную хронологию событий.

Если для текущего состояния задан `action()`, Queuety ставит в очередь задание при входе в состояние. Это действие может просто обновить публичное состояние и оставить автомат в ожидании следующего события либо отправить `_event` и при необходимости `_event_payload`, чтобы сразу выполнить следующий переход. Если у состояния нет действия и оно не является конечным, автомат просто ждёт внешних событий, отправленных с помощью `Queuety::machine_event()`.

## Выбор между рабочими процессами и конечными автоматами

Используйте рабочий процесс, когда выполнение по своей сути состоит из шагов. Получите данные, обработайте каждый элемент, дождитесь результата, сформируйте итог и завершите работу. Эта модель подходит, когда есть чёткие начало и конец, а главный вопрос — какую задачу выполнять следующей.

Используйте конечный автомат, когда допустимое поведение зависит от текущей фазы жизненного цикла. Он лучше подходит, если события могут приходить в разном порядке, сущность может существовать долго или нужно явно проверять жизненный цикл с состояниями вроде `awaiting_user`, `planning`, `running_agents`, `awaiting_review` и `completed`.

## Как они взаимодействуют

Эти примитивы лучше работают вместе, дополняя друг друга. Действие состояния может запустить рабочий процесс, если переход требует более сложного выполнения, а рабочий процесс по завершении работы может отправить событие обратно в автомат. На практике это позволяет конечному автомату управлять жизненным циклом, а рабочим процессам — выполнять ресурсоёмкие или строго структурированные задачи.

## Структура конструктора

`StateMachineBuilder` намеренно оставлен небольшим. `initial()` и `state()` задают граф жизненного цикла. `action()` привязывает поставленную в очередь задачу к одному состоянию. `on()` объявляет, какие события могут перевести автомат в следующее состояние. `on_queue()`, `with_priority()` и `max_attempts()` управляют выполнением действий состояний, а `version()` и `idempotency_key()` добавляют метаданные для надёжной диспетчеризации. `dispatch()` создаёт экземпляр автомата.

Действия состояний также могут предоставлять необязательный метод `config()`, использующий те же ключи политики ресурсов, что и шаги и обработчики рабочих процессов. Это позволяет включить ресурсоёмкий переход состояния в общую группу параллелизма или задать ему больший вес затрат, если действие расходует больше бюджета, чем обычный шаг.

Более широкая модель ресурсов описана в разделе [Управление ресурсами](https://queuety.inline0.dev/ru/docs/features/resource-management).

## Контракты

Действия при входе в состояние реализуют `Queuety\Contracts\StateAction`:

```php
namespace Queuety\Contracts;

interface StateAction {
    public function handle( array $state, ?string $event = null, array $event_payload = [], array $payload = [] ): array|string;
}
```

`$payload` — это структурированная полезная нагрузка из определения действия; она пуста, если действие задано обычным именем класса. Queuety создаёт действия с помощью `new $class()`, поэтому у класса действия должен быть конструктор без обязательных аргументов.

Действия состояний также могут определять необязательный метод `config()` с `concurrency_group`, `concurrency_limit` и `cost_units`, если поставленное в очередь действие при входе должно участвовать в допуске к выполнению рабочими процессами, бюджетах взвешенной очереди или провайдера и других политиках ресурсов.

Условия перехода реализуют `Queuety\Contracts\StateGuard`:

```php
namespace Queuety\Contracts;

interface StateGuard {
    public function allows( array $state, array $event_payload, string $event, array $payload = [] ): bool;
}
```

## API для проверки состояния

Используйте фасад для проверки выполняющихся автоматов:

```php
$status   = Queuety::machine_status( $machine_id );
$machines = Queuety::list_machines();
$events   = Queuety::machine_timeline( $machine_id );
$trace    = Queuety::machine_trace( $machine_id );
$state    = Queuety::machine_state_at( $machine_id, $event_id );
```

Для конечных автоматов нет команд WP-CLI: `wp queuety workflow ...` предназначен только для рабочих процессов. Используйте эти API PHP, например через `wp eval`, чтобы проверять автоматы из командной строки.

Эти API предоставляют имя текущего состояния, статус жизненного цикла, публичное состояние, доступные события, метаданные определения, полную хронологию переходов и нормализованный пакет трассировки для интерфейсов отладки. Строки списка автоматов содержат версию и хеш определения, а также ключ идемпотентности, поэтому экраны списка могут показывать исходную и исполняемую версии, не загружая каждый автомат отдельно. Пакет трассировки включает сгруппированные состояния, исходные события, связанные задания действий, журналы, ссылки на артефакты и фрагменты, входные и выходные данные обработчика, состояние до и после, контекст, временные показатели и структурированные ошибки.

## Сеансы агентов

Конечные автоматы особенно полезны для систем агентов, которые больше похожи на сеансы, чем на конвейеры. Сеанс, переходящий между `awaiting_user`, `planning`, `running_agents`, `awaiting_review` и `completed`, обычно проще представить как явный жизненный цикл, чем как одно очень длинное определение рабочего процесса со множеством ожиданий и переходов назад.

## См. также

- [Neuron Конечный автомат сеанса](https://queuety.inline0.dev/ru/docs/examples/neuron-session-state-machine)
- [Neuron Конечный автомат памяти и проверки](https://queuety.inline0.dev/ru/docs/examples/neuron-memory-review)
- [Оркестрация агентов](https://queuety.inline0.dev/ru/docs/workflows/agent-orchestration)
- [Сигналы рабочего процесса](https://queuety.inline0.dev/ru/docs/workflows/signals)
- [Зависимости рабочего процесса](https://queuety.inline0.dev/ru/docs/workflows/dependencies)
- [Повторы](https://queuety.inline0.dev/ru/docs/workflows/repeats)
- [Артефакты](https://queuety.inline0.dev/ru/docs/features/artifacts)
- [ PHP API](https://queuety.inline0.dev/ru/docs/api)
