
# 에이전트 오케스트레이션

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/ko/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/ko/docs/examples)
- [액션 트리거](https://queuety.inline0.dev/ko/docs/features/action-triggers)
- [동적 For-Each](https://queuety.inline0.dev/ko/docs/workflows/for-each)
- [비동기 핸드오프](https://queuety.inline0.dev/ko/docs/workflows/async-handoffs)
- [워크플로 종속성](https://queuety.inline0.dev/ko/docs/workflows/dependencies)
- [아티팩트](https://queuety.inline0.dev/ko/docs/features/artifacts)
- [워크플로 신호](https://queuety.inline0.dev/ko/docs/workflows/signals)
- [워크플로 가드레일](https://queuety.inline0.dev/ko/docs/workflows/guardrails)
