Laravel AI SDK
13.x.
Почему это важно?
- Введение
- Установка
- Конфигурирование
- Пользовательские базовые URL
- Поддержка провайдеров
- Агенты
- Запросы к агентам
- Контекст разговора
- Структурированный вывод
- Вложения
- Потоковая передача
- Broadcasting
- Очереди
- Инструменты
- Инструменты провайдеров
- Middleware
- Анонимные агенты
- Конфигурация агента
- Опции провайдера
- Изображения
- Аудио
- Транскрипции
- Embeddings
- Запросы по embeddings
- Кеширование embeddings
- Реранжирование
- Реранжирование коллекций
- Файлы
- Использование сохраненных файлов в разговорах
- Vector stores
- Добавление файлов в хранилища
- Failover
- Тестирование
- Агенты
- Изображения
- Аудио
- Транскрипции
- Embeddings
- Реранжирование
- Файлы
- Vector stores
- События
Введение
Laravel AI SDK предоставляет единый выразительный API для работы с AI-провайдерами, такими как OpenAI, Anthropic, Gemini и другими. С AI SDK можно создавать интеллектуальных агентов с инструментами и структурированным выводом, генерировать изображения, синтезировать и транскрибировать аудио, создавать vector embeddings и многое другое через последовательный Laravel-friendly интерфейс.
Установка
Установите Laravel AI SDK через Composer:
composer require laravel/ai
Затем опубликуйте конфигурацию и миграции AI SDK с помощью Artisan-команды vendor:publish:
php artisan vendor:publish --provider="Laravel\Ai\AiServiceProvider"
После этого выполните миграции приложения. Они создадут таблицы agent_conversations и agent_conversation_messages, которые AI SDK использует для хранения разговоров:
php artisan migrate
Конфигурирование
Учетные данные AI-провайдеров можно определить в файле config/ai.php или через переменные окружения в .env:
ANTHROPIC_API_KEY=
COHERE_API_KEY=
ELEVENLABS_API_KEY=
GEMINI_API_KEY=
MISTRAL_API_KEY=
OLLAMA_API_KEY=
OPENAI_API_KEY=
JINA_API_KEY=
VOYAGEAI_API_KEY=
XAI_API_KEY=
Модели по умолчанию для текста, изображений, аудио, транскрипций и embeddings также настраиваются в config/ai.php.
Пользовательские базовые URL
По умолчанию Laravel AI SDK подключается напрямую к публичному API endpoint каждого провайдера. Иногда запросы нужно направлять через другой endpoint, например через proxy-сервис для централизованного управления API-ключами, rate limiting или корпоративный gateway.
Пользовательские базовые 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'),
],
],
Это полезно при маршрутизации запросов через proxy-сервисы вроде LiteLLM или Azure OpenAI Gateway, а также при использовании альтернативных endpoints. Пользовательские URL поддерживаются для OpenAI, Anthropic, Gemini, Groq, Cohere, DeepSeek, xAI и OpenRouter.
Поддержка провайдеров
AI SDK поддерживает разных провайдеров для разных возможностей:
| Возможность | Провайдеры |
|---|---|
| Text | OpenAI, Anthropic, Gemini, Azure, Groq, xAI, DeepSeek, Mistral, Ollama |
| Images | OpenAI, Gemini, xAI |
| TTS | OpenAI, ElevenLabs |
| STT | OpenAI, ElevenLabs, Mistral |
| Embeddings | OpenAI, Gemini, Azure, Cohere, Mistral, Jina, VoyageAI |
| Reranking | Cohere, Jina |
| Files | OpenAI, Anthropic, Gemini |
Enum Laravel\Ai\Enums\Lab можно использовать для ссылки на провайдеров в коде вместо строк:
use Laravel\Ai\Enums\Lab;
Lab::Anthropic;
Lab::OpenAI;
Lab::Gemini;
// ...
Агенты
Агенты – основной строительный блок для взаимодействия с AI-провайдерами в Laravel AI SDK. Каждый агент является отдельным PHP-классом, инкапсулирующим инструкции, контекст разговора, инструменты и схему вывода, необходимые для работы с большой языковой моделью. Думайте об агенте как о специализированном помощнике: sales coach, анализатор документов, support bot, которого вы настраиваете один раз и затем вызываете по мере необходимости.
Агента можно создать Artisan-командой make:agent:
php artisan make:agent SalesCoach
php artisan make:agent SalesCoach --structured
В сгенерированном классе можно определить system prompt / instructions, контекст сообщений, доступные tools и схему вывода:
<?php
namespace App\Ai\Agents;
use App\Ai\Tools\RetrievePreviousTranscripts;
use App\Models\History;
use App\Models\User;
use Illuminate\Contracts\JsonSchema\JsonSchema;
use Laravel\Ai\Contracts\Agent;
use Laravel\Ai\Contracts\Conversational;
use Laravel\Ai\Contracts\HasStructuredOutput;
use Laravel\Ai\Contracts\HasTools;
use Laravel\Ai\Messages\Message;
use Laravel\Ai\Promptable;
use Stringable;
class SalesCoach implements Agent, Conversational, HasTools, HasStructuredOutput
{
use Promptable;
public function __construct(public User $user) {}
public function instructions(): Stringable|string
{
return '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();
}
public function tools(): iterable
{
return [
new RetrievePreviousTranscripts,
];
}
public function schema(JsonSchema $schema): array
{
return [
'feedback' => $schema->string()->required(),
'score' => $schema->integer()->min(1)->max(10)->required(),
];
}
}
Запросы к агентам
Чтобы обратиться к агенту, создайте экземпляр через make или обычный конструктор, затем вызовите prompt:
$response = (new SalesCoach)
->prompt('Analyze this sales transcript...');
return (string) $response;
Метод make разрешает агента из контейнера, поэтому работает автоматический dependency injection. В конструктор агента также можно передавать аргументы:
$agent = SalesCoach::make(user: $user);
Передав дополнительные аргументы в prompt, можно переопределить provider, model или HTTP timeout:
$response = (new SalesCoach)->prompt(
'Analyze this sales transcript...',
provider: Lab::Anthropic,
model: 'claude-haiku-4-5-20251001',
timeout: 120,
);
Контекст разговора
Если агент реализует интерфейс 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опубликуйте и выполните миграции AI SDK черезvendor:publish, чтобы создать таблицы для хранения разговоров.
Если вы хотите, чтобы Laravel автоматически сохранял и извлекал историю разговоров агента, используйте трейт RemembersConversations. Он позволяет сохранять сообщения в базе данных без ручной реализации Conversational:
use Laravel\Ai\Concerns\RemembersConversations;
use Laravel\Ai\Contracts\Agent;
use Laravel\Ai\Contracts\Conversational;
use Laravel\Ai\Promptable;
class SalesCoach implements Agent, Conversational
{
use Promptable, RemembersConversations;
public function instructions(): string
{
return 'You are a sales coach...';
}
}
Чтобы начать новый разговор для пользователя, вызовите forUser перед prompt:
$response = (new SalesCoach)->forUser($user)->prompt('Hello!');
$conversationId = $response->conversationId;
ID разговора возвращается в response и может быть сохранен для дальнейшего использования. Продолжить существующий разговор можно методом continue:
$response = (new SalesCoach)
->continue($conversationId, as: $user)
->prompt('Tell me more about that.');
При использовании RemembersConversations предыдущие сообщения автоматически загружаются и включаются в контекст при prompt. Новые сообщения пользователя и ассистента сохраняются после каждого взаимодействия.
Структурированный вывод
Если агент должен возвращать структурированный вывод, реализуйте интерфейс HasStructuredOutput и определите метод schema:
use Illuminate\Contracts\JsonSchema\JsonSchema;
use Laravel\Ai\Contracts\Agent;
use Laravel\Ai\Contracts\HasStructuredOutput;
use Laravel\Ai\Promptable;
class SalesCoach implements Agent, HasStructuredOutput
{
use Promptable;
public function schema(JsonSchema $schema): array
{
return [
'score' => $schema->integer()->required(),
];
}
}
Ответ StructuredAgentResponse можно читать как массив:
$response = (new SalesCoach)->prompt('Analyze this sales transcript...');
return $response['score'];
Вложения
При prompt можно передать вложения, чтобы модель могла анализировать изображения и документы:
use App\Ai\Agents\SalesCoach;
use Laravel\Ai\Files;
$response = (new SalesCoach)->prompt(
'Analyze the attached sales transcript...',
attachments: [
Files\Document::fromStorage('transcript.pdf'),
Files\Document::fromPath('/home/laravel/transcript.md'),
$request->file('transcript'),
]
);
Класс Laravel\Ai\Files\Image используется для прикрепления изображений:
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. Возвращаемый StreamableAgentResponse можно вернуть из маршрута, чтобы автоматически отправить клиенту streaming response (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...
});
});
Также можно вручную итерироваться по streamed events:
$stream = (new SalesCoach)->stream('Analyze this sales transcript...');
foreach ($stream as $event) {
// ...
}
Потоковая передача через протокол Vercel AI SDK
Чтобы передавать события через stream protocol Vercel AI SDK, вызовите usingVercelDataProtocol:
use App\Ai\Agents\SalesCoach;
Route::get('/coach', function () {
return (new SalesCoach)
->stream('Analyze this sales transcript...')
->usingVercelDataProtocol();
});
Broadcasting
Streamed events можно транслировать несколькими способами. Например, вызвать broadcast или broadcastNow на 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'));
}
Или вызвать broadcastOnQueue, чтобы поставить операцию агента в очередь и транслировать streamed events по мере готовности:
(new SalesCoach)->broadcastOnQueue(
'Analyze this sales transcript...'
new Channel('channel-name'),
);
Очереди
Метод queue позволяет отправить prompt агенту и обработать ответ в фоне, сохраняя приложение быстрым и отзывчивым. Методы then и catch регистрируют callbacks, которые вызываются при готовом ответе или исключении:
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 дают агентам дополнительные возможности, которыми они могут пользоваться при ответе. Tool создается Artisan-командой make:tool:
php artisan make:tool RandomNumberGenerator
Сгенерированный tool будет размещен в app/Ai/Tools. Каждый tool содержит метод handle, вызываемый агентом при необходимости:
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(),
];
}
}
Tool возвращается из метода tools любого агента:
use App\Ai\Tools\RandomNumberGenerator;
public function tools(): iterable
{
return [
new RandomNumberGenerator,
];
}
Similarity Search
Tool SimilaritySearch позволяет агентам искать документы, похожие на запрос, используя vector embeddings из базы данных. Это удобно для retrieval-augmented generation (RAG), когда агенту нужен доступ к данным приложения.
Самый простой способ создать similarity search tool – метод usingModel с Eloquent-моделью, имеющей vector embeddings:
use App\Models\Document;
use Laravel\Ai\Tools\SimilaritySearch;
public function tools(): iterable
{
return [
SimilaritySearch::usingModel(Document::class, 'embedding'),
];
}
Первый аргумент – класс Eloquent-модели, второй – столбец с vector embeddings. Можно указать минимальный порог сходства, лимит и замыкание для настройки запроса:
SimilaritySearch::usingModel(
model: Document::class,
column: 'embedding',
minSimilarity: 0.7,
limit: 10,
query: fn ($query) => $query->where('published', true),
),
Для полного контроля создайте tool с пользовательским замыканием:
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();
}),
];
}
Описание tool можно настроить методом withDescription:
SimilaritySearch::usingModel(Document::class, 'embedding')
->withDescription('Search the knowledge base for relevant articles.'),
Инструменты провайдеров
Provider tools – специальные инструменты, нативно реализованные AI-провайдерами. Они дают возможности вроде web search, URL fetching и file search. В отличие от обычных tools, provider tools выполняются самим провайдером, а не вашим приложением, и возвращаются из метода tools агента.
Web Search
Provider tool WebSearch позволяет агентам искать в интернете актуальную информацию. Это полезно для вопросов о текущих событиях, свежих данных или темах, которые могли измениться после даты обучения модели.
Поддерживаемые провайдеры: 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'
);
Web Fetch
Provider tool WebFetch позволяет агентам получать и читать содержимое веб-страниц. Это полезно, когда агент должен анализировать конкретные URL или получить подробную информацию с известных страниц.
Поддерживаемые провайдеры: Anthropic, Gemini
use Laravel\Ai\Providers\Tools\WebFetch;
public function tools(): iterable
{
return [
new WebFetch,
];
}
Количество fetch-запросов и домены можно ограничить:
(new WebFetch)->max(3)->allow(['docs.laravel.com']),
File Search
Provider tool FileSearch позволяет агентам искать по файлам, сохраненным в vector stores. Это включает RAG-сценарии, где агент ищет релевантную информацию в загруженных документах.
Поддерживаемые провайдеры: OpenAI, Gemini
use Laravel\Ai\Providers\Tools\FileSearch;
public function tools(): iterable
{
return [
new FileSearch(stores: ['store_id']),
];
}
Можно указать несколько vector store IDs:
new FileSearch(stores: ['store_1', 'store_2']);
Если у файлов есть metadata, результаты можно фильтровать через аргумент 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'])
);
Middleware
Агенты поддерживают middleware, позволяя перехватывать и изменять prompts до отправки провайдеру. Middleware создается командой make:agent-middleware:
php artisan make:agent-middleware LogPrompts
Чтобы добавить middleware к агенту, реализуйте HasMiddleware и верните список middleware:
use App\Ai\Middleware\LogPrompts;
use Laravel\Ai\Contracts\Agent;
use Laravel\Ai\Contracts\HasMiddleware;
use Laravel\Ai\Promptable;
class SalesCoach implements Agent, HasMiddleware
{
use Promptable;
public function middleware(): array
{
return [
new LogPrompts,
];
}
}
Middleware-класс определяет метод handle, получающий AgentPrompt и Closure для передачи запроса дальше:
use Closure;
use Laravel\Ai\Prompts\AgentPrompt;
class LogPrompts
{
public function handle(AgentPrompt $prompt, Closure $next)
{
Log::info('Prompting agent', ['prompt' => $prompt->prompt]);
return $next($prompt);
}
}
Метод then на response позволяет выполнить код после завершения обработки, как для синхронных, так и для streaming responses:
public function handle(AgentPrompt $prompt, Closure $next)
{
return $next($prompt)->then(function (AgentResponse $response) {
Log::info('Agent responded', ['text' => $response->text]);
});
}
Анонимные агенты
Иногда нужно быстро обратиться к модели без отдельного класса агента. Для этого используйте функцию agent:
use function Laravel\Ai\{agent};
$response = agent(
instructions: 'You are an expert at software development.',
messages: [],
tools: [],
)->prompt('Tell me about Laravel')
Анонимные агенты также могут возвращать структурированный вывод:
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: максимальное количество шагов агента при использовании tools.MaxTokens: максимальное количество tokens, которое может сгенерировать модель.Model: модель агента.Provider: AI-провайдер или список провайдеров для failover.Temperature: sampling temperature для генерации (от0.0до1.0).Timeout: HTTP timeout в секундах (по умолчанию 60).UseCheapestModel: использовать самую дешевую text-модель провайдера.UseSmartestModel: использовать самую мощную text-модель провайдера.
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;
}
Атрибуты UseCheapestModel и UseSmartestModel автоматически выбирают наиболее экономичную или мощную модель без явного имени модели:
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;
}
#[UseSmartestModel]
class ComplexReasoner implements Agent
{
use Promptable;
}
Вместо атрибутов Provider, Model и Timeout можно определить методы provider, model и timeout, которые вычисляют значения во время выполнения. То же относится к maxSteps, maxTokens и temperature. Если для одной опции определены и метод, и атрибут, метод имеет приоритет:
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);
}
}
Опции провайдера
Если агенту нужно передать provider-specific опции, например OpenAI reasoning effort или penalty settings, реализуйте HasProviderOptions и метод providerOptions:
use Laravel\Ai\Contracts\Agent;
use Laravel\Ai\Contracts\HasProviderOptions;
use Laravel\Ai\Enums\Lab;
use Laravel\Ai\Promptable;
class SalesCoach implements Agent, HasProviderOptions
{
use Promptable;
public function providerOptions(Lab|string $provider): array
{
return match ($provider) {
Lab::OpenAI => [
'reasoning' => ['effort' => 'low'],
'frequency_penalty' => 0.5,
'presence_penalty' => 0.3,
],
Lab::Anthropic => [
'thinking' => ['budget_tokens' => 1024],
],
default => [],
};
}
}
Метод получает текущего провайдера (Lab enum или строку), поэтому можно возвращать разные опции для каждого провайдера. Это особенно полезно с failover, где каждый fallback-провайдер может иметь свою конфигурацию.
Изображения
Класс Laravel\Ai\Image используется для генерации изображений через провайдеры openai, gemini или xai:
use Laravel\Ai\Image;
$image = Image::of('A donut sitting on the kitchen counter')->generate();
$rawContent = (string) $image;
Методы square, portrait и landscape управляют aspect ratio, quality задает желаемое качество (high, medium, low), а timeout задает HTTP timeout:
$image = Image::of('A donut sitting on the kitchen counter')
->quality('high')
->landscape()
->timeout(120)
->generate();
Можно прикреплять reference images:
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();
Сгенерированные изображения легко сохранить на default disk из config/filesystems.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');
Генерацию изображений можно поставить в очередь:
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 генерирует аудио из текста:
use Laravel\Ai\Audio;
$audio = Audio::of('I love coding with Laravel.')->generate();
$rawContent = (string) $audio;
Методы male, female и voice задают голос:
$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 позволяет динамически подсказать модели, как должно звучать аудио:
$audio = Audio::of('I love coding with Laravel.')
->female()
->instructions('Said like a pirate')
->generate();
Аудио можно сохранить на default disk и генерировать через очередь:
$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');
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 создает transcript для аудио:
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 добавляет diarized transcript, сегментированный по говорящим:
$transcript = Transcription::fromStorage('audio.mp3')
->diarize()
->generate();
Транскрипцию также можно поставить в очередь:
use Laravel\Ai\Transcription;
use Laravel\Ai\Responses\TranscriptionResponse;
Transcription::fromStorage('audio.mp3')
->queue()
->then(function (TranscriptionResponse $transcript) {
// ...
});
Embeddings
Vector embeddings для строки можно сгенерировать через метод toEmbeddings, доступный в Stringable:
use Illuminate\Support\Str;
$embeddings = Str::of('Napa Valley has great wine.')->toEmbeddings();
Для нескольких входов используйте класс Embeddings:
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, ...]]
Можно указать размеры и провайдера:
$response = Embeddings::for(['Napa Valley has great wine.'])
->dimensions(1536)
->generate(Lab::OpenAI, 'text-embedding-3-small');
Запросы по embeddings
Обычно embeddings сохраняются в vector-столбце базы данных для последующих запросов. Laravel поддерживает vector-столбцы PostgreSQL через расширение pgvector:
Schema::ensureVectorExtensionExists();
Schema::create('documents', function (Blueprint $table) {
$table->id();
$table->string('title');
$table->text('content');
$table->vector('embedding', dimensions: 1536);
$table->timestamps();
});
Для ускорения similarity search добавьте vector index. При вызове index Laravel создаст HNSW-индекс с cosine distance:
$table->vector('embedding', dimensions: 1536)->index();
В Eloquent-модели приведите vector-столбец к array:
protected function casts(): array
{
return [
'embedding' => 'array',
];
}
Для поиска похожих записей используйте whereVectorSimilarTo. Метод фильтрует результаты по минимальному cosine similarity (от 0.0 до 1.0) и сортирует по сходству:
use App\Models\Document;
$documents = Document::query()
->whereVectorSimilarTo('embedding', $queryEmbedding, minSimilarity: 0.4)
->limit(10)
->get();
$queryEmbedding может быть массивом floats или обычной строкой. Если передана строка, Laravel автоматически сгенерирует embeddings:
$documents = Document::query()
->whereVectorSimilarTo('embedding', 'best wineries in Napa Valley')
->limit(10)
->get();
Для низкоуровневого контроля используйте whereVectorDistanceLessThan, selectVectorDistance и orderByVectorDistance:
$documents = Document::query()
->select('*')
->selectVectorDistance('embedding', $queryEmbedding, as: 'distance')
->whereVectorDistanceLessThan('embedding', $queryEmbedding, maxDistance: 0.3)
->orderByVectorDistance('embedding', $queryEmbedding)
->limit(10)
->get();
Если агенту нужен similarity search как tool, смотрите раздел Similarity Search.
Vector-запросы сейчас поддерживаются только на PostgreSQL-соединениях с расширением
pgvector.
Кеширование embeddings
Генерацию embeddings можно кешировать, чтобы избежать повторных API-вызовов для одинаковых входов. Для включения кеша установите ai.caching.embeddings.cache в true:
'caching' => [
'embeddings' => [
'cache' => true,
'store' => env('CACHE_STORE', 'database'),
// ...
],
],
При включенном кеше embeddings хранятся 30 дней. Cache key строится из provider, model, dimensions и input content, поэтому одинаковые запросы получают cached results, а разные конфигурации генерируют новые embeddings.
Кеш можно включить для конкретного запроса методом cache, даже если глобально он отключен:
$response = Embeddings::for(['Napa Valley has great wine.'])
->cache()
->generate();
Можно указать duration в секундах:
$response = Embeddings::for(['Napa Valley has great wine.'])
->cache(seconds: 3600)
->generate();
Метод toEmbeddings также принимает аргумент cache:
$embeddings = Str::of('Napa Valley has great wine.')->toEmbeddings(cache: true);
$embeddings = Str::of('Napa Valley has great wine.')->toEmbeddings(cache: 3600);
Реранжирование
Reranking позволяет переупорядочить список документов по релевантности заданному запросу. Это полезно для улучшения результатов поиска через семантическое понимание:
use Laravel\Ai\Reranking;
$response = Reranking::of([
'Django is a Python web framework.',
'Laravel is a PHP web application framework.',
'React is a JavaScript library for building user interfaces.',
])->rerank('PHP frameworks');
$response->first()->document; // "Laravel is a PHP web application framework."
$response->first()->score; // 0.95
$response->first()->index; // 1 (original position)
Метод limit ограничивает количество возвращаемых результатов:
$response = Reranking::of($documents)
->limit(5)
->rerank('search query');
Реранжирование коллекций
Для удобства коллекции Laravel можно rerank через macro rerank. Первый аргумент задает поле или поля, второй – запрос:
$posts = Post::all()
->rerank('body', 'Laravel tutorials');
$reranked = $posts->rerank(['title', 'body'], 'Laravel tutorials');
$reranked = $posts->rerank(
fn ($post) => $post->title.': '.$post->body,
'Laravel tutorials'
);
Также можно ограничить количество результатов и указать provider:
$reranked = $posts->rerank(
by: 'content',
query: 'Laravel tutorials',
limit: 10,
provider: Lab::Cohere
);
Файлы
Класс Laravel\Ai\Files или отдельные file-классы позволяют сохранять файлы у AI-провайдера для последующего использования в разговорах. Это полезно для больших документов или файлов, на которые нужно ссылаться многократно без повторной загрузки:
use Laravel\Ai\Files\Document;
use Laravel\Ai\Files\Image;
$response = Document::fromPath('/home/laravel/document.pdf')->put();
$response = Image::fromPath('/home/laravel/photo.jpg')->put();
$response = Document::fromStorage('document.pdf', disk: 'local')->put();
$response = Image::fromStorage('photo.jpg', disk: 'local')->put();
$response = Document::fromUrl('https://example.com/document.pdf')->put();
$response = Image::fromUrl('https://example.com/photo.jpg')->put();
return $response->id;
Также можно сохранять raw content или uploaded files:
use Laravel\Ai\Files;
use Laravel\Ai\Files\Document;
$stored = Document::fromString('Hello, World!', 'text/plain')->put();
$stored = Document::fromUpload($request->file('document'))->put();
После сохранения файла можно ссылаться на него при генерации текста через agents, не загружая файл повторно:
use App\Ai\Agents\SalesCoach;
use Laravel\Ai\Files;
$response = (new SalesCoach)->prompt(
'Analyze the attached sales transcript...'
attachments: [
Files\Document::fromId('file-id')
]
);
Чтобы получить ранее сохраненный файл, используйте метод get у экземпляра файла:
use Laravel\Ai\Files\Document;
$file = Document::fromId('file-id')->get();
$file->id;
$file->mimeType();
Чтобы удалить файл у провайдера, используйте метод delete:
Document::fromId('file-id')->delete();
По умолчанию Files использует default AI provider из config/ai.php. Для большинства операций можно указать другого provider:
$response = Document::fromPath(
'/home/laravel/document.pdf'
)->put(provider: Lab::Anthropic);
Использование сохраненных файлов в разговорах
После сохранения файла у провайдера можно ссылаться на него в agent conversations через fromId у Document или Image:
use App\Ai\Agents\DocumentAnalyzer;
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),
],
);
Сохраненные изображения подключаются аналогично:
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 позволяют создавать searchable collections файлов для retrieval-augmented generation (RAG). Класс Laravel\Ai\Stores предоставляет методы для создания, получения и удаления vector stores:
use Laravel\Ai\Stores;
$store = Stores::create('Knowledge Base');
$store = Stores::create(
name: 'Knowledge Base',
description: 'Documentation and reference materials.',
expiresWhenIdleFor: days(30),
);
return $store->id;
Получить существующее хранилище можно методом get:
use Laravel\Ai\Stores;
$store = Stores::get('store_id');
$store->id;
$store->name;
$store->fileCounts;
$store->ready;
Удалить vector store можно через Stores::delete или экземпляр store:
use Laravel\Ai\Stores;
Stores::delete('store_id');
$store = Stores::get('store_id');
$store->delete();
Добавление файлов в хранилища
После создания vector store добавьте в него файлы методом add. Файлы автоматически индексируются для семантического поиска через file search provider tool:
use Laravel\Ai\Files\Document;
use Laravel\Ai\Stores;
$store = Stores::get('store_id');
$document = $store->add('file_id');
$document = $store->add(Document::fromId('file_id'));
$document = $store->add(Document::fromPath('/path/to/document.pdf'));
$document = $store->add(Document::fromStorage('manual.pdf'));
$document = $store->add($request->file('document'));
$document->id;
$document->fileId;
Note: Обычно при добавлении ранее сохраненного файла в vector store возвращаемый document ID совпадает с ID файла, но некоторые провайдеры могут вернуть новый “document ID”. Поэтому рекомендуется хранить оба ID в базе данных.
К файлам можно добавить metadata, чтобы затем фильтровать результаты при использовании file search provider tool:
$store->add(Document::fromPath('/path/to/document.pdf'), metadata: [
'author' => 'Taylor Otwell',
'department' => 'Engineering',
'year' => 2026,
]);
Удалить файл из store можно методом remove:
$store->remove('file_id');
Удаление файла из vector store не удаляет его из file storage провайдера. Чтобы удалить файл и из store, и из file storage, используйте deleteFile:
$store->remove('file_abc123', deleteFile: true);
Failover
При prompt или генерации медиа можно передать массив providers / models, чтобы автоматически переключаться на резервного провайдера / модель при сбое сервиса или rate limit основного провайдера:
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]);
Тестирование
Агенты
Чтобы подменить ответы агента в тестах, вызовите fake на классе агента. Можно передать список ответов или замыкание:
use App\Ai\Agents\SalesCoach;
use Laravel\Ai\Prompts\AgentPrompt;
SalesCoach::fake();
SalesCoach::fake([
'First response',
'Second response',
]);
SalesCoach::fake(function (AgentPrompt $prompt) {
return 'Response for: '.$prompt->prompt;
});
Note: Если
Agent::fake()вызван для агента со structured output, Laravel автоматически сгенерирует fake data, соответствующие схеме вывода.
После prompt можно утверждать, какие prompts были получены:
use Laravel\Ai\Prompts\AgentPrompt;
SalesCoach::assertPrompted('Analyze this...');
SalesCoach::assertPrompted(function (AgentPrompt $prompt) {
return $prompt->contains('Analyze');
});
SalesCoach::assertNotPrompted('Missing prompt');
SalesCoach::assertNeverPrompted();
Для queued invocations используйте queued assertions:
use Laravel\Ai\QueuedAgentPrompt;
SalesCoach::assertQueued('Analyze this...');
SalesCoach::assertQueued(function (QueuedAgentPrompt $prompt) {
return $prompt->contains('Analyze');
});
SalesCoach::assertNotQueued('Missing prompt');
SalesCoach::assertNeverQueued();
Метод preventStrayPrompts гарантирует, что для каждого обращения к агенту есть fake response:
SalesCoach::fake()->preventStrayPrompts();
Изображения
Генерацию изображений можно подменить методом fake класса Image, после чего доступны assertions по записанным prompts:
use Laravel\Ai\Image;
use Laravel\Ai\Prompts\ImagePrompt;
use Laravel\Ai\Prompts\QueuedImagePrompt;
Image::fake();
Image::fake([
base64_encode($firstImage),
base64_encode($secondImage),
]);
Image::fake(function (ImagePrompt $prompt) {
return base64_encode('...');
});
Image::assertGenerated(function (ImagePrompt $prompt) {
return $prompt->contains('sunset') && $prompt->isLandscape();
});
Image::assertNotGenerated('Missing prompt');
Image::assertNothingGenerated();
Для queued image generations используйте queued assertion methods:
Image::assertQueued(
fn (QueuedImagePrompt $prompt) => $prompt->contains('sunset')
);
Image::assertNotQueued('Missing prompt');
Image::assertNothingQueued();
Чтобы убедиться, что для каждой генерации изображения есть соответствующий fake response, используйте preventStrayImages. Если изображение будет сгенерировано без заданного fake response, будет выброшено исключение:
Image::fake()->preventStrayImages();
Аудио
Аудиогенерацию можно подменить методом fake класса Audio:
use Laravel\Ai\Audio;
use Laravel\Ai\Prompts\AudioPrompt;
use Laravel\Ai\Prompts\QueuedAudioPrompt;
Audio::fake();
Audio::fake([
base64_encode($firstAudio),
base64_encode($secondAudio),
]);
Audio::fake(function (AudioPrompt $prompt) {
return base64_encode('...');
});
Audio::assertGenerated(function (AudioPrompt $prompt) {
return $prompt->contains('Hello') && $prompt->isFemale();
});
Audio::assertNotGenerated('Missing prompt');
Audio::assertNothingGenerated();
Для queued audio generations используйте queued assertion methods:
Audio::assertQueued(
fn (QueuedAudioPrompt $prompt) => $prompt->contains('Hello')
);
Audio::assertNotQueued('Missing prompt');
Audio::assertNothingQueued();
Чтобы убедиться, что для каждой генерации аудио есть соответствующий fake response, используйте preventStrayAudio. Если аудио будет сгенерировано без заданного fake response, будет выброшено исключение:
Audio::fake()->preventStrayAudio();
Транскрипции
Транскрипции можно подменить методом fake класса Transcription:
use Laravel\Ai\Transcription;
use Laravel\Ai\Prompts\TranscriptionPrompt;
use Laravel\Ai\Prompts\QueuedTranscriptionPrompt;
Transcription::fake();
Transcription::fake([
'First transcription text.',
'Second transcription text.',
]);
Transcription::fake(function (TranscriptionPrompt $prompt) {
return 'Transcribed text...';
});
Transcription::assertGenerated(function (TranscriptionPrompt $prompt) {
return $prompt->language === 'en' && $prompt->isDiarized();
});
Transcription::assertNotGenerated(
fn (TranscriptionPrompt $prompt) => $prompt->language === 'fr'
);
Transcription::assertNothingGenerated();
Для queued transcriptions используйте queued assertion methods:
Transcription::assertQueued(
fn (QueuedTranscriptionPrompt $prompt) => $prompt->isDiarized()
);
Transcription::assertNotQueued(
fn (QueuedTranscriptionPrompt $prompt) => $prompt->language === 'fr'
);
Transcription::assertNothingQueued();
Чтобы убедиться, что для каждой транскрипции есть соответствующий fake response, используйте preventStrayTranscriptions. Если транскрипция будет создана без заданного fake response, будет выброшено исключение:
Transcription::fake()->preventStrayTranscriptions();
Embeddings
Генерацию embeddings можно подменить методом fake класса Embeddings:
use Laravel\Ai\Embeddings;
use Laravel\Ai\Prompts\EmbeddingsPrompt;
use Laravel\Ai\Prompts\QueuedEmbeddingsPrompt;
Embeddings::fake();
Embeddings::fake([
[$firstEmbeddingVector],
[$secondEmbeddingVector],
]);
Embeddings::fake(function (EmbeddingsPrompt $prompt) {
return array_map(
fn () => Embeddings::fakeEmbedding($prompt->dimensions),
$prompt->inputs
);
});
Embeddings::assertGenerated(function (EmbeddingsPrompt $prompt) {
return $prompt->contains('Laravel') && $prompt->dimensions === 1536;
});
Embeddings::assertNotGenerated(
fn (EmbeddingsPrompt $prompt) => $prompt->contains('Other')
);
Embeddings::assertNothingGenerated();
Для queued embeddings используйте queued assertion methods:
Embeddings::assertQueued(
fn (QueuedEmbeddingsPrompt $prompt) => $prompt->contains('Laravel')
);
Embeddings::assertNotQueued(
fn (QueuedEmbeddingsPrompt $prompt) => $prompt->contains('Other')
);
Embeddings::assertNothingQueued();
Чтобы убедиться, что для каждой генерации embeddings есть соответствующий fake response, используйте preventStrayEmbeddings. Если embeddings будут сгенерированы без заданного fake response, будет выброшено исключение:
Embeddings::fake()->preventStrayEmbeddings();
Реранжирование
Операции reranking можно подменить методом fake класса Reranking:
use Laravel\Ai\Reranking;
use Laravel\Ai\Prompts\RerankingPrompt;
use Laravel\Ai\Responses\Data\RankedDocument;
Reranking::fake();
Reranking::fake([
[
new RankedDocument(index: 0, document: 'First', score: 0.95),
new RankedDocument(index: 1, document: 'Second', score: 0.80),
],
]);
Reranking::assertReranked(function (RerankingPrompt $prompt) {
return $prompt->contains('Laravel') && $prompt->limit === 5;
});
Reranking::assertNotReranked(
fn (RerankingPrompt $prompt) => $prompt->contains('Django')
);
Reranking::assertNothingReranked();
Файлы
File operations можно подменить методом fake класса Files:
use Laravel\Ai\Files;
Files::fake();
После этого можно проверять uploads и deletions:
use Laravel\Ai\Contracts\Files\StorableFile;
use Laravel\Ai\Files\Document;
Document::fromString('Hello, Laravel!', mimeType: 'text/plain')
->as('hello.txt')
->put();
Files::assertStored(fn (StorableFile $file) =>
(string) $file === 'Hello, Laravel!' &&
$file->mimeType() === 'text/plain';
);
Files::assertNotStored(fn (StorableFile $file) =>
(string) $file === 'Hello, World!'
);
Files::assertNothingStored();
Для deletions можно передать file ID:
Files::assertDeleted('file-id');
Files::assertNotDeleted('file-id');
Files::assertNothingDeleted();
Vector stores
Операции vector store можно подменить методом fake класса Stores. Это автоматически подменит и file operations:
use Laravel\Ai\Stores;
Stores::fake();
Далее можно проверять созданные или удаленные stores:
use Laravel\Ai\Stores;
$store = Stores::create('Knowledge Base');
Stores::assertCreated('Knowledge Base');
Stores::assertCreated(fn (string $name, ?string $description) =>
$name === 'Knowledge Base'
);
Stores::assertNotCreated('Other Store');
Stores::assertNothingCreated();
Для удаления:
Stores::assertDeleted('store_id');
Stores::assertNotDeleted('other_store_id');
Stores::assertNothingDeleted();
Чтобы проверить добавление или удаление файлов из store, используйте assertion methods на экземпляре Store:
Stores::fake();
$store = Stores::get('store_id');
$store->add('added_id');
$store->remove('removed_id');
$store->assertAdded('added_id');
$store->assertRemoved('removed_id');
$store->assertNotAdded('other_file_id');
$store->assertNotRemoved('other_file_id');
Если файл одновременно сохраняется у провайдера и добавляется в vector store, provider ID может быть неизвестен. В этом случае передайте замыкание в assertAdded:
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 отправляет разные события, включая:
AddingFileToStoreAgentPromptedAgentStreamedAudioGeneratedCreatingStoreEmbeddingsGeneratedFileAddedToStoreFileDeletedFileRemovedFromStoreFileStoredGeneratingAudioGeneratingEmbeddingsGeneratingImageGeneratingTranscriptionImageGeneratedInvokingToolPromptingAgentRemovingFileFromStoreRerankedRerankingStoreCreatedStoringFileStreamingAgentToolInvokedTranscriptionGenerated
Вы можете слушать любые из этих событий, чтобы логировать или сохранять информацию об использовании AI SDK.