Costruire un chatbot con Google Gemini su una tua base di conoscenza
Tutorial passo passo, in italiano. Alla fine avrai un assistente che risponde solo con le informazioni dei tuoi documenti, cita le fonti e costa zero (o quasi).
1. L'idea: cos'è un RAG
Un modello linguistico non conosce i tuoi documenti. Con il RAG (Retrieval-Augmented Generation) a ogni domanda:
- Recuperi i 3-5 pezzi di documento più pertinenti (ricerca);
- li incolli nel prompt come "CONTESTO";
- chiedi a Gemini di rispondere solo da lì, citando le fonti.
Così non serve addestrare nulla: aggiornare la conoscenza = cambiare un file.
2. Vertex AI Agent Builder o Gemini API?
| Vertex AI Agent Builder (Google Cloud) | Gemini API + RAG fatto in casa (questo tutorial) | |
|---|---|---|
| Account | Progetto Google Cloud con fatturazione attiva | Solo account Google, chiave da AI Studio |
| Costo | A consumo (ricerca + modello + indice); prova gratuita con crediti | Free tier gratuito; a pagamento pochi centesimi |
| Ricerca | Gestita da Google (Vertex AI Search, datastore) | Tua: BM25 in 40 righe, o embedding |
| Quando sceglierlo | Aziende, molti documenti (PDF, siti, Drive), SLA, dati in UE | Prototipi, piccole KB, budget zero, pieno controllo |
Il chatbot di esempio usa la seconda via: gratis e funzionante senza carta di credito. La sezione 8 spiega come passare ad Agent Builder.
3. Ottenere la chiave Gemini (gratis)
- Vai su aistudio.google.com/apikey e accedi con un account Google.
- Clicca Create API key (crea un progetto nuovo se richiesto). Non serve la fatturazione.
- Copia la chiave (inizia con
AIza…) e mettila solo nel file.envdel server:
GEMINI_API_KEY=AIza...la-tua-chiave GEMINI_MODEL=gemini-2.5-flash
chmod 600 .env # leggibile solo dal proprietario echo .env >> .gitignore # mai su git
4. Preparare la base di conoscenza
Una cartella kb/ con file Markdown. Ogni titolo (#, ##) diventa un "pezzo" (chunk) indicizzato.
kb/ 01-azienda.md # chi siamo, orari, contatti 02-spedizioni.md # costi, tempi, resi 03-prodotti.md # catalogo 04-faq.md # domande frequenti
pdftotext) e ricontrolla.5. Il server (Python + FastAPI)
Tre pezzi: indice BM25, chiamata a Gemini, limiti anti-abuso. Il cuore:
# ricerca: punteggio BM25 per ogni chunk, prendo i migliori 4
ctx = idx.search(domanda)
SYSTEM = ("Sei l'assistente clienti di Caffè Aurora. Rispondi in italiano, "
"SOLO con le informazioni nel CONTESTO. Se la risposta non c'è, dillo.")
body = {
"systemInstruction": {"parts": [{"text": SYSTEM}]},
"contents": [{"role": "user", "parts": [{"text":
f"CONTESTO:\n{contesto}\n\nDOMANDA: {domanda}"}]}],
"generationConfig": {"temperature": 0.2, "maxOutputTokens": 600}
}
url = f"https://generativelanguage.googleapis.com/v1beta/models/{MODEL}:generateContent"
r = httpx.post(url, json=body, headers={"x-goog-api-key": API_KEY})
risposta = r.json()["candidates"][0]["content"]["parts"][0]["text"]
Il file completo (~100 righe) è app/main.py nel progetto. Dettagli utili:
- temperature 0.2: risposte stabili, meno fantasia.
- La chiave va nell'header
x-goog-api-key, non nell'URL (finirebbe nei log). - Se manca la chiave, il server risponde mostrando i passaggi trovati: utile per testare la ricerca da sola.
- L'indice si ricarica da solo quando cambia un file in
kb/.
Volendo, ricerca semantica: al posto di BM25 calcola gli embedding di ogni chunk con il modello gemini-embedding-001 (endpoint :embedContent) e confronta con la domanda per similarità del coseno. Trova sinonimi ("consegna" ≈ "spedizione"), ma costa una chiamata in più a domanda.
6. La pagina della chat
Un HTML con un fetch('/api/chat') che manda domanda e ultimi 6 messaggi (per la memoria della conversazione) e mostra risposta e fonti. Usa sempre textContent, mai innerHTML, per non eseguire codice arrivato dal modello.
7. Pubblicare (Docker + Caddy)
# docker-compose.yml
services:
gemini-agent:
build: .
restart: unless-stopped
env_file: .env
ports: ["127.0.0.1:8850:8000"] # solo locale: Docker scavalca il firewall!
volumes: ["./kb:/app/kb:ro"]
mem_limit: 256m
# Caddyfile: HTTPS automatico
chatbot.tuodominio.it {
reverse_proxy 127.0.0.1:8850
}
docker compose up -d --build curl -s https://chatbot.tuodominio.it/api/health
Prima crea il record DNS A del sottodominio verso l'IP del server, poi ricarica Caddy (systemctl reload caddy).
8. Variante: Vertex AI Agent Builder
- Su console.cloud.google.com crea un progetto e attiva la fatturazione (metti subito un budget con avviso, es. 5 €).
- Cerca AI Applications (ex Agent Builder / Vertex AI Search) → Create app → tipo Search o Agent.
- Crea un data store: carica i documenti su Cloud Storage, o indica un sito o un Drive. Scegli la regione eu se i dati devono restare in Europa.
- Attendi l'indicizzazione, prova nella Preview, poi integra con il widget web o con l'API.
9. Manutenzione e aggiornamento della base di conoscenza
- Aggiornare la KB: modifica o aggiungi un
.mdinkb/. Nessun riavvio: l'indice si ricarica alla domanda dopo. - Test di regressione: tieni una lista di 10-20 domande con la risposta attesa; rilanciala dopo ogni modifica della KB o del modello.
- Domande senza risposta: se il bot dice spesso "non lo so" su un tema, manca un documento. Scrivilo.
- Modelli: Google ritira i modelli vecchi. Il nome sta in
GEMINI_MODEL: cambi una riga e riavvii (docker compose up -d). Controlla gli avvisi di deprecazione. - Aggiornamenti: ogni tanto
docker compose build --pullper le patch di sicurezza dell'immagine Python. - Log:
docker logs gemini-agent --tail 50. Errori 429 = limite superato; 400/403 = chiave o modello.
10. Costi
| Voce | Costo |
|---|---|
| Gemini API free tier (Flash) | 0 € entro i limiti di richieste al minuto e al giorno |
| Gemini API a pagamento (Flash) | pochi centesimi ogni migliaio di domande brevi |
| Server (VPS già esistente) | 0 € in più (container da ~60 MB di RAM) |
| Vertex AI Agent Builder | a consumo, con crediti di prova; mettere sempre un budget |
I limiti e i prezzi cambiano: verificali su ai.google.dev/gemini-api/docs/pricing. La demo ha un tetto di domande al giorno (DAILY_MAX) e al minuto per IP (RATE_PER_MIN): così non si esce dal gratuito.
11. Sicurezza
- Chiave solo in
.env(permessi 600), mai nel codice, nel browser o su git. Se scappa: revocala in AI Studio e creane un'altra. - Il browser parla solo col tuo server; è il server che chiama Gemini.
- Porte Docker legate a
127.0.0.1; fuori si passa solo da Caddy in HTTPS. - Limiti di frequenza e lunghezza della domanda (800 caratteri), storia massima 10 messaggi.
- Prompt injection: un utente può scrivere "ignora le istruzioni…". Il prompt di sistema lo prevede, ma non metterai mai nella KB nulla che non possa diventare pubblico: tutto ciò che è nella KB può uscire.
- Risposte mostrate come testo (
textContent), mai come HTML. - Nella chiave su Google Cloud puoi impostare restrizioni API (solo "Generative Language API").
12. Errori comuni
| Sintomo | Causa e rimedio |
|---|---|
400 API key not valid | Chiave copiata male, con spazi o virgolette nel .env. |
404 model not found | Nome modello sbagliato o ritirato: aggiorna GEMINI_MODEL. |
429 RESOURCE_EXHAUSTED | Superato il free tier: abbassa i limiti, aspetta, o attiva il piano a pagamento. |
| Risposte inventate | Il contesto non contiene la risposta, o il prompt non dice "solo dal CONTESTO". Abbassa la temperatura. |
| "Non lo so" a domande ovvie | La ricerca non trova il pezzo: parole diverse tra domanda e documento. Aggiungi sinonimi al testo o passa agli embedding. |
| Risposta tagliata | maxOutputTokens troppo basso, o i modelli "thinking" consumano token: imposta thinkingBudget: 0. |
| Sito irraggiungibile | Manca il record DNS, o Caddy non ricaricato, o container fermo (docker ps). |
| Chiave nel repo | Revocala subito; riscrivere la storia git non basta. |
Demo con dati di fantasia ("Caffè Aurora"). Aggiornato a ottobre 2026.