Site icon Syrus

Creare un chatbot con API LLM in Python: tutorial Claude

Creare un chatbot con API LLM in Python: tutorial Claude

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’:

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

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

Come estendere il progetto

Hai una base solida. Da qui puoi spingerti oltre:

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.

Syrus

Exit mobile version