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

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) aDatabaseSessionServiceper 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

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.

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.

Dopo aver atteso un po' di tempo, se visualizzi questo output nella console, puoi passare al passaggio successivo 
Lo script eseguito esegue i seguenti passaggi:
- Verificare di avere un account di fatturazione di prova attivo
- Controlla se esiste un progetto in
.env(se presente) - Crea un nuovo progetto o riutilizza quello esistente
- Collega l'account di fatturazione di prova al tuo progetto
- Salva l'ID progetto in .env
- 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.

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-passwordimposta 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.

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?

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:

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 | Sì |
| Tutte le sessioni per questo utente | Sì |
| Tutte le sessioni, tutti gli utenti | Sì |
| 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:
place_ordereget_order_summaryutilizzano chiavi senza prefisso (current_order). Questo stato è legato alla sessione corrente: una nuova conversazione inizia con un ordine vuoto.set_dietary_preferenceeget_dietary_preferencesutilizzano il prefissouser:(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_agentcon 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.

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 |
|
| Un messaggio che hai digitato nella chat |
|
| La risposta di testo dell'agente |
|
| L'agente ha deciso di chiamare uno strumento (mostra il nome della funzione e gli argomenti) |
|
| 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"]}

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.

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_ordernon ha prefisso, quindi è basata sulle sessioni. Esiste solo in questa conversazione e scompare al termine della sessione.user:dietary_preferencesha il prefissouser:, 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.

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.

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.

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.

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? |
| (nessuno) | Sessione | No |
|
| Utente | Sì |
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.dbpredefinito è 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.
Opzione 1: elimina il progetto (consigliata)
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