> ## Documentation Index
> Fetch the complete documentation index at: https://kawax.biz/llms.txt
> Use this file to discover all available pages before exploring further.

# Laravel AI SDK

> Complete gids voor de Laravel AI SDK, waarmee je meerdere AI-providers zoals OpenAI, Anthropic en Gemini via één uniforme interface gebruikt. Behandelt agents, afbeeldingen genereren, audio, embeddings en testen.

## Introductie

De [Laravel AI SDK](https://github.com/laravel/ai) biedt een uniforme, expressieve API om te communiceren met AI-providers zoals OpenAI, Anthropic en Gemini. Met de AI SDK bouw je intelligente agents met tools en gestructureerde output, genereer je afbeeldingen, doe je spraaksynthese en transcriptie, en maak je vector-embeddings — allemaal via één consistente, Laravel-achtige interface.

<Info>
  De Laravel AI SDK is een officieel package dat is toegevoegd in Laravel 13. Het wordt aangeboden als `laravel/ai` en laat je meerdere AI-providers via een uniforme API gebruiken.
</Info>

## Overzicht van providerondersteuning

| Functionaliteit        | Ondersteunde providers                                                                                         |
| ---------------------- | -------------------------------------------------------------------------------------------------------------- |
| Tekstgeneratie         | OpenAI, OpenAI Compatible, Anthropic, Gemini, Azure, Bedrock, Groq, xAI, DeepSeek, Mistral, Ollama, OpenRouter |
| Afbeeldingen genereren | OpenAI, Gemini, xAI, Azure, Bedrock, OpenRouter                                                                |
| Spraaksynthese (TTS)   | OpenAI, ElevenLabs, Gemini, Mistral                                                                            |
| Spraakherkenning (STT) | OpenAI, OpenAI Compatible, ElevenLabs, Groq, Mistral, Gemini                                                   |
| Embeddings             | OpenAI, OpenAI Compatible, Gemini, Azure, Bedrock, Cohere, Mistral, Jina, VoyageAI, Ollama, OpenRouter         |
| Reranking              | Cohere, Jina, VoyageAI, Bedrock                                                                                |
| Bestanden              | OpenAI, Anthropic, Gemini, Azure                                                                               |

## Installatie

<Steps>
  <Step title="Het package installeren">
    Installeer de Laravel AI SDK met Composer.

    ```shell theme={null}
    composer require laravel/ai
    ```
  </Step>

  <Step title="Configuratiebestand en migrations publiceren">
    Publiceer het configuratiebestand en de migrations met het `vendor:publish` Artisan-commando.

    ```shell theme={null}
    php artisan vendor:publish --provider="Laravel\Ai\AiServiceProvider"
    ```
  </Step>

  <Step title="Migrations uitvoeren">
    Voer de databasemigrations uit. Hiermee worden de tabellen `agent_conversations` en `agent_conversation_messages` aangemaakt, die worden gebruikt voor het opslaan van gespreksgeschiedenis.

    ```shell theme={null}
    php artisan migrate
    ```
  </Step>
</Steps>

## Configuratie

### Omgevingsvariabelen

Stel de API-sleutels van de AI-providers die je gebruikt in via het `.env`-bestand.

```ini theme={null}
ANTHROPIC_API_KEY=
AZURE_OPENAI_API_KEY=
COHERE_API_KEY=
DEEPSEEK_API_KEY=
ELEVENLABS_API_KEY=
GEMINI_API_KEY=
GROQ_API_KEY=
MISTRAL_API_KEY=
OLLAMA_API_KEY=
OPENAI_API_KEY=
OPENROUTER_API_KEY=
JINA_API_KEY=
VOYAGEAI_API_KEY=
XAI_API_KEY=
```

De standaardmodellen voor tekst, afbeeldingen, audio, transcriptie en embeddings kun je ook instellen in `config/ai.php`.

### Aangepaste basis-URL's

Wil je via een proxyservice werken, dan kun je per provider een aangepaste URL instellen.

```php theme={null}
'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'),
    ],
],
```

Aangepaste basis-URL's zijn beschikbaar voor OpenAI, Anthropic, Gemini, Groq, Cohere, DeepSeek, xAI en OpenRouter.

### OpenAI-Compatible providers

Gebruik je een OpenAI-compatibele API zoals LM Studio, vLLM, Together, Fireworks of een lokale gateway, dan kun je de provider configureren met de `openai-compatible` driver. `url` is verplicht; geef je een `key` op, dan wordt die als Bearer-token meegestuurd.

```php theme={null}
'providers' => [
    'local' => [
        'driver' => 'openai-compatible',
        'url' => env('LOCAL_AI_URL'),
        'key' => env('LOCAL_AI_API_KEY'),
    ],
],
```

Na de configuratie kun je de provider, net als andere providers, op naam aanspreken.

```php theme={null}
agent()->prompt('What is Laravel?', provider: 'local', model: 'local-model');
```

Stel je een standaard tekstmodel in, dan hoef je niet telkens een model op te geven.

```php theme={null}
'local' => [
    'driver' => 'openai-compatible',
    'url' => env('LOCAL_AI_URL'),
    'key' => env('LOCAL_AI_API_KEY'),
    'models' => [
        'text' => [
            'default' => env('LOCAL_AI_MODEL'),
        ],
    ],
],
```

Om aan alle uitgaande requests aangepaste HTTP-headers toe te voegen, definieer je een `headers`-array in de configuratie. Dit is handig voor endpoints die andere authenticatie- of identificatieheaders nodig hebben dan een Bearer-token.

```php theme={null}
'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-Compatible providers ondersteunen tekstgeneratie, streaming, tools, gestructureerde output, afbeeldingsbijlagen, embeddings en transcriptie. Heeft je endpoint extra velden in de request body nodig, gebruik dan [provideropties](#provideropties).

#### Embeddings met OpenAI-Compatible

Omdat er voor een willekeurig endpoint geen bekende modellen zijn, moet je een standaard embeddingsmodel configureren om `embeddings()` met een OpenAI-Compatible provider te gebruiken. Laat je `dimensions` weg, dan wordt de parameter niet in de request opgenomen en wordt het modelspecifieke aantal dimensies gebruikt.

```php theme={null}
'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, // optioneel
        ],
    ],
],
```

#### Transcriptie met OpenAI-Compatible

Om `Transcription` met een OpenAI-Compatible provider te gebruiken, configureer je een standaard transcriptiemodel. De audio wordt als een standaard multipart-request geüpload naar `/audio/transcriptions` van je endpoint.

```php theme={null}
'local' => [
    'driver' => 'openai-compatible',
    'url' => env('LOCAL_AI_URL'),
    'key' => env('LOCAL_AI_API_KEY'),
    'models' => [
        'transcription' => [
            'default' => 'whisper-1',
        ],
    ],
],
```

<Note>
  De OpenAI-Compatible en Groq providers ondersteunen geen sprekerscheiding. Roep je bij deze providers de `diarize`-methode aan, dan wordt er een exception gegooid.
</Note>

### Lab enum

Gebruik de `Lab` enum om in je code naar providers te verwijzen.

```php theme={null}
use Laravel\Ai\Enums\Lab;

Lab::Anthropic;
Lab::OpenAI;
Lab::Gemini;
```

***

## Agents

Agents zijn de fundamentele bouwstenen van de Laravel AI SDK. Met het `make:agent`-commando genereer je een agentklasse.

```shell theme={null}
php artisan make:agent SalesCoach

# Een agent met gestructureerde output genereren
php artisan make:agent SalesCoach --structured
```

Gegenereerde agents worden geplaatst in de map `app/Ai/Agents/`. Hieronder zie je een voorbeeld van een agent die alle belangrijke interfaces implementeert.

```php theme={null}
<?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(),
        ];
    }
}
```

```mermaid theme={null}
flowchart TD
    A["Prompt van de gebruiker"] --> B["Agent::prompt()"]
    B --> C["Opties toevoegen"]
    C -->|"Bij Conversational"| D["Gespreksgeschiedenis toevoegen"]
    C -->|"Bij HasTools"| E["Toollijst toevoegen"]
    C -->|"Bij HasStructuredOutput"| F["JSON-schema toevoegen"]
    D --> G["Verzenden naar AI-provider"]
    E --> G
    F --> G
    B --> G
    G --> H["Respons"]
    H -->|"Toolaanroep"| I["Tool uitvoeren"]
    I --> G
    H -->|"Voltooid"| J["AgentResponse"]
```

### Prompts

Met de `prompt()`-methode stuur je een bericht naar een agent.

```php theme={null}
$response = (new SalesCoach)->prompt('Analyze this sales transcript...');
return (string) $response;
```

Met de statische `make()`-methode maak je een instantie waarbij de dependencies via de container worden geresolved.

```php theme={null}
$agent = SalesCoach::make(user: $user);
```

Provider, model en timeout kun je overschrijven via de argumenten van `prompt()`.

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

#### Ruwe HTTP-respons

De respons van een tekstgenererende agent heeft een `raw`-property die de ruwe HTTP-respons van de provider-API-aanroep teruggeeft. Hiermee kun je providerspecifieke informatie ophalen die niet in de gemeenschappelijke respons van de AI SDK zit, zoals rate-limit-headers of request-ID's.

```php theme={null}
$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');
```

In een toolaanroepenloop bewaart elke stap de ruwe respons van zijn eigen request.

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

<Info>
  `raw` is `null` bij streaming, bij Bedrock (omdat de API via de AWS SDK wordt aangeroepen) en bij fake-responses waarbij geen expliciete `withRawResponse` is opgegeven.
</Info>

### Gesprekscontext

Implementeer je de `Conversational`-interface en definieer je een `messages()`-methode, dan kun je eerdere gespreksgeschiedenis aan de AI doorgeven.

Met de `RemembersConversations`-trait wordt de gespreksgeschiedenis automatisch opgeslagen in en opgehaald uit de database.

```php theme={null}
use Laravel\Ai\Concerns\RemembersConversations;

class SalesCoach implements Agent, Conversational
{
    use Promptable, RemembersConversations;

    public function instructions(): string
    {
        return 'You are a sales coach...';
    }
}
```

Start een gesprek met `forUser()` en gebruik het teruggegeven `conversationId` om het gesprek voort te zetten met `continue()`.

```php theme={null}
$response = (new SalesCoach)->forUser($user)->prompt('Hello!');
$conversationId = $response->conversationId;

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

### Gestructureerde output

Implementeer je de `HasStructuredOutput`-interface en definieer je een JSON-schema in de `schema()`-methode, dan ontvang je de respons van de AI als gestructureerde data.

```php theme={null}
public function schema(JsonSchema $schema): array
{
    return ['score' => $schema->integer()->required()];
}

$response = (new SalesCoach)->prompt('Analyze this...');
return $response['score'];
```

#### Geneste objecten

```php theme={null}
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 van objecten

```php theme={null}
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 (keuze uit meerdere schema's)

Kan een waarde met een van meerdere schema's overeenkomen, gebruik dan de `anyOf`-methode.

```php theme={null}
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(),
    ];
}
```

### Bijlagen

Via het `attachments`-argument geef je documenten en afbeeldingen door aan een agent.

```php theme={null}
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'),
    ]
);
```

Afbeeldingen voeg je op dezelfde manier toe.

```php theme={null}
$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'),
]);
```

### Streaming

Met de `stream()`-methode geef je de respons in chunks terug. Dit is ideaal om lange responses in realtime naar de frontend te sturen.

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

Met een `then()`-callback beschrijf je wat er moet gebeuren nadat de streaming is voltooid.

```php theme={null}
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...
        });
});
```

Je kunt ook handmatig over de stream itereren.

```php theme={null}
$stream = (new SalesCoach)->stream('Analyze this sales transcript...');

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

#### Vercel AI SDK-protocol

Gebruik je de Vercel AI SDK op de frontend, roep dan `usingVercelDataProtocol()` aan.

```php theme={null}
Route::get('/coach', function () {
    return (new SalesCoach)->stream('Analyze...')->usingVercelDataProtocol();
});
```

### Broadcasting

Je kunt de events van een stream naar een broadcastkanaal sturen, bijvoorbeeld via Laravel Echo.

```php theme={null}
use Illuminate\Broadcasting\Channel;

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

foreach ($stream as $event) {
    $event->broadcast(new Channel('channel-name'));
}
```

Met `broadcastOnQueue()` kun je via de queue broadcasten.

```php theme={null}
(new SalesCoach)->broadcastOnQueue(
    'Analyze this sales transcript...',
    new Channel('channel-name'),
);
```

#### Grote events overslaan

Sommige broadcastplatformen beperken WebSocket-berichten tot ongeveer 10KB. Stream-events met veel data, zoals grote toolresultaten, kunnen deze limiet overschrijden waardoor het broadcasten mislukt. Met het `WithoutBroadcasting`-attribuut kun je specifieke eventtypes uitsluiten van broadcasting.

```php theme={null}
<?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;

    // ...
}
```

Uitgesloten events worden niet gebroadcast, maar worden nog steeds opgeslagen in de tabel `agent_conversation_messages`. Daardoor kan de frontend na afloop van de stream alsnog alle tooldata ophalen. Dit werkt zowel via de queue (`broadcastOnQueue`) als synchroon (`broadcast` / `broadcastNow`).

### Queues

Met de `queue()`-methode zet je een prompt op de queue voor asynchrone verwerking.

```php theme={null}
use Laravel\Ai\Responses\AgentResponse;

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

    return back();
});
```

### Tools

Met tools kan de AI functies in je code aanroepen. Met het `make:tool`-commando genereer je een toolklasse.

```shell theme={null}
php artisan make:tool RandomNumberGenerator
```

```php theme={null}
<?php

namespace App\Ai\Tools;

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

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

    public function handle(Request $request): Stringable|string
    {
        return (string) random_int($request['min'], $request['max']);
    }

    public function schema(JsonSchema $schema): array
    {
        return [
            'min' => $schema->integer()->min(0)->required(),
            'max' => $schema->integer()->required(),
        ];
    }
}
```

Registreer tools in de `tools()`-methode van de agent.

```php theme={null}
public function tools(): iterable
{
    return [new RandomNumberGenerator];
}
```

#### Validatie van toolargumenten

Met de `schema`-methode van een tool kun je het type van de ontvangen argumenten beperken, maar dat garandeert niet dat de waarden die het model doorgeeft ook correct zijn. Gebruik de `validate`-methode van de request om de toolargumenten te valideren.

```php theme={null}
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']);
}
```

Mislukt de validatie, dan wordt de foutmelding als toolresultaat aan het model teruggegeven, zodat het model de argumenten kan corrigeren en de tool opnieuw kan aanroepen.

#### Similarity search tool

Je kunt eenvoudig een similarity search tool toevoegen die vector-embeddings gebruikt.

```php theme={null}
use App\Models\Document;
use Laravel\Ai\Tools\SimilaritySearch;

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

Je kunt ook opties opgeven.

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

Met een closure definieer je je eigen zoeklogica.

```php theme={null}
new SimilaritySearch(using: function (string $query) {
    return Document::query()
        ->where('user_id', $this->user->id)
        ->whereVectorSimilarTo('embedding', $query)
        ->limit(10)
        ->get();
}),
```

Met `withDescription()` pas je de beschrijving van de tool aan.

```php theme={null}
SimilaritySearch::usingModel(Document::class, 'embedding')
    ->withDescription('Search the knowledge base for relevant articles.'),
```

### File storage tools

Met de `FileStorage`-toolfactory geef je een agent toegang tot Laravel [filesystem disks](/nl/filesystem). De `all`-methode geeft een set tools terug om bestanden op de opgegeven disk te tonen, lezen, van URL's te voorzien, schrijven, verwijderen en kopiëren.

```php theme={null}
use Laravel\Ai\Tools\FileStorage;

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

Wil je alleen leestoegang toestaan, gebruik dan de `readOnly`-methode.

```php theme={null}
return FileStorage::readOnly('local');
```

Deze methoden geven een `Illuminate\Support\Collection` terug, zodat je de aangeboden tools verder kunt inperken.

```php theme={null}
use Laravel\Ai\Tools\Filesystem\DeleteFile;

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

### MCP-tools

Gebruikt je applicatie [Laravel MCP](/nl/mcp), dan kun je tools die worden aangeboden door een [Model Context Protocol](https://modelcontextprotocol.io)-server aan je agents beschikbaar stellen. Met de [Laravel MCP-client](/nl/mcp#mcp-client) maak je verbinding met een remote of lokale MCP-server en geef je de tools ervan direct door aan je agent.

<Info>
  Om MCP-tools te gebruiken moet het [Laravel MCP](/nl/mcp)-package in je applicatie geïnstalleerd zijn.
</Info>

De `tools`-methode van de MCP-client geeft een collectie terug, dus gebruik de `...` spread-operator om deze in de `tools`-array van de agent uit te spreiden.

```php theme={null}
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,
    ];
}
```

De AI SDK wrapt elke MCP-tool automatisch, zodat de agent ze net als andere tools kan aanroepen. Je kunt ook [benoemde MCP-clients](/nl/mcp#benoemde-clients) gebruiken.

```php theme={null}
use Laravel\Mcp\Facades\Mcp;

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

Of je maakt verbinding met een [lokale MCP-server](/nl/mcp#lokale-server).

```php theme={null}
use Laravel\Mcp\Client;

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

Zie de [documentatie over de MCP-client](/nl/mcp#mcp-client) voor het aanmaken en authenticeren van MCP-clients (met Bearer-tokens, OAuth en meer).

### Providertools

Dit zijn speciale tools die AI-providers native implementeren.

#### Zoeken op het web

Voegt webzoeken toe aan je agent. Ondersteund door Anthropic, OpenAI, Azure, Gemini, xAI en OpenRouter.

```php theme={null}
use Laravel\Ai\Providers\Tools\WebSearch;

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

Optioneel kun je het aantal zoekresultaten, toegestane domeinen en locatiegegevens opgeven.

```php theme={null}
(new WebSearch)->max(5)->allow(['laravel.com', 'php.net']),
(new WebSearch)->location(city: 'New York', region: 'NY', country: 'US');
```

#### Web fetch

Een tool die de content van een opgegeven URL ophaalt. Ondersteund door Anthropic, Gemini en OpenRouter.

```php theme={null}
use Laravel\Ai\Providers\Tools\WebFetch;

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

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

#### File search

Een tool die documenten doorzoekt in een vector store. Ondersteund door OpenAI, Gemini en xAI.

```php theme={null}
use Laravel\Ai\Providers\Tools\FileSearch;

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

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

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

Met `FileSearchQuery` kun je ook complexe filters opgeven.

```php theme={null}
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'])
);
```

### Tools lazy laden

Alle tools die een agent aanbiedt worden standaard bij elk request naar de provider gestuurd. Bij agents met veel tools kost dat tokens en kan de nauwkeurigheid waarmee het model tools kiest afnemen. Bij OpenAI of Anthropic kun je met de `ToolSearch`-providertool het laden van tooldefinities uitstellen tot ze nodig zijn.

```php theme={null}
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,
        ]),
    ];
}
```

De gewrapte tools zelf hoef je niet aan te passen. De provider zoekt en laadt de tools die relevant zijn voor de prompt en roept ze net als gewone tools aan.

Bij Anthropic kun je met het `strategy`-argument de zoekmethode opgeven. Beschikbare strategieën zijn het standaard `regex` en `bm25`.

```php theme={null}
new ToolSearch(tools: [new SearchInvoices], strategy: 'bm25'),
```

Anthropic-specifieke opties geef je door via de `withProviderOptions`-methode.

```php theme={null}
(new ToolSearch(tools: [new SearchInvoices]))
    ->withProviderOptions(['cache_control' => ['type' => 'ephemeral']]),
```

<Warning>
  Providers die tool search niet ondersteunen gooien een exception in plaats van lazy tools stilzwijgend te negeren. Bij Anthropic moet er buiten `ToolSearch` ook minstens één tool aanwezig zijn.
</Warning>

### Toolaanroepen repareren

Gebruik het `RepairToolCalls`-attribuut om een agent in staat te stellen een aanroep te repareren wanneer het model een niet-bestaande lokale tool aanroept. Laravel stuurt de mislukte aanroep en de namen van de beschikbare lokale tools terug naar het model, zodat het model de aanroep kan corrigeren.

```php theme={null}
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;

    // ...
}
```

Bepaalt Laravel het maximale aantal stappen automatisch, dan wordt er door dit attribuut één stap toegevoegd voor de reparatie-aanroep. Heb je met `MaxSteps` expliciet een limiet ingesteld, dan verandert die limiet niet.

### Subagents

Een agent kan ook worden teruggegeven vanuit de `tools()`-methode van een andere agent. Registreer je een agent als tool, dan kan de bovenliggende agent specifieke taken delegeren aan een subagent en het resultaat in de oorspronkelijke respons verwerken. Dat is handig wanneer een generieke agent toegang nodig heeft tot gespecialiseerde agents met eigen instructies, tools, model- en providerconfiguratie.

Bijvoorbeeld: een klantenservice-agent die vragen over het terugbetalingsbeleid delegeert aan een terugbetalingsspecialist.

```php theme={null}
<?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;

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

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

Om aan te passen hoe de subagent aan de bovenliggende agent wordt gepresenteerd, implementeer je op de subagent de `CanActAsTool`-interface en definieer je een naam en beschrijving voor de tool.

```php theme={null}
<?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;

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

    public function name(): string
    {
        return 'refunds_specialist';
    }

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

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

Voor subagents die `CanActAsTool` niet implementeren, gebruikt Laravel de klassenaam als toolnaam en genereert het automatisch een generieke beschrijving. Elke aanroep van een subagent gebeurt onafhankelijk en neemt de gespreksgeschiedenis van de bovenliggende agent niet over.

### Middleware

Voeg middleware toe aan een agent om prompts en responses te onderscheppen.

```shell theme={null}
php artisan make:agent-middleware LogPrompts
```

Implementeer op de agent de `HasMiddleware`-interface en registreer de middleware in de `middleware()`-methode.

```php theme={null}
<?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;

    public function middleware(): array
    {
        return [new LogPrompts];
    }
}
```

Een voorbeeldimplementatie van een middlewareklasse.

```php theme={null}
<?php

namespace App\Ai\Middleware;

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

Met `then()` voeg je ook verwerking toe na de respons.

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

### Anonieme agents

Met de `agent()`-helper gebruik je een anonieme agent zonder een klasse te definiëren.

```php theme={null}
use function Laravel\Ai\{agent};

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

Je kunt ook anonieme agents met gestructureerde output maken.

```php theme={null}
use Illuminate\Contracts\JsonSchema\JsonSchema;

$response = agent(
    schema: fn (JsonSchema $schema) => ['number' => $schema->integer()->required()],
)->prompt('Generate a random number less than 100');
```

### Agentconfiguratie (PHP attributes)

Met PHP attributes beschrijf je de standaardinstellingen van een agent declaratief.

| Attribute             | Beschrijving                               |
| --------------------- | ------------------------------------------ |
| `#[Provider]`         | Geeft de te gebruiken provider op          |
| `#[Model]`            | Geeft het te gebruiken model op            |
| `#[MaxSteps]`         | Maximaal aantal stappen voor toolaanroepen |
| `#[MaxTokens]`        | Maximaal aantal tokens                     |
| `#[Temperature]`      | Temperatuurparameter (0.0–1.0)             |
| `#[TopP]`             | Kans voor nucleus sampling (0.0–1.0)       |
| `#[Timeout]`          | Timeout (in seconden)                      |
| `#[UseCheapestModel]` | Kiest automatisch het goedkoopste model    |
| `#[UseSmartestModel]` | Kiest automatisch het krachtigste model    |

```php theme={null}
<?php

namespace App\Ai\Agents;

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

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

Er zijn ook shortcut-attributes voor modelselectie.

```php theme={null}
// Het goedkoopste model gebruiken
#[UseCheapestModel]
class SimpleSummarizer implements Agent
{
    use Promptable;
}

// Het krachtigste model gebruiken
#[UseSmartestModel]
class ComplexReasoner implements Agent
{
    use Promptable;
}
```

### Provideropties

Implementeer je de `HasProviderOptions`-interface, dan kun je providerspecifieke opties doorgeven.

```php theme={null}
<?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;

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

De `providerOptions`-methode ontvangt de provider die op dat moment wordt gebruikt (als `Lab` enum of string), zodat je per provider andere opties kunt teruggeven. Omdat je elke fallback-provider een eigen configuratie kunt meegeven, is dit vooral handig bij gebruik van [failover](#failover).

In het Anthropic-voorbeeld hierboven wordt met `cache_control` ook [prompt-caching](#prompt-caching) ingeschakeld.

### Prompt-caching

Veel providers cachen automatisch herhaalde promptprefixen en rekenen voor het gecachte deel een gereduceerd tarief. OpenAI, Gemini, Groq, DeepSeek en xAI vereisen geen configuratie; de besparing kun je terugvinden in de usage van de respons.

```php theme={null}
$response->usage->cacheReadInputTokens;
$response->usage->cacheWriteInputTokens;
```

De `anthropic`- en `bedrock`-providers cachen alleen wanneer je dat expliciet aangeeft. Met de attributen `CacheInstructions` en `CacheToolDefinitions` plaats je cache-breakpoints aan het einde van de instructies en tooldefinities van de agent. Daardoor lezen alle gesprekken die prefix uit de cache en hoeft die niet telkens opnieuw te worden geschreven.

```php theme={null}
use Laravel\Ai\Attributes\CacheInstructions;
use Laravel\Ai\Attributes\CacheToolDefinitions;

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

    // ...
}
```

Verandert de inhoud van je instructies per request, bijvoorbeeld omdat je de huidige datum invoegt, gebruik dan alleen `CacheToolDefinitions`. Cache je een prefix die per request verandert, dan wordt er telkens een nieuwe cache-entry aangemaakt die nooit wordt hergebruikt, terwijl je wel de schrijfkosten betaalt.

Providers die deze attributen niet ondersteunen negeren ze gewoon, dus je kunt ze ook bij gebruik van [failover](#failover) veilig declareren.

Een gecachte prefix blijft standaard 5 minuten bewaard. Bij Anthropic kun je door een TTL aan het attribuut mee te geven de prefix tot 1 uur bewaren.

```php theme={null}
#[CacheInstructions('1h')]
#[CacheToolDefinitions('1h')]
```

Je kunt de automatische caching van Anthropic ook inschakelen via de top-level `cache_control` [provideroptie](#provideropties). Daarbij wordt één breakpoint na het laatste blok van de request geplaatst; naarmate het gesprek vordert, schuift het breakpoint mee en leest elke beurt de vorige beurt uit de cache. Beide mechanismen zijn ook te combineren.

<Warning>
  Omdat providers de prompt opbouwen in de volgorde tools, instructies, berichten, moet je bij het 1 uur cachen van instructies ook de tooldefinities 1 uur cachen. Mix je de twee, dan wordt er een `InvalidArgumentException` gegooid.
</Warning>

***

## Menselijke goedkeuring (human tool approval)

<Warning>
  Voor toolgoedkeuring is een `Conversational`-agent nodig waarvan de gespreksgeschiedenis wordt gepersisteerd. De `RemembersConversations`-trait biedt de persistentie die nodig is om een gepauzeerde aanroep te hervatten.
</Warning>

Voor tools die gevoelige of onomkeerbare operaties uitvoeren, zoals het verwijderen van bestanden of het overmaken van geld, kun je vóór uitvoering menselijke goedkeuring vereisen. Om een tool goedkeuringsplichtig te maken, implementeer je het `Approvable`-contract en gebruik je de `InteractsWithApprovals`-trait. Goedkeuringsplichtige tools vereisen standaard goedkeuring.

```php theme={null}
<?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;

    /**
     * Haal de beschrijving van het doel van de tool op
     */
    public function description(): Stringable|string
    {
        return 'Verwijder een bestand uit de storage.';
    }

    /**
     * Voer de tool uit
     */
    public function handle(Request $request): Stringable|string
    {
        Storage::delete($request['path']);

        return "Verwijderd: [{$request['path']}]";
    }

    /**
     * Haal de schemadefinitie van de tool op
     */
    public function schema(JsonSchema $schema): array
    {
        return [
            'path' => $schema->string()->required(),
        ];
    }
}
```

Wil je op basis van de argumenten van de toolaanroep bepalen of goedkeuring nodig is, definieer dan een `needsApproval`-methode op de tool. Deze methode kan een boolean teruggeven, of een `Approval`-instantie met een reden voor de goedkeuring.

```php theme={null}
use Laravel\Ai\Approvals\Approval;

/**
 * Bepaal of de tool goedkeuring nodig heeft voor de gegeven request
 */
protected function needsApproval(Request $request): Approval|bool
{
    return str_starts_with($request['path'], 'temporary/')
        ? false
        : Approval::required('Dit bestand wordt permanent verwijderd.');
}
```

Je kunt de goedkeuringsvereisten ook overschrijven wanneer je de tool teruggeeft vanuit de `tools`-methode van de agent.

```php theme={null}
public function tools(): iterable
{
    return [
        (new SendNotification)->withoutApproval(),
        (new DeleteFile)->requireApproval('Voor verwijderen is bevestiging nodig.'),
    ];
}
```

Wordt een goedkeuringsplichtige tool aangeroepen, dan pauzeert de agent vóór uitvoering. Via `pendingApprovals` op de respons zie je per toolaanroep het ID, de toolnaam, de argumenten en de reden van de goedkeuring.

```php theme={null}
$response = (new FileAssistant)
    ->forUser($user)
    ->prompt('Verwijder de oude facturen.');

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

Om de agent te hervatten, zet je het gesprek voort en geef je een `Decisions`-instantie door met een beslissing voor elke openstaande toolaanroep. Met een beslissing kun je de aanroep goedkeuren, afwijzen of de argumenten vóór uitvoering aanpassen.

```php theme={null}
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('Deze factuur moet bewaard blijven.'),
    ]));
```

De booleans `true` en `false` kun je gebruiken als verkorte notatie voor respectievelijk goedkeuren en afwijzen. Alle openstaande toolaanroepen hebben een beslissing nodig. Geef je een onbekend, ontbrekend of al afgehandeld toolaanroep-ID op, dan wordt er een `ApprovalMismatchException` gegooid. Voor aanroepen zonder expliciete beslissing kun je met de methoden `approveRemaining` of `rejectRemaining` een standaardbeslissing opgeven.

```php theme={null}
$decisions = Decisions::from([
    'call_abc' => true,
])->rejectRemaining('Niet goedgekeurd.');

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

Wijs je af met een resultaat, zoals `Decision::reject('Niet goedgekeurd.')`, dan wordt dat aan het model teruggegeven en gaat de respons verder. Wijs je af zonder resultaat, dan stopt de generatieloop zodra de afwijzing is vastgelegd.

Toolgoedkeuring wordt ondersteund door de methoden `prompt`, `stream`, `queue`, `broadcast`, `broadcastNow` en `broadcastOnQueue`.

Tijdens streaming en broadcasting wordt een pauze weergegeven als een `tool_approval_request`-event. Gebruik je het [Vercel AI SDK-streamprotocol](#vercel-ai-sdk-protocol), dan worden goedkeuringsverzoeken en resultaten uitgezonden als de native tool-approval-parts van het protocol.

Bij agents in de queue wordt de resulterende respons doorgegeven aan de `then`-callback, en dispatcht Laravel ook het `ToolApprovalRequested`-event.

Laravel slaat het uitvoeringsresultaat van goedgekeurde tools op voordat het model wordt gevraagd verder te gaan. Mislukt de generatie daarna, dan zijn de goedkeuringen al afgehandeld. Stuur dezelfde goedkeuringsbeslissingen niet opnieuw, maar zet het gesprek voort met een gewone tekstprompt.

### Volledige goedkeuringsflow

De onderstaande routes tonen een volledige goedkeuringsflow. De `GET`-route geeft het chatscherm terug en de `POST`-route ontvangt vanuit het chatscherm ofwel een nieuwe tekstprompt, ofwel goedkeuringsbeslissingen. Dit voorbeeld gaat ervan uit dat het `User`-model van je applicatie de `HasConversations`-trait gebruikt.

```php theme={null}
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');
```

Is de status van de respons `awaiting_approval`, dan moet het chatscherm de openstaande goedkeuringen tonen en de keuzes van de gebruiker, met het toolaanroep-ID als sleutel, naar hetzelfde endpoint sturen.

```json theme={null}
{
    "decisions": {
        "call_abc": {
            "action": "approve"
        },
        "call_def": {
            "action": "reject",
            "result": "Deze factuur moet bewaard blijven."
        }
    }
}
```

Voor een gewoon chatbericht stuur je in plaats daarvan een `message`-waarde.

```json theme={null}
{
    "message": "Verwijder de oude facturen."
}
```

<Tip>
  De goedkeuringsflow geeft AI-agents krachtige bevoegdheden en laat je tegelijkertijd een menselijke controle inbouwen vóór uitvoering. Zet dit vooral in bij tools met onomkeerbare operaties, zoals het verwijderen van bestanden, betalingsverwerking of schrijfacties naar externe API's.
</Tip>

***

## Afbeeldingen genereren

Met de `Image`-klasse genereer je afbeeldingen. De providers OpenAI, Gemini en xAI worden ondersteund.

```php theme={null}
use Laravel\Ai\Image;

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

Je kunt kwaliteit, beeldverhouding en timeout opgeven.

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

Je kunt ook een referentieafbeelding bijvoegen en bewerken.

```php theme={null}
use Laravel\Ai\Files;

$image = Image::of('Update this photo to be in the style of an impressionist painting.')
    ->attachments([Files\Image::fromStorage('photo.jpg')])
    ->landscape()
    ->generate();
```

### Afbeeldingen opslaan

```php theme={null}
$path = $image->store();
$path = $image->storeAs('image.jpg');
$path = $image->storePublicly();
$path = $image->storePubliclyAs('image.jpg');
```

### Afbeeldingen genereren via de queue

```php theme={null}
use Laravel\Ai\Responses\ImageResponse;

Image::of('A donut sitting on the kitchen counter')
    ->portrait()
    ->queue()
    ->then(function (ImageResponse $image) {
        $path = $image->store();
    });
```

***

## Spraaksynthese (TTS)

Met de `Audio`-klasse zet je tekst om in spraak. De providers OpenAI en ElevenLabs worden ondersteund.

```php theme={null}
use Laravel\Ai\Audio;

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

Je kunt het geslacht van de stem, een specifiek voice-ID en spreekinstructies opgeven.

```php theme={null}
$audio = Audio::of('I love coding with Laravel.')->female()->generate();
$audio = Audio::of('I love coding with Laravel.')->voice('voice-id-or-name')->generate();
$audio = Audio::of('I love coding with Laravel.')->female()->instructions('Said like a pirate')->generate();
```

### Audio opslaan

```php theme={null}
$path = $audio->store();
$path = $audio->storeAs('audio.mp3');
$path = $audio->storePublicly();
$path = $audio->storePubliclyAs('audio.mp3');
```

### Audio genereren via de queue

```php theme={null}
use Laravel\Ai\Responses\AudioResponse;

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

***

## Transcriptie (STT)

Met de `Transcription`-klasse zet je audiobestanden om in tekst. De providers OpenAI, OpenAI Compatible, ElevenLabs, Groq, Mistral en Gemini worden ondersteund.

```php theme={null}
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;
```

### Sprekerscheiding (diarisatie)

Met `diarize()` krijg je een transcriptie die per spreker is gescheiden.

```php theme={null}
$transcript = Transcription::fromStorage('audio.mp3')->diarize()->generate();
```

### Transcriptie via de queue

```php theme={null}
use Laravel\Ai\Responses\TranscriptionResponse;

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

***

## Tekst samenvatten (text summarization)

Met de `summarize`-methode van Laravels `Stringable`-klasse vat je tekst samen. Standaard wordt er samengevat in maximaal drie zinnen en wordt het goedkoopste tekstmodel van de geconfigureerde provider gebruikt.

```php theme={null}
use Illuminate\Support\Str;

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

Je kunt ook het maximale aantal zinnen, de provider, het model en de timeout opgeven. De `Str`-klasse heeft daarnaast een statische variant.

```php theme={null}
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

Zet tekst om in vectorrepresentaties, bijvoorbeeld voor similarity search.

```php theme={null}
use Illuminate\Support\Str;

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

```php theme={null}
use Laravel\Ai\Embeddings;

// Meerdere teksten in één keer verwerken
$response = Embeddings::for(['Napa Valley has great wine.', 'Laravel is a PHP framework.'])->generate();
$response->embeddings; // [[0.123, 0.456, ...], [0.789, 0.012, ...]]
```

Je kunt ook provider, model en aantal dimensies opgeven.

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

### Multimodale embeddings

De `Embeddings::for`-methode accepteert niet alleen strings, maar ook afbeeldingen, audio, documenten en video als input, zodat je ook embeddings kunt genereren voor niet-tekstuele content. Gemini ondersteunt embeddings van afbeeldingen, audio, documenten en video; VoyageAI ondersteunt embeddings van afbeeldingen en video.

```php theme={null}
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);
```

Voor multimodale input gebruik je [dezelfde bestandsklassen als voor bijlagen](#bijlagen). Deze bestanden kunnen worden gemaakt vanaf een lokaal pad, een filesystem disk, een remote URL of Base64-gecodeerde content. Afbeeldingen, documenten en video's kunnen ook uit geüploade bestanden worden gemaakt, en documenten ook uit ruwe stringcontent.

```php theme={null}
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'));
```

<Info>
  VoyageAI staat niet toe dat media van remote URL's en Base64-gecodeerde media in dezelfde request worden gecombineerd. Lokale, storage- en geüploade bestanden worden verstuurd als Base64-gecodeerde content; tekstinput kan met beide mediabronnen worden gecombineerd. Raadpleeg de documentatie van elke provider voor de beschikbare multimodale modellen en inputs.
</Info>

### Vectorzoeken (pgvector)

Een configuratievoorbeeld voor vectorzoeken met PostgreSQL en de pgvector-extensie.

<Steps>
  <Step title="Migration maken">
    ```php theme={null}
    Schema::ensureVectorExtensionExists();

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

    // HNSW-index toevoegen
    $table->vector('embedding', dimensions: 1536)->index();
    ```
  </Step>

  <Step title="Model configureren">
    ```php theme={null}
    use Illuminate\Database\Eloquent\Casts\AsVector;

    protected function casts(): array
    {
        return ['embedding' => AsVector::class];
    }
    ```
  </Step>

  <Step title="Similarity search query">
    ```php theme={null}
    // Zoeken met een embedding-vector
    $documents = Document::query()
        ->whereVectorSimilarTo('embedding', $queryEmbedding, minSimilarity: 0.4)
        ->limit(10)
        ->get();

    // Geef je een string door, dan wordt de embedding automatisch gegenereerd
    $documents = Document::query()
        ->whereVectorSimilarTo('embedding', 'best wineries in Napa Valley')
        ->limit(10)
        ->get();
    ```
  </Step>
</Steps>

Er zijn ook low-level methoden beschikbaar.

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

### Embeddings cachen

Je kunt embeddings cachen om te voorkomen dat dezelfde tekst herhaaldelijk wordt verwerkt.

Configureer de standaard cache-instellingen in `config/ai.php`.

```php theme={null}
'caching' => [
    'embeddings' => [
        'cache' => true,
        'store' => env('CACHE_STORE', 'database'),
        'individually' => true,
        // ...
    ],
],
```

Is caching ingeschakeld, dan worden embeddings 30 dagen gecacht. De cachesleutel wordt gegenereerd op basis van provider, model, aantal dimensies en de inputcontent, zodat identieke requests het gecachte resultaat teruggeven en andere configuraties opnieuw genereren.

Standaard wordt de embedding van elke input onder een eigen sleutel gecacht, zodat je zelfs bij een andere set of volgorde van inputs cache-hits krijgt voor eerder geziene inputs. Wil je de volledige input onder één sleutel cachen, zet dan de configuratie `ai.caching.embeddings.individually` op `false`.

Je kunt de cache ook per request aansturen.

```php theme={null}
$response = Embeddings::for(['Napa Valley has great wine.'])->cache()->generate();
$response = Embeddings::for(['Napa Valley has great wine.'])->cache(seconds: 3600)->generate();

// Ook via Stringable
$embeddings = Str::of('Napa Valley has great wine.')->toEmbeddings(cache: true);
$embeddings = Str::of('Napa Valley has great wine.')->toEmbeddings(cache: 3600);
```

***

## Reranking

Je kunt zoekresultaten reranken (herschikken) op relevantie voor een query. De providers Cohere en Jina worden ondersteund.

```php theme={null}
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
```

Met `limit()` beperk je het aantal teruggegeven resultaten.

```php theme={null}
$response = Reranking::of($documents)->limit(5)->rerank('search query');
```

### Collecties reranken

Eloquent-collecties kun je direct reranken.

```php theme={null}
// Eén veld
$posts = Post::all()->rerank('body', 'Laravel tutorials');

// Meerdere velden (verstuurd als JSON)
$reranked = $posts->rerank(['title', 'body'], 'Laravel tutorials');

// Eigen tekstopbouw via een closure
$reranked = $posts->rerank(fn ($post) => $post->title.': '.$post->body, 'Laravel tutorials');

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

***

## Bestandsbeheer

Je kunt bestanden uploaden naar een AI-provider en er later naar verwijzen.

```php theme={null}
use Laravel\Ai\Files\Document;
use Laravel\Ai\Files\Image;

// Vanaf een pad
$response = Document::fromPath('/home/laravel/document.pdf')->put();
$response = Image::fromPath('/home/laravel/photo.jpg')->put();

// Vanuit storage
$response = Document::fromStorage('document.pdf', disk: 'local')->put();
$response = Image::fromStorage('photo.jpg', disk: 'local')->put();

// Vanaf een URL
$response = Document::fromUrl('https://example.com/document.pdf')->put();
$response = Image::fromUrl('https://example.com/photo.jpg')->put();

return $response->id;
```

Ook strings en formulieruploads worden ondersteund.

```php theme={null}
$stored = Document::fromString('Hello, World!', 'text/plain')->put();
$stored = Document::fromUpload($request->file('document'))->put();
```

### Verwijzen naar opgeslagen bestanden

Een geüpload bestand kun je via het bestands-ID als bijlage aan een agent meegeven.

```php theme={null}
use Laravel\Ai\Files;

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

### Bestanden ophalen en verwijderen

```php theme={null}
// Ophalen
$file = Document::fromId('file-id')->get();
$file->id;
$file->mimeType();

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

### Een provider opgeven

```php theme={null}
$response = Document::fromPath('/home/laravel/document.pdf')->put(provider: Lab::Anthropic);
```

### Providerspecifieke opties opgeven

Met de `withProviderOptions`-methode geef je providerspecifieke uploadopties door. Zo kun je bijvoorbeeld de `purpose` van een OpenAI-bestand instellen.

```php theme={null}
use Laravel\Ai\Files\Document;

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

Wil je per provider andere opties opgeven, geef dan een closure door.

```php theme={null}
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();
```

***

## Vector stores

Met vector stores beheer je documenten aan de kant van de provider.

```php theme={null}
use Laravel\Ai\Stores;

// Aanmaken
$store = Stores::create('Knowledge Base');
$store = Stores::create(
    name: 'Knowledge Base',
    description: 'Documentation.',
    expiresWhenIdleFor: days(30),
);
return $store->id;

// Ophalen
$store = Stores::get('store_id');
$store->id;
$store->name;
$store->fileCounts;
$store->ready;

// Verwijderen
Stores::delete('store_id');
$store->delete();
```

### Bestanden toevoegen aan een store

```php theme={null}
$store = Stores::get('store_id');

// Bestanden op verschillende manieren toevoegen
$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;
```

Je kunt ook metadata meegeven.

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

### Bestanden verwijderen uit een store

```php theme={null}
$store->remove('file_id');

// Ook het bestand zelf verwijderen
$store->remove('file_abc123', deleteFile: true);
```

***

## Failover

Geef je meerdere providers op als array, dan valt de SDK automatisch terug op de volgende provider wanneer de eerste faalt.

```php theme={null}
use App\Ai\Agents\SalesCoach;
use Laravel\Ai\Image;

// Failover voor agents
$response = (new SalesCoach)->prompt(
    'Analyze this sales transcript...',
    provider: [Lab::OpenAI, Lab::Anthropic],
);

// Failover voor afbeeldingsgeneratie
$image = Image::of('A donut sitting on the kitchen counter')
    ->generate(provider: [Lab::Gemini, Lab::xAI]);
```

***

## Testen

De Laravel AI SDK biedt fake-functionaliteit voor tests, zodat je kunt testen zonder de echte API aan te roepen.

Fake je een gequeued afbeeldings-, audio-, transcriptie- of embeddingsgeneratie, dan wordt de `then`-callback die je bij de generatie hebt geregistreerd aangeroepen met de fake-respons. Wil je dat de callback ook niet wordt uitgevoerd, gebruik dan daarnaast `Queue::fake()`.

### Agents testen

```php theme={null}
use App\Ai\Agents\SalesCoach;
use Laravel\Ai\Prompts\AgentPrompt;

// Fake met vaste responses
SalesCoach::fake();
SalesCoach::fake(['First response', 'Second response']);

// Dynamische responses via een closure
SalesCoach::fake(function (AgentPrompt $prompt) {
    return 'Response for: '.$prompt->prompt;
});

// Assertions
SalesCoach::assertPrompted('Analyze this...');
SalesCoach::assertPrompted(function (AgentPrompt $prompt) {
    return $prompt->contains('Analyze');
});
SalesCoach::assertPromptedTimes(3);
SalesCoach::assertNotPrompted('Missing prompt');
SalesCoach::assertNeverPrompted();
```

Er zijn ook assertions voor queueing.

```php theme={null}
use Laravel\Ai\QueuedAgentPrompt;

SalesCoach::assertQueued('Analyze this...');
SalesCoach::assertQueued(function (QueuedAgentPrompt $prompt) {
    return $prompt->contains('Analyze');
});
SalesCoach::assertNotQueued('Missing prompt');
SalesCoach::assertNeverQueued();
```

Met `preventStrayPrompts()` wordt er een exception gegooid zodra een prompt wordt aangeroepen die niet in de fake is gedefinieerd.

```php theme={null}
SalesCoach::fake()->preventStrayPrompts();
```

Fake je een agent met gestructureerde output, dan kun je de respons als array opgeven. De agent geeft dan een gestructureerde respons terug met de opgegeven data.

```php theme={null}
SalesCoach::fake([
    ['score' => 87],
]);
```

<Info>
  Wordt `fake()` op een agent met gestructureerde output aangeroepen zonder expliciete fake-data, dan genereert Laravel automatisch fake-data die voldoet aan het door de agent gedefinieerde schema.
</Info>

Voor het testen van anonieme agents gebruik je `AnonymousAgent::fake()`.

```php theme={null}
use Laravel\Ai\AnonymousAgent;

AnonymousAgent::fake(['Test response']);
```

### Afbeeldingsgeneratie testen

```php theme={null}
use Laravel\Ai\Image;
use Laravel\Ai\Prompts\ImagePrompt;

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

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

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

### Spraaksynthese testen

```php theme={null}
use Laravel\Ai\Audio;
use Laravel\Ai\Prompts\AudioPrompt;

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

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

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

### Transcriptie testen

```php theme={null}
use Laravel\Ai\Transcription;
use Laravel\Ai\Prompts\TranscriptionPrompt;

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

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

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

### Embeddings testen

```php theme={null}
use Laravel\Ai\Embeddings;
use Laravel\Ai\Prompts\EmbeddingsPrompt;

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

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

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

### Reranking testen

```php theme={null}
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();
```

### Bestanden testen

```php theme={null}
use Laravel\Ai\Files;
use Laravel\Ai\Contracts\Files\StorableFile;
use Laravel\Ai\Files\Document;

Files::fake();

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

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

### Vector stores testen

```php theme={null}
use Laravel\Ai\Stores;

Stores::fake(); // Bestandsoperaties worden ook gefaked

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

Je kunt ook assertions doen op bestandsoperaties op een store.

```php theme={null}
$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');

// Content verifiëren met een closure
$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

De Laravel AI SDK dispatcht de onderstaande events. Door naar deze events te luisteren kun je bijvoorbeeld loggen en monitoren.

<AccordionGroup>
  <Accordion title="Agentgerelateerd">
    * `AgentFailed` — wanneer de agentverwerking mislukt
    * `AgentFailedOver` — bij failover van een agent
    * `PromptingAgent` — vóór het versturen van een prompt
    * `AgentPrompted` — na het versturen van een prompt
    * `StreamingAgent` — bij de start van streaming
    * `AgentStreamed` — na afronding van streaming
    * `InvokingTool` — vóór een toolaanroep
    * `ToolInvoked` — na een toolaanroep
    * `ToolApprovalRequested` — wanneer toolgoedkeuring wordt aangevraagd
    * `ToolApprovalResolved` — nadat een toolgoedkeuring is afgehandeld
    * `ProviderFailedOver` — bij failover van een provider
    * `StartingStep` — bij de start van een stap
    * `StepCompleted` — na afronding van een stap
    * `StepFailed` — wanneer een stap mislukt
    * `ToolFailed` — wanneer de uitvoering van een tool mislukt
  </Accordion>

  <Accordion title="Afbeeldingen, audio en transcriptie">
    * `GeneratingImage` — vóór het genereren van een afbeelding
    * `ImageGenerated` — na het genereren van een afbeelding
    * `GeneratingAudio` — vóór het genereren van audio
    * `AudioGenerated` — na het genereren van audio
    * `GeneratingTranscription` — vóór een transcriptie
    * `TranscriptionGenerated` — na een transcriptie
  </Accordion>

  <Accordion title="Embeddings en reranking">
    * `GeneratingEmbeddings` — vóór het genereren van embeddings
    * `EmbeddingsGenerated` — na het genereren van embeddings
    * `Reranking` — vóór reranking
    * `Reranked` — na reranking
  </Accordion>

  <Accordion title="Bestanden en stores">
    * `StoringFile` — vóór het opslaan van een bestand
    * `FileStored` — na het opslaan van een bestand
    * `FileDeleted` — na het verwijderen van een bestand
    * `CreatingStore` — vóór het aanmaken van een store
    * `StoreCreated` — na het aanmaken van een store
    * `StoreDeleted` — na het verwijderen van een store
    * `AddingFileToStore` — vóór het toevoegen van een bestand aan een store
    * `FileAddedToStore` — na het toevoegen van een bestand aan een store
    * `RemovingFileFromStore` — vóór het verwijderen van een bestand uit een store
    * `FileRemovedFromStore` — na het verwijderen van een bestand uit een store
  </Accordion>
</AccordionGroup>


## Related topics

- [Laravel-updates van maart 2026](/nl/blog/changelog/202603.md)
- [Nieuwe functies in Laravel 13](/nl/blog/laravel-13-new-features.md)
- [Laravel AI SDK-integratie](/nl/packages/laravel-copilot-sdk/ai-sdk.md)
- [Laravel AI SDK-integratie - VOICEVOX for Laravel](/nl/packages/laravel-voicevox/ai-sdk.md)
- [Amazon Bedrock-driver voor de Laravel AI SDK](/nl/packages/laravel-amazon-bedrock.md)
