Crea agentes de IA persistentes con el ADK y Cloud SQL

1. Introducción

En esta sesión práctica, irás más allá de los chatbots básicos sin estado para crear un asistente inteligente para cafeterías, un agente de IA potenciado por Gemini que actúa como un barista amigable. Toma pedidos de café que se registran en el estado de la sesión, recuerda las preferencias dietéticas a largo plazo en el estado con alcance del usuario y persiste todo en una base de datos de Cloud SQL PostgreSQL. Al final, tu agente recordará que eres intolerante a la lactosa incluso después de reiniciar la aplicación y comenzar una conversación completamente nueva.

Esta es la arquitectura del sistema que compilaremos

a98bbd65ddedd29c.jpeg

Requisitos previos

  • Una cuenta de Google Cloud con una cuenta de facturación de prueba
  • Conocimientos básicos sobre Python
  • No se requiere experiencia previa con el ADK, los agentes de IA ni Cloud SQL.

Qué aprenderás

  • Crea un agente de IA con el Kit de desarrollo de agentes (ADK) de Google y herramientas personalizadas
  • Define herramientas que leen y escriben el estado de la sesión a través de ToolContext
  • Distingue entre el estado centrado en la sesión y el estado centrado en el usuario (prefijo user:).
  • Aprovisiona una instancia de Cloud SQL PostgreSQL y conéctate a ella desde Cloud Shell
  • Migra del almacenamiento local (que es el valor predeterminado cuando usas el comando adk web) a DatabaseSessionService para el almacenamiento persistente con una base de datos dedicada
  • Verifica que la memoria del agente persista en los reinicios de la aplicación y en las sesiones de conversación separadas.

Requisitos

  • Una computadora que funcione y una conexión a Internet confiable
  • Un navegador, como Chrome, para acceder a la consola de Google Cloud
  • Una mente curiosa y ganas de aprender

2. Configura tu entorno

En este paso, se prepara tu entorno de Cloud Shell y se configura tu proyecto de Google Cloud.

Abra Cloud Shell

Abre Cloud Shell en tu navegador. Cloud Shell proporciona un entorno preconfigurado con todas las herramientas que necesitas para este codelab. Haz clic en Autorizar cuando se te solicite

Tu interfaz debería verse similar a la siguiente

86307fac5da2f077.png

Esta será nuestra interfaz principal, con el IDE en la parte superior y la terminal en la parte inferior.

Configura tu directorio de trabajo

Crea tu directorio de trabajo. Todo el código que escribas en este codelab se encontrará aquí, separado del repositorio de referencia:

# 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

Para generar tu terminal, busca Ver -> Terminal.

ccc3214812750f1c.png

Configura tu proyecto de Google Cloud y las variables de entorno iniciales

Descarga la secuencia de comandos de configuración del proyecto en tu directorio de trabajo:

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

Ejecuta la secuencia de comandos. Verifica tu cuenta de facturación de prueba, crea un proyecto nuevo (o valida uno existente), guarda el ID del proyecto en un archivo .env en el directorio actual y establece el proyecto activo en la terminal.

bash setup_verify_trial_project.sh && source .env

Cuando ejecutes este comando, se te solicitará un nombre de ID del proyecto sugerido. Puedes presionar Enter para continuar.

54b615cd15f2a535.png

Después de esperar un tiempo, si ves este resultado en la consola, puedes continuar con el siguiente paso e576b4c13d595156.png.

La secuencia de comandos ejecutada realiza los siguientes pasos:

  1. Verifica que tengas una cuenta de facturación de prueba activa
  2. Verifica si existe un proyecto en .env (si corresponde)
  3. Crea un proyecto nuevo o reutiliza el existente
  4. Vincula la cuenta de facturación de prueba a tu proyecto
  5. Guarda el ID del proyecto en .env
  6. Configura el proyecto como el proyecto activo de gcloud

Para verificar que el proyecto esté configurado correctamente, revisa el texto amarillo junto a tu directorio de trabajo en el mensaje de la terminal de Cloud Shell. Debería mostrar el ID de tu proyecto.

9e11ee21cd23405f.png

Habilita las APIs obligatorias

Habilita las APIs de Google Cloud necesarias para este codelab:

gcloud services enable \
  aiplatform.googleapis.com \
  sqladmin.googleapis.com \
  compute.googleapis.com
  • API de Vertex AI (aiplatform.googleapis.com): Tu agente usa los modelos de Gemini a través de Vertex AI.
  • API de Cloud SQL Admin (sqladmin.googleapis.com): Aprovisionas y administras una instancia de PostgreSQL para el almacenamiento persistente.
  • API de Compute Engine (compute.googleapis.com): Se requiere para crear instancias de Cloud SQL.

Configura la región de los productos de Gemini y Cloud

Antes de continuar, también configuraremos la ubicación o región necesarias para el producto con el que interactuamos. Agrega la siguiente configuración a nuestro archivo .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

Continuemos con el siguiente paso

3. Configura Cloud SQL

En este paso, se aprovisiona una instancia de Cloud SQL PostgreSQL y se cambia el agente del almacenamiento en memoria al almacenamiento respaldado por la base de datos. La creación de la instancia tarda unos minutos, por lo que la iniciarás primero y podremos continuar con nuestra conversación sobre el siguiente tema mientras esperamos a que finalice.

Inicia la creación de la instancia

Agrega la contraseña de la base de datos a tu archivo .env y vuelve a cargarlo. Usaremos cafe-agent-pwd-2025 como contraseña.

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

Ejecuta este comando para crear una instancia de Cloud SQL PostgreSQL. La operación tarda unos minutos en completarse, por lo que puedes dejarla en ejecución y continuar con la siguiente sección.

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 &

Algunas notas sobre el comando anterior:

  • db-f1-micro es el nivel de Cloud SQL más pequeño (y económico), suficiente para este codelab.
  • --root-password establece la contraseña para el usuario de postgres predeterminado.
  • El sufijo & en el comando ejecuta el comando en segundo plano para que puedas seguir trabajando.

El proceso se ejecutará en segundo plano, pero el resultado de la consola se mostrará ocasionalmente en la terminal actual. Abramos una pestaña de terminal nueva en Cloud Shell (haz clic en el ícono +) para que podamos concentrarnos más.

b01e3fbd89f17332.png

Vuelve a tu directorio de trabajo y activa el proyecto con la secuencia de comandos de configuración anterior.

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

Luego, continuemos con la siguiente sección.

4. Compila el agente de conserjería de la cafetería

En este paso, se crea la estructura del proyecto para tu agente de ADK y se define un asistente de cafetería básico con una herramienta de menú.

Inicializa el proyecto de Python

En este codelab, se usa uv, un administrador de paquetes de Python rápido que controla los entornos virtuales y las dependencias en una sola herramienta. Ya está preinstalada en Cloud Shell.

Inicializa un proyecto de Python y agrega el ADK como dependencia:

uv init
uv add google-adk==1.25.0 asyncpg

uv init crea un pyproject.toml y un entorno virtual. uv add instala la dependencia y la registra en pyproject.toml.

Inicializa la estructura del proyecto del agente

El ADK espera un diseño de carpeta específico: un directorio con el nombre de tu agente que contenga __init__.py, agent.py y también .env dentro del directorio del agente.

El ADK tiene un comando integrado para ayudarte a establecer esto rápidamente. Ejecuta el siguiente comando:

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

Este comando creará una estructura de agente con gemini-2.5-flash como cerebro. Ahora, tu directorio debería verse así:

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

Escribe el agente

Abre cafe_concierge/agent.py en el editor de Cloud Shell.

cloudshell edit cafe_concierge/agent.py

y reemplaza el archivo con el siguiente código

# 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],
)

Esto define un agente básico con una herramienta: get_menu(). El agente puede responder preguntas sobre el menú, pero aún no puede hacer un seguimiento de los pedidos ni recordar las preferencias.

Verifica que el agente se ejecute

Inicia la IU de desarrollo del ADK desde tu directorio de trabajo:

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

Abre la URL que se muestra en la terminal (por lo general, http://localhost:8000) con la función de Vista previa en la Web de Cloud Shell. Selecciona cafe_concierge en el menú desplegable del agente, ubicado en la esquina superior izquierda.

Escribe el siguiente texto en la barra de chat y verifica que el agente responda con elementos de menú y precios.

What's on the menu?

376ee6b189657e7a.png

Detén la IU para desarrolladores con Ctrl + C antes de continuar.

5. Agrega la administración de pedidos con estado

El agente puede mostrar el menú, pero no puede tomar pedidos ni recordar preferencias. En este paso, se agregan cuatro herramientas que usan el sistema de estado de ADK para hacer un seguimiento de los pedidos dentro de una conversación y almacenar las preferencias dietéticas en todas las conversaciones.

Información sobre los eventos y el estado de la sesión

Cada conversación del ADK se encuentra dentro de un objeto Session. Una sesión hace un seguimiento de dos elementos distintos: eventos y estado. Comprender la diferencia es clave para crear agentes que recuerden las cosas correctas de la manera adecuada.

Los eventos son el registro cronológico de todo lo que sucede en una conversación. Cada mensaje del usuario, cada respuesta del agente, cada llamada a una herramienta y su valor de devolución se registran como un Event y se agregan a la lista de eventos de la sesión. Los eventos son inmutables: una vez que se registran, nunca cambian. Piensa en los eventos como la transcripción completa de una conversación.

El estado es un bloc de notas de clave-valor que el agente lee y escribe durante una conversación. A diferencia de los eventos, el estado es mutable: los valores cambian a medida que evoluciona la conversación. El estado es donde el agente almacena los datos estructurados que necesita para actuar: el pedido actual, las preferencias del cliente y un total acumulado. Piensa en el estado como notas adhesivas que el agente guarda junto a la transcripción.

A continuación, se explica cómo se relacionan:

cd9871699451867d.png

Las herramientas leen y escriben el estado a través de ToolContext, un objeto que el ADK inyecta automáticamente en cualquier función de herramienta que lo declare como parámetro. No lo creas tú mismo. A través de tool_context.state, una herramienta puede leer y escribir en el bloc de notas de la sesión. El ADK inspecciona la firma de la función: se inyectan los parámetros con el tipo ToolContext y el LLM completa todos los demás parámetros según la conversación.

Cuando una herramienta escribe en tool_context.state, ADK registra ese cambio como un state_delta dentro del evento. Luego, SessionService aplica el delta al estado actual de la sesión. Esto significa que los cambios de estado siempre se pueden rastrear hasta el evento que los causó. Esto también se aplica a otras formas de contexto, como callback_context.

Información sobre los prefijos de estado

Las claves de estado usan prefijos para controlar su alcance:

Prefijo

Alcance

¿Sobrevive al reinicio? (con DB)

(ninguno)

Solo la sesión actual

user:

Todas las sesiones de este usuario

app:

Todas las sesiones y todos los usuarios

temp:

Solo la invocación actual

No

En este codelab, usarás dos de estos prefijos: claves sin prefijo para los datos con alcance de sesión (el pedido actual, relevante solo para esta conversación) y claves user: para los datos con alcance del usuario (preferencias dietéticas, relevantes para todas las conversaciones de este usuario).

Agrega las herramientas con estado

Abre cafe_concierge/agent.py en el editor de Cloud Shell.

cloudshell edit cafe_concierge/agent.py

Luego, agrega las siguientes cuatro funciones encima de la definición de 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."}

Debes tener en cuenta lo siguiente:

  1. place_order y get_order_summary usan claves sin prefijo (current_order). Este estado está vinculado a la sesión actual: una conversación nueva comienza con un pedido vacío.
  2. set_dietary_preference y get_dietary_preferences usan el prefijo user: (user:dietary_preferences). Este estado se comparte en todas las sesiones del mismo usuario.

Actualiza el agente con nuevas herramientas e instrucciones

Reemplaza la definición de root_agent existente en la parte inferior del archivo por lo siguiente:

# 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,
    ],
)

La instrucción usa la plantilla de inyección de estado {user:dietary_preferences?} para insertar las preferencias guardadas de este cliente directamente en la instrucción.

Verifica el archivo completo

Tu cafe_concierge/agent.py ahora debería contener lo siguiente:

  • El diccionario de CAFE_MENU
  • Cinco funciones de herramientas: get_menu, place_order, get_order_summary, set_dietary_preference y get_dietary_preferences
  • La definición de root_agent con las cinco herramientas

6. Prueba el agente con la IU de desarrollo del ADK

En este paso, se ejecuta el agente y se ejercitan todas las funciones con estado: ordenamiento, seguimiento de preferencias y memoria entre sesiones (dentro del mismo proceso). También inspeccionarás los paneles Events y State para ver cómo el ADK hace un seguimiento interno de la conversación.

Cómo iniciar la IU de desarrollo

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

Abre la vista previa en la Web en el puerto 8000 y selecciona cafe_concierge en el menú desplegable.

Conversación 1: Haz un pedido y establece preferencias

Prueba estas instrucciones en secuencia:

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?

Cómo inspeccionar eventos de sesión

Todos los Eventos se capturarán y mostrarán en la IU web. Verás que, en el cuadro de chat, no solo se muestran tu instrucción y tu respuesta, sino también tool_call y tool_response.

9051b46978c8017b.png

Deberías ver una lista de eventos en orden. Cada evento tiene un autor (quien lo produjo) y un tipo (qué tipo de interacción representa):

Autor

Tipo

Qué representa

user

message

Un mensaje que escribiste en el chat

cafe_concierge

message

La respuesta de texto del agente

cafe_concierge

tool_call

El agente decidió llamar a una herramienta (muestra el nombre de la función y los argumentos).

cafe_concierge

tool_response

El valor de retorno de una llamada a una herramienta

Haz clic en uno de los eventos tool_call, por ejemplo, la llamada set_dietary_preference. Deberías ver lo siguiente:

  • Nombre de la función: set_dietary_preference
  • Argumentos: {"preference": "lactose intolerant"}

Ahora haz clic en el evento tool_response correspondiente que se encuentra directamente debajo. Deberías ver el valor de devolución:

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

b528f4efd6a9f337.png

Busca el campo state_delta dentro del evento tool_response. Esto muestra exactamente qué estado cambió como resultado de esta llamada a la herramienta:

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

Cada cambio de estado se puede rastrear hasta un evento específico. Así es como el ADK garantiza que el bloc de notas de estado se mantenga sincronizado con el historial de conversación.

Cómo inspeccionar el estado de la sesión

Haz clic en la pestaña Estado. A diferencia del registro de eventos (que muestra el historial completo), la pestaña de estado muestra una instantánea de lo que sabe el agente en este momento, es decir, el valor actual de cada clave de estado.

5e06fb54f3f0d8d6.png

Deberías ver dos entradas:

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

Observa la diferencia en los nombres de las claves:

  • current_order no tiene prefijo, ya que se limita a la sesión. Solo existe en esta conversación y desaparece cuando finaliza la sesión.
  • user:dietary_preferences tiene el prefijo user:, por lo que se limita al usuario. Se comparte en todas las sesiones de este usuario.

Esta distinción es invisible en el código (ambos usan tool_context.state), pero controla hasta dónde llegan los datos. Verás cómo se desarrolla esto en la siguiente prueba.

Conversación 2: Verifica el estado del usuario en varias sesiones

Haz clic en el botón New Session en la IU de desarrollo para iniciar una conversación nueva. Esto crea una sesión nueva para el mismo usuario.

57408cfae5f041ac.png

Prueba esta instrucción:

What do you recommend for me?

Verifica la pestaña Estado en la sesión nueva. La tecla user:dietary_preferences se mantiene, pero current_order desaparece, ya que ese estado estaba vinculado a la sesión anterior.

764eb3885251307d.png

7. Observa la limitación del almacenamiento local

El agente recuerda las preferencias entre sesiones, pero solo mientras exista el almacenamiento local. En este paso, se demuestra la limitación fundamental del almacenamiento local.

Vuelve a iniciar el agente

Detuviste la IU de desarrollo al final del paso anterior. Ahora, quitemos el almacenamiento local y volvamos a iniciarlo para simular el entorno sin servidores que no tiene estado:

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

Ahora, abre la vista previa en la Web en el puerto 8000 y selecciona cafe_concierge.

Prueba de recuperación de preferencias

Tipo:

Do you remember my dietary preferences?

El agente no recuerda nada. Las preferencias alimentarias, el historial de pedidos… todo desapareció.

82a5e05434cafe83.png

Todo se borró cuando eliminamos el almacenamiento local, lo que suele ocurrir cuando utilizamos un entorno sin servidor. session.db almacena todo el estado en la memoria del proceso. Si lo quitas, se borrará todo.

La solución es especificar DatabaseSessionService, que, en este instructivo, almacenará todos los datos de la sesión en una base de datos de PostgreSQL en Cloud SQL. El código y las herramientas del agente siguen siendo exactamente los mismos, solo cambia el backend de almacenamiento.

Detén la IU para desarrolladores con Ctrl+C antes de continuar.

8. Vuelve a analizar la configuración de la base de datos

En este punto, ya debería haber finalizado la creación de la instancia de base de datos. Para verificarlo, ejecuta el siguiente comando:

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

Deberías ver el siguiente resultado. Márcalo como finalizado.

RUNNABLE

Crea la base de datos

Crea una base de datos dedicada para los datos de la sesión del agente:

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

Inicia el proxy de Cloud SQL Auth

El proxy de autenticación de Cloud SQL proporciona una conexión segura y autenticada desde Cloud Shell a tu instancia de Cloud SQL sin necesidad de incluir direcciones IP en la lista de entidades permitidas. Ya está preinstalado en Cloud Shell.

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

El sufijo & en el comando hace que el proxy se ejecute en segundo plano. Deberías ver un resultado que confirme que el proxy está listo, como se muestra a continuación.

[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!

Verifica la conexión

Prueba que puedes conectarte a la base de datos a través del proxy:

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

Deberías ver lo siguiente:

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

9. Cómo verificar la memoria persistente en las sesiones

Este paso demuestra que la memoria del agente sobrevive al restablecimiento cuando nos aseguramos de que se quite cafe_concierge/.adk/session_db (la base de datos local) y abarque sesiones de conversación.

Inicia el agente

Asegúrate de que el proxy de Cloud SQL Auth siga en ejecución (verifica los trabajos). Si no es así, reinícialo:

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

Luego, inicia la IU de desarrollo del ADK especificando la base de datos como el servicio de sesión.

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

Abre la vista previa web en el puerto 8000 y selecciona cafe_concierge.

Prueba 1: Haz un pedido y establece preferencias

En la primera sesión, ejecuta estos mensajes:

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

Prueba 2: Sobrevive a un reinicio

Detén la IU para desarrolladores con Ctrl+C y asegúrate de que se quite el session.db local.

rm -f cafe_concierge/.adk/session.db

Luego, vuelve a ejecutar el servidor de la IU para desarrolladores.

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

Abre la vista previa web en el puerto 8000, selecciona cafe_concierge y comienza una sesión nueva. Luego, pregunta

What are my dietary preferences?

El agente responde con tus preferencias guardadas: vegana. Los datos sobrevivieron al reinicio porque ahora se almacenan en PostgreSQL, no en el almacenamiento local. Este será el mismo caso si creamos una sesión nueva, ya que el estado de user: se transfiere a cada sesión nueva de este usuario.

9c139bf89becb748.png

Inspecciona la base de datos directamente

Abre una nueva pestaña de la terminal en Cloud Shell y consulta la base de datos para ver los datos almacenados:

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

Deberías ver tablas que el ADK creó automáticamente para almacenar sesiones, eventos y estados, como en este ejemplo.

                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)

Resumen del comportamiento del estado

Clave de estado

Prefijo

Alcance

¿Se comparte entre sesiones?

current_order

(ninguno)

Sesión

No

user:dietary_preferences

user:

Usuario

10. Felicitaciones y limpieza

¡Felicitaciones! Creaste correctamente un agente de IA persistente y con estado usando el ADK y Cloud SQL.

Qué aprendiste

  • Cómo crear un agente de ADK con herramientas personalizadas que leen y escriben el estado de la sesión
  • La diferencia entre el estado con alcance de sesión (sin prefijo) y el estado con alcance del usuario (prefijo user:)
  • Por qué el session.db local predeterminado del ADK solo es adecuado para el desarrollo: todos los datos se pierden cuando se quita (y es fácil de quitar, no hay copia de seguridad), no es adecuado para la implementación sin servidores que no tiene estado
  • Cómo aprovisionar una instancia de Cloud SQL para PostgreSQL y conectarse a ella con el proxy de autenticación de Cloud SQL
  • Cómo conectarse a DatabaseSessionService con PostgreSQL en CloudSQL con un cambio de código mínimo: mismas herramientas, mismo agente, diferente backend
  • Cómo persiste el estado centrado en el usuario en diferentes sesiones de conversación

Realiza una limpieza

Para evitar que se apliquen cargos a tu cuenta de Google Cloud, limpia los recursos que creaste en este codelab.

La manera más fácil de realizar una limpieza es borrar el proyecto. Esto quita todos los recursos asociados con el proyecto.

gcloud projects delete ${GOOGLE_CLOUD_PROJECT}

Opción 2: Borra los recursos individuales

Si deseas conservar el proyecto, pero quitar solo los recursos creados en este codelab, haz lo siguiente:

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