> ## 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 的 Agent 中介層

> 說明 Laravel 13 AI SDK 中，每次模型呼叫時中介層的執行順序、回應後的處理，以及依累積 token 停止的方式。

當 Agent 使用多個 Tool 時，有時會希望**在呼叫模型前確認用量，並記錄每個步驟的回應**。Laravel AI SDK 的中介層並不是只包住整個 Agent 一次，而是在生成的每個步驟中執行。本頁以 Laravel 13 官方文件與 `laravel/ai` `v1.3.0` 的實作為基礎，比 [AI SDK 的基本操作](/zh-TW/ai-sdk) 更深入一步，整理執行順序與停止條件。

## 中介層執行的單位

當 Agent 收到模型的回應、執行 Tool 後再次呼叫模型時，下一次的模型呼叫會是**另一個步驟**。在每個步驟之前，會依宣告順序通過 `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 -->|是| D["不進行此步驟的模型呼叫並結束"]
    C -->|否| E["呼叫模型"]
    E --> F["收到 StepResponse"]
    F --> G["執行 then() 回呼"]
    G --> H["視需要執行 Tool"]
    H --> I{"需要下一個步驟?"}
    I -->|是| A
    I -->|否| J["確定 Agent 的回應"]
```

附加在 `$next($step)` 結果上的 `then()`，會在**模型回應之後、Tool 呼叫執行之前**運作。它並不是只在 Tool 執行後才呼叫的 hook。此外，此時的 `usage` 是先前已完成步驟的合計，並不會預測進行中請求的 token 數。

## 註冊每個步驟的處理

以下範例只將步驟編號與到目前為止使用的輸入 token 數記錄到日誌。請避免無條件地將 prompt 內容或模型輸出存入日誌。

```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]);
        });
    }
}
```

於 Agent 實作 `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` 在該步驟中不會執行。若順序相反，日誌可能只記錄「開始」，而不會記錄「模型已回應」。請從 `handle()` 回傳 `$next($step)` 的結果；`then()` 的回呼則不需要回傳回應。

## Stop 會停止下一次模型呼叫

`Stop::when(...)` 在條件為 true 時、`Stop::unless(...)` 在條件為 false 時，會在該步驟之前結束處理。上面的範例在**已完成步驟**的累積輸入 token 達到 50,000 以上時，就不會進行下一次呼叫。這並不是在單次模型呼叫超過門檻前強制停止的硬性限制。

| 判斷時機 | 條件成立時的結果 |
| - | - |
| 呼叫第一個步驟之前 | 不呼叫模型，已完成步驟與回應文字皆為空 |
| Tool 執行後、呼叫下一個步驟之前 | 保留到目前為止已完成的步驟與用量並結束 |
| 串流中呼叫下一個步驟之前 | 串流的結束原因為 `cancelled` |

> [!WARNING]
>
> `Stop` 不會中斷進行中對供應商的請求。若需要嚴格保證用量上限，請不要只依賴下一個步驟的判斷，也應考慮模型或供應商端的限制。

想以自訂條件停止的中介層，也可以回傳 `Laravel\Ai\Gateway\StepResult::stop()`。`handle()` 應回傳 `$next($step)` 的結果、`StepResponse` 或 `StepResult::stop()`。回傳無關的型別會拋出例外。

## 驗證執行順序

為了避免混淆模型回應與 Tool 執行，請至少確認以下情境。

* 只有一個步驟的生成中，開始日誌與模型回應日誌各輸出一次。
* 在 Tool 之後的追加步驟中，編號從 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

- [進階主題](/zh-TW/advanced/index.md)
- [Laravel AI SDK 的 Amazon Bedrock 驅動](/zh-TW/packages/laravel-amazon-bedrock.md)
- [Laravel AI SDK](/zh-TW/ai-sdk.md)
- [套件中介層的註冊與適用範圍](/zh-TW/advanced/package-middleware.md)
- [Controller 的 PHP Attributes](/zh-TW/advanced/controller-attributes.md)


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