
# ワークフローのガードレール

エージェント型ワークフローは柔軟ですが、重複した実行を作成したり、プランナーが想定を超える作業を生み出したりしやすくなります。Queuetyはワークフローレベルで軽量なガードレールを提供し、制御を保ちながら柔軟性を活用できるようにします。

ワーカーの受け入れやジョブごとのコストメタデータに関する、より広範なリソースモデルについては、[リソース管理](https://queuety.inline0.dev/ja/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エンドポイント
- 重複した配信を受け取る可能性のあるWebhookコンシューマー
- 外部エンティティごとに1つのオーケストレーション実行だけを作成すべきプランナーステップ

冪等性キーは`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()`

1つの`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, ... ]
```
