Zum Hauptinhalt springen

Wann Sie einen eigenen Provider brauchen

Das Laravel AI SDK unterstützt OpenAI, Anthropic, Gemini, Mistral und andere zentrale KI-Dienste out of the box. In folgenden Fällen reichen die Standard-Provider aber nicht:
  • Ein aufstrebender KI-Dienst, der noch nicht offiziell unterstützt wird.
  • Sie möchten über ein internes Modell-Gateway oder eine Abrechnungsschicht laufen.
  • Ein On-Prem-Inferenzserver mit eigenem Protokoll oder Auth-Verfahren.
In solchen Fällen implementieren Sie einen eigenen Provider und registrieren ihn am AiManager des SDK. Die Nutzung ist dann identisch zu den Standard-Providern.
Für OpenAI-kompatible APIs den eingebauten Driver nutzenAb SDK 0.9 ist der Treiber openai-compatible standardmäßig verfügbar. Für interne OpenAI-kompatible Inferenzserver (etwa den OpenAI-kompatiblen Endpoint von Ollama) genügt eine Ergänzung in config/ai.php — eine eigene Provider-Implementierung ist nicht nötig.
'my-server' => [
    'driver' => 'openai-compatible',
    'key'    => env('MY_SERVER_API_KEY'),
    'url'    => env('MY_SERVER_URL', 'http://localhost:8080/v1'),
    'models' => [
        'text' => ['default' => 'llama3.3-70b'],
    ],
],

Überblick der Architektur

Zweischichtiger Aufbau

Das Laravel AI SDK besteht aus zwei Schichten: Provider und Gateway.
SchichtAufgabeBeispiel
ProviderSchnittstelle Richtung Anwendung; Modellauflösung, KonfigurationshaltungOpenAiProvider, AnthropicProvider
GatewaySendet den eigentlichen einzelnen API-RequestAnthropicGateway, OpenAiCompatibleGateway
Alle Provider erben von der abstrakten Klasse Laravel\Ai\Providers\Provider und implementieren feature-spezifische Contracts. Die mehrstufige Schleife mit Tool-Aufrufen läuft in TextGenerationLoop, das das Gateway wiederholt aufruft.

Übersicht der Contracts

Sie implementieren nur die Contracts der Funktionen, die Sie anbieten wollen.
ContractNamespaceFunktion
TextProviderLaravel\Ai\Contracts\Providers\TextProviderTextgenerierung / Agent
EmbeddingProviderLaravel\Ai\Contracts\Providers\EmbeddingProviderVektor-Embeddings
ImageProviderLaravel\Ai\Contracts\Providers\ImageProviderBildgenerierung
AudioProviderLaravel\Ai\Contracts\Providers\AudioProviderSprachsynthese (TTS)
TranscriptionProviderLaravel\Ai\Contracts\Providers\TranscriptionProviderSpracherkennung (STT)
In den meisten Fällen genügt die Implementierung von TextProvider.

Der TextProvider-Contract

Das ist das Interface, das ein Text-Provider implementiert (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;
}
Die Implementierung von prompt() und stream() können Sie an vorhandene Traits (GeneratesText, StreamsText) abgeben — Sie müssen also nur drei Methoden für die Modellnamen und zusätzlich textGateway() implementieren (insgesamt vier). Die mehrstufige Tool-Schleife übernimmt TextGenerationLoop; das Gateway verarbeitet nur den Request eines einzelnen Schritts.

Implementierungsbeispiel: eigener Provider

Beispiel: Ein Inferenzdienst mit eigener API wird als Provider my-inference registriert.
1

Provider-Klasse anlegen

Legen Sie app/Ai/Providers/MyInferenceProvider.php an.
<?php

declare(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

Im AppServiceProvider registrieren

Registrieren Sie den Provider in der boot-Methode Ihres App\Providers\AppServiceProvider per extend().
<?php

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

Provider in config/ai.php ergänzen

'providers' => [
    // ... bestehende Provider ...

    'my-inference' => [
        'driver' => 'my-inference',
        'key'    => env('MY_INFERENCE_API_KEY'),
        'url'    => env('MY_INFERENCE_URL', 'http://localhost:8080'),
    ],
],
In der .env ergänzen Sie:
MY_INFERENCE_API_KEY=your-api-key
MY_INFERENCE_URL=https://inference.example.internal
4

Aus einem Agenten heraus nutzen

Nach der Registrierung geben Sie in prompt() den Provider-Namen an — die Nutzung entspricht den Standard-Providern.
use App\Ai\Agents\SummaryAgent;

$response = SummaryAgent::make()->prompt('Bitte fasse diesen Artikel zusammen.', provider: 'my-inference');

echo $response->text;
Als Default-Provider ändern Sie den default-Key in config/ai.php.
'default' => 'my-inference',

Ein eigenes Gateway implementieren

Für Dienste mit eigener, nicht OpenAI-kompatibler API brauchen Sie ein Gateway, das den StepTextGateway-Contract implementiert.

Der Contract StepTextGateway

Definiert in src/Contracts/Gateway/StepTextGateway.php. Das Gateway verarbeitet einen einzelnen Schritt der Konversation und gibt eine StepResponse zurück. Die Tool-Schleife übernimmt der aufrufende TextGenerationLoop.
interface StepTextGateway
{
    public function generateTextStep(
        TextProvider $provider,
        string $model,
        ?string $instructions,
        array $messages,
        array $tools,
        ?array $schema,
        ?TextGenerationOptions $options,
        ?int $timeout,
        StepContext $stepContext,
    ): StepResponse;

    public function generateStreamStep(
        string $invocationId,
        TextProvider $provider,
        string $model,
        ?string $instructions,
        array $messages,
        array $tools,
        ?array $schema,
        ?TextGenerationOptions $options,
        ?int $timeout,
        StepContext $stepContext,
    ): Generator;
}
Der ältere TextGateway-Contract (generateText(), stream(), onToolInvocation()) aus 0.8 wurde in 0.9 entfernt. Bestehende eigene Gateways müssen auf StepTextGateway migriert werden. Das onToolInvocation() bei Tool-Aufrufen ist in den TextGenerationLoop gewandert.

Beispiel eines eigenen Gateways

Ein Gerüst, das HTTP-Requests an eine eigene Inferenz-API sendet.
<?php

declare(strict_types=1);

namespace App\Ai\Gateway;

use Generator;
use Illuminate\Contracts\Events\Dispatcher;
use Illuminate\Support\Facades\Http;
use Laravel\Ai\Contracts\Gateway\StepTextGateway;
use Laravel\Ai\Contracts\Providers\TextProvider;
use Laravel\Ai\Gateway\StepContext;
use Laravel\Ai\Gateway\StepResponse;
use Laravel\Ai\Gateway\TextGenerationOptions;
use Laravel\Ai\Responses\Data\FinishReason;
use Laravel\Ai\Responses\Data\Meta;
use Laravel\Ai\Responses\Data\Usage;

class MyInferenceGateway implements StepTextGateway
{
    public function __construct(protected Dispatcher $events) {}

    public function generateTextStep(
        TextProvider $provider,
        string $model,
        ?string $instructions,
        array $messages,
        array $tools,
        ?array $schema,
        ?TextGenerationOptions $options,
        ?int $timeout,
        StepContext $stepContext,
    ): StepResponse {
        $credentials = $provider->providerCredentials();
        $config      = $provider->additionalConfiguration();

        $response = Http::withToken($credentials['key'])
            ->baseUrl($config['url'])
            ->timeout($timeout ?? 30)
            ->post('/generate', [
                'model'        => $model,
                'instructions' => $instructions,
                'messages'     => $this->formatMessages($messages),
            ]);

        $data = $response->json();

        return new StepResponse(
            text: $data['output']['text'] ?? '',
            toolCalls: [],
            finishReason: FinishReason::Stop,
            usage: new Usage(
                promptTokens: $data['usage']['input_tokens'] ?? 0,
                completionTokens: $data['usage']['output_tokens'] ?? 0,
            ),
            meta: new Meta(
                provider: $provider->name(),
                model: $model,
            ),
        );
    }

    public function generateStreamStep(
        string $invocationId,
        TextProvider $provider,
        string $model,
        ?string $instructions,
        array $messages,
        array $tools,
        ?array $schema,
        ?TextGenerationOptions $options,
        ?int $timeout,
        StepContext $stepContext,
    ): Generator {
        // Streaming-Implementierung (ausgelassen)
        yield from [];

        return null;
    }

    protected function formatMessages(array $messages): array
    {
        return array_map(fn ($message) => [
            'role'    => $message->role->value,
            'content' => $message->content,
        ], $messages);
    }
}
Für Tool-Aufrufe (Function Calling) legen Sie in generateTextStep() das Ergebnis der Tool-Auswertung in toolCalls ab und setzen finishReason auf FinishReason::ToolCalls. Die Tool-Ausführung und den nächsten Schritt übernimmt TextGenerationLoop. Referenzimplementierung: AnthropicGateway.php.

Wie Sie testen

Das fake() der Agent-Klasse verwenden

In Tests der Agent-Klasse mit Ihrem eigenen Provider verwenden Sie die fake()-Methode der Agent-Klasse. Sie setzt unabhängig vom Provider ein Fake-Gateway.
<?php

namespace 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(['Dies ist die Zusammenfassung.']);

        $response = SummaryAgent::make()->prompt('Sehr langer Artikeltext …', provider: 'my-inference');

        $this->assertEquals('Dies ist die Zusammenfassung.', $response->text);

        SummaryAgent::assertPrompted('Sehr langer Artikeltext …');
    }
}
Ab 0.9 laufen Agent::fake()-Responses durch denselben TextGenerationLoop wie echte Provider. Definieren Sie Fake-Tool-Aufrufe bei einem Agenten ohne Tools, wird eine NoSuchToolException geworfen.

Mock-Provider per extend()

Sie können auch einen Test-Provider über den Container registrieren.
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);
        }
    );

    // Testcode
}

OllamaProvider.php — schlichtes Provider-Beispiel

Ein minimaler Provider, der einen lokalen Modell-Server anbindet — als Vorlage für eigene Provider.

AnthropicGateway.php — Beispiel-Gateway

Beispielimplementierung eines Gateways, das StepTextGateway implementiert. Zeigt generateTextStep() und generateStreamStep().

StepTextGateway Contract

Definition des Interfaces, das Text-Gateways implementieren.

TextProvider Contract

Definition des Interfaces, das Text-Provider implementieren.
Zuletzt geändert am 13. Juli 2026