Preskočiť na obsah
AI Laravel 21. máj 2026 · 12 min čítania

Laravel AI chatbot: od nuly po živé odpovede za jedno popoludnie

Slovenský e-shop platí 89 EUR mesačne za chatbota, ktorý odpovie „Neviem pomôcť" na každú tretiu otázku. Vlastná integrácia cez OpenAI API vyšla klienta 3,50 EUR za prvý mesiac — a odpovedala presne na otázky o jeho produktoch. Tu je celý postup.

DC

Dušan Chlpek

PHP vývojár, GEAR s.r.o. · 25+ rokov praxe

Prečo vlastný chatbot, nie hotové riešenie

Hotové chatboty (Tidio, Intercom, Crisp) majú jednu zásadnú nevýhodu: nepoznajú váš konkrétny katalóg, ceny ani podmienky. Vyškolíte ich na firemné dokumenty, ale akonáhle zákazník položí otázku mimo trénovacej sady, dostane generickú odpoveď — alebo presmeruje na živého operátora, čo ste chceli práve eliminovať.

Priama integrácia cez OpenAI API (alebo Anthropic Claude API) vám dáva úplnú kontrolu:

Riešenie Cena/mesiac Prispôsobiteľnosť Kontrola dát
Tidio AI~59 EURObmedzená❌ U dodávateľa
Intercom Finod 89 EURStredná❌ U dodávateľa
Vlastná integrácia (OpenAI)1–10 EUR*✅ Plná✅ Váš server

* Pri bežnej záťaži e-shopu s GPT-4o-mini modelom (~500 konverzácií/mesiac).

Čo budete potrebovať

Inštalácia a konfigurácia

Existuje niekoľko PHP klientov pre OpenAI API. Najrozšírenejší je openai-php/client — má Laravel service provider a dá sa nakonfigurovať za minútu:

composer require openai-php/laravel

Publikujte konfiguračný súbor a doplňte kľúč do .env:

php artisan vendor:publish --provider="OpenAI\Laravel\ServiceProvider"

# .env
OPENAI_API_KEY=sk-proj-...
OPENAI_ORGANIZATION=         # voliteľné
Claude API alternatíva: Ak preferujete Anthropic Claude, použite composer require anthropic-sdk-php/sdk. Celá logika zostane rovnaká — zmeníte len metódu volania a štruktúru správ. Claude 3.5 Haiku je cenovo porovnateľný s GPT-4o-mini.

ChatController — prvé volanie API

Vytvorte controller s jedným POST endpointom. Vstup je JSON so správou od používateľa, výstup je odpoveď modelu.

php artisan make:controller ChatController
// app/Http/Controllers/ChatController.php
namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Illuminate\Http\JsonResponse;
use OpenAI\Laravel\Facades\OpenAI;

class ChatController extends Controller
{
    private const SYSTEM_PROMPT = <<validate(['message' => 'required|string|max:500']);

        $response = OpenAI::chat()->create([
            'model'    => 'gpt-4o-mini',
            'messages' => [
                ['role' => 'system',  'content' => self::SYSTEM_PROMPT],
                ['role' => 'user',    'content' => $request->input('message')],
            ],
            'max_tokens'  => 300,
            'temperature' => 0.4,   // nižšia = konzistentnejšie odpovede
        ]);

        return response()->json([
            'reply' => $response->choices[0]->message->content,
        ]);
    }
}

Route pridajte do routes/api.php:

Route::middleware('throttle:chat')->post('/chat', [ChatController::class, 'send']);
Pozor na system prompt: Čím presnejší system prompt, tým lepšie správanie. Explicitne zakážte vymýšľanie faktov (nikdy nevymýšľaj ceny) — LLM modely majú tendenciu „halucinovať" čísla, ak ich neobmedzíte.

SSE streaming — živé odpovede znak po znaku

Jednorázový request funguje, ale odpoveď príde naraz po 2–5 sekundách. Používatelia to vnímajú ako zaseknutie. Server-Sent Events (SSE) riešia problém elegantne — model „píše" a klient zobrazuje znaky postupne, presne ako ChatGPT.

Backend: StreamedResponse

// app/Http/Controllers/ChatController.php — pridajte metódu stream()
public function stream(Request $request): \Symfony\Component\HttpFoundation\StreamedResponse
{
    $request->validate(['message' => 'required|string|max:500']);
    $message = $request->input('message');

    return response()->stream(function () use ($message) {
        $stream = OpenAI::chat()->createStreamed([
            'model'    => 'gpt-4o-mini',
            'messages' => [
                ['role' => 'system', 'content' => self::SYSTEM_PROMPT],
                ['role' => 'user',   'content' => $message],
            ],
            'max_tokens' => 300,
        ]);

        foreach ($stream as $response) {
            $delta = $response->choices[0]->delta->content ?? '';
            if ($delta !== '') {
                echo 'data: ' . json_encode(['token' => $delta]) . "\n\n";
                ob_flush();
                flush();
            }
        }
        echo "data: [DONE]\n\n";
        ob_flush();
        flush();
    }, 200, [
        'Content-Type'  => 'text/event-stream',
        'Cache-Control' => 'no-cache',
        'X-Accel-Buffering' => 'no',   // vypnúť Nginx buffering
    ]);
}

Frontend: EventSource (vanilla JS)

// Žiadna knižnica — čistý Fetch + ReadableStream
async function sendMessage(message) {
    const output = document.getElementById('chat-output');
    const response = await fetch('/api/chat/stream', {
        method: 'POST',
        headers: {
            'Content-Type': 'application/json',
            'X-CSRF-TOKEN': document.querySelector('meta[name=csrf-token]').content,
        },
        body: JSON.stringify({ message }),
    });

    const reader = response.body.getReader();
    const decoder = new TextDecoder();
    let buffer = '';

    while (true) {
        const { value, done } = await reader.read();
        if (done) break;

        buffer += decoder.decode(value, { stream: true });
        const lines = buffer.split('\n');
        buffer = lines.pop(); // posledný nedokončený riadok

        for (const line of lines) {
            if (!line.startsWith('data: ')) continue;
            const payload = line.slice(6);
            if (payload === '[DONE]') return;
            const { token } = JSON.parse(payload);
            output.textContent += token;  // alebo innerHTML += escapeHtml(token)
        }
    }
}
Nginx a buffering: Hlavička X-Accel-Buffering: no je kľúčová — bez nej Nginx akumuluje SSE správy a posiela ich naraz, čo streaming úplne zruší. Na Apache doplňte Header set X-Accel-Buffering "no" do .htaccess.

Kontextové okno a pamäť konverzácie

Aktuálny controller zabudne každú správu ihneď po odpovedi. Pre zmysluplný chatbot potrebujete uchovávať históriu konverzácie a posielať ju s každým requestom.

Jednoduchá historia cez session

public function send(Request $request): JsonResponse
{
    $request->validate(['message' => 'required|string|max:500']);
    $userMsg = $request->input('message');

    // Načítanie histórie (max. posledných 10 výmen = 20 správ)
    $history = session()->get('chat_history', []);

    $messages = array_merge(
        [['role' => 'system', 'content' => self::SYSTEM_PROMPT]],
        array_slice($history, -20),   // sliding window
        [['role' => 'user', 'content' => $userMsg]]
    );

    $response = OpenAI::chat()->create([
        'model'      => 'gpt-4o-mini',
        'messages'   => $messages,
        'max_tokens' => 300,
    ]);

    $assistantMsg = $response->choices[0]->message->content;

    // Uloženie do histórie
    $history[] = ['role' => 'user',      'content' => $userMsg];
    $history[] = ['role' => 'assistant', 'content' => $assistantMsg];
    session()->put('chat_history', $history);

    return response()->json(['reply' => $assistantMsg]);
}

Redis pre dlhodobejšiu pamäť

Session funguje dobre pre jednu reláciu. Pre chatbotov, kde chcete pamäť naprieč reláciami (napr. VIP zákazník sa prihlási a chatbot si pamätá jeho predchádzajúci dopyt), uložte históriu do Redisu kľúčovaného ID zákazníka. Viac o Redis v Laraveli nájdete v článku Redis a Laravel: cache, session a queues.

// Pre autentifikovaných používateľov — historia naprieč reláciami
$key = 'chat:' . auth()->id();
$history = Cache::get($key, []);
// ... po odpovedi ...
Cache::put($key, array_slice($history, -20), now()->addDays(7));

Rate limiting a náklady pod kontrolou

Každé volanie API niečo stojí. Bez rate limitingu vás môže jeden zákazník (alebo bot) pripojiť na desiatky dolárov za hodinu. Laravel má vstavaný rate limiter — stačí ho nakonfigurovať:

// app/Providers/RouteServiceProvider.php (alebo bootstrap/app.php v Laravel 11+)
RateLimiter::for('chat', function (Request $request) {
    return [
        // Max. 20 správ za minútu na IP
        Limit::perMinute(20)->by($request->ip()),
        // Max. 200 správ za deň na autentifikovaného používateľa
        Limit::perDay(200)->by(optional($request->user())->id ?? $request->ip()),
    ];
});

Monitorovanie nákladov

OpenAI vracia v každej odpovedi počet spotrebovaných tokenov. Logujte ich do databázy — budete vedieť, ktoré konverzácie sú najdrahšie a kde optimalizovať system prompt:

// Po každom volaní
$usage = $response->usage;
Log::info('OpenAI usage', [
    'user_id'          => auth()->id(),
    'prompt_tokens'    => $usage->promptTokens,
    'completion_tokens'=> $usage->completionTokens,
    'total_tokens'     => $usage->totalTokens,
    // GPT-4o-mini: input $0.15/1M, output $0.60/1M tokenov
    'cost_usd'         => round(
        ($usage->promptTokens * 0.00000015) +
        ($usage->completionTokens * 0.0000006),
        6
    ),
]);

Záver: checklist pred spustením

Čo ďalej: RAG a function calling

Tento tutoriál pokrýva základ. V ďalšom kroku môžete pridať:

Integrácia AI do e-shopu nie je veda — je to 150 riadkov Laravelu a jedno popoludnie. Väčšina nákladov ide na premýšľanie nad správnym system promptom, nie na kód.

Chcete AI chatbota pre váš e-shop?

Naprogramujem ho na mieru — so znalosťou vašich produktov, v slovenčine, bez paušálnych poplatkov. Dopyt bez záväzkov, odpoveď do 24 hodín.

Ďalšie články

Zavolať E-mail Dopyt

Ochrana súkromia

Táto stránka využíva cookies pre nevyhnutné fungovanie. Rešpektujeme vaše súkromie a legislatívu GDPR.