The Laravel AI SDK provides a unified, expressive API for interacting with AI providers such as OpenAI, Anthropic, Gemini, and more. With the AI SDK, you can build intelligent agents with tools and structured output, generate images, synthesize and transcribe audio, create vector embeddings, and much more — all using a consistent, Laravel-friendly interface.
Installation
You can install the Laravel AI SDK via Composer:
composer require laravel/aiNext, you should publish the AI SDK configuration and migration files using the vendor:publish Artisan command:
php artisan vendor:publish --provider="Laravel\Ai\AiServiceProvider"Finally, you should run your application's database migrations. This will create a agent_conversations and agent_conversation_messages table that the AI SDK uses to power its conversation storage:
php artisan migrateConfiguration
You may define your AI provider credentials in your application's config/ai.php configuration file or as environment variables in your application's .env file:
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=The default models used for text, images, audio, transcription, and embeddings may also be configured in your application's config/ai.php configuration file.
Custom Base URLs
By default, the Laravel AI SDK connects directly to each provider's public API endpoint. However, you may need to route requests through a different endpoint - for example, when using a proxy service to centralize API key management, implement rate limiting, or route traffic through a corporate gateway.
You may configure custom base URLs by adding a url parameter to your provider configuration:
'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'),
],
],This is useful when routing requests through a proxy service (such as LiteLLM or Azure OpenAI Gateway) or using alternative endpoints.
Custom base URLs are supported for the following providers: OpenAI, Anthropic, Gemini, Groq, Cohere, DeepSeek, xAI, and OpenRouter.
The Laravel\Ai\Enums\Lab enum may be used to reference providers throughout your code instead of using plain strings:
use Laravel\Ai\Enums\Lab;
Lab::Anthropic;
Lab::OpenAI;
Lab::Gemini;
// ...Agents
Agents are the fundamental building block for interacting with AI providers in the Laravel AI SDK. Each agent is a dedicated PHP class that encapsulates the instructions, conversation context, tools, and output schema needed to interact with a large language model. Think of an agent as a specialized assistant — a sales coach, a document analyzer, a support bot — that you configure once and prompt as needed throughout your application.
You can create an agent via the make:agent Artisan command:
php artisan make:agent SalesCoach
php artisan make:agent SalesCoach --structuredWithin the generated agent class, you can define the system prompt / instructions, message context, available tools, and output schema (if applicable):
<?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(),
];
}
}Prompting
To prompt an agent, first create an instance using the make method or standard instantiation, then call prompt:
$response = (new SalesCoach)
->prompt('Analyze this sales transcript...');
$response = SalesCoach::make()
->prompt('Analyze this sales transcript...');
return (string) $response;The make method resolves your agent from the container, allowing automatic dependency injection. You may also pass arguments to the agent's constructor:
$agent = SalesCoach::make(user: $user);By passing additional arguments to the prompt method, you may override the default provider, model, or HTTP timeout when prompting:
$response = (new SalesCoach)->prompt(
'Analyze this sales transcript...',
provider: Lab::Anthropic,
model: 'claude-haiku-4-5-20251001',
timeout: 120,
);Conversation Context
If your agent implements the Conversational interface, you may use the messages method to return the previous conversation context, if applicable:
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();
}Remembering Conversations
Before using the
RemembersConversationstrait, you should publish and run the AI SDK migrations using thevendor:publishArtisan command. These migrations will create the necessary database tables to store conversations.
If you would like Laravel to automatically store and retrieve conversation history for your agent, you may use the RemembersConversations trait. This trait provides a simple way to persist conversation messages to the database without manually implementing the Conversational interface:
<?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...';
}
}To start a new conversation for a user, call the forUser method before prompting:
$response = (new SalesCoach)->forUser($user)->prompt('Hello!');
$conversationId = $response->conversationId;The conversation ID is returned on the response and can be stored for future reference, or you can retrieve all of a user's conversations from the agent_conversations table directly.
To continue an existing conversation, use the continue method:
$response = (new SalesCoach)
->continue($conversationId, as: $user)
->prompt('Tell me more about that.');When using the RemembersConversations trait, previous messages are automatically loaded and included in the conversation context when prompting. New messages (both user and assistant) are automatically stored after each interaction.
Structured Output
If you would like your agent to return structured output, implement the HasStructuredOutput interface, which requires that your agent define a schema method:
<?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(),
];
}
}When prompting an agent that returns structured output, you can access the returned StructuredAgentResponse like an array:
$response = (new SalesCoach)->prompt('Analyze this sales transcript...');
return $response['score'];Attachments
When prompting, you may also pass attachments with the prompt to allow the model to inspect images and documents:
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...
]
);Likewise, the Laravel\Ai\Files\Image class may be used to attach images to a prompt:
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...
]
);Streaming
You may stream an agent's response by invoking the stream method. The returned StreamableAgentResponse may be returned from a route to automatically send a streaming response (SSE) to the client:
use App\Ai\Agents\SalesCoach;
Route::get('/coach', function () {
return (new SalesCoach)->stream('Analyze this sales transcript...');
});The then method may be used to provide a closure that will be invoked when the entire response has been streamed to the client:
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...
});
});Alternatively, you may iterate through the streamed events manually:
$stream = (new SalesCoach)->stream('Analyze this sales transcript...');
foreach ($stream as $event) {
// ...
}Streaming Using the Vercel AI SDK Protocol
You may stream the events using the Vercel AI SDK stream protocol by invoking the usingVercelDataProtocol method on the streamable response:
use App\Ai\Agents\SalesCoach;
Route::get('/coach', function () {
return (new SalesCoach)
->stream('Analyze this sales transcript...')
->usingVercelDataProtocol();
});Broadcasting
You may broadcast streamed events in a few different ways. First, you can simply invoke the broadcast or broadcastNow method on a streamed event:
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'));
}Or, you can invoke an agent's broadcastOnQueue method to queue the agent operation and broadcast the streamed events as they are available:
(new SalesCoach)->broadcastOnQueue(
'Analyze this sales transcript...'
new Channel('channel-name'),
);Queueing
Using an agent's queue method, you may prompt the agent, but allow it to process the response in the background, keeping your application feeling fast and responsive. The then and catch methods may be used to register closures that will be invoked when a response is available or if an exception occurs:
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();
});Tools
Tools may be used to give agents additional functionality that they can utilize while responding to prompts. Tools can be created using the make:tool Artisan command:
php artisan make:tool RandomNumberGeneratorThe generated tool will be placed in your application's app/Ai/Tools directory. Each tool contains a handle method that will be invoked by the agent when it needs to utilize the tool:
<?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(),
];
}
}Once you have defined your tool, you may return it from the tools method of any of your agents:
use App\Ai\Tools\RandomNumberGenerator;
/**
* Get the tools available to the agent.
*
* @return Tool[]
*/
public function tools(): iterable
{
return [
new RandomNumberGenerator,
];
}Similarity Search
The SimilaritySearch tool allows agents to search for documents similar to a given query using vector embeddings stored in your database. This is useful for retrieval-augmented generation (RAG) when you want to give agents access to search your application's data.
The simplest way to create a similarity search tool is using the usingModel method with an Eloquent model that has vector embeddings:
use App\Models\Document;
use Laravel\Ai\Tools\SimilaritySearch;
public function tools(): iterable
{
return [
SimilaritySearch::usingModel(Document::class, 'embedding'),
];
}The first argument is the Eloquent model class, and the second argument is the column containing the vector embeddings.
You may also provide a minimum similarity threshold between 0.0 and 1.0 and a closure to customize the query:
SimilaritySearch::usingModel(
model: Document::class,
column: 'embedding',
minSimilarity: 0.7,
limit: 10,
query: fn ($query) => $query->where('published', true),
),For more control, you may create a similarity search tool with a custom closure that returns the search results:
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();
}),
];
}You may customize the tool's description using the withDescription method:
SimilaritySearch::usingModel(Document::class, 'embedding')
->withDescription('Search the knowledge base for relevant articles.'),Provider Tools
Provider tools are special tools implemented natively by AI providers, offering capabilities like web searching, URL fetching, and file searching. Unlike regular tools, provider tools are executed by the provider itself rather than your application.
Provider tools can be returned by your agent's tools method.
Web Search
The WebSearch provider tool allows agents to search the web for real-time information. This is useful for answering questions about current events, recent data, or topics that may have changed since the model's training cutoff.
Supported Providers: Anthropic, OpenAI, Gemini
use Laravel\Ai\Providers\Tools\WebSearch;
public function tools(): iterable
{
return [
new WebSearch,
];
}You may configure the web search tool to limit the number of searches or restrict results to specific domains:
(new WebSearch)->max(5)->allow(['laravel.com', 'php.net']),To refine search results based on user location, use the location method:
(new WebSearch)->location(
city: 'New York',
region: 'NY',
country: 'US'
);Web Fetch
The WebFetch provider tool allows agents to fetch and read the contents of web pages. This is useful when you need the agent to analyze specific URLs or retrieve detailed information from known web pages.
Supported providers: Anthropic, Gemini
use Laravel\Ai\Providers\Tools\WebFetch;
public function tools(): iterable
{
return [
new WebFetch,
];
}You may configure the web fetch tool to limit the number of fetches or restrict to specific domains:
(new WebFetch)->max(3)->allow(['docs.laravel.com']),File Search
The FileSearch provider tool allows agents to search through files stored in vector stores. This enables retrieval-augmented generation (RAG) by allowing the agent to search your uploaded documents for relevant information.
Supported providers: OpenAI, Gemini
use Laravel\Ai\Providers\Tools\FileSearch;
public function tools(): iterable
{
return [
new FileSearch(stores: ['store_id']),
];
}You may provide multiple vector store IDs to search across multiple stores:
new FileSearch(stores: ['store_1', 'store_2']);If your files have metadata, you may filter the search results by providing a where argument. For simple equality filters, pass an array:
new FileSearch(stores: ['store_id'], where: [
'author' => 'Taylor Otwell',
'year' => 2026,
]);For more complex filters, you may pass a closure that receives a FileSearchQuery instance:
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'])
);Middleware
Agents support middleware, allowing you to intercept and modify prompts before they are sent to the provider. To add middleware to an agent, implement the HasMiddleware interface and define a middleware method that returns an array of middleware classes:
<?php
namespace App\Ai\Agents;
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,
];
}
}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).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\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;
// ...
}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)...
}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;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.
Vector queries are currently only supported on PostgreSQL connections using the
pgvectorextension.
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);








