1. Introducción
En este codelab, crearás un agente interactivo de barista con IA para una cafetería. Con el Kit de desarrollo de agentes (ADK) de código abierto de Google y el modelo Gemini 3.5 Flash, implementarás la generación mejorada por recuperación (RAG) para fundamentar las recomendaciones del agente en un conjunto de datos de menú simulado. Por último, integrarás el agente en una interfaz de usuario de Streamlit y lo implementarás en Cloud Run.
Actividades
- Crea una fuente de datos de RAG (
menu.json) que contenga elementos de café, etiquetas y alérgenos. - Compila un agente de IA con el ADK
LlmAgenty conecta una herramienta de Python para cargar los datos del menú. - Envuelve el agente en una aplicación de chat de Streamlit que administra el historial de conversaciones.
- Implementa la app de Streamlit en Cloud Run con la implementación basada en la fuente.
- Prueba la fundamentación de RAG y la detección de alérgenos.

Requisitos
- Un navegador web, como Chrome
- Un proyecto de Google Cloud con facturación habilitada.
- Conocimientos básicos sobre Python
Este codelab es para desarrolladores de todos los niveles, incluidos los principiantes.
Costo estimado: Menos de USD 1.
2. Antes de comenzar
Crea un proyecto de Google Cloud
- En la consola de Google Cloud, selecciona o crea un proyecto de Google Cloud.
- Asegúrate de que la facturación esté habilitada para tu proyecto de Cloud.
Inicie Cloud Shell
- Haz clic en Activar Cloud Shell en la parte superior de la consola de Google Cloud.

- Verifica la autenticación:

gcloud auth list
- Confirma que tu proyecto activo esté configurado:
gcloud config get project
Si el ID del proyecto que se muestra no es correcto o no se configuró ninguno, ejecuta el siguiente comando:
gcloud config set project <YOUR_PROJECT_ID>
Habilita las APIs
Ejecuta este comando para habilitar todas las APIs requeridas:
gcloud services enable \
run.googleapis.com \
aiplatform.googleapis.com \
cloudbuild.googleapis.com
3. Configura tu proyecto
En este paso, inicializarás las variables de entorno de tu proyecto y crearás un directorio de trabajo para él.
- En tu sesión activa de Cloud Shell, inicializa las siguientes variables de entorno del proyecto:
export PROJECT_ID=$(gcloud config get-value project)
Nota: Usa la región más cercana
Busca tu región más cercana y reemplaza insert-region-here por ella en el siguiente comando:
export REGION=[insert-region-here]
- Crea un directorio de proyecto nuevo llamado
coffee-barista-agenty cambia a él:
mkdir coffee-barista-agent && cd coffee-barista-agent
4. Crea la fuente de datos del menú simulado
Para fundamentar a AI Barista y evitar que alucine elementos inexistentes, crearás un conjunto de datos de menú local. El agente leerá este archivo en el tiempo de ejecución a través de una herramienta personalizada.
- Crea y abre
menu.jsonen el Editor de Cloud Shell:
cloudshell edit menu.json
- Pega el siguiente contenido JSON en el editor y guarda el archivo:
[
{
"name": "Espresso Solo",
"description": "A single shot of rich, bold espresso.",
"price": 2.50,
"tags": ["strong", "hot", "dairy-free", "sugar-free"],
"allergens": []
},
{
"name": "Oat Milk Honey Latte",
"description": "Creamy steamed oat milk with espresso and a touch of honey.",
"price": 5.00,
"tags": ["sweet", "hot", "dairy-free"],
"allergens": []
},
{
"name": "Cold Brew Coffee",
"description": "Smooth, slow-steeped cold brew served over ice.",
"price": 4.00,
"tags": ["strong", "cold", "dairy-free", "sugar-free"],
"allergens": []
},
{
"name": "Seasonal Pumpkin Latte",
"description": "Spiced pumpkin sauce, espresso, and steamed milk, topped with whipped cream.",
"price": 5.50,
"tags": ["sweet", "hot", "seasonal"],
"allergens": ["dairy"]
},
{
"name": "Classic Croissant",
"description": "Flaky, buttery traditional French pastry.",
"price": 3.50,
"tags": ["bakery", "savory"],
"allergens": ["wheat", "dairy"]
},
{
"name": "Vegan Blueberry Muffin",
"description": "Soft, sweet muffin packed with real blueberries, entirely plant-based.",
"price": 3.75,
"tags": ["bakery", "sweet", "dairy-free", "vegan"],
"allergens": ["wheat"]
},
{
"name": "Nitro Cold Brew",
"description": "Cold brew infused with nitrogen for a super smooth, creamy head.",
"price": 4.50,
"tags": ["strong", "cold", "dairy-free", "sugar-free"],
"allergens": []
},
{
"name": "Iced Caramel Macchiato",
"description": "Chilled milk and vanilla syrup marked with espresso and caramel drizzle.",
"price": 5.25,
"tags": ["sweet", "cold"],
"allergens": ["dairy"]
}
]
- Verifica que el archivo JSON tenga el formato correcto:
cat menu.json | python3 -m json.tool > /dev/null && echo "Valid JSON!"
💬 Debate: Bases de datos activas vs. JSON local
¿Por qué usamos un archivo local simple menu.json en lugar de una base de datos activa?
Para un prototipo o un instructivo rápido, un archivo JSON local elimina el tiempo y la complejidad de la configuración inicial de la base de datos. Sin embargo, en una aplicación de producción empresarial del mundo real, conectarías el agente a una base de datos administrada, como Cloud Firestore, AlloyDB o Cloud SQL.
El uso de una base de datos activa permite que los administradores de la cafetería agreguen artículos de temporada, actualicen los precios o ajusten las etiquetas de alérgenos de forma dinámica sin volver a compilar la imagen del contenedor ni volver a implementar el código de la aplicación. Usaremos una base de datos activa como paso opcional más adelante en el codelab.
5. Compila el agente del ADK
Ahora instalarás los paquetes necesarios y compilarás la lógica principal del agente del ADK. Definirás una herramienta get_menu() y la pasarás a un LlmAgent.
- Crea y abre
requirements.txten el Editor de Cloud Shell:
cloudshell edit requirements.txt
- Pega las siguientes dependencias en el editor y guarda el archivo:
google-adk==2.2.0
streamlit==1.58.0
- Crea y abre
agent.pyen el Editor de Cloud Shell:
cloudshell edit agent.py
- Pega el siguiente código en
agent.py:
# agent.py
import json
from google.adk.agents import LlmAgent
# [START get_menu]
def get_menu() -> str:
"""Retrieves the coffee shop menu from menu.json.
Returns:
str: A JSON string representing the list of menu items.
"""
try:
with open("menu.json", "r") as f:
menu_data = json.load(f)
return json.dumps(menu_data)
except Exception as e:
return json.dumps({"error": f"Could not retrieve menu: {str(e)}"})
# [END get_menu]
# Create the barista agent
barista_agent = LlmAgent(
name="barista_agent",
model="gemini-3.5-flash",
instruction="""You are a friendly barista at ☕ Coffee Shop.
Your job is to recommend drinks and pastries to customers based on their preferences.
Rules you MUST follow:
1. You must recommend items ONLY from the menu returned by get_menu().
2. Do NOT recommend or suggest any item that is not present in the menu.
3. If a user's preference is vague or unclear, ask exactly ONE friendly clarifying question to narrow down what they want (e.g., cold or hot, sweet or strong, coffee or pastry).
4. Be warm and welcoming, but remain professional.
5. Ground your recommendations in the actual tags, descriptions, and allergens listed in the menu (e.g., if a user is dairy-free, recommend ONLY items tagged 'dairy-free' or with no dairy allergens).
""",
tools=[get_menu]
)
from google.adk.apps import App
# Define the App object
app = App(
name="coffee_barista_app",
root_agent=barista_agent
)
- Crea y abre
app.pyen el Editor de Cloud Shell:
cloudshell edit app.py
- Pega el siguiente código en
app.py:
# app.py
import streamlit as st
import json
# Set page config for a premium look
st.set_page_config(
page_title="☕ Coffee Shop - Barista Bot",
page_icon="☕",
layout="wide",
initial_sidebar_state="expanded"
)
# Custom CSS to make the header sticky (adapts to light/dark themes)
st.markdown("""
<style>
div[data-testid="element-container"]:has(.header-container),
div.element-container:has(.header-container) {
position: sticky;
top: 2.875rem;
z-index: 999;
background-color: transparent;
padding-bottom: 10px;
}
</style>
""", unsafe_allow_html=True)
# App Header (using inline styles for the permanent coffee theme look)
st.markdown("""
<div class="header-container" style="text-align: center; padding: 20px; background: linear-gradient(135deg, #8B5E3C, #6F4E37); color: white; border-radius: 12px; box-shadow: 0 4px 15px rgba(0,0,0,0.1);">
<h1 style="margin: 0; font-size: 2.5rem; font-weight: 700; color: white;">☕ ☕ Coffee Shop</h1>
<p style="margin: 5px 0 0 0; font-size: 1.1rem; opacity: 0.9; color: white;">Your friendly AI Barista is ready to help you find the perfect drink or pastry!</p>
</div>
""", unsafe_allow_html=True)
# Load Menu for the sidebar
# [START load_menu]
try:
with open("menu.json", "r") as f:
menu_items = json.load(f)
except Exception as e:
st.error(f"Error loading menu: {e}")
menu_items = []
# [END load_menu]
# Sidebar Menu & Configuration
with st.sidebar:
st.markdown("## ☕ Coffee Shop Menu")
st.markdown("Explore our offerings and ask the barista for recommendations.")
st.markdown("---")
for item in menu_items:
with st.container(border=True):
st.markdown(f"**{item['name']}** • **${item['price']:.2f}**")
st.caption(item['description'])
# Tags & Allergens as native badges
tags = " ".join([f"`{t}`" for t in item.get("tags", [])])
if tags:
st.markdown(tags)
allergens = ", ".join(item.get("allergens", []))
if allergens:
st.markdown(f"⚠️ *Allergens: {allergens}*")
# Chat Interface
if "session_id" not in st.session_state:
import uuid
st.session_state.session_id = str(uuid.uuid4())
if "runner" not in st.session_state:
from google.adk.runners import InMemoryRunner
from agent import app
st.session_state.runner = InMemoryRunner(app=app)
if "messages" not in st.session_state:
st.session_state.messages = [
{"role": "assistant", "content": "Welcome to ☕ Coffee Shop! What can I get started for you today?"}
]
# Display existing messages
for msg in st.session_state.messages:
with st.chat_message(msg["role"]):
st.markdown(msg["content"])
# User Input
if prompt := st.chat_input("Ask for recommendations (e.g., 'What dairy-free pastries do you have?')"):
# Display user message
with st.chat_message("user"):
st.markdown(prompt)
st.session_state.messages.append({"role": "user", "content": prompt})
# Generate response
with st.chat_message("assistant"):
try:
import asyncio
# Run the ADK runner asynchronously using asyncio.run
async def fetch_response():
return await st.session_state.runner.run_debug(
prompt,
session_id=st.session_state.session_id
)
res_events = asyncio.run(fetch_response())
response_text = "".join([
part.text
for event in res_events
if event.content and event.content.parts
for part in event.content.parts
if part.text
])
st.markdown(response_text)
st.session_state.messages.append({"role": "assistant", "content": response_text})
except Exception as e:
st.error(f"Apologies, I ran into an error: {e}")
💬 Debate: Compromisos del modelo y eficiencia de los tokens de recuperación
¿Por qué llamar a una herramienta de función para recuperar el menú en lugar de simplemente pegar todo el texto del menú en las instrucciones del sistema del agente?
¡Economía de tokens! Incluir 8 elementos en la instrucción es económico, pero ¿qué sucede si la cafetería se expande a 500 elementos, incluidos ingredientes personalizados? Pegar grandes conjuntos de datos directamente en la instrucción del sistema aumenta el recuento de tokens de la instrucción, lo que incrementa los costos de transacción y la latencia de respuesta de la API en cada consulta.
Cuando se usa una herramienta del ADK, el agente solicita de forma dinámica leer el menú SOLO cuando es necesario. El LLM solo recibe los datos del menú pertinentes como contexto, lo que minimiza el tamaño de los tokens de la instrucción.
💬 Debate: Almacenes de producción y estado de la memoria
¿El historial de chat almacenado en st.session_state de Streamlit persiste cuando un usuario cierra la pestaña del navegador?
No, no lo sabe. st.session_state se encuentra completamente en la memoria y es único para la conexión activa del navegador. Si un usuario actualiza la página o cierra la pestaña, se perderá su historial de conversación con el barista.
En el caso de una aplicación de producción, conectarías el ejecutor del ADK a un backend de almacenamiento persistente, como Cloud Firestore o Redis. El ADK proporciona abstracciones de servicio integradas (como SessionService) que facilitan guardar y reanudar el historial de chat en las recargas de páginas y los dispositivos.
6. Implementa el agente en Cloud Run
Implementarás la aplicación de Streamlit directamente desde el código fuente con los buildpacks integrados de Cloud Run. Para seguir el principio de privilegio mínimo, crearás y realizarás la implementación con una cuenta de servicio personalizada dedicada en lugar de usar la cuenta de servicio predeterminada de Compute Engine.
- Crea una cuenta de servicio dedicada:
gcloud iam service-accounts create barista-agent-sa \
--description="Service account for Coffee Barista ADK agent on Cloud Run" \
--display-name="Barista Agent Service Account"
- Otorga el rol de usuario de Gemini Enterprise Agent Platform (
roles/aiplatform.user) a la nueva cuenta de servicio:
gcloud projects add-iam-policy-binding $PROJECT_ID \
--member="serviceAccount:barista-agent-sa@$PROJECT_ID.iam.gserviceaccount.com" \
--role="roles/aiplatform.user"
- Implementa el servicio con
gcloud run deployy pasa el nuevo correo electrónico de la cuenta de servicio a través de la marca--service-account:
gcloud run deploy coffee-barista \
--source . \
--region $REGION \
--allow-unauthenticated \
--labels dev-tutorial=codelab-streamlit-rag-adk \
--command "/cnb/lifecycle/launcher" \
--args "sh,-c,python3 -m streamlit run app.py --server.port=\$PORT --server.address=0.0.0.0 --server.enableCORS=false --server.enableXsrfProtection=false" \
--service-account "barista-agent-sa@$PROJECT_ID.iam.gserviceaccount.com" \
--set-env-vars GOOGLE_GENAI_USE_VERTEXAI=TRUE,GOOGLE_CLOUD_PROJECT=$PROJECT_ID,GOOGLE_CLOUD_LOCATION=global
- Una vez que se implemente, busca la URL del servicio en el resultado del comando.
💬 Debate: Implementación de contenedores frente a código fuente y seguridad de IAM
Realizamos la implementación en Cloud Run con gcloud run deploy --source sin crear un Dockerfile ni un Procfile. ¿Cómo supo Cloud Run cómo compilar y ejecutar nuestra app de Python?
Cloud Run usa Buildpacks de forma interna para analizar tu repositorio. Cuando detecta la presencia derequirements.txt y archivos fuente de Python, el motor compila y empaqueta automáticamente un contenedor de entorno de ejecución de Python.
Escribir un Dockerfile personalizado te brinda control total sobre los paquetes del sistema y las capas base de tu contenedor. Un Procfile es una forma más simple de declarar el comando de inicio sin configurar por completo un contenedor. Sin embargo, para las implementaciones rápidas, la implementación desde la fuente (--source) es muy eficiente.
¿Por qué realizamos el paso adicional de crear una cuenta de servicio personalizada barista-agent-sa en lugar de usar la cuenta de servicio predeterminada de Compute Engine?
La seguridad ante todo La cuenta de servicio predeterminada de Compute Engine tiene permisos de editor extremadamente amplios de forma predeterminada. Ejecutar nuestro contenedor de Cloud Run con la cuenta de servicio predeterminada significa que, si nuestra app tiene un error de seguridad, un atacante podría leer, escribir o borrar otros recursos en nuestro proyecto de Google Cloud.
Si creamos una cuenta de servicio dedicada y solo le asignamos el rol de roles/aiplatform.user, cumplimos con el principio de privilegio mínimo: la app tiene exactamente el acceso que necesita para llamar a Gemini y nada más.
7. Prueba el comportamiento de la RAG
Abre la URL del servicio de Cloud Run en un navegador web y hazle preguntas a AI Barista para probar sus restricciones de fundamentación y seguridad.
- Solicitud en el menú: Pregunta: "Recomiéndame algo fuerte y cálido".Respuesta esperada:El agente recomienda un espresso.
- Pregunta trampa fuera del menú: Pregunta: "¿Tienen un frappuccino de matcha?" Respuesta esperada: El agente rechaza la solicitud de forma cortés y explica que no está en el menú.
- Solicitud que tiene en cuenta las alergias: Pregunta: "Soy intolerante a la lactosa. ¿Qué puedo pedir?" Respuesta esperada: El agente solo recomienda elementos del menú sin lácteos (como el café con leche de avena, el espresso y el café frío). NO recomienda Cappuccino ni Croissant.

8. Opcional: Fundamenta tu agente en Firestore con la búsqueda de vectores
En una situación de producción, no es ideal almacenar los elementos del menú en un archivo menu.json local, ya que cualquier cambio en el menú requiere recompilar la imagen de contenedor y volver a implementar el servicio de Cloud Run.
Para que la aplicación sea dinámica y escalable, puedes migrar los datos del menú a Cloud Firestore y usar la búsqueda de vectores para recuperar solo los elementos del menú más relevantes según la similitud semántica.

1. Habilita la API de Firestore y, luego, inicializa la base de datos
Ejecuta los siguientes comandos para habilitar la API de Firestore y crear una base de datos de Firestore llamada coffee-menu en modo nativo:
gcloud services enable firestore.googleapis.com
gcloud firestore databases create --database="coffee-menu" --location=$REGION
Nota: La habilitación de la API puede tardar de 1 a 2 minutos en propagarse. Si el comando de creación de la base de datos te solicita API [firestore.googleapis.com] not enabled on project... Would you like to enable and retry?, escribe Y para continuar o espera un minuto y vuelve a ejecutar el comando.
2. Propaga Firestore con los datos del menú
Para propagar rápidamente tu base de datos de Firestore con los elementos del menú de tu archivo menu.json, puedes ejecutar una secuencia de comandos de Python de forma local en Cloud Shell.
- Instala las bibliotecas cliente de Firestore y GenAI de forma local en Cloud Shell para ejecutar la secuencia de comandos de inicialización:
pip3 install google-cloud-firestore==2.27.0 google-genai==2.11.0
- Crea una secuencia de comandos de inicialización
seed.py:
cloudshell edit seed.py
- Pega el siguiente código en
seed.py:
# seed.py
import json
import os
from google import genai
from google.cloud import firestore
from google.cloud.firestore_v1.vector import Vector
db = firestore.Client(database="coffee-menu")
client = genai.Client(
vertexai=True,
project=os.environ.get("PROJECT_ID"),
location=os.environ.get("REGION", "us-central1")
)
with open("menu.json", "r") as f:
menu_items = json.load(f)
for item in menu_items:
# Use the name as the document ID
doc_id = item["name"].lower().replace(" ", "-")
# Generate text embedding using Vertex AI text-embedding-004 model
text_to_embed = f"{item['name']}: {item['description']}"
response = client.models.embed_content(
model="text-embedding-004",
contents=text_to_embed,
)
embedding = response.embeddings[0].values
# Add embedding vector to the menu item data
item["embedding"] = Vector(embedding)
db.collection("menu").document(doc_id).set(item)
print("Firestore menu collection seeded with vector embeddings successfully!")
- Ejecuta la secuencia de comandos:
python3 seed.py
3. Crea un índice vectorial de Firestore
Para realizar búsquedas de vectores en los elementos del menú, debes crear un índice de vectores compuesto en el campo embedding de tu base de datos de Firestore.
Ejecuta el siguiente comando en la terminal de Cloud Shell:
gcloud firestore indexes composite create \
--collection-group=menu \
--query-scope=COLLECTION \
--database="coffee-menu" \
--field-config=field-path=embedding,vector-config='{"dimension":"768", "flat": "{}"}'
Nota: La creación de índices de Firestore se ejecuta en segundo plano y puede tardar unos minutos en completarse. Puedes continuar con los siguientes pasos del codelab mientras se compila el índice.
4. Otorga acceso a Firestore a la cuenta de servicio
Para que tu servicio de Cloud Run consulte Firestore, debes otorgarle a su cuenta de servicio el rol de Usuario de Cloud Datastore (roles/datastore.user):
gcloud projects add-iam-policy-binding $PROJECT_ID \
--member="serviceAccount:barista-agent-sa@$PROJECT_ID.iam.gserviceaccount.com" \
--role="roles/datastore.user"
Nota: Aunque usamos Cloud Firestore en modo nativo, Google Cloud utiliza los roles unificados de IAM de Cloud Datastore (roles/datastore.viewer o roles/datastore.user) para administrar el control de acceso.
5. Actualiza el código
Ahora, actualiza tu código para recuperar el menú de Firestore en lugar de leerlo de menu.json.
- Abre
requirements.txten el editor de Cloud Shell:
cloudshell edit requirements.txt
- Agrega las bibliotecas cliente de Firestore y GenAI al final del archivo y guárdalo:
google-cloud-firestore==2.27.0
google-genai==2.11.0
- Abre
agent.pyen el editor de Cloud Shell:
cloudshell edit agent.py
- Busca el bloque
# [START get_menu]enagent.pyy reemplázalo por completo (desde# [START get_menu]hasta# [END get_menu]) con la siguiente implementación de Firestore:
# [START get_menu]
from google import genai
from google.cloud import firestore
from google.cloud.firestore_v1.base_vector_query import DistanceMeasure
from google.cloud.firestore_v1.vector import Vector
def get_menu(query: str) -> str:
"""Retrieves coffee shop menu items matching the user's query.
Args:
query: The search query or preference to find matching menu items.
Returns:
str: A JSON string representing the list of top matching menu items.
"""
try:
# Initialize clients
db = firestore.Client(database="coffee-menu")
client = genai.Client()
# Generate embedding for the search query
response = client.models.embed_content(
model="text-embedding-004",
contents=query,
)
query_vector = response.embeddings[0].values
# Search the Firestore database using Vector Search
results = db.collection("menu").find_nearest(
vector_field="embedding",
query_vector=Vector(query_vector),
distance_measure=DistanceMeasure.COSINE,
limit=3,
).stream()
menu_data = []
for doc in results:
item = doc.to_dict()
# Remove embedding field to save tokens
item.pop("embedding", None)
menu_data.append(item)
return json.dumps(menu_data)
except Exception as e:
return json.dumps({"error": f"Could not retrieve menu: {str(e)}"})
# [END get_menu]
- Abre
app.pyen el editor de Cloud Shell:
cloudshell edit app.py
- Busca el bloque
# [START load_menu]enapp.pyy reemplázalo por completo (desde# [START load_menu]hasta# [END load_menu]) con la siguiente lógica de carga de Firestore:
# [START load_menu]
from google.cloud import firestore
try:
db = firestore.Client(database="coffee-menu")
docs = db.collection("menu").stream()
menu_items = []
for doc in docs:
item = doc.to_dict()
item.pop("embedding", None)
menu_items.append(item)
except Exception as e:
st.error(f"Error loading menu from Firestore: {e}")
menu_items = []
# [END load_menu]
6. Vuelve a implementar en Cloud Run
Implementa la aplicación actualizada:
gcloud run deploy coffee-barista \
--source . \
--region $REGION \
--allow-unauthenticated \
--command "/cnb/lifecycle/launcher" \
--args "sh,-c,python3 -m streamlit run app.py --server.port=\$PORT --server.address=0.0.0.0 --server.enableCORS=false --server.enableXsrfProtection=false" \
--service-account "barista-agent-sa@$PROJECT_ID.iam.gserviceaccount.com" \
--set-env-vars GOOGLE_GENAI_USE_VERTEXAI=TRUE,GOOGLE_CLOUD_PROJECT=$PROJECT_ID,GOOGLE_CLOUD_LOCATION=global
7. Verifica la integración de Firestore
Para probar la conexión del agente a Firestore, agrega un elemento de menú nuevo directamente en Firestore y verifica que el agente lo recomiende.
- Ejecuta el siguiente comando en Cloud Shell para escribir un documento nuevo en la colección
menuen Firestore con Python:
python3 -c "
import os
from google import genai
from google.cloud import firestore
from google.cloud.firestore_v1.vector import Vector
db = firestore.Client(database='coffee-menu')
client = genai.Client(
vertexai=True,
project=os.environ.get('PROJECT_ID'),
location=os.environ.get('REGION', 'us-central1')
)
name = 'Matcha Green Tea Latte'
desc = 'Creamy steamed milk infused with premium Japanese matcha powder.'
res = client.models.embed_content(
model='text-embedding-004',
contents=f'{name}: {desc}'
)
embedding = res.embeddings[0].values
db.collection('menu').document('matcha-latte').set({
'name': name,
'description': desc,
'price': 5.50,
'tags': ['sweet', 'hot', 'dairy-free'],
'allergens': [],
'embedding': Vector(embedding)
})
print('Successfully added Matcha Latte with vector embeddings!')
"
- Actualiza tu app de Streamlit en el navegador para borrar la sesión de chat y cargar el nuevo estado de la base de datos.
- Ten en cuenta lo siguiente:
- Matcha Green Tea Latte aparecerá automáticamente en el menú de la barra lateral.
- Pregúntale al chatbot: "¿Tienen bebidas de matcha?"
- El agente debería recomendar correctamente el nuevo Matcha Green Tea Latte con la descripción y el precio que acabas de agregar. Esto confirma que el agente se basa en la consulta directamente en tu base de datos activa de Firestore.
9. Limpia
Para evitar cargos continuos en tu cuenta de facturación de Google Cloud, borra el servicio de Cloud Run implementado y la cuenta de servicio personalizada.
Borra el servicio de Cloud Run:
gcloud run services delete coffee-barista --region $REGION --quiet
Borra la cuenta de servicio personalizada:
gcloud iam service-accounts delete barista-agent-sa@$PROJECT_ID.iam.gserviceaccount.com --quiet
(Opcional) Borra la base de datos de Firestore (si la creaste):
gcloud firestore databases delete --database="coffee-menu" --quiet
Paso opcional: Borra todo el proyecto. ⚠️SOLO haz esto si creaste un proyecto específico para este lab.
gcloud projects delete $PROJECT_ID
10. Felicitaciones
¡Felicitaciones! Creaste e implementaste un agente de barista de IA con generación aumentada por recuperación (RAG) usando el ADK de Google y Cloud Run.
Qué aprendiste
- Cómo construir herramientas simples de RAG en Python
- Utiliza
LlmAgentyInMemoryRunnerdel ADK. - Crea experiencias de chat con estado en Streamlit.
- Implementa Streamlit en Cloud Run con compilaciones basadas en la fuente.