Creazione di agenti AI persistenti con ADK e Cloud SQL

1. Introduzione

In questa sessione pratica, passerai dai chatbot di base e senza stato a un concierge intelligente per un bar, un agente AI basato su Gemini che funge da barista amichevole. Prende gli ordini di caffè monitorati nello stato della sessione, ricorda le preferenze alimentari a lungo termine nello stato con ambito utente e salva tutto in un database Cloud SQL PostgreSQL. Alla fine, l'agente ricorda che sei intollerante al lattosio anche dopo aver riavviato l'applicazione e iniziato una nuova conversazione.

Ecco l'architettura del sistema che creeremo

a98bbd65ddedd29c.jpeg

Prerequisiti

  • Un account Google Cloud con un account di fatturazione di prova
  • Conoscenza di base di Python
  • Non è richiesta alcuna esperienza pregressa con l'ADK, gli agenti AI o Cloud SQL

Cosa imparerai a fare

  • Crea un agente AI utilizzando l'Agent Development Kit (ADK) di Google con strumenti personalizzati
  • Definisci gli strumenti che leggono e scrivono lo stato della sessione tramite ToolContext
  • Distinguere tra stato basato sulle sessioni e stato basato sugli utenti (prefisso user:)
  • Esegui il provisioning di un'istanza Cloud SQL PostgreSQL e connettiti a questa da Cloud Shell
  • Esegui la migrazione dall'archiviazione locale (impostazione predefinita quando utilizzi il comando adk web) a DatabaseSessionService per l'archiviazione permanente con un database dedicato
  • Verificare che la memoria dell'agente venga mantenuta dopo i riavvii dell'applicazione e tra sessioni di conversazione separate

Che cosa ti serve

  • Un computer funzionante e una connessione a internet affidabile.
  • Un browser, ad esempio Chrome, per accedere a Google Cloud Console
  • Una mente curiosa e la voglia di imparare.

2. Configura l'ambiente

Questo passaggio prepara l'ambiente Cloud Shell e configura il progetto Google Cloud

Apri Cloud Shell

Apri Cloud Shell nel browser. Cloud Shell fornisce un ambiente preconfigurato con tutti gli strumenti necessari per questo codelab. Quando richiesto, fai clic su Autorizza.

L'interfaccia dovrebbe essere simile a questa

86307fac5da2f077.png

Questa sarà la nostra interfaccia principale, con l'IDE in alto e il terminale in basso.

Configurare la directory di lavoro

Crea la directory di lavoro. Tutto il codice che scrivi in questo codelab si trova qui, separato dal repository di riferimento:

# Create your working directory
mkdir -p ~/build-agent-adk-cloudsql

# Change cloudshell workspace and working directory into previously created dir
cloudshell workspace ~/build-agent-adk-cloudsql && cd ~/build-agent-adk-cloudsql

Per generare il terminale, vai a Visualizza -> Terminale.

ccc3214812750f1c.png

Configura il progetto Google Cloud e le variabili di ambiente iniziali

Scarica lo script di configurazione del progetto nella directory di lavoro:

curl -sL https://raw.githubusercontent.com/alphinside/cloud-trial-project-setup/main/setup_verify_trial_project.sh -o setup_verify_trial_project.sh

Esegui lo script. Verifica il tuo account di fatturazione di prova, crea un nuovo progetto (o ne convalida uno esistente), salva l'ID progetto in un file .env nella directory corrente e imposta il progetto attivo nel terminale.

bash setup_verify_trial_project.sh && source .env

Quando esegui questo comando, ti verrà chiesto di inserire il nome dell'ID progetto suggerito. Puoi premere Enter per continuare.

54b615cd15f2a535.png

Dopo aver atteso un po' di tempo, se visualizzi questo output nella console, puoi passare al passaggio successivo e576b4c13d595156.png

Lo script eseguito esegue i seguenti passaggi:

  1. Verificare di avere un account di fatturazione di prova attivo
  2. Controlla se esiste un progetto in .env (se presente)
  3. Crea un nuovo progetto o riutilizza quello esistente
  4. Collega l'account di fatturazione di prova al tuo progetto
  5. Salva l'ID progetto in .env
  6. Imposta il progetto come progetto gcloud attivo

Verifica che il progetto sia impostato correttamente controllando il testo giallo accanto alla directory di lavoro nel prompt del terminale Cloud Shell. Dovrebbe essere visualizzato l'ID progetto.

9e11ee21cd23405f.png

Abilita le API richieste

Abilita le API Google Cloud necessarie per questo codelab:

gcloud services enable \
  aiplatform.googleapis.com \
  sqladmin.googleapis.com \
  compute.googleapis.com
  • API Vertex AI (aiplatform.googleapis.com): l'agente utilizza i modelli Gemini tramite Vertex AI.
  • API Cloud SQL Admin (sqladmin.googleapis.com): esegui il provisioning e la gestione di un'istanza PostgreSQL per l'archiviazione permanente.
  • API Compute Engine (compute.googleapis.com): necessaria per creare istanze Cloud SQL.

Configurare la regione di Gemini e dei prodotti Cloud

Prima di continuare, configuriamo anche la posizione/regione necessaria per il prodotto con cui interagiamo. Aggiungi la seguente configurazione al nostro file .env

# This is for our Gemini endpoint
echo "GOOGLE_CLOUD_LOCATION=global" >> .env

# This is for our other Cloud products
echo "REGION=us-central1" >> .env

source .env

Continuiamo con il passaggio successivo.

3. Configura Cloud SQL

Questo passaggio esegue il provisioning di un'istanza Cloud SQL PostgreSQL e passa l'agente dall'archiviazione in memoria all'archiviazione basata su database. La creazione dell'istanza richiede alcuni minuti, quindi inizieremo prima e potremo continuare la discussione sull'argomento successivo mentre attendiamo il completamento.

Avvia la creazione dell'istanza

Aggiungi la password del database al file .env e ricaricalo. Utilizzeremo cafe-agent-pwd-2025 come password.

echo "DB_PASSWORD=cafe-agent-pwd-2025" >> .env
source .env

Esegui questo comando per creare un'istanza Cloud SQL PostgreSQL. L'operazione richiede alcuni minuti. Lascia in esecuzione e continua con la sezione successiva.

gcloud sql instances create cafe-concierge-db \
  --database-version=POSTGRES_17 \
  --edition=ENTERPRISE \
  --region=${REGION} \
  --availability-type=ZONAL \
  --project=${GOOGLE_CLOUD_PROJECT} \
  --tier=db-f1-micro \
  --root-password=${DB_PASSWORD} \
  --quiet &

Alcune note sul comando precedente:

  • db-f1-micro è il livello Cloud SQL più piccolo (e più economico), sufficiente per questo codelab.
  • --root-password imposta la password per l'utente postgres predefinito.
  • Il suffisso & nel comando esegue il comando in background, così puoi continuare a lavorare.

Il processo verrà eseguito in background, ma l'output della console verrà mostrato di tanto in tanto nel terminale corrente. Apriamo una nuova scheda del terminale in Cloud Shell (fai clic sull'icona +) per concentrarci meglio.

b01e3fbd89f17332.png

Torna alla directory di lavoro e attiva il progetto utilizzando lo script di configurazione precedente.

cd ~/build-agent-adk-cloudsql
bash setup_verify_trial_project.sh && source .env

Poi, continuiamo con la sezione successiva.

4. Crea l'agente concierge del bar

Questo passaggio crea la struttura del progetto per l'agente ADK e definisce un semplice Cafe Concierge con uno strumento di menu.

Inizializza il progetto Python

Questo codelab utilizza uv, un gestore di pacchetti Python veloce che gestisce ambienti virtuali e dipendenze in un unico strumento. È preinstallato in Cloud Shell.

Inizializza un progetto Python e aggiungi l'ADK come dipendenza:

uv init
uv add google-adk==1.25.0 asyncpg

uv init crea un pyproject.toml e un ambiente virtuale. uv add installa la dipendenza e la registra in pyproject.toml.

Inizializzare la struttura del progetto dell'agente

L'ADK prevede un layout di cartelle specifico: una directory denominata come l'agente contenente __init__.py, agent.py e anche .env all'interno della directory dell'agente

ADK ha un comando integrato per aiutarti a stabilire rapidamente questa connessione. Esegui il seguente comando:

uv run adk create cafe_concierge \
    --model gemini-2.5-flash \
    --project ${GOOGLE_CLOUD_PROJECT} \
    --region ${GOOGLE_CLOUD_LOCATION}

Questo comando creerà una struttura di agenti con gemini-2.5-flash come cervello. La directory dovrebbe ora avere il seguente aspetto:

build-agent-adk-cloudsql/
├── cafe_concierge/
│   ├── __init__.py
│   ├── agent.py
│   └── .env
├── pyproject.toml
├── .env      
├── .venv/
└── ...

Scrivere l'agente

Apri cafe_concierge/agent.py nell'editor di Cloud Shell

cloudshell edit cafe_concierge/agent.py

e sovrascrivi il file con il seguente codice

# cafe_concierge/agent.py
from google.adk.agents import LlmAgent
from google.adk.tools import ToolContext

CAFE_MENU = {
    "espresso": {
        "price": 3.50,
        "description": "Rich and bold single shot",
        "tags": ["vegan", "dairy-free", "gluten-free"],
    },
    "latte": {
        "price": 5.00,
        "description": "Espresso with steamed milk",
        "tags": ["gluten-free"],
    },
    "oat milk latte": {
        "price": 5.50,
        "description": "Espresso with steamed oat milk",
        "tags": ["vegan", "dairy-free", "gluten-free"],
    },
    "cappuccino": {
        "price": 4.50,
        "description": "Espresso with equal parts steamed milk and foam",
        "tags": ["gluten-free"],
    },
    "cold brew": {
        "price": 4.00,
        "description": "Slow-steeped for 12 hours, served over ice",
        "tags": ["vegan", "dairy-free", "gluten-free"],
    },
    "matcha latte": {
        "price": 5.50,
        "description": "Ceremonial grade matcha with steamed milk",
        "tags": ["gluten-free"],
    },
    "croissant": {
        "price": 3.00,
        "description": "Buttery, flaky French pastry",
        "tags": [],
    },
    "banana bread": {
        "price": 3.50,
        "description": "Homemade with walnuts",
        "tags": ["vegan"],
    },
}


def get_menu() -> dict:
    """Returns the full cafe menu with prices, descriptions, and dietary tags.

    Use this tool when the customer asks what's available, wants to see
    the menu, or asks about specific items.
    """
    return CAFE_MENU


root_agent = LlmAgent(
    name="cafe_concierge",
    model="gemini-2.5-flash",
    instruction="""You are a friendly and knowledgeable barista at "The Cloud Cafe".

Your job:
- Help customers browse the menu and answer questions about items.
- Take coffee and food orders.
- Remember and respect dietary preferences.

Be conversational, warm, and concise. If a customer mentions a dietary
restriction, acknowledge it and suggest suitable options from the menu.
""",
    tools=[get_menu],
)

Definisce un agente di base con uno strumento: get_menu(). L'agente può rispondere a domande sul menu, ma non può ancora monitorare gli ordini o ricordare le preferenze.

Verifica che l'agente venga eseguito

Avvia l'interfaccia utente di sviluppo dell'ADK dalla directory di lavoro:

cd ~/build-agent-adk-cloudsql
uv run adk web

Apri l'URL mostrato nel terminale (in genere http://localhost:8000) utilizzando la funzionalità Anteprima web di Cloud Shell. Seleziona cafe_concierge dal menu a discesa dell'agente nell'angolo in alto a sinistra.

Digita il seguente testo nella barra della chat e verifica che l'agente risponda con le voci di menu e i prezzi.

What's on the menu?

376ee6b189657e7a.png

Interrompi l'interfaccia utente di sviluppo con Ctrl+C prima di procedere.

5. Aggiungere la gestione degli ordini stateful

L'agente può mostrare il menu, ma non può prendere ordini o ricordare le preferenze. Questo passaggio aggiunge quattro strumenti che utilizzano il sistema di stati di ADK per monitorare gli ordini all'interno di una conversazione e memorizzare le preferenze alimentari tra le conversazioni.

Comprendere gli eventi e lo stato della sessione

Ogni conversazione ADK si trova all'interno di un oggetto Session. Una sessione monitora due elementi distinti: eventi e stato. Comprendere la differenza è fondamentale per creare agenti che ricordino le cose giuste nel modo giusto.

Gli eventi sono il log cronologico di tutto ciò che accade in una conversazione. Ogni messaggio dell'utente, ogni risposta dell'agente, ogni chiamata di strumenti e il relativo valore restituito vengono registrati come Event e aggiunti all'elenco eventi della sessione. Gli eventi sono immutabili: una volta registrati, non cambiano mai. Pensa agli eventi come alla trascrizione completa di una conversazione.

Lo stato è un blocco note chiave-valore che l'agente legge e scrive durante una conversazione. A differenza degli eventi, lo stato è modificabile: i valori cambiano man mano che la conversazione si evolve. Lo stato è il luogo in cui l'agente memorizza i dati strutturati su cui deve agire: l'ordine corrente, le preferenze del cliente, un totale parziale. Pensa allo stato come a dei post-it che l'agente tiene accanto alla trascrizione.

Ecco come si relazionano:

cd9871699451867d.png

Gli strumenti leggono e scrivono lo stato tramite ToolContext, un oggetto che ADK inserisce automaticamente in qualsiasi funzione dello strumento che lo dichiara come parametro. Non lo crei tu. Tramite tool_context.state, uno strumento può leggere e scrivere nel blocco note dello stato della sessione. ADK esamina la firma della funzione: i parametri di tipo ToolContext vengono inseriti, tutti gli altri parametri vengono compilati dall'LLM in base alla conversazione.

Quando uno strumento scrive in tool_context.state, ADK registra la modifica come state_delta all'interno dell'evento. SessionService applica quindi la differenza allo stato attuale della sessione. Ciò significa che le modifiche di stato sono sempre riconducibili all'evento che le ha causate. Lo stesso vale per altre forme di contesto, come callback_context

Informazioni sui prefissi di stato

Le chiavi di stato utilizzano i prefissi per controllare il loro ambito:

Prefisso

Ambito

Survives restart? (con DB)

(nessuno)

Solo per la sessione corrente

user:

Tutte le sessioni per questo utente

app:

Tutte le sessioni, tutti gli utenti

temp:

Solo l'invocazione corrente

No

In questo codelab utilizzi due di questi prefissi: chiavi senza prefisso per i dati con ambito sessione (l'ordine corrente, pertinente solo a questa conversazione) e chiavi user: per i dati con ambito utente (preferenze alimentari, pertinenti a tutte le conversazioni per questo utente).

Aggiungere gli strumenti stateful

Apri cafe_concierge/agent.py nell'editor di Cloud Shell.

cloudshell edit cafe_concierge/agent.py

Poi, aggiungi le quattro funzioni seguenti sopra la definizione di root_agent:

# cafe_concierge/agent.py (add below get_menu, above root_agent)

def place_order(tool_context: ToolContext, items: list[str]) -> dict:
    """Places an order for the specified menu items.

    Use this tool when the customer confirms they want to order something.

    Args:
        tool_context: Provided automatically by ADK.
        items: A list of menu item names the customer wants to order.
    """
    valid_items = []
    invalid_items = []
    total = 0.0

    for item in items:
        item_lower = item.lower()
        if item_lower in CAFE_MENU:
            valid_items.append(item_lower)
            total += CAFE_MENU[item_lower]["price"]
        else:
            invalid_items.append(item)

    if not valid_items:
        return {"error": f"None of these items are on our menu: {invalid_items}"}

    order = {"items": valid_items, "total": round(total, 2)}
    tool_context.state["current_order"] = order

    result = {"order": order}
    if invalid_items:
        result["warning"] = f"These items are not on our menu: {invalid_items}"
    return result


def get_order_summary(tool_context: ToolContext) -> dict:
    """Returns the current order summary for this session.

    Use this tool when the customer asks about their current order,
    wants to review what they ordered, or asks for the total.

    Args:
        tool_context: Provided automatically by ADK.
    """
    order = tool_context.state.get("current_order")
    if order:
        return {"order": order}
    return {"message": "No order has been placed yet in this session."}


def set_dietary_preference(tool_context: ToolContext, preference: str) -> dict:
    """Saves a dietary preference that persists across all conversations.

    Use this tool when the customer mentions a dietary restriction or
    preference (e.g., "I'm vegan", "I'm lactose intolerant",
    "I have a nut allergy").

    Args:
        tool_context: Provided automatically by ADK.
        preference: The dietary preference to save (e.g., "vegan",
            "lactose intolerant", "nut allergy").
    """
    existing = tool_context.state.get("user:dietary_preferences", [])
    if not isinstance(existing, list):
        existing = []

    preference_lower = preference.lower().strip()
    if preference_lower not in existing:
        existing.append(preference_lower)

    tool_context.state["user:dietary_preferences"] = existing
    return {
        "saved": preference_lower,
        "all_preferences": existing,
    }


def get_dietary_preferences(tool_context: ToolContext) -> dict:
    """Retrieves the customer's saved dietary preferences.

    Use this tool when you need to check the customer's dietary
    restrictions before making recommendations.

    Args:
        tool_context: Provided automatically by ADK.
    """
    preferences = tool_context.state.get("user:dietary_preferences", [])
    if preferences:
        return {"preferences": preferences}
    return {"message": "No dietary preferences saved yet."}

Due aspetti da notare:

  1. place_order e get_order_summary utilizzano chiavi senza prefisso (current_order). Questo stato è legato alla sessione corrente: una nuova conversazione inizia con un ordine vuoto.
  2. set_dietary_preference e get_dietary_preferences utilizzano il prefisso user: (user:dietary_preferences). Questo stato viene condiviso in tutte le sessioni dello stesso utente.

Aggiornare l'agente con nuovi strumenti e istruzioni

Sostituisci la definizione esistente di root_agent nella parte inferiore del file con:

# cafe_concierge/agent.py (replace the existing root_agent)

root_agent = LlmAgent(
    name="cafe_concierge",
    model="gemini-2.5-flash",
    instruction="""You are a friendly and knowledgeable barista at "The Cloud Cafe".

Your job:
- Help customers browse the menu and answer questions about items.
- Take coffee and food orders.
- Remember and respect dietary preferences.

The customer's saved dietary preferences are: {user:dietary_preferences?}

IMPORTANT RULES:
- When a customer mentions a dietary restriction, ALWAYS save it using the
  set_dietary_preference tool before doing anything else.
- Before recommending items, check the customer's dietary preferences. If they
  have preferences saved, only recommend items compatible with those
  restrictions. Check the menu item tags to determine compatibility.
- When placing an order, confirm the items and total with the customer.

Be conversational, warm, and concise.
""",
    tools=[
        get_menu,
        place_order,
        get_order_summary,
        set_dietary_preference,
        get_dietary_preferences,
    ],
)

L'istruzione utilizza il modello di inserimento dello stato {user:dietary_preferences?} per inserire le preferenze salvate di questo cliente direttamente nel prompt.

Verificare il file completo

Il tuo cafe_concierge/agent.py ora dovrebbe contenere:

  • Il dizionario CAFE_MENU
  • Cinque funzioni dello strumento: get_menu, place_order, get_order_summary, set_dietary_preference, get_dietary_preferences
  • La definizione di root_agent con tutti e cinque gli strumenti

6. Testare l'agente con l'interfaccia utente di sviluppo dell'ADK

Questo passaggio esegue l'agente ed esercita tutte le funzionalità con stato: ordinamento, monitoraggio delle preferenze e memoria tra sessioni (all'interno dello stesso processo). Esaminerai anche i riquadri Eventi e Stato per vedere come ADK tiene traccia della conversazione internamente.

Avviare l'interfaccia utente per sviluppatori

cd ~/build-agent-adk-cloudsql
uv run adk web

Apri l'anteprima web sulla porta 8000 e seleziona cafe_concierge dal menu a discesa.

Conversazione 1: effettuare un ordine e impostare le preferenze

Prova questi prompt in sequenza:

What's on the menu?
I'm lactose intolerant
What would you recommend?
I'll have an oat milk latte and a banana bread
What's my order?

Controllare gli eventi della sessione

Tutto l'evento verrà acquisito e visualizzato nell'interfaccia utente web. Nella casella di chat vedrai non solo il prompt e la risposta, ma anche tool_call e tool_response.

9051b46978c8017b.png

Dovresti vedere un elenco di eventi in ordine. Ogni evento ha un autore (chi lo ha prodotto) e un tipo (il tipo di interazione che rappresenta):

Autore

Tipo

Cosa rappresenta

user

message

Un messaggio che hai digitato nella chat

cafe_concierge

message

La risposta di testo dell'agente

cafe_concierge

tool_call

L'agente ha deciso di chiamare uno strumento (mostra il nome della funzione e gli argomenti)

cafe_concierge

tool_response

Il valore restituito da una chiamata allo strumento

Fai clic su uno degli eventi tool_call, ad esempio la chiamata set_dietary_preference. Dovresti vedere:

  • Nome funzione: set_dietary_preference
  • Argomenti: {"preference": "lactose intolerant"}

Ora fai clic sull'evento tool_response corrispondente direttamente sotto. Dovresti vedere il valore restituito:

  • Risposta: {"saved": "lactose intolerant", "all_preferences": ["lactose intolerant"]}

b528f4efd6a9f337.png

Cerca il campo state_delta all'interno dell'evento tool_response. Mostra esattamente lo stato modificato a seguito di questa chiamata allo strumento:

state_delta: {"user:dietary_preferences": ["lactose intolerant"]}

Ogni modifica dello stato è riconducibile a un evento specifico. In questo modo, ADK garantisce che lo spazio di lavoro dello stato rimanga sincronizzato con la cronologia delle conversazioni.

Controllare lo stato della sessione

Fai clic sulla scheda Stato. A differenza del log eventi (che mostra la cronologia completa), la scheda Stato mostra una istantanea di ciò che l'agente sa al momento, ovvero il valore attuale di ogni chiave di stato.

5e06fb54f3f0d8d6.png

Dovresti vedere due voci:

  • current_order - {"items": ["oat milk latte", "banana bread"], "total": 9.0}
  • user:dietary_preferences - ["lactose intolerant"]

Nota la differenza nei nomi delle chiavi:

  • current_order non ha prefisso, quindi è basata sulle sessioni. Esiste solo in questa conversazione e scompare al termine della sessione.
  • user:dietary_preferences ha il prefisso user:, quindi è basata sugli utenti. È condiviso in ogni sessione per questo utente.

Questa distinzione è invisibile nel codice (entrambi utilizzano tool_context.state), ma controlla la portata dei dati. Vedrai questo risultato nel test successivo.

Conversazione 2: verifica lo stato dell'utente tra le sessioni

Fai clic sul pulsante Nuova sessione nell'interfaccia utente per sviluppatori per avviare una nuova conversazione. In questo modo viene creata una nuova sessione per lo stesso utente.

57408cfae5f041ac.png

Prova questo prompt:

What do you recommend for me?

Controlla la scheda Stato nella nuova sessione. La chiave user:dietary_preferences viene trasferita, ma current_order non è più presente, in quanto questo stato era legato alla sessione precedente.

764eb3885251307d.png

7. Osservare la limitazione dello spazio di archiviazione locale

L'agente ricorda le preferenze tra le sessioni, ma solo finché esiste l'archiviazione locale. Questo passaggio dimostra la limitazione fondamentale dell'archiviazione locale.

Avviare di nuovo l'agente

Hai interrotto l'interfaccia utente per sviluppatori alla fine del passaggio precedente. Ora rimuoviamo l'archiviazione locale e riavviamola per simulare l'ambiente serverless senza stato:

cd ~/build-agent-adk-cloudsql
rm -f cafe_concierge/.adk/session.db
uv run adk web

Ora apri l'anteprima web sulla porta 8000 e seleziona cafe_concierge.

Testare il richiamo delle preferenze

Tipo:

Do you remember my dietary preferences?

L'agente non ricorda. Le preferenze alimentari, la cronologia degli ordini, tutto è sparito.

82a5e05434cafe83.png

Quando abbiamo eliminato lo spazio di archiviazione locale, è stato cancellato tutto, in genere quando utilizziamo un ambiente serverless. session.db memorizza tutto lo stato nella memoria del processo. Se lo rimuovi, tutti i dati vengono cancellati.

La soluzione: specifica DatabaseSessionService, che in questo tutorial archivierà tutti i dati di sessione in un database PostgreSQL in Cloud SQL. Il codice e gli strumenti dell'agente rimangono esattamente gli stessi, cambia solo il backend di archiviazione.

Interrompi la UI di sviluppo con Ctrl+C prima di procedere.

8. Rivedi la configurazione del database

A questo punto, la creazione dell'istanza di database dovrebbe essere già terminata. Verifichiamolo eseguendo questo comando

gcloud sql instances describe cafe-concierge-db --format="value(state)"

Dovresti vedere l'output seguente, contrassegnalo come completato

RUNNABLE

Crea il database

Crea un database dedicato per i dati di sessione dell'agente:

gcloud sql databases create agent_db --instance=cafe-concierge-db

Avvia il proxy di autenticazione Cloud SQL

Il proxy di autenticazione Cloud SQL fornisce una connessione sicura e autenticata da Cloud Shell all'istanza Cloud SQL senza dover inserire indirizzi IP nella lista consentita. È già preinstallato in Cloud Shell.

cloud-sql-proxy ${GOOGLE_CLOUD_PROJECT}:${REGION}:cafe-concierge-db --port 5432 &

Il suffisso & nel comando fa sì che il proxy venga eseguito in background. Dovresti vedere un output che conferma che il proxy è pronto, come mostrato di seguito.

[your-project-id:your-region:cafe-concierge-db] Listening on 127.0.0.1:5432
The proxy has started successfully and is ready for new connections!

Verificare la connessione

Verifica di poter connetterti al database tramite il proxy:

psql "host=127.0.0.1 port=5432 dbname=agent_db user=postgres password=$DB_PASSWORD" -c "SELECT 'Connection ok' AS status;"

Dovresti vedere:

      status
---------------------
 Connection ok
(1 row)

9. Verificare la memoria persistente tra le sessioni

Questo passaggio dimostra che la memoria dell'agente sopravvive al ripristino quando ci assicuriamo che cafe_concierge/.adk/session_db (il database locale) venga rimosso e si estenda alle sessioni di conversazione.

Avvia l'agente

Assicurati che il proxy di autenticazione Cloud SQL sia ancora in esecuzione (controlla i job). Se non lo è, riavvialo:

if ss -tlnp | grep -q ':5432 '; then
  echo "Cloud SQL Auth Proxy is already running."
else
  cloud-sql-proxy ${GOOGLE_CLOUD_PROJECT}:${REGION}:cafe-concierge-db --port 5432 &
fi

Poi, avviamo l'interfaccia utente di sviluppo dell'ADK specificando il database come servizio di sessione

uv run adk web --session_service_uri postgresql+asyncpg://postgres:${DB_PASSWORD}@127.0.0.1:5432/agent_db

Apri l'anteprima web sulla porta 8000 e seleziona cafe_concierge.

Test 1: effettua un ordine e imposta le preferenze

Nella prima sessione, esegui questi prompt:

Show me the menu
I'm vegan
What can I eat?
I'll have a cold brew and banana bread

Test 2: sopravvivere a un riavvio

Arresta la UI di sviluppo con Ctrl+C e assicurati che session.db locale venga rimosso

rm -f cafe_concierge/.adk/session.db

Quindi, esegui di nuovo il server della UI di sviluppo.

uv run adk web --session_service_uri postgresql+asyncpg://postgres:${DB_PASSWORD}@127.0.0.1:5432/agent_db

Apri l'anteprima web sulla porta 8000, seleziona cafe_concierge e avvia una nuova sessione. Poi chiedi

What are my dietary preferences?

L'agente risponde con le tue preferenze salvate: vegana. I dati sono sopravvissuti al riavvio perché ora sono archiviati in PostgreSQL, non nell'archiviazione locale. Lo stesso vale se creiamo una nuova sessione, poiché lo stato user: viene trasferito a ogni nuova sessione per questo utente.

9c139bf89becb748.png

Ispezionare direttamente il database

Apri una nuova scheda del terminale in Cloud Shell ed esegui una query sul database per visualizzare i dati archiviati:

psql "host=127.0.0.1 port=5432 dbname=agent_db user=postgres password=$DB_PASSWORD" -c "\dt"

Dovresti vedere tabelle create automaticamente da ADK per archiviare sessioni, eventi e stati, come in questo esempio

                List of relations
 Schema |         Name          | Type  |  Owner   
--------+-----------------------+-------+----------
 public | adk_internal_metadata | table | postgres
 public | app_states            | table | postgres
 public | events                | table | postgres
 public | sessions              | table | postgres
 public | user_states           | table | postgres
(5 rows)

Riepilogo del comportamento dello stato

Chiave di stato

Prefisso

Ambito

Condivisi tra le sessioni?

current_order

(nessuno)

Sessione

No

user:dietary_preferences

user:

Utente

10. Congratulazioni / Esegui la pulizia

Complimenti! Hai creato correttamente un agente AI stateful e persistente utilizzando ADK e Cloud SQL.

Che cosa hai imparato

  • Come creare un agente ADK con strumenti personalizzati che leggono e scrivono lo stato della sessione
  • La differenza tra lo stato con ambito sessione (nessun prefisso) e lo stato con ambito utente (prefisso user:)
  • Perché l'adk locale session.db predefinito è adatto solo allo sviluppo: tutti i dati vengono persi alla rimozione (e sono facili da rimuovere, nessun backup), non è adatto al deployment serverless, che è stateless
  • Come eseguire il provisioning di un'istanza Cloud SQL PostgreSQL e connettersi a essa con il proxy di autenticazione Cloud SQL
  • Come connettersi a DatabaseSessionService con PostgreSQL su CloudSQL con una modifica minima del codice: stessi strumenti, stesso agente, backend diverso
  • Come lo stato basato sugli utenti viene mantenuto in sessioni di conversazione separate

Pulizia

Per evitare che al tuo account Google Cloud vengano addebitati costi, elimina le risorse create in questo codelab.

Il modo più semplice per eseguire la pulizia è eliminare il progetto. Vengono rimosse tutte le risorse associate al progetto.

gcloud projects delete ${GOOGLE_CLOUD_PROJECT}

Opzione 2: elimina singole risorse

Se vuoi conservare il progetto, ma rimuovere solo le risorse create in questo codelab:

gcloud sql instances delete cafe-concierge-db --quiet