Laravel AI SDK: A Practical Guide to Building AI-Powered Features
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
- Queue long-running prompts. Use
dispatch(fn () => $agent->prompt($text))for anything non-blocking. - Cache repeated queries. If 100 users ask "What are your shipping policies?", cache that response.
- 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.
- Set timeouts. AI APIs can be slow. Set appropriate timeouts:
->prompt($text, timeout: 30). - 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.