Laravel + AI: Building smarter applications

Last updated:

Reading time: 8 min

Laravel + AI: Building smarter applications

Most conversations about "adding AI" to a web application start with a business goal: automate support responses, let users search in plain language, qualify leads automatically, summarize a pile of unstructured data. They rarely start with "let's pick an AI vendor and hand-roll an HTTP client for it." Yet for years, that second part was unavoidable if you wanted to build on Laravel.

That changed with the Laravel AI SDK, a first-party package that reached stability alongside Laravel 13. It gives Laravel applications a single, consistent way to work with OpenAI, Anthropic, Google Gemini, Groq, xAI, and other providers, along with the building blocks, agents, tools, structured output, streaming, and vector search, needed to turn "we should add AI" into a shipped feature.

This post walks through what that actually looks like in practice.

Why this matters beyond the hype

Before the code, it is worth being specific about what AI is actually good for inside a typical business application, because "smarter applications" can mean very different things:

Support and internal tooling: an agent that answers questions using your own documentation instead of generic web knowledge.

Semantic search: letting users search by meaning rather than exact keyword matches, useful for product catalogs, knowledge bases, and CRM records.

Structured data extraction: turning messy inputs, contact form submissions, uploaded documents, support tickets, into clean, typed data your application can act on.

Natural language interfaces: letting non-technical users query data ("show me orders over $500 last month") without writing SQL.

None of this requires a data science team. It requires an application layer that can call a model, hand it the right context, and do something useful with a structured response. That is exactly what the AI SDK is built for.

One SDK, every major provider

Getting started looks like installing any other Laravel package:

composer require laravel/ai
OPENAI_API_KEY=sk-...
ANTHROPIC_API_KEY=sk-ant-...

From there, generating text does not require learning each provider's request format:

use Laravel\Ai\Facades\Ai;

$response = Ai::prompt('Summarize this customer feedback in two sentences: ' . $feedback);

Switching providers, or comparing how two models handle the same prompt, is a parameter change rather than a rewrite:

$response = Ai::provider('anthropic')->prompt($feedback);

That portability matters more than it sounds. Model pricing, rate limits, and quality shift constantly. An application wired directly to one vendor's SDK inherits that vendor's outages and pricing changes with no easy exit. An application built on the AI SDK can swap providers, or fail over between them automatically, without touching business logic.

Agents: the building block for real features

For anything beyond a single prompt, the SDK organizes AI logic into agent classes rather than scattering API calls through controllers.

php artisan make:agent SupportAgent
class SupportAgent extends Agent
{
    public function instructions(): string
    {
        return 'You are a support assistant for Brightness Group clients. '
            . 'Answer using the provided documentation. If you are not sure, say so.';
    }

    public function tools(): array
    {
        return [
            new SearchDocumentation(),
        ];
    }
}

$response = SupportAgent::make()->prompt('How do I reset my API token?');

Because an agent is a plain PHP class resolved through Laravel's service container, it behaves like the rest of your application: it can be dependency-injected, unit tested, and versioned in source control instead of living as a prompt string buried in a controller.

Giving agents tools

Agents become genuinely useful once they can act, not just respond. Tools are how you grant that ability, whether that is querying your own database, calling an internal API, or searching the web.

php artisan make:tool CheckOrderStatus

class CheckOrderStatus extends Tool
{
    public function schema(): array
    {
        return ['order_id' => 'required|integer'];
    }

    public function handle(array $input): string
    {
        $order = Order::findOrFail($input['order_id']);

        return "Order #{$order->id} is currently: {$order->status}";
    }
}

Once a tool is listed in an agent's tools() method, the model decides when to call it based on the conversation, and the SDK handles executing it and feeding the result back into the response. The provider-managed WebSearch, WebFetch, and FileSearch tools extend this further, letting an agent look things up live rather than relying only on what it already knows.

Structured output for automation, not just conversation

Chat interfaces are the obvious AI feature, but structured output is often the more valuable one for internal automation. Instead of free-form text, you can force a model to return data matching a schema your application already understands.

class LeadExtractorAgent extends Agent implements HasStructuredOutput
{
    public function outputSchema(): array
    {
        return [
            'is_qualified' => 'boolean',
            'company_size' => 'string',
            'stated_need' => 'string',
        ];
    }
}

$result = LeadExtractorAgent::make()->prompt($formSubmission);

if ($result['is_qualified']) {
    Lead::create($result);
}

This is the pattern behind a lot of practical business automation: classifying support tickets, extracting structured data from contracts, or scoring leads from a contact form, all without writing manual parsing rules that break the moment someone phrases something differently.

Semantic search and retrieval-augmented generation

Keyword search fails the moment a user's phrasing does not match your data's phrasing. Semantic search compares meaning instead of exact text, and the AI SDK builds this directly into Laravel's query builder through embeddings and PostgreSQL's pgvector extension.

$embedding = Str::of($product->description)->toEmbeddings();

Product::where('id', $product->id)->update(['embedding' => $embedding]);

$matches = Product::whereVectorSimilarTo('embedding', 'cozy winter jacket for hiking')
    ->limit(5)
    ->get();

Agents can use this as a tool as well, via the built-in SimilaritySearch tool, so a support or sales agent can answer questions grounded in your product catalog or documentation instead of general model knowledge. This combination, retrieve relevant data, then generate a response using it, is what most people mean by retrieval-augmented generation, and it is a few lines of code rather than a separate vector database and integration layer.

Keeping AI interactions fast

Model calls are slow relative to a typical database query, often one to several seconds. Two features handle this without making the user stare at a blank screen.

Streaming sends tokens to the browser as the model generates them, so a response starts appearing immediately instead of arriving all at once:

foreach (Ai::prompt($question)->stream() as $chunk) {
    echo $chunk;
}

For work that does not need to happen in the request cycle at all, background generation, batch classification, scheduled reports, queue it instead:

GenerateProductDescription::dispatch($product)->onQueue('ai');
Conversation memory without building it yourself

Multi-turn conversations need history, and building that storage layer manually is tedious. The RemembersConversations trait handles it with a couple of migrations:

class SupportAgent extends Agent
{
    use RemembersConversations;
}

$conversation = SupportAgent::make()->startConversation();
$conversation->prompt('What is your return policy?');
$conversation->prompt('And how long does a refund take?');

The second prompt has access to the first exchange automatically, without you managing message arrays or a custom database schema.


Practical considerations before you ship

A few things are worth deciding deliberately rather than discovering in production.

Cost and caching. Embedding the same text repeatedly wastes money. The SDK supports caching embeddings so identical inputs are not re-sent to a provider on every request.

Data sent to third parties. Anything passed into a prompt or tool call typically leaves your infrastructure and goes to an external provider. For customer data, PII, or anything contractually restricted, this needs the same scrutiny you would apply to any third-party integration, and providers like Ollama let you run models locally when that matters.

Failure handling. Providers do have outages and rate limits. Passing an array of providers instead of one lets a request fail over automatically rather than surfacing an error to the user.

Testing agents. The SDK includes a testing layer specifically for mocking model responses, so agent behavior can be covered by your normal test suite instead of relying on manual, ad hoc verification against a live API.

None of these are reasons to avoid building with AI. They are the same category of decision you make with any external dependency, and the SDK gives you the hooks to handle them properly rather than bolting them on afterward.

Where to start

The easiest entry point is rarely a customer-facing chatbot. It is usually an internal, lower-stakes workflow: classifying incoming support tickets, extracting structured data from a form, or adding semantic search to an existing search box. These are contained enough to ship quickly, and they build the team's familiarity with agents, tools, and structured output before tackling something customer-facing.

If your team is scoping an AI feature for an existing Laravel application, or evaluating whether a use case is a good fit before committing engineering time, that scoping conversation is worth having early. The architecture decisions, what runs synchronously, what gets queued, what data can leave your infrastructure, are much cheaper to get right before the first line of code than after.