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.
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.
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ù
config.php escluso da git e dalla webroot pubblica.
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
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.
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
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">×</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)
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.
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.phpfuori 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_TIMEOUTa 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
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.
"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
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.
$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.