> ## Documentation Index
> Fetch the complete documentation index at: https://kawax.biz/llms.txt
> Use this file to discover all available pages before exploring further.

# Laravel AI SDKのエージェントミドルウェア

> Laravel 13のAI SDKで、モデル呼び出しごとのミドルウェア実行順、応答後の処理、累積トークンに応じた停止を解説します。

エージェントが複数のツールを使うとき、**モデルを呼び出す前に使用量を確認し、各ステップの応答を記録したい**場合があります。Laravel AI SDKのミドルウェアは、エージェント全体を一度だけ包むものではなく、生成の各ステップで実行されます。このページではLaravel 13の公式ドキュメントと `laravel/ai` の `v1.3.0` の実装をもとに、[AI SDKの基本操作](/jp/ai-sdk)より一歩踏み込んで実行順と停止条件を整理します。

## ミドルウェアが実行される単位

エージェントがモデルの応答を受け取り、ツールを実行して再びモデルを呼ぶ場合は、次のモデル呼び出しが**別のステップ**になります。各ステップの直前に、`HasMiddleware::middleware()` で返したミドルウェアを宣言順に通します。

ミドルウェアの `handle(PendingStep $step, Closure $next)` に渡される `PendingStep` には、実行済みステップの配列 `steps`、その累積使用量 `usage`、現在のゼロ始まりのステップ番号 `number` が含まれます。初回の `usage` はまだ今回のモデル呼び出しの使用量を含みません。

```mermaid theme={null}
flowchart TD
    A["次のPendingStep<br>完了済みステップの累積usage"] --> B["middleware()の宣言順に評価"]
    B --> C{"Stop条件に一致?"}
    C -->|Yes| D["このステップのモデル呼び出しをせず終了"]
    C -->|No| E["モデルを呼び出す"]
    E --> F["StepResponseを受け取る"]
    F --> G["then()コールバックを実行"]
    G --> H["必要ならツールを実行"]
    H --> I{"次のステップが必要?"}
    I -->|Yes| A
    I -->|No| J["エージェントの応答を確定"]
```

`$next($step)` の結果に付けた `then()` は、**モデルが応答した後、ツール呼び出しが実行される前**に動きます。ツール実行後にだけ呼ばれるフックではありません。また、この時点の `usage` は以前に完了したステップの合計であり、進行中のリクエストのトークン数を予測しません。

## ステップごとの処理を登録する

ステップ番号と、それまでに使った入力トークン数だけをログに残す例です。プロンプト本文やモデルの出力をログへ無条件に保存しないようにします。

```php theme={null}
<?php

namespace App\Ai\Middleware;

use Closure;
use Illuminate\Support\Facades\Log;
use Laravel\Ai\Gateway\StepResponse;
use Laravel\Ai\PendingStep;

final class LogAgentSteps
{
    public function handle(PendingStep $step, Closure $next)
    {
        Log::info('AI step starting', [
            'step' => $step->number,
            'input_tokens_used' => $step->usage->inputTokens,
        ]);

        return $next($step)->then(function (StepResponse $response) use ($step): void {
            Log::info('AI model responded', ['step' => $step->number]);
        });
    }
}
```

エージェントで `HasMiddleware` を実装し、`middleware()` からインスタンスを返します。

```php theme={null}
<?php

namespace App\Ai\Agents;

use App\Ai\Middleware\LogAgentSteps;
use Laravel\Ai\Contracts\Agent;
use Laravel\Ai\Contracts\HasMiddleware;
use Laravel\Ai\Middleware\Stop;
use Laravel\Ai\PendingStep;
use Laravel\Ai\Promptable;

class SupportAgent implements Agent, HasMiddleware
{
    use Promptable;

    public function instructions(): string
    {
        return 'Answer support questions concisely.';
    }

    public function middleware(): array
    {
        return [
            Stop::when(fn (PendingStep $step): bool => $step->usage->inputTokens >= 50_000),
            new LogAgentSteps,
        ];
    }
}
```

`middleware()` の並び順では、`Stop` を先に置きます。上限を超えて**次のステップを開始しない場合**、後続の `LogAgentSteps` はそのステップで実行されません。順序を逆にすると、ログの「開始」だけが記録され、「モデルが応答した」は記録されないことがあります。`$next($step)` の結果を `handle()` から返し、`then()` のコールバックではレスポンスを返す必要はありません。

## Stopは次のモデル呼び出しを止める

`Stop::when(...)` は条件がtrueになったとき、`Stop::unless(...)` は条件がfalseになったときに、そのステップの前で処理を終了します。上の例は**完了済みステップ**の累積入力トークンが50,000以上になったら次の呼び出しを行いません。1回のモデル呼び出しが閾値を超える前に強制停止するハードリミットではありません。

| 判定時点 | 条件が成立したときの結果 |
| - | - |
| 最初のステップを呼ぶ前 | モデルを呼ばず、完了済みステップも応答テキストも空になる |
| ツール実行後の次のステップを呼ぶ前 | それまでに完了したステップと使用量を保持して終了する |
| ストリーミングの次のステップを呼ぶ前 | ストリームの終了理由は `cancelled` になる |

> [!WARNING]
>
> `Stop` は進行中のプロバイダーへのリクエストを中断しません。使用量の上限を厳密に保証する必要があるなら、次のステップの判定だけに依存せず、モデルやプロバイダー側の制限も検討してください。

独自条件で停止したいミドルウェアは `Laravel\Ai\Gateway\StepResult::stop()` を返すこともできます。`handle()` からは `$next($step)` の結果、`StepResponse`、または `StepResult::stop()` を返します。無関係な型を返すと例外になります。

## 実行順を検証する

モデル応答とツール実行を混同しないよう、少なくとも次のケースを確認します。

* 1ステップだけの生成では、開始ログとモデル応答ログが1回ずつ出る。
* ツールに続く追加ステップでは、番号が0から増え、後続ステップの `usage` に前のステップの使用量が含まれる。
* 最初から停止条件が成立する場合、モデル呼び出しと `LogAgentSteps` のログが発生しない。
* 途中で条件に達する場合、それまでのステップは残り、次のステップのモデル呼び出しは発生しない。

通常の経路と停止する経路を分け、各ステップの実行回数を確認します。`then()` に副作用が必要な場合も、モデル応答を得る前に失敗・停止した経路では呼ばれない前提で設計してください。

## 参照した一次情報

* [Laravel 13公式ドキュメント: AI SDKのミドルウェア](https://github.com/laravel/docs/blob/13.x/ai-sdk.md)
* [Laravel AI SDK v1.3.0: PendingStepの値](https://github.com/laravel/ai/blob/v1.3.0/src/PendingStep.php#L18-L46)
* [Laravel AI SDK v1.3.0: 生成ループ](https://github.com/laravel/ai/blob/v1.3.0/src/Gateway/TextGenerationLoop.php#L133-L188)・[ミドルウェアの実行順](https://github.com/laravel/ai/blob/v1.3.0/src/Gateway/TextGenerationLoop.php#L441-L468)
* [Laravel AI SDK v1.3.0: StepResult::stop()とthen()](https://github.com/laravel/ai/blob/v1.3.0/src/Gateway/StepResult.php#L37-L59)
* [Laravel AI SDK v1.3.0: Stopミドルウェア](https://github.com/laravel/ai/blob/v1.3.0/src/Middleware/Stop.php)
* [Laravel AI SDK v1.3.0: ステップ停止のテスト](https://github.com/laravel/ai/blob/v1.3.0/tests/Feature/AgentMiddlewareTest.php#L300-L345)


## Related topics

- [応用トピック](/jp/advanced/index.md)
- [Laravel Agent Detector — AIエージェント検出パッケージ](/jp/blog/agent-detector-introduction.md)
- [Laravel AI SDK](/jp/ai-sdk.md)
- [Laravel Fetch Metadata](/jp/packages/laravel-fetch-metadata.md)
- [ブログ](/jp/blog/index.md)


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.