> ## 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의 기본 조작](/ko/ai-sdk)보다 한 걸음 더 나아가 실행 순서와 중단 조건을 정리합니다.

## 미들웨어가 실행되는 단위

에이전트가 모델의 응답을 받고 도구를 실행한 뒤 다시 모델을 호출하는 경우, 다음 모델 호출은 **별도의 스텝**이 됩니다. 각 스텝 직전에 `HasMiddleware::middleware()`에서 반환한 미들웨어를 선언 순서대로 통과합니다.

미들웨어의 `handle(PendingStep $step, Closure $next)`에 전달되는 `PendingStep`에는 실행이 끝난 스텝의 배열 `steps`, 그 누적 사용량 `usage`, 현재의 0부터 시작하는 스텝 번호 `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스텝만으로 이루어진 생성에서는 시작 로그와 모델 응답 로그가 한 번씩 출력된다.
* 도구에 이어지는 추가 스텝에서는 번호가 0부터 증가하고, 후속 스텝의 `usage`에 이전 스텝의 사용량이 포함된다.
* 처음부터 중단 조건이 성립하는 경우, 모델 호출과 `LogAgentSteps`의 로그가 발생하지 않는다.
* 도중에 조건에 도달하는 경우, 그때까지의 스텝은 남고 다음 스텝의 모델 호출은 발생하지 않는다.

일반 경로와 중단되는 경로를 나누어 각 스텝의 실행 횟수를 확인합니다. `then()`에 부수 효과가 필요한 경우에도, 모델 응답을 얻기 전에 실패하거나 중단된 경로에서는 호출되지 않는다는 전제로 설계하세요.

## 참고한 1차 자료

* [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

- [고급 주제](/ko/advanced/index.md)
- [Laravel Agent Detector — AI 에이전트 감지 패키지](/ko/blog/agent-detector-introduction.md)
- [AI 에이전트는 '올바른' 코드에서 'Laravel다운' 코드로 - Boost Benchmarks의 다음 한 수](/ko/blog/boost-benchmarks-idiomatic-laravel.md)
- [Laravel AI SDK](/ko/ai-sdk.md)
- [블로그](/ko/blog/index.md)


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