翻译进度
28
分块数量
3
参与人数

AI SDK

这是一篇协同翻译的文章,你可以点击『我来翻译』按钮来参与翻译。


Laravel AI SDK

简介

Laravel AI SDK」 为开发者提供了一套统一且富有表达力的 API,用于与 OpenAI、Anthropic、Gemini 等 AI 服务商进行交互。通过该 SDK,你可以构建具备工具调用与结构化输出能力的智能 Agent,还能生成图像、合成与转录音频、创建向量嵌入等功能 —— 所有操作都通过一致且符合 Laravel 风格的接口实现。

安装

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

composer require laravel/ai

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

php artisan vendor:publish --provider="Laravel\Ai\AiServiceProvider"
Ohihi 翻译于 1周前

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

php artisan migrate

配置

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

ANTHROPIC_API_KEY=
AZURE_OPENAI_API_KEY=
COHERE_API_KEY=
DEEPSEEK_API_KEY=
ELEVENLABS_API_KEY=
GEMINI_API_KEY=
GROQ_API_KEY=
MISTRAL_API_KEY=
OLLAMA_API_KEY=
OPENAI_API_KEY=
OPENROUTER_API_KEY=
JINA_API_KEY=
VOYAGEAI_API_KEY=
XAI_API_KEY=

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

自定义基础 URL

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

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

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

dkp 翻译于 3周前

提供商支持

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

Feature Providers
Text OpenAI, Anthropic, Gemini, Azure, Bedrock, Groq, xAI, DeepSeek, Mistral, Ollama, OpenRouter
Images OpenAI, Gemini, xAI, Azure, Bedrock, OpenRouter
TTS OpenAI, ElevenLabs, Gemini
STT OpenAI, ElevenLabs, Mistral, Gemini
Embeddings OpenAI, Gemini, Azure, Bedrock, Cohere, Mistral, Jina, VoyageAI, Ollama, OpenRouter
Reranking Cohere, Jina, VoyageAI
Files OpenAI, Anthropic, Gemini

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

use Laravel\Ai\Enums\Lab;

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

Agents

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

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

php artisan make:agent SalesCoach

php artisan make:agent SalesCoach --structured

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

<?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(),
        ];
    }
}
dkp 翻译于 3周前

提示

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

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

return (string) $response;

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

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

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

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

对话上下文

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

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

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...';
    }
}
dkp 翻译于 3周前

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

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

$conversationId = $response->conversationId;

对话 ID 会在响应中返回,可以存储以供将来引用。如果你想使用 Eloquent 检索用户的所有对话,可以将 HasConversations trait 添加到你的用户模型:

<?php

namespace App\Models;

use Illuminate\Foundation\Auth\User as Authenticatable;
use Laravel\Ai\Concerns\HasConversations;

class User extends Authenticatable
{
    use HasConversations;
}

将该 trait 添加到模型后,你可以通过 conversations 关联检索和查询用户的对话:

$conversations = $user->conversations()
    ->latest('updated_at')
    ->paginate(20);

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

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

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

结构化输出

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

<?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(),
        ];
    }
}
dkp 翻译于 3周前

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

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

return $response['score'];

嵌套对象

要定义嵌套的结构化输出,可以结合闭包使用 object 方法:

<?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(),
            'metadata' => $schema->object(fn ($schema) => [
                'confidence' => $schema->string()->enum(['low', 'medium', 'high'])->required(),
                'language' => $schema->string()->required(),
            ])->required(),
        ];
    }
}

对象数组

如果你的 agent 应返回由结构化条目组成的列表,可以组合 arrayobject 方法:

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

附件

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

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'), // 附加上传的文件...
    ]
);
dkp 翻译于 3周前

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

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

use App\Ai\Agents\SalesCoach;

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

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

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

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

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

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

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

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

use App\Ai\Agents\SalesCoach;

Route::get('/coach', function () {
    return (new SalesCoach)
        ->stream('Analyze this sales transcript...')
        ->usingVercelDataProtocol();
});
dkp 翻译于 3周前

广播

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

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 操作加入队列,并在流式事件可用时广播它们:

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

队列

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

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

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

    return back();
});

工具

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

php artisan make:tool RandomNumberGenerator

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

<?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(),
        ];
    }
}
dkp 翻译于 3周前

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

use App\Ai\Tools\RandomNumberGenerator;

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

相似性搜索

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

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

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

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

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

你还可以提供 0.01.0 之间的最小相似度阈值和一个闭包来自定义查询:

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

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

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 方法自定义工具的描述:

SimilaritySearch::usingModel(Document::class, 'embedding')
    ->withDescription('Search the knowledge base for relevant articles.'),
dkp 翻译于 3周前

提供商工具

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

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

网页搜索

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

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

use Laravel\Ai\Providers\Tools\WebSearch;

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

你可以配置网页搜索工具以限制搜索次数或将结果限制到特定域名:

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

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

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

网页抓取

WebFetch 提供商工具允许 agent 抓取和读取网页内容。当你需要 agent 分析特定 URL 或从已知网页检索详细信息时,这非常有用。

支持的供应商: Anthropic, Gemini

use Laravel\Ai\Providers\Tools\WebFetch;

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

你可以配置网页抓取工具以限制抓取次数或限制到特定域名:

(new WebFetch)->max(3)->allow(['docs.laravel.com']),
dkp 翻译于 2周前

文件搜索

FileSearch 提供商工具允许 agent 搜索存储在向量存储中的文件。这通过允许 agent 搜索你上传的文档中的相关信息来实现检索增强生成(RAG)。

支持的提供商: OpenAI, Gemini

use Laravel\Ai\Providers\Tools\FileSearch;

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

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

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

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

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

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

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

子 Agents

Agent 也可以从另一个 agent 的 tools 方法中返回。当一个 agent 作为工具返回时,父 agent 可以将特定任务委派给子 agent,并在回答原始提示时使用子 agent 的响应。当通用型 agent 需要访问拥有独立指令、工具、模型配置或提供商偏好的专用 agent 时,这会很有用。

例如,客服 agent 可以将退款资格问题委派给专门的退款 agent:

<?php

namespace App\Ai\Agents;

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

class CustomerSupportAgent implements Agent, HasTools
{
    use Promptable;

    /**
     * 获取 agent 应遵循的指令。
     */
    public function instructions(): string
    {
        return 'You help customers with account, order, and billing questions. Delegate refund policy questions to the refunds specialist.';
    }

    /**
     * 获取 agent 可用的工具。
     *
     * @return Tool[]
     */
    public function tools(): iterable
    {
        return [
            new RefundsAgent,
        ];
    }
}
dkp 翻译于 2周前

要自定义子 agent 暴露给父 agent 的方式,请在子 agent 上实现 CanActAsTool 接口,并定义面向工具的名称和描述:

<?php

namespace App\Ai\Agents;

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

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

    /**
     * 获取 agent 应遵循的指令。
     */
    public function instructions(): string
    {
        return 'You are a refunds specialist. Use order details and the refund policy to give concise eligibility guidance.';
    }

    /**
     * 获取 agent 的工具名称。
     */
    public function name(): string
    {
        return 'refunds_specialist';
    }

    /**
     * 获取 agent 的工具描述。
     */
    public function description(): string
    {
        return 'Determine whether an order is eligible for a refund and explain the next step.';
    }

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

如果子 agent 未实现 CanActAsTool,Laravel 将根据 agent 的类名生成默认工具名称和描述。

中间件

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

php artisan make:agent-middleware LogPrompts

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

<?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,
        ];
    }
}
dkp 翻译于 2周前

Each middleware class should define a handle method that receives the AgentPrompt and a Closure to pass the prompt to the next middleware:

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

You may use the then method on the response to execute code after the agent has finished processing. This works for both synchronous and streaming responses:

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

Anonymous Agents

Sometimes you may want to quickly interact with a model without creating a dedicated agent class. You can create an ad-hoc, anonymous agent using the agent function:

use function Laravel\Ai\{agent};

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

Anonymous agents may also produce structured output:

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 Configuration

You may configure text generation options for an agent using PHP attributes. The following attributes are available:

  • MaxSteps: The maximum number of steps the agent may take when using tools.
  • MaxTokens: The maximum number of tokens the model may generate.
  • Model: The model the agent should use.
  • Provider: The AI provider (or providers for failover) to use for the agent.
  • Temperature: The sampling temperature to use for generation (0.0 to 1.0).
  • Timeout: The HTTP timeout in seconds for agent requests (default: 60).
  • TopP: The nucleus sampling probability to use for generation (0.0 to 1.0).
  • UseCheapestModel: Use the provider's cheapest text model for cost optimization.
  • UseSmartestModel: Use the provider's most capable text model for complex tasks.
<?php

namespace App\Ai\Agents;

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

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

    // ...
}

The UseCheapestModel and UseSmartestModel attributes allow you to automatically select the most cost-effective or most capable model for a given provider without specifying a model name. This is useful when you want to optimize for cost or capability across different providers:

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

[!NOTE]
The underlying model selected by UseCheapestModel and UseSmartestModel may change between releases of the Laravel AI SDK as providers release new models. Switching models can introduce behavioral changes, deprecated parameters, and significant cost differences. If you need a stable, predictable model and pricing, specify the model explicitly using the Model attribute.

Provider Options

If your agent needs to pass provider-specific options (such as OpenAI reasoning effort or penalty settings), implement the HasProviderOptions contract and define a providerOptions method:

<?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],
                'cache_control' => ['type' => 'ephemeral'],
            ],
            default => [],
        };
    }
}

The providerOptions method receives the provider currently being used (Lab enum or string), allowing you to return different options per provider. This is especially useful when using failover, since each fallback provider can receive its own configuration.

The Anthropic example above also enables prompt caching via cache_control.

Images

The Laravel\Ai\Image class may be used to generate images using the openai, gemini, or xai providers:

use Laravel\Ai\Image;

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

$rawContent = (string) $image;

The square, portrait, and landscape methods may be used to control the aspect ratio of the image, while the quality method may be used to guide the model on final image quality (high, medium, low). The timeout method may be used to specify the HTTP timeout in seconds:

use Laravel\Ai\Image;

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

You may attach reference images using the attachments method:

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

Generated images may be easily stored on the default disk configured in your application's config/filesystems.php configuration file:

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

Image generation may also be queued:

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

        // ...
    });

Audio

The Laravel\Ai\Audio class may be used to generate audio from the given text:

use Laravel\Ai\Audio;

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

$rawContent = (string) $audio;

You may also generate audio from a string using the toAudio method available via Laravel's Stringable class:

use Illuminate\Support\Str;

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

The male, female, and voice methods may be used to determine the voice of the generated audio:

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

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

Similarly, the instructions method may be used to dynamically coach the model on how the generated audio should sound:

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

Generated audio may be easily stored on the default disk configured in your application's config/filesystems.php configuration file:

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

Audio generation may also be queued:

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

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

        // ...
    });

Transcriptions

The Laravel\Ai\Transcription class may be used to generate a transcript of the given audio:

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;

The diarize method may be used to indicate you would like the response to include the diarized transcript in addition to the raw text transcript, allowing you to access the segmented transcript by speaker:

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

Transcription generation may also be queued:

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

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

Embeddings

You may easily generate vector embeddings for any given string using the new toEmbeddings method available via Laravel's Stringable class:

use Illuminate\Support\Str;

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

Alternatively, you may use the Embeddings class to generate embeddings for multiple inputs at once:

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

You may specify the dimensions and provider for the embeddings:

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

Querying Embeddings

Once you have generated embeddings, you will typically store them in a vector column in your database for later querying. Laravel provides native support for vector columns on PostgreSQL via the pgvector extension. To get started, define a vector column in your migration, specifying the number of dimensions:

Schema::ensureVectorExtensionExists();

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

You may also add a vector index to speed up similarity searches. When calling index on a vector column, Laravel will automatically create an HNSW index with cosine distance:

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

On your Eloquent model, you should cast the vector column to an array:

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

To query for similar records, use the whereVectorSimilarTo method. This method filters results by a minimum cosine similarity (between 0.0 and 1.0, where 1.0 is identical) and orders the results by similarity:

use App\Models\Document;

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

The $queryEmbedding may be an array of floats or a plain string. When a string is given, Laravel will automatically generate embeddings for it:

$documents = Document::query()
    ->whereVectorSimilarTo('embedding', 'best wineries in Napa Valley')
    ->limit(10)
    ->get();

If you need more control, you may use the lower-level whereVectorDistanceLessThan, selectVectorDistance, and orderByVectorDistance methods independently:

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

If you would like to give an agent the ability to perform similarity searches as a tool, check out the Similarity Search tool documentation.

[!NOTE]
Vector queries are currently only supported on PostgreSQL connections using the pgvector extension.

Caching Embeddings

Embedding generation can be cached to avoid redundant API calls for identical inputs. To enable caching, set the ai.caching.embeddings.cache configuration option to true:

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

When caching is enabled, embeddings are cached for 30 days. The cache key is based on the provider, model, dimensions, and input content, ensuring that identical requests return cached results while different configurations generate fresh embeddings.

You may also enable caching for a specific request using the cache method, even when global caching is disabled:

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

You may specify a custom cache duration in seconds:

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

The toEmbeddings Stringable method also accepts a cache argument:

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

Reranking

Reranking allows you to reorder a list of documents based on their relevance to a given query. This is useful for improving search results by using semantic understanding:

The Laravel\Ai\Reranking class may be used to rerank documents:

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)

The limit method may be used to restrict the number of results returned:

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

Reranking Collections

For convenience, Laravel collections may be reranked using the rerank macro. The first argument specifies which field(s) to use for reranking, and the second argument is the query:

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

You may also limit the number of results and specify a provider:

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

Files

The Laravel\Ai\Files class or the individual file classes may be used to store files with your AI provider for later use in conversations. This is useful for large documents or files you want to reference multiple times without re-uploading:

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;

You may also store raw content or uploaded files:

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

Once a file has been stored, you may reference the file when generating text via agents instead of re-uploading the file:

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...
    ]
);

To retrieve a previously stored file, use the get method on a file instance:

use Laravel\Ai\Files\Document;

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

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

To delete a file from the provider, use the delete method:

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

By default, the Files class uses the default AI provider configured in your application's config/ai.php configuration file. For most operations, you may specify a different provider using the provider argument:

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

Using Stored Files in Conversations

Once a file has been stored with a provider, you may reference it in agent conversations using the fromId method on the Document or Image classes:

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

Similarly, stored images may be referenced using the Image class:

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

Vector Stores

Vector stores allow you to create searchable collections of files that can be used for retrieval-augmented generation (RAG). The Laravel\Ai\Stores class provides methods for creating, retrieving, and deleting vector stores:

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;

To retrieve an existing vector store by its ID, use the get method:

use Laravel\Ai\Stores;

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

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

To delete a vector store, use the delete method on the Stores class or the store instance:

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

Once you have a vector store, you may add files to it using the add method. Files added to a store are automatically indexed for semantic searching using the file search provider tool:

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;

Note: Typically, when adding previously stored files to vector stores, the returned document ID will match the file's previously assigned ID; however, some vector storage providers may return a new, different "document ID". Therefore, it's recommended that you always store both IDs in your database for future reference.

You may attach metadata to files when adding them to a store. This metadata can later be used to filter search results when using the file search provider tool:

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

To remove a file from a store, use the remove method:

$store->remove('file_id');

Removing a file from a vector store does not remove it from the provider's file storage. To remove a file from the vector store and delete it permanently from file storage, use the deleteFile argument:

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

Failover

When prompting or generating other media, you may provide an array of providers / models to automatically failover to a backup provider / model if a service interruption or rate limit is encountered on the primary provider:

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

Testing

Agents

To fake an agent's responses during tests, call the fake method on the agent class. You may optionally provide an array of responses or a closure:

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

Note: When Agent::fake() is invoked on an agent that returns structured output, Laravel will automatically generate fake data that matches your agent's defined output schema.

After prompting the agent, you may make assertions about the prompts that were received:

use Laravel\Ai\Prompts\AgentPrompt;

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

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

SalesCoach::assertNotPrompted('Missing prompt');

SalesCoach::assertNeverPrompted();

For queued agent invocations, use the queued assertion methods:

use Laravel\Ai\QueuedAgentPrompt;

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

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

SalesCoach::assertNotQueued('Missing prompt');

SalesCoach::assertNeverQueued();

To ensure all agent invocations have a corresponding fake response, you may use preventStrayPrompts. If an agent is invoked without a defined fake response, an exception will be thrown:

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

Images

Image generations may be faked by invoking the fake method on the Image class. Once image has been faked, various assertions may be performed against the recorded image generation prompts:

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

After generating images, you may make assertions about the prompts that were received:

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

Image::assertNotGenerated('Missing prompt');

Image::assertNothingGenerated();

For queued image generations, use the queued assertion methods:

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

Image::assertNotQueued('Missing prompt');

Image::assertNothingQueued();

To ensure all image generations have a corresponding fake response, you may use preventStrayImages. If an image is generated without a defined fake response, an exception will be thrown:

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

Audio

Audio generations may be faked by invoking the fake method on the Audio class. Once audio has been faked, various assertions may be performed against the recorded audio generation prompts:

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

After generating audio, you may make assertions about the prompts that were received:

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

Audio::assertNotGenerated('Missing prompt');

Audio::assertNothingGenerated();

For queued audio generations, use the queued assertion methods:

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

Audio::assertNotQueued('Missing prompt');

Audio::assertNothingQueued();

To ensure all audio generations have a corresponding fake response, you may use preventStrayAudio. If audio is generated without a defined fake response, an exception will be thrown:

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

Transcriptions

Transcription generations may be faked by invoking the fake method on the Transcription class. Once transcription has been faked, various assertions may be performed against the recorded transcription generation prompts:

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

After generating transcriptions, you may make assertions about the prompts that were received:

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

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

Transcription::assertNothingGenerated();

For queued transcription generations, use the queued assertion methods:

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

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

Transcription::assertNothingQueued();

To ensure all transcription generations have a corresponding fake response, you may use preventStrayTranscriptions. If a transcription is generated without a defined fake response, an exception will be thrown:

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

Embeddings

Embeddings generations may be faked by invoking the fake method on the Embeddings class. Once embeddings has been faked, various assertions may be performed against the recorded embeddings generation prompts:

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

After generating embeddings, you may make assertions about the prompts that were received:

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

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

Embeddings::assertNothingGenerated();

For queued embeddings generations, use the queued assertion methods:

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

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

Embeddings::assertNothingQueued();

To ensure all embeddings generations have a corresponding fake response, you may use preventStrayEmbeddings. If embeddings are generated without a defined fake response, an exception will be thrown:

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

Reranking

Reranking operations may be faked by invoking the fake method on the Reranking class:

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

After reranking, you may make assertions about the operations that were performed:

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

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

Reranking::assertNothingReranked();

Files

File operations may be faked by invoking the fake method on the Files class:

use Laravel\Ai\Files;

Files::fake();

Once file operations have been faked, you may make assertions about the uploads and deletions that occurred:

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

For asserting against file deletions, you may pass a file ID:

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

Vector Stores

Vector store operations may be faked by invoking the fake method on the Stores class. Faking stores will also fake file operations automatically:

use Laravel\Ai\Stores;

Stores::fake();

Once store operations have been faked, you may make assertions about the stores that were created or deleted:

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

For asserting against store deletions, you may provide the store ID:

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

To assert files were added or removed from a store, use the assertion methods on a given Store instance:

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

If a file is stored in the provider's file storage and added to a vector store in the same request, you may not know the file's provider ID. In this case, you can pass a closure to the assertAdded method to assert against the content of the added file:

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

Events

The Laravel AI SDK dispatches a variety of events, including:

  • 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

You can listen to any of these events to log or store AI SDK usage information.

本文章首发在 LearnKu.com 网站上。

本文中的所有译文仅用于学习和交流目的,转载请务必注明文章译者、出处、和本文链接
我们的翻译工作遵照 CC 协议,如果我们的工作有侵犯到您的权益,请及时联系我们。

《L02 从零构建论坛系统》
以构建论坛项目 LaraBBS 为线索,展开对 Laravel 框架的全面学习。应用程序架构思路贴近 Laravel 框架的设计哲学。
《L05 电商实战》
从零开发一个电商项目,功能包括电商后台、商品 & SKU 管理、购物车、订单管理、支付宝支付、微信支付、订单退款流程、优惠券等
贡献者:3
讨论数量: 0
发起讨论 只看当前版本


暂无话题~