Cosa costruiamo
In questo tutorial pratico realizziamo un chatbot da terminale in Python che dialoga con un modello LLM di Claude tramite le API ufficiali di Anthropic. Niente teoria astratta: partiamo da una singola chiamata e arriviamo, passo dopo passo, a un assistente completo.
Alla fine il tuo chatbot sapra’:
- mantenere la memoria della conversazione, ricordando i messaggi precedenti;
- fare streaming della risposta, stampandola parola per parola come una chat reale;
- gestire gli errori di rete e di quota senza crashare;
- tracciare token e costi di ogni messaggio in tempo reale.
E’ il classico progetto end-to-end che trasforma gli LLM da concetto a competenza spendibile: oggi saper integrare un modello via API e’ una delle skill piu’ richieste sul mercato.
Prerequisiti
- Python 3.9 o superiore installato (verifica con
python3 --version). - Conoscenza di base di funzioni, dizionari e cicli in Python.
- Una chiave API Anthropic, che ottieni dalla console su console.anthropic.com.
Una nota importante: la chiamata alle API e’ un servizio a consumo. Hai un piccolo credito iniziale, ma ogni richiesta ha un costo legato ai token. Per questo nel tutorial integriamo subito il monitoraggio della spesa.
Passo 1 — Ambiente e chiave API
Creiamo un ambiente virtuale isolato e installiamo l’unica dipendenza che ci serve, la libreria ufficiale anthropic.
python3 -m venv venv
source venv/bin/activate
pip install anthropic
Adesso impostiamo la chiave come variabile d’ambiente. Non scrivere mai la chiave nel codice: se finisce su Git, e’ compromessa. La libreria la legge in automatico dalla variabile ANTHROPIC_API_KEY.
export ANTHROPIC_API_KEY='la-tua-chiave-segreta'
Su Windows (PowerShell) usa $env:ANTHROPIC_API_KEY='...'. Per un progetto serio valuta un file .env con python-dotenv, ricordandoti di aggiungerlo a .gitignore.
Passo 2 — La prima chiamata alle API di Claude
Iniziamo dal minimo: una domanda, una risposta. Crea il file first_call.py.
import anthropic
client = anthropic.Anthropic()
response = client.messages.create(
model='claude-sonnet-4-6',
max_tokens=300,
messages=[
{'role': 'user',
'content': 'Spiegami cosa e un LLM in due righe.'}
],
)
print(response.content[0].text)
print('Token:', response.usage.input_tokens,
'+', response.usage.output_tokens)
Eseguilo:
python first_call.py
Tre cose da capire qui. Il parametro model sceglie il modello: claude-sonnet-4-6 e’ un ottimo equilibrio tra qualita’ e costo; per risparmiare puoi passare a claude-haiku-4-5. Il parametro max_tokens e’ il tetto massimo di token della risposta, non un valore fisso. Infine response.content e’ una lista di blocchi: per il testo leggiamo response.content[0].text.
Passo 3 — Dare memoria alla conversazione
Le API LLM sono stateless: ogni chiamata e’ indipendente e il modello non ricorda nulla. La memoria la costruisci tu, inviando l’intera cronologia a ogni richiesta. Il trucco e’ una semplice lista di messaggi con ruoli alternati user e assistant.
messages = []
def ask(client, messages, user_text):
messages.append({'role': 'user', 'content': user_text})
response = client.messages.create(
model='claude-sonnet-4-6',
max_tokens=512,
messages=messages,
)
answer = response.content[0].text
messages.append({'role': 'assistant',
'content': answer})
return answer
Dopo ogni risposta aggiungiamo anche il testo del modello alla lista: cosi’ al turno successivo Claude vede tutto il contesto e puo’ rispondere a frasi come e quindi? capendo a cosa ti riferisci. Attenzione: piu’ lunga e’ la cronologia, piu’ token invii (e paghi) a ogni richiesta.
Passo 4 — Streaming delle risposte
Aspettare 5 secondi una risposta in silenzio e’ frustrante. Con lo streaming il testo appare a pezzi, appena il modello lo genera. La libreria offre un context manager dedicato e un iteratore comodo, text_stream.
with client.messages.stream(
model='claude-sonnet-4-6',
max_tokens=512,
messages=messages,
) as stream:
for chunk in stream.text_stream:
print(chunk, end='', flush=True)
print()
Il parametro flush=True forza la stampa immediata di ogni frammento. A streaming concluso possiamo recuperare il messaggio completo con stream.get_final_message(), che contiene anche i dati di utilizzo dei token: ci servira’ tra poco per calcolare i costi.
Passo 5 — Il chatbot completo: errori, token e costi
Mettiamo tutto insieme in chatbot.py. Questo file unisce memoria, streaming, gestione degli errori e calcolo della spesa in un ciclo interattivo.
import anthropic
MODEL = 'claude-sonnet-4-6'
MAX_TOKENS = 1024
# Prezzi indicativi in USD per milione di token.
# Verifica i valori aggiornati sulla pagina pricing.
PRICE_IN = 3.0 / 1_000_000
PRICE_OUT = 15.0 / 1_000_000
SYSTEM_PROMPT = (
'Sei un assistente tecnico in italiano. '
'Rispondi in modo chiaro e conciso.'
)
def stream_reply(client, messages):
print('Claude: ', end='', flush=True)
text = ''
with client.messages.stream(
model=MODEL,
max_tokens=MAX_TOKENS,
system=SYSTEM_PROMPT,
messages=messages,
) as stream:
for chunk in stream.text_stream:
print(chunk, end='', flush=True)
text += chunk
final = stream.get_final_message()
usage = final.usage
cost = (usage.input_tokens * PRICE_IN
+ usage.output_tokens * PRICE_OUT)
print(f'\n[token in={usage.input_tokens} '
f'out={usage.output_tokens} '
f'costo=${cost:.5f}]\n')
return text, cost
def main():
client = anthropic.Anthropic() # legge la chiave
messages = []
spent = 0.0
print('Chatbot Claude pronto. Scrivi esci per uscire.\n')
while True:
try:
user_input = input('Tu: ').strip()
except (EOFError, KeyboardInterrupt):
print('\nA presto!')
break
if not user_input:
continue
if user_input.lower() in ('esci', 'exit', 'quit'):
print(f'A presto! Costo sessione: ${spent:.4f}')
break
messages.append({'role': 'user',
'content': user_input})
try:
reply, cost = stream_reply(client, messages)
except anthropic.RateLimitError:
print('Troppe richieste: attendi e riprova.')
messages.pop()
continue
except anthropic.APIConnectionError:
print('Problema di rete: controlla la linea.')
messages.pop()
continue
except anthropic.APIStatusError as e:
print(f'Errore API ({e.status_code}): {e.message}')
messages.pop()
continue
messages.append({'role': 'assistant',
'content': reply})
spent += cost
if __name__ == '__main__':
main()
Avvialo con python chatbot.py e prova una conversazione:
Tu: Ciao, in due parole chi sei?
Claude: Sono Claude, un assistente AI.
[token in=26 out=11 costo=$0.00025]
Tu: esci
A presto! Costo sessione: $0.0003
Cosa abbiamo aggiunto
Il system prompt (parametro system) definisce personalita’ e regole del bot: e’ separato dai messaggi e vale per tutta la sessione. La gestione errori intercetta i tre casi piu’ frequenti: limite di richieste, problemi di rete e risposte di errore del server. Nota il messages.pop() dentro ogni except: se la chiamata fallisce, rimuoviamo il messaggio utente appena aggiunto, cosi’ la cronologia non resta sbilanciata con un turno senza risposta.
Errori comuni e trappole
- Chiave in chiaro nel codice. E’ l’errore numero uno. Usa sempre variabili d’ambiente o un file
.envignorato da Git. - Dimenticare di salvare la risposta in
messages. Senza il messaggioassistantin cronologia, il bot perde il filo e i ruoli si disallineano. - Ruoli non alternati. L’API richiede che i messaggi alternino
usereassistant. Dueuserdi fila generano un errore400. - Cronologia infinita. Conversazioni lunghe gonfiano i token di input a ogni chiamata. Imposta un limite e taglia i messaggi piu’ vecchi quando serve.
- Prezzi hardcoded. Le tariffe cambiano: tieni i valori in costanti e verificali sulla pagina pricing ufficiale di Anthropic.
Come estendere il progetto
Hai una base solida. Da qui puoi spingerti oltre:
- Salvare le conversazioni su file JSON per riprenderle in seguito.
- Limitare la finestra di contesto mantenendo solo gli ultimi N messaggi, per controllare i costi.
- Aggiungere i tool (function calling) per far chiamare al modello funzioni reali, come una ricerca o una query a un database.
- Passare a un’interfaccia web con FastAPI o Streamlit, riusando esattamente la stessa logica.
- Implementare il prompt caching per ridurre il costo dei prompt di sistema lunghi e ripetuti.
Conclusione
Hai costruito un chatbot Python funzionante che parla con un LLM di Claude, ricorda la conversazione, fa streaming e tiene sotto controllo errori e costi. Sono gli stessi mattoni con cui si costruiscono assistenti e agenti in produzione: cambia la scala, non i principi.
Se hai bisogno di portare un’idea basata su LLM dal prototipo al prodotto, con team che conoscono architettura, sicurezza e costi, parla con gli sviluppatori di Syrus Industry e trasforma il tuo progetto AI in software professionale.

