1. Cómo generar confianza para fomentar la generosidad

El momento de inspiración
El teléfono vibra. Ves una noticia sobre un programa de alfabetización exitoso que ayuda a los niños de comunidades desatendidas a aprender a leer. Sientes un fuerte impulso por contribuir. Abres tu navegador y buscas "donaciones para programas de alfabetización infantil".

Aparecen cientos de resultados.
Haces clic en el primer vínculo. El sitio web se ve profesional. Te desplazas hacia abajo hasta sus finanzas. "Los costos administrativos son del 28%". Haces una pausa. Solo 72 centavos de cada dólar que dones se destinarán a financiar el programa. ¿Te parece bien? No lo sabes con certeza.
Prueba con otra organización. Nunca has oído hablar de ellos. ¿Son legítimos? Una búsqueda rápida te lleva a un agujero de conejo. Encuentras un hilo de Reddit de hace dos años en el que un usuario afirma: "Es una estafa, mi donación nunca llegó a ningún lado". Otro los defiende con pasión: "Están en el terreno haciendo un trabajo real". La ambigüedad es paralizante.
Treinta minutos después, te encuentras en un laberinto de opiniones contradictorias, calificaciones de eficiencia y registros del IRS, y aún no has hecho la donación. La chispa inicial de generosidad se reemplazó por la fricción de la investigación. La pestaña permanece abierta durante algunos días, como un pequeño recordatorio de una buena intención, hasta que finalmente la cierras.
Esto no es un fracaso personal, sino un fracaso del sistema
Esta experiencia es universal. El deseo de dar es abundante, pero el proceso está lleno de obstáculos que causan dudas y vacilaciones:
- ❌ Fricción en la investigación: Cada organización benéfica requiere su propia investigación.
- ❌ Verificación de confianza: Es difícil distinguir las organizaciones altamente eficaces de las ineficientes o incluso de las estafas directas.
- ❌ Parálisis por análisis: Una cantidad abrumadora de opciones genera fatiga por toma de decisiones.
- ❌ Pérdida de impulso: El impulso emocional para dar se desvanece a medida que aumenta la carga logística.
Esta fricción tiene un costo asombroso en el mundo real. Las donaciones individuales en Estados Unidos son enormes: los donantes individuales aportaron aproximadamente USD 374,000 millones solo en 2023, según Giving USA 2024. Sin embargo, la investigación demuestra que las barreras para donar, como los costos de búsqueda, la fricción psicológica y las limitaciones de tiempo, reducen significativamente la cantidad que llega a las causas benéficas. Los estudios que involucran a millones de donantes descubrieron que incluso pequeñas fricciones en el proceso de donación en línea impiden que las personas cumplan con sus intenciones caritativas.
Esto representa miles de millones de dólares en donaciones previstas que nunca llegan a las causas que las necesitan.
La Visión
Imagina una experiencia diferente. En lugar de una sesión de investigación de 30 minutos, simplemente dices:
"Quiero donar USD 50 a un programa de alfabetización para niños. Búscame una organización benéfica eficiente, verificada y con buenas calificaciones".
En segundos, obtendrás una respuesta que te dará confianza:
Esta es la promesa de un agente de donaciones impulsado por IA. Sin embargo, para hacer realidad esta visión, debemos resolver un desafío fundamental: cuando un agente autónomo de IA maneja dinero, la confianza no es opcional, sino que es la base de todo.
- ¿Cómo podemos demostrar lo que autorizó un usuario?
- ¿Quién es responsable si se comete un error?
- ¿Cómo les brindamos a los donantes, las organizaciones benéficas y las redes de pagos la confianza necesaria para participar?
Tu misión de hoy
En este taller, crearás ese agente confiable combinando dos tecnologías potentes:
Google Agent Development Kit (ADK) | Agent Payments Protocol (AP2) | |
Función | La fábrica para crear agentes de IA de nivel de producción | El plan arquitectónico para la confianza en las transacciones de IA |
Qué proporciona | • Framework para la organización de varios agentes | • Límites de seguridad basados en roles |
Más información |
Qué compilarás
Al final de este taller, habrás creado lo siguiente:
✅ Un sistema multiagente con roles especializados:
- Un agente de compras que encuentra organizaciones benéficas verificadas
- Un agente comercial que crea ofertas de donación vinculantes
- Un proveedor de credenciales que procesa pagos de forma segura
- Un organizador que coordina todo el flujo
✅ Tres tipos de credenciales verificables:
- IntentMandate: "Búscame una organización benéfica educativa"
- CartMandate: "Donación de USD 50 a Room to Read, firmada por el comercio"
- PaymentMandate: "Process via simulated payment"
✅ Seguridad en cada capa:
- Límites de confianza basados en roles
- Consentimiento explícito del usuario
✅ Un registro de auditoría completo:
- Cada decisión es rastreable
- Todos los consentimientos registrados
- Se muestra cada transferencia
🔒 Importante: Este es un entorno de aprendizaje seguro
¿Todo listo para fomentar la confianza?
En el próximo módulo, configuraremos tu entorno de desarrollo y compilaremos tu primer agente de IA. Descubrirás rápidamente por qué los agentes simples no son confiables y, luego, dedicarás el resto del taller a aprender cómo solucionar ese problema.
Comencemos por comprender el problema de primera mano.
2. Prepara tu espacio de trabajo
La base de los agentes confiables
Antes de poder compilar nuestro agente de donaciones con IA, debemos preparar un entorno de desarrollo limpio, coherente y configurado correctamente. Este módulo es un paso enfocado en garantizar que todos los servicios y las herramientas necesarios estén disponibles.
Completar esta configuración correctamente significa que puedes enfocarte por completo en el emocionante trabajo de crear la lógica del agente en los módulos que se incluyen más adelante, sin preocuparte por problemas de configuración.
Accede a Cloud Shell
Primero, abriremos Cloud Shell, que es una terminal basada en el navegador con el SDK de Google Cloud y otras herramientas esenciales preinstaladas.
¿Necesitas créditos de Google Cloud?
Haz clic en Activar Cloud Shell en la parte superior de la consola de Google Cloud (es el ícono de terminal en la barra de navegación de la esquina superior derecha).

Busca tu ID del proyecto de Google Cloud:
- Abre la consola de Google Cloud: https://console.cloud.google.com
- Selecciona el proyecto que deseas usar para este taller en el menú desplegable de proyectos que se encuentra en la parte superior de la página.
- Tu ID del proyecto se muestra en la tarjeta de información del proyecto en el panel

Una vez que se abra Cloud Shell, verifica que te hayas autenticado:
# Check that you are logged in
gcloud auth list
Deberías ver tu cuenta como (ACTIVE).
Configura tu proyecto
Ahora, configuremos tu proyecto de Google Cloud y habilitemos las APIs necesarias.
Establece el ID de tu proyecto
# Set your project using the auto-detected environment variable in Cloud Shell
gcloud config set project $GOOGLE_CLOUD_PROJECT
# Verify the project has been set
echo "Your active Google Cloud project is: $(gcloud config get-value project)"
Habilita las API obligatorias
Tus agentes necesitan acceso a varios servicios de Google Cloud:
gcloud services enable \
aiplatform.googleapis.com \
secretmanager.googleapis.com \
cloudtrace.googleapis.com
Esto puede llevar entre 1 y 2 minutos En esta página verá lo siguiente:
Operation "operations/..." finished successfully.
Qué proporcionan estas APIs:
- aiplatform.googleapis.com: Acceso a los modelos de Gemini para el razonamiento de agentes
- secretmanager.googleapis.com: Almacenamiento seguro para claves de API (práctica recomendada para producción)
- cloudtrace.googleapis.com: Observabilidad para nuestro registro de responsabilidad
Clona el código de partida
Obtén el repositorio del taller con todo el código y los recursos de la plantilla:
git clone https://github.com/ayoisio/adk-ap2-charity-agents
cd adk-ap2-charity-agents
git checkout codelab
Verifiquemos lo que tenemos:
ls -la
Deberías ver lo siguiente:
charity_advisor/: Donde compilaremos nuestros agentes y herramientasscripts/: Secuencias de comandos auxiliares para pruebas y verificacióndeploy.sh: Es una secuencia de comandos auxiliar para la implementación.setup.py: Es una secuencia de comandos auxiliar para la instalación de módulos..env.template: Archivo de variables de entorno
Configura el entorno de Python
Ahora crearemos un entorno aislado de Python para nuestro proyecto.
Crea y activa el entorno virtual
# Create the virtual environment
python3 -m venv venv
# Activate it
source venv/bin/activate
✅ Verificación: Tu instrucción ahora debería mostrar el prefijo (venv).
Instala las dependencias
pip install -r charity_advisor/requirements.txt
pip install -e .
Se instala lo siguiente:
- google-adk: Es el framework del Agent Development Kit.
- google-cloud-aiplatform: Integración de Vertex AI y Gemini
- ap2: SDK del protocolo de pagos de agentes (de GitHub)
- python-dotenv: Administración de variables de entorno
La marca -e te permite importar módulos adk_ap2_charity_agents desde cualquier lugar.
Configura el archivo de entorno
Crea tu configuración a partir de la plantilla:
# Copy the template
cp .env.template .env
# Get your current Project ID
PROJECT_ID=$(gcloud config get-value project)
# Replace the placeholder with your actual project ID
sed -i "s/your-project-id/$PROJECT_ID/g" .env
# Verify the replacement worked
grep GOOGLE_CLOUD_PROJECT .env
Deberías ver lo siguiente:
GOOGLE_CLOUD_PROJECT=your-actual-project-id
Verificación
Ejecuta la secuencia de comandos de verificación para asegurarte de que todo esté configurado correctamente:
python scripts/verify_setup.py
Deberías ver todas las marcas de verificación verdes:
======================================================================
SETUP VERIFICATION
======================================================================
✓ Python version: 3.11.x
✓ google-adk: 1.17.0
✓ google-cloud-aiplatform: 1.111.0+
✓ ap2: 0.1.0
✓ python-dotenv: 1.0.0+
✓ .env file found and contains project ID
✓ Google Cloud project configured: your-project-id
✓ Mock charity database found
✓ Agent templates ready
✓ All directories present
======================================================================
✓ Setup complete! You are ready to build trustworthy agents.
======================================================================
Solución de problemas
¿Qué sigue?
Tu entorno ya está completamente preparado. Aprendiste a realizar lo siguiente:
- ✅ Proyecto de Google Cloud configurado
- ✅ APIs obligatorias habilitadas
- ✅ Se instalaron las bibliotecas del ADK y de AP2
- ✅ El código de la plantilla está listo para modificarse
En el próximo módulo, crearás tu primer agente de IA con unas pocas líneas de código y descubrirás por qué los agentes simples no son confiables cuando manejan transacciones financieras.
3. Tu primer agente y el descubrimiento de la brecha de confianza

De la idea a la interacción
En el módulo anterior, preparamos nuestro entorno de desarrollo. Ahora comienza el trabajo emocionante. Compilaremos y ejecutaremos nuestro primer agente, le daremos su primera capacidad y, al hacerlo, descubriremos los desafíos fundamentales que debemos resolver para que sea realmente confiable.
Este módulo es tu foto del "antes", el momento que revela por qué crear agentes confiables requiere más que solo darles acceso a herramientas a los LLM.
Paso 1: Examina el agente inicial
Primero, veamos la plantilla de nuestro primer agente. Contiene una estructura básica con marcadores de posición que completaremos en los próximos pasos.
👉 Abre el archivo .
charity_advisor/simple_agent/agent.py
en tu editor.
En esta página verá lo siguiente:
"""
A simple agent that can research charities using Google Search.
"""
# MODULE_3_STEP_2_IMPORT_COMPONENTS
simple_agent = Agent(
name="SimpleAgent",
model="gemini-2.5-flash",
# MODULE_3_STEP_3_WRITE_INSTRUCTION
instruction="""""",
# MODULE_3_STEP_4_ADD_TOOLS
tools=[]
)
Observa que los comentarios de marcador de posición siguen un patrón: MODULE_3_STEP_X_DESCRIPTION. Reemplazaremos estos marcadores para compilar nuestro agente de forma progresiva.
Paso 2: Importa los componentes requeridos
Antes de que podamos crear una instancia de la clase Agent o usar la herramienta google_search, debemos importarlas a nuestro archivo.
👉 Buscar:
# MODULE_3_STEP_2_IMPORT_COMPONENTS
👉 Reemplaza esa sola línea por lo siguiente:
from google.adk.agents import Agent
from google.adk.tools import google_search
Ahora, la clase Agent y la herramienta google_search están disponibles en nuestro archivo.
Paso 3: Escribe la instrucción del agente
La instrucción es la "descripción del trabajo" del agente, ya que le indica al LLM cuándo y cómo usar sus herramientas. Escribamos una que guíe a nuestro agente para buscar información sobre organizaciones benéficas.
👉 Buscar:
# MODULE_3_STEP_3_WRITE_INSTRUCTION
instruction="""""",
👉 Reemplaza esas dos líneas por lo siguiente:
instruction="""You are a helpful research assistant. When a user asks you to find information about charities,
use the google_search tool to find the most relevant and up-to-date results from the web.
Synthesize the search results into a helpful summary.""",
Paso 4: Agrega la herramienta de búsqueda
Un agente sin herramientas es solo un conversador. Démosle a nuestro agente su primera capacidad: la de buscar en la Web.
👉 Buscar:
# MODULE_3_STEP_4_ADD_TOOLS
tools=[]
👉 Reemplaza esas dos líneas por lo siguiente:
tools=[google_search]
Paso 5: Verifica tu agente completo
Confirmemos que todas las piezas estén en su lugar antes de realizar la prueba.
👉 Tu completa
charity_advisor/simple_agent/agent.py
El archivo ahora debería verse exactamente así:
"""
A simple agent that can research charities using Google Search.
"""
from google.adk.agents import Agent
from google.adk.tools import google_search
simple_agent = Agent(
name="SimpleAgent",
model="gemini-2.5-flash",
instruction="""You are a helpful research assistant. When a user asks you to find information about charities,
use the google_search tool to find the most relevant and up-to-date results from the web.
Synthesize the search results into a helpful summary.""",
tools=[google_search]
)
Paso 6: Prueba el agente y expón las brechas de confianza
Ahora que nuestro agente está completamente configurado, probémoslo y analicemos su comportamiento. Aquí es donde descubrimos por qué los agentes simples no son confiables cuando se trata de tomar decisiones financieras.
Prueba 1: El problema del descubrimiento
👉 En la terminal de Cloud Shell, ejecuta el siguiente comando:
adk run charity_advisor/simple_agent
Deberías ver un resultado como el siguiente:
INFO:google.adk.agents:Loading agent from charity_advisor/simple_agent
INFO:google.adk.agents:Agent 'SimpleAgent' ready
[user]:
La instrucción [user]: ahora espera tu entrada.
👉 En la instrucción [user], escribe lo siguiente:
Can you find me a verified, highly-rated charity for children's literacy?
👉 Presiona Intro y observa la respuesta.
Después de un momento, el agente sintetizará los resultados de la búsqueda en una respuesta como esta:
Según una búsqueda en la Web, algunas organizaciones benéficas reconocidas para la alfabetización de los niños son Reading Is Fundamental y Room to Read. A menudo, se recomiendan fuentes como Charity Navigator y GuideStar para verificar su estado y calificaciones. También encontré varios debates en línea, incluso en foros como Reddit, en los que los usuarios comparten experiencias personales con diversos programas de alfabetización locales más pequeños.
Analicemos esto. ¿El agente resolvió nuestro problema?
❌ No. Replicó a la perfección la experiencia humana que describimos en el módulo 1. Automatizó con éxito el proceso de "buscar en Google" y nos devolvió el problema de la "parálisis por análisis".
Esto expone la primera brecha de confianza: la falta de datos autorizados.
El agente busca en la Web abierta, lo que significa lo siguiente:
- ✅ Encontró resultados rápidamente (experiencia del usuario mejorada)
- ❌ Mezcla organizaciones con calificaciones altas con debates de Reddit (fuentes no confiables).
- ❌ No puede distinguir entre organizaciones benéficas verificadas y posibles estafas (no hay verificación).
- ❌ Nos pide a nosotros que verifiquemos la información que acaba de proporcionar (transfiere la carga de nuevo).
Prueba 2: El problema de ejecución
Ahora, pasemos a la segunda prueba crucial. En el mensaje [user]:, intenta completar la donación:
Okay, please donate $50 to Room to Read for me.
El agente responderá con una disculpa y admitirá su limitación:
Entiendo que quieres hacer una donación, lo cual es maravilloso. Sin embargo, soy asistente de investigación y no puedo manejar transacciones financieras ni procesar pagos. Para hacer una donación, deberás visitar directamente el sitio web oficial de Room to Read.
Este es el segundo momento de "¡Ajá!", que es igual de importante.
No solo no se puede confiar en que el agente encuentre la organización benéfica adecuada, sino que tampoco se puede confiar en que realice la acción de donar.
👉 Presiona
Ctrl+C
para salir cuando termines de realizar pruebas.
Visualización de las dos brechas
Qué aprendiste
En este módulo, creaste y equipaste correctamente tu primer agente de IA. De esta manera, descubriste los dos desafíos fundamentales para crear un sistema confiable.
Conceptos clave dominados
✅ La clase Agent:
- Componente básico del ADK
- Combina el razonamiento del LLM (cerebro) con herramientas (manos)
- Configurado con el modelo, las instrucciones y las herramientas
✅ Estructura basada en carpetas:
- Cada agente se encuentra en su propia carpeta.
- El ADK busca
agent_folder/agent.py - Correr con
adk run agent_folder
✅ La lista de herramientas:
- Define las capacidades del agente
- El LLM decide cuándo y cómo usar las herramientas
- Puede contener varias herramientas para diferentes acciones
✅ La instrucción:
- Guía el comportamiento del agente como una descripción del trabajo
- Especifica el rol, los activadores, las acciones y el formato de salida
- Es fundamental para el uso confiable de la herramienta
✅ El problema de la confianza:
- Brecha de descubrimiento: Fuentes no verificadas, calidad mixta
- Brecha de ejecución: No hay capacidades seguras, no hay consentimiento, no hay registro de auditoría
Pasos siguientes
En el siguiente módulo, comenzaremos a compilar la solución implementando la arquitectura basada en roles de AP2.
Creemos el primer agente y veamos la separación de roles en acción.
4. Cómo compilar el agente de Shopping: Descubrimiento basado en roles

La base de la confianza: separación de roles
En el último módulo, descubriste que un agente simple y de uso general falla en dos aspectos: no puede proporcionar un descubrimiento confiable y no puede ejecutar transacciones seguras. Ahora comenzaremos a resolver estos problemas implementando el primer principio del Protocolo de pagos de agentes: arquitectura basada en roles.
Antes de escribir cualquier código, comprendamos por qué es importante este principio.
Principio de AP2: Separación de roles
El problema con los agentes que "hacen todo"
Imagina que contratas a una persona para que sea tu asesor financiero, contador y agente de inversiones. ¿Es conveniente? Sí. ¿Es seguro? Ni loca. Tendrían lo siguiente:
- Tus objetivos de inversión (rol de asesor)
- Acceso a tus cuentas (rol de contador)
- Autoridad para transferir tu dinero (rol de intermediario)
Si esta persona se ve comprometida o comete un error, todo está en riesgo.
Solución de AP2: Un agente, un trabajo
AP2 aplica el principio de separación de problemas para crear límites de confianza:
Por qué es importante:
- ✅ Radio de explosión limitado: Si se vulnera el agente de compras, el atacante no puede acceder a las credenciales de pago.
- ✅ Privacidad: El proveedor de credenciales nunca ve tu conversación de compras.
- ✅ Cumplimiento: Es más fácil cumplir con los requisitos de PCI DSS cuando los datos de pago están aislados.
- ✅ Responsabilidad: Responsabilidad clara para cada paso
Cómo se comunican los agentes: El estado como bloc de notas compartido
Dado que los agentes no pueden acceder directamente a los datos de los demás, se comunican a través de un estado compartido. Piensa en ella como una pizarra en la que todos los agentes pueden escribir y leer:
# Shopping Agent writes:
state["intent_mandate"] = {
"natural_language_description": "Donate $50 to Room to Read",
"merchants": ["Room to Read"],
"intent_expiry": "2024-11-07T15:32:16Z",
"amount": 50.0
}
# Merchant Agent reads:
intent = state["intent_mandate"]
charity_name = intent["merchants"][0]
amount = intent["amount"]
# Creates CartMandate based on IntentMandate...
# Credentials Provider reads:
cart_mandate = state["cart_mandate"]
# Processes payment...
Así es como mantenemos los límites de confianza y, al mismo tiempo, permitimos la colaboración.
Nuestro primer agente: el agente de compras
La responsabilidad del agente de compras es simple y enfocada:
- Usa la herramienta
find_charitiespara consultar nuestra base de datos de confianza - Presentar opciones al usuario
- Usa la herramienta
save_user_choicepara crear un IntentMandate y guardarlo en el estado. - Transferencia al siguiente agente (el comercio)
Eso es todo. No se manejan pagos ni se crean carritos, solo se realiza el descubrimiento y la transferencia.
Construyámoslo paso a paso.
Paso 1: Agrega Input Validation Helper
Cuando se compilan herramientas de producción, la validación de entrada es fundamental. Creemos una función de ayuda que valide los datos de la organización benéfica antes de guardarlos en el estado.
👉 Abrir
charity_advisor/tools/charity_tools.py
Verás la función find_charities (ya completa) en la parte superior. Desplázate hacia abajo para encontrar lo siguiente:
# MODULE_4_STEP_1_ADD_VALIDATION_HELPER
👉 Reemplaza esa sola línea por lo siguiente:
def _validate_charity_data(charity_name: str, charity_ein: str, amount: float) -> tuple[bool, str]:
"""
Validates charity selection data before saving to state.
This helper function performs basic validation to ensure data quality
before it gets passed to other agents in the pipeline.
Args:
charity_name: Name of the selected charity
charity_ein: Employer Identification Number (should be format: XX-XXXXXXX)
amount: Donation amount in USD
Returns:
(is_valid, error_message): Tuple where is_valid is True if all checks pass,
and error_message contains details if validation fails
"""
# Validate charity name
if not charity_name or not charity_name.strip():
return False, "Charity name cannot be empty"
# Validate EIN format (should be XX-XXXXXXX)
if not charity_ein or len(charity_ein) != 10 or charity_ein[2] != '-':
return False, f"Invalid EIN format: {charity_ein}. Expected format: XX-XXXXXXX"
# Validate amount
if amount <= 0:
return False, f"Donation amount must be positive, got: ${amount}"
if amount > 1_000_000:
return False, f"Donation amount exceeds maximum of $1,000,000: ${amount}"
# All checks passed
return True, ""
Paso 2: Agrega el asistente para la creación de IntentMandate
Ahora, creemos el asistente que compila la estructura de IntentMandate de la AP2. Esta es una de las tres credenciales verificables en AP2.
👉 En el mismo archivo, busca lo siguiente:
# MODULE_4_STEP_2_ADD_INTENTMANDATE_CREATION_HELPER
👉 Reemplaza esa sola línea por lo siguiente:
def _create_intent_mandate(charity_name: str, charity_ein: str, amount: float) -> dict:
"""
Creates an IntentMandate - AP2's verifiable credential for user intent.
This function uses the official Pydantic model from the `ap2` package
to create a validated IntentMandate object before converting it to a dictionary.
Args:
charity_name: Name of the selected charity
charity_ein: Employer Identification Number
amount: Donation amount in USD
Returns:
Dictionary containing the IntentMandate structure per AP2 specification
"""
from datetime import datetime, timedelta, timezone
from ap2.types.mandate import IntentMandate
# Set the expiry for the intent
expiry = datetime.now(timezone.utc) + timedelta(hours=1)
# Step 1: Instantiate the Pydantic model with official AP2 fields
intent_mandate_model = IntentMandate(
user_cart_confirmation_required=True,
natural_language_description=f"Donate ${amount:.2f} to {charity_name}",
merchants=[charity_name],
skus=None,
requires_refundability=False,
intent_expiry=expiry.isoformat()
)
# Step 2: Convert the validated model to a dictionary for state storage
intent_mandate_dict = intent_mandate_model.model_dump()
# Step 3: Add the codelab's custom fields to the dictionary
timestamp = datetime.now(timezone.utc)
intent_mandate_dict.update({
"timestamp": timestamp.isoformat(),
"intent_id": f"intent_{charity_ein.replace('-', '')}_{int(timestamp.timestamp())}",
"charity_ein": charity_ein,
"amount": amount,
"currency": "USD"
})
return intent_mandate_dict
Paso 3: Compila la herramienta de transferencia de estado con IntentMandate
Ahora, compilaremos la herramienta que crea el IntentMandate y lo guarda en el estado.
👉 En el mismo archivo, desplázate hacia abajo hasta
save_user_choice
Función . Buscar:
# MODULE_4_STEP_3_COMPLETE_SAVE_TOOL
👉 Reemplaza esa sola línea por lo siguiente:
# Validate inputs before creating IntentMandate
is_valid, error_message = _validate_charity_data(charity_name, charity_ein, amount)
if not is_valid:
logger.error(f"Validation failed: {error_message}")
return {"status": "error", "message": error_message}
# Create AP2 IntentMandate using our updated helper function
intent_mandate = _create_intent_mandate(charity_name, charity_ein, amount)
# Write the IntentMandate to shared state for the next agent
tool_context.state["intent_mandate"] = intent_mandate
logger.info(f"Successfully created IntentMandate and saved to state")
logger.info(f"Intent ID: {intent_mandate['intent_id']}")
logger.info(f"Intent expires: {intent_mandate['intent_expiry']}")
# Return success confirmation
return {
"status": "success",
"message": f"Created IntentMandate: ${amount:.2f} donation to {charity_name} (EIN: {charity_ein})",
"intent_id": intent_mandate["intent_id"],
"expiry": intent_mandate["intent_expiry"]
}
Paso 4: Agrega el asistente de formato de pantalla
Antes de crear el agente, agreguemos un auxiliar más que formatee los datos de la organización benéfica para que se muestren de forma fácil de usar.
👉 Desplázate para encontrar lo siguiente:
# MODULE_4_STEP_4_ADD_FORMATTING_HELPER
👉 Reemplaza esa sola línea por lo siguiente:
def _format_charity_display(charity: dict) -> str:
"""
Formats a charity dictionary into a user-friendly display string.
This helper function demonstrates how to transform structured data
into readable text for the user.
Args:
charity: Dictionary containing charity data (name, ein, mission, rating, efficiency)
Returns:
Formatted string suitable for display to the user
"""
name = charity.get('name', 'Unknown')
ein = charity.get('ein', 'N/A')
mission = charity.get('mission', 'No mission statement available')
rating = charity.get('rating', 0.0)
efficiency = charity.get('efficiency', 0.0)
# Format efficiency as percentage
efficiency_pct = int(efficiency * 100)
# Build formatted string
display = f"""
**{name}** (EIN: {ein})
⭐ Rating: {rating}/5.0
💰 Efficiency: {efficiency_pct}% of funds go to programs
📋 Mission: {mission}
""".strip()
return display
Paso 5: Compila el agente de Shopping: importa componentes
Ahora que nuestras herramientas están completas y son sólidas, creemos el agente que las usará.
👉 Abrir
charity_advisor/shopping_agent/agent.py
Verás una plantilla con comentarios de marcador de posición. Construyámoslo paso a paso.
👉 Buscar:
# MODULE_4_STEP_5_IMPORT_COMPONENTS
👉 Reemplaza esa sola línea por lo siguiente:
from google.adk.agents import Agent
from google.adk.tools import FunctionTool
from charity_advisor.tools.charity_tools import find_charities, save_user_choice
Paso 6: Escribe la instrucción del agente
La instrucción es donde definimos la descripción del trabajo y el flujo de trabajo del agente. Esto es fundamental, ya que una instrucción mal escrita genera un comportamiento poco confiable.
👉 Buscar:
# MODULE_4_STEP_6_WRITE_INSTRUCTION
instruction="""""",
👉 Reemplaza esas dos líneas por lo siguiente:
instruction="""You are a research specialist helping users find verified charities.
Your workflow:
1. When the user describes what cause they want to support (e.g., "education", "health", "environment"),
use the find_charities tool to search our vetted database.
2. Present the results clearly. The tool returns formatted charity information that you should
show to the user.
3. When the user selects a charity and specifies an amount, use the save_user_choice tool
to create an IntentMandate and record their decision. You MUST call save_user_choice with:
- charity_name: The exact name of the chosen charity
- charity_ein: The EIN of the chosen charity
- amount: The donation amount in dollars (as a number, not a string)
4. After successfully saving, inform the user:
- That you've created an IntentMandate (mention the intent ID if provided)
- When the intent expires
- That you're passing their request to the secure payment processor
IMPORTANT BOUNDARIES:
- Your ONLY job is discovery and creating the IntentMandate
- You do NOT process payments
- You do NOT see the user's payment methods
- You do NOT create cart offers (that's the Merchant Agent's job)
- After calling save_user_choice, your work is done
WHAT IS AN INTENTMANDATE:
An IntentMandate is a structured record of what the user wants to do. It includes:
- Natural language description ("Donate $50 to Room to Read")
- Which merchants can fulfill it
- When the intent expires
- Whether user confirmation is required
This is the first of three verifiable credentials in our secure payment system.
If the user asks you to do anything related to payment processing, politely explain that
you don't have that capability and that their request will be handled by the appropriate
specialist agent.""",
Paso 7: Agrega herramientas al agente
Ahora, otorguemos acceso al agente a ambas herramientas.
👉 Buscar:
# MODULE_4_STEP_7_ADD_TOOLS
👉 Reemplaza esas dos líneas por lo siguiente:
tools=[
FunctionTool(func=find_charities),
FunctionTool(func=save_user_choice)
]
Paso 8: Verifica tu agente completo
Comprobemos que todo esté conectado correctamente.
👉 Tu completa
charity_advisor/shopping_agent/agent.py
ahora debería verse así:
"""
Shopping Agent - Finds charities from a trusted database and saves the user's choice.
This agent acts as our specialized "Research Analyst."
"""
from google.adk.agents import Agent
from google.adk.tools import FunctionTool
from charity_advisor.tools.charity_tools import find_charities, save_user_choice
shopping_agent = Agent(
name="ShoppingAgent",
model="gemini-2.5-pro",
description="Finds and recommends vetted charities from a trusted database, then creates an IntentMandate capturing the user's donation intent.",
instruction="""You are a research specialist helping users find verified charities.
Your workflow:
1. When the user describes what cause they want to support (e.g., "education", "health", "environment"),
use the find_charities tool to search our vetted database.
2. Present the results clearly. The tool returns formatted charity information that you should
show to the user.
3. When the user selects a charity and specifies an amount, use the save_user_choice tool
to create an IntentMandate and record their decision. You MUST call save_user_choice with:
- charity_name: The exact name of the chosen charity
- charity_ein: The EIN of the chosen charity
- amount: The donation amount in dollars (as a number, not a string)
4. After successfully saving, inform the user:
- That you've created an IntentMandate (mention the intent ID if provided)
- When the intent expires
- That you're passing their request to the secure payment processor
IMPORTANT BOUNDARIES:
- Your ONLY job is discovery and creating the IntentMandate
- You do NOT process payments
- You do NOT see the user's payment methods
- You do NOT create cart offers (that's the Merchant Agent's job)
- After calling save_user_choice, your work is done
WHAT IS AN INTENTMANDATE:
An IntentMandate is a structured record of what the user wants to do. It includes:
- Natural language description ("Donate $50 to Room to Read")
- Which merchants can fulfill it
- When the intent expires
- Whether user confirmation is required
This is the first of three verifiable credentials in our secure payment system.
If the user asks you to do anything related to payment processing, politely explain that
you don't have that capability and that their request will be handled by the appropriate
specialist agent.""",
tools=[
FunctionTool(func=find_charities),
FunctionTool(func=save_user_choice)
]
)
✅ ¡Perfecto! Creaste un agente de calidad de producción que cumple con AP2 con las siguientes características:
- Validación de entradas
- Creación adecuada de IntentMandate con modelos Pydantic de AP2
- Salida con formato
- Límites claros de los roles
- Instrucciones detalladas
- Manejo de errores
Paso 9: Prueba el agente de Shopping
Verifiquemos que nuestro agente funcione correctamente, cree IntentMandates adecuados y respete sus límites.
👉 En la terminal de Cloud Shell, ejecuta el siguiente comando:
adk run charity_advisor/shopping_agent
Aparecerá el mensaje de [user]:.
Prueba 1: Descubrimiento con la base de datos de confianza
👉 Tipo:
I want to donate to an education charity. What are my options?
Después de un momento, el agente te brindará una respuesta. Gracias a nuestro asistente _format_charity_display, los resultados tienen un formato atractivo:
Encontré 3 organizaciones benéficas educativas verificadas en nuestra base de datos:
Room to Read (EIN: 77-0479905)
⭐ Calificación: 4.9/5.0
💰 Eficiencia: El 88% de los fondos se destina a programas
📋 Misión: Transformar la vida de millones de niños en comunidades de bajos ingresos enfocándose en la alfabetización y la igualdad de género en la educación
Teach For America (EIN: 13-3541913)
⭐ Calificación: 4.7/5.0
💰 Eficiencia: El 81% de los fondos se destina a programas
📋 Misión: Trabaja para ampliar las oportunidades educativas de los niños que enfrentan adversidades.
Tech Education Alliance (EIN: 45-2345678)
⭐ Calificación: 4.8/5.0
💰 Eficiencia: El 92% de los fondos se destina a programas
📋 Misión: Llevar la educación informática a las escuelas con recursos insuficientes
¿A qué organización benéfica quieres apoyar y cuánto quieres donar?
Compara esto con el agente simple del módulo 3, que te proporcionó resultados de Google sin verificar. Esta es la diferencia entre el descubrimiento no confiable y el confiable.
Prueba 2: Registra la elección del usuario y crea un IntentMandate
👉 Tipo:
I'll donate $50 to Room to Read.
Deberías ver que el agente llama a la herramienta save_user_choice con los parámetros correctos. Gracias a la creación de IntentMandate con modelos de AP2, verás una respuesta más estructurada:
¡Perfecto! Creé un IntentMandate para tu donación:
Detalles de la intención:
- ID del intent: intent_774795905_1730927536
- Importe: USD 50.00 para Room to Read (EIN: 77-0479905)
- Vence el 2024-11-07T15:32:16Z (en 1 hora)
Este IntentMandate captura tu intención de donar y contiene restricciones para garantizar que la transacción se procese de forma segura. Ahora le pasaré esta información a nuestro procesador de pagos seguro para crear una oferta formal y completar tu donación.
Detrás de escena, sucedieron varias cosas:
_validate_charity_data()verificó las entradas_create_intent_mandate()usó el modelo AP2 Pydantic para compilar la estructura:
# Created and validated via Pydantic
intent_mandate_model = IntentMandate(
user_cart_confirmation_required=True,
natural_language_description="Donate $50.00 to Room to Read",
merchants=["Room to Read"],
skus=None,
requires_refundability=False,
intent_expiry="2024-11-07T15:32:16Z"
)
# Converted to dict and extended with custom fields
intent_mandate_dict = intent_mandate_model.model_dump()
intent_mandate_dict.update({
"charity_ein": "77-0479905",
"amount": 50.0,
"currency": "USD"
})
- La herramienta SOLO escribió el IntentMandate en el estado compartido:
state["intent_mandate"] = intent_mandate_dict
- Se devolvió al LLM un mensaje de éxito con detalles de la intención.
- El LLM redactó el mensaje final para el usuario.
Este IntentMandate ya está listo para que lo recoja el agente del comercio (módulo 5). El comercio extraerá el nombre y el importe de la organización benéfica del IntentMandate y validará que no haya vencido.
Prueba 3: Validación en acción
Verifiquemos que nuestro asistente de validación detecte entradas incorrectas.
👉 Tipo:
I'll donate -$25 to Room to Read.
El agente debe detectar el importe no válido:
Tuve un problema: El importe de la donación debe ser positivo. Se recibió: USD -25.0
Especifica un importe de donación positivo y crearé el IntentMandate por ti.
Esto es programación defensiva en acción. Nuestro asistente _validate_charity_data evitó que ingresaran datos no válidos en el sistema y que se creara un objeto IntentMandate con formato incorrecto.
Prueba 4: Verifica el límite de confianza
👉 Intenta pedirle al agente que procese el pago:
Now process my credit card payment.
El agente debe negarse, respetando los límites de su rol:
No tengo la capacidad de procesar pagos, ya que no es parte de mi función. Mi trabajo es ayudarte a encontrar organizaciones benéficas verificadas y crear el IntentMandate que capture tu intención de donar.
Tu IntentMandate ya se creó y se pasó a nuestro procesador de pagos seguro. El agente del comercio creará una oferta formal (CartMandate) y, luego, el proveedor de credenciales se encargará del pago real con tu consentimiento explícito.
Este es el límite de confianza en acción. El agente sabe que no está autorizado para controlar los datos de pago, y su instrucción lo guía explícitamente para que les explique esto a los usuarios y, al mismo tiempo, les enseñe sobre el concepto de IntentMandate.
👉 Presiona
Ctrl+C
para salir cuando termines de realizar pruebas.
Qué acabas de compilar
Implementaste correctamente la primera parte de la arquitectura de AP2 con la creación adecuada de IntentMandate a través de los modelos de Pydantic de AP2.
Conceptos clave dominados
✅ Arquitectura basada en roles:
- Cada agente tiene un trabajo claramente definido.
- Los agentes se comunican a través de un estado compartido, no con acceso directo
- Los límites de confianza limitan el impacto de la vulneración
✅ IntentMandate (credencial de AP2 núm. 1):
- Se creó con modelos Pydantic de AP2 oficiales para la validación
- Captura estructurada de la intención del usuario
- Incluye el vencimiento por seguridad (evita ataques de repetición)
- Especifica restricciones (comercios, reembolsos, confirmación)
- Descripción en lenguaje natural para humanos
- Legible por máquinas para los agentes
- Modelo validado antes de la conversión a diccionario
✅ Estado como memoria compartida:
tool_context.statees el "bloc de notas" al que pueden acceder todos los agentes- Escribir en el estado = poner a disposición credenciales verificables
- Lectura del estado = consumo y validación de credenciales
- Los agentes posteriores extraen lo que necesitan de las credenciales
✅ FunctionTool:
- Convierte funciones de Python en herramientas a las que se puede llamar desde el LLM
- Se basa en cadenas de documentación y sugerencias de tipos para la comprensión del LLM
- Controla la invocación automáticamente
- Componibilidad de herramientas: herramientas pequeñas y enfocadas > herramientas monolíticas
✅ Instrucciones para el agente:
- Orientación paso a paso sobre el flujo de trabajo
- Límites explícitos (“NO hagas…”)
- Especificaciones de parámetros para evitar errores
- Definiciones técnicas (qué es IntentMandate)
- Manejo de casos extremos (qué decir cuando…)
Pasos siguientes
En el próximo módulo, compilaremos el agente del comercio para recibir el IntentMandate y crear la segunda credencial verificable: CartMandate.
El agente de compras creó un IntentMandate que captura la intención del usuario con fecha de vencimiento. Ahora necesitamos un agente que lea esa credencial, valide que no haya vencido y cree una oferta formal y firmada que diga: "Yo, el comercio, respetaré este precio y entregaré estos bienes".
Creemos el agente comercial y veamos la segunda credencial de la AP2 en acción.
5. Cómo compilar el agente del comercio: Binding Offers y CartMandate

Del descubrimiento al compromiso
En el módulo anterior, creaste el agente de compras, un especialista que encuentra organizaciones benéficas verificadas y crea un IntentMandate que captura la intención del usuario. Ahora necesitamos un agente que reciba ese IntentMandate y cree una oferta formal y vinculante.
Aquí es donde entra en juego el segundo principio clave de la AP2: credenciales verificables a través de CartMandate.
Principio de la AP2: CartMandate y ofertas vinculantes
Por qué necesitamos un rol de comercio
En el módulo 4, el agente de Shopping creó un IntentMandate y lo guardó en el estado:
state["intent_mandate"] = {
"natural_language_description": "Donate $50 to Room to Read",
"merchants": ["Room to Read"],
"amount": 50.0,
"intent_expiry": "2024-11-07T15:32:16Z"
}
Pero esto es solo la intención del usuario. Antes de procesar cualquier pago, necesitamos lo siguiente:
- Una estructura de oferta formal que comprendan los sistemas de pago
- Prueba de que el comercio respetará este precio
- Un compromiso vinculante que no se puede alterar durante la transacción
- Validación de que la intención no haya vencido
Este es el trabajo del agente comercial.
¿Qué es un CartMandate?
Un objeto CartMandate es el término que usa la AP2 para referirse a un "carrito de compras digital" que funciona como una oferta vinculante. Está estructurada según el estándar PaymentRequest de W3C, lo que significa que:
- Los procesadores de pagos de todo el mundo reconocen el formato.
- Contiene todos los detalles de la transacción de forma estandarizada.
- Se puede firmar criptográficamente para demostrar su autenticidad.
Piensa en ello como una cotización escrita de un contratista:
- ❌ Verbal: "Sí, puedo hacer ese trabajo por unos cincuenta dólares".
- ✅ Cotización escrita: Costos detallados, total, firma y fecha
La cotización escrita es vinculante. El CartMandate es el equivalente digital.
Estructura de un CartMandate
Un objeto CartMandate en la AP2 tiene una estructura anidada específica:
cart_mandate = {
"contents": { # ← AP2 wrapper
"id": "cart_xyz123",
"cart_expiry": "2024-11-07T15:47:16Z",
"merchant_name": "Room to Read",
"user_cart_confirmation_required": False,
"payment_request": { # ← W3C PaymentRequest nested inside
"method_data": [...],
"details": {...},
"options": {...}
}
},
"merchant_authorization": "SIG_a3f7b2c8" # ← Merchant signature
}
Tres componentes principales:
1. contents: Es el wrapper del carrito que contiene lo siguiente:
- ID y vencimiento del carrito
- Nombre del comercio
- La API de PaymentRequest de W3C
2. payment_request (dentro de contents) - Qué se compra:
- method_data: Tipos de pago aceptados
- Detalles: Artículos y total
- Opciones: Envío, requisitos de información del pagador
3. merchant_authorization: Firma criptográfica
Firmas de comercios: Prueba de compromiso
La firma del comercio es fundamental. Prueba lo siguiente:
- Esta oferta proviene de un comercio autorizado
- El comercio se compromete a respetar este precio exacto.
- La oferta no se manipuló desde su creación.
En producción, esta sería una firma criptográfica que usa PKI (infraestructura de clave pública) o JWT (tokens web JSON). En nuestro taller educativo, simularemos esto con un hash SHA-256.
# Production (real signature):
signature = sign_with_private_key(cart_data, merchant_private_key)
# Workshop (simulated signature):
cart_hash = hashlib.sha256(cart_json.encode()).hexdigest()
signature = f"SIG_{cart_hash[:16]}"
Nuestra misión: Compilar el agente comercial
El agente del comercio hará lo siguiente:
- Leer el IntentMandate del estado (lo que escribió el agente de Shopping)
- Valida que la intención no haya vencido
- Extrae el nombre de la organización benéfica, el importe y otros detalles
- Crea una estructura de PaymentRequest compatible con W3C usando modelos Pydantic de AP2
- Encapsúlalo en CartMandate de AP2 con fecha de vencimiento
- Agrega una firma de comercio simulada
- Escribe el CartMandate en el estado del proveedor de credenciales (próximo módulo)
Construyámoslo paso a paso.
Paso 1: Agrega Expiry Validation Helper
Primero, configuremos el archivo de herramientas relacionadas con el comercio y agreguemos un asistente para validar el vencimiento de IntentMandate.
👉 Abrir
charity_advisor/tools/merchant_tools.py
Agreguemos la validación de vencimiento:
👉 Buscar:
# MODULE_5_STEP_1_ADD_EXPIRY_VALIDATION_HELPER
👉 Reemplaza esa sola línea por lo siguiente:
def _validate_intent_expiry(intent_expiry_str: str) -> tuple[bool, str]:
"""
Validates that the IntentMandate hasn't expired.
This is a critical security check - expired intents should not be processed.
Args:
intent_expiry_str: The ISO 8601 timestamp string from the IntentMandate.
Returns:
(is_valid, error_message): Tuple indicating if intent is still valid.
"""
try:
# The .replace('Z', '+00:00') is for compatibility with older Python versions
expiry_time = datetime.fromisoformat(intent_expiry_str.replace('Z', '+00:00'))
now = datetime.now(timezone.utc)
if expiry_time < now:
return False, f"IntentMandate expired at {intent_expiry_str}"
time_remaining = expiry_time - now
logger.info(f"IntentMandate valid. Expires in {time_remaining.total_seconds():.0f} seconds")
return True, ""
except (ValueError, TypeError) as e:
return False, f"Invalid intent_expiry format: {e}"
Paso 2: Agrega el asistente de generación de firmas
Ahora, creemos un asistente que genere la firma simulada del comercio.
👉 Buscar:
# MODULE_5_STEP_2_ADD_SIGNATURE_HELPER
👉 Reemplaza esa sola línea por lo siguiente:
def _generate_merchant_signature(cart_contents: CartContents) -> str:
"""
Generates a simulated merchant signature for the CartMandate contents.
In production, this would use PKI or JWT with the merchant's private key.
For this codelab, we use a SHA-256 hash of the sorted JSON representation.
Args:
cart_contents: The Pydantic model of the cart contents to sign.
Returns:
Simulated signature string (format: "SIG_" + first 16 chars of hash).
"""
# Step 1: Dump the Pydantic model to a dictionary. The `mode='json'` argument
# ensures that complex types like datetimes are serialized correctly.
cart_contents_dict = cart_contents.model_dump(mode='json')
# Step 2: Use the standard json library to create a stable, sorted JSON string.
# separators=(',', ':') removes whitespace for a compact and canonical representation.
cart_json = json.dumps(cart_contents_dict, sort_keys=True, separators=(',', ':'))
# Step 3: Generate SHA-256 hash.
cart_hash = hashlib.sha256(cart_json.encode('utf-8')).hexdigest()
# Step 4: Create signature in a recognizable format.
signature = f"SIG_{cart_hash[:16]}"
logger.info(f"Generated merchant signature: {signature}")
return signature
Paso 3A: Crea la firma y la configuración de la herramienta
Ahora comencemos a compilar la herramienta principal. La crearemos de forma incremental en cuatro subpasos. Primero, la firma de la función y la configuración inicial.
👉 Buscar:
# MODULE_5_STEP_3A_CREATE_TOOL_SIGNATURE
👉 Reemplaza esa sola línea por lo siguiente:
async def create_cart_mandate(tool_context: Any) -> Dict[str, Any]:
"""
Creates a W3C PaymentRequest-compliant CartMandate from the IntentMandate.
This tool reads the IntentMandate from shared state, validates it, and
creates a formal, signed offer using the official AP2 Pydantic models.
Returns:
Dictionary containing status and the created CartMandate.
"""
logger.info("Tool called: Creating CartMandate from IntentMandate")
# MODULE_5_STEP_3B_ADD_VALIDATION_LOGIC
Paso 3B: Agrega lógica de validación
Ahora agreguemos la lógica para leer y validar el IntentMandate con los modelos AP2 de Pydantic, y extraer los datos que necesitamos.
👉 Buscar:
# MODULE_5_STEP_3B_ADD_VALIDATION_LOGIC
👉 Reemplaza esa sola línea por lo siguiente:
# 1. Read IntentMandate dictionary from state
intent_mandate_dict = tool_context.state.get("intent_mandate")
if not intent_mandate_dict:
logger.error("No IntentMandate found in state")
return {
"status": "error",
"message": "No IntentMandate found. Shopping Agent must create intent first."
}
# 2. Parse dictionary into a validated Pydantic model
try:
intent_mandate_model = IntentMandate.model_validate(intent_mandate_dict)
except Exception as e:
logger.error(f"Could not validate IntentMandate structure: {e}")
return {"status": "error", "message": f"Invalid IntentMandate structure: {e}"}
# 3. Validate that the intent hasn't expired (CRITICAL security check)
is_valid, error_message = _validate_intent_expiry(intent_mandate_model.intent_expiry)
if not is_valid:
logger.error(f"IntentMandate validation failed: {error_message}")
return {"status": "error", "message": error_message}
# 4. Extract data. Safely access standard fields from the model, and
# custom fields (like 'amount') from the original dictionary.
charity_name = intent_mandate_model.merchants[0] if intent_mandate_model.merchants else "Unknown Charity"
amount = intent_mandate_dict.get("amount", 0.0)
# MODULE_5_STEP_3C_CREATE_CARTMANDATE_STRUCTURE
Paso 3C: Crea la estructura de CartMandate
Ahora, compilaremos la estructura de PaymentRequest compatible con W3C y la encapsularemos en el AP2 CartMandate con modelos de Pydantic.
👉 Buscar:
# MODULE_5_STEP_3C_CREATE_CARTMANDATE_STRUCTURE
👉 Reemplaza esa sola línea por lo siguiente:
# 5. Build the nested Pydantic models for the CartMandate
timestamp = datetime.now(timezone.utc)
cart_id = f"cart_{hashlib.sha256(f'{charity_name}{timestamp.isoformat()}'.encode()).hexdigest()[:12]}"
cart_expiry = timestamp + timedelta(minutes=15)
payment_request_model = PaymentRequest(
method_data=[PaymentMethodData(
supported_methods="CARD",
data={"supported_networks": ["visa", "mastercard", "amex"], "supported_types": ["debit", "credit"]}
)],
details=PaymentDetailsInit(
id=f"order_{cart_id}",
display_items=[PaymentItem(
label=f"Donation to {charity_name}",
amount=PaymentCurrencyAmount(currency="USD", value=amount) # Pydantic v2 handles float -> str conversion
)],
total=PaymentItem(
label="Total Donation",
amount=PaymentCurrencyAmount(currency="USD", value=amount)
)
),
options=PaymentOptions(request_shipping=False)
)
cart_contents_model = CartContents(
id=cart_id,
cart_expiry=cart_expiry.isoformat(),
merchant_name=charity_name,
user_cart_confirmation_required=False,
payment_request=payment_request_model
)
# MODULE_5_STEP_3D_ADD_SIGNATURE_AND_SAVE
Paso 3D: Agrega la firma y guarda el estado
Por último, firmemos el CartMandate con nuestro modelo de Pydantic y guardémoslo en el estado para el siguiente agente.
👉 Buscar:
# MODULE_5_STEP_3D_ADD_SIGNATURE_AND_SAVE
👉 Reemplaza esa sola línea por lo siguiente:
# 6. Generate signature from the validated Pydantic model
signature = _generate_merchant_signature(cart_contents_model)
# 7. Create the final CartMandate model, now including the signature
cart_mandate_model = CartMandate(
contents=cart_contents_model,
merchant_authorization=signature
)
# 8. Convert the final model to a dictionary for state storage and add the custom timestamp
cart_mandate_dict = cart_mandate_model.model_dump(mode='json')
cart_mandate_dict["timestamp"] = timestamp.isoformat()
# 9. Write the final dictionary to state
tool_context.state["cart_mandate"] = cart_mandate_dict
logger.info(f"CartMandate created successfully: {cart_id}")
return {
"status": "success",
"message": f"Created signed CartMandate {cart_id} for ${amount:.2f} donation to {charity_name}",
"cart_id": cart_id,
"cart_expiry": cart_expiry.isoformat(),
"signature": signature
}
Paso 4: Compila el agente del comercio: importa componentes
Ahora, creemos el agente que usará esta herramienta.
👉 Abrir
charity_advisor/merchant_agent/agent.py
Verás una plantilla con marcadores de posición. Comencemos por importar lo que necesitamos.
👉 Buscar:
# MODULE_5_STEP_4_IMPORT_COMPONENTS
👉 Reemplaza esa sola línea por lo siguiente:
from google.adk.agents import Agent
from google.adk.tools import FunctionTool
from charity_advisor.tools.merchant_tools import create_cart_mandate
Paso 5: Escribe la instrucción del agente comercial
Ahora, escribamos la instrucción que le indica al agente cuándo y cómo usar su herramienta.
👉 Buscar:
# MODULE_5_STEP_5_WRITE_INSTRUCTION
instruction="""""",
👉 Reemplaza esas dos líneas por lo siguiente:
instruction="""You are a merchant specialist responsible for creating formal, signed offers (CartMandates).
Your workflow:
1. Read the IntentMandate from shared state.
The IntentMandate was created by the Shopping Agent and contains:
- merchants: List of merchant names
- amount: Donation amount
- charity_ein: Tax ID
- intent_expiry: When the intent expires
2. Use the create_cart_mandate tool to create a W3C PaymentRequest-compliant CartMandate.
This tool will:
- Validate the IntentMandate hasn't expired (CRITICAL security check)
- Extract the charity name and amount from the IntentMandate
- Create a structured offer with payment methods, transaction details, and merchant info
- Generate a merchant signature to prove authenticity
- Save the CartMandate to state for the payment processor
3. After creating the CartMandate, inform the user:
- That you've created a formal, signed offer
- The cart ID
- When the cart expires (15 minutes)
- That you're passing it to the secure payment processor
IMPORTANT BOUNDARIES:
- Your ONLY job is creating signed CartMandates from valid IntentMandates
- You do NOT process payments
- You do NOT see the user's payment methods or credentials
- You do NOT interact with payment networks
- You MUST validate that the IntentMandate hasn't expired before creating a cart
- After calling create_cart_mandate, your work is done
WHAT IS A CARTMANDATE:
A CartMandate is a binding commitment that says:
"I, the merchant, commit to accepting $X for this charity donation, and I prove it with my signature."
This commitment is structured using the W3C PaymentRequest standard and includes:
- Payment methods accepted (card, bank transfer)
- Transaction details (amount, charity name)
- Cart expiry (15 minutes from creation)
- Merchant signature (proof of commitment)
This is the second of three verifiable credentials in our secure payment system.""",
Paso 6: Agrega herramientas al agente de Merchant
👉 Buscar:
# MODULE_5_STEP_6_ADD_TOOLS
tools=[],
👉 Reemplaza esas dos líneas por lo siguiente:
tools=[
FunctionTool(func=create_cart_mandate)
],
Paso 7: Verifica el agente de comercio completo
Confirmemos que todo esté conectado correctamente.
👉 Tu completa
charity_advisor/merchant_agent/agent.py
ahora debería verse así:
"""
Merchant Agent - Creates W3C-compliant CartMandates with merchant signatures.
This agent acts as our "Contract Creator."
"""
from google.adk.agents import Agent
from google.adk.tools import FunctionTool
from charity_advisor.tools.merchant_tools import create_cart_mandate
merchant_agent = Agent(
name="MerchantAgent",
model="gemini-2.5-flash",
description="Creates formal, signed CartMandates for charity donations following W3C PaymentRequest standards.",
tools=[
FunctionTool(func=create_cart_mandate)
],
instruction="""You are a merchant specialist responsible for creating formal, signed offers (CartMandates).
Your workflow:
1. Read the IntentMandate from shared state.
The IntentMandate was created by the Shopping Agent and contains:
- merchants: List of merchant names
- amount: Donation amount
- charity_ein: Tax ID
- intent_expiry: When the intent expires
2. Use the create_cart_mandate tool to create a W3C PaymentRequest-compliant CartMandate.
This tool will:
- Validate the IntentMandate hasn't expired (CRITICAL security check)
- Extract the charity name and amount from the IntentMandate
- Create a structured offer with payment methods, transaction details, and merchant info
- Generate a merchant signature to prove authenticity
- Save the CartMandate to state for the payment processor
3. After creating the CartMandate, inform the user:
- That you've created a formal, signed offer
- The cart ID
- When the cart expires (15 minutes)
- That you're passing it to the secure payment processor
IMPORTANT BOUNDARIES:
- Your ONLY job is creating signed CartMandates from valid IntentMandates
- You do NOT process payments
- You do NOT see the user's payment methods or credentials
- You do NOT interact with payment networks
- You MUST validate that the IntentMandate hasn't expired before creating a cart
- After calling create_cart_mandate, your work is done
WHAT IS A CARTMANDATE:
A CartMandate is a binding commitment that says:
"I, the merchant, commit to accepting $X for this charity donation, and I prove it with my signature."
This commitment is structured using the W3C PaymentRequest standard and includes:
- Payment methods accepted (card, bank transfer)
- Transaction details (amount, charity name)
- Cart expiry (15 minutes from creation)
- Merchant signature (proof of commitment)
This is the second of three verifiable credentials in our secure payment system."""
)
✅ Punto de control: Ahora tienes un agente comercial completo con la creación adecuada de CartMandate de AP2 con modelos de Pydantic.
Paso 8: Prueba el agente comercial
Ahora, verifiquemos que nuestro agente cree correctamente CartMandates con firmas y valide la fecha de vencimiento.
Configuración de la prueba: Ejecuta la secuencia de comandos de prueba
👉 En la terminal de Cloud Shell, ejecuta el siguiente comando:
python scripts/test_merchant.py
Resultado esperado:
======================================================================
MERCHANT AGENT TEST
======================================================================
Simulated IntentMandate from Shopping Agent:
charity: Room to Read
amount: $50.00
expiry: 2024-11-07T16:32:16Z
----------------------------------------------------------------------
Merchant Agent Response:
----------------------------------------------------------------------
Perfect! I've received your IntentMandate and created a formal, signed offer (CartMandate) for your donation.
**CartMandate Details:**
- **Cart ID**: cart_3b4c5d6e7f8a
- **Donation Amount**: $50.00 to Room to Read
- **Payment Methods Accepted**: Credit/debit cards (Visa, Mastercard, Amex) or bank transfer
- **Cart Expires**: 2024-11-07T15:47:16Z (in 15 minutes)
- **Merchant Signature**: SIG_a3f7b2c8d9e1f4a2
This signed CartMandate proves my commitment to accept this donation amount. I'm now passing this to the secure payment processor to complete your transaction.
======================================================================
CARTMANDATE CREATED:
======================================================================
ID: cart_3b4c5d6e7f8a
Amount: 50.00
Merchant: Room to Read
Expires: 2024-11-07T15:47:16Z
Signature: SIG_a3f7b2c8d9e1f4a2
======================================================================
Prueba 2: Verifica el cumplimiento de W3C
Validemos que nuestra estructura CartMandate cumpla con los estándares de AP2 y PaymentRequest de W3C.
👉 Ejecuta la secuencia de comandos de validación:
python scripts/validate_cartmandate.py
Resultado esperado:
======================================================================
AP2 & W3C PAYMENTREQUEST VALIDATION
======================================================================
✅ CartMandate is AP2 and W3C PaymentRequest compliant
Structure validation passed:
✓ AP2 'contents' wrapper present
✓ AP2 'merchant_authorization' signature present
✓ cart_expiry present
✓ payment_request nested inside contents
✓ method_data present and valid
✓ details.total.amount present with currency and value
✓ All required W3C PaymentRequest fields present
======================================================================
Qué acabas de compilar
Implementaste correctamente CartMandate de la AP2 con modelos de Pydantic para garantizar la estructura adecuada, la validación del vencimiento y las firmas del comercio.
Conceptos clave dominados
✅ CartMandate (credencial de AP2 núm. 2):
- Se creó con los modelos oficiales de AP2 Pydantic
- Estructura de AP2 con contenedor de contenido
- PaymentRequest de W3C anidado dentro
- Vencimiento del carrito (más corto que la intención)
- Firma del comercio para el compromiso vinculante
- La validación del modelo garantiza el cumplimiento de las especificaciones
✅ Validación de vencimiento:
- Cómo leer IntentMandate desde el estado
- Validación de la estructura con
IntentMandate.model_validate() - Cómo analizar marcas de tiempo ISO 8601
- Comparación con la hora actual
- Función de seguridad que evita el procesamiento obsoleto
✅ Firma del comercio:
- Demuestra autenticidad y compromiso
- Se genera a partir de un modelo Pydantic validado
- Usa
model_dump(mode='json')para la representación canónica - Simulada con SHA-256 para fines educativos
- La producción usa PKI/JWT
- Firma el modelo de contenido, no los diccionarios.
✅ W3C PaymentRequest:
- Se compila con el modelo Pydantic PaymentRequest de la AP2
- Estándar de la industria para los datos de pago
- Anidado dentro de la estructura de AP2
- Contiene method_data, details y options
- Permite la interoperabilidad
✅ Cadena de credenciales con modelos:
- Shopping → IntentMandate (validado)
- El comercio lee IntentMandate → CartMandate (ambos modelos validados)
- El proveedor de credenciales leerá CartMandate → PaymentMandate
- Cada paso valida la credencial anterior con Pydantic.
✅ Desarrollo basado en modelos:
- Validación de entrada a través de
model_validate() - Construcción con seguridad de tipos
- Serialización automática a través de
model_dump() - Patrones listos para producción
Pasos siguientes
En el próximo módulo, compilaremos el proveedor de credenciales para procesar pagos de forma segura.
El agente del comercio creó una oferta vinculante con vencimiento usando modelos de AP2. Ahora necesitamos un agente que lea ese CartMandate, obtenga el consentimiento del usuario y ejecute el pago.
Compilaremos el proveedor de credenciales y completaremos la cadena de credenciales de AP2.
6. Cómo compilar el proveedor de credenciales: Ejecución de pagos seguros

Desde la oferta vinculante hasta la ejecución del pago
En el módulo 5, creaste el agente del comercio, un especialista que lee IntentMandates, valida que no hayan vencido y crea CartMandates vinculantes con firmas del comercio. Ahora necesitamos un agente para recibir ese CartMandate y ejecutar el pago real.
Aquí es donde entra en juego el tercer y último principio de la AP2: ejecución segura de pagos a través de PaymentMandate.
Principio de la AP2: Mandato de pago y ejecución del pago
Por qué necesitamos un rol de proveedor de credenciales
En el módulo 5, el agente de Merchant creó un objeto CartMandate y lo guardó en el estado:
state["cart_mandate"] = {
"contents": {
"id": "cart_abc123",
"cart_expiry": "2025-11-07:15:47:16Z",
"payment_request": {
"details": {
"total": {
"amount": {"currency": "USD", "value": "50.00"}
}
}
}
},
"merchant_authorization": "SIG_a3f7b2c8"
}
Sin embargo, esta es solo una oferta vinculante. Antes de que se pueda ejecutar el pago, necesitamos lo siguiente:
- Validación de que el carrito no haya vencido
- Consentimiento del usuario para proceder con el pago
- Es una credencial que autoriza la ejecución del pago.
- Procesamiento de pagos real (o simulación para nuestro taller)
Este es el trabajo del proveedor de credenciales.
¿Qué es un PaymentMandate?
Un PaymentMandate es el término que usa AP2 para la autorización final que permite que se ejecute el pago. Es la tercera y última credencial verificable de la cadena de AP2.
Piensa en las tres credenciales como un proceso de firma de contrato:
- IntentMandate: "Me interesa comprar esto" (carta de intención)
- CartMandate: "Yo, el comercio, ofrezco vender a este precio" (cotización escrita)
- PaymentMandate: "Autorizo a que se cobre mi forma de pago" (contrato firmado)
El pago solo se puede ejecutar después de que existan las tres credenciales.
Estructura de un objeto PaymentMandate
Un objeto PaymentMandate en la AP2 tiene una estructura específica:
payment_mandate = {
"payment_mandate_contents": { # ← AP2 wrapper
"payment_mandate_id": "payment_xyz123",
"payment_details_id": "cart_abc123", # Links to CartMandate
"user_consent": True,
"consent_timestamp": "2025-11-07T15:48:00Z",
"amount": {
"currency": "USD",
"value": "50.00"
},
"merchant_name": "Room to Read"
},
"agent_present": True, # Human-in-the-loop flow
"timestamp": "2025-11-07T15:48:00Z"
}
Componentes clave:
1. payment_mandate_contents: Es el wrapper de autorización que contiene lo siguiente:
- payment_mandate_id: Identificador único
- payment_details_id: Vincula a CartMandate
- user_consent: Indica si el usuario aprobó el consentimiento.
- importe: Importe del pago (extraído de CartMandate)
2. agent_present: Indica si se trata de un flujo con interacción humana.
3. timestamp: Fecha y hora en que se creó la autorización
Nuestra misión: Crear el proveedor de credenciales
El proveedor de credenciales hará lo siguiente:
- Leer el CartMandate del estado (lo que escribió el agente del comercio)
- Valida que el carrito no haya vencido con los modelos AP2 de Pydantic
- Extrae los detalles de pago de la estructura anidada
- Crea un PaymentMandate con el consentimiento del usuario a través de los modelos de la AP2
- Simula el procesamiento de pagos (en producción, llamaría a la API de pagos real)
- Escribe el PaymentMandate y el resultado del pago en el estado
Construyámoslo paso a paso.
Paso 1: Agrega el asistente de validación de vencimiento del carrito
Primero, creemos un asistente que valide que CartMandate no haya vencido, al igual que validamos el vencimiento de IntentMandate en el Módulo 5.
👉 Abrir
charity_advisor/tools/payment_tools.py
Agreguemos la validación de vencimiento:
👉 Buscar:
# MODULE_6_STEP_1_ADD_CART_EXPIRY_VALIDATION_HELPER
👉 Reemplaza esa sola línea por lo siguiente:
def _validate_cart_expiry(cart: CartMandate) -> tuple[bool, str]:
"""
Validates that the CartMandate hasn't expired.
This is a critical security check - expired carts should not be processed.
Args:
cart: The Pydantic CartMandate model to validate.
Returns:
(is_valid, error_message): Tuple indicating if cart is still valid.
"""
try:
expiry_str = cart.contents.cart_expiry
expiry_time = datetime.fromisoformat(expiry_str.replace('Z', '+00:00'))
now = datetime.now(timezone.utc)
if expiry_time < now:
return False, f"CartMandate expired at {expiry_str}"
time_remaining = expiry_time - now
logger.info(f"CartMandate valid. Expires in {time_remaining.total_seconds():.0f} seconds")
return True, ""
except (ValueError, TypeError, AttributeError) as e:
return False, f"Invalid cart_expiry format or structure: {e}"
Paso 2: Agrega PaymentMandate Creation Helper
Ahora, creemos un asistente que compile la estructura PaymentMandate con los modelos oficiales de AP2 Pydantic.
👉 Buscar:
# MODULE_6_STEP_2_ADD_PAYMENT_MANDATE_CREATION_HELPER
👉 Reemplaza esa sola línea por lo siguiente:
def _create_payment_mandate(cart: CartMandate, consent_granted: bool) -> dict:
"""
Creates a PaymentMandate using the official AP2 Pydantic models.
It links to the CartMandate and includes user consent status.
Args:
cart: The validated Pydantic CartMandate model being processed.
consent_granted: Whether the user has consented to the payment.
Returns:
A dictionary representation of the final, validated PaymentMandate.
"""
timestamp = datetime.now(timezone.utc)
# Safely extract details from the validated CartMandate model
cart_id = cart.contents.id
merchant_name = cart.contents.merchant_name
total_item = cart.contents.payment_request.details.total
# Create the nested PaymentResponse model for the mandate
payment_response_model = PaymentResponse(
request_id=cart_id,
method_name="CARD", # As per the simulated flow
details={"token": "simulated_payment_token_12345"}
)
# Create the PaymentMandateContents model
payment_mandate_contents_model = PaymentMandateContents(
payment_mandate_id=f"payment_{hashlib.sha256(f'{cart_id}{timestamp.isoformat()}'.encode()).hexdigest()[:12]}",
payment_details_id=cart_id,
payment_details_total=total_item,
payment_response=payment_response_model,
merchant_agent=merchant_name,
timestamp=timestamp.isoformat()
)
# Create the top-level PaymentMandate model
# In a real system, a user signature would be added to this model
payment_mandate_model = PaymentMandate(
payment_mandate_contents=payment_mandate_contents_model
)
# Convert the final Pydantic model to a dictionary for state storage
final_dict = payment_mandate_model.model_dump(mode='json')
# Add any custom/non-standard fields required by the codelab's logic to the dictionary
# The spec does not have these fields, but your original code did. We add them
# back to ensure compatibility with later steps.
final_dict['payment_mandate_contents']['user_consent'] = consent_granted
final_dict['payment_mandate_contents']['consent_timestamp'] = timestamp.isoformat() if consent_granted else None
final_dict['agent_present'] = True
return final_dict
Paso 3A: Crea la firma y la configuración de la herramienta
Ahora comencemos a compilar la herramienta principal de forma incremental. Primero, la firma de la función y la configuración inicial.
👉 Buscar:
# MODULE_6_STEP_3A_CREATE_TOOL_SIGNATURE
👉 Reemplaza esa sola línea por lo siguiente:
async def create_payment_mandate(tool_context: Any) -> Dict[str, Any]:
"""
Creates a PaymentMandate and simulates payment processing using Pydantic models.
This tool now reads the CartMandate from state, parses it into a validated model,
and creates a spec-compliant PaymentMandate.
"""
logger.info("Tool called: Creating PaymentMandate and processing payment")
# MODULE_6_STEP_3B_VALIDATE_CARTMANDATE
Paso 3B: Valida CartMandate
Ahora agreguemos la lógica para leer y validar el CartMandate con los modelos AP2 de Pydantic, y para verificar la fecha de vencimiento.
👉 Buscar:
# MODULE_6_STEP_3B_VALIDATE_CARTMANDATE
👉 Reemplaza esa sola línea por lo siguiente:
# 1. Read CartMandate dictionary from state
cart_mandate_dict = tool_context.state.get("cart_mandate")
if not cart_mandate_dict:
logger.error("No CartMandate found in state")
return { "status": "error", "message": "No CartMandate found. Merchant Agent must create cart first." }
# 2. Parse dictionary into a validated Pydantic model
try:
cart_model = CartMandate.model_validate(cart_mandate_dict)
except Exception as e:
logger.error(f"Could not validate CartMandate structure: {e}")
return {"status": "error", "message": f"Invalid CartMandate structure: {e}"}
# 3. Validate that the cart hasn't expired using the Pydantic model
is_valid, error_message = _validate_cart_expiry(cart_model)
if not is_valid:
logger.error(f"CartMandate validation failed: {error_message}")
return {"status": "error", "message": error_message}
# MODULE_6_STEP_3C_EXTRACT_PAYMENT_DETAILS
Paso 3C: Extrae los detalles de pago de la estructura anidada
Ahora, naveguemos por el modelo CartMandate validado para extraer los detalles de pago que necesitamos.
👉 Buscar:
# MODULE_6_STEP_3C_EXTRACT_PAYMENT_DETAILS
👉 Reemplaza esa sola línea por lo siguiente:
# 4. Safely extract data from the validated model
cart_id = cart_model.contents.id
merchant_name = cart_model.contents.merchant_name
amount_value = cart_model.contents.payment_request.details.total.amount.value
currency = cart_model.contents.payment_request.details.total.amount.currency
consent_granted = True # Assume consent for this codelab flow
# MODULE_6_STEP_3D_CREATE_PAYMENTMANDATE_AND_SIMULATE
Paso 3D: Crea un PaymentMandate y simula un pago
Por último, crearemos el PaymentMandate con nuestro asistente basado en Pydantic, simularemos el procesamiento del pago y guardaremos todo en el estado.
👉 Buscar:
# MODULE_6_STEP_3D_CREATE_PAYMENTMANDATE_AND_SIMULATE
👉 Reemplaza esa sola línea por lo siguiente:
# 5. Create the spec-compliant PaymentMandate using the validated CartMandate model
payment_mandate_dict = _create_payment_mandate(cart_model, consent_granted)
# 6. Simulate payment processing
transaction_id = f"txn_{hashlib.sha256(f'{cart_id}{datetime.now(timezone.utc).isoformat()}'.encode()).hexdigest()[:16]}"
payment_result = {
"transaction_id": transaction_id,
"status": "completed",
"amount": amount_value,
"currency": currency,
"merchant": merchant_name,
"timestamp": datetime.now(timezone.utc).isoformat(),
"simulation": True
}
# 7. Write the compliant PaymentMandate dictionary and result to state
tool_context.state["payment_mandate"] = payment_mandate_dict
tool_context.state["payment_result"] = payment_result
logger.info(f"Payment processed successfully: {transaction_id}")
return {
"status": "success",
"message": f"Payment of {currency} {amount_value:.2f} to {merchant_name} processed successfully",
"transaction_id": transaction_id,
"payment_mandate_id": payment_mandate_dict["payment_mandate_contents"]["payment_mandate_id"]
}
Paso 4: Compila el agente de Credentials Provider: importa componentes
Ahora, creemos el agente que usa esta herramienta.
👉 Abrir
charity_advisor/credentials_provider/agent.py
Verás una plantilla con marcadores de posición. Comencemos por importar lo que necesitamos.
👉 Buscar:
# MODULE_6_STEP_4_IMPORT_COMPONENTS
👉 Reemplaza esa sola línea por lo siguiente:
from google.adk.agents import Agent
from google.adk.tools import FunctionTool
from charity_advisor.tools.payment_tools import create_payment_mandate
Paso 5: Escribe la instrucción del proveedor de credenciales
Ahora, escribamos la instrucción que guiará al agente.
👉 Buscar:
# MODULE_6_STEP_5_WRITE_INSTRUCTION
instruction="""""",
👉 Reemplaza esas dos líneas por lo siguiente:
instruction="""You are a payment specialist responsible for securely processing payments with user consent.
Your workflow:
1. Read the CartMandate from shared state.
The CartMandate was created by the Merchant Agent and has this structure:
- contents: AP2 wrapper containing:
- id: Cart identifier
- cart_expiry: When the cart expires
- merchant_name: Who is receiving payment
- payment_request: W3C PaymentRequest with transaction details
- merchant_authorization: Merchant's signature
2. Extract payment details from the nested structure:
- Navigate: cart_mandate["contents"]["payment_request"]["details"]["total"]["amount"]
- This gives you the currency and value
3. **IMPORTANT - Two-Turn Conversational Confirmation Pattern:**
Before calling create_payment_mandate, you MUST:
- Present the payment details clearly to the user
- Ask explicitly: "I'm ready to process a payment of $X to [Charity Name]. Do you want to proceed with this donation?"
- WAIT for the user's explicit confirmation (e.g., "yes", "proceed", "confirm")
- ONLY call create_payment_mandate AFTER receiving explicit confirmation
- If user says "no" or "cancel", DO NOT call the tool
4. After user confirms, use the create_payment_mandate tool to:
- Validate the CartMandate hasn't expired (CRITICAL security check)
- Create a PaymentMandate (the third AP2 credential)
- Simulate payment processing
- Record the transaction result
5. After processing, inform the user:
- That payment was processed successfully (this is a simulation)
- The transaction ID
- The amount and merchant
- That this completes the three-agent AP2 credential chain
IMPORTANT BOUNDARIES:
- Your ONLY job is creating PaymentMandates and processing payments
- You do NOT discover charities (that's Shopping Agent's job)
- You do NOT create offers (that's Merchant Agent's job)
- You MUST validate that the CartMandate hasn't expired before processing
- You MUST get explicit user confirmation before calling create_payment_mandate
- In production, this consent mechanism would be even more robust
WHAT IS A PAYMENTMANDATE:
A PaymentMandate is the final credential that authorizes payment execution. It:
- Links to the CartMandate (proving the merchant's offer)
- Records user consent
- Contains payment details extracted from the CartMandate
- Enables the actual payment transaction
This is the third and final verifiable credential in our secure payment system.
THE COMPLETE AP2 CREDENTIAL CHAIN:
1. Shopping Agent creates IntentMandate (user's intent)
2. Merchant Agent reads IntentMandate, creates CartMandate (merchant's binding offer)
3. You read CartMandate, get user confirmation, create PaymentMandate (authorized payment execution)
Each credential:
- Has an expiry time (security feature)
- Links to the previous credential
- Is validated before the next step
- Creates an auditable chain of trust""",
Paso 6: Agrega herramientas al proveedor de credenciales
👉 Buscar:
# MODULE_6_STEP_6_ADD_TOOLS
tools=[],
👉 Reemplaza esas dos líneas por lo siguiente:
tools=[
FunctionTool(func=create_payment_mandate)
],
Paso 7: Verifica el proveedor de credenciales completo
Confirmemos que todo esté conectado correctamente.
👉 Tu completa
charity_advisor/credentials_provider/agent.py
ahora debería verse así:
"""
Credentials Provider Agent - Handles payment processing with user consent.
This agent acts as our "Payment Processor."
"""
from google.adk.agents import Agent
from google.adk.tools import FunctionTool
from charity_advisor.tools.payment_tools import create_payment_mandate
credentials_provider = Agent(
name="CredentialsProvider",
model="gemini-2.5-flash",
description="Securely processes payments by creating PaymentMandates and executing transactions with user consent.",
tools=[
FunctionTool(func=create_payment_mandate)
],
instruction="""You are a payment specialist responsible for securely processing payments with user consent.
Your workflow:
1. Read the CartMandate from shared state.
The CartMandate was created by the Merchant Agent and has this structure:
- contents: AP2 wrapper containing:
- id: Cart identifier
- cart_expiry: When the cart expires
- merchant_name: Who is receiving payment
- payment_request: W3C PaymentRequest with transaction details
- merchant_authorization: Merchant's signature
2. Extract payment details from the nested structure:
- Navigate: cart_mandate["contents"]["payment_request"]["details"]["total"]["amount"]
- This gives you the currency and value
3. **IMPORTANT - Two-Turn Conversational Confirmation Pattern:**
Before calling create_payment_mandate, you MUST:
- Present the payment details clearly to the user
- Ask explicitly: "I'm ready to process a payment of $X to [Charity Name]. Do you want to proceed with this donation?"
- WAIT for the user's explicit confirmation (e.g., "yes", "proceed", "confirm")
- ONLY call create_payment_mandate AFTER receiving explicit confirmation
- If user says "no" or "cancel", DO NOT call the tool
4. After user confirms, use the create_payment_mandate tool to:
- Validate the CartMandate hasn't expired (CRITICAL security check)
- Create a PaymentMandate (the third AP2 credential)
- Simulate payment processing
- Record the transaction result
5. After processing, inform the user:
- That payment was processed successfully (this is a simulation)
- The transaction ID
- The amount and merchant
- That this completes the three-agent AP2 credential chain
IMPORTANT BOUNDARIES:
- Your ONLY job is creating PaymentMandates and processing payments
- You do NOT discover charities (that's Shopping Agent's job)
- You do NOT create offers (that's Merchant Agent's job)
- You MUST validate that the CartMandate hasn't expired before processing
- You MUST get explicit user confirmation before calling create_payment_mandate
- In production, this consent mechanism would be even more robust
WHAT IS A PAYMENTMANDATE:
A PaymentMandate is the final credential that authorizes payment execution. It:
- Links to the CartMandate (proving the merchant's offer)
- Records user consent
- Contains payment details extracted from the CartMandate
- Enables the actual payment transaction
This is the third and final verifiable credential in our secure payment system.
THE COMPLETE AP2 CREDENTIAL CHAIN:
1. Shopping Agent creates IntentMandate (user's intent)
2. Merchant Agent reads IntentMandate, creates CartMandate (merchant's binding offer)
3. You read CartMandate, get user confirmation, create PaymentMandate (authorized payment execution)
Each credential:
- Has an expiry time (security feature)
- Links to the previous credential
- Is validated before the next step
- Creates an auditable chain of trust"""
)
✅ Punto de control: Ahora tienes un proveedor de credenciales completo con la lectura adecuada de CartMandate y la creación de PaymentMandate con los modelos AP2 de Pydantic.
Paso 8: Prueba el proveedor de credenciales
Ahora, verifiquemos que nuestro agente procese los pagos correctamente y complete la cadena de credenciales.
👉 En la terminal de Cloud Shell, ejecuta el siguiente comando:
python scripts/test_credentials_provider.py
Resultado esperado:
======================================================================
CREDENTIALS PROVIDER TEST (MOCK - NO CONFIRMATION)
======================================================================
Simulated CartMandate from Merchant Agent:
- Cart ID: cart_test123
- Merchant: Room to Read
- Amount: $50.00
- Expires: 2025-11-07T15:47:16Z
- Signature: SIG_test_signature
Calling Credentials Provider to process payment...
======================================================================
INFO:charity_advisor.tools.payment_tools:Tool called: Creating PaymentMandate and processing payment
INFO:charity_advisor.tools.payment_tools:CartMandate valid. Expires in 900 seconds
INFO:charity_advisor.tools.payment_tools:Payment processed successfully: txn_a3f7b2c8d9e1f4a2
======================================================================
CREDENTIALS PROVIDER RESPONSE:
======================================================================
I've successfully processed your payment. Here are the details:
**Payment Completed** (Simulated)
- Transaction ID: txn_a3f7b2c8d9e1f4a2
- Amount: USD 50.00
- Merchant: Room to Read
- Status: Completed
This completes the three-agent AP2 credential chain:
1. ✓ Shopping Agent created IntentMandate (your intent)
2. ✓ Merchant Agent created CartMandate (binding offer)
3. ✓ Credentials Provider created PaymentMandate (payment authorization)
Your donation has been processed securely through our verifiable credential system.
======================================================================
PAYMENTMANDATE CREATED:
======================================================================
Payment Mandate ID: payment_3b4c5d6e7f8a
Linked to Cart: cart_test123
User Consent: True
Amount: USD 50.00
Merchant: Room to Read
Agent Present: True
======================================================================
======================================================================
PAYMENT RESULT:
======================================================================
Transaction ID: txn_a3f7b2c8d9e1f4a2
Status: completed
Amount: USD 50.00
Merchant: Room to Read
Simulation: True
======================================================================
Paso 9: Prueba la canalización completa de tres agentes
Ahora probemos los tres agentes trabajando juntos.
👉 Ejecuta la prueba de canalización completa:
python scripts/test_full_pipeline.py
Resultado esperado:
======================================================================
THREE-AGENT PIPELINE TEST (AP2 CREDENTIAL CHAIN)
======================================================================
[1/3] SHOPPING AGENT - Finding charity and creating IntentMandate...
----------------------------------------------------------------------
✓ IntentMandate created
- Intent ID: intent_774799058_1730927536
- Description: Donate $75.00 to Room to Read
- Merchant: Room to Read
- Amount: $75.0
- Expires: 2025-11-07T16:32:16Z
[2/3] MERCHANT AGENT - Reading IntentMandate and creating CartMandate...
----------------------------------------------------------------------
✓ CartMandate created
- ID: cart_3b4c5d6e7f8a
- Expires: 2025-11-07T15:47:16Z
- Signature: SIG_a3f7b2c8d9e1f4a2
[3/3] CREDENTIALS PROVIDER - Creating PaymentMandate and processing...
----------------------------------------------------------------------
NOTE: In the web UI, this would show a confirmation dialog
For this test, consent is automatically granted
✓ Payment processed (SIMULATED)
- Transaction ID: txn_a3f7b2c8d9e1f4a2
- Amount: $75.0
- Status: completed
======================================================================
COMPLETE AP2 CREDENTIAL CHAIN
======================================================================
✓ Credential 1: IntentMandate (User's Intent)
- Intent ID: intent_774799058_1730927536
- Description: Donate $75.00 to Room to Read
- Expiry: 2025-11-07T16:32:16Z
✓ Credential 2: CartMandate (Merchant's Offer)
- Cart ID: cart_3b4c5d6e7f8a
- Cart Expiry: 2025-11-07T15:47:16Z
- Merchant Signature: SIG_a3f7b2c8d9e1f4a2
✓ Credential 3: PaymentMandate (Payment Execution)
- Payment Mandate ID: payment_3b4c5d6e7f8a
- Linked to Cart: cart_3b4c5d6e7f8a
- Agent Present: True
✓ Transaction Result:
- Transaction ID: txn_a3f7b2c8d9e1f4a2
- Simulation: True
======================================================================
✅ COMPLETE PIPELINE TEST PASSED
======================================================================
Esta es la cadena de credenciales de AP2 completa en acción.
Cada agente:
- Lee una credencial del estado.
- La valida con modelos de Pydantic (estructura y verificación de vencimiento)
- Crea la siguiente credencial con los modelos de AP2
- Escribe en el estado para el siguiente agente
Qué acabas de compilar
Completaste correctamente la cadena de credenciales de tres agentes de la AP2 con la validación de estructura adecuada usando modelos de Pydantic y la simulación de pagos.
Conceptos clave dominados
✅ PaymentMandate (credencial de AP2 núm. 3):
- Se creó con los modelos oficiales de AP2 Pydantic
- Credencial final que autoriza la ejecución del pago
- Vincula a CartMandate a través de payment_details_id
- Registra el consentimiento del usuario y la marca de tiempo
- Contiene el importe del pago extraído de CartMandate
- Se incluye la marca agent_present para la interacción humana
- La validación del modelo garantiza el cumplimiento de las especificaciones
✅ Lectura de CartMandate:
- Valida la estructura con
CartMandate.model_validate() - Acceso a atributos con seguridad de tipos:
cart_model.contents.payment_request.details.total.amount - Comprende la diferencia entre el wrapper de AP2 y la separación estándar de W3C
- Extrae merchant_name, amount y currency de forma segura del modelo
- Pydantic detecta errores de estructura automáticamente
✅ Validación del vencimiento del carrito:
- Acepta el modelo
CartMandatePydantic validado - Lecturas de
cart.contents.cart_expiry(acceso a atributos) - Función de seguridad que evita el procesamiento de carritos inactivos
- Duración más corta (15 min) que la intención (1 hora)
✅ Simulación de pago:
- Simulación educativa de un procesador de pagos real
- Genera un ID de transacción
- Registra payment_result en el estado
- Estar claramente marcada como simulación (marca simulation: True)
✅ Cadena de AP2 completa con modelos:
- Tres agentes, tres credenciales y tres validaciones de Pydantic
- Cada agente valida la estructura de las credenciales anteriores con modelos.
- Cada credencial se vincula a la anterior para el registro de auditoría
- Las transferencias basadas en el estado mantienen la separación de roles
- Seguridad de tipos en toda la cadena
✅ Desarrollo basado en modelos:
- Validación de entrada a través de
model_validate() - Construcción con seguridad de tipos con modelos anidados
- Serialización automática a través de
model_dump(mode='json') - Patrones listos para la producción desde el principio
Pasos siguientes
En el próximo módulo, compilaremos el agente de orquestación que coordina los tres agentes especializados.
Creaste tres potentes agentes especializados con modelos AP2 de Pydantic. Ahora, creemos el conductor que los coordine para brindar una experiencia de donación fluida.
Compilaremos Orchestrator y veremos el sistema completo en acción.
7. Organización: Reunir todo en un solo lugar
De especialistas a experiencia fluida
En los módulos anteriores, creaste tres agentes especializados:
- Agente de Shopping: Busca organizaciones benéficas y crea un IntentMandate
- Agente del comercio: Crea CartMandate a partir de IntentMandate
- Proveedor de credenciales: Crea PaymentMandate y procesa el pago
Estos agentes se dividen naturalmente en dos fases:
- Fase 1 (Shopping): Conversación de varios turnos para encontrar y seleccionar una organización benéfica
- Fase 2 (procesamiento): Ejecución atómica de la creación de la oferta y el pago
Sin embargo, en este momento, tendrías que coordinar estas fases de forma manual.
Aquí es donde se destacan los patrones de organización del ADK.
Principio AP2: La organización aplica límites de confianza
Por qué la organización es importante para la seguridad
La orquestación no se trata solo de la comodidad, sino también de aplicar límites de confianza a través de la arquitectura.
Sin organización:
# User could accidentally skip steps or reorder them
shopping_agent.run("Find charity")
# Oops, forgot to create CartMandate!
credentials_provider.run("Process payment") # No offer to validate!
Con organización:
# Pipeline enforces correct order
donation_processing_pipeline = SequentialAgent(
sub_agents=[
merchant_agent, # Must run first
credentials_provider # Must run second
]
)
# Steps ALWAYS run in order, no skipping allowed
La canalización secuencial garantiza lo siguiente:
- ✅ IntentMandate creado antes de CartMandate
- ✅ Se creó CartMandate antes del procesamiento del pago
- ✅ Cada agente se ejecuta en su contexto aislado.
- ✅ El estado fluye hacia adelante a través de la cadena de credenciales
Nuestra misión: Crear el sistema completo
Compilaremos dos capas:
Capa 1: La canalización de procesamiento (SequentialAgent)
- Conexión del comercio → Credenciales
- Se ejecuta automáticamente en secuencia después de seleccionar la organización benéfica.
- Ejecución atómica de la oferta y el pago
Capa 2: El organizador raíz (agente orientado al usuario)
- Personalidad amigable
- Delega a shopping_agent para la selección de organizaciones benéficas
- Delega a la canalización de procesamiento después de que se crea IntentMandate
- Controla las transiciones de conversación y fase
Este enfoque de dos capas coincide con el flujo natural:
- Fase de compra: Conversación de varios turnos (el usuario navega, hace preguntas y toma una decisión)
- Fase de procesamiento: Ejecución atómica (oferta → pago)
Construyamos ambos.
Paso 1: Importa componentes de orquestación
Primero, configuremos el archivo de orquestación con las importaciones necesarias.
👉 Abrir
charity_advisor/agent.py
Comencemos con las importaciones:
👉 Buscar:
# MODULE_7_STEP_1_IMPORT_COMPONENTS
👉 Reemplaza esa sola línea por lo siguiente:
from google.adk.agents import Agent, SequentialAgent
from charity_advisor.shopping_agent.agent import shopping_agent
from charity_advisor.merchant_agent.agent import merchant_agent
from charity_advisor.credentials_provider.agent import credentials_provider
Paso 2: Crea la canalización de procesamiento
Ahora, creemos la canalización que ejecuta la creación de ofertas y el procesamiento de pagos de forma atómica.
👉 Buscar:
# MODULE_7_STEP_2_CREATE_SEQUENTIAL_PIPELINE
👉 Reemplaza esas dos líneas por lo siguiente:
# Create the donation processing pipeline
# This runs Merchant → Credentials in sequence AFTER charity is selected
donation_processing_pipeline = SequentialAgent(
name="DonationProcessingPipeline",
description="Creates signed offer and processes payment after charity is selected",
sub_agents=[
merchant_agent,
credentials_provider
]
)
Paso 3A: Crea la configuración del agente raíz
Ahora, creemos el agente orientado al usuario que coordina ambas fases. Lo haremos en tres partes: configuración (3A), instrucción (3B) y subagentes (3C).
👉 Buscar:
# MODULE_7_STEP_3A_CREATE_ROOT_AGENT_SETUP
👉 Reemplaza esa sola línea por lo siguiente:
# Create the root orchestrator agent
# This is what users interact with directly
root_agent = Agent(
name="CharityAdvisor",
model="gemini-2.5-pro",
description="A friendly charity giving assistant that helps users donate to verified organizations.",
# MODULE_7_STEP_3B_WRITE_ROOT_AGENT_INSTRUCTION
Paso 3B: Escribe la instrucción del agente raíz
Ahora, agreguemos la instrucción que guía el comportamiento del asesor de caridad en ambas fases.
👉 Buscar:
# MODULE_7_STEP_3B_WRITE_ROOT_AGENT_INSTRUCTION
👉 Reemplaza esa sola línea por lo siguiente:
instruction="""You are a helpful and friendly charity giving advisor.
Your workflow has TWO distinct phases:
PHASE 1: CHARITY SELECTION (delegate to shopping_agent)
When a user expresses interest in donating:
1. Delegate to shopping_agent immediately
2. The shopping_agent will:
- Search for charities matching their cause
- Present verified options with ratings
- Engage in conversation (user may ask questions, change their mind)
- Wait for user to select a specific charity and amount
- Create an IntentMandate when user decides
3. Wait for shopping_agent to complete
You'll know Phase 1 is complete when shopping_agent's response includes:
- "IntentMandate created" or "Intent ID: intent_xxx"
- Charity name and donation amount
PHASE 2: PAYMENT PROCESSING (delegate to DonationProcessingPipeline)
After shopping_agent completes:
1. Acknowledge the user's selection naturally:
"Perfect! Let me process your $X donation to [Charity]..."
2. Delegate to DonationProcessingPipeline
3. The pipeline will automatically:
- Create signed cart offer (MerchantAgent)
- Get consent and process payment (CredentialsProvider)
4. After pipeline completes, summarize the transaction
CRITICAL RULES:
- Phase 1 may take multiple conversation turns (this is normal)
- Only proceed to Phase 2 after IntentMandate exists
- Don't rush the user during charity selection
- Don't ask user to "proceed" between phases - transition automatically
EXAMPLE FLOW:
User: "I want to donate to education"
You: [delegate to shopping_agent]
Shopping: "Here are 3 education charities..." [waits]
User: "Tell me more about the first one"
Shopping: "Room to Read focuses on..." [waits]
User: "Great, I'll donate $50 to Room to Read"
Shopping: "IntentMandate created (ID: intent_123)..."
You: "Perfect! Processing your $50 donation to Room to Read..." [delegate to DonationProcessingPipeline]
Pipeline: [creates offer, gets consent, processes payment]
You: "Done! Your donation was processed successfully. Transaction ID: txn_456"
Your personality:
- Warm and encouraging
- Patient during charity selection
- Clear about educational nature
- Smooth transitions between phases""",
# MODULE_7_STEP_3C_ADD_ROOT_AGENT_SUBAGENTS
Paso 3C: Agrega los subagentes
Por último, otorguemos al asesor de caridad acceso al agente de compras y a la canalización de procesamiento, y cerremos la definición del agente.
👉 Buscar:
# MODULE_7_STEP_3C_ADD_ROOT_AGENT_SUBAGENTS
👉 Reemplaza esa sola línea por lo siguiente:
sub_agents=[
shopping_agent,
donation_processing_pipeline
]
)
Paso 4: Verifica el sistema completo
Confirmemos que la orquestación esté conectada correctamente.
👉 Tu completa
charity_advisor/agent.py
ahora debería verse así:
"""
Main orchestration: The donation processing pipeline and root orchestrator agent.
"""
from google.adk.agents import Agent, SequentialAgent
from charity_advisor.shopping_agent.agent import shopping_agent
from charity_advisor.merchant_agent.agent import merchant_agent
from charity_advisor.credentials_provider.agent import credentials_provider
# Create the donation processing pipeline
# This runs Merchant → Credentials in sequence AFTER charity is selected
donation_processing_pipeline = SequentialAgent(
name="DonationProcessingPipeline",
description="Creates signed offer and processes payment after charity is selected",
sub_agents=[
merchant_agent,
credentials_provider
]
)
# Create the root orchestrator agent
# This is what users interact with directly
root_agent = Agent(
name="CharityAdvisor",
model="gemini-2.5-flash",
description="A friendly charity giving assistant that helps users donate to verified organizations.",
instruction="""You are a helpful and friendly charity giving advisor.
Your workflow has TWO distinct phases:
PHASE 1: CHARITY SELECTION (delegate to shopping_agent)
When a user expresses interest in donating:
1. Delegate to shopping_agent immediately
2. The shopping_agent will:
- Search for charities matching their cause
- Present verified options with ratings
- Engage in conversation (user may ask questions, change their mind)
- Wait for user to select a specific charity and amount
- Create an IntentMandate when user decides
3. Wait for shopping_agent to complete
You'll know Phase 1 is complete when shopping_agent's response includes:
- "IntentMandate created" or "Intent ID: intent_xxx"
- Charity name and donation amount
PHASE 2: PAYMENT PROCESSING (delegate to DonationProcessingPipeline)
After shopping_agent completes:
1. Acknowledge the user's selection naturally:
"Perfect! Let me process your $X donation to [Charity]..."
2. Delegate to DonationProcessingPipeline
3. The pipeline will automatically:
- Create signed cart offer (MerchantAgent)
- Get consent and process payment (CredentialsProvider)
4. After pipeline completes, summarize the transaction
CRITICAL RULES:
- Phase 1 may take multiple conversation turns (this is normal)
- Only proceed to Phase 2 after IntentMandate exists
- Don't rush the user during charity selection
- Don't ask user to "proceed" between phases - transition automatically
EXAMPLE FLOW:
User: "I want to donate to education"
You: [delegate to shopping_agent]
Shopping: "Here are 3 education charities..." [waits]
User: "Tell me more about the first one"
Shopping: "Room to Read focuses on..." [waits]
User: "Great, I'll donate $50 to Room to Read"
Shopping: "IntentMandate created (ID: intent_123)..."
You: "Perfect! Processing your $50 donation to Room to Read..." [delegate to DonationProcessingPipeline]
Pipeline: [creates offer, gets consent, processes payment]
You: "Done! Your donation was processed successfully. Transaction ID: txn_456"
Your personality:
- Warm and encouraging
- Patient during charity selection
- Clear about educational nature
- Smooth transitions between phases""",
sub_agents=[
shopping_agent,
donation_processing_pipeline
]
)
Paso 5: Refuerza la seguridad con devoluciones de llamada de validación (opcional; puedes omitir este paso y pasar al paso 7)

El SequentialAgent garantiza el orden de ejecución, pero ¿qué sucede en los siguientes casos?
- El agente de compras falla de forma silenciosa (nunca se crea IntentMandate)
- Pasa una hora entre Shopping y el comercio (vence la intención)
- El estado se daña o se borra
- Alguien intenta llamar directamente al comercio, sin pasar por Shopping
Devoluciones de llamada que agregan cumplimiento arquitectónico: Validan los requisitos previos incluso antes de que un agente inicie su llamada al LLM. Esta es una defensa en profundidad: las herramientas validan durante la ejecución y las devoluciones de llamada validan antes de la ejecución.
Agreguemos devoluciones de llamada de validación a nuestros agentes de Merchant y Credentials Provider.
Paso 5A: Agrega la validación del comercio: Importa tipos de devolución de llamada
Primero, agreguemos las importaciones necesarias para las devoluciones de llamada.
👉 Abrir
charity_advisor/merchant_agent/agent.py
En la parte superior del archivo, después de las importaciones existentes, agrega lo siguiente:
from typing import Optional
from datetime import datetime, timezone
from google.adk.agents.callback_context import CallbackContext
from google.genai.types import Content, Part
import logging
logger = logging.getLogger(__name__)
Paso 5B: Compila la función de validación de intención
Ahora, creemos una función de devolución de llamada que valide el IntentMandate antes de que se ejecute Merchant Agent.
👉 En
charity_advisor/merchant_agent/agent.py
, agrega esta función ANTES de
merchant_agent = Agent(...)
definición:
def validate_intent_before_merchant(
callback_context: CallbackContext,
) -> Optional[Content]:
"""
Validates IntentMandate exists and hasn't expired before Merchant runs.
This callback enforces that the Shopping Agent completed successfully
before the Merchant Agent attempts to create a CartMandate.
Returns:
None: Allow Merchant Agent to proceed normally
Content: Skip Merchant Agent and return error to user
"""
state = callback_context.state
# Check credential exists
if "intent_mandate" not in state:
logger.error("❌ IntentMandate missing - Shopping Agent may have failed")
return Content(parts=[Part(text=(
"Error: Cannot create cart. User intent was not properly recorded. "
"Please restart the donation process."
))])
intent_mandate = state["intent_mandate"]
# Validate expiry (critical security check)
try:
expiry_time = datetime.fromisoformat(
intent_mandate["intent_expiry"].replace('Z', '+00:00')
)
now = datetime.now(timezone.utc)
if expiry_time < now:
logger.error(f"❌ IntentMandate expired at {intent_mandate['intent_expiry']}")
return Content(parts=[Part(text=(
"Error: Your donation intent has expired. "
"Please select a charity again to restart."
))])
time_remaining = expiry_time - now
logger.info(f"✓ IntentMandate validated. Expires in {time_remaining.total_seconds():.0f}s")
except (KeyError, ValueError) as e:
logger.error(f"❌ Invalid IntentMandate structure: {e}")
return Content(parts=[Part(text=(
"Error: Invalid intent data. Please restart the donation."
))])
# All checks passed - allow Merchant Agent to proceed
logger.info(f"✓ Prerequisites met for Merchant Agent: {intent_mandate['intent_id']}")
return None
Paso 5C: Adjunta la devolución de llamada al agente del comercio
Ahora conectemos la devolución de llamada al agente.
👉 En
charity_advisor/merchant_agent/agent.py
, modifica el
merchant_agent = Agent(...)
definición:
Busca esta línea en la definición del agente:
merchant_agent = Agent(
name="MerchantAgent",
model="gemini-2.5-flash",
description="Creates formal, signed CartMandates for charity donations following W3C PaymentRequest standards.",
Agrega esta línea justo después de
description
línea:
before_agent_callback=validate_intent_before_merchant,
La definición del agente ahora debería verse así:
merchant_agent = Agent(
name="MerchantAgent",
model="gemini-2.5-flash",
description="Creates formal, signed CartMandates for charity donations following W3C PaymentRequest standards.",
before_agent_callback=validate_intent_before_merchant,
tools=[
FunctionTool(func=create_cart_mandate)
],
instruction="""..."""
)
Paso 6: Agrega la validación del proveedor de credenciales (opcional; omite este paso y ve al paso 7)
El mismo patrón: agreguemos validación para el paso de pago.
Paso 6A: Importa tipos de devolución de llamada
👉 Abrir
charity_advisor/credentials_provider/agent.py
En la parte superior del archivo, después de las importaciones existentes, agrega lo siguiente:
from typing import Optional
from datetime import datetime, timezone
from google.adk.agents.callback_context import CallbackContext
from google.genai.types import Content, Part
import logging
logger = logging.getLogger(__name__)
Paso 6B: Compila la función de validación del carrito
👉 En
charity_advisor/credentials_provider/agent.py
, agrega esta función ANTES de
credentials_provider = Agent(...)
definición:
def validate_cart_before_payment(
callback_context: CallbackContext,
) -> Optional[Content]:
"""
Validates CartMandate exists and hasn't expired before payment processing.
This callback enforces that the Merchant Agent completed successfully
before the Credentials Provider attempts to process payment.
Returns:
None: Allow Credentials Provider to proceed
Content: Skip payment processing and return error
"""
state = callback_context.state
# Check credential exists
if "cart_mandate" not in state:
logger.error("❌ CartMandate missing - Merchant Agent may have failed")
return Content(parts=[Part(text=(
"Error: Cannot process payment. Cart was not properly created. "
"Please restart the donation process."
))])
cart_mandate = state["cart_mandate"]
# Validate AP2 structure
if "contents" not in cart_mandate:
logger.error("❌ CartMandate missing AP2 contents wrapper")
return Content(parts=[Part(text=(
"Error: Invalid cart structure. Please restart."
))])
# Validate expiry
try:
contents = cart_mandate["contents"]
expiry_time = datetime.fromisoformat(
contents["cart_expiry"].replace('Z', '+00:00')
)
now = datetime.now(timezone.utc)
if expiry_time < now:
logger.error(f"❌ CartMandate expired at {contents['cart_expiry']}")
return Content(parts=[Part(text=(
"Error: Your cart has expired (15 minute limit). "
"Please restart the donation to get a fresh offer."
))])
time_remaining = expiry_time - now
logger.info(f"✓ CartMandate validated. Expires in {time_remaining.total_seconds():.0f}s")
except (KeyError, ValueError) as e:
logger.error(f"❌ Invalid CartMandate structure: {e}")
return Content(parts=[Part(text=(
"Error: Invalid cart data. Please restart the donation."
))])
# All checks passed - allow payment processing
logger.info(f"✓ Prerequisites met for Credentials Provider: {contents['id']}")
return None
Paso 6C: Adjunta la devolución de llamada al proveedor de credenciales
👉 En
charity_advisor/credentials_provider/agent.py
, modifica el
credentials_provider = Agent(...)
definición:
Busca esta línea en la definición del agente:
credentials_provider = Agent(
name="CredentialsProvider",
model="gemini-2.5-flash",
description="Securely processes payments by creating PaymentMandates and executing transactions with user consent.",
Agrega esta línea justo después de
description
línea:
before_agent_callback=validate_cart_before_payment,
La definición del agente ahora debería verse así:
credentials_provider = Agent(
name="CredentialsProvider",
model="gemini-2.5-flash",
description="Securely processes payments by creating PaymentMandates and executing transactions with user consent.",
before_agent_callback=validate_cart_before_payment,
tools=[
FunctionTool(func=create_payment_mandate)
],
instruction="""..."""
)
Paso 7: Prueba con la IU web del ADK
Ahora probemos el sistema completo reforzado con las devoluciones de llamada de validación activas.
👉 En la terminal de Cloud Shell, ejecuta el siguiente comando:
adk web
Deberías ver un resultado como el siguiente:
+-----------------------------------------------------------------------------+
| ADK Web Server started |
| |
| For local testing, access at http://localhost:8000. |
+-----------------------------------------------------------------------------+
INFO: Application startup complete.
INFO: Uvicorn running on http://0.0.0.0:8000 (Press CTRL+C to quit)
👉 A continuación, para acceder a la IU web del ADK desde tu navegador, haz lo siguiente:
En el ícono de Vista previa en la Web (parece un ojo o un cuadrado con una flecha) en la barra de herramientas de Cloud Shell (por lo general, en la parte superior derecha), selecciona Cambiar puerto. En la ventana emergente, establece el puerto en 8000 y haz clic en "Cambiar y obtener vista previa". Luego, Cloud Shell abrirá una nueva pestaña del navegador que mostrará la IU web del ADK.

👉 Selecciona tu agente en el menú desplegable:
En la IU web del ADK, verás un menú desplegable en la parte superior. Selecciona charity_advisor en la lista.

Verás la interfaz web del ADK con lo siguiente:
- Panel del chat: Se encuentra en el lado izquierdo y se usa para la conversación.
- Panel de seguimiento: Lado derecho, para la observabilidad (lo usaremos en el módulo 9)
Prueba 1: Completa el flujo de donación (caso normal)
👉 En la interfaz de chat, escribe lo siguiente:
I want to donate to an education charity
Mira el flujo completo:


Qué sucede (visible en el panel de seguimiento a la derecha):
1. El asesor delega en ShoppingAgent:
- ShoppingAgent busca organizaciones benéficas educativas
- Te muestra 3 opciones verificadas con detalles
2. Interactúas con ShoppingAgent (puede llevar varios turnos):
User: "Tell me more about Room to Read"
Shopping: [explains mission and impact]
User: "I'll donate $50 to Room to Read"
3. ShoppingAgent crea IntentMandate:
- Crea y firma el intent
- Devuelve la confirmación con el ID de Intent
4. El asesor pasa a la fase de procesamiento:
¡Perfecto! Estamos procesando tu donación de USD 50 a Room to Read…
5. Se activa DonationProcessingPipeline:
- La devolución de llamada del comercio valida el IntentMandate (✓ aprobado) ← ¡NUEVO!
- El agente del comercio crea CartMandate con firma
- La devolución de llamada de credenciales valida CartMandate (✓ aprobado) ← ¡NUEVO!
- El proveedor de credenciales prepara el pago
6. Procesos de pago:
- El proveedor de credenciales crea un objeto PaymentMandate
- Simula el procesamiento de pagos
- ID de transacción de devoluciones
7. El asesor resume lo siguiente:
¡Perfecto! Se procesó correctamente tu donación. 🎉
Details:
- Importe: USD 50.00
- Organización benéfica: Room to Read (EIN: 77-0479905)
- ID de transacción: txn_a3f7b2c8d9e1f4a2
Prueba 2: Verifica que las devoluciones de llamada detecten errores (prueba avanzada opcional)
¿Quieres ver las devoluciones de llamada en acción detectando errores? Deberías dañar el estado de forma manual (depuración avanzada), pero, en producción, las devoluciones de llamada detectarían lo siguiente:
- Falla la herramienta Shopping Agent → Se bloquean las devoluciones de llamada del comercio: "Error: No se puede crear el carrito…"
- Transcurren 2 horas → Se bloquean las devoluciones de llamada del comercio: "Error: Intent expired…"
- Vence el carrito → Bloques de devolución de llamada de credenciales: "Error: Venció el carrito (límite de 15 min)…"
Tus devoluciones de llamada de validación ahora imponen arquitectónicamente estos casos extremos.
Qué acabas de compilar
Orquestaste correctamente tres agentes especializados en un sistema confiable y sin interrupciones con validación arquitectónica.
Pasos siguientes
Ya completaste el núcleo técnico de la creación de agentes confiables:
Compilaste un sistema completo y confiable que aplica la cadena de credenciales de forma local. Ahora, hagamos que esté disponible para los usuarios reales a través de la implementación en producción y habilitemos el registro de responsabilidad que hace posible el módulo 9.
Implementemos tu agente reforzado en Google Cloud.
8. Implementación

Tu sistema de donaciones confiable ahora se completa con tres agentes especializados que trabajan de forma local:
Sin embargo, solo se ejecuta en tu máquina de desarrollo. Para que este sistema sea útil para los usuarios reales y para capturar los registros de responsabilidad que demuestran la confiabilidad, debes implementarlo en producción.
En este módulo, se explica cómo implementar tu agente en Google Cloud con la observabilidad habilitada desde el primer día. La marca --trace_to_cloud que usarás durante la implementación es lo que hace posible el registro de auditoría en el módulo 9.
Información sobre las opciones de implementación
El ADK admite varios destinos de implementación. Cada uno tiene diferentes características de complejidad, administración de sesiones, escalabilidad y costo:
Factor | Local ( | Motor del agente | Cloud Run |
Complejidad | Mínimo | Baja | Medio |
Persistencia de sesión | Solo en la memoria (se pierde cuando se reinicia) | Administrado por Vertex AI (automático) | Cloud SQL (PostgreSQL) o en memoria |
Infraestructura | Ninguno (solo para la máquina de desarrollo) | Completamente administrado | Contenedor y base de datos opcional |
Inicio en frío | N/A | De 100 a 500 ms | De 100 a 2,000 ms |
Escalamiento | Instancia única | Automático | Automática (hasta cero) |
Modelo de costos | Gratis (procesamiento local) | Basado en procesamiento | Basado en solicitudes y nivel gratuito |
Asistencia de la IU | Sí (integrado) | No (solo para la API) | Sí (a través de la marca |
Configuración de observabilidad | Lector de seguimiento local | Automático con | Requiere la marca |
Ideal para | Desarrollo y pruebas | Agentes de producción | Agentes de producción |
Recomendación: Para este sistema de donaciones confiable, recomendamos Agent Engine como tu implementación de producción principal porque proporciona lo siguiente:
- Infraestructura completamente administrada (sin contenedores que administrar)
- Persistencia de sesión integrada a través de
VertexAiSessionService - Ajuste de escala automático sin inicios en frío
- Implementación simplificada (no se requieren conocimientos de Docker)
- Integración inmediata en Cloud Trace
Opción adicional: Google Kubernetes Engine (GKE)
Para los usuarios avanzados que requieren control a nivel de Kubernetes, redes personalizadas o coordinación de varios servicios, está disponible la implementación de GKE. Esta opción proporciona la máxima flexibilidad, pero requiere más experiencia operativa (administración de clústeres, manifiestos, cuentas de servicio).
La implementación de GKE no se aborda en este codelab, pero se documenta por completo en la Guía de implementación de GKE del ADK.
Requisitos previos
1. Configuración del proyecto de Google Cloud
Necesitas un proyecto de Google Cloud con la facturación habilitada. Si no tienes uno, haz lo siguiente:
- Crea un proyecto: Google Cloud Console
- Habilita la facturación: Habilitar facturación
- Anota tu ID del proyecto (no el nombre ni el número del proyecto).
2. Reautenticación (opcional)
Autentica con Google Cloud:
gcloud auth application-default login
gcloud config set project YOUR_PROJECT_ID
Reemplaza YOUR_PROJECT_ID por el ID real de tu proyecto de Google Cloud.
Verifica tu autenticación:
gcloud config get-value project
# Should output: YOUR_PROJECT_ID
3. Variables de entorno
Usa estos comandos para completar automáticamente tu archivo .env:
# Get your current Project ID
PROJECT_ID=$(gcloud config get-value project)
STAGING_BUCKET_VALUE="gs://${PROJECT_ID}-staging"
ENV_FILE=".env"
# Check if STAGING_BUCKET is already set in the .env file
if grep -q "^STAGING_BUCKET=" "${ENV_FILE}"; then
# If it exists, replace the line
# The sed command finds the line starting with STAGING_BUCKET= and replaces the entire line.
# Using | as a delimiter to avoid issues with slashes in the bucket name.
sed -i "s|^STAGING_BUCKET=.*|STAGING_BUCKET=${STAGING_BUCKET_VALUE}|" "${ENV_FILE}"
echo "Updated STAGING_BUCKET in ${ENV_FILE}"
else
# If it doesn't exist, add it to the end of the file
echo "STAGING_BUCKET=${STAGING_BUCKET_VALUE}" >> "${ENV_FILE}"
echo "Added STAGING_BUCKET to ${ENV_FILE}"
fi
# Verify it was added or updated correctly
echo "Current STAGING_BUCKET setting:"
grep "^STAGING_BUCKET=" "${ENV_FILE}"
Deberías ver lo siguiente:
STAGING_BUCKET=gs://your-actual-project-id-staging
Notas importantes:
- Reemplaza
YOUR_PROJECT_IDpor el ID de tu proyecto real (o usa los comandos anteriores). - Para
GOOGLE_CLOUD_LOCATION, usa una región admitida. - El bucket de almacenamiento en etapa intermedia se creará automáticamente si no existe cuando ejecutes la secuencia de comandos de implementación.
4. Habilita las API obligatorias
El proceso de implementación requiere que se habiliten varias APIs de Google Cloud. Ejecuta este comando para habilitarlos:
gcloud services enable \
aiplatform.googleapis.com \
storage.googleapis.com \
cloudbuild.googleapis.com \
cloudtrace.googleapis.com \
compute.googleapis.com
Este comando habilita lo siguiente:
- API de AI Platform: Para los modelos de Agent Engine y Vertex AI
- API de Cloud Storage: Para el bucket de etapa de pruebas
- API de Cloud Build: Para la compilación de contenedores (Cloud Run)
- API de Cloud Trace: Para registros de observabilidad y responsabilidad
- API de Compute Engine: Para la administración de cuentas de servicio
Paso 1: Comprende la infraestructura de implementación
Tu proyecto incluye una secuencia de comandos de implementación unificada (deploy.sh) que controla todos los modos de implementación.
👉 Revisa la secuencia de comandos de implementación (opcional):
cat deploy.sh
La secuencia de comandos proporciona tres modos de implementación:
./deploy.sh local: Ejecuta de forma local con almacenamiento en memoria./deploy.sh agent-engine: Implementa en Vertex AI Agent Engine (recomendado)./deploy.sh cloud-run: Implementa en Cloud Run con una IU opcional
Cómo funciona de forma interna:
En el caso de la implementación de Agent Engine, la secuencia de comandos ejecuta lo siguiente:
adk deploy agent_engine \
--project=$GOOGLE_CLOUD_PROJECT \
--region=$GOOGLE_CLOUD_LOCATION \
--staging_bucket=$STAGING_BUCKET \
--display_name="Charity Advisor" \
--trace_to_cloud \
charity_advisor
Para la implementación de Cloud Run, se ejecuta lo siguiente:
adk deploy cloud_run \
--project=$GOOGLE_CLOUD_PROJECT \
--region=$GOOGLE_CLOUD_LOCATION \
--service_name="charity-advisor" \
--app_name="charity_advisor" \
--with_ui \
--trace_to_cloud \
charity_advisor
La marca --trace_to_cloud es fundamental para ambos tipos de implementación, ya que habilita la integración de Cloud Trace para el registro de responsabilidad que explorarás en el módulo 9.
Paso 2: Prepara el wrapper de Agent Engine
Agent Engine requiere un punto de entrada específico que encapsule tu agente para el entorno de ejecución administrado. Este archivo se creó para ti.
👉 Revisar
charity_advisor/agent_engine_app.py
:
"""Agent Engine application wrapper.
This file prepares the Charity Advisor agent for deployment to Vertex AI Agent Engine.
"""
from vertexai import agent_engines
from .agent import root_agent
# Wrap the agent in an AdkApp object for Agent Engine deployment
app = agent_engines.AdkApp(
agent=root_agent,
enable_tracing=True, # Enables Cloud Trace integration automatically
)
Por qué se necesita este archivo:
- Agent Engine requiere que el agente esté encapsulado en un objeto
AdkApp - El parámetro
enable_tracing=Truehabilita la integración de Cloud Trace automáticamente. - La CLI del ADK hace referencia a este wrapper durante la implementación.
- Configura
VertexAiSessionServicepara la persistencia automática de la sesión
Paso 3: Realiza la implementación en Agent Engine (RECOMENDADO)
Agent Engine es la implementación de producción recomendada para tu sistema de donaciones confiable, ya que proporciona una infraestructura completamente administrada con persistencia de sesión integrada.
Ejecuta la implementación
Desde la raíz de tu proyecto, haz lo siguiente:
chmod +x deploy.sh
./deploy.sh agent-engine
Fases de implementación
Observa cómo la secuencia de comandos ejecuta estas fases:
Phase 1: API Enablement
✓ aiplatform.googleapis.com
✓ storage.googleapis.com
✓ cloudbuild.googleapis.com
✓ cloudtrace.googleapis.com
✓ compute.googleapis.com
Phase 2: IAM Setup
✓ Getting project number
✓ Granting Storage Object Admin
✓ Granting Vertex AI User
✓ Granting Cloud Trace Agent
Phase 3: Staging Bucket
✓ Creating gs://your-project-id-staging (if needed)
✓ Setting permissions
Phase 4: Validation
✓ Checking agent.py exists
✓ Verifying root_agent defined
✓ Checking agent_engine_app.py exists
✓ Validating requirements.txt
Phase 5: Build & Deploy
✓ Packaging agent code
✓ Uploading to staging bucket
✓ Creating Agent Engine instance
✓ Configuring session persistence
✓ Setting up Cloud Trace integration
✓ Running health checks
Este proceso tarda entre 5 y 10 minutos, ya que empaqueta el agente y lo implementa en la infraestructura de Vertex AI.
Guarda tu ID de Agent Engine
Después de una implementación exitosa, haz lo siguiente:
✅ Agent Engine created successfully!
Agent Engine ID: 7917477678498709504
Resource Name: projects/123456789/locations/us-central1/reasoningEngines/7917477678498709504
Endpoint: https://us-central1-aiplatform.googleapis.com/v1/...
⚠️ IMPORTANT: Save the Agent Engine ID from the output above
Add it to your .env file as:
AGENT_ENGINE_ID=7917477678498709504
This ID is required for:
- Testing the deployed agent
- Updating the deployment later
- Accessing logs and traces
Actualiza tu archivo .env de inmediato:
echo "AGENT_ENGINE_ID=7917477678498709504" >> .env
Qué se implementó
Tu implementación de Agent Engine ahora incluye lo siguiente:
✅ Los tres agentes (Shopping, Merchant y Credentials) se ejecutan en el entorno de ejecución administrado
✅ Lógica completa de la cadena de credenciales (intención → carrito → autorizaciones de pago)
✅ Mecanismo de consentimiento del usuario con flujo de trabajo de confirmación
✅ Persistencia automática de la sesión a través de VertexAiSessionService
✅ Infraestructura de escalamiento automático administrada por Google
✅ Integración de Cloud Trace para una observabilidad completa
Paso 4: Prueba el agente implementado
Actualiza tu entorno
Verifica que tu .env incluya el ID de Agent Engine:
AGENT_ENGINE_ID=7917477678498709504 # From deployment output
GOOGLE_CLOUD_PROJECT=your-project-id
GOOGLE_CLOUD_LOCATION=us-central1
STAGING_BUCKET=gs://your-project-id-staging
Ejecuta la secuencia de comandos de prueba
Tu proyecto incluye una secuencia de comandos de prueba específicamente para las implementaciones de Agent Engine.
👉 Ejecuta la prueba:
python scripts/test_deployed_agent.py
Resultado esperado
Testing Agent Engine deployment...
Project: your-project-id
Location: us-central1
Agent Engine ID: 7917477678498709504
Endpoint: https://us-central1-aiplatform.googleapis.com/v1/...
Creating session...
✓ Session created: 4857885913439920384
Sending donation query...
✓ Response received:
Event 1: I'll help you donate $50 to a children's education charity...
Event 2: Here are some highly-rated children's education charities...
Event 3: Which charity would you like to support?...
✅ Test completed successfully!
Session ID: 4857885913439920384
This donation generated a trace in Cloud Trace.
View it in Module 9: Observability
To view traces:
https://console.cloud.google.com/traces/list?project=your-project-id
Lista de tareas de verificación
Después de la prueba, verifica lo siguiente:
✅ El agente responde a las preguntas
✅ Los tres agentes se ejecutan en secuencia (Shopping → Comercio → Credenciales)
✅ Se activa el mecanismo de consentimiento (se solicita confirmación)
✅ La sesión persiste en todas las solicitudes
✅ No hay errores de autenticación
✅ No hay tiempos de espera de conexión
Si encuentras errores, haz lo siguiente:
- Verifica que las variables de entorno estén configuradas correctamente
- Verifica que las APIs estén habilitadas:
gcloud services list --enabled - Verifica los registros de Agent Engine en la consola de Vertex AI
- Verifica que el archivo
agent_engine_app.pyexista en tu carpetacharity_advisor.
Paso 5: Realiza la implementación en Cloud Run (opcional)
Si bien se recomienda Agent Engine para una implementación de producción optimizada, Cloud Run ofrece más control y admite la IU web del ADK. Esta sección es opcional.
Cuándo usar Cloud Run
Elige Cloud Run si necesitas lo siguiente:
- La IU web del ADK para la interacción del usuario
- Control total del entorno del contenedor
- Configuraciones de bases de datos personalizadas
- Integración con los servicios existentes de Cloud Run
Ejecuta la implementación
chmod +x deploy.sh
./deploy.sh cloud-run
Qué cambió:
La secuencia de comandos hará lo siguiente automáticamente:
- Compila un contenedor de Docker con el código de tu agente
- Crea una base de datos de PostgreSQL en Cloud SQL (si es necesario)
- Configura la conexión de la base de datos
- Realiza la implementación con la IU web del ADK habilitada
La implementación tarda entre 10 y 15 minutos debido al aprovisionamiento de Cloud SQL.
Administración de sesiones:
- Usa
DatabaseSessionServiceen lugar deVertexAiSessionService - Requiere credenciales de la base de datos en
.env(o generadas automáticamente) - El estado persiste en la base de datos de PostgreSQL
Compatibilidad con la IU:
- La IU web está disponible en
https://charity-advisor-xyz.a.run.app.
Prueba la implementación de Cloud Run
Si realizaste la implementación en Cloud Run con --with_ui, puedes realizar pruebas directamente en tu navegador:
- Navega a la URL de tu servicio (proporcionada en el resultado de la implementación).
- Verás la interfaz web del ADK. Selecciona tu agente en el menú desplegable.
- Comienza una donación de prueba:
I want to donate $50 to a children's education charity
- Observa el flujo de ejecución:
- ShoppingAgent encuentra organizaciones benéficas y guarda tu intención
- MerchantAgent crea la autorización del carrito
- CredentialProvider crea un mandato de pago y solicita confirmación
- Después de que confirmes, se procesará el pago
- Verifica que la respuesta incluya lo siguiente:
- Recomendaciones de organizaciones benéficas
- Solicitud de confirmación
- Mensaje de éxito después de la aprobación
Solución de problemas
Problemas comunes
Problema: ERROR: GOOGLE_CLOUD_PROJECT is not set
Solución: Asegúrate de que tu archivo .env tenga el ID del proyecto correcto:
GOOGLE_CLOUD_PROJECT=your-actual-project-id
Problema: El bucket de etapa de pruebas no se creó automáticamente
Solución: La secuencia de comandos debería crear el bucket automáticamente. De lo contrario, créalo de forma manual:
gsutil mb -p $GOOGLE_CLOUD_PROJECT -l $GOOGLE_CLOUD_LOCATION $STAGING_BUCKET
Resumen
Completaste correctamente lo siguiente:
✅ Comprendiste la infraestructura de implementación que proporciona deploy.sh
✅ Revisaste la configuración del wrapper de Agent Engine
✅ Implementaste tu sistema de donaciones confiable en Agent Engine (recomendado)
✅ Habilitaste la integración de Cloud Trace con --trace_to_cloud
✅ Verificaste que el agente sea accesible y funcional
✅ Creaste la base para los registros de responsabilidad en el módulo 9
En el siguiente módulo, verás exactamente qué desbloquea esta marca: visibilidad completa de cada donación, cada momento de consentimiento y cada paso de la cadena de credenciales.
9. Observabilidad


En el módulo 1, aprendiste sobre un problema fundamental: cuando un agente de IA maneja dinero, ¿cómo se puede demostrar lo que sucedió?
Un usuario podría reclamar lo siguiente:
- "¡Yo nunca elegí esa organización benéfica!"
- "¡No autoricé ese pago!"
- "El sistema me cobró sin mi consentimiento".
En un sistema de IA de caja negra tradicional, no tendrías forma de demostrar lo contrario. Pero tu sistema de donaciones confiable es diferente. En el módulo 8, realizaste la implementación con la marca --trace_to_cloud, lo que significa que cada donación ahora crea un registro de auditoría completo y a prueba de manipulaciones en Cloud Trace.
En este módulo, aprenderás a leer esos registros y a usarlos como evidencia. Aprenderás a:
- Navega por el Explorador de Cloud Trace para encontrar seguimientos de producción
- Lee la vista de cascada para comprender el flujo de ejecución
- Encuentra la cadena de credenciales (intención → carrito → autorizaciones de pago)
- Cómo ubicar momentos de consentimiento con prueba de marca de tiempo
- Usa los registros para la resolución de disputas
- Exporta registros para el cumplimiento y las auditorías
Esto es lo que diferencia a los sistemas confiables de los capaces, pero opacos: la capacidad de demostrar lo que sucedió con precisión forense.
Información sobre los seguimientos y los intervalos
Antes de ver los registros en Cloud Trace, debes comprender lo que estás viendo.
¿Qué es un registro?
Un seguimiento es la línea de tiempo completa de tu agente que controla una sola solicitud. Captura todo el proceso, desde que un usuario envía una búsqueda hasta que se entrega la respuesta final.
Cada registro muestra lo siguiente:
- Duración total de la solicitud
- Todas las operaciones que se ejecutaron
- Cómo se relacionan las operaciones entre sí (relaciones principal-secundaria)
- Cuándo comenzó y finalizó cada operación
- Estado de éxito o falla
Para tu agente de caridad: Un seguimiento equivale a un flujo de donación completo, desde "Quiero donar" hasta "Pago realizado correctamente".
¿Qué es un intervalo?
Un intervalo representa una sola unidad de trabajo dentro de un registro. Piensa en los tramos como los componentes básicos de un registro.
Tipos de tramos comunes en tu sistema de donaciones:
Tipo de tramo | Qué representa | Ejemplo |
| Ejecución de un agente |
|
| Solicitud a un modelo de lenguaje |
|
| Ejecución de funciones de herramientas |
|
| Lectura de la memoria de la sesión | Cómo recuperar |
| Cómo escribir en la memoria de la sesión | Almacenamiento de |
Cada intervalo contiene lo siguiente:
- Nombre: Qué operación representa
- Cuánto tiempo llevó (hora de inicio → hora de finalización)
- Atributos: Metadatos como entradas de herramientas, respuestas del modelo y recuentos de tokens
- Estado: Éxito (
OK) o error (ERROR) - Relaciones entre operaciones principales y secundarias: Qué operaciones activaron otras
Cómo los intervalos forman un registro
Los tramos se anidan entre sí para mostrar la causalidad:
Root Span: CharityAdvisor.run (entire request)
└─ Child: DonationPipeline.run (sequential workflow)
├─ Child: ShoppingAgent.run
│ ├─ Grandchild: call_llm (Gemini processes charity search)
│ ├─ Grandchild: execute_tool (find_charities)
│ └─ Grandchild: execute_tool (save_user_choice)
├─ Child: MerchantAgent.run
│ ├─ Grandchild: call_llm (Gemini generates cart)
│ └─ Grandchild: execute_tool (create_cart_mandate)
└─ Child: CredentialsProvider.run
├─ Grandchild: call_llm (Gemini processes payment)
└─ Grandchild: execute_tool (create_payment_mandate) [CONSENT!]
Esta jerarquía muestra exactamente lo que sucedió y en qué orden. Puedes ver que el mandato de pago se creó después del mandato del carrito, que se creó después de que el usuario seleccionara una organización benéfica.
Paso 1: Accede al Explorador de Cloud Trace
Ahora, veamos los registros reales de tu agente implementado.
Navega a Cloud Trace
- Abre la consola de Google Cloud: console.cloud.google.com
- Selecciona tu proyecto en el menú desplegable de la parte superior (debería estar preseleccionado si has estado trabajando en él).
- Navega al Explorador de Cloud Trace:
- En la barra lateral izquierda, desplázate a la sección Observabilidad.
- Haz clic en Trace.
- O bien, usa el vínculo directo: console.cloud.google.com/traces/list
Qué estás viendo
El Explorador de registros muestra una lista de todos los registros de tu proyecto:
Columna | Qué muestra |
Solicitud | Método y extremo HTTP (para solicitudes a la API) |
Hora de inicio | Cuándo comenzó la solicitud |
Latencia | Duración total de la solicitud |
Intervalos | Cantidad de operaciones en el registro |
Cada fila representa una solicitud completa a tu agente implementado.
Cómo generar registros de prueba (si es necesario)
Si aún no ves ningún registro, es posible que la lista esté vacía por los siguientes motivos:
- Aún no se realizaron solicitudes a tu agente implementado
- Los registros tardan entre 1 y 2 minutos en aparecer después de una solicitud.
Genera un registro de prueba:
Si realizaste la implementación en Cloud Run con IU, visita la URL de tu servicio y completa una donación en el navegador.
Si realizaste la implementación en Agent Engine, ejecuta la secuencia de comandos de prueba del módulo 8:
python scripts/test_deployed_agent.py
Espera 1 o 2 minutos y, luego, actualiza la página del Explorador de Cloud Trace. Ahora deberías ver los registros.
Cómo filtrar registros
Usa las opciones de filtro en la parte superior para encontrar registros específicos:
- Intervalo de tiempo: Si es necesario, cambia de "Última hora" a "Últimas 24 horas".
- Latencia mínima / Latencia máxima: Filtra las solicitudes lentas
- Filtro de solicitud: Busca operaciones específicas (p.ej., "DonationPipeline")
En este módulo, enfócate en los registros con duraciones más largas (más de 5 segundos), ya que representan flujos de donación completos con los tres agentes en ejecución.
Paso 2: Examina un flujo de donación completo
Haz clic en cualquier registro de la lista para abrir la vista de cascada. Aquí es donde pasarás la mayor parte del tiempo analizando el comportamiento del agente.
Información sobre la vista de cascada
La vista de cascada es un diagrama de Gantt que muestra el cronograma de ejecución completo:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Timeline (horizontal = time) →
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
invocation ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ 8.2s
agent_run: CharityAdvisor ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ 8.1s
agent_run: DonationPipeline ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ 7.9s
agent_run: ShoppingAgent ▓▓▓▓▓▓ 2.1s
call_llm: gemini-2.5-flash ▓▓▓▓ 1.2s
execute_tool: find_charities ▓▓ 0.5s
execute_tool: save_user_choice ▓ 0.3s
agent_run: MerchantAgent ▓▓▓ 1.8s
call_llm: gemini-2.5-flash ▓▓ 0.9s
execute_tool: create_cart_mandate ▓ 0.7s
agent_run: CredentialsProvider ▓▓▓▓▓▓▓▓ 4.0s
call_llm: gemini-2.5-flash ▓▓ 0.8s
execute_tool: create_payment_mandate ▓▓▓▓▓ 3.0s [CONSENT]
Cómo leer el gráfico
Cada barra representa un intervalo:
- Posición horizontal: Cuándo comenzó
- Duración: Cuánto tiempo llevó
- Indentación: Muestra las relaciones entre elementos superiores y secundarios
- Color: Por lo general, azul para normal y rojo para errores
Observaciones clave de este registro de ejemplo:
✅ Duración total: 8.2 segundos
✅ Ejecución secuencial: ShoppingAgent se completó antes de que se iniciara MerchantAgent
✅ MerchantAgent se completó
before
CredentialsProvider started
✅ Consent was the longest operation: 3.0 seconds for create_payment_mandate (because it waited for user confirmation)
✅ LLM calls are visible: Each agent made one Gemini request
✅ Tool calls are captured: All six tools executed successfully
Este elemento visual te muestra de inmediato dónde se invierte el tiempo y en qué orden se ejecutaron las operaciones.
Haz clic en un intervalo para ver los detalles
Haz clic en el intervalo invocation (el intervalo raíz en la parte superior). En el panel derecho, verás atributos detallados:
{
"http.method": "POST",
"http.status_code": 200,
"http.url": "https://charity-advisor-xyz.a.run.app/api/run",
"user_id": "test_user_123",
"session_id": "4857885913439920384",
"trace_id": "a1b2c3d4e5f6...",
"span_id": "1234567890abcdef"
}
Estos atributos proporcionan contexto sobre toda la solicitud.
Paso 3: Busca la cadena de credenciales
Tu sistema confiable usa una cadena de credenciales para demostrar la autorización en cada paso:
IntentMandate (User chose charity)
↓
CartMandate (Merchant created cart, signed IntentMandate)
↓
PaymentMandate (Payment provider created payment, signed CartMandate)
Busquemos cada mandato en el registro.
Cómo encontrar el objeto IntentMandate
Haz clic en el intervalo execute_tool: save_user_choice (en ShoppingAgent).
En el panel de atributos, verás lo siguiente:
{
"tool.name": "save_user_choice",
"tool.input.charity_name": "Save the Children",
"tool.input.amount": 50,
"tool.output.status": "success",
"tool.output.intent_mandate": {
"charity_name": "Save the Children",
"amount": 50,
"timestamp": "2024-11-08T15:30:12.345Z",
"signature": "a3f7b9c1d2e4..."
}
}
Esto demuestra lo siguiente:
- ✅ El usuario seleccionó "Save the Children".
- ✅ El importe era de USD 50
- ✅ La elección se registró a las 15:30:12 UTC.
- ✅ Se generó la firma (en producción, sería criptográfica).
El IntentMandate ahora está en el estado de sesión y está disponible para los agentes posteriores.
Cómo encontrar el CartMandate
Haz clic en el intervalo execute_tool: create_cart_mandate (en MerchantAgent).
En el panel de atributos, haz lo siguiente:
{
"tool.name": "create_cart_mandate",
"tool.input.intent_mandate": {
"charity_name": "Save the Children",
"amount": 50,
"signature": "a3f7b9c1d2e4..."
},
"tool.output.status": "success",
"tool.output.cart_mandate": {
"cart_id": "cart_7893",
"intent_signature": "a3f7b9c1d2e4...",
"cart_signature": "e8f2a9b3c7d1...",
"timestamp": "2024-11-08T15:30:14.789Z"
}
}
Esto demuestra lo siguiente:
- ✅ MerchantAgent recibió el IntentMandate (la entrada lo muestra).
- ✅ Se creó el carrito con el ID “cart_7893”
- ✅ La firma del carrito hace referencia a la firma de IntentMandate (¡vínculo encadenado!).
- ✅ Se creó a las 15:30:14 UTC (2.4 segundos después de la intención).
Ahora, CartMandate hace referencia a IntentMandate, lo que forma la cadena.
Cómo encontrar el objeto PaymentMandate
Haz clic en el intervalo execute_tool: create_payment_mandate (en CredentialsProvider).
En el panel de atributos, haz lo siguiente:
{
"tool.name": "create_payment_mandate",
"tool.input.cart_mandate": {
"cart_id": "cart_7893",
"intent_signature": "a3f7b9c1d2e4...",
"cart_signature": "e8f2a9b3c7d1..."
},
"tool.confirmation_required": true,
"tool.confirmation_timestamp": "2024-11-08T15:30:17.891Z",
"tool.user_response": "CONFIRMED",
"tool.wait_duration_ms": 29168,
"tool.output.status": "success",
"tool.output.payment_mandate": {
"payment_id": "pay_9821",
"cart_signature": "e8f2a9b3c7d1...",
"payment_signature": "b4c9e2a7f8d3...",
"timestamp": "2024-11-08T15:30:47.059Z"
}
}
Esto prueba la cadena completa:
- ✅ CredentialsProvider recibió el CartMandate (la entrada lo muestra).
- ✅ El pago hace referencia a la firma de CartMandate (¡vínculo de cadena!)
- ✅ Se requirió confirmación (
confirmation_required: true) - ✅ El usuario confirmó a las 15:30:17 UTC
- ✅ El sistema esperó 29.2 segundos para que el usuario tomara una decisión.
- ✅ El pago se creó después de la confirmación (marca de tiempo: 15:30:47).
Visualiza la cadena
El registro demuestra que la cadena de credenciales se ejecutó correctamente:
15:30:12 UTC → IntentMandate created (signature: a3f7...)
↓
15:30:14 UTC → CartMandate created (references: a3f7...)
↓
15:30:17 UTC → User consent requested
↓
15:30:47 UTC → PaymentMandate created (references: e8f2...)
Cada mandato hace referencia a la firma del anterior. Es a prueba de manipulaciones, ya que puedes verificar la cadena comprobando que las firmas coincidan.
Paso 4: Analiza el rendimiento y los cuellos de botella
Cloud Trace no solo demuestra lo que sucedió, sino que también te muestra dónde se invierte el tiempo para que puedas optimizarlo.
Identifica la ruta crítica
En la vista de cascada, busca los intervalos más largos en la pila vertical. Estos representan los cuellos de botella en tu rendimiento.
En nuestro ejemplo de registro:
Total: 8.2 seconds
Breakdown:
- ShoppingAgent: 2.1s (26%)
- MerchantAgent: 1.8s (22%)
- CredentialsProvider: 4.0s (49%) ← Bottleneck
- Other overhead: 0.3s (3%)
Estadística fundamental: CredentialsProvider representa el 49% del tiempo total. ¿Por qué?
Explora en detalle el intervalo de CredentialsProvider:
CredentialsProvider: 4.0s
- call_llm: 0.8s (20%)
- create_payment_mandate: 3.0s (75%) ← User consent wait
- Other: 0.2s (5%)
La demora de 3 segundos es esperada y buena, ya que el usuario está deliberando antes de confirmar. Esto no es un problema de rendimiento, sino una prueba de consentimiento reflexivo.
Seguimiento de los costos de los LLM
Haz clic en cualquier intervalo call_llm para ver el uso de tokens:
{
"llm.model": "gemini-2.5-flash",
"llm.usage.prompt_tokens": 487,
"llm.usage.completion_tokens": 156,
"llm.usage.total_tokens": 643,
"llm.response_time_ms": 1243
}
Usa esta API para lo siguiente:
- Haz un seguimiento del costo por solicitud (tokens × precios del modelo)
- Identifica las instrucciones innecesariamente largas
- Comparar el rendimiento del modelo (Flash vs. Pro)
- Optimiza la latencia en comparación con la calidad
Ejemplo de cálculo:
Gemini 2.5 Flash pricing (as of Nov 2024):
Input: $0.075 per 1M tokens
Output: $0.30 per 1M tokens
This request:
Input: 487 tokens × $0.075 / 1M = $0.000037
Output: 156 tokens × $0.30 / 1M = $0.000047
Total: = $0.000084 (~$0.00008)
For 10,000 donations/month:
10,000 × 3 agents × $0.00008 = $2.40/month in LLM costs
Esta visibilidad detallada te ayuda a tomar decisiones basadas en datos sobre la selección de modelos.
Comparación entre registros
Filtra varias rutas y compara las duraciones:
Trace 1: 8.2s (with consent wait: 3.0s)
Trace 2: 12.5s (with consent wait: 7.8s) ← User took longer
Trace 3: 5.1s (with consent wait: 0.2s) ← User clicked fast
Trace 4: 6.3s (with consent wait: 1.5s)
Estadística: La mayor parte de la variación proviene del tiempo de decisión del usuario, no del rendimiento del sistema. La ejecución del agente principal (sin consentimiento) es constante en aproximadamente 5 segundos.
Esto indica que el sistema funciona de forma confiable.
En el caso de los sistemas de producción, configura alertas para detectar problemas antes de que los usuarios se quejen.
Alerta sobre tasas de errores altas
Crea una alerta cuando más del 5% de los registros contengan errores:
- Navega a Cloud Monitoring.
- Haz clic en "Alertas" → "Crear política".
- Configuración:
Resource: Cloud Trace Span Metric: Span error count Condition: Rate > 5% over 5 minutes Notification: Email your-team@example.com
Alerta sobre latencia alta
Crea una alerta cuando la latencia del percentil 95 supere los 15 segundos:
Resource: Cloud Trace
Metric: Span duration (95th percentile)
Condition: > 15000ms for 5 minutes
Notification: PagerDuty
Esto detecta la degradación del rendimiento antes de que afecte la experiencia del usuario.
Alerta sobre la falta de consentimiento
Crear una alerta si se procesa algún pago sin confirmación:
Resource: Cloud Trace Span
Filter: tool.name="create_payment_mandate" AND tool.confirmation_required!=true
Condition: Any match
Notification: Critical alert to security team
Este es un detector de incumplimientos de seguridad. Si se activa, significa que hay un problema grave con tu mecanismo de consentimiento.
Qué aprendiste
Con Cloud Trace, ahora sabes cómo hacer lo siguiente:
✅ Navega por el Explorador de Cloud Trace para encontrar registros de producción
✅ Lee las vistas de cascada para ver el flujo de ejecución completo
✅ Realiza un seguimiento de la cadena de credenciales a través de IntentMandate → CartMandate → PaymentMandate ✅ Usa los registros como evidencia para la resolución de disputas
✅ Analiza el rendimiento para identificar cuellos de botella
✅ Realiza un seguimiento de los costos de los LLM a un nivel detallado
La diferencia que esto genera
Compara dos sistemas que manejan el mismo reclamo de "¡Nunca autoricé esto!":
Sistema sin observabilidad
User: "I never authorized that $50 donation!"
You: "Our logs show the transaction completed successfully."
User: "But I didn't approve it!"
You: "The system requires confirmation before processing."
User: "I never saw any confirmation!"
You: "..." [no way to prove what happened]
Result: Refund issued, trust lost, user never returns.
Sistema con Cloud Trace
User: "I never authorized that $50 donation!"
You: "Let me pull up the trace from your session..."
[Shows waterfall with consent span]
You: "Here's the evidence:
- 15:30:17 UTC: System asked for confirmation
- Message shown: 'You are about to donate $50...'
- 15:30:47 UTC: You clicked 'CONFIRM'
- Wait time: 29.2 seconds
The system waited almost 30 seconds for your decision.
Here's the exact timestamp of your confirmation."
User: "Oh... I remember now. My mistake. Sorry!"
Result: Trust preserved, no refund needed, user continues using service.
Este es el poder de los registros de responsabilidad. Pasas de "confía en nosotros" a "permítenos mostrarte exactamente lo que sucedió".
Pasos siguientes
Ya completaste el núcleo técnico de la creación de agentes confiables:
✅ Módulos 1 a 6: Diseñaste una arquitectura confiable (roles, credenciales, consentimiento).
✅ Módulo 7: Orquestaste flujos de trabajo complejos (SequentialAgent).
✅ Módulo 8: Realizaste la implementación con la observabilidad habilitada.
✅ Módulo 9: Aprendiste a leer y usar registros de responsabilidad.
La arquitectura que creaste (separación de roles, cadenas de credenciales, mecanismos de consentimiento, observabilidad completa) se transfiere directamente a los sistemas de producción que manejan dinero real, datos reales y consecuencias reales.
10. Tu camino a seguir
Qué compilaste
Comenzaste este taller con una pregunta: "¿Cómo creo agentes de IA en los que realmente pueda confiar para administrar dinero?"
Ahora tienes la respuesta.
Dónde comenzaste (módulo 3):
simple_agent = Agent(
model="gemini-2.5-flash",
instruction="Find charities and donate",
tools=[google_search]
)
Dónde te encuentras ahora (módulo 10):
- ✅ Tres agentes especializados con separación de roles
- ✅ Tres credenciales verificables (intención → carrito → mandatos de pago)
- ✅ Cadena de credenciales completa con validación de vencimiento en cada paso
- ✅ Mecanismo de consentimiento explícito con prueba de marca de tiempo
- ✅ Implementación en producción en Agent Engine con observabilidad
- ✅ Registro de responsabilidad completo en Cloud Trace
- ✅ Evidencia forense para la resolución de disputas
Taller vs. Producción: La brecha
Tu sistema demuestra la arquitectura y los patrones correctos, pero usa simplificaciones educativas que deben actualizarse para dinero real y usuarios reales.
A continuación, se indica exactamente qué se simplificó y qué requiere la producción:
Componente | Implementación del taller | Requisitos de producción |
Firmas | Hashes SHA-256 ( | Firmas criptográficas reales con PKI o JWT con claves privadas |
Procesamiento de pagos | Devoluciones simuladas (marca | Integración con APIs de pago reales (Stripe, PayPal, Square) |
Autenticación del usuario | Confianza implícita (no se requiere acceso) | OAuth 2.0, WebAuthn o administración de sesiones |
Administración de secretos | Variables de entorno en el archivo | Google Secret Manager o Cloud KMS con encriptación |
Base de datos de organizaciones benéficas | Archivo JSON simulado con 9 organizaciones benéficas | Integración de API en vivo (búsqueda de organizaciones exentas de impuestos del IRS, API de Charity Navigator) |
Manejo de errores | Bloque try-catch básico con mensajes de error | Lógica de reintento con retirada exponencial, disyuntores y colas de mensajes no entregados |
Pruebas | Verificación manual a través de secuencias de comandos | Paquete de pruebas integral de unidades, integración y E2E con CI/CD |
Persistencia de sesión | En la memoria (local) o automática (Agent Engine) | Base de datos de producción con copias de seguridad y recuperación ante desastres |
Límite de frecuencia | Ninguno (entorno educativo) | Límites de frecuencia por usuario, limitación basada en IP y detección de abuso |
Patrones arquitectónicos clave que dominas
Los patrones que aprendiste en este taller son patrones de producción. No dudes de ellos.
Separación de roles (principio 1 de AP2)
Cada agente tiene UN trabajo claro y ve SOLO lo que necesita. Si se vulnera un agente, el atacante no puede acceder a los datos de otros agentes. Esto limita el radio de impacto.
Sistemas de producción que usan esta función: Procesamiento de pagos, flujos de trabajo de documentos, cadenas de aprobación, formularios de varios pasos con puertas de validación.
Credenciales verificables (principio 2 de la AP2)
Cada credencial tiene una fecha de vencimiento, hace referencia a la credencial anterior y requiere validación antes del siguiente paso. Esto crea una cadena de auditoría a prueba de manipulaciones.
Valor de producción: Prueba completa de lo que sucedió, cuándo y en qué orden. Es esencial para la resolución de disputas y el cumplimiento de las reglamentaciones.
Consentimiento explícito (principio 3 de la AP2)
Es la marca de tiempo que demuestra que el usuario aprobó la acción. No se puede impugnar.
Valor de producción: Es un requisito legal para las transacciones financieras. Protege tanto al usuario como a la empresa.
Organización secuencial (patrón del ADK)
Aplica el orden de ejecución correcto. Evita que se omitan pasos. Garantiza que cada agente vea el resultado del agente anterior.
Valor de producción: Es ideal para los sistemas que requieren la intervención humana, en los que los usuarios esperan resultados inmediatos. Este es el patrón correcto para los flujos de donación, los procesos de confirmación de compra y las cadenas de aprobación.
Observabilidad completa (OpenTelemetry + Cloud Trace)
Cada decisión, llamada a herramienta, momento de consentimiento y transferencia de credenciales se capturan automáticamente.
Valor de producción: Evidencia forense para disputas. Son los datos de optimización del rendimiento. Registros de auditoría de cumplimiento Depura problemas de producción con precisión.
Recursos para el aprendizaje continuo
Documentación del ADK:
AP2 y estándares relacionados:
Servicios de Google Cloud:
Limpia los recursos
Para evitar cargos continuos, borra tu implementación:
Agent Engine: Sigue los pasos que se indican en los documentos de Agent Engine.
Cloud Run (si se implementó):
gcloud run services delete charity-advisor \
--region=$GOOGLE_CLOUD_LOCATION
Buckets de almacenamiento:
gsutil -m rm -r gs://$GOOGLE_CLOUD_PROJECT-staging
gsutil -m rm -r gs://$GOOGLE_CLOUD_PROJECT-artifacts
Tu recorrido continúa
Comenzaste con una pregunta simple y creaste una respuesta completa. Dominaste los patrones fundamentales para los agentes de IA confiables. Estos patrones se transfieren a cualquier dominio en el que los agentes de IA manejan operaciones sensibles: transacciones financieras, decisiones de atención médica, documentos legales, operaciones de la cadena de suministro.
Se transfieren los principios. El modelo de confianza funciona.
¡Ahora crea algo confiable! ❤️
