Skip to content
虚位以待
虚位以待
虚位以待
虚位以待
虚位以待
虚位以待
虚位以待
虚位以待
虚位以待
虚位以待
虚位以待
虚位以待
虚位以待
虚位以待
虚位以待
虚位以待
虚位以待
虚位以待

Laravel AI SDK

简介

Laravel AI SDK 提供了一个统一、富有表现力的 API,用于与 OpenAI、Anthropic、Gemini 等 AI 提供商进行交互。借助 AI SDK,你可以使用工具和结构化输出构建智能智能体,生成图像,合成和转录音频,创建向量嵌入等等——所有这些都通过一致、Laravel 友好的接口完成。

安装

你可以通过 Composer 安装 Laravel AI SDK:

shell
composer require laravel/ai

接下来,你应该使用 vendor:publish Artisan 命令发布 AI SDK 的配置和迁移文件:

shell
php artisan vendor:publish --provider="Laravel\Ai\AiServiceProvider"

最后,你应该运行应用的数据库迁移。这将创建 agent_conversationsagent_conversation_messages 表,AI SDK 使用这些表来支持其对话存储功能:

shell
php artisan migrate

配置

你可以在应用的 config/ai.php 配置文件中定义 AI 提供商凭证,或者作为环境变量定义在应用的 .env 文件中:

ini
ANTHROPIC_API_KEY=
COHERE_API_KEY=
ELEVENLABS_API_KEY=
GEMINI_API_KEY=
MISTRAL_API_KEY=
OLLAMA_API_KEY=
OPENAI_API_KEY=
JINA_API_KEY=
VOYAGEAI_API_KEY=
XAI_API_KEY=

用于文本、图像、音频、转录和嵌入的默认模型也可以在应用的 config/ai.php 配置文件中进行配置。

自定义基础 URL

默认情况下,Laravel AI SDK 直接连接到每个提供商的公共 API 端点。但是,你可能需要将请求路由到不同的端点——例如,当使用代理服务集中管理 API 密钥、实施速率限制或通过企业网关路由流量时。

你可以通过向提供商配置添加 url 参数来配置自定义基础 URL:

php
'providers' => [
    'openai' => [
        'driver' => 'openai',
        'key' => env('OPENAI_API_KEY'),
        'url' => env('OPENAI_BASE_URL'),
    ],

    'anthropic' => [
        'driver' => 'anthropic',
        'key' => env('ANTHROPIC_API_KEY'),
        'url' => env('ANTHROPIC_BASE_URL'),
    ],
],

这在需要通过代理服务(如 LiteLLM 或 Azure OpenAI Gateway)或使用替代端点路由请求时非常有用。

以下提供商支持自定义基础 URL:OpenAI、Anthropic、Gemini、Groq、Cohere、DeepSeek、xAI 和 OpenRouter。

提供商支持

AI SDK 在其各项功能中支持多种提供商。下表总结了每个功能可用的提供商:

功能提供商
文本OpenAI, Anthropic, Gemini, Azure, Groq, xAI, DeepSeek, Mistral, Ollama
图像OpenAI, Gemini, xAI
TTSOpenAI, ElevenLabs
STTOpenAI, ElevenLabs, Mistral
嵌入OpenAI, Gemini, Azure, Cohere, Mistral, Jina, VoyageAI
重排序Cohere, Jina
文件OpenAI, Anthropic, Gemini

Laravel\Ai\Enums\Lab 枚举可用于在整个代码中引用提供商,而不是使用纯字符串:

php
use Laravel\Ai\Enums\Lab;

Lab::Anthropic;
Lab::OpenAI;
Lab::Gemini;
// ...

智能体

智能体是与 Laravel AI SDK 中 AI 提供商交互的基本构建块。每个智能体都是一个专用的 PHP 类,封装了与大型语言模型交互所需的指令、对话上下文、工具和输出模式。可以把智能体想象成一个专门的助手——销售教练、文档分析器、支持机器人——你只需配置一次,然后在应用程序中根据需要提示它。

你可以通过 make:agent Artisan 命令创建智能体:

shell
php artisan make:agent SalesCoach

php artisan make:agent SalesCoach --structured

在生成的智能体类中,你可以定义系统提示/指令、消息上下文、可用工具和输出模式(如果适用):

php
<?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 '你是一名销售教练,负责分析对话记录并提供反馈和整体销售实力评分。';
    }

    /**
     * 获取构成到目前为止对话的消息列表。
     */
    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();
    }

    /**
     * 获取智能体可用的工具。
     *
     * @return Tool[]
     */
    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(),
        ];
    }
}

提示

要提示智能体,首先使用 make 方法或标准实例化创建一个实例,然后调用 prompt

php
$response = (new SalesCoach)
    ->prompt('分析这段销售对话记录...');

return (string) $response;

make 方法从容器中解析你的智能体,允许自动依赖注入。你也可以向智能体的构造函数传递参数:

php
$agent = SalesCoach::make(user: $user);

通过向 prompt 方法传递额外参数,你可以在提示时覆盖默认的提供商、模型或 HTTP 超时:

php
$response = (new SalesCoach)->prompt(
    '分析这段销售对话记录...',
    provider: Lab::Anthropic,
    model: 'claude-haiku-4-5-20251001',
    timeout: 120,
);

对话上下文

如果你的智能体实现了 Conversational 接口,你可以使用 messages 方法返回之前的对话上下文(如果适用):

php
use App\Models\History;
use Laravel\Ai\Messages\Message;

/**
 * 获取构成到目前为止对话的消息列表。
 */
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();
}

记住对话

NOTE

在使用 RemembersConversations trait 之前,你应该使用 vendor:publish Artisan 命令发布并运行 AI SDK 的迁移。这些迁移将创建必要的数据库表来存储对话。

如果你希望 Laravel 自动为你的智能体存储和检索对话历史,你可以使用 RemembersConversations trait。这个 trait 提供了一种简单的方法来将对话消息持久化到数据库,而无需手动实现 Conversational 接口:

php
<?php

namespace App\Ai\Agents;

use Laravel\Ai\Concerns\RemembersConversations;
use Laravel\Ai\Contracts\Agent;
use Laravel\Ai\Contracts\Conversational;
use Laravel\Ai\Promptable;

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

    /**
     * 获取智能体应遵循的指令。
     */
    public function instructions(): string
    {
        return '你是一名销售教练...';
    }
}

要为某个用户开始一个新对话,在提示之前调用 forUser 方法:

php
$response = (new SalesCoach)->forUser($user)->prompt('你好!');

$conversationId = $response->conversationId;

对话 ID 会在响应中返回,可以存储起来以备将来参考,或者你也可以直接从 agent_conversations 表中检索用户的所有对话。

要继续现有对话,使用 continue 方法:

php
$response = (new SalesCoach)
    ->continue($conversationId, as: $user)
    ->prompt('再多告诉我一些。');

当使用 RemembersConversations trait 时,之前的消息会在提示时自动加载并包含在对话上下文中。每次交互后,新消息(用户和助手的)都会自动存储。

结构化输出

如果你希望智能体返回结构化输出,请实现 HasStructuredOutput 接口,这要求你的智能体定义一个 schema 方法:

php
<?php

namespace App\Ai\Agents;

use Illuminate\Contracts\JsonSchema\JsonSchema;
use Laravel\Ai\Contracts\Agent;
use Laravel\Ai\Contracts\HasStructuredOutput;
use Laravel\Ai\Promptable;

class SalesCoach implements Agent, HasStructuredOutput
{
    use Promptable;

    // ...

    /**
     * 获取智能体的结构化输出模式定义。
     */
    public function schema(JsonSchema $schema): array
    {
        return [
            'score' => $schema->integer()->required(),
        ];
    }
}

当提示一个返回结构化输出的智能体时,你可以像访问数组一样访问返回的 StructuredAgentResponse

php
$response = (new SalesCoach)->prompt('分析这段销售对话记录...');

return $response['score'];

附件

在提示时,你还可以随提示传递附件,以便模型检查图像和文档:

php
use App\Ai\Agents\SalesCoach;
use Laravel\Ai\Files;

$response = (new SalesCoach)->prompt(
    '分析附带的销售对话记录...',
    attachments: [
        Files\Document::fromStorage('transcript.pdf') // 从文件系统磁盘附加文档...
        Files\Document::fromPath('/home/laravel/transcript.md') // 从本地路径附加文档...
        $request->file('transcript'), // 附加上传的文件...
    ]
);

同样,Laravel\Ai\Files\Image 类可用于将图像附加到提示:

php
use App\Ai\Agents\ImageAnalyzer;
use Laravel\Ai\Files;

$response = (new ImageAnalyzer)->prompt(
    '这张图片里有什么?',
    attachments: [
        Files\Image::fromStorage('photo.jpg') // 从文件系统磁盘附加图像...
        Files\Image::fromPath('/home/laravel/photo.jpg') // 从本地路径附加图像...
        $request->file('photo'), // 附加上传的文件...
    ]
);

流式

你可以通过调用 stream 方法来流式传输智能体的响应。返回的 StreamableAgentResponse 可以从路由返回,以自动向客户端发送流式响应(SSE):

php
use App\Ai\Agents\SalesCoach;

Route::get('/coach', function () {
    return (new SalesCoach)->stream('分析这段销售对话记录...');
});

then 方法可用于提供一个闭包,该闭包将在整个响应流式传输到客户端后被调用:

php
use App\Ai\Agents\SalesCoach;
use Laravel\Ai\Responses\StreamedAgentResponse;

Route::get('/coach', function () {
    return (new SalesCoach)
        ->stream('分析这段销售对话记录...')
        ->then(function (StreamedAgentResponse $response) {
            // $response->text, $response->events, $response->usage...
        });
});

或者,你可以手动遍历流式事件:

php
$stream = (new SalesCoach)->stream('分析这段销售对话记录...');

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

使用 Vercel AI SDK 协议进行流式传输

你可以通过调用流式响应上的 usingVercelDataProtocol 方法,使用 Vercel AI SDK 流协议 流式传输事件:

php
use App\Ai\Agents\SalesCoach;

Route::get('/coach', function () {
    return (new SalesCoach)
        ->stream('分析这段销售对话记录...')
        ->usingVercelDataProtocol();
});

广播

你可以通过几种不同的方式广播流式事件。首先,你可以简单地在流式事件上调用 broadcastbroadcastNow 方法:

php
use App\Ai\Agents\SalesCoach;
use Illuminate\Broadcasting\Channel;

$stream = (new SalesCoach)->stream('分析这段销售对话记录...');

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

或者,你可以调用智能体的 broadcastOnQueue 方法,将智能体操作排队,并在事件可用时广播它们:

php
(new SalesCoach)->broadcastOnQueue(
    '分析这段销售对话记录...'
    new Channel('channel-name'),
);

队列

使用智能体的 queue 方法,你可以提示智能体,但允许它在后台处理响应,从而使你的应用程序保持快速和响应。thencatch 方法可用于注册闭包,这些闭包将在响应可用或发生异常时被调用:

php
use Illuminate\Http\Request;
use Laravel\Ai\Responses\AgentResponse;
use Throwable;

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

    return back();
});

工具

工具可用于为智能体提供额外的功能,以便它们在响应提示时可以利用这些功能。可以使用 make:tool Artisan 命令创建工具:

shell
php artisan make:tool RandomNumberGenerator

生成的工具将放在你的应用程序的 app/Ai/Tools 目录中。每个工具都包含一个 handle 方法,当智能体需要使用该工具时,将调用该方法:

php
<?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 '此工具可用于生成加密安全的随机数。';
    }

    /**
     * 执行工具。
     */
    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
use App\Ai\Tools\RandomNumberGenerator;

/**
 * 获取智能体可用的工具。
 *
 * @return Tool[]
 */
public function tools(): iterable
{
    return [
        new RandomNumberGenerator,
    ];
}

相似性搜索

SimilaritySearch 工具允许智能体使用存储在数据库中的向量嵌入来搜索与给定查询相似的文档。这对于检索增强生成(RAG)非常有用,当你想让智能体访问搜索应用程序的数据时。

创建相似性搜索工具的最简单方法是使用带有具有向量嵌入的 Eloquent 模型的 usingModel 方法:

php
use App\Models\Document;
use Laravel\Ai\Tools\SimilaritySearch;

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

第一个参数是 Eloquent 模型类,第二个参数是包含向量嵌入的列。

你还可以提供介于 0.01.0 之间的最小相似度阈值,以及一个用于自定义查询的闭包:

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

为了获得更多控制,你可以使用返回搜索结果的自定义闭包来创建相似性搜索工具:

php
use App\Models\Document;
use Laravel\Ai\Tools\SimilaritySearch;

public function tools(): iterable
{
    return [
        new SimilaritySearch(using: function (string $query) {
            return Document::query()
                ->where('user_id', $this->user->id)
                ->whereVectorSimilarTo('embedding', $query)
                ->limit(10)
                ->get();
        }),
    ];
}

你可以使用 withDescription 方法自定义工具的描述:

php
SimilaritySearch::usingModel(Document::class, 'embedding')
    ->withDescription('搜索知识库以查找相关文章。'),

提供商工具

提供商工具是由 AI 提供商原生实现的特殊工具,提供诸如网络搜索、URL 获取和文件搜索等功能。与常规工具不同,这些工具由提供商本身执行,而不是由你的应用程序执行。

提供商工具可以由你的智能体的 tools 方法返回。

网络搜索

WebSearch 提供商工具允许智能体搜索网络以获取实时信息。这对于回答关于当前事件、最近数据或自模型训练截止日期以来可能发生变化的话题非常有用。

支持的提供商: Anthropic, OpenAI, Gemini

php
use Laravel\Ai\Providers\Tools\WebSearch;

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

你可以配置网络搜索工具来限制搜索次数或将结果限制在特定域:

php
(new WebSearch)->max(5)->allow(['laravel.com', 'php.net']),

要根据用户位置优化搜索结果,请使用 location 方法:

php
(new WebSearch)->location(
    city: 'New York',
    region: 'NY',
    country: 'US'
);

网络获取

WebFetch 提供商工具允许智能体获取并读取网页的内容。当您需要智能体分析特定 URL 或从已知网页检索详细信息时,这很有用。

支持的提供商: Anthropic, Gemini

php
use Laravel\Ai\Providers\Tools\WebFetch;

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

你可以配置网络获取工具来限制获取次数或限制在特定域:

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

文件搜索

FileSearch 提供商工具允许智能体搜索存储在向量存储中的文件。这使得智能体能够搜索你上传的文档以查找相关信息,从而实现检索增强生成(RAG)。

支持的提供商: OpenAI, Gemini

php
use Laravel\Ai\Providers\Tools\FileSearch;

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

你可以提供多个向量存储 ID 以跨多个存储进行搜索:

php
new FileSearch(stores: ['store_1', 'store_2']);

如果你的文件有元数据,你可以通过提供 where 参数来过滤搜索结果。对于简单的相等过滤,传递一个数组:

php
new FileSearch(stores: ['store_id'], where: [
    'author' => 'Taylor Otwell',
    'year' => 2026,
]);

对于更复杂的过滤,你可以传递一个接收 FileSearchQuery 实例的闭包:

php
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'])
);

中间件

智能体支持中间件,允许你在将提示发送给提供商之前拦截和修改它们。可以使用 make:agent-middleware Artisan 命令创建中间件:

shell
php artisan make:agent-middleware LogPrompts

生成的中间件将放在你的应用程序的 app/Ai/Middleware 目录中。要向智能体添加中间件,请实现 HasMiddleware 接口并定义一个返回中间件类数组的 middleware 方法:

php
<?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,
        ];
    }
}

每个中间件类应定义一个 handle 方法,该方法接收 AgentPrompt 和一个 Closure,以将提示传递给下一个中间件:

php
<?php

namespace App\Ai\Middleware;

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

class LogPrompts
{
    /**
     * 处理传入的提示。
     */
    public function handle(AgentPrompt $prompt, Closure $next)
    {
        Log::info('正在提示智能体', ['prompt' => $prompt->prompt]);

        return $next($prompt);
    }
}

你可以在响应上使用 then 方法,在智能体完成处理后执行代码。这适用于同步和流式响应:

php
public function handle(AgentPrompt $prompt, Closure $next)
{
    return $next($prompt)->then(function (AgentResponse $response) {
        Log::info('智能体已响应', ['text' => $response->text]);
    });
}

匿名智能体

有时你可能想快速与模型交互,而无需创建一个专门的智能体类。你可以使用 agent 函数创建一个临时的、匿名的智能体:

php
use function Laravel\Ai\{agent};

$response = agent(
    instructions: '你是一名软件开发专家。',
    messages: [],
    tools: [],
)->prompt('告诉我关于 Laravel 的事情')

匿名智能体也可以产生结构化输出:

php
use Illuminate\Contracts\JsonSchema\JsonSchema;

use function Laravel\Ai\{agent};

$response = agent(
    schema: fn (JsonSchema $schema) => [
        'number' => $schema->integer()->required(),
    ],
)->prompt('生成一个小于 100 的随机数')

智能体配置

你可以使用 PHP 属性为智能体配置文本生成选项。以下属性可用:

  • MaxSteps:智能体在使用工具时可能采取的最大步数。
  • MaxTokens:模型可能生成的最大令牌数。
  • Model:智能体应使用的模型。
  • Provider:用于智能体的 AI 提供商(或用于故障转移的多个提供商)。
  • Temperature:用于生成内容的采样温度(0.0 到 1.0)。
  • Timeout:智能体请求的 HTTP 超时时间(秒,默认:60)。
  • UseCheapestModel:使用提供商最便宜的文本模型以优化成本。
  • UseSmartestModel:使用提供商最强大的文本模型以处理复杂任务。
php
<?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\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)]
class SalesCoach implements Agent
{
    use Promptable;

    // ...
}

UseCheapestModelUseSmartestModel 属性允许你在不指定模型名称的情况下,为给定的提供商自动选择最具成本效益或最强大的模型。当你希望在不同提供商之间优化成本或能力时,这很有用:

php
use Laravel\Ai\Attributes\UseCheapestModel;
use Laravel\Ai\Attributes\UseSmartestModel;
use Laravel\Ai\Contracts\Agent;
use Laravel\Ai\Promptable;

#[UseCheapestModel]
class SimpleSummarizer implements Agent
{
    use Promptable;

    // 将使用最便宜的模型(例如 Haiku)...
}

#[UseSmartestModel]
class ComplexReasoner implements Agent
{
    use Promptable;

    // 将使用最强大的模型(例如 Opus)...
}

提供商选项

如果你的智能体需要传递特定于提供商的选项(例如 OpenAI 的推理努力或惩罚设置),请实现 HasProviderOptions 契约并定义一个 providerOptions 方法:

php
<?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 => [],
        };
    }
}

providerOptions 方法接收当前正在使用的提供商(Lab 枚举或字符串),允许你为每个提供商返回不同的选项。这在使用故障转移时特别有用,因为每个后备提供商都可以接收其自己的配置。

图像

Laravel\Ai\Image 类可用于使用 openaigeminixai 提供商生成图像:

php
use Laravel\Ai\Image;

$image = Image::of('一个放在厨房柜台上的甜甜圈')->generate();

$rawContent = (string) $image;

squareportraitlandscape 方法可用于控制图像的宽高比,而 quality 方法可用于指导模型最终的图像质量(highmediumlow)。timeout 方法可用于指定 HTTP 超时时间(秒):

php
use Laravel\Ai\Image;

$image = Image::of('一个放在厨房柜台上的甜甜圈')
    ->quality('high')
    ->landscape()
    ->timeout(120)
    ->generate();

你可以使用 attachments 方法附加参考图像:

php
use Laravel\Ai\Files;
use Laravel\Ai\Image;

$image = Image::of('把这张我的照片更新为印象派绘画的风格。')
    ->attachments([
        Files\Image::fromStorage('photo.jpg'),
        // Files\Image::fromPath('/home/laravel/photo.jpg'),
        // Files\Image::fromUrl('https://example.com/photo.jpg'),
        // $request->file('photo'),
    ])
    ->landscape()
    ->generate();

生成的图像可以很容易地存储在应用程序的 config/filesystems.php 配置文件中配置的默认磁盘上:

php
$image = Image::of('一个放在厨房柜台上的甜甜圈');

$path = $image->store();
$path = $image->storeAs('image.jpg');
$path = $image->storePublicly();
$path = $image->storePubliclyAs('image.jpg');

图像生成也可以排队:

php
use Laravel\Ai\Image;
use Laravel\Ai\Responses\ImageResponse;

Image::of('一个放在厨房柜台上的甜甜圈')
    ->portrait()
    ->queue()
    ->then(function (ImageResponse $image) {
        $path = $image->store();

        // ...
    });

音频

Laravel\Ai\Audio 类可用于根据给定的文本生成音频:

php
use Laravel\Ai\Audio;

$audio = Audio::of('我喜欢用 Laravel 编码。')->generate();

$rawContent = (string) $audio;

malefemalevoice 方法可用于确定生成音频的语音:

php
$audio = Audio::of('我喜欢用 Laravel 编码。')
    ->female()
    ->generate();

$audio = Audio::of('我喜欢用 Laravel 编码。')
    ->voice('voice-id-or-name')
    ->generate();

类似地,instructions 方法可用于动态地指导模型生成的音频听起来应该是什么样子:

php
$audio = Audio::of('我喜欢用 Laravel 编码。')
    ->female()
    ->instructions('像海盗一样说')
    ->generate();

生成的音频可以很容易地存储在应用程序的 config/filesystems.php 配置文件中配置的默认磁盘上:

php
$audio = Audio::of('我喜欢用 Laravel 编码。')->generate();

$path = $audio->store();
$path = $audio->storeAs('audio.mp3');
$path = $audio->storePublicly();
$path = $audio->storePubliclyAs('audio.mp3');

音频生成也可以排队:

php
use Laravel\Ai\Audio;
use Laravel\Ai\Responses\AudioResponse;

Audio::of('我喜欢用 Laravel 编码。')
    ->queue()
    ->then(function (AudioResponse $audio) {
        $path = $audio->store();

        // ...
    });

转录

Laravel\Ai\Transcription 类可用于生成给定音频的转录文本:

php
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;

diarize 方法可用于表示你希望响应中包含带说话人区分的转录文本,除了原始文本转录文本之外,允许你按说话人访问分段转录文本:

php
$transcript = Transcription::fromStorage('audio.mp3')
    ->diarize()
    ->generate();

转录生成也可以排队:

php
use Laravel\Ai\Transcription;
use Laravel\Ai\Responses\TranscriptionResponse;

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

嵌入

你可以使用 Laravel 的 Stringable 类上可用的新 toEmbeddings 方法轻松为任何给定字符串生成向量嵌入:

php
use Illuminate\Support\Str;

$embeddings = Str::of('纳帕谷有很棒的葡萄酒。')->toEmbeddings();

或者,你可以使用 Embeddings 类一次为多个输入生成嵌入:

php
use Laravel\Ai\Embeddings;

$response = Embeddings::for([
    '纳帕谷有很棒的葡萄酒。',
    'Laravel 是一个 PHP 框架。',
])->generate();

$response->embeddings; // [[0.123, 0.456, ...], [0.789, 0.012, ...]]

你可以指定嵌入的维度和提供商:

php
$response = Embeddings::for(['纳帕谷有很棒的葡萄酒。'])
    ->dimensions(1536)
    ->generate(Lab::OpenAI, 'text-embedding-3-small');

查询嵌入

一旦你生成了嵌入,通常会将它们存储在数据库的 vector 列中,以便以后查询。Laravel 通过 pgvector 扩展为 PostgreSQL 上的向量列提供原生支持。首先,在迁移中定义一个 vector 列,指定维度数量:

php
Schema::ensureVectorExtensionExists();

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

你还可以添加向量索引以加快相似性搜索。当在向量列上调用 index 时,Laravel 将自动使用余弦距离创建一个 HNSW 索引:

php
$table->vector('embedding', dimensions: 1536)->index();

在你的 Eloquent 模型上,你应该将向量列转换为 array

php
protected function casts(): array
{
    return [
        'embedding' => 'array',
    ];
}

要查询相似记录,请使用 whereVectorSimilarTo 方法。此方法通过最小余弦相似度(介于 0.01.0 之间,其中 1.0 表示完全相同)过滤结果,并按相似度排序结果:

php
use App\Models\Document;

$documents = Document::query()
    ->whereVectorSimilarTo('embedding', $queryEmbedding, minSimilarity: 0.4)
    ->limit(10)
    ->get();

$queryEmbedding 可以是浮点数数组或纯字符串。当给定字符串时,Laravel 将自动为其生成嵌入:

php
$documents = Document::query()
    ->whereVectorSimilarTo('embedding', '纳帕谷最好的酒庄')
    ->limit(10)
    ->get();

如果你需要更多控制,你可以独立使用较低级别的 whereVectorDistanceLessThanselectVectorDistanceorderByVectorDistance 方法:

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

如果你想让智能体能够将相似性搜索作为工具使用,请查看相似性搜索工具文档。

NOTE

向量查询目前仅在使用了 pgvector 扩展的 PostgreSQL 连接上受支持。

缓存嵌入

可以缓存嵌入生成,以避免对相同输入进行重复的 API 调用。要启用缓存,请将 ai.caching.embeddings.cache 配置选项设置为 true

php
'caching' => [
    'embeddings' => [
        'cache' => true,
        'store' => env('CACHE_STORE', 'database'),
        // ...
    ],
],

启用缓存后,嵌入会被缓存 30 天。缓存键基于提供商、模型、维度和输入内容,确保相同的请求返回缓存结果,而不同的配置则生成新的嵌入。

你还可以使用 cache 方法为特定请求启用缓存,即使全局缓存被禁用:

php
$response = Embeddings::for(['纳帕谷有很棒的葡萄酒。'])
    ->cache()
    ->generate();

你可以指定自定义的缓存持续时间(秒):

php
$response = Embeddings::for(['纳帕谷有很棒的葡萄酒。'])
    ->cache(seconds: 3600) // 缓存 1 小时
    ->generate();

toEmbeddings 字符串方法也接受一个 cache 参数:

php
// 使用默认持续时间缓存...
$embeddings = Str::of('纳帕谷有很棒的葡萄酒。')->toEmbeddings(cache: true);

// 缓存特定持续时间...
$embeddings = Str::of('纳帕谷有很棒的葡萄酒。')->toEmbeddings(cache: 3600);

重排序

重排序允许你根据文档与给定查询的相关性重新排序文档列表。这对于通过使用语义理解来改进搜索结果非常有用:

Laravel\Ai\Reranking 类可用于重排序文档:

php
use Laravel\Ai\Reranking;

$response = Reranking::of([
    'Django 是一个 Python Web 框架。',
    'Laravel 是一个 PHP Web 应用程序框架。',
    'React 是一个用于构建用户界面的 JavaScript 库。',
])->rerank('PHP 框架');

// 访问最相关的结果...
$response->first()->document; // "Laravel 是一个 PHP Web 应用程序框架。"
$response->first()->score;    // 0.95
$response->first()->index;    // 1 (原始位置)

limit 方法可用于限制返回的结果数量:

php
$response = Reranking::of($documents)
    ->limit(5)
    ->rerank('搜索查询');

重排序集合

为了方便起见,可以使用 rerank 宏对 Laravel 集合进行重排序。第一个参数指定用于重排序的字段,第二个参数是查询:

php
// 按单个字段重排序...
$posts = Post::all()
    ->rerank('body', 'Laravel 教程');

// 按多个字段重排序(作为 JSON 发送)...
$reranked = $posts->rerank(['title', 'body'], 'Laravel 教程');

// 使用闭包构建文档进行重排序...
$reranked = $posts->rerank(
    fn ($post) => $post->title.': '.$post->body,
    'Laravel 教程'
);

你还可以限制结果数量并指定提供商:

php
$reranked = $posts->rerank(
    by: 'content',
    query: 'Laravel 教程',
    limit: 10,
    provider: Lab::Cohere
);

文件

Laravel\Ai\Files 类或各个文件类可用于将文件与你的 AI 提供商一起存储,以便以后在对话中使用。这对于大型文档或你想多次引用而无需重新上传的文件非常有用:

php
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
use Laravel\Ai\Files;
use Laravel\Ai\Files\Document;

// 存储原始内容...
$stored = Document::fromString('Hello, World!', 'text/plain')->put();

// 存储上传的文件...
$stored = Document::fromUpload($request->file('document'))->put();

一旦文件被存储,你可以通过智能体在生成文本时引用该文件,而无需重新上传文件:

php
use App\Ai\Agents\SalesCoach;
use Laravel\Ai\Files;

$response = (new SalesCoach)->prompt(
    '分析附带的销售对话记录...'
    attachments: [
        Files\Document::fromId('file-id') // 附加一个已存储的文档...
    ]
);

要检索以前存储的文件,请在文件实例上使用 get 方法:

php
use Laravel\Ai\Files\Document;

$file = Document::fromId('file-id')->get();

$file->id;
$file->mimeType();

要从提供商处删除文件,请使用 delete 方法:

php
Document::fromId('file-id')->delete();

默认情况下,Files 类使用应用程序的 config/ai.php 配置文件中配置的默认 AI 提供商。对于大多数操作,你可以使用 provider 参数指定不同的提供商:

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

在对话中使用已存储的文件

一旦文件已与提供商一起存储,你可以在智能体对话中使用 DocumentImage 类的 fromId 方法引用它:

php
use App\Ai\Agents\DocumentAnalyzer;
use Laravel\Ai\Files;
use Laravel\Ai\Files\Document;

$stored = Document::fromPath('/path/to/report.pdf')->put();

$response = (new DocumentAnalyzer)->prompt(
    '总结这个文档。',
    attachments: [
        Document::fromId($stored->id),
    ],
);

类似地,已存储的图像可以使用 Image 类引用:

php
use Laravel\Ai\Files;
use Laravel\Ai\Files\Image;

$stored = Image::fromPath('/path/to/photo.jpg')->put();

$response = (new ImageAnalyzer)->prompt(
    '这张图片里有什么?',
    attachments: [
        Image::fromId($stored->id),
    ],
);

向量存储

向量存储允许你创建可搜索的文件集合,这些集合可用于检索增强生成(RAG)。Laravel\Ai\Stores 类提供了创建、检索和删除向量存储的方法:

php
use Laravel\Ai\Stores;

// 创建一个新的向量存储...
$store = Stores::create('知识库');

// 创建带有额外选项的存储...
$store = Stores::create(
    name: '知识库',
    description: '文档和参考资料。',
    expiresWhenIdleFor: days(30),
);

return $store->id;

要按 ID 检索现有向量存储,请使用 get 方法:

php
use Laravel\Ai\Stores;

$store = Stores::get('store_id');

$store->id;
$store->name;
$store->fileCounts;
$store->ready;

要删除向量存储,请在 Stores 类或存储实例上使用 delete 方法:

php
use Laravel\Ai\Stores;

// 按 ID 删除...
Stores::delete('store_id');

// 或者通过存储实例删除...
$store = Stores::get('store_id');

$store->delete();

向存储添加文件

一旦你有了向量存储,你可以使用 add 方法将文件添加到其中。添加到存储的文件会自动编入索引,以便使用文件搜索提供商工具进行语义搜索:

php
use Laravel\Ai\Files\Document;
use Laravel\Ai\Stores;

$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;

NOTE

通常,当将以前存储的文件添加到向量存储时,返回的文档 ID 将与文件先前分配的 ID 匹配;但是,某些向量存储提供商可能会返回一个新的、不同的“文档 ID”。因此,建议你始终将两个 ID 都存储在数据库中,以备将来参考。

你可以在将文件添加到存储时附加元数据。以后在使用文件搜索提供商工具时,可以使用此元数据来过滤搜索结果:

php
$store->add(Document::fromPath('/path/to/document.pdf'), metadata: [
    'author' => 'Taylor Otwell',
    'department' => '工程部',
    'year' => 2026,
]);

要从存储中删除文件,请使用 remove 方法:

php
$store->remove('file_id');

从向量存储中删除文件不会将其从提供商的文件存储中删除。要从向量存储中删除文件并永久从文件存储中删除它,请使用 deleteFile 参数:

php
$store->remove('file_abc123', deleteFile: true);

故障转移

在提示或生成其他媒体时,你可以提供一个提供商/模型数组,以便在主提供商遇到服务中断或速率限制时自动故障转移到备份提供商/模型:

php
use App\Ai\Agents\SalesCoach;
use Laravel\Ai\Image;

$response = (new SalesCoach)->prompt(
    '分析这段销售对话记录...',
    provider: [Lab::OpenAI, Lab::Anthropic],
);

$image = Image::of('一个放在厨房柜台上的甜甜圈')
    ->generate(provider: [Lab::Gemini, Lab::xAI]);

测试

智能体

要在测试中伪造智能体的响应,请在智能体类上调用 fake 方法。你可以选择提供响应数组或闭包:

php
use App\Ai\Agents\SalesCoach;
use Laravel\Ai\Prompts\AgentPrompt;

// 为每个提示自动生成固定响应...
SalesCoach::fake();

// 提供一系列提示响应...
SalesCoach::fake([
    '第一个响应',
    '第二个响应',
]);

// 根据传入的提示动态处理响应...
SalesCoach::fake(function (AgentPrompt $prompt) {
    return '对以下内容的响应:'.$prompt->prompt;
});

NOTE

当在返回结构化输出的智能体上调用 Agent::fake() 时,Laravel 将自动生成符合智能体定义输出模式的假数据。

在提示智能体之后,你可以对接收到的提示进行断言:

php
use Laravel\Ai\Prompts\AgentPrompt;

SalesCoach::assertPrompted('分析这个...');

SalesCoach::assertPrompted(function (AgentPrompt $prompt) {
    return $prompt->contains('分析');
});

SalesCoach::assertNotPrompted('缺失的提示');

SalesCoach::assertNeverPrompted();

对于排队的智能体调用,请使用排队断言方法:

php
use Laravel\Ai\QueuedAgentPrompt;

SalesCoach::assertQueued('分析这个...');

SalesCoach::assertQueued(function (QueuedAgentPrompt $prompt) {
    return $prompt->contains('分析');
});

SalesCoach::assertNotQueued('缺失的提示');

SalesCoach::assertNeverQueued();

为了确保所有智能体调用都有相应的伪造响应,你可以使用 preventStrayPrompts。如果在没有定义伪造响应的情况下调用智能体,将抛出异常:

php
SalesCoach::fake()->preventStrayPrompts();

图像

可以通过在 Image 类上调用 fake 方法来伪造图像生成。一旦图像被伪造,可以对记录的图像生成提示执行各种断言:

php
use Laravel\Ai\Image;
use Laravel\Ai\Prompts\ImagePrompt;
use Laravel\Ai\Prompts\QueuedImagePrompt;

// 为每个提示自动生成固定响应...
Image::fake();

// 提供一系列提示响应...
Image::fake([
    base64_encode($firstImage),
    base64_encode($secondImage),
]);

// 根据传入的提示动态处理响应...
Image::fake(function (ImagePrompt $prompt) {
    return base64_encode('...');
});

在生成图像之后,你可以对接收到的提示进行断言:

php
Image::assertGenerated(function (ImagePrompt $prompt) {
    return $prompt->contains('日落') && $prompt->isLandscape();
});

Image::assertNotGenerated('缺失的提示');

Image::assertNothingGenerated();

对于排队的图像生成,请使用排队断言方法:

php
Image::assertQueued(
    fn (QueuedImagePrompt $prompt) => $prompt->contains('日落')
);

Image::assertNotQueued('缺失的提示');

Image::assertNothingQueued();

为了确保所有图像生成都有相应的伪造响应,你可以使用 preventStrayImages。如果在没有定义伪造响应的情况下生成图像,将抛出异常:

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

音频

可以通过在 Audio 类上调用 fake 方法来伪造音频生成。一旦音频被伪造,可以对记录的音频生成提示执行各种断言:

php
use Laravel\Ai\Audio;
use Laravel\Ai\Prompts\AudioPrompt;
use Laravel\Ai\Prompts\QueuedAudioPrompt;

// 为每个提示自动生成固定响应...
Audio::fake();

// 提供一系列提示响应...
Audio::fake([
    base64_encode($firstAudio),
    base64_encode($secondAudio),
]);

// 根据传入的提示动态处理响应...
Audio::fake(function (AudioPrompt $prompt) {
    return base64_encode('...');
});

在生成音频之后,你可以对接收到的提示进行断言:

php
Audio::assertGenerated(function (AudioPrompt $prompt) {
    return $prompt->contains('你好') && $prompt->isFemale();
});

Audio::assertNotGenerated('缺失的提示');

Audio::assertNothingGenerated();

对于排队的音频生成,请使用排队断言方法:

php
Audio::assertQueued(
    fn (QueuedAudioPrompt $prompt) => $prompt->contains('你好')
);

Audio::assertNotQueued('缺失的提示');

Audio::assertNothingQueued();

为了确保所有音频生成都有相应的伪造响应,你可以使用 preventStrayAudio。如果在没有定义伪造响应的情况下生成音频,将抛出异常:

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

转录

可以通过在 Transcription 类上调用 fake 方法来伪造转录生成。一旦转录被伪造,可以对记录的转录生成提示执行各种断言:

php
use Laravel\Ai\Transcription;
use Laravel\Ai\Prompts\TranscriptionPrompt;
use Laravel\Ai\Prompts\QueuedTranscriptionPrompt;

// 为每个提示自动生成固定响应...
Transcription::fake();

// 提供一系列提示响应...
Transcription::fake([
    '第一段转录文本。',
    '第二段转录文本。',
]);

// 根据传入的提示动态处理响应...
Transcription::fake(function (TranscriptionPrompt $prompt) {
    return '转录的文本...';
});

在生成转录之后,你可以对接收到的提示进行断言:

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

Transcription::assertNotGenerated(
    fn (TranscriptionPrompt $prompt) => $prompt->language === 'fr'
);

Transcription::assertNothingGenerated();

对于排队的转录生成,请使用排队断言方法:

php
Transcription::assertQueued(
    fn (QueuedTranscriptionPrompt $prompt) => $prompt->isDiarized()
);

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

Transcription::assertNothingQueued();

为了确保所有转录生成都有相应的伪造响应,你可以使用 preventStrayTranscriptions。如果在没有定义伪造响应的情况下生成转录,将抛出异常:

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

嵌入

可以通过在 Embeddings 类上调用 fake 方法来伪造嵌入生成。一旦嵌入被伪造,可以对记录的嵌入生成提示执行各种断言:

php
use Laravel\Ai\Embeddings;
use Laravel\Ai\Prompts\EmbeddingsPrompt;
use Laravel\Ai\Prompts\QueuedEmbeddingsPrompt;

// 为每个提示自动生成具有正确维度的伪造嵌入...
Embeddings::fake();

// 提供一系列提示响应...
Embeddings::fake([
    [$firstEmbeddingVector],
    [$secondEmbeddingVector],
]);

// 根据传入的提示动态处理响应...
Embeddings::fake(function (EmbeddingsPrompt $prompt) {
    return array_map(
        fn () => Embeddings::fakeEmbedding($prompt->dimensions),
        $prompt->inputs
    );
});

在生成嵌入之后,你可以对接收到的提示进行断言:

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

Embeddings::assertNotGenerated(
    fn (EmbeddingsPrompt $prompt) => $prompt->contains('其他')
);

Embeddings::assertNothingGenerated();

对于排队的嵌入生成,请使用排队断言方法:

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

Embeddings::assertNotQueued(
    fn (QueuedEmbeddingsPrompt $prompt) => $prompt->contains('其他')
);

Embeddings::assertNothingQueued();

为了确保所有嵌入生成都有相应的伪造响应,你可以使用 preventStrayEmbeddings。如果在没有定义伪造响应的情况下生成嵌入,将抛出异常:

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

重排序

可以通过在 Reranking 类上调用 fake 方法来伪造重排序操作:

php
use Laravel\Ai\Reranking;
use Laravel\Ai\Prompts\RerankingPrompt;
use Laravel\Ai\Responses\Data\RankedDocument;

// 自动生成伪造的重排序响应...
Reranking::fake();

// 提供自定义响应...
Reranking::fake([
    [
        new RankedDocument(index: 0, document: '第一个', score: 0.95),
        new RankedDocument(index: 1, document: '第二个', score: 0.80),
    ],
]);

在重排序之后,你可以对执行的操作进行断言:

php
Reranking::assertReranked(function (RerankingPrompt $prompt) {
    return $prompt->contains('Laravel') && $prompt->limit === 5;
});

Reranking::assertNotReranked(
    fn (RerankingPrompt $prompt) => $prompt->contains('Django')
);

Reranking::assertNothingReranked();

文件

可以通过在 Files 类上调用 fake 方法来伪造文件操作:

php
use Laravel\Ai\Files;

Files::fake();

一旦文件操作被伪造,你可以对发生的上传和删除进行断言:

php
use Laravel\Ai\Contracts\Files\StorableFile;
use Laravel\Ai\Files\Document;

// 存储文件...
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();

对于断言文件删除,你可以传递文件 ID:

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

向量存储

可以通过在 Stores 类上调用 fake 方法来伪造向量存储操作。伪造存储也会自动伪造文件操作

php
use Laravel\Ai\Stores;

Stores::fake();

一旦存储操作被伪造,你可以对创建或删除的存储进行断言:

php
use Laravel\Ai\Stores;

// 创建存储...
$store = Stores::create('知识库');

// 进行断言...
Stores::assertCreated('知识库');

Stores::assertCreated(fn (string $name, ?string $description) =>
    $name === '知识库'
);

Stores::assertNotCreated('其他存储');

Stores::assertNothingCreated();

对于断言存储删除,你可以提供存储 ID:

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

要断言文件已添加或从存储中删除,请在给定的 Store 实例上使用断言方法:

php
Stores::fake();

$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');

如果文件存储在提供商的文件存储中,并在同一个请求中添加到向量存储,你可能不知道文件的提供商 ID。在这种情况下,你可以向 assertAdded 方法传递一个闭包,以针对添加的文件的内容进行断言:

php
use Laravel\Ai\Contracts\Files\StorableFile;
use Laravel\Ai\Files\Document;

$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 会分发各种事件,包括:

  • AddingFileToStore (正在将文件添加到存储)
  • AgentPrompted (智能体已提示)
  • AgentStreamed (智能体流式已传输)
  • AudioGenerated (音频已生成)
  • CreatingStore (正在创建存储)
  • EmbeddingsGenerated (嵌入已生成)
  • FileAddedToStore (文件已添加到存储)
  • FileDeleted (文件已删除)
  • FileRemovedFromStore (文件已从存储中移除)
  • FileStored (文件已存储)
  • GeneratingAudio (正在生成音频)
  • GeneratingEmbeddings (正在生成嵌入)
  • GeneratingImage (正在生成图像)
  • GeneratingTranscription (正在生成转录)
  • ImageGenerated (图像已生成)
  • InvokingTool (正在调用工具)
  • PromptingAgent (正在提示智能体)
  • RemovingFileFromStore (正在从存储中移除文件)
  • Reranked (已重排序)
  • Reranking (正在重排序)
  • StoreCreated (存储已创建)
  • StoringFile (正在存储文件)
  • StreamingAgent (正在流式传输智能体)
  • ToolInvoked (工具已调用)
  • TranscriptionGenerated (转录已生成)

你可以监听这些事件中的任何一个,以记录或存储 AI SDK 的使用信息。