
# Ограничения рабочего процесса

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

См. [Управление ресурсами](https://queuety.inline0.dev/ru/docs/features/resource-management), чтобы узнать о более общей модели ресурсов, включая допуск рабочих процессов и метаданные затрат для каждого задания.

## Версионирование определения рабочего процесса

Используйте `version()`, чтобы добавить к определению рабочего процесса метку версии на уровне приложения:

```php
Queuety::workflow( 'research_run' )
    ->version( 'research.v2' )
    ->then( PlanResearchStep::class )
    ->then( DraftResearchSummaryStep::class )
    ->dispatch();
```

Версия сохраняется вместе с состоянием рабочего процесса, доступна через `workflow_status()`, выводится с помощью `wp queuety workflow status` и включается в экспорт рабочих процессов.

Queuety также сохраняет детерминированный `definition_hash` для каждого запуска рабочего процесса. Хеш вычисляется на основе сериализованного определения рабочего процесса и помогает точно определить, какую структуру рабочего процесса выполняет уже запущенный процесс после развертывания.

## Обеспечение идемпотентности диспетчеризации

Используйте `idempotency_key()`, если вызывающая сторона может повторить тот же запрос:

```php
Queuety::workflow( 'research_run' )
    ->idempotency_key( 'brief:42:research' )
    ->then( PlanResearchStep::class )
    ->dispatch( [ 'brief_id' => 42 ] );
```

Если рабочий процесс запускается повторно с уже использованным ключом, Queuety возвращает ID исходного рабочего процесса, а не создает дубликат. Ключи глобальны для всех имен рабочих процессов, поэтому добавляйте имя рабочего процесса в ключ, если разные процессы могут использовать один ключ.

Это особенно полезно для:

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

Ключи идемпотентности хранятся в таблице `queuety_workflow_dispatch_keys`.

## Настройка бюджетов рабочего процесса

Используйте бюджеты рабочего процесса, чтобы завершать с ошибкой запуски, которые превышают запланированные пределы:

```php
Queuety::workflow( 'research_run' )
    ->max_transitions( 20 )
    ->max_for_each_items( 12 )
    ->max_state_bytes( 32768 )
    ->max_cost_units( 40 )
    ->max_started_workflows( 8 )
    ->then( PlanResearchStep::class )
    ->for_each( 'tasks', ExecuteResearchTask::class, 'results' )
    ->dispatch();
```

### `max_transitions()`

Ограничивает число шагов рабочего процесса, которые могут завершиться до сбоя запуска. Это полезно для защиты рабочих процессов с динамическим ветвлением или повторным прохождением одних и тех же участков через `_next_step`.

### `max_for_each_items()`

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

### `max_state_bytes()`

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

### `max_cost_units()`

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

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

### `max_started_workflows()`

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

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

## Наблюдение за ограничениями

Статус рабочего процесса показывает:

- `definition_version`
- `definition_hash`
- `idempotency_key`
- `budget`

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

Пример:

```php
$state = Queuety::workflow_status( $workflow_id );

$state->definition_version; // 'research.v2'
$state->definition_hash;    // '4c7a...'
$state->idempotency_key;    // 'brief:42:research'
$state->budget;             // [ 'max_transitions' => 20, ... ]
```
