
# Оркестрация агентов

Queuety хорошо подходит для систем агентов, которым нужна адаптивная работа без потери надёжного состояния, повторов, возможности проверки и явной семантики ожидания.

Для большинства сценариев работы агентов не нужен механизм DAG с возможностью менять структуру во время выполнения. Обычно достаточно небольшого набора надёжных операций оркестрации. `for_each()` распределяет работу внутри одного запуска рабочего процесса. `start_agents()` и `wait_for_agents()` передают обнаруженные во время выполнения задачи в независимые рабочие процессы верхнего уровня. `wait_for_decision()` и `wait_for_input()` приостанавливают выполнение для участия человека. `wait_for_workflow()`, `wait_for_workflows()` и `wait_for_agent_group()` позволяют одному запуску верхнего уровня дождаться другого запуска или запущенной группы, достигшей кворума.

## Полезная модель для понимания

Оркестрацию агентов проще всего рассматривать по уровням. Планировщик определяет, какая работа нужна. Queuety превращает её в надёжные задания или рабочие процессы. Запуски специализированных агентов формируют результаты и сохраняют артефакты. Контролирующий этап объединяет результаты и решает, что делать дальше. В любой момент человек может одобрить, отклонить или скорректировать запуск, прежде чем он продолжится.

Ключевое различие в том, что агент решает, какую работу выполнять, а Queuety по-прежнему определяет, как эта работа сохраняется, повторяется, возобновляется и объединяется. Если цель оркестрации — долгоживущий сеанс, а не ограниченный по времени запуск, используйте вместе с [Конечными автоматами](https://queuety.inline0.dev/ru/docs/state-machines).

## Шаблон 1: планировщик и исполнитель с независимыми запусками агентов

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

```php
use Queuety\Enums\WaitMode;
use Queuety\Queuety;

$agent_run = Queuety::workflow( 'agent_run' )
    ->then( FetchSourcesStep::class )
    ->then( DraftFindingStep::class );

Queuety::workflow( 'brief_research' )
    ->version( 'brief-research.v1' )
    ->idempotency_key( 'brief:42:research' )
    ->max_transitions( 20 )
    ->then( PlanResearchTasksStep::class )
    ->start_agents( 'agent_tasks', $agent_run, group_key: 'researchers' )
    ->wait_for_agent_group( 'researchers', WaitMode::Quorum, 2, 'agent_results' )
    ->then( SynthesizeBriefStep::class )
    ->dispatch( [ 'brief_id' => 42 ] );
```

В этом шаблоне `PlanResearchTasksStep` записывает обнаруженные во время выполнения элементы в `$state['agent_tasks']`, `start_agents()` запускает отдельный надёжный рабочий процесс для каждой задачи, а `wait_for_agent_group()` приостанавливает родительский процесс до выполнения выбранного условия завершения. Затем `SynthesizeBriefStep` получает завершённые состояния дочерних процессов в `$state['agent_results']`.

Это лучший отправной шаблон для систем с планировщиком и исполнителем: каждый запуск агента можно проверять и повторять отдельно.

## Шаблон 2: проверка человеком перед продолжением запуска

Используйте `wait_for_decision()`, когда рабочий процесс должен остановиться для принятия решения «да» или «нет», и `wait_for_input()`, когда оператору нужно передать структурированную обратную связь обратно в запуск.

```php
use Queuety\Queuety;

$workflow_id = Queuety::workflow( 'brief_review' )
    ->then( DraftBriefStep::class )
    ->wait_for_decision( result_key: 'review' )
    ->then( HandleReviewDecisionStep::class )
    ->wait_for_input( result_key: 'revision_notes' )
    ->then( ApplyRevisionNotesStep::class )
    ->dispatch( [ 'brief_id' => 42 ] );
```

Если проверяющий одобряет:

```php
Queuety::approve_workflow(
    $workflow_id,
    [
        'reviewer' => 'editor@example.com',
        'notes' => 'Looks good, publish after legal check',
    ],
    'approved'
);
```

Если проверяющий отклоняет:

```php
Queuety::reject_workflow(
    $workflow_id,
    [
        'reviewer' => 'editor@example.com',
        'reason' => 'Needs citations and a stronger conclusion',
    ],
    'rejected'
);
```

После `wait_for_decision()` рабочий процесс получает структурированную полезную нагрузку с итогом, сигналом, возобновившим запуск, и данными проверяющего, если они были переданы. Так создаётся полноценная контрольная точка с участием человека, при этом процесс PHP не остаётся открытым во время ожидания.

## Шаблон 3: один рабочий процесс верхнего уровня ожидает другой

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

```php
use Queuety\Enums\WaitMode;
use Queuety\Queuety;

$research_id = Queuety::workflow( 'research_brief' )
    ->then( PlanResearchTasksStep::class )
    ->start_agents( 'agent_tasks', $agent_run )
    ->wait_for_agents( mode: WaitMode::All, result_key: 'agent_results' )
    ->then( SynthesizeBriefStep::class )
    ->dispatch( [ 'brief_id' => 42 ] );

Queuety::workflow( 'publish_brief' )
    ->wait_for_workflow( $research_id, 'research' )
    ->wait_for_decision( result_key: 'editor_review' )
    ->then( PublishBriefStep::class )
    ->dispatch( [ 'brief_id' => 42 ] );
```

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

## Выбор подходящего примитива

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

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

`wait_for_workflow()` и `wait_for_workflows()` подходят, когда один рабочий процесс верхнего уровня зависит от другого и нужна надёжная асинхронная координация между уровнями оркестрации.

`wait_for_decision()` и `wait_for_input()` подходят для случаев, когда человеку нужно одобрить, отклонить или направить запуск, пока рабочий процесс приостановлен и не расходует ресурсы рабочих процессов.

## Рекомендуемые соглашения для состояний

Системы агентов проще отлаживать, если ключи состояния предсказуемы. Обычно `agent_tasks` удобно использовать для результатов планировщика, `agent_workflow_ids` — для запущенных процессов верхнего уровня, `agent_results` — для состояния завершённых дочерних рабочих процессов, а `review` или `decision` — для данных одобрения или отклонения. Большие результаты, например черновики, цитаты или манифесты исследований, обычно лучше хранить в хранилище артефактов, а не в основном наборе состояния рабочего процесса.

## Рекомендуемые ограничения для запусков агентов

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

```php
Queuety::workflow( 'agent_research' )
    ->version( 'agent-research.v2' )
    ->idempotency_key( 'brief:42:agent-research' )
    ->max_transitions( 20 )
    ->max_for_each_items( 12 )
    ->max_state_bytes( 32768 );
```

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

## См. также

- [Примеры](https://queuety.inline0.dev/ru/docs/examples)
- [Триггеры действий](https://queuety.inline0.dev/ru/docs/features/action-triggers)
- [Динамический цикл For-Each](https://queuety.inline0.dev/ru/docs/workflows/for-each)
- [Асинхронная передача задач](https://queuety.inline0.dev/ru/docs/workflows/async-handoffs)
- [Зависимости рабочего процесса](https://queuety.inline0.dev/ru/docs/workflows/dependencies)
- [Артефакты](https://queuety.inline0.dev/ru/docs/features/artifacts)
- [Сигналы рабочего процесса](https://queuety.inline0.dev/ru/docs/workflows/signals)
- [Ограничения рабочего процесса](https://queuety.inline0.dev/ru/docs/workflows/guardrails)
