Skip to content

Laravel AI SDK

简介

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

安装

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

shell
composer require laravel/ai

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

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

最后,你应该运行应用程序的数据库迁移。这将创建 AI SDK 用于存储对话的 agent_conversationsagent_conversation_messages 表:

shell
php artisan migrate

配置

你可以在应用程序的 config/ai.php 配置文件中或在 .env 文件中以环境变量的形式定义 AI 提供商凭据:

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=

用于文本、图像、音频、转录和 embeddings 的默认模型也可以在应用程序的 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
EmbeddingsOpenAI, 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;
// ...

Agent

Agent 是 Laravel AI SDK 中与 AI 提供商交互的基本构建块。每个 agent 都是一个专用的 PHP 类,封装了与大语言模型交互所需的指令、对话上下文、工具和输出模式。可以将 agent 视为一个专业助手——销售教练、文档分析器、客服机器人——你只需配置一次,即可在整个应用程序中按需使用。

你可以通过 make:agent Artisan 命令创建一个 agent:

shell
php artisan make:agent SalesCoach

php artisan make:agent SalesCoach --structured

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

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) {}

    /**
     * 获取 agent 应遵循的指令。
     */
    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();
    }

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

    /**
     * 获取 agent 的结构化输出模式定义。
     */
    public function schema(JsonSchema $schema): array
    {
        return [
            'feedback' => $schema->string()->required(),
            'score' => $schema->integer()->min(1)->max(10)->required(),
        ];
    }
}

提示

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

php
$response = (new SalesCoach)
    ->prompt('Analyze this sales transcript...');

return (string) $response;

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

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

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

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

对话上下文

如果你的 agent 实现了 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 自动存储和检索 agent 的对话历史,你可以使用 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;

    /**
     * 获取 agent 应遵循的指令。
     */
    public function instructions(): string
    {
        return 'You are a sales coach...';
    }
}

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

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

$conversationId = $response->conversationId;

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

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

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

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

结构化输出

如果你希望 agent 返回结构化输出,请实现 HasStructuredOutput 接口,该接口要求你的 agent 定义一个 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;

    // ...

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

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

php
$response = (new SalesCoach)->prompt('Analyze this sales transcript...');

return $response['score'];

附件

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

php
use App\Ai\Agents\SalesCoach;
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'), // 附加上传的文件...
    ]
);

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

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

$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 方法流式传输 agent 的响应。返回的 StreamableAgentResponse 可以从路由返回,以自动向客户端发送流式响应(SSE):

php
use App\Ai\Agents\SalesCoach;

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

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

php
use App\Ai\Agents\SalesCoach;
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
$stream = (new SalesCoach)->stream('Analyze this sales transcript...');

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('Analyze this sales transcript...')
        ->usingVercelDataProtocol();
});

广播

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

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

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

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

或者,你可以调用 agent 的 broadcastOnQueue 方法将 agent 操作加入队列,并在流式事件可用时广播它们:

php
(new SalesCoach)->broadcastOnQueue(
    'Analyze this sales transcript...'
    new Channel('channel-name'),
);

队列

使用 agent 的 queue 方法,你可以提示 agent,但允许它在后台处理响应,使你的应用程序保持快速和响应。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();
});

工具

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

shell
php artisan make:tool RandomNumberGenerator

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

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 '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(),
        ];
    }
}

定义工具后,你可以从任何 agent 的 tools 方法中返回它:

php
use App\Ai\Tools\RandomNumberGenerator;

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

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

创建相似性搜索工具最简单的方法是使用 usingModel 方法,传入一个具有向量 embeddings 的 Eloquent 模型:

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

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

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

你还可以提供 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('Search the knowledge base for relevant articles.'),

提供商工具

提供商工具是由 AI 提供商原生实现的特殊工具,提供网页搜索、URL 抓取和文件搜索等功能。与常规工具不同,提供商工具由提供商自身执行,而非你的应用程序。

提供商工具可以从 agent 的 tools 方法中返回。

WebSearch 提供商工具允许 agent 搜索网页以获取实时信息。这对于回答有关时事、最新数据或自模型训练截止后可能已发生变化的主题的问题非常有用。

支持的提供商: 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 提供商工具允许 agent 抓取和读取网页内容。当你需要 agent 分析特定 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 提供商工具允许 agent 搜索存储在向量存储中的文件。这通过允许 agent 搜索你上传的文档中的相关信息来实现检索增强生成(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'])
);

中间件

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

shell
php artisan make:agent-middleware LogPrompts

生成的中间件将放置在应用程序的 app/Ai/Middleware 目录中。要向 agent 添加中间件,请实现 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;

    // ...

    /**
     * 获取 agent 的中间件。
     */
    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('Prompting agent', ['prompt' => $prompt->prompt]);

        return $next($prompt);
    }
}

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

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

匿名 Agent

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

php
use function Laravel\Ai\{agent};

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

匿名 agent 也可以生成结构化输出:

php
use Illuminate\Contracts\JsonSchema\JsonSchema;

use function Laravel\Ai\{agent};

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

Agent 配置

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

  • MaxSteps:agent 使用工具时可以执行的最大步骤数。
  • MaxTokens:模型可以生成的最大 token 数。
  • Model:agent 应使用的模型。
  • Provider:用于 agent 的 AI 提供商(或用于故障转移的多个提供商)。
  • Temperature:用于生成的采样温度(0.0 到 1.0)。
  • Timeout:agent 请求的 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)...
}

提供商选项

如果你的 agent 需要传递提供商特定的选项(如 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('A donut sitting on the kitchen counter')->generate();

$rawContent = (string) $image;

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

php
use Laravel\Ai\Image;

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

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

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

$image = Image::of('Update this photo of me to be in the style of an impressionist painting.')
    ->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('A donut sitting on the kitchen counter');

$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('A donut sitting on the kitchen counter')
    ->portrait()
    ->queue()
    ->then(function (ImageResponse $image) {
        $path = $image->store();

        // ...
    });

音频(TTS)

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

php
use Laravel\Ai\Audio;

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

$rawContent = (string) $audio;

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

php
$audio = Audio::of('I love coding with Laravel.')
    ->female()
    ->generate();

$audio = Audio::of('I love coding with Laravel.')
    ->voice('voice-id-or-name')
    ->generate();

同样,instructions 方法可用于动态指导模型生成音频的听觉效果:

php
$audio = Audio::of('I love coding with Laravel.')
    ->female()
    ->instructions('Said like a pirate')
    ->generate();

生成的音频可以轻松存储到应用程序 config/filesystems.php 配置文件中配置的默认磁盘上:

php
$audio = Audio::of('I love coding with 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('I love coding with Laravel.')
    ->queue()
    ->then(function (AudioResponse $audio) {
        $path = $audio->store();

        // ...
    });

转录(STT)

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) {
        // ...
    });

Embeddings

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

php
use Illuminate\Support\Str;

$embeddings = Str::of('Napa Valley has great wine.')->toEmbeddings();

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

php
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, ...]]

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

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

查询 Embeddings

生成 embeddings 后,你通常会将它们存储在数据库的 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 会自动为其生成 embeddings:

php
$documents = Document::query()
    ->whereVectorSimilarTo('embedding', 'best wineries in Napa Valley')
    ->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();

如果你想让 agent 能够作为工具执行相似性搜索,请查看相似性搜索工具文档。

NOTE

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

缓存 Embeddings

Embedding 生成可以被缓存以避免对相同输入的冗余 API 调用。要启用缓存,请将 ai.caching.embeddings.cache 配置选项设置为 true

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

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

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

php
$response = Embeddings::for(['Napa Valley has great wine.'])
    ->cache()
    ->generate();

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

php
$response = Embeddings::for(['Napa Valley has great wine.'])
    ->cache(seconds: 3600) // 缓存 1 小时
    ->generate();

toEmbeddings Stringable 方法也接受 cache 参数:

php
// 使用默认持续时间缓存...
$embeddings = Str::of('Napa Valley has great wine.')->toEmbeddings(cache: true);

// 缓存指定持续时间...
$embeddings = Str::of('Napa Valley has great wine.')->toEmbeddings(cache: 3600);

重排序

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

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

php
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
$response = Reranking::of($documents)
    ->limit(5)
    ->rerank('search query');

重排序集合

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

php
// 按单个字段重排序...
$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'
);

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

php
$reranked = $posts->rerank(
    by: 'content',
    query: 'Laravel tutorials',
    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();

文件存储后,你可以在通过 agent 生成文本时引用该文件,而无需重新上传:

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

$response = (new SalesCoach)->prompt(
    'Analyze the attached sales transcript...'
    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 方法在 agent 对话中引用它:

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(
    'Summarize this document.',
    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(
    'What is in this image?',
    attachments: [
        Image::fromId($stored->id),
    ],
);

向量存储

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

php
use Laravel\Ai\Stores;

// 创建新的向量存储...
$store = Stores::create('Knowledge Base');

// 使用额外选项创建存储...
$store = Stores::create(
    name: 'Knowledge Base',
    description: 'Documentation and reference materials.',
    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' => 'Engineering',
    '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(
    '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]);

测试

Agent

要在测试中模拟 agent 的响应,请在 agent 类上调用 fake 方法。你可以选择性地提供响应数组或闭包:

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

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

// 提供提示响应列表...
SalesCoach::fake([
    'First response',
    'Second response',
]);

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

Note: 当在返回结构化输出的 agent 上调用 Agent::fake() 时,Laravel 会自动生成与 agent 定义的输出模式匹配的假数据。

提示 agent 后,你可以对接收到的提示进行断言:

php
use Laravel\Ai\Prompts\AgentPrompt;

SalesCoach::assertPrompted('Analyze this...');

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

SalesCoach::assertNotPrompted('Missing prompt');

SalesCoach::assertNeverPrompted();

对于加入队列的 agent 调用,使用队列断言方法:

php
use Laravel\Ai\QueuedAgentPrompt;

SalesCoach::assertQueued('Analyze this...');

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

SalesCoach::assertNotQueued('Missing prompt');

SalesCoach::assertNeverQueued();

要确保所有 agent 调用都有对应的假响应,你可以使用 preventStrayPrompts。如果 agent 在没有定义假响应的情况下被调用,将抛出异常:

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('sunset') && $prompt->isLandscape();
});

Image::assertNotGenerated('Missing prompt');

Image::assertNothingGenerated();

对于加入队列的图像生成,使用队列断言方法:

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

Image::assertNotQueued('Missing prompt');

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('Hello') && $prompt->isFemale();
});

Audio::assertNotGenerated('Missing prompt');

Audio::assertNothingGenerated();

对于加入队列的音频生成,使用队列断言方法:

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

Audio::assertNotQueued('Missing prompt');

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([
    'First transcription text.',
    'Second transcription text.',
]);

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

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

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

可以通过在 Embeddings 类上调用 fake 方法来模拟 embeddings 生成。模拟 embeddings 后,可以对记录的 embeddings 生成提示执行各种断言:

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

// 自动为每个提示生成适当维度的假 embeddings...
Embeddings::fake();

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

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

生成 embeddings 后,你可以对接收到的提示进行断言:

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

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

Embeddings::assertNothingGenerated();

对于加入队列的 embeddings 生成,使用队列断言方法:

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

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

Embeddings::assertNothingQueued();

要确保所有 embeddings 生成都有对应的假响应,你可以使用 preventStrayEmbeddings。如果在没有定义假响应的情况下生成 embeddings,将抛出异常:

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: 'First', score: 0.95),
        new RankedDocument(index: 1, document: 'Second', 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('Knowledge Base');

// 进行断言...
Stores::assertCreated('Knowledge Base');

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

Stores::assertNotCreated('Other Store');

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 使用信息。