Skip to content
全部文档

Laravel AI SDK

简介

Laravel AI SDK 提供了一个统一的、富有表现力的 API,用于与 OpenAI、Anthropic、Gemini 等 AI 提供商进行交互。借助 AI SDK,你可以使用工具和结构化输出构建智能 agent、生成图像、合成和转录音频、创建向量嵌入等等 - 所有这些都使用一致的、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 配置文件中进行配置。

自定义 Base 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 网关)路由请求或使用替代终结点时,这非常有用。

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

提供商支持

AI SDK 支持各种提供商的功能。下表总结了每个功能可用的提供程序:

FeatureProviders
TextOpenAI, Anthropic, Gemini, Azure, Groq, xAI, DeepSeek, Mistral, Ollama
ImagesOpenAI, Gemini, xAI
TTSOpenAI, ElevenLabs
STTOpenAI, ElevenLabs, Mistral
EmbeddingsOpenAI, Gemini, Azure, Cohere, Mistral, Jina, VoyageAI
RerankingCohere, Jina
FilesOpenAI, Anthropic, Gemini

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

php
use Laravel\Ai\Enums\Lab;

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

Agent

代理是与 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) {}

    /**
     * Get the instructions that the agent should follow.
     */
    public function instructions(): Stringable|string
    {
        return 'You are a sales coach, analyzing transcripts and providing feedback and an overall sales strength score.';
    }

    /**
     * Get the list of messages comprising the conversation so far.
     */
    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();
    }

    /**
     * Get the tools available to the agent.
     *
     * @return Tool[]
     */
    public function tools(): iterable
    {
        return [
            new RetrievePreviousTranscripts,
        ];
    }

    /**
     * Get the agent's structured output schema definition.
     */
    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('Analyze this sales transcript...');

return (string) $response;

make 方法从容器解析你的代理,从而允许自动依赖项注入。你还可以将参数传递给代理的构造函数:

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,
);

对话情境

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

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

/**
 * Get the list of messages comprising the conversation so far.
 */
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();
}

记住对话

注意: 在使用 RemembersConversations trait 之前,应使用 vendor:publish Artisan 命令发布并运行 AI SDK 迁移。这些迁移会创建存储会话所需的数据库表。

如果你希望 Laravel 自动存储和检索代理的对话历史记录,你可以使用 RemembersConversations 特征。此特征提供了一种将对话消息保存到数据库的简单方法,而无需手动实现 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;

    /**
     * Get the instructions that the agent should follow.
     */
    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 特征时,系统会在提示时自动加载以前的消息并将其包含在对话上下文中。每次交互后都会自动存储新消息(用户和助理)。

结构化输出

如果你希望代理返回结构化输出,请实现 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;

    // ...

    /**
     * Get the agent's structured output schema definition.
     */
    public function schema(JsonSchema $schema): array
    {
        return [
            'score' => $schema->integer()->required(),
        ];
    }
}

当提示代理返回结构化输出时,你可以像数组一样访问返回的 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') // Attach a document from a filesystem disk...
        Files\Document::fromPath('/home/laravel/transcript.md') // Attach a document from a local path...
        $request->file('transcript'), // Attach an uploaded file...
    ]
);

同样, 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') // Attach an image from a filesystem disk...
        Files\Image::fromPath('/home/laravel/photo.jpg') // Attach an image from a local path...
        $request->file('photo'), // Attach an uploaded file...
    ]
);

流式输出

你可以通过调用 stream 方法来流式传输代理的响应。返回的 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 stream protocol 流式传输事件:

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

或者,你可以调用代理的 broadcastOnQueue 方法对代理操作进行排队并广播可用的流式事件:

php
(new SalesCoach)->broadcastOnQueue(
    'Analyze this sales transcript...'
    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
{
    /**
     * Get the description of the tool's purpose.
     */
    public function description(): Stringable|string
    {
        return 'This tool may be used to generate cryptographically secure random numbers.';
    }

    /**
     * Execute the tool.
     */
    public function handle(Request $request): Stringable|string
    {
        return (string) random_int($request['min'], $request['max']);
    }

    /**
     * Get the tool's schema definition.
     */
    public function schema(JsonSchema $schema): array
    {
        return [
            'min' => $schema->integer()->min(0)->required(),
            'max' => $schema->integer()->required(),
        ];
    }
}

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

php
use App\Ai\Tools\RandomNumberGenerator;

/**
 * Get the tools available to the agent.
 *
 * @return Tool[]
 */
public function tools(): iterable
{
    return [
        new RandomNumberGenerator,
    ];
}

相似性搜索

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

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

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('Search the knowledge base for relevant articles.'),

提供商工具

提供商工具是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 提供程序工具允许代理搜索存储在 vector stores 中的 files。这允许代理搜索你上传的文档以获取相关信息,从而实现检索增强生成 (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']);

如果你的文件具有 metadata,你可以通过提供 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;

    // ...

    /**
     * Get the agent's middleware.
     */
    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
{
    /**
     * Handle the incoming prompt.
     */
    public function handle(AgentPrompt $prompt, Closure $next)
    {
        Log::info('Prompting agent', ['prompt' => $prompt->prompt]);

        return $next($prompt);
    }
}

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

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

匿名代理

有时你可能希望快速与模型交互,而不创建专用的代理类。你可以使用 agent 函数创建临时匿名代理:

php
use function Laravel\Ai\{agent};

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

匿名代理也可能产生结构化输出:

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

代理配置

你可以使用 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;

    // Will use the cheapest model (e.g., Haiku)...
}

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

    // Will use the most capable model (e.g., Opus)...
}

ProviderModelTimeout 属性外,你还可以在 agent 上定义 providermodeltimeout 方法,以便在运行时解析这些值;当配置取决于数据库记录、配置值或其他运行时状态时很有用。maxStepsmaxTokenstemperature 同理:

php
<?php

namespace App\Ai\Agents;

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

class SalesCoach implements Agent
{
    use Promptable;

    public function maxSteps(): int
    {
        return config('agents.sales_coach.max_steps', 5);
    }

    public function maxTokens(): int
    {
        return $this->user->plan->maxTokens();
    }

    public function temperature(): float
    {
        return Setting::get('sales_coach_temperature', 0.7);
    }
}

当同一选项同时定义了方法与属性时,以方法为准。

提供商选项

如果你的代理需要传递特定于提供商的选项(例如 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;

    // ...

    /**
     * Get provider-specific generation options.
     */
    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 枚举或字符串),允许你为每个提供程序返回不同的选项。这在使用 failover 时特别有用,因为每个后备提供程序都可以接收自己的配置。

图像

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();

        // ...
    });

音频

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();

        // ...
    });

转录

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('Napa Valley has great wine.')->toEmbeddings();

或者,你可以使用 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, ...]]

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

php
$response = Embeddings::for(['Napa Valley has great wine.'])
    ->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', '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();

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

INFO

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

缓存嵌入

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

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

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

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

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

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

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

toEmbeddings Stringable 方法还接受 cache 参数:

php
// Cache with default duration...
$embeddings = Str::of('Napa Valley has great wine.')->toEmbeddings(cache: true);

// Cache for a specific duration...
$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');

// Access the top result...
$response->first()->document; // "Laravel is a PHP web application framework."
$response->first()->score;    // 0.95
$response->first()->index;    // 1 (original position)

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

php
$response = Reranking::of($documents)
    ->limit(5)
    ->rerank('search query');

重新排列集合

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

php
// Rerank by a single field...
$posts = Post::all()
    ->rerank('body', 'Laravel tutorials');

// Rerank by multiple fields (sent as JSON)...
$reranked = $posts->rerank(['title', 'body'], 'Laravel tutorials');

// Rerank using a closure to build the document...
$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;

// Store a file from a local path...
$response = Document::fromPath('/home/laravel/document.pdf')->put();
$response = Image::fromPath('/home/laravel/photo.jpg')->put();

// Store a file that is stored on a filesystem disk...
$response = Document::fromStorage('document.pdf', disk: 'local')->put();
$response = Image::fromStorage('photo.jpg', disk: 'local')->put();

// Store a file that is stored on a remote 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;

// Store raw content...
$stored = Document::fromString('Hello, World!', 'text/plain')->put();

// Store an uploaded file...
$stored = Document::fromUpload($request->file('document'))->put();

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

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') // Attach a stored document...
    ]
);

要检索以前存储的文件,请在文件实例上使用 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(
    '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;

// Create a new vector store...
$store = Stores::create('Knowledge Base');

// Create a store with additional options...
$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;

// Delete by ID...
Stores::delete('store_id');

// Or delete via a store instance...
$store = Stores::get('store_id');

$store->delete();

Adding Files to Stores

一旦你拥有向量存储,你就可以使用 add 方法将 files 添加到其中。添加到存储的文件会使用 file search provider tool 自动索引以进行语义搜索:

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

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

// Add a file that has already been stored with the provider...
$document = $store->add('file_id');
$document = $store->add(Document::fromId('file_id'));

// Or, store and add a file in one step...
$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;

注意: 通常,当将以前存储的文件添加到向量存储时,返回的文档 ID 将与该文件以前分配的 ID 相匹配;然而,一些向量存储提供商可能会返回一个新的、不同的「文档ID」。因此,建议你始终将这两个 ID 存储在数据库中以供将来参考。

将文件添加到商店时,你可以将元数据附加到文件。此元数据稍后可用于在使用 file search provider tool 时过滤搜索结果:

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

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

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

从向量存储中删除文件不会将其从提供商的 file storage 中删除。要从向量存储中删除文件并将其从文件存储中永久删除,请使用 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

要在测试期间伪造代理的响应,请调用代理类上的 fake 方法。你可以选择提供一系列响应或结束语:

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

// Automatically generate a fixed response for every prompt...
SalesCoach::fake();

// Provide a list of prompt responses...
SalesCoach::fake([
    'First response',
    'Second response',
]);

// Dynamically handle prompt responses based on the incoming prompt...
SalesCoach::fake(function (AgentPrompt $prompt) {
    return 'Response for: '.$prompt->prompt;
});

注意: 当对返回结构化输出的 agent 调用 Agent::fake() 时,Laravel 会自动生成与 agent 已定义输出 schema 匹配的假数据。

提示代理后,你可以对收到的提示做出断言:

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();

对于排队代理调用,请使用排队断言方法:

php
use Laravel\Ai\QueuedAgentPrompt;

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

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

SalesCoach::assertNotQueued('Missing prompt');

SalesCoach::assertNeverQueued();

为了确保所有代理调用都有相应的虚假响应,你可以使用 preventStrayPrompts。如果在没有定义假响应的情况下调用代理,则会抛出异常:

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

图像

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

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

// Automatically generate a fixed response for every prompt...
Image::fake();

// Provide a list of prompt responses...
Image::fake([
    base64_encode($firstImage),
    base64_encode($secondImage),
]);

// Dynamically handle prompt responses based on the incoming prompt...
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;

// Automatically generate a fixed response for every prompt...
Audio::fake();

// Provide a list of prompt responses...
Audio::fake([
    base64_encode($firstAudio),
    base64_encode($secondAudio),
]);

// Dynamically handle prompt responses based on the incoming prompt...
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;

// Automatically generate a fixed response for every prompt...
Transcription::fake();

// Provide a list of prompt responses...
Transcription::fake([
    'First transcription text.',
    'Second transcription text.',
]);

// Dynamically handle prompt responses based on the incoming prompt...
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 类上的 fake 方法来伪造嵌入生成。一旦嵌入被伪造,就可以针对记录的嵌入生成提示执行各种断言:

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

// Automatically generate fake embeddings of the proper dimensions for every prompt...
Embeddings::fake();

// Provide a list of prompt responses...
Embeddings::fake([
    [$firstEmbeddingVector],
    [$secondEmbeddingVector],
]);

// Dynamically handle prompt responses based on the incoming prompt...
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('Other')
);

Embeddings::assertNothingGenerated();

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

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

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

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;

// Automatically generate a fake reranked responses...
Reranking::fake();

// Provide custom responses...
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;

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

// Make assertions...
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 方法来伪造。伪造商店也会自动伪造 file operations

php
use Laravel\Ai\Stores;

Stores::fake();

一旦商店操作被伪造,你可以对创建或删除的商店做出断言:

php
use Laravel\Ai\Stores;

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

// Make assertions...
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');

// Add / remove files...
$store->add('added_id');
$store->remove('removed_id');

// Make assertions...
$store->assertAdded('added_id');
$store->assertRemoved('removed_id');

$store->assertNotAdded('other_file_id');
$store->assertNotRemoved('other_file_id');

如果文件存储在提供程序的 file storage 中并在同一请求中添加到向量存储中,你可能不知道该文件的提供程序 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 调度各种 events,包括:

  • AddingFileToStore
  • AgentPrompted
  • AgentStreamed
  • AudioGenerated
  • CreatingStore
  • EmbeddingsGenerated
  • FileAddedToStore
  • FileDeleted
  • FileRemovedFromStore
  • FileStored
  • GeneratingAudio
  • GeneratingEmbeddings
  • GeneratingImage
  • GeneratingTranscription
  • ImageGenerated
  • InvokingTool
  • PromptingAgent
  • RemovingFileFromStore
  • Reranked
  • 重排序
  • StoreCreated
  • StoringFile
  • StreamingAgent
  • ToolInvoked
  • TranscriptionGenerated

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