> ## 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 中每次模型调用的中间件执行顺序、响应后的处理，以及根据累计 token 停止的方法。

当智能体使用多个工具时，有时你会希望**在调用模型之前检查用量，并记录每个步骤的响应**。Laravel AI SDK 的中间件并不是只包裹整个智能体一次，而是在生成过程的每个步骤中执行。本页基于 Laravel 13 官方文档和 `laravel/ai` `v1.3.0` 的实现，在 [AI SDK 基本操作](/zh-CN/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 -->|是| D["不调用本步骤的模型，直接结束"]
    C -->|否| E["调用模型"]
    E --> F["接收 StepResponse"]
    F --> G["执行 then() 回调"]
    G --> H["如有需要则执行工具"]
    H --> I{"需要下一个步骤?"}
    I -->|是| A
    I -->|否| J["确定智能体的响应"]
```

附加在 `$next($step)` 结果上的 `then()` 会在**模型响应之后、工具调用执行之前**运行。它并不是只在工具执行后才调用的钩子。此外，此时的 `usage` 是之前已完成步骤的合计，并不会预测进行中请求的 token 数。

## 注册每个步骤的处理

下面的示例只记录步骤编号以及到目前为止使用的输入 token 数。请避免无条件地将提示正文或模型输出保存到日志中。

```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` 在该步骤中不会执行。如果顺序颠倒，可能只会记录“开始”日志，而不会记录“模型已响应”日志。需要从 `handle()` 返回 `$next($step)` 的结果，而 `then()` 的回调无需返回响应。

## Stop 会阻止下一次模型调用

`Stop::when(...)` 在条件为 true 时、`Stop::unless(...)` 在条件为 false 时，会在该步骤之前结束处理。上面的示例在**已完成步骤**的累计输入 token 达到 50,000 以上时，不再进行下一次调用。它并不是在单次模型调用超过阈值之前强制停止的硬性限制。

| 判断时机 | 条件成立时的结果 |
| - | - |
| 调用第一个步骤之前 | 不调用模型，已完成步骤和响应文本均为空 |
| 工具执行后、调用下一个步骤之前 | 保留此前已完成的步骤和用量并结束 |
| 流式传输中调用下一个步骤之前 | 流的结束原因为 `cancelled` |

> [!WARNING]
>
> `Stop` 不会中断正在进行的提供商请求。如果需要严格保证用量上限，请不要只依赖对下一个步骤的判断，也应考虑模型或提供商一侧的限制。

需要按自定义条件停止的中间件，也可以返回 `Laravel\Ai\Gateway\StepResult::stop()`。`handle()` 应返回 `$next($step)` 的结果、`StepResponse` 或 `StepResult::stop()`。返回无关的类型会抛出异常。

## 验证执行顺序

为避免混淆模型响应与工具执行，至少应确认以下情况。

* 只有一个步骤的生成中，开始日志和模型响应日志各输出一次。
* 在工具之后的追加步骤中，编号从 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-CN/advanced/index.md)
- [Laravel Agent Detector — AI 智能体检测包](/zh-CN/blog/agent-detector-introduction.md)
- [Laravel AI SDK](/zh-CN/ai-sdk.md)
- [Laravel Doctor — 应用程序诊断工具](/zh-CN/blog/laravel-doctor-introduction.md)
- [Laravel AI 智能体支持 MCP 服务器](/zh-CN/blog/ai-sdk-mcp-client.md)


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