Back to engineering notes
Architecture & AI9 min read·

AI Integration: How to Add LLMs to Legacy Laravel Monoliths (Without Tech Debt)

The architectural blueprint I use to safely integrate LLMs, RAG, and structured extraction into existing Laravel and PHP apps — without breaking business logic.

ArchitectureAILaravelLLMRAGPHP
Legacy monolith connected through a teal AI gateway to LLM services

When clients ask me to integrate AI features into 8-year-old Laravel or legacy monolith apps, their biggest fear is breaking existing business logic or getting locked into heavy technical debt. Ripping and replacing a production monolith for AI is almost always a mistake.

In my years auditing and architecting enterprise monoliths, the most common error I see teams make is executing LLM API calls synchronously inside web request handlers. This instantly degrades response times from 100ms to 3+ seconds and creates fragile API coupling.

Here is the exact architectural blueprint I use to safely integrate Large Language Models (LLMs), RAG pipelines, and automated data extraction into existing Laravel and PHP applications.

Laravel monolith dispatching jobs to Redis, then an AI gateway with Zod, Qdrant, and Redis cache before calling the LLM provider
Decoupled Laravel + AI Gateway Architecture
01

The Decoupled Architecture

Never place third-party AI SDK calls directly inside your core Eloquent models or HTTP Controllers. Instead, isolate the LLM integration behind asynchronous queues and gateway interfaces.

  • Asynchronous Dispatch (Redis / Horizon): Offload all LLM requests to background queue workers (ShouldQueue). Your HTTP responses stay instantaneous while background jobs handle network latency and API retry logic.
  • AI Gateway / Middleware: Host prompt templates, vector search indices, and guardrails outside the main framework codebase.
  • Strict JSON Schemas: Force LLMs to return structured, deterministic payloads so your existing database schemas are never corrupted by unvalidated text outputs.
02

Production Code Blueprint: Laravel + AI Middleware

Instead of writing custom parsing code for unstructured LLM text, enforce structured output schemas using Laravel's HTTP client and Spatie Data Transfer Objects (DTOs) or native PHP 8 typed properties.

Step 1 — Strict Data Contract (DTO)
php
namespace App\DataTransferObjects;

class TicketAnalysisData
{
    public function __construct(
        public readonly string $urgency, // 'low' | 'medium' | 'high' | 'critical'
        public readonly string $category,
        public readonly string $summary,
        public readonly array $suggestedActions
    ) {}

    public static function fromArray(array $data): self
    {
        return new self(
            urgency: $data['urgency'] ?? 'medium',
            category: $data['category'] ?? 'General',
            summary: $data['summary'] ?? '',
            suggestedActions: $data['suggested_actions'] ?? []
        );
    }
}
Step 2 — Offload Request to Background Job
php
namespace App\Jobs;

use App\DataTransferObjects\TicketAnalysisData;
use App\Models\SupportTicket;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Http;

class ProcessTicketWithAI implements ShouldQueue
{
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;

    public int $tries = 3;
    public int $backoff = 10; // Backoff strategy for rate limits

    public function __construct(public SupportTicket $ticket) {}

    public function handle(): void
    {
        // Call external AI Service Layer / Gateway
        $response = Http::timeout(15)
            ->withHeaders(['X-Api-Key' => config('services.ai_gateway.key')])
            ->post(config('services.ai_gateway.url') . '/analyze-ticket', [
                'ticket_id' => $this->ticket->id,
                'content' => $this->ticket->message_body,
            ]);

        if ($response->successful()) {
            $analysis = TicketAnalysisData::fromArray($response->json());

            // Safely update legacy database model
            $this->ticket->update([
                'priority' => $analysis->urgency,
                'category' => $analysis->category,
                'ai_summary' => $analysis->summary,
                'processed_at' => now(),
            ]);
        }
    }
}
03

Controlling Costs & Latency (The Fractional CTO Perspective)

Adding AI features can quickly cause cloud bills to spike if not properly architected. When advising engineering teams, I advocate for three primary cost-control rules:

  • Dynamic Model Tiering: Don't use top-tier models (GPT-4o or Claude 3.5 Sonnet) for simple data parsing. Use lightweight models (GPT-4o-mini, Claude 3.5 Haiku) for 90% of routine tasks like classification, entity extraction, and sentiment routing. Reserve heavy models strictly for multi-step reasoning, complex document synthesis, or code analysis.
  • Embedding Caching with Redis: Before sending a query to a vector database or LLM API, compute a hash of the input string and check a fast key-value cache (Redis). If an exact or high-similarity query was run within the last 24 hours, serve the cached answer immediately. This cuts latency to under 10ms and reduces API cost to $0.
  • Hard Rate-Limiting and Token Caps: Set strict max-token parameters (max_tokens) on every API request. A bad prompt design or infinite loop in a background job can exhaust monthly API budgets within hours without proper rate limiters.
04

Where to Start: High-Value Entry Points

If you are evaluating where to introduce AI into your existing monolith, target low-risk, high-return workflows:

High-value AI entry points
Use CaseMonolith ProblemAI Solution
Smart System SearchMySQL/PostgreSQL LIKE queries fail on typos and semantic intentAsynchronous RAG pipeline over vector embeddings
Document ProcessingManual data entry from customer PDFs and email support ticketsStructured JSON extraction directly into DB queues
Support AutomationStatic, rigid FAQ bots that constantly breakFunction calling mapped directly to existing REST/GraphQL endpoints
05

Final Thoughts

Integrating AI into legacy codebases isn't about replacing your core engineering stack — it's about wrapping intelligent services around the battle-tested systems that already power your business. By enforcing strict schemas, utilizing background queues, and implementing aggressive caching, you can ship modern AI features without accumulating tech debt.