Laravel AI SDK supporta di default i principali servizi AI (OpenAI, Anthropic, Gemini, Mistral). Ma nei casi seguenti i provider standard non bastano.
Servizi AI emergenti non ancora ufficialmente supportati
Vuoi passare attraverso un gateway di modelli interno o un livello di gestione fatturazione aziendale
Server di inferenza on-premise con protocolli o metodi di autenticazione proprietari
In queste situazioni implementi un provider personalizzato e lo registri nel AiManager dell’SDK: potrai poi usarlo con la stessa API dei provider standard.
Per API OpenAI-compatibili usa il driver integratoDa SDK 0.9 è disponibile il driver openai-compatible. Per server di inferenza interni OpenAI-compatibili (per esempio l’endpoint OpenAI-compatibile di Ollama) basta aggiungere una voce a config/ai.php senza implementare un provider custom.
Laravel AI SDK è composto da due livelli: Provider e Gateway.
Layer
Ruolo
Esempi
Provider
Interfaccia lato app. Risoluzione del nome modello e conservazione della configurazione
OpenAiProvider, AnthropicProvider
Gateway
Invia la singola richiesta API concreta
AnthropicGateway, OpenAiCompatibleGateway
Tutti i provider estendono la classe astratta Laravel\Ai\Providers\Provider e implementano i contract (interfacce) delle funzionalità che offrono. Il loop multi-step con chiamata di tool è realizzato da TextGenerationLoop che invoca ripetutamente il gateway.
Interfaccia che i provider di generazione testo implementano (src/Contracts/Providers/TextProvider.php).
interface TextProvider extends Provider{ public function prompt(AgentPrompt $prompt): AgentResponse; public function stream(AgentPrompt $prompt): StreamableAgentResponse; public function useTextGateway(StepTextGateway $gateway): self; public function textGenerationLoop(): TextGenerationLoop; public function defaultTextModel(): string; public function cheapestTextModel(): string; public function smartestTextModel(): string;}
L’implementazione di prompt() e stream() può essere delegata ai trait esistenti (GeneratesText, StreamsText), quindi in pratica devi implementare quattro metodi: i tre che restituiscono i nomi dei modelli e textGateway(). Il loop multi-step per i tool è gestito da TextGenerationLoop: il gateway elabora solo la richiesta di un singolo step.
Esempio di implementazione: provider personalizzato
Esempio di registrazione di un servizio di inferenza proprietario con un provider chiamato my-inference.
1
Creare la classe provider
Crea app/Ai/Providers/MyInferenceProvider.php.
<?phpdeclare(strict_types=1);namespace App\Ai\Providers;use Illuminate\Contracts\Events\Dispatcher;use Laravel\Ai\Contracts\Gateway\StepTextGateway;use Laravel\Ai\Contracts\Providers\TextProvider;use Laravel\Ai\Providers\Concerns\GeneratesText;use Laravel\Ai\Providers\Concerns\HasTextGateway;use Laravel\Ai\Providers\Concerns\StreamsText;use Laravel\Ai\Providers\Provider;class MyInferenceProvider extends Provider implements TextProvider{ use GeneratesText; use HasTextGateway; use StreamsText; public function __construct(protected array $config, protected Dispatcher $events) { // } /** * Get the credentials for the provider. */ public function providerCredentials(): array { return ['key' => $this->config['key'] ?? null]; } /** * Get the provider's text gateway. */ public function textGateway(): StepTextGateway { return $this->textGateway ??= new \App\Ai\Gateway\MyInferenceGateway($this->events); } public function defaultTextModel(): string { return $this->config['models']['text']['default'] ?? 'my-model-v1'; } public function cheapestTextModel(): string { return $this->config['models']['text']['cheapest'] ?? 'my-model-v1'; } public function smartestTextModel(): string { return $this->config['models']['text']['smartest'] ?? 'my-model-v1'; }}
2
Registrarlo in AppServiceProvider
Nel metodo boot di App\Providers\AppServiceProvider registra il provider con extend().
<?phpnamespace App\Providers;use App\Ai\Providers\MyInferenceProvider;use Illuminate\Contracts\Events\Dispatcher;use Illuminate\Support\ServiceProvider;use Laravel\Ai\AiManager;class AppServiceProvider extends ServiceProvider{ public function boot(): void { $this->app->make(AiManager::class)->extend( 'my-inference', fn (array $config) => new MyInferenceProvider( $config, $this->app->make(Dispatcher::class) ) ); }}
Interfaccia definita in src/Contracts/Gateway/StepTextGateway.php. Il gateway gestisce la richiesta di un solo step della conversazione e restituisce uno StepResponse. Il loop delle chiamate di tool è gestito dal TextGenerationLoop chiamante.
Il vecchio contract TextGateway (generateText(), stream(), onToolInvocation()) presente fino alla 0.8 è stato rimosso nella 0.9. Se hai gateway custom devi migrare a StepTextGateway. onToolInvocation() è stato spostato in TextGenerationLoop.
Se vuoi supportare la chiamata di tool (function calling), in generateTextStep() includi le tool call in toolCalls e imposta finishReason su FinishReason::ToolCalls. L’esecuzione dei tool e il passaggio allo step successivo sono gestiti automaticamente da TextGenerationLoop. Come riferimento consulta AnthropicGateway.php.
Per testare un agente che usa un provider custom, usa il metodo fake() della classe Agent. Indipendentemente dal fatto che sia custom o meno, un gateway fake viene assegnato al provider.
<?phpnamespace Tests\Feature;use App\Ai\Agents\SummaryAgent;use Illuminate\Foundation\Testing\RefreshDatabase;use Tests\TestCase;class SummaryAgentTest extends TestCase{ use RefreshDatabase; public function test_summary_agent_returns_text(): void { SummaryAgent::fake(['Questo è un riassunto.']); $response = SummaryAgent::make()->prompt('Testo di un articolo lungo...', provider: 'my-inference'); $this->assertEquals('Questo è un riassunto.', $response->text); SummaryAgent::assertPrompted('Testo di un articolo lungo...'); }}
Da 0.9, le risposte di Agent::fake() passano attraverso lo stesso TextGenerationLoop del provider reale. Se imposti una chiamata di tool fake su un agente che non registra tool, viene sollevata NoSuchToolException.
Puoi anche registrare un provider di test tramite il container usando extend().
public function test_with_mock_provider(): void{ $this->app->make(AiManager::class)->extend( 'my-inference', function (array $config) { $events = $this->app->make(\Illuminate\Contracts\Events\Dispatcher::class); return new MyInferenceProvider($config, $events); } ); // codice del test}