メインコンテンツまでスキップ
バージョン: 13.x

Laravel AI SDK

Introduction

Laravel AI SDK は、OpenAI、Anthropic、Gemini などの AI プロバイダと対話するための統合された表現力豊かな API を提供します。 AI SDK を使用すると、一貫した Laravel フレンドリーなインターフェイスを使用して、ツールと構造化された出力を備えたインテリジェント エージェントの構築、画像の生成、音声の合成と転写、ベクトル埋め込みの作成などを行うことができます。

Installation

Laravel AI SDK は Composer 経由でインストールできます。

composer require laravel/ai

次に、vendor:publish Artisan コマンドを使用して、AI SDK 構成ファイルと移行ファイルを公開する必要があります。

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

最後に、アプリケーションのデータベース移行を実行する必要があります。これにより、AI SDK が会話ストレージを強化するために使用する agent_conversations テーブルと agent_conversation_messages テーブルが作成されます。

php artisan migrate

Configuration

AI プロバイダの資格情報は、アプリケーションの config/ai.php 構成ファイルで定義することも、アプリケーションの .env ファイルで環境変数として定義することもできます。

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=
OPENAI_COMPATIBLE_API_KEY=
OPENAI_COMPATIBLE_URL=
OPENROUTER_API_KEY=
JINA_API_KEY=
VOYAGEAI_API_KEY=
XAI_API_KEY=

テキスト、画像、オーディオ、文字起こし、埋め込みに使用されるデフォルトのモデルは、アプリケーションの config/ai.php 構成ファイルで構成することもできます。

Custom Base URLs

デフォルトでは、Laravel AI SDK は各プロバイダのパブリック API エンドポイントに直接接続します。ただし、プロキシ サービスを使用して API キー管理を一元化したり、レート制限を実装したり、企業ゲートウェイ経由でトラフィックをルーティングしたりする場合など、別のエンドポイントを介してリクエストをルーティングする必要がある場合があります。

プロバイダ設定に url パラメータを追加することで、カスタム ベース URL を設定できます。

'providers' => [
'openai' => [
'driver' => 'openai',
'key' => env('OPENAI_API_KEY'),
'url' => env('OPENAI_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 のプロバイダでサポートされています。

OpenAI-Compatible Providers

OpenAI 互換の API を使用している場合は、LM Studio、vLLM、Together、Fireworks、ローカルのゲートウェイなどに対して、openai-compatible プロバイダを設定できます。url オプションは必須で、key オプションは任意です。指定した場合は bearer token として送信されます。

'providers' => [
'local' => [
'driver' => 'openai-compatible',
'url' => env('LOCAL_AI_URL'),
'key' => env('LOCAL_AI_API_KEY'),
],
],

設定後は、他のプロバイダと同じように名前付きプロバイダを使えます。

agent()->prompt('What is Laravel?', provider: 'local', model: 'local-model');

プロバイダにデフォルトのテキストモデルも設定しておけば、毎回モデルを明示的に渡す必要がなくなります。

'local' => [
'driver' => 'openai-compatible',
'url' => env('LOCAL_AI_URL'),
'key' => env('LOCAL_AI_API_KEY'),
'models' => [
'text' => [
'default' => env('LOCAL_AI_MODEL'),
],
],
],

プロバイダへのすべての送信リクエストにカスタム HTTP ヘッダーを追加するには、設定で headers 配列を定義します。これは、エンドポイントが bearer トークン以外に識別用または認証用のヘッダーを必要とする場合に便利です。

'local' => [
'driver' => 'openai-compatible',
'url' => env('LOCAL_AI_URL'),
'key' => env('LOCAL_AI_API_KEY'),
'headers' => [
'X-Tenant-Id' => env('LOCAL_AI_TENANT_ID'),
],
],

OpenAI 互換プロバイダは、テキスト生成、ストリーミング、ツール、構造化出力、画像添付、埋め込み、文字起こしをサポートします。エンドポイントでリクエストボディに追加のフィールドが必要な場合は、provider options を使用して指定してください。

OpenAI-Compatible Embeddings

任意のエンドポイントには既知のモデルがないため、OpenAI互換プロバイダで embeddings() を使用するには、デフォルトの embeddings モデルを設定する必要があります。固定の次元数を設定することもできます。省略した場合、リクエストは dimensions パラメータなしで送信され、モデル固有の次元数が使用されます。

'local' => [
'driver' => 'openai-compatible',
'url' => env('LOCAL_AI_URL'),
'key' => env('LOCAL_AI_API_KEY'),
'models' => [
'embeddings' => [
'default' => 'text-embedding-qwen3-embedding-0.6b',
'dimensions' => 1024, // optional
],
],
],

OpenAI-Compatible Transcriptions

同様に、OpenAI互換プロバイダで Transcription を使用するためのデフォルトの文字起こしモデルを設定する必要があります。音声は、標準的な multipart リクエストとしてエンドポイントの /audio/transcriptions ルートにアップロードされます。

'local' => [
'driver' => 'openai-compatible',
'url' => env('LOCAL_AI_URL'),
'key' => env('LOCAL_AI_API_KEY'),
'models' => [
'transcription' => [
'default' => 'whisper-1',
],
],
],

OpenAI-compatible プロバイダと Groq プロバイダは話者分離に対応していません。これらのプロバイダを使用して diarize メソッドを呼び出すと、例外が発生します。

Provider Support

AI SDK は、その機能全体にわたってさまざまなプロバイダをサポートします。次の表は、各機能で利用できるプロバイダをまとめたものです。

機能プロバイダ
TextOpenAI, OpenAI Compatible, Anthropic, Gemini, Azure, Bedrock, Groq, xAI, DeepSeek, Mistral, Ollama, OpenRouter
ImagesOpenAI, Gemini, xAI, Azure, Bedrock, OpenRouter
TTSOpenAI, ElevenLabs, Gemini, Mistral
STTOpenAI, OpenAI Compatible, ElevenLabs, Groq, Mistral, Gemini
埋め込みOpenAI, OpenAI Compatible, Gemini, Azure, Bedrock, Cohere, Mistral, Jina, VoyageAI, Ollama, OpenRouter
リランキングCohere, Jina, VoyageAI, Bedrock
FilesOpenAI, Anthropic, Gemini, Azure

Laravel\Ai\Enums\Lab 列挙型は、プレーン文字列を使用する代わりに、コード全体でプロバイダを参照するために使用できます。

use Laravel\Ai\Enums\Lab;

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

Agents

エージェントは、Laravel AI SDK で AI プロバイダと対話するための基本的な構成要素です。各エージェントは、大規模な言語モデルと対話するために必要な命令、会話コンテキスト、ツール、出力スキーマをカプセル化する専用の PHP クラスです。エージェントは、一度構成すれば、アプリケーション全体で必要に応じてプロンプトを表示できる、セールス コーチ、ドキュメント アナライザー、サポート ボットなどの専門アシスタントと考えてください。

make:agent Artisan コマンドを使用してエージェントを作成できます。

php artisan make:agent SalesCoach

php artisan make:agent SalesCoach --structured

生成されたエージェント クラス内で、システム プロンプト/指示、メッセージ コンテキスト、利用可能なツール、および出力スキーマ (該当する場合) を定義できます。

<?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

エージェントにプロンプ​​トを表示するには、まず make メソッドまたは標準のインスタンス化を使用してインスタンスを作成し、次に prompt を呼び出します。

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

return (string) $response;

make メソッドはコンテナからエージェントを解決し、自動依存注入を可能にします。エージェントのコンストラクターに引数を渡すこともできます。

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

追加の引数を prompt メソッドに渡すことで、プロンプトが表示されたときにデフォルトのプロバイダ、モデル、または HTTP タイムアウトをオーバーライドできます。

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

Raw HTTP Responses

テキスト生成エージェントから返されるすべてのレスポンスには、raw プロパティを介して、基盤となるプロバイダ API 呼び出しの生の HTTP レスポンスが公開されます。これにより、レート制限ヘッダー、リクエスト ID、その他の正確なペイロードフィールドなど、AI SDK の汎用レスポンスには含まれないプロバイダ固有の情報にアクセスできます。

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

$response->raw; // Illuminate\Http\Client\Response|null

$response->raw->header('X-RateLimit-Remaining-Requests');
$response->raw->json('id');

ツール呼び出しループでは、各ステップが自身のリクエストに対する生のレスポンスを保持します。

foreach ($response->steps as $step) {
$step->raw?->header('X-RateLimit-Remaining-Requests');
}

ストリーミングでレスポンスを返す場合、Bedrock プロバイダを使用する場合(API 呼び出しに HTTP クライアントではなく AWS SDK を使用します)、および明示的に withRawResponse で指定していないフェイクレスポンスでは、raw プロパティは null になります。

Conversation Context

エージェントが Conversational インターフェイスを実装している場合、該当する場合は、messages メソッドを使用して前の会話コンテキストを返すことができます。

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

RemembersConversations トレイトを使用する前に、vendor:publish Artisan コマンドを使って AI SDK のマイグレーションを公開し、実行してください。これらのマイグレーションにより、会話の保存に必要なデータベーステーブルが作成されます。

Laravel にエージェントの会話履歴を自動的に保存および取得させたい場合は、RemembersConversations トレイトを使用できます。この特性は、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;

/**
* Get the instructions that the agent should follow.
*/
public function instructions(): string
{
return 'You are a sales coach...';
}
}

RemembersConversations トレイトを使用する場合は、エージェントクラスに messages メソッドを手動で定義しないでください。messages メソッドが存在すると、トレイトの実装より優先され、会話履歴がデータベースから読み込まれなくなります。

ユーザーに対して新しい会話を開始するには、プロンプトを表示する前に forUser メソッドを呼び出します。

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

$conversationId = $response->conversationId;

会話 ID は応答で返され、将来の参照のために保存できます。 Eloquent を使用してユーザーの会話をすべて取得したい場合は、HasConversations トレイトをユーザー モデルに追加できます。

<?php

namespace App\Models;

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

class User extends Authenticatable
{
use HasConversations;
}

特性がモデルに追加されると、conversations 関係を介してユーザーの会話を取得してクエリできます。

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

既存の会話を続行するには、continue メソッドを使用します。

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

RemembersConversations トレイトを使用すると、以前のメッセージが自動的にロードされ、プロンプトが表示されたときに会話コンテキストに組み込まれます。新しいメッセージ (ユーザーとアシスタントの両方) は、各対話後に自動的に保存されます。

Conversation Participants

ユーザーが会話の参加者になることが最も一般的ですが、会話は任意の Eloquent モデルに属することができます。別の種類のモデルで会話を開始するには、forParticipant メソッドを使用します。

$response = (new SalesCoach)
->forParticipant($team)
->prompt('Review our latest sales results.');

参加者の morph クラスと主キーは会話とともに保存されます。そのため、User ID 1Team ID 1 のように主キーが同じでも型が異なるモデルは、それぞれ別の会話履歴を持ちます。forUser メソッドは forParticipant のエイリアスです。

参加者との直近の会話は、continueLastConversation メソッドを使用して続行できます。

$response = (new SalesCoach)
->continueLastConversation($team)
->prompt('Tell me more about that.');

特定の会話を続ける場合は、参加者を continue メソッドに渡します。

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

会話に参加する任意の Eloquent モデルに HasConversations トレイトを追加できます。これにより作成される conversations リレーションは、そのモデルのタイプと主キーにスコープされたポリモーフィックリレーションです。また、逆リレーションを通じて会話を所有する参加者にもアクセスできます。

$conversations = $team->conversations;

$participant = $conversation->participant;

アプリケーションで複数の参加者モデルの種類を使用している場合は、保存される参加者タイプがモデルクラス名に依存しないよう、Eloquent morph map の定義を検討してください。

continue メソッドは、指定された参加者がその会話を所有しているかを確認しません。会話を継続する前に、アプリケーションで会話へのアクセスを認可してください。

Structured Output

エージェントが構造化された出力を返すようにするには、HasStructuredOutput インターフェイスを実装します。これには、エージェントが 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;

// ...

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

構造化された出力を返すエージェントにプロンプ​​トを表示する場合、配列のように返された StructuredAgentResponse にアクセスできます。

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

return $response['score'];

Nested Objects

ネストされた構造化出力を定義するには、クロージャを指定した 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;

// ...

/**
* Get the agent's structured output schema definition.
*/
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(),
];
}
}

Arrays of Objects

エージェントが構造化アイテムのリストを返す必要がある場合は、array メソッドと object メソッドを組み合わせます。

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

値が複数のスキーマのいずれかに一致する可能性がある場合は、anyOf メソッドを使用します。

public function schema(JsonSchema $schema): array
{
return [
'content' => $schema->anyOf([
$schema->object(fn ($schema) => [
'type' => $schema->string()->enum(['article'])->required(),
'title' => $schema->string()->required(),
]),
$schema->object(fn ($schema) => [
'type' => $schema->string()->enum(['image'])->required(),
'url' => $schema->string()->required(),
]),
])->required(),
];
}

Attachments

プロンプトを表示するときに、プロンプトとともに添付ファイルを渡して、モデルが画像やドキュメントを検査できるようにすることもできます。

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

$response = (new SalesCoach)->prompt(
'Analyze the attached sales transcript...',
attachments: [
Files\Document::fromStorage('transcript.pdf'), // Attach a document from a filesystem disk...
Files\Document::fromPath('/home/laravel/transcript.md'), // Attach a document from a local path...
$request->file('transcript'), // Attach an uploaded file...
]
);

同様に、Laravel\Ai\Files\Image クラスを使用して、プロンプトに画像を添付できます。

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

stream メソッドを呼び出すことで、エージェントの応答をストリーミングできます。返される 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) {
// ...
}

Streaming Using the Vercel AI SDK Protocol

ストリーミング可能な応答で usingVercelDataProtocol メソッドを呼び出すことにより、Vercel AI SDK stream protocol を使用してイベントをストリーミングできます。

use App\Ai\Agents\SalesCoach;

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

Broadcasting

ストリーミング イベントは、いくつかの異なる方法でブロードキャストできます。まず、ストリーミング イベントで broadcast メソッドまたは broadcastNow メソッドを呼び出すだけです。

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 メソッドを呼び出して、エージェントの操作をキューに入れ、ストリーミング イベントが利用可能になったときにブロードキャストすることもできます。

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

Skipping Oversized Events

一部のブロードキャストプラットフォームでは、WebSocket メッセージのサイズが 10KB 前後に制限されています。大きな tool の結果のようにデータ量の多い stream event は、この制限を超えてブロードキャストに失敗することがあります。WithoutBroadcasting 属性を使うと、特定の event type をブロードキャスト対象から除外できます。

<?php

namespace App\Ai\Agents;

use Laravel\Ai\Attributes\WithoutBroadcasting;
use Laravel\Ai\Contracts\Agent;
use Laravel\Ai\Contracts\HasTools;
use Laravel\Ai\Promptable;
use Laravel\Ai\Streaming\Events\ToolCall;
use Laravel\Ai\Streaming\Events\ToolResult;

#[WithoutBroadcasting(ToolCall::class, ToolResult::class)]
class SearchAgent implements Agent, HasTools
{
use Promptable;

// ...
}

除外した event は一切ブロードキャストされませんが、agent_conversation_messages テーブルには保存されるため、stream の完了後にフロントエンドで完全な tool data を読み込めます。これは、キューを使う場合の broadcastOnQueue と、同期的な broadcast / broadcastNow の両方で機能します。

Queueing

エージェントの queue メソッドを使用すると、エージェントにプロンプ​​トを表示しながら、エージェントがバックグラウンドで応答を処理できるようにすることで、アプリケーションの高速性と応答性を維持できます。 then メソッドと catch メソッドは、応答が利用可能な場合、または例外が発生した場合に呼び出されるクロージャを登録するために使用できます。

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

Tools

ツールを使用して、エージェントがプロンプトに応答する際に利用できる追加機能を提供できます。ツールは、make:tool Artisan コマンドを使用して作成できます。

php artisan make:tool RandomNumberGenerator

生成されたツールは、アプリケーションの app/Ai/Tools ディレクトリに配置されます。各ツールには、ツールを利用する必要があるときにエージェントによって呼び出される handle メソッドが含まれています。

<?php

namespace App\Ai\Tools;

use Illuminate\Contracts\JsonSchema\JsonSchema;
use Laravel\Ai\Contracts\Tool;
use Laravel\Ai\Tools\Request;
use Stringable;

class RandomNumberGenerator implements Tool
{
/**
* Get the description of the tool's purpose.
*/
public function description(): Stringable|string
{
return 'This tool may be used to generate cryptographically secure random numbers.';
}

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

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

ツールを定義したら、エージェントの tools メソッドからツールを返すことができます。

use App\Ai\Tools\RandomNumberGenerator;

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

Validating Tool Arguments

ツールのスキーマによってモデルが指定できる引数は制限されますが、リクエストの validate メソッドを使って受け取った引数をバリデーションできます。

public function handle(Request $request): Stringable|string
{
$validated = $request->validate([
'city' => 'required|string',
'days' => 'required|integer|max:7',
]);

return $this->forecast($validated['city'], $validated['days']);
}

バリデーションに失敗すると、バリデーションメッセージがツールの結果としてモデルに返されます。これにより、モデルは引数を修正してツールを再度呼び出せます。

Repairing Tool Calls

RepairToolCalls 属性を使用すると、モデルが未知のローカルツールを呼び出した場合に、エージェントが復旧できるようになります。Laravel は利用可能なローカルツールの名前とともに失敗した呼び出しをモデルへ返すため、モデルは呼び出しを修正できます。

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

#[RepairToolCalls]
class SupportAgent implements Agent, HasTools
{
use Promptable;

// ...
}

Laravel がステップの最大数を自動的に算出する場合、この属性によって修復された呼び出し用のステップが1つ追加されます。明示的に指定した MaxSteps の制限は変わりません。

SimilaritySearch ツールを使用すると、エージェントはデータベースに保存されているベクトル埋め込みを使用して、特定のクエリに類似したドキュメントを検索できます。これは、アプリケーションのデータを検索するためのアクセス権をエージェントに付与する場合の検索拡張生成 (RAG) に役立ちます。

類似性検索ツールを作成する最も簡単な方法は、ベクトル埋め込みを含む Eloquent モデルで usingModel メソッドを使用することです。

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

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

最初の引数は Eloquent モデル クラスで、2 番目の引数はベクトル エンベディングを含む列です。

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

Deferred Tool Loading

デフォルトでは、エージェントが提供するすべてのツールがリクエストごとにプロバイダへ送信されます。エージェントが多数のツールを提供すると、トークンを消費し、モデルによるツール選択の精度が低下する可能性があります。OpenAI または Anthropic でプロバイダツールの ToolSearch を使用すると、必要になったときにプロバイダがツール定義を読み込むよう、ツール定義の読み込みを遅延できます。

use App\Ai\Tools\RefundOrder;
use App\Ai\Tools\SearchInvoices;
use App\Ai\Tools\Weather;
use Laravel\Ai\Providers\Tools\ToolSearch;

public function tools(): iterable
{
return [
new Weather,
new ToolSearch(tools: [
new SearchInvoices,
new RefundOrder,
]),
];
}

ラップされたツールを変更する必要はありません。プロバイダがプロンプトに関連するツールを検索して読み込むため、その後エージェントは他のツールと同じようにそれらを呼び出せます。

Anthropic を使用する場合、strategy 引数でプロバイダが遅延ツールを検索する方法を決定できます。サポートされている戦略は regex(デフォルト)と bm25 です。

new ToolSearch(tools: [new SearchInvoices], strategy: 'bm25'),

Anthropic を使用する場合、withProviderOptions メソッドを使って検索ツールにプロバイダ固有の追加オプションを渡せます。

(new ToolSearch(tools: [new SearchInvoices]))
->withProviderOptions(['cache_control' => ['type' => 'ephemeral']]),

ツール検索をサポートしていないプロバイダでは、遅延ツールを暗黙的に破棄せず、例外をスローします。また、Anthropic では、少なくとも1つのツールを ToolSearch ラッパーの外側で提供する必要があります。

File Storage Tools

FileStorage ツールファクトリを使用すると、エージェントから Laravel の filesystem disk にアクセスできるようにします。all メソッドは、指定したディスク上のファイルの一覧表示、読み取り、検査、URL 生成、書き込み、削除、コピーをエージェントで実行できるツールを返します。

use Laravel\Ai\Tools\FileStorage;

public function tools(): iterable
{
return FileStorage::all('local');
}

エージェントがファイルの確認だけを行えるようにしたい場合は、readOnly メソッドを使います。

return FileStorage::readOnly('local');

これらのメソッドは Illuminate\Support\Collection を返すため、エージェントに提供するツールをさらに絞り込めます。

use Laravel\Ai\Tools\Filesystem\DeleteFile;

return FileStorage::all('s3')
->reject(fn ($tool) => $tool instanceof DeleteFile);

MCP Tools

アプリケーションで Laravel MCP を使用している場合、Model Context Protocol サーバーが公開するツールをエージェントに提供できます。Laravel MCP client を使用すると、リモートまたはローカルの MCP サーバーに接続し、そのツールをエージェントへ直接渡せます。

MCP ツールを使用するには、アプリケーションに Laravel MCP パッケージをインストールする必要があります。

MCP クライアントの tools メソッドはコレクションを返すため、... 演算子を使ってエージェントの tools 配列にスプレッドします。

use App\Ai\Tools\RandomNumberGenerator;
use Laravel\Mcp\Client;

/**
* Get the tools available to the agent.
*
* @return Tool[]
*/
public function tools(): iterable
{
return [
...Client::web('https://mcp.example.com')
->withToken($token)
->tools(),

new RandomNumberGenerator,
];
}

AI SDKは各MCPツールを自動的にラップするため、エージェントは他のツールと同じように呼び出せます。また、named MCP clientも使用できます。

use Laravel\Mcp\Facades\Mcp;

public function tools(): iterable
{
return [
...Mcp::client('github')->tools(),
];
}

または、local MCP server に接続します。

use Laravel\Mcp\Client;

public function tools(): iterable
{
return [
...Client::local('php', ['artisan', 'mcp:start'])->tools(),
];
}

MCP クライアントの作成と認証について詳しくは、bearer token や OAuth についての説明を含む MCP client documentation を参照してください。

Provider Tools

プロバイダ ツールは、AI プロバイダによってネイティブに実装される特別なツールで、Web 検索、URL フェッチ、ファイル検索などの機能を提供します。通常のツールとは異なり、プロバイダ ツールはアプリケーションではなくプロバイダ自体によって実行されます。

プロバイダ ツールは、エージェントの tools メソッドによって返されます。

WebSearch プロバイダ ツールを使用すると、エージェントは Web でリアルタイム情報を検索できます。これは、現在のイベント、最近のデータ、またはモデルのトレーニングのカットオフ以降に変更された可能性のあるトピックに関する質問に答えるのに役立ちます。

対応プロバイダ: Anthropic, OpenAI, Azure, Gemini, xAI, OpenRouter

use Laravel\Ai\Providers\Tools\WebSearch;

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

Web 検索ツールを構成して、検索数を制限したり、結果を特定のドメインに制限したりすることができます。

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

ユーザーの場所に基づいて検索結果を絞り込むには、location メソッドを使用します。

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

Web Fetch

WebFetch プロバイダ ツールを使用すると、エージェントは Web ページのコンテンツをフェッチして読み取ることができます。これは、エージェントが特定の URL を分析したり、既知の Web ページから詳細情報を取得したりする必要がある場合に役立ちます。

対応プロバイダ: Anthropic、Gemini、OpenRouter

use Laravel\Ai\Providers\Tools\WebFetch;

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

Web 取得ツールを設定して、取得数を制限したり、特定のドメインに制限したりすることができます。

(new WebFetch)->max(3)->allow(['docs.laravel.com']),

FileSearch プロバイダ ツールを使用すると、エージェントは files に保存されている vector stores を検索できます。これにより、エージェントがアップロードされたドキュメントで関連情報を検索できるようになり、検索拡張生成 (RAG) が可能になります。

対応プロバイダ: OpenAI, Gemini, xAI

use Laravel\Ai\Providers\Tools\FileSearch;

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

複数のベクトル ストア ID を指定して、複数のストアを検索できます。

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

Sub-Agents

エージェントは、別のエージェントの tools メソッドから返される場合もあります。エージェントがツールとして返されると、親エージェントは特定のタスクをサブエージェントに委任し、元のプロンプトに応答する際にサブエージェントの応答を使用することができます。これは、汎用エージェントが独自の命令、ツール、モデル構成、またはプロバイダ設定を備えた専門エージェントにアクセスする必要がある場合に役立ちます。

たとえば、カスタマー サポート エージェントは、払い戻し資格に関する質問を専用の払い戻しエージェントに委任できます。

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

/**
* Get the instructions that the agent should follow.
*/
public function instructions(): string
{
return 'You help customers with account, order, and billing questions. Delegate refund policy questions to the refunds specialist.';
}

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

サブエージェントが親エージェントに公開される方法をカスタマイズするには、サブエージェントに 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;

/**
* Get the instructions that the agent should follow.
*/
public function instructions(): string
{
return 'You are a refunds specialist. Use order details and the refund policy to give concise eligibility guidance.';
}

/**
* Get the agent's tool name.
*/
public function name(): string
{
return 'refunds_specialist';
}

/**
* Get the agent's tool description.
*/
public function description(): string
{
return 'Determine whether an order is eligible for a refund and explain the next step.';
}

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

サブエージェントが CanActAsTool を実装していない場合、Laravel はエージェントのクラスのベース名をツール名として使用し、親エージェントに明確な自己完結型タスクの説明を渡すように要求する一般的な説明を使用します。各サブエージェントの呼び出しは独立して実行され、親エージェントの会話履歴を受け取りません。

Middleware

エージェントはミドルウェアをサポートしているため、プロンプトがプロバイダに送信される前にインターセプトして変更することができます。ミドルウェアは、make:agent-middleware Artisan コマンドを使用して作成できます。

php artisan make:agent-middleware LogPrompts

生成されたミドルウェアは、アプリケーションの app/Ai/Middleware ディレクトリに配置されます。エージェントにミドルウェアを追加するには、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;

// ...

/**
* Get the agent's middleware.
*/
public function middleware(): array
{
return [
new LogPrompts,
];
}
}

各ミドルウェア クラスは、AgentPromptClosure を受け取り、プロンプトを次のミドルウェアに渡す handle メソッドを定義する必要があります。

<?php

namespace App\Ai\Middleware;

use Closure;
use Laravel\Ai\Prompts\AgentPrompt;

class LogPrompts
{
/**
* Handle the incoming prompt.
*/
public function handle(AgentPrompt $prompt, Closure $next)
{
Log::info('Prompting agent', ['prompt' => $prompt->prompt]);

return $next($prompt);
}
}

エージェントの処理が完了した後に、応答で then メソッドを使用してコードを実行できます。これは、同期応答とストリーミング応答の両方で機能します。

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

Anonymous Agents

場合によっては、専用のエージェント クラスを作成せずにモデルをすばやく操作したい場合があります。 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')

Agent Configuration

PHP 属性を使用して、エージェントのテキスト生成オプションを構成できます。次の属性が使用可能です。

  • MaxSteps: ツールの使用時にエージェントが実行できるステップの最大数。
  • MaxTokens: モデルが生成できるトークンの最大数。
  • Model: エージェントが使用するモデル。
  • Provider: エージェントで使用する AI プロバイダ(フェイルオーバー用に複数指定することもできます)。
  • Temperature: 生成に使用するサンプリング温度(0.0〜1.0)。
  • Timeout: エージェントのリクエストに対する HTTP タイムアウト(秒)。デフォルトは 60 です。
  • TopP: 生成に使用する nucleus sampling の確率(0.0〜1.0)。
  • UseCheapestModel: コストを最適化するため、プロバイダで最も安価なテキストモデルを使用します。
  • UseSmartestModel: 複雑なタスクに対応するため、プロバイダで最も高性能なテキストモデルを使用します。
<?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-sonnet-5')]
#[MaxSteps(10)]
#[MaxTokens(4096)]
#[Temperature(0.7)]
#[Timeout(120)]
#[TopP(0.9)]
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;

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

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

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

UseCheapestModelUseSmartestModel が選択する基盤モデルは、プロバイダが新しいモデルをリリースすると、Laravel AI SDK のリリース間で変わる可能性があります。モデルを切り替えると、動作の変更、非推奨パラメータ、コストの大きな差が生じることがあります。安定した予測可能なモデルと料金が必要な場合は、Model 属性を使ってモデルを明示的に指定してください。

Provider Options

エージェントがプロバイダ固有のオプション (OpenAI 推論作業やペナルティ設定など) を渡す必要がある場合は、HasProviderOptions コントラクトを実装し、providerOptions メソッドを定義します。

<?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 => [],
};
}
}

providerOptions メソッドは、現在使用されているプロバイダ (Lab 列挙型または文字列) を受け取り、プロバイダごとに異なるオプションを返すことができます。各フォールバック プロバイダが独自の構成を受け取ることができるため、これは failover を使用する場合に特に便利です。

上記の Anthropic の例では、cache_control を介して prompt caching も有効になります。

Prompt Caching

多くのプロバイダは、繰り返し使用されるプロンプトのプレフィックスを自動的にキャッシュし、キャッシュされた部分には割引料金を適用します。OpenAI、Gemini、Groq、DeepSeek、xAI では設定は必要ありません。レスポンスの使用量から節約額を確認できます。

$response->usage->cacheReadInputTokens;
$response->usage->cacheWriteInputTokens;

anthropic プロバイダと bedrock プロバイダは、明示的に指定された場合にのみキャッシュします。CacheInstructions 属性と CacheToolDefinitions 属性は、エージェントの指示とツール定義の末尾にキャッシュブレークポイントを設定します。これにより、各会話ではそのプレフィックスを再度書き込む代わりに、キャッシュから読み取ります。

use Laravel\Ai\Attributes\CacheInstructions;
use Laravel\Ai\Attributes\CacheToolDefinitions;

#[CacheInstructions]
#[CacheToolDefinitions]
class SalesCoach implements Agent
{
use Promptable;

// ...
}

リクエストごとに指示が変わる場合、たとえば現在の日付を埋め込む場合は、CacheToolDefinitions だけを使用してください。リクエストごとに変化するプレフィックスをキャッシュすると、毎回新しいキャッシュエントリが作成されます。そのため、再利用することなくキャッシュへの書き込みコストだけが発生します。

これらの属性をサポートしないプロバイダは無視するため、エージェントは failover の使用時にも安全に宣言できます。

キャッシュされたプレフィックスは、デフォルトで5分間保持されます。属性に TTL を渡すと、Anthropic はこれらを1時間保持する場合があります。

#[CacheInstructions('1h')]
#[CacheToolDefinitions('1h')]

また、トップレベルの cache_control provider option を使って、Anthropic の自動キャッシュを有効にすることもできます。これによりリクエストの最後のブロックの後ろに1つのブレークポイントが設定されるため、会話が進むにつれてブレークポイントも移動し、各ターンでは直前までのターンをキャッシュから読み取ります。どちらの仕組みも組み合わせて使用できます。

プロバイダはツール、指示、メッセージの順にプロンプトを構築するため、指示を1時間キャッシュするにはツール定義も1時間キャッシュする必要があります。この2つのキャッシュ時間が異なると、InvalidArgumentException がスローされます。

Human Tool Approval

ツールの承認には、会話履歴が永続化されており、一時停止した呼び出しを再開できる Conversational エージェントが必要です。RemembersConversations トレイトが必要な永続化機能を提供します。

機密性の高い操作や取り消せない操作を実行するツールでは、実行前に人間の承認が必要になる場合があります。ツールを承認可能にするには、Approvable コントラクトを実装し、InteractsWithApprovals トレイトを使用します。承認可能なツールは、デフォルトで承認が必要です。

<?php

namespace App\Ai\Tools;

use Illuminate\Contracts\JsonSchema\JsonSchema;
use Illuminate\Support\Facades\Storage;
use Laravel\Ai\Concerns\InteractsWithApprovals;
use Laravel\Ai\Contracts\Approvable;
use Laravel\Ai\Contracts\Tool;
use Laravel\Ai\Tools\Request;
use Stringable;

class DeleteFile implements Approvable, Tool
{
use InteractsWithApprovals;

/**
* Get the description of the tool's purpose.
*/
public function description(): Stringable|string
{
return 'Delete a file from storage.';
}

/**
* Execute the tool.
*/
public function handle(Request $request): Stringable|string
{
Storage::delete($request['path']);

return "Deleted [{$request['path']}].";
}

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

ツール呼び出しの引数に基づいて承認が必要かどうかを判断するには、ツールに needsApproval メソッドを定義します。このメソッドは、ブール値または承認を求める理由を含む Approval インスタンスを返せます。

use Laravel\Ai\Approvals\Approval;

/**
* Determine whether the tool needs approval for the given request.
*/
protected function needsApproval(Request $request): Approval|bool
{
return str_starts_with($request['path'], 'temporary/')
? false
: Approval::required('This will permanently delete a file.');
}

エージェントの tools メソッドからツールを返す際に、ツールの承認要件を上書きできます。

public function tools(): iterable
{
return [
(new SendNotification)->withoutApproval(),
(new DeleteFile)->requireApproval('Deletion review required.'),
];
}

承認が必要なツールが呼び出されると、エージェントは実行前に一時停止します。レスポンスの保留中の承認を確認できます。そこには各ツール呼び出しの ID、ツール名、引数、承認理由が含まれます。

$response = (new FileAssistant)
->forUser($user)
->prompt('Delete the old invoice.');

if ($response->hasPendingApprovals()) {
foreach ($response->pendingApprovals as $approval) {
// $approval->id
// $approval->tool
// $approval->arguments
// $approval->reason
}
}

エージェントを再開するには、会話を続け、保留中の各ツール呼び出しに対する判断を含む Decisions インスタンスを渡します。判断では、呼び出しを承認または拒否したり、実行前に引数を編集したりできます。

use Laravel\Ai\Approvals\Decision;
use Laravel\Ai\Approvals\Decisions;

$response = (new FileAssistant)
->continue($conversationId, as: $user)
->prompt(Decisions::from([
'call_abc' => Decision::approve(),
'call_ghi' => Decision::reject('The invoice must be retained.'),
]));

ブール値の truefalse は、それぞれ承認と拒否の略記として使用できます。保留中のすべてのツール呼び出しには、判定を与える必要があります。不明なツール呼び出し ID、判定が指定されていないツール呼び出し ID、またはすでに解決済みのツール呼び出し ID があると、ApprovalMismatchException がスローされます。明示的な判定がない呼び出しに対しては、approveRemaining または rejectRemaining メソッドを使用してデフォルトの判定を指定できます。

$decisions = Decisions::from([
'call_abc' => true,
])->rejectRemaining('Not approved.');

$response = (new FileAssistant)
->continue($conversationId, as: $user)
->prompt($decisions);

結果を伴う拒否(Decision::reject('Not approved.') など)はモデルに返され、モデルは応答を続けられます。結果を伴わない拒否は、拒否を記録したあと生成ループを停止します。

promptstreamqueuebroadcastbroadcastNowbroadcastOnQueue メソッドでは、ツールの承認をサポートしています。

ストリーミングとブロードキャスト中、一時停止は tool_approval_request イベントで表されます。Vercel AI SDK stream protocol を使用する場合、承認リクエストと結果は、そのプロトコル固有のツール承認パーツを使って出力されます。

キューに入れたエージェントでは、生成されたレスポンスが then コールバックに渡され、Laravel は ToolApprovalRequested イベントもディスパッチします。

Laravel は、モデルに続きを尋ねる前に、承認されたツールの実行結果を保存します。その後の生成に失敗した場合、承認処理はすでに完了しています。同じ承認決定を再度送信するのではなく、通常のテキストプロンプトで会話を続けてください。

Complete Approval Flow

以下のルートは、承認フロー全体を示しています。GET ルートはチャット画面を返し、POST ルートは新しいテキストプロンプトまたはチャット画面からの承認結果を受け取ります。この例では、アプリケーションの User モデルが HasConversations トレイトを使用していることを前提としています:

use App\Ai\Agents\FileAssistant;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Gate;
use Illuminate\Support\Facades\Route;
use Illuminate\Validation\Rule;
use Laravel\Ai\Approvals\Decision;
use Laravel\Ai\Approvals\Decisions;
use Laravel\Ai\Models\Conversation;

Route::get('/chat/{conversation}', function (Request $request, Conversation $conversation) {
Gate::authorize('view', $conversation);

return view('chat', [
'conversation' => $conversation,
]);
})->middleware('auth');

Route::post('/chat/{conversation}', function (Request $request, Conversation $conversation) {
Gate::authorize('view', $conversation);

$validated = $request->validate([
'message' => ['nullable', 'string', 'required_without:decisions', 'prohibits:decisions'],
'decisions' => ['nullable', 'array', 'required_without:message', 'prohibits:message'],
'decisions.*.action' => ['required_with:decisions', Rule::in(['approve', 'reject'])],
'decisions.*.result' => ['nullable', 'string'],
]);

$prompt = isset($validated['decisions'])
? Decisions::from(collect($validated['decisions'])->map(
fn (array $decision) => match ($decision['action']) {
'approve' => Decision::approve(),
'reject' => Decision::reject($decision['result'] ?? null),
}
)->all())
: $validated['message'];

$response = (new FileAssistant)
->continue($conversation->id, as: $request->user())
->prompt($prompt);

return [
'conversation_id' => $response->conversationId,
'status' => $response->hasPendingApprovals() ? 'awaiting_approval' : 'complete',
'message' => $response->text,
'approvals' => $response->pendingApprovals,
];
})->middleware('auth');

レスポンスのステータスが awaiting_approval の場合、チャット画面には保留中の承認を表示し、ユーザーの選択を各判断のキーとしてツール呼び出し ID を使用し、同じエンドポイントへ送信します。

{
"decisions": {
"call_abc": {
"action": "approve"
},
"call_def": {
"action": "reject",
"result": "The invoice must be retained."
}
}
}

通常のチャットメッセージの場合、画面から代わりに message の値を送信することもできます。

{
"message": "Delete the old invoice."
}

Images

Laravel\Ai\Image クラスは、openaigemini、または xai プロバイダを使用してイメージを生成するために使用できます。

use Laravel\Ai\Image;

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

$rawContent = (string) $image;

squareportrait、および landscape メソッドは画像のアスペクト比を制御するために使用できますが、quality メソッドは最終的な画像品質 (highmediumlow) についてモデルをガイドするために使用できます。 timeout メソッドを使用して、HTTP タイムアウトを秒単位で指定できます。

use Laravel\Ai\Image;

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

attachments メソッドを使用して参照画像を添付できます。

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

$image = Image::of('Update this photo of me to be in the style of an impressionist painting.')
->attachments([
Files\Image::fromStorage('photo.jpg'),
// Files\Image::fromPath('/home/laravel/photo.jpg'),
// Files\Image::fromUrl('https://example.com/photo.jpg'),
// $request->file('photo'),
])
->landscape()
->generate();

生成されたイメージは、アプリケーションの config/filesystems.php 構成ファイルで構成されたデフォルトのディスクに簡単に保存できます。

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

// ...
});

Audio

Laravel\Ai\Audio クラスは、指定されたテキストから音声を生成するために使用できます。

use Laravel\Ai\Audio;

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

$rawContent = (string) $audio;

Laravel の Stringable クラスから利用できる toAudio メソッドを使用して、文字列からオーディオを生成することもできます。

use Illuminate\Support\Str;

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

malefemale、および 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();

生成されたオーディオは、アプリケーションの config/filesystems.php 構成ファイルで構成されたデフォルトのディスクに簡単に保存できます。

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

$path = $audio->store();
$path = $audio->storeAs('audio.mp3');
$path = $audio->storePublicly();
$path = $audio->storePubliclyAs('audio.mp3');

オーディオ生成もキューに入れられる場合があります。

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

Laravel\Ai\Transcription クラスは、指定された音声のトランスクリプトを生成するために使用できます。

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 メソッドを使用すると、生のテキストのトランスクリプトに加えて話者分離されたトランスクリプトを応答に含めることを希望することを示すことができ、これにより、話者ごとにセグメント化されたトランスクリプトにアクセスできるようになります。

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

Text Summarization

Laravel の Stringable クラスで利用できる summarize メソッドを使って、テキストを要約できます。デフォルトでは、要約は3文以内で、設定したプロバイダが提供する最も安価なテキストモデルを使って生成されます。

use Illuminate\Support\Str;

$summary = Str::of($article)->summarize();

要約の生成に使用する文の最大数、プロバイダ、モデル、タイムアウトを指定できます。Str クラスには、このメソッドの static 版も用意されています。

use Laravel\Ai\Enums\Lab;

$summary = Str::of($article)->summarize(
sentences: 4,
provider: Lab::Anthropic,
model: 'claude-sonnet-5',
timeout: 30,
);

$summary = Str::summarize($article, sentences: 4);

Embeddings

Laravel の Stringable クラスから利用できる新しい toEmbeddings メソッドを使用すると、任意の文字列のベクトル埋め込みを簡単に生成できます。

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

Multimodal Embeddings

文字列に加えて、Embeddings::for メソッドは画像、音声、ドキュメント、動画の入力にも対応しているため、テキスト以外のコンテンツの埋め込みを生成できます。Gemini は画像、音声、ドキュメント、動画の埋め込みをサポートし、VoyageAI は画像と動画の埋め込みをサポートします。

use Laravel\Ai\Embeddings;
use Laravel\Ai\Enums\Lab;
use Laravel\Ai\Files\Image;
use Laravel\Ai\Files\Video;

$response = Embeddings::for([
'A vineyard at sunset.',
Image::fromStorage('vineyard.jpg'),
Video::fromPath('/home/laravel/tour.mp4'),
])->generate(Lab::Gemini);

マルチモーダル入力では、file classes used for attachments と同じファイルクラスを使用します。これらのファイルは、ローカルパス、ファイルシステムディスク、リモート URL、または Base64 エンコードされたコンテンツから作成できます。画像、ドキュメント、動画はアップロードされたファイルからも作成でき、ドキュメントは生の文字列コンテンツから作成することもできます。

use Laravel\Ai\Files\Audio;
use Laravel\Ai\Files\Document;
use Laravel\Ai\Files\Image;
use Laravel\Ai\Files\Video;

Image::fromPath('/home/laravel/photo.jpg');
Image::fromStorage('photo.jpg');
Image::fromUpload($request->file('photo'));

Audio::fromPath('/home/laravel/clip.mp3');
Audio::fromStorage('clip.mp3');
Audio::fromUpload($request->file('clip.mp3'));

Video::fromPath('/home/laravel/video.mp4');
Video::fromStorage('video.mp4');
Video::fromUpload($request->file('video'));

Document::fromUrl('https://example.com/report.pdf');
Document::fromString('Laravel is a PHP framework.', 'text/plain');
Document::fromUpload($request->file('report'));

VoyageAI では、1つのリクエスト内でリモート URL のメディアと Base64 エンコードされたメディアを混在させることはできません。ローカルファイル、保存済みファイル、アップロードされたファイルは Base64 エンコードされたコンテンツとして送信され、テキスト入力はどちらのメディアソースとも組み合わせられます。利用できるマルチモーダルモデルと入力については、プロバイダのドキュメントを確認してください。

Querying Embeddings

埋め込みを生成したら、通常は後でクエリできるように、データベースの vector 列に保存します。Laravel は、pgvector 拡張機能を介した PostgreSQL のベクトル列に加え、MariaDB のベクトル列もネイティブでサポートしています。まず、マイグレーションで vector 列を定義し、次元数を指定します。

Schema::ensureVectorExtensionExists();

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

類似性検索を高速化するためにベクトル インデックスを追加することもできます。ベクトル列で index を呼び出すと、Laravel はコサイン距離を使用して HNSW インデックスを自動的に作成します。

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

Eloquent モデルでは、AsVector castを使ってベクトル列をcastする必要があります。

use Illuminate\Database\Eloquent\Casts\AsVector;

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

同様のレコードをクエリするには、whereVectorSimilarTo メソッドを使用します。このメソッドは、最小のコサイン類似度 (0.01.0 の間、1.0 は同一) によって結果をフィルターし、類似度によって結果を並べ替えます。

use App\Models\Document;

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

$queryEmbedding は、浮動小数点数の配列またはプレーン文字列の場合があります。文字列が指定されると、Laravel はその文字列の埋め込みを自動的に生成します。

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

より詳細な制御が必要な場合は、下位レベルの whereVectorDistanceLessThanselectVectorDistance、および orderByVectorDistance メソッドを個別に使用できます。

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

エージェントにツールとして類似性検索を実行できるようにしたい場合は、Similarity Search ツールのドキュメントを確認してください。

現在、ベクトルクエリは pgvector 拡張機能を使用する PostgreSQL 接続と、MariaDB 11.7 以降でサポートされています。

Caching Embeddings

埋め込み生成をキャッシュして、同一の入力に対する冗長な API 呼び出しを回避できます。キャッシュを有効にするには、ai.caching.embeddings.cache 構成オプションを true に設定します。

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

キャッシュが有効になっている場合、埋め込みは 30 日間キャッシュされます。キャッシュ キーはプロバイダ、モデル、ディメンション、および入力コンテンツに基づいており、異なる構成で新しい埋め込みが生成される一方で、同一のリクエストがキャッシュされた結果を返すことが保証されます。

デフォルトでは、各入力の埋め込みがそれぞれ固有のキーでキャッシュされます。そのため、入力のセットや順序が変わっていても、後続のリクエストで以前に処理した入力のキャッシュが使用されることがあります。入力のセット全体を1つのキーでキャッシュするには、設定オプション ai.caching.embeddings.individuallyfalse に設定してください。

グローバル キャッシュが無効になっている場合でも、cache メソッドを使用して特定のリクエストのキャッシュを有効にすることもできます。

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

カスタムのキャッシュ期間を秒単位で指定できます。

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

toEmbeddings Stringable メソッドは、cache 引数も受け入れます。

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

リランキングを使用すると、特定のクエリとの関連性に基づいてドキュメントのリストを並べ替えることができます。これは、意味的理解を使用して検索結果を改善するのに役立ちます。

Laravel\Ai\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');

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

limit メソッドを使用して、返される結果の数を制限できます。

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

Reranking Collections

便宜上、Laravel コレクションは rerank マクロを使用してリランキングできます。最初の引数はリランキングに使用するフィールドを指定し、2 番目の引数はクエリです。

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

結果の数を制限してプロバイダを指定することもできます。

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

Files

Laravel\Ai\Files クラスまたは個々のファイル クラスは、後で会話で使用するために AI プロバイダでファイルを保存するために使用できます。これは、再アップロードせずに何度も参照したい大きなドキュメントやファイルの場合に便利です。

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;

未加工のコンテンツやアップロードされたファイルを保存することもできます。

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

ファイルが保存されると、ファイルを再アップロードする代わりに、エージェント経由でテキストを生成するときにファイルを参照できます。

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

$response = (new SalesCoach)->prompt(
'Analyze the attached sales transcript...'
attachments: [
Files\Document::fromId('file-id') // Attach a stored document...
]
);

以前に保存されたファイルを取得するには、ファイル インスタンスで get メソッドを使用します。

use Laravel\Ai\Files\Document;

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

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

プロバイダからファイルを削除するには、delete メソッドを使用します。

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

デフォルトでは、Files クラスは、アプリケーションの config/ai.php 構成ファイルで構成されたデフォルトの AI プロバイダを使用します。ほとんどの操作では、provider 引数を使用して別のプロバイダを指定できます。

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

withProviderOptions メソッドを使用して、プロバイダごとのアップロードオプションを渡せます。たとえば、OpenAI のファイル purpose を設定できます:

use Laravel\Ai\Files\Document;

$response = Document::fromPath('/home/laravel/knowledge.txt')
->withProviderOptions(['purpose' => 'assistants'])
->put();

プロバイダごとにオプションを分けるには、現在のプロバイダを受け取るクロージャを渡します:

use Laravel\Ai\Enums\Lab;
use Laravel\Ai\Files\Document;

$response = Document::fromPath('/home/laravel/training.jsonl')
->withProviderOptions(fn (Lab|string $provider) => match ($provider) {
Lab::OpenAI => ['purpose' => 'fine-tune'],
default => [],
})
->put();

Using Stored Files in Conversations

ファイルがプロバイダに保存されたら、Document クラスまたは Image クラスの fromId メソッドを使用して、エージェントの会話でそのファイルを参照できます。

use App\Ai\Agents\DocumentAnalyzer;
use Laravel\Ai\Files;
use Laravel\Ai\Files\Document;

$stored = Document::fromPath('/path/to/report.pdf')->put();

$response = (new DocumentAnalyzer)->prompt(
'Summarize this document.',
attachments: [
Document::fromId($stored->id),
],
);

同様に、格納されたイメージは、Image クラスを使用して参照できます。

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

ベクター ストアを使用すると、検索拡張生成 (RAG) に使用できる、検索可能なファイルのコレクションを作成できます。 Laravel\Ai\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;

既存のベクター ストアを ID で取得するには、get メソッドを使用します。

use Laravel\Ai\Stores;

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

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

ベクター ストアを削除するには、Stores クラスまたはストア インスタンスで delete メソッドを使用します。

use Laravel\Ai\Stores;

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

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

$store->delete();

Adding Files to Stores

ベクター ストアを作成したら、add メソッドを使用してそれに files を追加できます。ストアに追加されたファイルは、file search provider tool を使用したセマンティック検索のために自動的にインデックス付けされます。

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

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

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

// Or, store and add a file in one step...
$document = $store->add(Document::fromPath('/path/to/document.pdf'));
$document = $store->add(Document::fromStorage('manual.pdf'));
$document = $store->add($request->file('document'));

$document->id;
$document->fileId;

通常、以前に保存したファイルをベクトルストアに追加すると、返されるドキュメント ID はそのファイルに以前割り当てられた ID と一致します。ただし、一部のベクトルストレージプロバイダは、新しく異なる「ドキュメント ID」を返すことがあります。そのため、後で参照できるよう、データベースには常に両方の ID を保存することをおすすめします。

ファイルをストアに追加するときに、ファイルにメタデータを添付できます。このメタデータは、後で file search provider tool を使用するときに検索結果をフィルタリングするために使用できます。

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

ストアからファイルを削除するには、remove メソッドを使用します。

$store->remove('file_id');

ベクター ストアからファイルを削除しても、プロバイダの file storage からは削除されません。ファイルをベクター ストアから削除し、ファイルストレージから完全に削除するには、deleteFile 引数を使用します。

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

Failover

他のメディアをプロンプトまたは生成するときに、プライマリ プロバイダでサービスの中断またはレート制限が発生した場合に、バックアップ プロバイダ/モデルに自動的にフェイルオーバーするプロバイダ/モデルの配列を指定できます。

use App\Ai\Agents\SalesCoach;
use Laravel\Ai\Enums\Lab;
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]);

フェイルオーバーは、FailoverableException がスローされた場合にのみ発生します。たとえば、レート制限 (RateLimitedException)、過負荷または利用不可のプロバイダ (ProviderOverloadedException)、クレジット不足 (InsufficientCreditsException) などです。バリデーションエラーや不正なリクエストエラーといった通常のエラーでは、フェイルオーバーは発生しません。

[Lab::OpenAI, Lab::Anthropic] のようにプロバイダの単純なリストを渡した場合、各プロバイダはそのデフォルトモデルを使用します。フェイルオーバーチェーン内の各プロバイダに特定のモデルを指定するには、Lab enum の value をキーとして連想配列を渡します(enum のケースは PHP の配列キーとして直接使用できません)。

use Laravel\Ai\Enums\Lab;

$response = (new SalesCoach)->prompt(
'Analyze this sales transcript...',
provider: [
Lab::Gemini->value => 'gemini-3-flash-preview',
Lab::DeepSeek->value => 'deepseek-v4-pro',
],
);

Testing

キューに入れた画像、音声、文字起こし、または埋め込みの生成をフェイクすると、キューに入れた生成に登録された then コールバックはフェイクしたレスポンスを受け取って呼び出されます。これにより、コールバックに含まれるロジックをテストできます。これらのコールバックを呼び出したくない場合は、Queue::fake() を使ってキューもフェイクできます。

Agents

テスト中にエージェントの応答を偽装するには、エージェント クラスで fake メソッドを呼び出します。必要に応じて、応答の配列またはクロージャを指定できます。

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

構造化された出力を返すエージェントをフェイクする場合、レスポンスとして配列を指定できます。エージェントは、与えたデータを含む構造化レスポンスを返します:

SalesCoach::fake([
['score' => 87],
]);

ツールの承認を待機しているレスポンスを偽装することもできます。

use Laravel\Ai\Approvals\PendingApproval;
use Laravel\Ai\Responses\AgentResponse;

FileAssistant::fake([
AgentResponse::fakeWithPendingApprovals([
new PendingApproval(
id: 'call_abc',
tool: 'DeleteFile',
arguments: ['path' => 'invoice.pdf'],
reason: 'This will permanently delete a file.',
),
]),
]);

$response = (new FileAssistant)->prompt('Delete the invoice.');

$response->hasPendingApprovals(); // true

構造化出力を返すエージェントに対して Agent::fake() を呼び出し、偽の出力を明示的に指定していない場合、Laravel はエージェントに定義された出力スキーマに一致する偽のデータを自動的に生成します。

エージェントにプロンプ​​トを出した後、受け取ったプロンプトについてアサーションを行うことができます。

use Laravel\Ai\Prompts\AgentPrompt;

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

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

SalesCoach::assertPromptedTimes(3);

SalesCoach::assertNotPrompted('Missing prompt');

SalesCoach::assertNeverPrompted();

承認の継続を検証する際は、プロンプトの承認判断を確認できます。

use Laravel\Ai\Approvals\Decisions;
use Laravel\Ai\Prompts\AgentPrompt;

FileAssistant::fake();

(new FileAssistant)->prompt(Decisions::from([
'call_abc' => true,
]));

FileAssistant::assertPrompted(function (AgentPrompt $prompt) {
return $prompt->hasApprovalDecisions()
&& $prompt->approvalDecisions->get('call_abc')->isApproved();
});

キューに入れられたエージェント呼び出しの場合は、キューに入れられたアサーション メソッドを使用します。

use Laravel\Ai\QueuedAgentPrompt;

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

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

SalesCoach::assertNotQueued('Missing prompt');

SalesCoach::assertNeverQueued();

すべてのエージェント呼び出しに対応する偽の応答があることを確認するには、preventStrayPrompts を使用できます。偽の応答が定義されていない状態でエージェントが呼び出された場合、例外がスローされます。

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

Images

Image クラスの fake メソッドを呼び出すことで、イメージの生成を偽装することができます。画像が偽造されると、記録された画像生成プロンプトに対してさまざまなアサーションが実行される可能性があります。

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

イメージを生成した後、受信したプロンプトについてアサーションを行うことができます。

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

Image::assertNotGenerated('Missing prompt');

Image::assertNothingGenerated();

キューに入れられたイメージを生成するには、キューに入れられたアサーション メソッドを使用します。

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

Image::assertNotQueued('Missing prompt');

Image::assertNothingQueued();

すべてのイメージ生成に対応する偽の応答があることを確認するには、preventStrayImages を使用できます。偽の応答が定義されていない状態でイメージが生成された場合、例外がスローされます。

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

Audio

オーディオ生成は、Audio クラスの fake メソッドを呼び出すことによって偽装される可能性があります。オーディオが偽造されると、録音されたオーディオ生成プロンプトに対してさまざまなアサーションが実行される可能性があります。

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

音声を生成した後、受信したプロンプトについてアサーションを行うことができます。

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

Audio::assertNotGenerated('Missing prompt');

Audio::assertNothingGenerated();

キューに入れられたオーディオ生成の場合は、キューに入れられたアサーション メソッドを使用します。

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

Audio::assertNotQueued('Missing prompt');

Audio::assertNothingQueued();

すべてのオーディオ生成に対応する偽の応答があることを確認するには、preventStrayAudio を使用できます。定義された偽の応答なしでオーディオが生成された場合、例外がスローされます。

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

Transcriptions

転写世代は、Transcription クラスの fake メソッドを呼び出すことによって偽装される可能性があります。転写が偽造されると、記録された転写生成プロンプトに対してさまざまなアサーションが実行される可能性があります。

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

文字起こしを生成した後、受信したプロンプトについてアサーションを行うことができます。

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

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

Transcription::assertNothingGenerated();

キューに入れられたトランスクリプション生成の場合は、キューに入れられたアサーション メソッドを使用します。

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

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

Transcription::assertNothingQueued();

すべての転写生成に対応する偽の応答があることを確認するには、preventStrayTranscriptions を使用できます。偽の応答が定義されていない状態でトランスクリプションが生成された場合、例外がスローされます。

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

Embeddings

埋め込みの生成は、Embeddings クラスの fake メソッドを呼び出すことによって偽装される可能性があります。エンベディングが偽造されると、記録されたエンベディング生成プロンプトに対してさまざまなアサーションが実行される可能性があります。

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

埋め込みを生成した後、受信したプロンプトについてアサーションを行うことができます。

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

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

Embeddings::assertNothingGenerated();

キューに入れられた埋め込み生成の場合は、キューに入れられたアサーション メソッドを使用します。

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

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

Embeddings::assertNothingQueued();

すべての埋め込み生成に対応する偽の応答があることを確認するには、preventStrayEmbeddings を使用できます。偽の応答が定義されていない状態で埋め込みが生成された場合、例外がスローされます。

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

Reranking

リランキング操作は、Reranking クラスの fake メソッドを呼び出すことで偽装できます。

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

リランキング後、実行された操作についてアサーションを行うことができます。

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

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

Reranking::assertNothingReranked();

Files

ファイル操作は、Files クラスの fake メソッドを呼び出すことで偽装される可能性があります。

use Laravel\Ai\Files;

Files::fake();

ファイル操作が偽装されると、発生したアップロードと削除についてアサーションを行うことができます。

use Laravel\Ai\Contracts\Files\StorableFile;
use Laravel\Ai\Files\Document;

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

// Make assertions...
Files::assertStored(fn (StorableFile $file) =>
(string) $file === 'Hello, Laravel!' &&
$file->mimeType() === 'text/plain';
);

Files::assertNotStored(fn (StorableFile $file) =>
(string) $file === 'Hello, World!'
);

Files::assertNothingStored();

ファイルの削除をアサートするには、ファイル ID を渡すことができます。

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

Vector Stores

ベクター ストア操作は、Stores クラスの fake メソッドを呼び出すことで偽装される可能性があります。偽装ストアは自動的に file operations も偽装します。

use Laravel\Ai\Stores;

Stores::fake();

ストア操作が偽装されると、作成または削除されたストアについてアサーションを行うことができます。

use Laravel\Ai\Stores;

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

// Make assertions...
Stores::assertCreated('Knowledge Base');

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

Stores::assertNotCreated('Other Store');

Stores::assertNothingCreated();

ストアの削除に対してアサートするには、ストア ID を指定できます。

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

ファイルがストアに追加またはストアから削除されたことをアサートするには、特定の Store インスタンスでアサーション メソッドを使用します。

Stores::fake();

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

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

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

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

ファイルがプロバイダの file storage に保存され、同じリクエスト内のベクター ストアに追加された場合、ファイルのプロバイダ ID がわからない可能性があります。この場合、クロージャを assertAdded メソッドに渡して、追加されたファイルのコンテンツに対してアサートできます。

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

Laravel AI SDK は、次のようなさまざまな events をディスパッチします。

  • AddingFileToStore
  • AgentFailed
  • AgentFailedOver
  • AgentPrompted
  • AgentStreamed
  • AudioGenerated
  • CreatingStore
  • EmbeddingsGenerated
  • FileAddedToStore
  • FileDeleted
  • FileRemovedFromStore
  • FileStored
  • GeneratingAudio
  • GeneratingEmbeddings
  • GeneratingImage
  • GeneratingTranscription
  • ImageGenerated
  • InvokingTool
  • PromptingAgent
  • ProviderFailedOver
  • RemovingFileFromStore
  • Reranked
  • Reranking
  • StartingStep
  • StepCompleted
  • StepFailed
  • StoreCreated
  • StoreDeleted
  • StoringFile
  • StreamingAgent
  • ToolApprovalRequested
  • ToolApprovalResolved
  • ToolFailed
  • ToolInvoked
  • TranscriptionGenerated

これらのイベントのいずれかをリッスンして、AI SDK の使用情報を記録または保存できます。