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

> Guía completa del Laravel AI SDK, que permite trabajar con múltiples proveedores de IA (OpenAI, Anthropic, Gemini y otros) mediante una interfaz unificada. Cubre agentes, generación de imágenes, audio, embeddings y pruebas.

## Introducción

[Laravel AI SDK](https://github.com/laravel/ai) ofrece una API unificada y expresiva para interactuar con proveedores de IA como OpenAI, Anthropic o Gemini. Con el AI SDK puedes construir agentes inteligentes con herramientas y salidas estructuradas, generar imágenes, sintetizar y transcribir voz, crear embeddings vectoriales y mucho más, todo mediante una interfaz consistente al estilo Laravel.

<Info>
  Laravel AI SDK es un paquete oficial añadido en Laravel 13. Se distribuye como `laravel/ai` y permite usar múltiples proveedores de IA con una API unificada.
</Info>

## Proveedores compatibles

| Función                     | Proveedores compatibles                                                                                        |
| --------------------------- | -------------------------------------------------------------------------------------------------------------- |
| Generación de texto         | OpenAI, OpenAI Compatible, Anthropic, Gemini, Azure, Bedrock, Groq, xAI, DeepSeek, Mistral, Ollama, OpenRouter |
| Generación de imágenes      | OpenAI, Gemini, xAI, Azure, Bedrock, OpenRouter                                                                |
| Síntesis de voz (TTS)       | OpenAI, ElevenLabs, Gemini                                                                                     |
| Reconocimiento de voz (STT) | OpenAI, ElevenLabs, Mistral, Gemini                                                                            |
| Embeddings                  | OpenAI, Gemini, Azure, Bedrock, Cohere, Mistral, Jina, VoyageAI, Ollama, OpenRouter                            |
| Reranking                   | Cohere, Jina, VoyageAI                                                                                         |
| Archivos                    | OpenAI, Anthropic, Gemini, Azure                                                                               |

## Instalación

<Steps>
  <Step title="Instalar el paquete">
    Instala Laravel AI SDK con Composer.

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

  <Step title="Publicar el archivo de configuración y las migraciones">
    Publica el archivo de configuración y las migraciones con el comando Artisan `vendor:publish`.

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

  <Step title="Ejecutar las migraciones">
    Ejecuta las migraciones de la base de datos. Se crearán las tablas `agent_conversations` y `agent_conversation_messages`, que se emplean para almacenar el historial de conversaciones.

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

## Configuración

### Variables de entorno

Configura las claves de API de los proveedores de IA que vayas a usar en el archivo `.env`.

```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=
```

Los modelos por defecto para texto, imágenes, audio, transcripción y embeddings pueden configurarse en `config/ai.php`.

### URL base personalizada

Si necesitas enrutar el tráfico a través de un servicio proxy, puedes definir una URL personalizada por proveedor.

```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'),
    ],
],
```

La URL base personalizada está disponible para OpenAI, Anthropic, Gemini, Groq, Cohere, DeepSeek, xAI y OpenRouter.

### Proveedor OpenAI-Compatible

Si usas una API compatible con OpenAI (LM Studio, vLLM, Together, Fireworks, gateways locales, etc.), puedes configurar el proveedor con el driver `openai-compatible`. El campo `url` es obligatorio y, si se indica `key`, se enviará como token Bearer.

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

Una vez configurado, puedes referenciarlo por nombre como cualquier otro proveedor.

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

Si defines un modelo de texto por defecto, no necesitarás especificarlo cada vez.

```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'),
        ],
    ],
],
```

El proveedor OpenAI-Compatible admite generación de texto, streaming, herramientas, salida estructurada y adjuntos de imagen. Si el endpoint requiere campos adicionales en el cuerpo de la petición, utiliza las [opciones del proveedor](#opciones-del-proveedor).

### Enum `Lab`

Para referenciar proveedores desde el código, utiliza el enum `Lab`.

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

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

***

## Agentes

Los agentes son la unidad básica del Laravel AI SDK. Puedes generar una clase de agente con el comando `make:agent`.

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

# Generar un agente con salida estructurada
php artisan make:agent SalesCoach --structured
```

Los agentes generados se ubican en el directorio `app/Ai/Agents/`. A continuación se muestra un ejemplo de agente que implementa todas las interfaces principales.

```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 del usuario"] --> B["Agent::prompt()"]
    B --> C["Añadir opciones"]
    C -->|"Si es Conversational"| D["Añadir historial de conversación"]
    C -->|"Si es HasTools"| E["Añadir lista de herramientas"]
    C -->|"Si es HasStructuredOutput"| F["Añadir esquema JSON"]
    D --> G["Enviar al proveedor de IA"]
    E --> G
    F --> G
    B --> G
    G --> H["Respuesta"]
    H -->|"Llamada a herramienta"| I["Ejecutar herramienta"]
    I --> G
    H -->|"Completado"| J["AgentResponse"]
```

### Prompt

Con el método `prompt()` envías un mensaje al agente.

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

El método estático `make()` crea una instancia resolviendo las dependencias desde el contenedor.

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

Puedes sobrescribir el proveedor, el modelo y el timeout mediante los argumentos de `prompt()`.

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

### Contexto de conversación

Si implementas la interfaz `Conversational` y defines el método `messages()`, podrás pasar el historial de conversación a la IA.

Con el trait `RemembersConversations` el historial se guarda y recupera automáticamente en la base de datos.

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

Inicia una conversación con `forUser()` y utiliza el `conversationId` devuelto para continuar la conversación con `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.');
```

### Salida estructurada

Al implementar la interfaz `HasStructuredOutput` y definir un esquema JSON en el método `schema()`, la respuesta de la IA se recibe como datos estructurados.

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

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

#### Objetos anidados

```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 de objetos

```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 (selección entre varios esquemas)

Cuando un valor puede coincidir con uno de varios esquemas, utiliza el método `anyOf`.

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

### Adjuntos

Puedes pasar documentos e imágenes al agente mediante el argumento `attachments`.

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

Los adjuntos de imagen funcionan de forma equivalente.

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

Con el método `stream()` la respuesta se devuelve por fragmentos. Es ideal para enviar respuestas largas al frontend en tiempo real.

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

El callback `then()` te permite ejecutar código cuando finalice el streaming.

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

También puedes iterar el stream manualmente.

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

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

#### Protocolo del Vercel AI SDK

Si utilizas el Vercel AI SDK en el frontend, invoca `usingVercelDataProtocol()`.

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

### Broadcasting

Puedes emitir los eventos del stream a un canal de broadcasting, por ejemplo con 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'));
}
```

Con `broadcastOnQueue()` la emisión se realiza a través de una cola.

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

#### Saltar eventos muy grandes

Algunas plataformas de broadcasting limitan el tamaño de los mensajes WebSocket a unos 10 KB. Los eventos de stream con mucha carga (por ejemplo, resultados grandes de herramientas) pueden superar ese límite y fallar al emitirse. Con el atributo `WithoutBroadcasting` puedes excluir determinados tipos de evento del 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;

    // ...
}
```

Los eventos excluidos no se emiten por broadcasting, pero se siguen guardando en la tabla `agent_conversation_messages`. De este modo, el frontend puede recuperar todos los datos de la herramienta cuando el stream haya finalizado. Funciona tanto de forma síncrona (`broadcast` / `broadcastNow`) como a través de colas (`broadcastOnQueue`).

### Colas

El método `queue()` permite enviar el prompt a una cola para procesarlo de forma asíncrona.

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

### Herramientas

Las herramientas permiten que la IA invoque funciones de tu código. Genera una clase de herramienta con el comando `make:tool`.

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

Registra la herramienta en el método `tools()` del agente.

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

#### Herramienta de búsqueda por similitud

Puedes añadir fácilmente una herramienta de búsqueda por similitud basada en embeddings vectoriales.

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

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

También se pueden especificar opciones.

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

O bien definir la lógica de búsqueda a medida mediante una closure.

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

`withDescription()` permite personalizar la descripción de la herramienta.

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

### Herramientas de almacenamiento de archivos

La factoría `FileStorage` proporciona al agente acceso a los [discos del sistema de ficheros](/es/filesystem) de Laravel. El método `all` devuelve un conjunto de herramientas para listar, leer, generar URLs, escribir, eliminar y copiar archivos en el disco indicado.

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

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

Si solo quieres permitir acceso de lectura, utiliza el método `readOnly`.

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

Estos métodos devuelven un `Illuminate\Support\Collection`, por lo que puedes refinar todavía más las herramientas que ofreces.

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

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

### Herramientas MCP

Si tu aplicación utiliza [Laravel MCP](/es/mcp), puedes ofrecer al agente las herramientas expuestas por un servidor [Model Context Protocol](https://modelcontextprotocol.io). El [cliente MCP de Laravel](/es/mcp#mcp-client) se conecta a servidores MCP remotos o locales y puedes pasar sus herramientas directamente al agente.

<Info>
  Para usar herramientas MCP debes tener instalado el paquete [Laravel MCP](/es/mcp) en tu aplicación.
</Info>

El método `tools` del cliente MCP devuelve una colección, por lo que puedes expandirla con el operador spread `...` dentro del array `tools` del agente.

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

El AI SDK envuelve cada herramienta MCP automáticamente para que el agente pueda invocarlas como cualquier otra. También puedes usar [clientes MCP con nombre](/es/mcp#named-clients).

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

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

O conectar con un [servidor MCP local](/es/mcp#client-connecting).

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

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

Consulta la [documentación del cliente MCP](/es/mcp#mcp-client) para más información sobre la creación de clientes y la autenticación (Bearer tokens, OAuth, etc.).

### Herramientas del proveedor

Son herramientas especiales implementadas de forma nativa por los proveedores de IA.

#### Búsqueda web

Añade búsqueda web al agente. Compatible con Anthropic, OpenAI, Gemini y OpenRouter.

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

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

Puedes limitar el número de resultados, restringir dominios o indicar una ubicación.

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

#### Recuperación web

Herramienta para descargar el contenido de una URL indicada. Compatible con Anthropic y Gemini.

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

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

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

#### Búsqueda de archivos

Herramienta que busca documentos en un vector store. Compatible con OpenAI y Gemini.

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

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

// Múltiples stores
new FileSearch(stores: ['store_1', 'store_2']);

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

También se admiten filtros más complejos con `FileSearchQuery`.

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

### Subagentes

Un agente puede devolverse como resultado del método `tools()` de otro agente. Al registrarlo como herramienta, el agente padre puede delegar tareas específicas al subagente e incorporar el resultado a su respuesta. Es útil cuando un agente generalista necesita acceder a agentes especializados con instrucciones, herramientas, modelos o proveedores concretos.

Por ejemplo, un agente de atención al cliente puede delegar las preguntas sobre política de reembolsos a un agente especialista.

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

Para personalizar cómo se le presenta un subagente al agente padre, implementa la interfaz `CanActAsTool` en el subagente y define el nombre y la descripción con los que se expondrá como herramienta.

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

Cuando un subagente no implementa `CanActAsTool`, Laravel usa el nombre de la clase como nombre de la herramienta y genera automáticamente una descripción genérica. Cada invocación de un subagente es independiente y no hereda el historial de conversación del agente padre.

### Middleware

Puedes añadir middleware al agente para interceptar los prompts y las respuestas.

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

Implementa la interfaz `HasMiddleware` en el agente y registra el middleware en el método `middleware()`.

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

Ejemplo de implementación de la clase de middleware.

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

Con `then()` también puedes ejecutar código después de recibir la respuesta.

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

### Agentes anónimos

Con la función auxiliar `agent()` puedes utilizar agentes anónimos sin definir una clase.

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

También puedes crear agentes anónimos con salida estructurada.

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

### Configuración del agente (atributos PHP)

Puedes declarar la configuración por defecto del agente de forma declarativa mediante atributos PHP.

| Atributo              | Descripción                                      |
| --------------------- | ------------------------------------------------ |
| `#[Provider]`         | Indica el proveedor a usar                       |
| `#[Model]`            | Indica el modelo a usar                          |
| `#[MaxSteps]`         | Número máximo de pasos de llamada a herramientas |
| `#[MaxTokens]`        | Número máximo de tokens                          |
| `#[Temperature]`      | Parámetro de temperatura (0.0 a 1.0)             |
| `#[TopP]`             | Probabilidad de nucleus sampling (0.0 a 1.0)     |
| `#[Timeout]`          | Timeout (segundos)                               |
| `#[UseCheapestModel]` | Selecciona automáticamente el modelo más barato  |
| `#[UseSmartestModel]` | Selecciona automáticamente el modelo más potente |

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

También hay atributos de conveniencia para la selección de modelo.

```php theme={null}
// Usar el modelo más barato
#[UseCheapestModel]
class SimpleSummarizer implements Agent
{
    use Promptable;
}

// Usar el modelo más potente
#[UseSmartestModel]
class ComplexReasoner implements Agent
{
    use Promptable;
}
```

### Opciones del proveedor

Al implementar la interfaz `HasProviderOptions` puedes pasar opciones específicas del proveedor.

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

***

## Generación de imágenes

La clase `Image` permite generar imágenes. Los proveedores compatibles son OpenAI, Gemini y xAI.

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

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

Puedes indicar la calidad, la relación de aspecto y el timeout.

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

También puedes adjuntar imágenes de referencia para editarlas.

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

### Guardar imágenes

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

### Generación de imágenes en cola

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

***

## Síntesis de voz (TTS)

La clase `Audio` convierte texto en voz. Los proveedores compatibles son OpenAI y ElevenLabs.

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

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

Puedes especificar el género, un ID o nombre concreto de voz e instrucciones sobre el estilo de habla.

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

### Guardar el audio

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

### Generar audio en cola

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

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

***

## Transcripción (STT)

La clase `Transcription` convierte archivos de audio en texto. Los proveedores compatibles son OpenAI, ElevenLabs y Mistral.

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

### Diarización (separación de hablantes)

Con `diarize()` obtienes una transcripción con separación por hablante.

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

### Transcripción en cola

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

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

***

## Embeddings

Convierte texto en representaciones vectoriales para búsquedas por similitud y casos similares.

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

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

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

// Procesar varios textos a la vez
$response = Embeddings::for(['Napa Valley has great wine.', 'Laravel is a PHP framework.'])->generate();
$response->embeddings; // [[0.123, 0.456, ...], [0.789, 0.012, ...]]
```

También puedes indicar el proveedor, el modelo y las dimensiones.

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

### Búsqueda vectorial (pgvector)

Ejemplo de configuración de búsqueda vectorial con PostgreSQL y la extensión pgvector.

<Steps>
  <Step title="Crear la migración">
    ```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();
    });

    // Añadir índice HNSW
    $table->vector('embedding', dimensions: 1536)->index();
    ```
  </Step>

  <Step title="Configurar el modelo">
    ```php theme={null}
    protected function casts(): array
    {
        return ['embedding' => 'array'];
    }
    ```
  </Step>

  <Step title="Consulta de búsqueda por similitud">
    ```php theme={null}
    // Buscar mediante un vector de embedding
    $documents = Document::query()
        ->whereVectorSimilarTo('embedding', $queryEmbedding, minSimilarity: 0.4)
        ->limit(10)
        ->get();

    // Si pasas una cadena, se generan los embeddings automáticamente
    $documents = Document::query()
        ->whereVectorSimilarTo('embedding', 'best wineries in Napa Valley')
        ->limit(10)
        ->get();
    ```
  </Step>
</Steps>

También dispones de métodos de bajo nivel.

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

### Cacheo de embeddings

Puedes cachear los embeddings para no volver a generarlos para el mismo texto.

Configura los valores por defecto de cacheo en `config/ai.php`.

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

También puedes controlar la caché por cada petición.

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

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

***

## Reranking

Puedes reordenar los resultados de búsqueda según su relevancia respecto a una consulta. Los proveedores compatibles son Cohere y Jina.

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

Con `limit()` puedes restringir el número de resultados devueltos.

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

### Reranking de colecciones

Puedes reordenar directamente colecciones de Eloquent.

```php theme={null}
// Un solo campo
$posts = Post::all()->rerank('body', 'Laravel tutorials');

// Varios campos (se envían como JSON)
$reranked = $posts->rerank(['title', 'body'], 'Laravel tutorials');

// Generación de texto personalizada con una closure
$reranked = $posts->rerank(fn ($post) => $post->title.': '.$post->body, 'Laravel tutorials');

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

***

## Gestión de archivos

Puedes subir archivos al proveedor de IA y referenciarlos más tarde.

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

// Desde una ruta
$response = Document::fromPath('/home/laravel/document.pdf')->put();
$response = Image::fromPath('/home/laravel/photo.jpg')->put();

// Desde el sistema de ficheros
$response = Document::fromStorage('document.pdf', disk: 'local')->put();
$response = Image::fromStorage('photo.jpg', disk: 'local')->put();

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

return $response->id;
```

También puedes trabajar con cadenas o subidas de formulario.

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

### Referenciar archivos ya subidos

Puedes adjuntar un archivo previamente subido a un agente por su ID.

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

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

### Obtener y eliminar archivos

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

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

### Indicar el proveedor

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

### Opciones específicas del proveedor

Con el método `withProviderOptions` puedes pasar opciones de subida propias del proveedor. Por ejemplo, el `purpose` de un archivo de OpenAI.

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

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

Si necesitas opciones distintas para cada proveedor, pasa una closure.

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

Los vector stores permiten gestionar los documentos en el propio proveedor.

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

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

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

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

### Añadir archivos a un store

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

// Distintas formas de añadir archivos
$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;
```

También puedes adjuntar metadatos.

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

### Eliminar archivos de un store

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

// Si además quieres eliminar el archivo en sí
$store->remove('file_abc123', deleteFile: true);
```

***

## Failover

Si indicas varios proveedores en un array, cuando el primero falla se recurre automáticamente al siguiente.

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

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

// Failover en la generación de imágenes
$image = Image::of('A donut sitting on the kitchen counter')
    ->generate(provider: [Lab::Gemini, Lab::xAI]);
```

***

## Pruebas

Laravel AI SDK ofrece utilidades de fake para probar el código sin invocar realmente a la API.

### Pruebas de agentes

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

// Respuesta fija
SalesCoach::fake();
SalesCoach::fake(['First response', 'Second response']);

// Respuestas dinámicas mediante closure
SalesCoach::fake(function (AgentPrompt $prompt) {
    return 'Response for: '.$prompt->prompt;
});

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

También hay aserciones para prompts encolados.

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

Con `preventStrayPrompts()` se lanza una excepción si se invoca un prompt no definido en el fake.

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

Si el agente devuelve una salida estructurada, puedes indicar la respuesta como array. El agente devolverá una respuesta estructurada que incluye los datos proporcionados.

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

<Info>
  Si llamas a `fake()` sobre un agente con salida estructurada sin proporcionar datos, Laravel generará datos falsos que respeten el esquema definido por el agente.
</Info>

Para probar agentes anónimos utiliza `AnonymousAgent::fake()`.

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

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

### Pruebas de generación de imágenes

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

### Pruebas de síntesis de voz

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

### Pruebas de transcripción

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

### Pruebas de embeddings

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

### Pruebas de reranking

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

### Pruebas de archivos

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

### Pruebas de vector stores

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

Stores::fake(); // También hace fake de las operaciones de archivo

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

También puedes aplicar aserciones a operaciones de archivos sobre un 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');

// Validar el contenido con una 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!');
```

***

## Eventos

Laravel AI SDK despacha los siguientes eventos. Puedes escucharlos para generar logs, monitorizar o realizar otras acciones.

<AccordionGroup>
  <Accordion title="Relacionados con el agente">
    * `PromptingAgent` — antes de enviar el prompt
    * `AgentPrompted` — después de enviar el prompt
    * `StreamingAgent` — al iniciar el streaming
    * `AgentStreamed` — al finalizar el streaming
    * `InvokingTool` — antes de invocar una herramienta
    * `ToolInvoked` — después de invocar una herramienta
  </Accordion>

  <Accordion title="Imágenes, audio y transcripción">
    * `GeneratingImage` — antes de generar la imagen
    * `ImageGenerated` — después de generar la imagen
    * `GeneratingAudio` — antes de generar el audio
    * `AudioGenerated` — después de generar el audio
    * `GeneratingTranscription` — antes de transcribir
    * `TranscriptionGenerated` — después de transcribir
  </Accordion>

  <Accordion title="Embeddings y reranking">
    * `GeneratingEmbeddings` — antes de generar los embeddings
    * `EmbeddingsGenerated` — después de generar los embeddings
    * `Reranking` — antes del reranking
    * `Reranked` — después del reranking
  </Accordion>

  <Accordion title="Archivos y stores">
    * `StoringFile` — antes de guardar el archivo
    * `FileStored` — después de guardar el archivo
    * `FileDeleted` — después de eliminar el archivo
    * `CreatingStore` — antes de crear el store
    * `StoreCreated` — después de crear el store
    * `AddingFileToStore` — antes de añadir un archivo al store
    * `FileAddedToStore` — después de añadir un archivo al store
    * `RemovingFileFromStore` — antes de eliminar un archivo del store
    * `FileRemovedFromStore` — después de eliminar un archivo del store
  </Accordion>
</AccordionGroup>


## Related topics

- [Resumen de las novedades de Laravel 13](/es/blog/laravel-13-new-features.md)
- [Actualización de Laravel — Marzo de 2026](/es/blog/changelog/202603.md)
- [Integración con Laravel AI SDK](/es/packages/laravel-copilot-sdk/ai-sdk.md)
- [Integración con Laravel AI SDK - VOICEVOX for Laravel](/es/packages/laravel-voicevox/ai-sdk.md)
- [Driver de Amazon Bedrock para Laravel AI SDK](/es/packages/laravel-amazon-bedrock.md)
