← back to blog

Laravel AI SDK: A Practical Guide to Building AI-Powered Features

2026-03-23·7 min read·deep-dive

Laravel now has an official AI SDK that provides a unified API for working with OpenAI, Claude, Gemini, and more. No more juggling multiple packages or building your own abstractions. Here's how to actually use it in production.

Installation and Setup

composer require laravel/ai
php artisan vendor:publish --provider="Laravel\Ai\AiServiceProvider"
php artisan migrate

The migration creates tables for conversation history. Add your API keys to .env:

ANTHROPIC_API_KEY=your-key-here
OPENAI_API_KEY=your-key-here

The SDK supports OpenAI, Anthropic, Gemini, Azure, Groq, xAI, DeepSeek, Mistral, and Ollama out of the box. You only need keys for the providers you use.

Creating Your First Agent

Agents are the core concept. An agent is a class that defines how your AI feature behaves -- its instructions, tools, and output format.

php artisan make:agent ContentModerator

This generates a class in app/Ai/Agents/:

<?php

namespace App\Ai\Agents;

use Laravel\Ai\Contracts\Agent;
use Laravel\Ai\Promptable;

class ContentModerator implements Agent
{
    use Promptable;

    public function instructions(): string
    {
        return <<<'PROMPT'
        You are a content moderation assistant. Analyze the given text and
        determine if it violates any community guidelines. Check for:
        - Hate speech or discrimination
        - Spam or promotional content
        - Personal information exposure
        - Explicit or inappropriate content

        Be precise. Only flag content that clearly violates guidelines.
        PROMPT;
    }
}

Use it anywhere in your application:

$response = (new ContentModerator)->prompt($userSubmittedContent);
return (string) $response;

That's it. One class, one method call. The SDK handles the API communication, retries, and error handling.

Structured Output: Get Predictable JSON

Raw text responses are hard to work with programmatically. Structured output forces the AI to return data in a specific schema.

php artisan make:agent ContentModerator --structured
<?php

namespace App\Ai\Agents;

use Illuminate\Contracts\JsonSchema\JsonSchema;
use Laravel\Ai\Contracts\Agent;
use Laravel\Ai\Contracts\HasStructuredOutput;
use Laravel\Ai\Promptable;

class ContentModerator implements Agent, HasStructuredOutput
{
    use Promptable;

    public function instructions(): string
    {
        return 'Analyze text for community guideline violations. Be precise.';
    }

    public function schema(JsonSchema $schema): array
    {
        return [
            'is_safe' => $schema->boolean()->required(),
            'violations' => $schema->array()->items(
                $schema->string()
            ),
            'confidence' => $schema->integer()->min(1)->max(10)->required(),
            'explanation' => $schema->string()->required(),
        ];
    }
}

Now the response is array-accessible:

$result = (new ContentModerator)->prompt($text);

if (!$result['is_safe']) {
    Log::warning('Content flagged', [
        'violations' => $result['violations'],
        'confidence' => $result['confidence'],
    ]);

    return response()->json([
        'rejected' => true,
        'reason' => $result['explanation'],
    ], 422);
}

No parsing, no regex, no "hope the AI returns valid JSON." The schema is enforced.

Tools: Let the AI Call Your Code

Tools let the agent interact with your application -- query the database, call APIs, or perform actions.

<?php

namespace App\Ai\Tools;

use App\Models\Order;
use Laravel\Ai\Contracts\Tool;

class LookupOrder implements Tool
{
    public function name(): string
    {
        return 'lookup_order';
    }

    public function description(): string
    {
        return 'Look up an order by ID and return its status and details.';
    }

    public function parameters(): array
    {
        return [
            'order_id' => [
                'type' => 'integer',
                'description' => 'The order ID to look up',
                'required' => true,
            ],
        ];
    }

    public function execute(array $params): string
    {
        $order = Order::with('items')->find($params['order_id']);

        if (!$order) {
            return 'Order not found.';
        }

        return json_encode([
            'id' => $order->id,
            'status' => $order->status->value,
            'total' => $order->total,
            'items_count' => $order->items->count(),
            'created_at' => $order->created_at->toDateString(),
        ]);
    }
}

Wire it into your agent:

class CustomerSupport implements Agent, HasTools
{
    use Promptable;

    public function instructions(): string
    {
        return 'You are a customer support assistant. Help customers with their order inquiries. Use the lookup_order tool to find order details.';
    }

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

When a customer asks "Where is my order #4521?", the agent will automatically call LookupOrder, get the data, and respond with a helpful answer.

Conversation Management

Most AI features need to remember context. The SDK handles this with the RemembersConversations trait.

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

    public function instructions(): string
    {
        return 'You are a helpful customer support assistant.';
    }
}

Start and continue conversations:

// Start a new conversation
$response = (new CustomerSupport)
    ->forUser($user)
    ->prompt('I have a problem with my order.');

$conversationId = $response->conversationId;

// Later, continue the same conversation
$response = (new CustomerSupport)
    ->continue($conversationId, as: $user)
    ->prompt('The order number is #4521.');

The SDK stores the conversation history in the database. The agent sees all previous messages in the thread.

Switching Providers

One of the best features: switch between AI providers without changing your agent code.

use Laravel\Ai\Enums\Lab;

// Default provider (from config)
$response = (new ContentModerator)->prompt($text);

// Use Claude specifically
$response = (new ContentModerator)->prompt(
    $text,
    provider: Lab::Anthropic,
    model: 'claude-sonnet-4-6',
);

// Use a local Ollama model for development
$response = (new ContentModerator)->prompt(
    $text,
    provider: Lab::Ollama,
    model: 'llama3',
);

This means you can develop locally with Ollama (free), test with a cheaper model, and deploy with Claude or GPT in production.

Real-World Use Cases

Here are practical features you can build today:

1. Auto-Categorize Support Tickets

class TicketCategorizer implements Agent, HasStructuredOutput
{
    use Promptable;

    public function instructions(): string
    {
        return 'Categorize support tickets. Categories: billing, technical, account, feature_request, bug_report.';
    }

    public function schema(JsonSchema $schema): array
    {
        return [
            'category' => $schema->enum(['billing', 'technical', 'account', 'feature_request', 'bug_report'])->required(),
            'priority' => $schema->enum(['low', 'medium', 'high', 'urgent'])->required(),
            'summary' => $schema->string()->required(),
        ];
    }
}

// In your controller or event listener
$result = (new TicketCategorizer)->prompt($ticket->body);
$ticket->update([
    'category' => $result['category'],
    'priority' => $result['priority'],
    'ai_summary' => $result['summary'],
]);

2. Generate SEO Meta Descriptions

class SeoWriter implements Agent, HasStructuredOutput
{
    use Promptable;

    public function instructions(): string
    {
        return 'Generate SEO-optimized meta descriptions. Max 155 characters. Include the primary keyword naturally. Make it compelling for search results.';
    }

    public function schema(JsonSchema $schema): array
    {
        return [
            'meta_description' => $schema->string()->required(),
            'suggested_title' => $schema->string()->required(),
            'keywords' => $schema->array()->items($schema->string()),
        ];
    }
}

3. Summarize Long Documents

class DocumentSummarizer implements Agent, HasStructuredOutput
{
    use Promptable;

    public function instructions(): string
    {
        return 'Summarize documents into key points. Be concise and accurate. Extract action items if present.';
    }

    public function schema(JsonSchema $schema): array
    {
        return [
            'summary' => $schema->string()->required(),
            'key_points' => $schema->array()->items($schema->string()),
            'action_items' => $schema->array()->items($schema->string()),
        ];
    }
}

Testing

The SDK has built-in testing support. No real API calls needed:

use Laravel\Ai\Facades\Ai;

public function test_content_moderation_flags_hate_speech()
{
    Ai::fake([
        ContentModerator::class => [
            'is_safe' => false,
            'violations' => ['hate_speech'],
            'confidence' => 9,
            'explanation' => 'Contains discriminatory language.',
        ],
    ]);

    $response = $this->postJson('/api/comments', [
        'body' => 'test content',
    ]);

    $response->assertStatus(422);
    $response->assertJson(['rejected' => true]);
}

Performance Tips

  1. Queue long-running prompts. Use dispatch(fn () => $agent->prompt($text)) for anything non-blocking.
  2. Cache repeated queries. If 100 users ask "What are your shipping policies?", cache that response.
  3. Use the cheapest model that works. Don't use GPT-4 or Opus for simple categorization. Haiku or GPT-4o-mini handles most structured tasks fine.
  4. Set timeouts. AI APIs can be slow. Set appropriate timeouts: ->prompt($text, timeout: 30).
  5. Implement failover. The SDK supports automatic provider failover. If OpenAI is down, fall back to Claude automatically.

Key Takeaways

  • Laravel's AI SDK gives you a clean, unified API across all major AI providers
  • Agents are simple PHP classes -- easy to test, version control, and maintain
  • Structured output eliminates the "parsing AI responses" headache
  • Tools let the AI interact with your application safely
  • Conversation management is built-in with database persistence
  • You can switch providers with one parameter change

The AI SDK turns what used to be days of integration work into a composer require and a few classes. If you're building any feature that could benefit from language understanding, classification, or generation -- this is the way to do it in Laravel.

#laravel#ai#php#openai#claude#backend