Vai al contenuto principale

Gemini 2.0 Flash: tutorial passo dopo passo con progetto demo

Scopri come usare il modello Gemini 2.0 Flash di Google per sviluppare un assistente visivo capace di leggere i contenuti sullo schermo e rispondere a domande al riguardo usando Python.
· 12 min leggi

Scopri con l'IA

Google ha annunciato di recente Gemini 2.0, con il modello Gemini 2.0 Flash al centro—una versione più veloce e più potente pensata per migliorare l'elaborazione di immagini e suoni.

In questo tutorial ti guiderò nei passaggi per usare Gemini 2.0 Flash e creare un assistente visivo che sappia leggere ciò che c'è sullo schermo e rispondere alle domande al riguardo.

Ecco una demo di ciò che costruiremo:

Passaggio 1: configura la chiave API per Google AI Studio

Per impostare la chiave API, vai su Google AI Studio e clicca sul pulsante “Create API Key”. Ricordati di copiare la chiave e incollarla in un file chiamato .env, con il seguente formato:

GOOGLE_API_KEY=replace_this_with_api_key

Se hai già lavorato con Google Cloud Platform usando lo stesso account Google, Google AI Studio ti chiederà di scegliere uno dei tuoi progetti per attivare l'API.

Per seguire questo tutorial, il codice Python deve trovarsi nella stessa cartella del file .env.

Passaggio 2: installa le dipendenze Python

Per questo progetto useremo i seguenti pacchetti:

  • google-genai: una libreria Python per integrare i modelli di AI generativa di Google nelle nostre applicazioni.
  • pyautogui: una libreria multipiattaforma per controllare in modo programmatico mouse e tastiera per automatizzare attività. Nel nostro caso, la usiamo per fornire al modello AI il contenuto dello schermo.
  • python-dotenv: una libreria per gestire le variabili d'ambiente caricandole da file .env nella nostra applicazione Python.
  • sounddevice: una libreria Python per registrare e riprodurre suoni tramite semplici API per input e output audio.
  • numpy: una libreria fondamentale per il calcolo numerico in Python, che offre supporto per array, matrici e molte operazioni matematiche.

Per installare le dipendenze, possiamo usare pip:

pip install google-genai pyautogui python-dotenv sounddevice numpy

In alternativa, scarichiamo il file requirements.txt dal repository GitHub che ho preparato per questo progetto e usiamolo per creare un ambiente Conda:

conda create --name gemini python=3.11
conda activate gemini
pip install -r requirements.txt

Passaggio 3: crea un chatbot testuale con la Google GenAI API

Cominciamo creando una semplice interfaccia AI da riga di comando usando il modello Gemini 2 Flash di Google con la libreria google.genai. Ti consiglio di dare un'occhiata alla documentazione ufficiale di Gemini 2.0 in caso di intoppi lungo il percorso.

Il codice completo di questo esempio è disponibile nel file text.py sul mio repository GitHub.

Creare e connettersi al client Google GenAI

Il primo passo è caricare in modo sicuro la chiave API e inizializzare il client Google GenAI. Lo script usa dotenv per caricare le variabili d'ambiente dal file .env.

Ecco come impostare il client con le credenziali necessarie:

from google import genai
from dotenv import load_dotenv
import os

# Load environment variables from a .env file
load_dotenv()
client = genai.Client(
    api_key=os.getenv("GOOGLE_API_KEY"),
    http_options={"api_version": "v1alpha"},
)

print("Connected to the AI model!")

Effettuare chiamate API asincrone

Quando si lavora con API come Google GenAI, spesso è necessario gestire operazioni asincrone. La programmazione asincrona consente ad altre operazioni di proseguire mentre si attendono le richieste di rete, rendendo l'applicazione più reattiva. Ciò è particolarmente importante con operazioni ad alta latenza come le richieste di rete.

In Python, la programmazione asincrona è resa possibile dalla libreria asyncio e dalla sintassi async/await.

Ecco come possiamo fare una richiesta asincrona a Google GenAI:

from google import genai
from dotenv import load_dotenv
import os
import asyncio

# Load environment variables from a .env file
load_dotenv()
async def main():
    client = genai.Client(
        api_key=os.getenv("GOOGLE_API_KEY"),
        http_options={"api_version": "v1alpha"},
    )    
    # Define the AI model and configuration
    model_id = "gemini-2.0-flash-exp"
    config = {"response_modalities": ["TEXT"]}
    async with client.aio.live.connect(model=model_id, config=config) as session:
        await session.send("Hello", end_of_turn=True)
        # Process responses from the AI
        async for response in session.receive():
            if not response.server_content.turn_complete:
                for part in response.server_content.model_turn.parts:
                    print(part.text, end="", flush=True)

# Run the main function
asyncio.run(main())

Questa versione si connette al modello AI e invia un singolo messaggio “Hello”. La risposta viene stampata parola per parola sulla console.

Renderla interattiva

Per rendere l'applicazione interattiva, consentendo all'utente di chattare con il modello AI, aggiungiamo un ciclo che permette di inviare più messaggi. Il ciclo continua finché l'utente non digita "exit".

from google import genai
from dotenv import load_dotenv
import os
import asyncio

# Load environment variables from a .env file
load_dotenv()
async def main():
    client = genai.Client(
        api_key=os.getenv("GOOGLE_API_KEY"),
        http_options={"api_version": "v1alpha"},
    )
    # Define the AI model and configuration
    model_id = "gemini-2.0-flash-exp"
    config = {"response_modalities": ["TEXT"]}
    async with client.aio.live.connect(model=model_id, config=config) as session:
        while True:
            message = input("> ")
            print()
            # Exit the loop if the user types "exit"
            if message == "exit":
                print("Exiting...")
                break
            # Send the user's message to the AI model, marking the end of the turn
            await session.send(message, end_of_turn=True)
            # Receive responses asynchronously and process each response
            async for response in session.receive():
                if not response.server_content.turn_complete:
                    for part in response.server_content.model_turn.parts:
                        print(part.text, end="", flush=True)
            print()

# Run the main function
asyncio.run(main())

E questo è tutto! Con lo script qui sopra, abbiamo creato un chatbot AI da riga di comando usando la Google GenAI API. Ecco come appare:

Passaggio 4: aggiungi la modalità audio

La modalità audio consente al modello di rispondere con la voce invece che con il testo. Per adattare l'esempio precedente alla gestione delle risposte audio, dobbiamo:

  1. Importare sounddevice per la riproduzione audio e numpy per elaborare i dati audio.
  2. Cambiare la modalità di risposta da TEXT a AUDIO:
	config = {"response_modalities": ["AUDIO"]} 
  1. Inizializzare un flusso audio prima di connettersi al client:
	with sd.OutputStream(
	samplerate=24000, 
	channels=1, 
	dtype="int16",
) as audio_stream:
  1. Accedere ai dati audio dalla parte di risposta e aggiungerli al flusso audio per la riproduzione:
for part in response.server_content.model_turn.parts:
    # Get the audio data from the response part and add it to the steam
    inline_data = part.inline_data
    audio_data = np.frombuffer(inline_data.data, dtype="int16")
    audio_stream.write(audio_data)

Il file audio.py nel repository contiene lo script completo con queste modifiche applicate. Lo script include commenti sulle righe che sono cambiate.

Passaggio 5: aggiungi l'esecuzione di codice con gli strumenti

Una delle grandi funzionalità dei modelli di AI moderni è la capacità di chiamare autonomamente funzioni personalizzate nel nostro codice, e Gemini 2 non fa eccezione.

Il funzionamento è questo: diciamo al modello quali funzioni può chiamare registrandole come strumenti. Poi, analizzando il prompt, i nomi delle funzioni e le descrizioni, il modello deciderà se vuole effettuare una chiamata di funzione. Quando decide di farlo, invierà una risposta speciale con il nome della funzione che vuole chiamare e gli argomenti.

Per definire uno strumento, dobbiamo:

  1. Scrivere una funzione Python con lo stesso nome e gli stessi argomenti definiti nello schema.
  2. Creare lo schema della funzione, ovvero un dizionario con i metadati della funzione, come nome, descrizione testuale e specifica degli argomenti.
  3. Fornire lo schema della funzione al modello di AI.
  4. Eseguire la funzione quando il modello la richiede.

Per illustrarlo, definiamo uno strumento che possa leggere un file, consentendo al modello di rispondere a domande sui file presenti sulla nostra macchina locale.

Definire la funzione

Questa parte è semplice codice Python. Il modello si aspetta che la risposta sia un dizionario con una chiave ”result” se la funzione ha avuto successo e ”error” altrimenti.

def load_file_content(filename):
    try:
      with open(filename, "rt") as f:
          return {
              "result": f.read()
          }
    except Exception as e:
      return {
          "error": "Could not load file content",
      }

Specificare lo schema

Ecco come possiamo definire uno schema per questa funzione:

load_file_content_schema = {
    "name": "load_file_content",
    "description": "Load the content of a file",
    "parameters": {
        "type": "object",
        "properties": {
            "filename": {
                "type": "string",
                "description": "The name of the file",
            },
        },
        "required": ["filename"],
    },
    "output": {
        "type": "string",
        "description": "The text content of the file",
    },
}

Forniamo quattro campi:

  • ”name”: il nome della funzione.
  • ”description”: una descrizione testuale. Viene usata dal modello per decidere se chiamare la funzione.
  • ”properties”: descrizione degli argomenti della funzione.
  • ”output”: descrizione dell'output della funzione.

Consulta la documentazione ufficiale per maggiori informazioni sugli schemi delle funzioni.

Fornire la funzione al modello

Per far conoscere al modello la nostra funzione, forniamo lo schema della funzione nella configurazione del modello:

config = {
       "tools": [{"function_declarations": [load_file_content_schema]}], 
       "response_modalities": ["TEXT"]
}

Elaborare la richiesta di chiamata di funzione dal modello

Quando il modello decide di effettuare una chiamata di funzione, aggiunge un tool_call alla response. Questo conterrà il nome della funzione e gli argomenti. Potrebbe contenere più richieste di chiamata, quindi dobbiamo iterare su tutte, chiamare le funzioni corrispondenti e inviare il risultato al modello:

# A dictionary mapping the function names to the actual functions
FUNCTIONS = {"load_file_content": load_file_content}
if response.tool_call is not None:
    for fc in tool_call.function_calls:
        f = FUNCTIONS.get(fc.name)
        tool_response = types.LiveClientToolResponse(
            function_responses=[
                types.FunctionResponse(
                    name=fc.name,
                    id=fc.id,
                    response=f(**fc.args),
                )
            ]
        )
    await session.send(tool_response)

L'implementazione completa di questo esempio è fornita nel file tool.py nel repository contenente lo script completo con queste modifiche applicate. Le definizioni di funzione e schema sono nel file tool_spec.py.

Accesso al web

Usando gli strumenti, possiamo anche dare al modello la possibilità di accedere al web aggiungendo lo strumento di ricerca Google:

search_tool = {"google_search": {}}
config = {
    "tools": [search_tool],
    "response_modalities": ["TEXT"],
}

Questo strumento è integrato, quindi non è necessario fornire una funzione.

Esecuzione di codice

Un'altra funzione integrata è l'esecuzione di codice. L'esecuzione di codice consente al modello di scrivere ed eseguire codice Python per rispondere a domande complesse, di solito legate alla matematica. Ad esempio, con questo strumento, se gli chiediamo di calcolare la somma dei primi 10 numeri primi, prima scriverà del codice Python per calcolarla, poi lo eseguirà e infine fornirà la risposta.

Per attivare lo strumento, facciamo così:

code_execution_tool = {"code_execution": {}}
config = {
    "tools": [code_execution_tool],
    "response_modalities": ["TEXT"],
}

Ecco un esempio del comportamento del modello con l'esecuzione di codice:

​​> add the first 10 prime numbers
Okay, I understand. You want me to add the first 10 prime numbers.
Here's my plan:
1. **Identify the first 10 prime numbers:** I will use a python code to find prime numbers.
2. **Sum the prime numbers:** I will also sum them using python.
3. **Report the result**
The first 10 prime numbers are 2, 3, 5, 7, 11, 13, 17, 19, 23, and 29.  Their sum is 129.

Passaggio 6: crea un assistente visivo

In quest'ultima sezione, esploriamo le capacità visive di Gemini 2. L'obiettivo è creare un assistente AI in grado di comprendere il contenuto del nostro schermo e rispondere a domande al riguardo. Questo può essere utile, per esempio, quando gli chiediamo di spiegare un errore che vediamo nel terminale o di fornire informazioni su qualcosa attualmente visualizzato sullo schermo.

Negli esempi precedenti, abbiamo usato la programmazione asincrona per connetterci al modello AI e inviare dati avanti e indietro elaborando le risposte in tempo reale. Purtroppo, la versione attuale dell'SDK non supporta ancora la comunicazione in tempo reale con le immagini. Invece, forniamo i dati dell'immagine tramite un flusso richiesta-risposta. Nota che questa non è una limitazione di Gemini 2.0 in sé—è solo che l'attuale API beta non lo supporta ancora.

Inviare una richiesta sincrona a Google GenAI

Iniziamo imparando come inviare una richiesta con dati immagine alla Google GenAI API. Ecco come possiamo inviare una richiesta sincrona:

from google import genai
from dotenv import load_dotenv
import os

load_dotenv()

client = genai.Client(
    api_key=os.getenv("GOOGLE_API_KEY"),
    http_options={"api_version": "v1alpha"},
)

response = client.models.generate_content(
    model="gemini-2.0-flash-exp",
    contents=["Hello"],
)

print(response.text)

La differenza principale in questo esempio è che usiamo client.models.generate_content per inviare una richiesta all'API. Si tratta di una richiesta sincrona, il che significa che non offre un'esperienza di conversazione in tempo reale.

Inviare un'immagine

Possiamo inviare un'immagine caricandola e aggiungendola alla lista contents. Usiamo il pacchetto PIL per creare una funzione chiamata load_and_resize_image() che carica e ridimensiona l'immagine.

from PIL import Image

def load_and_resize_image(image_path):
    with Image.open(image_path) as img:
        aspect_ratio = img.height / img.width
        new_height = int(img.width * aspect_ratio)
        return img.resize((img.width, new_height), Image.Resampling.LANCZOS)    

image = load_and_resize_image("example_image.jpeg")

response = client.models.generate_content(
    model="gemini-2.0-flash-exp",
    contents=["Describe the image", image],
)

print(response.text)

Creare l'assistente visivo AI

L'assistente visivo AI elabora un prompt testuale insieme a uno screenshot per aiutarci a rispondere a domande su ciò che c'è sullo schermo. Ho sperimentato a lungo e il modello riesce a comprendere il contenuto dello schermo anche quando sono aperte più finestre.

Il modo più semplice per fornire al modello il contenuto dello schermo è fare uno screenshot. Per questo usiamo il pacchetto pyautogui, una libreria multipiattaforma per controllare in modo programmatico mouse e tastiera per automatizzare attività. Nel nostro caso, la useremo solo per fare screenshot, anche se potremmo estenderne la funzionalità per consentire all'AI di eseguire attività sul nostro computer in autonomia.

Ecco una funzione per fare uno screenshot:

def capture_screen():
    timestamp = time.strftime("%Y%m%d-%H%M%S")
    filename = f"screenshot_{timestamp}.jpeg"
    screenshot = pyautogui.screenshot()
    screenshot = screenshot.convert("RGB")
    screenshot.save(filename, format="JPEG")
    return filename

Per renderlo interattivo, ripetiamo i passaggi seguenti finché l'utente non decide di uscire dall'applicazione:

  1. Chiedere all'utente di inserire un prompt.
  2. Fare uno screenshot e inviarlo al modello AI insieme al prompt.
  3. Mostrare il risultato all'utente.

C'è una cosa da tenere a mente con questo approccio. L'applicazione girerà nel terminale, che è anch'esso visualizzato sullo schermo. Pertanto, è meglio istruire il modello a ignorare la finestra del terminale. Questo si può fare aggiungendo una system_instruction alla configurazione:

client.models.generate_content(
        model="gemini-2.0-flash-exp",
        contents=[prompt, screen],
        config=types.GenerateContentConfig(
            system_instruction="Ignore the terminal window in the image when analyzing the image",
        ),
)

Mettendo tutto insieme, ecco il nostro assistente visivo AI. Il file con il codice completo è disponibile anche come vision.py nel repository.

from google import genai
from google.genai import types
from PIL import Image
import pyautogui
import time
import os
from dotenv import load_dotenv
load_dotenv()

# Initialize the GenAI client
client = genai.Client(
    api_key=os.getenv("GOOGLE_API_KEY"),
    http_options={"api_version": "v1alpha"},
)
def capture_screen():
    timestamp = time.strftime("%Y%m%d-%H%M%S")
    filename = f"screenshot_{timestamp}.jpeg"
    screenshot = pyautogui.screenshot()
    screenshot = screenshot.convert("RGB")
    screenshot.save(filename, format="JPEG")
    return filename

def load_and_resize_image(image_path):
    with Image.open(image_path) as img:
        aspect_ratio = img.height / img.width
        new_height = int(img.width * aspect_ratio)
        return img.resize((img.width, new_height), Image.Resampling.LANCZOS)    

def get_genai_response(prompt):
    print("Analyzing screen...")
    screen = load_and_resize_image(capture_screen())
    response = client.models.generate_content(
        model="gemini-2.0-flash-exp",
        contents=[prompt, screen],
        config=types.GenerateContentConfig(
            system_instruction="Ignore the terminal window in the image when analyzing the image",
        ),
    )
    return response.text

def main():
    while True:
        prompt = input("> ")
        print()
        if prompt == "exit":
            break
        answer = get_genai_response(prompt)
        print(answer)
        print()

if __name__ == "__main__":
    main()

La versione attuale è un po' macchinosa: richiede che il terminale sia aperto in primo piano per poterla attivare. Un passo successivo naturale sarebbe eseguirla in background e usare input e output vocali invece del testo.

Conclusione

Abbiamo imparato a usare il modello Gemini 2.0 Flash per diverse applicazioni, come sviluppare chatbot capaci di conversazioni in tempo reale con testo e voce, abilitare il modello AI a eseguire azioni tramite chiamate di funzione e creare un assistente visivo in grado di analizzare i contenuti dello schermo del nostro computer.

Se vuoi approfondire, Google fornisce due esempi che mostrano la capacità del modello di rilevare oggetti in un'immagine fornendo i relativi riquadri di delimitazione con etichette. Questo esempio si concentra sulla comprensione spaziale 2D chiedendo al modello di identificare ed etichettare i cupcake in una foto. Gemini può anche comprendere il contesto 3D di un'immagine, come mostrato qui.

Nel complesso, Gemini 2.0 è piuttosto promettente, nonostante l'attuale versione dell'API non ci permetta ancora di sfruttarlo al massimo. Non vedo l'ora di usare appieno le capacità multimodali in tempo reale nel prossimo futuro.

François Aubry

Autore

François Aubry

Ingegnere full‑stack e fondatore di CheapGPT. Insegnare è sempre stata la mia passione. Fin dai primi anni da studente, cercavo con entusiasmo occasioni per fare da tutor e aiutare altri studenti. Questa passione mi ha portato a intraprendere un dottorato, durante il quale ho anche svolto il ruolo di assistente alla didattica a supporto del mio percorso accademico. In quegli anni ho trovato enorme soddisfazione nell'ambiente tradizionale dell'aula, creando connessioni e facilitando l'apprendimento. Con l'avvento delle piattaforme di apprendimento online, però, ho riconosciuto il potenziale trasformativo dell'educazione digitale. Di fatto, ho partecipato attivamente allo sviluppo di una di queste piattaforme nella nostra università. Sono profondamente impegnato a integrare i principi dell'insegnamento tradizionale con metodologie digitali innovative. La mia passione è creare corsi non solo coinvolgenti e informativi, ma anche accessibili a chi apprende in questa era digitale.

Argomenti

Correlato