> ## 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

> 统一接口对接 OpenAI、Anthropic、Gemini 等多家 AI 提供商的 Laravel AI SDK 完整指南。覆盖智能体、图像生成、音频、嵌入向量以及测试等。

## 简介

[Laravel AI SDK](https://github.com/laravel/ai) 为你提供了一套统一、表达力强的 API，用来与 OpenAI、Anthropic、Gemini 等 AI 提供商进行交互。借助 AI SDK，你可以构建带工具调用和结构化输出的智能体、生成图像、进行文本转语音与语音转文本、创建向量嵌入等，全部通过一致而符合 Laravel 风格的接口完成。

<Info>
  Laravel AI SDK 是在 Laravel 13 中新增的官方扩展包。它以 `laravel/ai` 的形式提供，允许你通过统一的 API 使用多家 AI 提供商。
</Info>

## 支持的提供商一览

| 功能             | 支持的提供商                                                                                                         |
| -------------- | -------------------------------------------------------------------------------------------------------------- |
| 文本生成           | OpenAI, OpenAI Compatible, Anthropic, Gemini, Azure, Bedrock, Groq, xAI, DeepSeek, Mistral, Ollama, OpenRouter |
| 图像生成           | OpenAI, Gemini, xAI, Azure, Bedrock, OpenRouter                                                                |
| 语音合成（TTS）      | OpenAI, ElevenLabs, Gemini                                                                                     |
| 语音识别（STT）      | OpenAI, ElevenLabs, Mistral, Gemini                                                                            |
| 嵌入向量           | OpenAI, Gemini, Azure, Bedrock, Cohere, Mistral, Jina, VoyageAI, Ollama, OpenRouter                            |
| 重排序（Reranking） | Cohere, Jina, VoyageAI                                                                                         |
| 文件             | OpenAI, Anthropic, Gemini, Azure                                                                               |

## 安装

<Steps>
  <Step title="安装扩展包">
    使用 Composer 安装 Laravel AI SDK。

    ```shell theme={null}
    composer require laravel/ai
    ```
  </Step>

  <Step title="发布配置文件和迁移">
    通过 `vendor:publish` Artisan 命令发布配置文件和迁移。

    ```shell theme={null}
    php artisan vendor:publish --provider="Laravel\Ai\AiServiceProvider"
    ```
  </Step>

  <Step title="执行迁移">
    运行数据库迁移。它会创建 `agent_conversations` 和 `agent_conversation_messages` 两张表，用于保存会话历史。

    ```shell theme={null}
    php artisan migrate
    ```
  </Step>
</Steps>

## 配置

### 环境变量

在 `.env` 文件中配置你所使用的 AI 提供商的 API 密钥。

```ini theme={null}
ANTHROPIC_API_KEY=
AZURE_OPENAI_API_KEY=
COHERE_API_KEY=
DEEPSEEK_API_KEY=
ELEVENLABS_API_KEY=
GEMINI_API_KEY=
GROQ_API_KEY=
MISTRAL_API_KEY=
OLLAMA_API_KEY=
OPENAI_API_KEY=
OPENROUTER_API_KEY=
JINA_API_KEY=
VOYAGEAI_API_KEY=
XAI_API_KEY=
```

文本、图像、音频、语音转文本以及嵌入向量所使用的默认模型也可以在 `config/ai.php` 中配置。

### 自定义 Base URL

如果要通过代理服务转发，可以为每个提供商设置自定义 URL。

```php theme={null}
'providers' => [
    'openai' => [
        'driver' => 'openai',
        'key' => env('OPENAI_API_KEY'),
        'url' => env('OPENAI_URL'),
    ],
    'anthropic' => [
        'driver' => 'anthropic',
        'key' => env('ANTHROPIC_API_KEY'),
        'url' => env('ANTHROPIC_BASE_URL'),
    ],
],
```

OpenAI、Anthropic、Gemini、Groq、Cohere、DeepSeek、xAI 和 OpenRouter 均支持自定义 Base URL。

### OpenAI-Compatible 提供商

对于 LM Studio、vLLM、Together、Fireworks 或本地网关等提供 OpenAI 兼容 API 的服务，可以使用 `openai-compatible` 驱动来配置提供商。`url` 是必填项；如果指定了 `key`，它会作为 Bearer 令牌发送。

```php theme={null}
'providers' => [
    'local' => [
        'driver' => 'openai-compatible',
        'url' => env('LOCAL_AI_URL'),
        'key' => env('LOCAL_AI_API_KEY'),
    ],
],
```

配置完成后，就可以像使用其他提供商一样通过提供商名称来指定。

```php theme={null}
agent()->prompt('What is Laravel?', provider: 'local', model: 'local-model');
```

设置默认文本模型后，就可以在每次调用时省略模型名称。

```php theme={null}
'local' => [
    'driver' => 'openai-compatible',
    'url' => env('LOCAL_AI_URL'),
    'key' => env('LOCAL_AI_API_KEY'),
    'models' => [
        'text' => [
            'default' => env('LOCAL_AI_MODEL'),
        ],
    ],
],
```

OpenAI-Compatible 提供商支持文本生成、流式响应、工具调用、结构化输出以及图像附件。如果你的端点需要额外的请求体字段，请使用[提供商选项](#プロバイダーオプション)。

### Lab 枚举

在代码中引用提供商时，可以使用 `Lab` 枚举。

```php theme={null}
use Laravel\Ai\Enums\Lab;

Lab::Anthropic;
Lab::OpenAI;
Lab::Gemini;
```

***

## 智能体（Agent）

智能体是 Laravel AI SDK 的基本构成单元。使用 `make:agent` 命令可以生成智能体类。

```shell theme={null}
php artisan make:agent SalesCoach

# 生成带结构化输出的智能体
php artisan make:agent SalesCoach --structured
```

生成的智能体会被放到 `app/Ai/Agents/` 目录下。下面是一个实现了所有主要接口的智能体示例。

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

namespace App\Ai\Agents;

use App\Ai\Tools\RetrievePreviousTranscripts;
use App\Models\History;
use App\Models\User;
use Illuminate\Contracts\JsonSchema\JsonSchema;
use Laravel\Ai\Contracts\Agent;
use Laravel\Ai\Contracts\Conversational;
use Laravel\Ai\Contracts\HasStructuredOutput;
use Laravel\Ai\Contracts\HasTools;
use Laravel\Ai\Messages\Message;
use Laravel\Ai\Promptable;
use Stringable;

class SalesCoach implements Agent, Conversational, HasTools, HasStructuredOutput
{
    use Promptable;

    public function __construct(public User $user) {}

    public function instructions(): Stringable|string
    {
        return 'You are a sales coach, analyzing transcripts and providing feedback and an overall sales strength score.';
    }

    public function messages(): iterable
    {
        return History::where('user_id', $this->user->id)
            ->latest()
            ->limit(50)
            ->get()
            ->reverse()
            ->map(function ($message) {
                return new Message($message->role, $message->content);
            })->all();
    }

    public function tools(): iterable
    {
        return [new RetrievePreviousTranscripts];
    }

    public function schema(JsonSchema $schema): array
    {
        return [
            'feedback' => $schema->string()->required(),
            'score' => $schema->integer()->min(1)->max(10)->required(),
        ];
    }
}
```

```mermaid theme={null}
flowchart TD
    A["用户提示词"] --> B["Agent::prompt()"]
    B --> C["附加选项"]
    C -->|"实现了 Conversational"| D["附加会话历史"]
    C -->|"实现了 HasTools"| E["附加工具列表"]
    C -->|"实现了 HasStructuredOutput"| F["附加 JSON Schema"]
    D --> G["发送到 AI 提供商"]
    E --> G
    F --> G
    B --> G
    G --> H["响应"]
    H -->|"工具调用"| I["执行工具"]
    I --> G
    H -->|"完成"| J["AgentResponse"]
```

### 发起提示

使用 `prompt()` 方法向智能体发送消息。

```php theme={null}
$response = (new SalesCoach)->prompt('Analyze this sales transcript...');
return (string) $response;
```

使用静态方法 `make()` 可以让容器解析依赖并生成实例。

```php theme={null}
$agent = SalesCoach::make(user: $user);
```

可以在 `prompt()` 的参数中覆盖提供商、模型和超时时间。

```php theme={null}
$response = (new SalesCoach)->prompt(
    'Analyze this sales transcript...',
    provider: Lab::Anthropic,
    model: 'claude-haiku-4-5-20251001',
    timeout: 120,
);
```

### 会话上下文

实现 `Conversational` 接口并定义 `messages()` 方法后，就可以把历史会话传给 AI。

使用 `RemembersConversations` trait 可以让会话历史自动保存到数据库并自动读取。

```php theme={null}
use Laravel\Ai\Concerns\RemembersConversations;

class SalesCoach implements Agent, Conversational
{
    use Promptable, RemembersConversations;

    public function instructions(): string
    {
        return 'You are a sales coach...';
    }
}
```

通过 `forUser()` 开始一段会话，并使用返回的 `conversationId` 通过 `continue()` 继续对话。

```php theme={null}
$response = (new SalesCoach)->forUser($user)->prompt('Hello!');
$conversationId = $response->conversationId;

$response = (new SalesCoach)->continue($conversationId, as: $user)->prompt('Tell me more about that.');
```

### 结构化输出

实现 `HasStructuredOutput` 接口，并在 `schema()` 方法中定义 JSON Schema，就能把 AI 响应作为结构化数据接收。

```php theme={null}
public function schema(JsonSchema $schema): array
{
    return ['score' => $schema->integer()->required()];
}

$response = (new SalesCoach)->prompt('Analyze this...');
return $response['score'];
```

#### 嵌套对象

```php theme={null}
public function schema(JsonSchema $schema): array
{
    return [
        'score' => $schema->integer()->required(),
        'metadata' => $schema->object(fn ($schema) => [
            'confidence' => $schema->string()->enum(['low', 'medium', 'high'])->required(),
            'language' => $schema->string()->required(),
        ])->required(),
    ];
}
```

#### 对象数组

```php theme={null}
public function schema(JsonSchema $schema): array
{
    return [
        'feedback' => $schema->array()->items(
            $schema->object(fn ($schema) => [
                'comment' => $schema->string()->required(),
                'score' => $schema->integer()->required(),
            ])
        )->required(),
    ];
}
```

#### anyOf（多个 schema 中任选其一）

如果一个值可能匹配多个 schema 之一，可以使用 `anyOf` 方法。

```php theme={null}
public function schema(JsonSchema $schema): array
{
    return [
        'content' => $schema->anyOf([
            $schema->object(fn ($schema) => [
                'type' => $schema->string()->enum(['article'])->required(),
                'title' => $schema->string()->required(),
            ]),
            $schema->object(fn ($schema) => [
                'type' => $schema->string()->enum(['image'])->required(),
                'url' => $schema->string()->required(),
            ]),
        ])->required(),
    ];
}
```

### 附件

通过 `attachments` 参数可以把文档或图像传给智能体。

```php theme={null}
use Laravel\Ai\Files;

$response = (new SalesCoach)->prompt(
    'Analyze the attached sales transcript...',
    attachments: [
        Files\Document::fromStorage('transcript.pdf'),
        Files\Document::fromPath('/home/laravel/transcript.md'),
        $request->file('transcript'),
    ]
);
```

图像附件的传入方式相同。

```php theme={null}
$response = (new ImageAnalyzer)->prompt('What is in this image?', attachments: [
    Files\Image::fromStorage('photo.jpg'),
    Files\Image::fromPath('/home/laravel/photo.jpg'),
    $request->file('photo'),
]);
```

### 流式响应

使用 `stream()` 方法可以按分块返回响应，非常适合把长响应实时推送到前端。

```php theme={null}
Route::get('/coach', function () {
    return (new SalesCoach)->stream('Analyze this sales transcript...');
});
```

可以通过 `then()` 回调在流式传输完成后执行处理逻辑。

```php theme={null}
use Laravel\Ai\Responses\StreamedAgentResponse;

Route::get('/coach', function () {
    return (new SalesCoach)
        ->stream('Analyze this sales transcript...')
        ->then(function (StreamedAgentResponse $response) {
            // $response->text, $response->events, $response->usage...
        });
});
```

你也可以手动迭代流。

```php theme={null}
$stream = (new SalesCoach)->stream('Analyze this sales transcript...');

foreach ($stream as $event) {
    // ...
}
```

#### Vercel AI SDK 协议

如果前端使用 Vercel AI SDK，可以调用 `usingVercelDataProtocol()`。

```php theme={null}
Route::get('/coach', function () {
    return (new SalesCoach)->stream('Analyze...')->usingVercelDataProtocol();
});
```

### 广播

可以把流事件推送到 Laravel Echo 等广播频道。

```php theme={null}
use Illuminate\Broadcasting\Channel;

$stream = (new SalesCoach)->stream('Analyze this sales transcript...');

foreach ($stream as $event) {
    $event->broadcast(new Channel('channel-name'));
}
```

使用 `broadcastOnQueue()` 可以通过队列进行广播。

```php theme={null}
(new SalesCoach)->broadcastOnQueue(
    'Analyze this sales transcript...',
    new Channel('channel-name'),
);
```

#### 跳过体积过大的事件

某些广播平台会把单条 WebSocket 消息限制在约 10KB。如果工具调用结果等数据量较大的流事件超出此上限，就会导致广播失败。你可以使用 `WithoutBroadcasting` Attribute 把特定类型的事件排除在广播之外。

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

namespace App\Ai\Agents;

use Laravel\Ai\Attributes\WithoutBroadcasting;
use Laravel\Ai\Contracts\Agent;
use Laravel\Ai\Contracts\HasTools;
use Laravel\Ai\Promptable;
use Laravel\Ai\Streaming\Events\ToolCall;
use Laravel\Ai\Streaming\Events\ToolResult;

#[WithoutBroadcasting(ToolCall::class, ToolResult::class)]
class SearchAgent implements Agent, HasTools
{
    use Promptable;

    // ...
}
```

被排除的事件不会被广播，但仍会保存到 `agent_conversation_messages` 表中。因此，前端在流结束后仍然可以获取到完整的工具数据。这一机制对通过队列（`broadcastOnQueue`）和同步方式（`broadcast` / `broadcastNow`）都有效。

### 队列

使用 `queue()` 方法可以把提示词放入队列进行异步处理。

```php theme={null}
use Laravel\Ai\Responses\AgentResponse;

Route::post('/coach', function (Request $request) {
    (new SalesCoach)
        ->queue($request->input('transcript'))
        ->then(function (AgentResponse $response) { /* ... */ })
        ->catch(function (Throwable $e) { /* ... */ });

    return back();
});
```

### 工具

工具允许 AI 调用你代码中的函数。使用 `make:tool` 命令可以生成工具类。

```shell theme={null}
php artisan make:tool RandomNumberGenerator
```

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

namespace App\Ai\Tools;

use Illuminate\Contracts\JsonSchema\JsonSchema;
use Laravel\Ai\Contracts\Tool;
use Laravel\Ai\Tools\Request;
use Stringable;

class RandomNumberGenerator implements Tool
{
    public function description(): Stringable|string
    {
        return 'This tool may be used to generate cryptographically secure random numbers.';
    }

    public function handle(Request $request): Stringable|string
    {
        return (string) random_int($request['min'], $request['max']);
    }

    public function schema(JsonSchema $schema): array
    {
        return [
            'min' => $schema->integer()->min(0)->required(),
            'max' => $schema->integer()->required(),
        ];
    }
}
```

在智能体的 `tools()` 方法中注册工具。

```php theme={null}
public function tools(): iterable
{
    return [new RandomNumberGenerator];
}
```

#### 相似度检索工具

可以轻松添加一个基于向量嵌入的相似度检索工具。

```php theme={null}
use App\Models\Document;
use Laravel\Ai\Tools\SimilaritySearch;

public function tools(): iterable
{
    return [
        SimilaritySearch::usingModel(Document::class, 'embedding'),
    ];
}
```

也可以指定选项。

```php theme={null}
SimilaritySearch::usingModel(
    model: Document::class,
    column: 'embedding',
    minSimilarity: 0.7,
    limit: 10,
    query: fn ($query) => $query->where('published', true),
),
```

还可以通过闭包定义自定义的检索逻辑。

```php theme={null}
new SimilaritySearch(using: function (string $query) {
    return Document::query()
        ->where('user_id', $this->user->id)
        ->whereVectorSimilarTo('embedding', $query)
        ->limit(10)
        ->get();
}),
```

通过 `withDescription()` 可以自定义工具的说明文本。

```php theme={null}
SimilaritySearch::usingModel(Document::class, 'embedding')
    ->withDescription('Search the knowledge base for relevant articles.'),
```

### 文件存储工具

使用 `FileStorage` 工具工厂可以让智能体访问 Laravel [文件系统磁盘](/zh/filesystem)。`all` 方法会返回一组工具，用于在指定磁盘上列出、读取、生成 URL、写入、删除和复制文件。

```php theme={null}
use Laravel\Ai\Tools\FileStorage;

public function tools(): iterable
{
    return FileStorage::all('local');
}
```

如果只允许只读访问，可以使用 `readOnly` 方法。

```php theme={null}
return FileStorage::readOnly('local');
```

这些方法都返回 `Illuminate\Support\Collection`，因此你可以进一步筛选要提供的工具。

```php theme={null}
use Laravel\Ai\Tools\Filesystem\DeleteFile;

return FileStorage::all('s3')
    ->reject(fn ($tool) => $tool instanceof DeleteFile);
```

### MCP 工具

如果你的应用使用了 [Laravel MCP](/zh/mcp)，可以把 [Model Context Protocol](https://modelcontextprotocol.io) 服务器暴露出的工具提供给智能体。通过 [Laravel MCP 客户端](/zh/mcp#mcp-client)，你可以连接远程或本地 MCP 服务器，并将其工具直接传给智能体。

<Info>
  使用 MCP 工具需要在应用中安装 [Laravel MCP](/zh/mcp) 扩展包。
</Info>

MCP 客户端的 `tools` 方法返回的是一个集合，因此需要使用 `...` 展开运算符将它展开到智能体的 `tools` 数组中。

```php theme={null}
use App\Ai\Tools\RandomNumberGenerator;
use Laravel\Mcp\Client;

/**
 * Get the tools available to the agent.
 *
 * @return Tool[]
 */
public function tools(): iterable
{
    return [
        ...Client::web('https://mcp.example.com')
            ->withToken($token)
            ->tools(),

        new RandomNumberGenerator,
    ];
}
```

AI SDK 会自动包装每个 MCP 工具，让智能体像调用其他工具一样使用它们。你也可以使用[命名 MCP 客户端](/zh/mcp#named-clients)。

```php theme={null}
use Laravel\Mcp\Facades\Mcp;

public function tools(): iterable
{
    return [
        ...Mcp::client('github')->tools(),
    ];
}
```

也可以连接到[本地 MCP 服务器](/zh/mcp#client-connecting)。

```php theme={null}
use Laravel\Mcp\Client;

public function tools(): iterable
{
    return [
        ...Client::local('php', ['artisan', 'mcp:start'])->tools(),
    ];
}
```

有关 MCP 客户端的创建与鉴权（Bearer 令牌、OAuth 等）请参考 [MCP 客户端文档](/zh/mcp#mcp-client)。

### 提供商内置工具

这些是 AI 提供商原生实现的特殊工具。

#### Web 搜索

为智能体添加网页搜索能力。Anthropic、OpenAI、Gemini 和 OpenRouter 均支持。

```php theme={null}
use Laravel\Ai\Providers\Tools\WebSearch;

public function tools(): iterable
{
    return [new WebSearch];
}
```

可以指定搜索结果数量、允许的域名以及地理位置。

```php theme={null}
(new WebSearch)->max(5)->allow(['laravel.com', 'php.net']),
(new WebSearch)->location(city: 'New York', region: 'NY', country: 'US');
```

#### Web 抓取

获取指定 URL 内容的工具。Anthropic 和 Gemini 支持。

```php theme={null}
use Laravel\Ai\Providers\Tools\WebFetch;

public function tools(): iterable
{
    return [new WebFetch];
}

(new WebFetch)->max(3)->allow(['docs.laravel.com']),
```

#### 文件检索

从向量库中搜索文档的工具。OpenAI 和 Gemini 支持。

```php theme={null}
use Laravel\Ai\Providers\Tools\FileSearch;

public function tools(): iterable
{
    return [new FileSearch(stores: ['store_id'])];
}

// 指定多个向量库
new FileSearch(stores: ['store_1', 'store_2']);

// 按元数据过滤
new FileSearch(stores: ['store_id'], where: ['author' => 'Taylor Otwell', 'year' => 2026]);
```

也可以使用 `FileSearchQuery` 指定更复杂的过滤条件。

```php theme={null}
use Laravel\Ai\Providers\Tools\FileSearchQuery;

new FileSearch(stores: ['store_id'], where: fn (FileSearchQuery $query) =>
    $query->where('author', 'Taylor Otwell')
          ->whereNot('status', 'draft')
          ->whereIn('category', ['news', 'updates'])
);
```

### 子智能体（Sub-agent）

智能体也可以从其他智能体的 `tools()` 方法中返回。把智能体注册为工具后，父智能体就能把特定任务委派给子智能体，并把结果整合到最终响应中。这在通用智能体需要访问具有专门指令、工具、模型配置或提供商配置的专用智能体时非常有用。

例如，可以让一个客服智能体把关于退款政策的问题委派给专门处理退款的智能体。

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

namespace App\Ai\Agents;

use Laravel\Ai\Contracts\Agent;
use Laravel\Ai\Contracts\HasTools;
use Laravel\Ai\Promptable;

class CustomerSupportAgent implements Agent, HasTools
{
    use Promptable;

    public function instructions(): string
    {
        return 'You help customers with account, order, and billing questions. Delegate refund policy questions to the refunds specialist.';
    }

    public function tools(): iterable
    {
        return [
            new RefundsAgent,
        ];
    }
}
```

如果需要自定义子智能体在父智能体眼中的呈现方式，可以让子智能体实现 `CanActAsTool` 接口，并为工具定义名称和描述。

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

namespace App\Ai\Agents;

use App\Ai\Tools\LookupOrder;
use Laravel\Ai\Attributes\Provider;
use Laravel\Ai\Contracts\Agent;
use Laravel\Ai\Contracts\CanActAsTool;
use Laravel\Ai\Contracts\HasTools;
use Laravel\Ai\Enums\Lab;
use Laravel\Ai\Promptable;

#[Provider(Lab::Anthropic)]
class RefundsAgent implements Agent, CanActAsTool, HasTools
{
    use Promptable;

    public function instructions(): string
    {
        return 'You are a refunds specialist. Use order details and the refund policy to give concise eligibility guidance.';
    }

    public function name(): string
    {
        return 'refunds_specialist';
    }

    public function description(): string
    {
        return 'Determine whether an order is eligible for a refund and explain the next step.';
    }

    public function tools(): iterable
    {
        return [
            new LookupOrder,
        ];
    }
}
```

对于未实现 `CanActAsTool` 的子智能体，Laravel 会以类名作为工具名，并自动生成一段通用描述。每次调用子智能体都是独立的，不会继承父智能体的会话历史。

### 中间件

你可以为智能体添加中间件，用来拦截提示词或响应。

```shell theme={null}
php artisan make:agent-middleware LogPrompts
```

在智能体上实现 `HasMiddleware` 接口，并在 `middleware()` 方法中注册中间件。

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

namespace App\Ai\Agents;

use App\Ai\Middleware\LogPrompts;
use Laravel\Ai\Contracts\Agent;
use Laravel\Ai\Contracts\HasMiddleware;
use Laravel\Ai\Promptable;

class SalesCoach implements Agent, HasMiddleware
{
    use Promptable;

    public function middleware(): array
    {
        return [new LogPrompts];
    }
}
```

下面是一个中间件类的实现示例。

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

namespace App\Ai\Middleware;

use Closure;
use Laravel\Ai\Prompts\AgentPrompt;

class LogPrompts
{
    public function handle(AgentPrompt $prompt, Closure $next)
    {
        Log::info('Prompting agent', ['prompt' => $prompt->prompt]);
        return $next($prompt);
    }
}
```

使用 `then()` 可以在响应之后追加处理逻辑。

```php theme={null}
public function handle(AgentPrompt $prompt, Closure $next)
{
    return $next($prompt)->then(function (AgentResponse $response) {
        Log::info('Agent responded', ['text' => $response->text]);
    });
}
```

### 匿名智能体

如果不想定义类，可以使用 `agent()` 辅助函数创建匿名智能体。

```php theme={null}
use function Laravel\Ai\{agent};

$response = agent(
    instructions: 'You are an expert at software development.',
    messages: [],
    tools: [],
)->prompt('Tell me about Laravel');
```

也可以创建带结构化输出的匿名智能体。

```php theme={null}
use Illuminate\Contracts\JsonSchema\JsonSchema;

$response = agent(
    schema: fn (JsonSchema $schema) => ['number' => $schema->integer()->required()],
)->prompt('Generate a random number less than 100');
```

### 智能体配置（PHP Attribute）

可以使用 PHP Attribute 以声明式的方式为智能体设置默认值。

| Attribute             | 说明             |
| --------------------- | -------------- |
| `#[Provider]`         | 指定使用的提供商       |
| `#[Model]`            | 指定使用的模型        |
| `#[MaxSteps]`         | 工具调用的最大步数      |
| `#[MaxTokens]`        | 最大 token 数     |
| `#[Temperature]`      | 温度参数（0.0～1.0）  |
| `#[TopP]`             | 核采样概率（0.0～1.0） |
| `#[Timeout]`          | 超时时间（秒）        |
| `#[UseCheapestModel]` | 自动选择最便宜的模型     |
| `#[UseSmartestModel]` | 自动选择性能最强的模型    |

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

namespace App\Ai\Agents;

use Laravel\Ai\Attributes\MaxSteps;
use Laravel\Ai\Attributes\MaxTokens;
use Laravel\Ai\Attributes\Model;
use Laravel\Ai\Attributes\Provider;
use Laravel\Ai\Attributes\Temperature;
use Laravel\Ai\Attributes\Timeout;
use Laravel\Ai\Attributes\TopP;
use Laravel\Ai\Contracts\Agent;
use Laravel\Ai\Enums\Lab;
use Laravel\Ai\Promptable;

#[Provider(Lab::Anthropic)]
#[Model('claude-haiku-4-5-20251001')]
#[MaxSteps(10)]
#[MaxTokens(4096)]
#[Temperature(0.7)]
#[Timeout(120)]
#[TopP(0.9)]
class SalesCoach implements Agent
{
    use Promptable;
}
```

此外还提供了模型选择的快捷 Attribute。

```php theme={null}
// 使用最便宜的模型
#[UseCheapestModel]
class SimpleSummarizer implements Agent
{
    use Promptable;
}

// 使用性能最强的模型
#[UseSmartestModel]
class ComplexReasoner implements Agent
{
    use Promptable;
}
```

### 提供商选项

实现 `HasProviderOptions` 接口后，可以传入提供商特有的选项。

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

namespace App\Ai\Agents;

use Laravel\Ai\Contracts\Agent;
use Laravel\Ai\Contracts\HasProviderOptions;
use Laravel\Ai\Enums\Lab;
use Laravel\Ai\Promptable;

class SalesCoach implements Agent, HasProviderOptions
{
    use Promptable;

    public function providerOptions(Lab|string $provider): array
    {
        return match ($provider) {
            Lab::OpenAI => [
                'reasoning' => ['effort' => 'low'],
                'frequency_penalty' => 0.5,
                'presence_penalty' => 0.3,
            ],
            Lab::Anthropic => [
                'thinking' => ['budget_tokens' => 1024],
            ],
            default => [],
        };
    }
}
```

***

## 图像生成

使用 `Image` 类可以生成图像。OpenAI、Gemini、xAI 提供商均支持。

```php theme={null}
use Laravel\Ai\Image;

$image = Image::of('A donut sitting on the kitchen counter')->generate();
$rawContent = (string) $image;
```

可以指定画质、宽高比和超时时间。

```php theme={null}
$image = Image::of('A donut sitting on the kitchen counter')
    ->quality('high')
    ->landscape()
    ->timeout(120)
    ->generate();
```

也可以附加参考图像进行加工。

```php theme={null}
use Laravel\Ai\Files;

$image = Image::of('Update this photo to be in the style of an impressionist painting.')
    ->attachments([Files\Image::fromStorage('photo.jpg')])
    ->landscape()
    ->generate();
```

### 保存图像

```php theme={null}
$path = $image->store();
$path = $image->storeAs('image.jpg');
$path = $image->storePublicly();
$path = $image->storePubliclyAs('image.jpg');
```

### 通过队列生成图像

```php theme={null}
use Laravel\Ai\Responses\ImageResponse;

Image::of('A donut sitting on the kitchen counter')
    ->portrait()
    ->queue()
    ->then(function (ImageResponse $image) {
        $path = $image->store();
    });
```

***

## 语音合成（TTS）

使用 `Audio` 类可以把文本转换为语音。OpenAI 和 ElevenLabs 提供商支持。

```php theme={null}
use Laravel\Ai\Audio;

$audio = Audio::of('I love coding with Laravel.')->generate();
$rawContent = (string) $audio;
```

可以指定声音性别、具体的 voice ID，或者朗读的风格提示。

```php theme={null}
$audio = Audio::of('I love coding with Laravel.')->female()->generate();
$audio = Audio::of('I love coding with Laravel.')->voice('voice-id-or-name')->generate();
$audio = Audio::of('I love coding with Laravel.')->female()->instructions('Said like a pirate')->generate();
```

### 保存音频

```php theme={null}
$path = $audio->store();
$path = $audio->storeAs('audio.mp3');
$path = $audio->storePublicly();
$path = $audio->storePubliclyAs('audio.mp3');
```

### 通过队列生成音频

```php theme={null}
use Laravel\Ai\Responses\AudioResponse;

Audio::of('I love coding with Laravel.')
    ->queue()
    ->then(function (AudioResponse $audio) {
        $path = $audio->store();
    });
```

***

## 语音识别（STT）

使用 `Transcription` 类可以把音频文件转换为文本。OpenAI、ElevenLabs 和 Mistral 提供商支持。

```php theme={null}
use Laravel\Ai\Transcription;

$transcript = Transcription::fromPath('/home/laravel/audio.mp3')->generate();
$transcript = Transcription::fromStorage('audio.mp3')->generate();
$transcript = Transcription::fromUpload($request->file('audio'))->generate();

return (string) $transcript;
```

### 说话人分离（Diarization）

使用 `diarize()` 可以得到按说话人分离的转写结果。

```php theme={null}
$transcript = Transcription::fromStorage('audio.mp3')->diarize()->generate();
```

### 通过队列进行语音识别

```php theme={null}
use Laravel\Ai\Responses\TranscriptionResponse;

Transcription::fromStorage('audio.mp3')
    ->queue()
    ->then(function (TranscriptionResponse $transcript) { /* ... */ });
```

***

## 嵌入向量（Embeddings）

可以把文本转换成向量表示，用于相似度搜索等场景。

```php theme={null}
use Illuminate\Support\Str;

// 通过 Stringable 使用
$embeddings = Str::of('Napa Valley has great wine.')->toEmbeddings();
```

```php theme={null}
use Laravel\Ai\Embeddings;

// 批量处理多个文本
$response = Embeddings::for(['Napa Valley has great wine.', 'Laravel is a PHP framework.'])->generate();
$response->embeddings; // [[0.123, 0.456, ...], [0.789, 0.012, ...]]
```

也可以指定提供商、模型和维度。

```php theme={null}
$response = Embeddings::for(['Napa Valley has great wine.'])
    ->dimensions(1536)
    ->generate(Lab::OpenAI, 'text-embedding-3-small');
```

### 向量检索（pgvector）

下面是使用 PostgreSQL 和 pgvector 扩展做向量检索的配置示例。

<Steps>
  <Step title="创建迁移">
    ```php theme={null}
    Schema::ensureVectorExtensionExists();

    Schema::create('documents', function (Blueprint $table) {
        $table->id();
        $table->string('title');
        $table->text('content');
        $table->vector('embedding', dimensions: 1536);
        $table->timestamps();
    });

    // 添加 HNSW 索引
    $table->vector('embedding', dimensions: 1536)->index();
    ```
  </Step>

  <Step title="配置模型">
    ```php theme={null}
    protected function casts(): array
    {
        return ['embedding' => 'array'];
    }
    ```
  </Step>

  <Step title="相似度查询">
    ```php theme={null}
    // 使用嵌入向量搜索
    $documents = Document::query()
        ->whereVectorSimilarTo('embedding', $queryEmbedding, minSimilarity: 0.4)
        ->limit(10)
        ->get();

    // 传入字符串会自动生成嵌入向量
    $documents = Document::query()
        ->whereVectorSimilarTo('embedding', 'best wineries in Napa Valley')
        ->limit(10)
        ->get();
    ```
  </Step>
</Steps>

也可以使用更底层的方法。

```php theme={null}
$documents = Document::query()
    ->select('*')
    ->selectVectorDistance('embedding', $queryEmbedding, as: 'distance')
    ->whereVectorDistanceLessThan('embedding', $queryEmbedding, maxDistance: 0.3)
    ->orderByVectorDistance('embedding', $queryEmbedding)
    ->limit(10)
    ->get();
```

### 缓存嵌入向量

可以对相同文本的嵌入向量进行缓存，避免重复生成。

在 `config/ai.php` 中设置默认的缓存行为。

```php theme={null}
'caching' => [
    'embeddings' => [
        'cache' => true,
        'store' => env('CACHE_STORE', 'database'),
    ],
],
```

也可以在每次请求时控制缓存。

```php theme={null}
$response = Embeddings::for(['Napa Valley has great wine.'])->cache()->generate();
$response = Embeddings::for(['Napa Valley has great wine.'])->cache(seconds: 3600)->generate();

// Stringable 也一样可用
$embeddings = Str::of('Napa Valley has great wine.')->toEmbeddings(cache: true);
$embeddings = Str::of('Napa Valley has great wine.')->toEmbeddings(cache: 3600);
```

***

## 重排序（Reranking）

可以按照与查询的相关度对检索结果重新排序。Cohere 和 Jina 提供商支持。

```php theme={null}
use Laravel\Ai\Reranking;

$response = Reranking::of([
    'Django is a Python web framework.',
    'Laravel is a PHP web application framework.',
    'React is a JavaScript library for building user interfaces.',
])->rerank('PHP frameworks');

$response->first()->document; // "Laravel is a PHP web application framework."
$response->first()->score;    // 0.95
$response->first()->index;    // 1
```

使用 `limit()` 可以限制返回数量。

```php theme={null}
$response = Reranking::of($documents)->limit(5)->rerank('search query');
```

### 对集合进行重排序

可以直接对 Eloquent 集合进行重排序。

```php theme={null}
// 单一字段
$posts = Post::all()->rerank('body', 'Laravel tutorials');

// 多个字段（会以 JSON 发送）
$reranked = $posts->rerank(['title', 'body'], 'Laravel tutorials');

// 使用闭包自定义生成的文本
$reranked = $posts->rerank(fn ($post) => $post->title.': '.$post->body, 'Laravel tutorials');

// 带选项
$reranked = $posts->rerank(
    by: 'content',
    query: 'Laravel tutorials',
    limit: 10,
    provider: Lab::Cohere,
);
```

***

## 文件管理

可以把文件上传到 AI 提供商，之后再引用它们。

```php theme={null}
use Laravel\Ai\Files\Document;
use Laravel\Ai\Files\Image;

// 从路径
$response = Document::fromPath('/home/laravel/document.pdf')->put();
$response = Image::fromPath('/home/laravel/photo.jpg')->put();

// 从存储
$response = Document::fromStorage('document.pdf', disk: 'local')->put();
$response = Image::fromStorage('photo.jpg', disk: 'local')->put();

// 从 URL
$response = Document::fromUrl('https://example.com/document.pdf')->put();
$response = Image::fromUrl('https://example.com/photo.jpg')->put();

return $response->id;
```

也可以从字符串或表单上传创建文件。

```php theme={null}
$stored = Document::fromString('Hello, World!', 'text/plain')->put();
$stored = Document::fromUpload($request->file('document'))->put();
```

### 引用已保存的文件

用上传得到的文件 ID 就可以把文件附加到智能体上。

```php theme={null}
use Laravel\Ai\Files;

$response = (new SalesCoach)->prompt(
    'Analyze the attached sales transcript...',
    attachments: [Files\Document::fromId('file-id')]
);
```

### 获取与删除文件

```php theme={null}
// 获取
$file = Document::fromId('file-id')->get();
$file->id;
$file->mimeType();

// 删除
Document::fromId('file-id')->delete();
```

### 指定提供商

```php theme={null}
$response = Document::fromPath('/home/laravel/document.pdf')->put(provider: Lab::Anthropic);
```

### 指定提供商专属选项

`withProviderOptions` 方法允许你传入提供商特有的上传选项。例如可以设置 OpenAI 文件的 `purpose`。

```php theme={null}
use Laravel\Ai\Files\Document;

$response = Document::fromPath('/home/laravel/knowledge.txt')
    ->withProviderOptions(['purpose' => 'assistants'])
    ->put();
```

如果要按提供商指定不同的选项，可以传入一个闭包。

```php theme={null}
use Laravel\Ai\Enums\Lab;
use Laravel\Ai\Files\Document;

$response = Document::fromPath('/home/laravel/training.jsonl')
    ->withProviderOptions(fn (Lab|string $provider) => match ($provider) {
        Lab::OpenAI => ['purpose' => 'fine-tune'],
        default => [],
    })
    ->put();
```

***

## 向量库（Vector Stores）

向量库允许你在提供商侧管理文档。

```php theme={null}
use Laravel\Ai\Stores;

// 创建
$store = Stores::create('Knowledge Base');
$store = Stores::create(
    name: 'Knowledge Base',
    description: 'Documentation.',
    expiresWhenIdleFor: days(30),
);
return $store->id;

// 获取
$store = Stores::get('store_id');
$store->id;
$store->name;
$store->fileCounts;
$store->ready;

// 删除
Stores::delete('store_id');
$store->delete();
```

### 向向量库添加文件

```php theme={null}
$store = Stores::get('store_id');

// 支持多种方式添加文件
$document = $store->add('file_id');
$document = $store->add(Document::fromId('file_id'));
$document = $store->add(Document::fromPath('/path/to/document.pdf'));
$document = $store->add(Document::fromStorage('manual.pdf'));
$document = $store->add($request->file('document'));

$document->id;
$document->fileId;
```

也可以附加元数据。

```php theme={null}
$store->add(
    Document::fromPath('/path/to/document.pdf'),
    metadata: [
        'author' => 'Taylor Otwell',
        'department' => 'Engineering',
        'year' => 2026,
    ]
);
```

### 从向量库中移除文件

```php theme={null}
$store->remove('file_id');

// 同时删除文件本身
$store->remove('file_abc123', deleteFile: true);
```

***

## 故障切换（Failover）

以数组方式指定多个提供商时，如果第一个提供商失败，会自动切换到下一个。

```php theme={null}
use App\Ai\Agents\SalesCoach;
use Laravel\Ai\Image;

// 智能体的故障切换
$response = (new SalesCoach)->prompt(
    'Analyze this sales transcript...',
    provider: [Lab::OpenAI, Lab::Anthropic],
);

// 图像生成的故障切换
$image = Image::of('A donut sitting on the kitchen counter')
    ->generate(provider: [Lab::Gemini, Lab::xAI]);
```

***

## 测试

Laravel AI SDK 提供了测试用的 fake 能力，可以在不调用真实 API 的情况下进行测试。

### 智能体的测试

```php theme={null}
use App\Ai\Agents\SalesCoach;
use Laravel\Ai\Prompts\AgentPrompt;

// 固定响应的 fake
SalesCoach::fake();
SalesCoach::fake(['First response', 'Second response']);

// 使用闭包动态返回响应
SalesCoach::fake(function (AgentPrompt $prompt) {
    return 'Response for: '.$prompt->prompt;
});

// 断言
SalesCoach::assertPrompted('Analyze this...');
SalesCoach::assertPrompted(function (AgentPrompt $prompt) {
    return $prompt->contains('Analyze');
});
SalesCoach::assertNotPrompted('Missing prompt');
SalesCoach::assertNeverPrompted();
```

针对队列也提供了断言。

```php theme={null}
use Laravel\Ai\QueuedAgentPrompt;

SalesCoach::assertQueued('Analyze this...');
SalesCoach::assertQueued(function (QueuedAgentPrompt $prompt) {
    return $prompt->contains('Analyze');
});
SalesCoach::assertNotQueued('Missing prompt');
SalesCoach::assertNeverQueued();
```

使用 `preventStrayPrompts()` 后，如果调用了 fake 中未定义的提示词，就会抛出异常。

```php theme={null}
SalesCoach::fake()->preventStrayPrompts();
```

对于返回结构化输出的智能体，可以用数组指定响应内容。智能体会返回包含指定数据的结构化响应。

```php theme={null}
SalesCoach::fake([
    ['score' => 87],
]);
```

<Info>
  如果对结构化输出的智能体调用 `fake()` 时没有显式传入 fake 数据，Laravel 会根据智能体定义的 schema 自动生成符合结构的 fake 数据。
</Info>

匿名智能体的测试可以使用 `AnonymousAgent::fake()`。

```php theme={null}
use Laravel\Ai\AnonymousAgent;

AnonymousAgent::fake(['Test response']);
```

### 图像生成的测试

```php theme={null}
use Laravel\Ai\Image;
use Laravel\Ai\Prompts\ImagePrompt;

Image::fake();
Image::fake([base64_encode($firstImage), base64_encode($secondImage)]);
Image::fake(function (ImagePrompt $prompt) {
    return base64_encode('...');
});

Image::assertGenerated(function (ImagePrompt $prompt) {
    return $prompt->contains('sunset') && $prompt->isLandscape();
});
Image::assertNotGenerated('Missing prompt');
Image::assertNothingGenerated();

Image::assertQueued(fn (QueuedImagePrompt $prompt) => $prompt->contains('sunset'));
Image::assertNotQueued('Missing prompt');
Image::assertNothingQueued();

Image::fake()->preventStrayImages();
```

### 语音合成的测试

```php theme={null}
use Laravel\Ai\Audio;
use Laravel\Ai\Prompts\AudioPrompt;

Audio::fake();
Audio::fake([base64_encode($firstAudio), base64_encode($secondAudio)]);
Audio::fake(function (AudioPrompt $prompt) {
    return base64_encode('...');
});

Audio::assertGenerated(function (AudioPrompt $prompt) {
    return $prompt->contains('Hello') && $prompt->isFemale();
});
Audio::assertNotGenerated('Missing prompt');
Audio::assertNothingGenerated();

Audio::assertQueued(fn (QueuedAudioPrompt $prompt) => $prompt->contains('Hello'));
Audio::assertNotQueued('Missing prompt');
Audio::assertNothingQueued();

Audio::fake()->preventStrayAudio();
```

### 语音识别的测试

```php theme={null}
use Laravel\Ai\Transcription;
use Laravel\Ai\Prompts\TranscriptionPrompt;

Transcription::fake();
Transcription::fake(['First transcription text.', 'Second transcription text.']);
Transcription::fake(function (TranscriptionPrompt $prompt) {
    return 'Transcribed text...';
});

Transcription::assertGenerated(function (TranscriptionPrompt $prompt) {
    return $prompt->language === 'en' && $prompt->isDiarized();
});
Transcription::assertNotGenerated(fn (TranscriptionPrompt $prompt) => $prompt->language === 'fr');
Transcription::assertNothingGenerated();

Transcription::assertQueued(fn (QueuedTranscriptionPrompt $prompt) => $prompt->isDiarized());
Transcription::assertNotQueued(fn (QueuedTranscriptionPrompt $prompt) => $prompt->language === 'fr');
Transcription::assertNothingQueued();

Transcription::fake()->preventStrayTranscriptions();
```

### 嵌入向量的测试

```php theme={null}
use Laravel\Ai\Embeddings;
use Laravel\Ai\Prompts\EmbeddingsPrompt;

Embeddings::fake();
Embeddings::fake([[$firstEmbeddingVector], [$secondEmbeddingVector]]);
Embeddings::fake(function (EmbeddingsPrompt $prompt) {
    return array_map(
        fn () => Embeddings::fakeEmbedding($prompt->dimensions),
        $prompt->inputs
    );
});

Embeddings::assertGenerated(function (EmbeddingsPrompt $prompt) {
    return $prompt->contains('Laravel') && $prompt->dimensions === 1536;
});
Embeddings::assertNotGenerated(fn (EmbeddingsPrompt $prompt) => $prompt->contains('Other'));
Embeddings::assertNothingGenerated();

Embeddings::assertQueued(fn (QueuedEmbeddingsPrompt $prompt) => $prompt->contains('Laravel'));
Embeddings::assertNotQueued(fn (QueuedEmbeddingsPrompt $prompt) => $prompt->contains('Other'));
Embeddings::assertNothingQueued();

Embeddings::fake()->preventStrayEmbeddings();
```

### 重排序的测试

```php theme={null}
use Laravel\Ai\Reranking;
use Laravel\Ai\Prompts\RerankingPrompt;
use Laravel\Ai\Responses\Data\RankedDocument;

Reranking::fake();
Reranking::fake([[
    new RankedDocument(index: 0, document: 'First', score: 0.95),
    new RankedDocument(index: 1, document: 'Second', score: 0.80),
]]);

Reranking::assertReranked(function (RerankingPrompt $prompt) {
    return $prompt->contains('Laravel') && $prompt->limit === 5;
});
Reranking::assertNotReranked(fn (RerankingPrompt $prompt) => $prompt->contains('Django'));
Reranking::assertNothingReranked();
```

### 文件的测试

```php theme={null}
use Laravel\Ai\Files;
use Laravel\Ai\Contracts\Files\StorableFile;
use Laravel\Ai\Files\Document;

Files::fake();

Document::fromString('Hello, Laravel!', mimeType: 'text/plain')->as('hello.txt')->put();

Files::assertStored(fn (StorableFile $file) =>
    (string) $file === 'Hello, Laravel!' && $file->mimeType() === 'text/plain'
);
Files::assertNotStored(fn (StorableFile $file) => (string) $file === 'Hello, World!');
Files::assertNothingStored();

Files::assertDeleted('file-id');
Files::assertNotDeleted('file-id');
Files::assertNothingDeleted();
```

### 向量库的测试

```php theme={null}
use Laravel\Ai\Stores;

Stores::fake(); // 文件操作也会被 fake

$store = Stores::create('Knowledge Base');

Stores::assertCreated('Knowledge Base');
Stores::assertCreated(fn (string $name, ?string $description) => $name === 'Knowledge Base');
Stores::assertNotCreated('Other Store');
Stores::assertNothingCreated();

Stores::assertDeleted('store_id');
Stores::assertNotDeleted('other_store_id');
Stores::assertNothingDeleted();
```

也可以对向量库上的文件操作进行断言。

```php theme={null}
$store = Stores::get('store_id');
$store->add('added_id');
$store->remove('removed_id');

$store->assertAdded('added_id');
$store->assertRemoved('removed_id');
$store->assertNotAdded('other_file_id');
$store->assertNotRemoved('other_file_id');

// 通过闭包验证内容
$store->add(Document::fromString('Hello, World!', 'text/plain')->as('hello.txt'));
$store->assertAdded(fn (StorableFile $file) => $file->name() === 'hello.txt');
$store->assertAdded(fn (StorableFile $file) => $file->content() === 'Hello, World!');
```

***

## 事件

Laravel AI SDK 会派发以下事件。你可以监听这些事件用于日志记录、监控等场景。

<AccordionGroup>
  <Accordion title="智能体相关">
    * `PromptingAgent` — 发送提示词之前
    * `AgentPrompted` — 发送提示词之后
    * `StreamingAgent` — 开始流式响应时
    * `AgentStreamed` — 流式响应完成后
    * `InvokingTool` — 工具调用之前
    * `ToolInvoked` — 工具调用之后
  </Accordion>

  <Accordion title="图像、音频、语音识别相关">
    * `GeneratingImage` — 生成图像之前
    * `ImageGenerated` — 生成图像之后
    * `GeneratingAudio` — 生成音频之前
    * `AudioGenerated` — 生成音频之后
    * `GeneratingTranscription` — 语音识别之前
    * `TranscriptionGenerated` — 语音识别之后
  </Accordion>

  <Accordion title="嵌入与重排序相关">
    * `GeneratingEmbeddings` — 生成嵌入向量之前
    * `EmbeddingsGenerated` — 生成嵌入向量之后
    * `Reranking` — 重排序之前
    * `Reranked` — 重排序之后
  </Accordion>

  <Accordion title="文件与向量库相关">
    * `StoringFile` — 保存文件之前
    * `FileStored` — 保存文件之后
    * `FileDeleted` — 删除文件之后
    * `CreatingStore` — 创建向量库之前
    * `StoreCreated` — 创建向量库之后
    * `AddingFileToStore` — 向量库中新增文件之前
    * `FileAddedToStore` — 向量库中新增文件之后
    * `RemovingFileFromStore` — 从向量库移除文件之前
    * `FileRemovedFromStore` — 从向量库移除文件之后
  </Accordion>
</AccordionGroup>


## Related topics

- [Laravel 13 新功能汇总](/zh/blog/laravel-13-new-features.md)
- [2026 年 3 月 Laravel 更新](/zh/blog/changelog/202603.md)
- [Laravel AI SDK 集成](/zh/packages/laravel-copilot-sdk/ai-sdk.md)
- [Laravel AI SDK 集成 - VOICEVOX for Laravel](/zh/packages/laravel-voicevox/ai-sdk.md)
- [用于 Laravel AI SDK 的 Amazon Bedrock 驱动](/zh/packages/laravel-amazon-bedrock.md)
