Vai al contenuto

Sviluppare un ChatBot AI con PHP e javascript

💡 PHP · Intelligenza Artificiale · Groq

Aggiungere un chatbot AI al proprio sito sembrava, fino a poco tempo fa, un lusso riservato a chi poteva permettersi abbonamenti costosi o integrazioni con OpenAI a pagamento. Oggi non è più così. Groq offre un'API completamente gratuita (con rate limit generosi) che permette di interrogare modelli LLM potentissimi — come Llama 3.3 70B — con latenze bassissime, grazie al loro chip LPU proprietario.

In questa guida costruiremo un chatbot funzionante in PHP puro, integrabile in qualsiasi sito WordPress o tema Neve, senza plugin di terze parti. Alla fine avrai un widget chat con frontend HTML/JS e un backend PHP che fa da proxy sicuro verso l'API Groq.

Cosa otterrai: Un chatbot con sistema prompt personalizzato, storico della conversazione, bottone flottante e risposta in tempo reale — il tutto in meno di 200 righe di codice, zero costi.
1

Perché Groq? Confronto con altri provider

Prima di sporcarci le mani, capiamo perché Groq è la scelta giusta per chi vuole partire gratis senza rinunciare alla qualità.

Provider Modello gratuito Velocità Rate limit free
Groq Llama 3.3 70B ~800 tok/s 30 req/min · 131K token/giorno
OpenAI — (solo a pagamento) Medio Solo a pagamento
Google Gemini Gemini Flash Medio 15 req/min (quota stretta)
Mistral Mistral 7B Medio Limitato

Groq si distingue per la velocità estrema — la risposta arriva quasi in tempo reale — e per avere un modello davvero capace nel tier gratuito. Per un sito WordPress di medie dimensioni, il piano free è più che sufficiente.

2

Ottieni la chiave API gratuita

Registrati su console.groq.com con Google o email. Una volta dentro:

  • Vai su API Keys nel menu laterale
  • Clicca Create API Key
  • Copia la chiave (inizia con gsk_...)
  • Salvala subito — non la rivedrai più
⚠️ Attenzione: Non inserire mai la chiave API nel codice JavaScript front-end. Deve stare solo nel file PHP lato server, meglio ancora in un file config.php escluso da git e dalla webroot pubblica.
3

Struttura file nel tema Neve

Nel tema figlio di Neve (o in un plugin custom), crea una cartella chatbot/. Useremo questa struttura minimale:

# Dentro il tema figlio di Neve
wp-content/themes/neve-child/
└── chatbot/
    ├── config.php     ← API key e impostazioni
    └── chat.php       ← proxy PHP verso Groq

# Il widget HTML viene iniettato via functions.php
STRUTTURA
💡 Consiglio: Se usi un tema figlio di Neve (child theme), metti i file nella cartella del figlio, non del padre. Così sopravvivono agli aggiornamenti del tema.
4

File di configurazione — config.php

Crea chatbot/config.php. Questo è l'unico file che contiene dati sensibili — non includerlo mai in git:

<?php
// chatbot/config.php — aggiungere a .gitignore

define('GROQ_API_KEY', 'gsk_la_tua_chiave_qui');
define('GROQ_MODEL',   'llama-3.3-70b-versatile');
define('GROQ_ENDPOINT','https://api.groq.com/openai/v1/chat/completions');

// System prompt: personalizza con le info del tuo sito
define('SYSTEM_PROMPT',
  'Sei un assistente virtuale esperto per il sito web [NOME SITO].
   Rispondi sempre in italiano, in modo cortese e professionale.
   Aiuta i visitatori con informazioni sui servizi, preventivi e contatti.
   Se non conosci la risposta, invita l\'utente a contattarci
   via email o telefono.'
);

define('MAX_TOKENS',  512);
define('TEMPERATURE', 0.7);
define('MAX_HISTORY', 10); // messaggi mantenuti in memoria
PHP – config.php

I modelli disponibili gratuitamente su Groq (aggiornati al 2025) includono anche llama-3.1-8b-instant (più veloce, meno potente), mixtral-8x7b-32768 e gemma2-9b-it. Puoi cambiarli in qualsiasi momento agendo solo su questa riga.

5

Backend PHP — chat.php

Questo file riceve i messaggi via POST, li passa a Groq tramite cURL e restituisce la risposta in JSON. Gestisce anche lo storico della conversazione nella sessione PHP, così il chatbot "ricorda" il contesto.

<?php
// chatbot/chat.php
session_start();
require_once __DIR__ . '/config.php';

// Solo richieste POST
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
    http_response_code(405);
    exit('Method Not Allowed');
}

// Leggi il body JSON
$input       = json_decode(file_get_contents('php://input'), true);
$userMessage = trim($input['message'] ?? '');
$resetChat   = $input['reset'] ?? false;

// Reset conversazione
if ($resetChat) {
    unset($_SESSION['chat_history']);
    echo json_encode(['ok' => true]);
    exit();
}

if (empty($userMessage)) {
    http_response_code(400);
    echo json_encode(['error' => 'Messaggio vuoto']);
    exit();
}

// Inizializza storico sessione
if (!isset($_SESSION['chat_history'])) {
    $_SESSION['chat_history'] = [];
}

// Aggiungi messaggio utente
$_SESSION['chat_history'][] = [
    'role'    => 'user',
    'content' => $userMessage
];

// Taglia storico se troppo lungo
if (count($_SESSION['chat_history']) > MAX_HISTORY * 2) {
    $_SESSION['chat_history'] = array_slice(
        $_SESSION['chat_history'], -MAX_HISTORY * 2
    );
}

// Costruisci array messaggi con system prompt
$messages = array_merge(
    [['role' => 'system', 'content' => SYSTEM_PROMPT]],
    $_SESSION['chat_history']
);

// Payload per Groq (formato OpenAI-compatibile)
$payload = json_encode([
    'model'       => GROQ_MODEL,
    'messages'    => $messages,
    'max_tokens'  => MAX_TOKENS,
    'temperature' => TEMPERATURE,
]);

// Chiamata cURL
$ch = curl_init(GROQ_ENDPOINT);
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_POST           => true,
    CURLOPT_POSTFIELDS     => $payload,
    CURLOPT_HTTPHEADER     => [
        'Content-Type: application/json',
        'Authorization: Bearer ' . GROQ_API_KEY,
    ],
    CURLOPT_TIMEOUT        => 30,
]);

$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);

if ($httpCode !== 200) {
    http_response_code(502);
    echo json_encode(['error' => 'Errore API Groq: ' . $httpCode]);
    exit();
}

$data      = json_decode($response, true);
$botReply  = $data['choices'][0]['message']['content'] ?? 'Nessuna risposta.';
$tokenUsed = $data['usage']['total_tokens'] ?? 0;

// Salva risposta del bot nello storico
$_SESSION['chat_history'][] = [
    'role'    => 'assistant',
    'content' => $botReply
];

// Risposta al frontend
header('Content-Type: application/json');
echo json_encode([
    'reply'  => $botReply,
    'tokens' => $tokenUsed,
]);
PHP – chat.php
6

Integrazione in Neve via functions.php

Apri il file functions.php del tema figlio di Neve e aggiungi questo codice. Inietta il widget HTML nel footer di tutte le pagine pubbliche, senza toccare il tema padre:

<?php
// Aggiungere nel functions.php del TEMA FIGLIO di Neve

add_action('wp_footer', 'neve_groq_chatbot_widget');

function neve_groq_chatbot_widget() {
    // Non mostrare nell'admin
    if (is_admin()) return;

    // URL assoluto al file chat.php (nel tema figlio)
    $chat_url = get_stylesheet_directory_uri() . '/chatbot/chat.php';
    ?>

    <!-- === STILE WIDGET CHATBOT === -->
    <style>
    #neve-chat-toggle {
        position: fixed; bottom: 24px; right: 24px; z-index: 99999;
        width: 58px; height: 58px; border-radius: 50%;
        background: var(--neve-primary-accent, #1a6e3c);
        border: none; cursor: pointer;
        box-shadow: 0 4px 20px rgba(0,0,0,.28);
        display: flex; align-items: center; justify-content: center;
        transition: transform .2s ease, box-shadow .2s ease;
    }
    #neve-chat-toggle:hover {
        transform: scale(1.08);
        box-shadow: 0 6px 28px rgba(0,0,0,.35);
    }
    #neve-chat-toggle svg { width: 26px; height: 26px; fill: #fff; }

    #neve-chat-widget {
        position: fixed; bottom: 96px; right: 24px; z-index: 99998;
        width: 360px; max-height: 520px;
        background: #fff; border-radius: 16px;
        box-shadow: 0 8px 40px rgba(0,0,0,.18);
        display: none; flex-direction: column; overflow: hidden;
        font-family: inherit;
    }
    #neve-chat-widget.nev-open { display: flex; }

    #nev-chat-header {
        background: var(--neve-primary-accent, #1a6e3c);
        color: #fff; padding: 14px 18px;
        font-weight: 600; font-size: 15px;
        display: flex; justify-content: space-between; align-items: center;
    }
    #nev-chat-header button {
        background: none; border: none;
        color: #fff; font-size: 22px; cursor: pointer; line-height: 1;
        padding: 0; opacity: .8;
    }
    #nev-chat-header button:hover { opacity: 1; }

    #nev-chat-messages {
        flex: 1; overflow-y: auto; padding: 16px;
        display: flex; flex-direction: column; gap: 10px;
    }
    .nev-msg {
        max-width: 85%; padding: 9px 14px;
        border-radius: 12px; font-size: 14px; line-height: 1.55;
    }
    .nev-msg.bot {
        background: #f0f4f0; color: #1a1a1a; align-self: flex-start;
        border-radius: 12px 12px 12px 2px;
    }
    .nev-msg.user {
        background: var(--neve-primary-accent, #1a6e3c);
        color: #fff; align-self: flex-end;
        border-radius: 12px 12px 2px 12px;
    }
    .nev-msg.typing { opacity: .65; font-style: italic; }

    #nev-chat-input-row {
        display: flex; gap: 8px; padding: 12px;
        border-top: 1px solid #e8e8e8;
    }
    #nev-chat-input {
        flex: 1; border: 1px solid #ddd; border-radius: 24px;
        padding: 9px 16px; font-size: 14px; outline: none;
        font-family: inherit;
        transition: border-color .2s;
    }
    #nev-chat-input:focus {
        border-color: var(--neve-primary-accent, #1a6e3c);
    }
    #nev-chat-send {
        background: var(--neve-primary-accent, #1a6e3c);
        color: #fff; border: none; border-radius: 50%;
        width: 40px; height: 40px; cursor: pointer;
        display: flex; align-items: center; justify-content: center;
        font-size: 18px; flex-shrink: 0;
        transition: opacity .2s;
    }
    #nev-chat-send:hover { opacity: .85; }

    @media (max-width: 480px) {
        #neve-chat-widget { width: calc(100vw - 24px); right: 12px; }
    }
    </style>

    <!-- === BOTTONE FLOTTANTE === -->
    <button id="neve-chat-toggle" aria-label="Apri chatbot">
        <svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
            <path d="M12 2C6.48 2 2 6.48 2 12c0 1.85.5 3.58 1.38 5.06L2 22l4.94-1.38A9.96 9.96 0 0 0 12 22c5.52 0 10-4.48 10-10S17.52 2 12 2zm1 15H7v-2h6v2zm3-4H7v-2h9v2zm0-4H7V7h9v2z"/>
        </svg>
    </button>

    <!-- === FINESTRA CHAT === -->
    <div id="neve-chat-widget" role="dialog" aria-label="Chatbot assistente">
        <div id="nev-chat-header">
            <span>🤖 Assistente Virtuale</span>
            <button id="nev-chat-close" aria-label="Chiudi chat">&times;</button>
        </div>
        <div id="nev-chat-messages">
            <div class="nev-msg bot">Ciao! 👋 Come posso aiutarti oggi?</div>
        </div>
        <div id="nev-chat-input-row">
            <input id="nev-chat-input" type="text"
                   placeholder="Scrivi un messaggio..." autocomplete="off" />
            <button id="nev-chat-send" aria-label="Invia">↑</button>
        </div>
    </div>

    <!-- === JAVASCRIPT === -->
    <script>
    (function() {
        const CHAT_URL = '<?php echo esc_url($chat_url); ?>';
        const toggle   = document.getElementById('neve-chat-toggle');
        const widget   = document.getElementById('neve-chat-widget');
        const close    = document.getElementById('nev-chat-close');
        const msgs     = document.getElementById('nev-chat-messages');
        const input    = document.getElementById('nev-chat-input');
        const sendBtn  = document.getElementById('nev-chat-send');

        toggle.addEventListener('click', () => widget.classList.toggle('nev-open'));
        close.addEventListener('click',  () => widget.classList.remove('nev-open'));

        async function sendMessage() {
            const text = input.value.trim();
            if (!text) return;

            addBubble(text, 'user');
            input.value = '';
            sendBtn.disabled = true;

            const typing = addBubble('Sto elaborando...', 'bot typing');

            try {
                const res  = await fetch(CHAT_URL, {
                    method:  'POST',
                    headers: { 'Content-Type': 'application/json' },
                    body:    JSON.stringify({ message: text })
                });
                const data = await res.json();
                typing.remove();
                addBubble(data.reply || 'Nessuna risposta.', 'bot');
            } catch (err) {
                typing.remove();
                addBubble('Errore di connessione. Riprova tra poco.', 'bot');
            } finally {
                sendBtn.disabled = false;
                input.focus();
            }
        }

        function addBubble(text, cls) {
            const div = document.createElement('div');
            div.className = 'nev-msg ' + cls;
            div.textContent = text;
            msgs.appendChild(div);
            msgs.scrollTop = msgs.scrollHeight;
            return div;
        }

        sendBtn.addEventListener('click', sendMessage);
        input.addEventListener('keydown', e => {
            if (e.key === 'Enter' && !e.shiftKey) {
                e.preventDefault();
                sendMessage();
            }
        });
    })();
    </script>

    <?php
}
PHP – functions.php (tema figlio Neve)
💡 Colore automatico Neve: Il codice usa var(--neve-primary-accent) come colore del chatbot. Questo significa che cambierà automaticamente seguendo il colore principale che hai impostato nel Customizer di Neve — zero configurazione aggiuntiva.
7

Sicurezza: rate limiting e protezioni

Prima di andare in produzione, aggiungi un rate limiter per evitare che qualcuno possa abusare del tuo piano Groq. Inserisci questo blocco all'inizio di chat.php, subito dopo session_start():

<?php
// Aggiungere all'inizio di chat.php, dopo session_start()

$rateKey = 'nev_rate_' . md5($_SERVER['REMOTE_ADDR']);
$maxReqs = 10;  // max richieste per finestra temporale
$window  = 60;  // secondi

$count = (int)($_SESSION[$rateKey]['count'] ?? 0);
$start = $_SESSION[$rateKey]['start'] ?? time();

if (time() - $start > $window) {
    $count = 0;
    $start = time();
}
if ($count >= $maxReqs) {
    http_response_code(429);
    header('Content-Type: application/json');
    echo json_encode(['error' => 'Troppe richieste. Aspetta un minuto.']);
    exit();
}
$_SESSION[$rateKey] = ['count' => $count + 1, 'start' => $start];
PHP – Rate limiter

Altre buone pratiche da adottare:

  • Metti config.php fuori dalla webroot pubblica se possibile
  • Aggiungi il file a .gitignore
  • Usa sempre HTTPS sul dominio
  • Aggiungi un nonce WordPress per le pagine riservate
  • Imposta CURLOPT_TIMEOUT a 30 secondi max
  • Non mostrare mai gli errori cURL a schermo in produzione
  • Logga gli errori su file con error_log()
  • Considera una cache breve (es. Transients API) per FAQ ripetute

8

Come personalizzare il System Prompt

Il system prompt è il segreto per fare in modo che il chatbot sembri davvero "tuo". Più è dettagliato e contestuale, più il bot sembrerà un membro reale del team.

Esempio per un'agenzia web:

"Sei Mario, assistente virtuale dell'Agenzia XYZ con sede a Roma. Conosci alla perfezione i nostri servizi: siti WordPress a partire da €800, e-commerce WooCommerce, SEO locale e manutenzione mensile. Rispondi in modo friendly ma professionale. Quando l'utente chiede un preventivo, domanda il tipo di sito, il settore e una email di contatto. Non fornire informazioni su cui non sei sicuro."

Puoi arricchire il prompt con questi elementi:

  • FAQ estratte dalle pagine del sito
  • Prezzi e pacchetti aggiornati
  • Nome e personalità del bot
  • Quando invitare a contattarti
  • Tono di voce del brand
  • Limitazioni esplicite ("non dare consigli legali")
  • Orari di apertura e contatti diretti
  • Lingua preferita e registro comunicativo

Anteprima del risultato finale

🤖
Ciao! 👋 Come posso aiutarti oggi?
👤
Quali servizi offrite per i siti web?
🤖
Offriamo siti WordPress su misura, e-commerce WooCommerce, SEO locale e manutenzione mensile. Vuoi un preventivo gratuito? 😊

Conclusioni

Hai appena visto come costruire un chatbot AI completamente gratuito su WordPress con tema Neve, senza plugin di terze parti e senza toccare il tema padre. Groq e Llama 3.3 70B ti danno accesso a un modello enterprise-grade senza spendere un centesimo, e il codice è abbastanza compatto da poter essere mantenuto anche da un solo sviluppatore.

Se il traffico crescesse oltre i limiti del piano free, Groq offre piani a pagamento con tariffe molto competitive — e il codice non cambierebbe di una riga.

🚀 Prossimi step consigliati: salva le conversazioni su database MySQL con $wpdb, aggiungi un form di contatto direttamente nella chat quando l'utente chiede un preventivo, o integra il chatbot con WooCommerce per rispondere su prodotti e ordini.

Hai domande o vuoi che implementiamo il chatbot per te? Contattaci su SitiWebSuMisura.com — realizziamo soluzioni su misura per ogni esigenza.

Vuoi maggiori informazioni? Chatta con noi
x-lab soft
Rispondiamo entro un giorno lavorativo