1. Creare fiducia per favorire la generosità

Il momento dell'ispirazione
Lo smartphone vibra. Vedi un articolo di notizie su un programma di alfabetizzazione di successo che aiuta i bambini delle comunità svantaggiate a imparare a leggere. Senti un forte desiderio di contribuire. Apri il browser e cerchi "donazioni per programmi di alfabetizzazione per bambini".

Vengono visualizzati centinaia di risultati.
Fai clic sul primo link. Il sito web ha un aspetto professionale. Scorri verso il basso fino ai dati finanziari. "Costi amministrativi: 28%." Metti in pausa. Solo 72 centesimi di ogni dollaro donato finanzieranno effettivamente il programma. Va bene? Non sei sicuro.
Prova con un'altra organizzazione. Non ne hai mai sentito parlare. Sono legittimi? Una rapida ricerca ti porta in un vicolo cieco. Trovi un thread di Reddit di due anni fa in cui un utente afferma: "È una truffa, la mia donazione non è mai arrivata a destinazione". Un altro li difende con passione: "Sono sul campo e fanno un lavoro vero!" L'ambiguità è paralizzante.
Trenta minuti dopo, ti trovi nel bel mezzo di un labirinto di recensioni contrastanti, valutazioni di efficienza e registri dell'IRS e non hai ancora effettuato la donazione. L'iniziale slancio di generosità è stato sostituito dalla difficoltà della ricerca. La scheda rimane aperta per alcuni giorni, un piccolo promemoria di una buona intenzione, finché non la chiudi.
Non si tratta di un fallimento personale, ma di un errore di sistema
Questa esperienza è universale. Il desiderio di donare è abbondante, ma la procedura è piena di ostacoli che causano esitazione e dubbi:
- ❌ Frizioni nella ricerca: ogni ente di beneficenza richiede una propria indagine.
- ❌ Verifica dell'affidabilità:è difficile distinguere le organizzazioni altamente efficaci da quelle inefficienti o addirittura da vere e proprie frodi.
- ❌ Paralisi decisionale:un numero eccessivo di scelte porta a una perdita di interesse causata dall'indecisione.
- ❌ Perdita di slancio:la motivazione emotiva a donare svanisce man mano che il carico logistico aumenta.
Questo attrito ha un costo reale e sorprendente. Le donazioni individuali negli Stati Uniti sono enormi: secondo Giving USA 2024, i donatori individuali hanno donato circa 374 miliardi di dollari solo nel 2023. Tuttavia, le ricerche dimostrano che gli ostacoli alla donazione, tra cui i costi di ricerca, l'attrito psicologico e i vincoli di tempo, riducono significativamente l'importo che raggiunge le cause di beneficenza. Studi che coinvolgono milioni di donatori hanno rilevato che anche piccoli attriti nel processo di donazione online impediscono alle persone di realizzare le loro intenzioni di beneficenza.
Si tratta di miliardi di dollari in donazioni previste che non raggiungono mai le cause che ne hanno bisogno.
La visione
Immagina un'esperienza diversa. Invece di una sessione di ricerca di 30 minuti, ti basterà dire:
"Voglio donare 50 $a un programma di alfabetizzazione per bambini. Trovami un ente di beneficenza efficiente, verificato e con un ottimo punteggio."
In pochi secondi, ricevi una risposta che ti rassicura:
Questa è la promessa di un agente AI per le donazioni. Ma per realizzare questa visione, dobbiamo risolvere una sfida fondamentale: quando un agente di AI autonomo gestisce denaro, la fiducia non è facoltativa, ma è l'intera base.
- Come possiamo dimostrare ciò che un utente ha autorizzato?
- Chi è responsabile in caso di errore?
- Come facciamo a dare a donatori, enti benefici e circuiti di pagamento la fiducia necessaria per partecipare?
La tua missione di oggi
In questo workshop, creerai questo agente affidabile combinando due potenti tecnologie:
Google Agent Development Kit (ADK) | Agent Payments Protocol (AP2) | |
Role | La fabbrica per la creazione di agenti AI di livello di produzione | Il progetto di architettura per la fiducia nelle transazioni AI |
Cosa offre | • Framework per l'orchestrazione multi-agente | • Limiti di sicurezza basati sui ruoli |
Scopri di più |
Cosa creerai
Al termine di questo workshop, avrai creato:
✅ Un sistema multi-agente con ruoli specializzati:
- Un agente di Shopping che trova enti di beneficenza verificati
- Un agente del commerciante che crea offerte di donazione vincolanti
- Un fornitore di credenziali che elabora i pagamenti in modo sicuro
- Un orchestratore che coordina l'intero flusso
✅ Tre tipi di credenziali verificabili:
- IntentMandate: "Find me an education charity" (trovami un ente di beneficenza per l'istruzione)
- CartMandate: "50 $ a Room to Read, firmato dal commerciante"
- PaymentMandate: "Process via simulated payment" (Elabora tramite pagamento simulato)
✅ Sicurezza a ogni livello:
- Confini di attendibilità basati sui ruoli
- Consenso esplicito dell'utente
✅ Un audit trail completo:
- Ogni decisione è tracciabile
- Ogni consenso registrato
- Ogni passaggio visibile
🔒 Importante: questo è un ambiente di apprendimento sicuro
Pronto a creare fiducia?
Nel prossimo modulo, configureremo l'ambiente di sviluppo e creeremo il tuo primo agente AI. Scoprirai rapidamente perché gli agenti semplici non sono affidabili e trascorrerai il resto del workshop imparando a risolvere il problema.
Iniziamo a comprendere il problema in prima persona.
2. Preparazione del workspace
The Foundation for Trustworthy Agents
Prima di poter creare il nostro AI Giving Agent, dobbiamo preparare un ambiente di sviluppo pulito, coerente e configurato correttamente. Questo modulo è un passaggio mirato per garantire che tutti gli strumenti e i servizi necessari siano disponibili.
Il completamento di questa configurazione ti consente di concentrarti interamente sul lavoro entusiasmante di creazione della logica dell'agente nei moduli futuri, senza preoccuparti di problemi di configurazione.
Accedere a Cloud Shell
Innanzitutto, apri Cloud Shell, un terminale basato su browser con Google Cloud SDK e altri strumenti essenziali preinstallati.
Hai bisogno di crediti Google Cloud?
Fai clic su Attiva Cloud Shell nella parte superiore della console Google Cloud (è l'icona del terminale nella barra di navigazione in alto a destra).

Trova l'ID progetto Google Cloud:
- Apri la console Google Cloud: https://console.cloud.google.com
- Seleziona il progetto che vuoi utilizzare per questo workshop dal menu a discesa dei progetti nella parte superiore della pagina.
- L'ID progetto viene visualizzato nella scheda informativa del progetto nella dashboard

Una volta aperto Cloud Shell, verifica di aver eseguito l'autenticazione:
# Check that you are logged in
gcloud auth list
Dovresti vedere il tuo account elencato come (ACTIVE).
Configurare il progetto
Ora configuriamo il progetto Google Cloud e abilitiamo le API necessarie.
Impostare l'ID progetto
# 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)"
Abilita le API richieste
I tuoi agenti devono accedere a diversi servizi Google Cloud:
gcloud services enable \
aiplatform.googleapis.com \
secretmanager.googleapis.com \
cloudtrace.googleapis.com
Potrebbero essere necessari un paio di minuti. Visualizzerai:
Operation "operations/..." finished successfully.
Cosa forniscono queste API:
- aiplatform.googleapis.com: accesso ai modelli Gemini per il ragionamento degli agenti
- secretmanager.googleapis.com: spazio di archiviazione sicuro per le chiavi API (best practice per la produzione)
- cloudtrace.googleapis.com: osservabilità per la nostra traccia di responsabilità
Clona il codice iniziale
Recupera il repository del workshop con tutto il codice e le risorse del modello:
git clone https://github.com/ayoisio/adk-ap2-charity-agents
cd adk-ap2-charity-agents
git checkout codelab
Verifichiamo cosa abbiamo:
ls -la
Dovresti vedere:
charity_advisor/: dove creeremo i nostri agenti e strumentiscripts/- Script di supporto per test e verificadeploy.sh- Helper script for deploymentsetup.py- Helper script for module installation.env.template- File delle variabili di ambiente
Configura l'ambiente Python
Ora creeremo un ambiente Python isolato per il nostro progetto.
Crea e attiva l'ambiente virtuale
# Create the virtual environment
python3 -m venv venv
# Activate it
source venv/bin/activate
✅ Verifica: ora il prompt dovrebbe mostrare il prefisso (venv).
Installa le dipendenze
pip install -r charity_advisor/requirements.txt
pip install -e .
Viene installato:
- google-adk: il framework dell'Agent Development Kit
- google-cloud-aiplatform: integrazione di Vertex AI e Gemini
- ap2: SDK Agent Payments Protocol (da GitHub)
- python-dotenv: gestione delle variabili di ambiente
Il flag -e ti consente di importare moduli adk_ap2_charity_agents da qualsiasi posizione.
Configura file di ambiente
Crea la configurazione dal modello:
# 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
Dovresti vedere:
GOOGLE_CLOUD_PROJECT=your-actual-project-id
Verifica
Esegui lo script di verifica per assicurarti che tutto sia configurato correttamente:
python scripts/verify_setup.py
Dovresti vedere tutti i segni di spunta verdi:
======================================================================
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.
======================================================================
Risoluzione dei problemi
Passaggi successivi
Il tuo ambiente è ora completamente preparato. Sei ora in grado di eseguire le seguenti attività:
- ✅ Progetto Google Cloud configurato
- ✅ API richieste abilitate
- ✅ Librerie ADK e AP2 installate
- ✅ Codice modello pronto per la modifica
Nel modulo successivo, creerai il tuo primo agente AI in poche righe di codice e scoprirai perché gli agenti semplici non sono affidabili quando gestiscono transazioni finanziarie.
3. Il tuo primo agente e la scoperta del divario di fiducia

Dall'idea all'interazione
Nel modulo precedente, abbiamo preparato il nostro ambiente di sviluppo. Ora inizia il lavoro entusiasmante. Creeremo ed eseguiremo il nostro primo agente, gli daremo la sua prima funzionalità e, così facendo, scopriremo le sfide fondamentali che dobbiamo risolvere per renderlo davvero affidabile.
Questo modulo è la tua immagine "prima": il momento che rivela perché la creazione di agenti affidabili richiede più che dare a un LLM l'accesso agli strumenti.
Passaggio 1: esamina l'agente iniziale
Innanzitutto, diamo un'occhiata al modello del nostro primo agente. Contiene una struttura di base con segnaposto che completeremo nei passaggi successivi.
👉 Apri il file
charity_advisor/simple_agent/agent.py
nell'editor.
Visualizzerai:
"""
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=[]
)
Nota che i commenti segnaposto seguono un pattern: MODULE_3_STEP_X_DESCRIPTION. Sostituiremo questi marcatori per creare progressivamente il nostro agente.
Passaggio 2: importa i componenti richiesti
Prima di poter creare un'istanza della classe Agent o utilizzare lo strumento google_search, dobbiamo importarli nel nostro file.
👉 Trova:
# MODULE_3_STEP_2_IMPORT_COMPONENTS
👉 Sostituisci quella singola riga con:
from google.adk.agents import Agent
from google.adk.tools import google_search
Ora la classe Agent e lo strumento google_search sono disponibili nel nostro file.
Passaggio 3: scrivi l'istruzione dell'agente
L'istruzione è la "descrizione del lavoro" dell'agente: indica al modello LLM quando e come utilizzare i suoi strumenti. Scriviamone una che guidi il nostro agente a cercare informazioni sulle organizzazioni non profit.
👉 Trova:
# MODULE_3_STEP_3_WRITE_INSTRUCTION
instruction="""""",
👉 Sostituisci queste due righe con:
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.""",
Passaggio 4: aggiungi lo strumento di ricerca
Un agente senza strumenti è solo un conversatore. Diamo al nostro agente la sua prima funzionalità: la possibilità di cercare sul web.
👉 Trova:
# MODULE_3_STEP_4_ADD_TOOLS
tools=[]
👉 Sostituisci queste due righe con:
tools=[google_search]
Passaggio 5: verifica l'agente completo
Prima di eseguire il test, assicuriamoci che tutti i pezzi siano al loro posto.
👉 Il tuo profilo completo
charity_advisor/simple_agent/agent.py
Il file ora dovrebbe avere esattamente questo aspetto:
"""
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]
)
Passaggio 6: prova l'agente, mettendo in evidenza i problemi di attendibilità
Ora che l'agente è completamente configurato, testiamolo e analizziamo il suo comportamento. È qui che scopriamo perché gli agenti semplici non sono affidabili quando prendono decisioni finanziarie.
Test 1: il problema della scoperta
👉 Nel terminale Cloud Shell, esegui questo comando:
adk run charity_advisor/simple_agent
Dovresti visualizzare un output simile al seguente:
INFO:google.adk.agents:Loading agent from charity_advisor/simple_agent
INFO:google.adk.agents:Agent 'SimpleAgent' ready
[user]:
Il prompt [user]: è ora in attesa del tuo input.
👉 Al prompt [user], digita:
Can you find me a verified, highly-rated charity for children's literacy?
👉 Premi Invio e osserva la risposta.
Dopo qualche istante, l'agente sintetizzerà i risultati di ricerca in una risposta come questa:
In base a una ricerca sul web, alcune organizzazioni di beneficenza ben considerate per l'alfabetizzazione dei bambini sembrano essere Reading Is Fundamental e Room to Read. Fonti come Charity Navigator e GuideStar sono spesso consigliate per verificare il loro stato e le loro valutazioni. Ho anche trovato diverse discussioni online, anche su forum come Reddit, in cui gli utenti condividono esperienze personali con vari programmi di alfabetizzazione locali più piccoli.
Analizziamo la situazione. L'agente ha risolto il nostro problema?
❌ No. Ha replicato perfettamente l'esperienza umana descritta nel Modulo 1. Ha automatizzato con successo il processo di "fare una ricerca su Google" e ci ha restituito il problema della "paralisi da analisi".
Ciò mette in evidenza il primo problema di fiducia: la mancanza di dati autorevoli.
L'agente sta cercando sul web aperto, il che significa che:
- ✅ Ha trovato risultati rapidamente (esperienza utente migliorata)
- ❌ Mescola organizzazioni con valutazioni elevate con discussioni su Reddit (fonti inaffidabili)
- ❌ Non è in grado di distinguere tra enti di beneficenza verificati e potenziali frodi (nessuna verifica)
- ❌ Ci chiede di verificare le informazioni che ha appena fornito (scaricando la responsabilità)
Test 2: il problema di esecuzione
Ora passiamo al secondo test, quello cruciale. Nel prompt [user]:, prova a completare la donazione:
Okay, please donate $50 to Room to Read for me.
L'agente risponderà scusandosi e ammettendo la sua limitazione:
Mi risulta che vorresti fare una donazione, il che è meraviglioso. Tuttavia, sono un assistente di ricerca e non ho la possibilità di gestire transazioni finanziarie o elaborare pagamenti. Per fare una donazione, devi visitare direttamente il sito web ufficiale di Room to Read.
Questo è il secondo momento "Aha!", altrettanto importante.
Non solo non ci si può fidare dell'agente per trovare l'ente di beneficenza giusto, ma non ci si può ancora fidare che esegua l'azione di donazione.
👉 Premi
Ctrl+C
per uscire al termine del test.
Visualizzazione dei due intervalli
Cosa hai appena imparato
In questo modulo, hai creato e configurato correttamente il tuo primo agente AI. In questo modo, hai scoperto le due sfide fondamentali per la creazione di un sistema affidabile.
Concetti fondamentali padroneggiati
✅ The Agent Class:
- Componente di base di ADK
- Combina il ragionamento (cervello) degli LLM con gli strumenti (mani)
- Configurato con modello, istruzioni e strumenti
✅ Struttura basata sulle cartelle:
- Ogni agente si trova in una cartella separata
- ADK cerca
agent_folder/agent.py - Corri con
adk run agent_folder
✅ Elenco degli strumenti:
- Definisce le funzionalità dell'agente
- Il modello LLM decide quando e come utilizzare gli strumenti
- Può contenere più strumenti per azioni diverse
✅ Il prompt di istruzioni:
- Guida il comportamento dell'agente come una descrizione del lavoro
- Specifica ruolo, trigger, azioni e formato di output
- Fondamentale per un utilizzo affidabile dello strumento
✅ Il problema della fiducia:
- Lacuna nella scoperta: fonti non verificate, qualità mista
- Lacuna di esecuzione: nessuna funzionalità sicura, nessun consenso, nessun audit trail
Passaggi successivi
Nel modulo successivo inizieremo a creare la soluzione implementando l'architettura basata sui ruoli di AP2.
Creiamo il primo agente e vediamo la separazione dei ruoli in azione.
4. Creazione dell'agente Shopping - Rilevamento basato sui ruoli

Principi di base di affidabilità: separazione dei ruoli
Nell'ultimo modulo, hai scoperto che un agente semplice e generico non funziona per due motivi: non può fornire una scoperta affidabile e non può eseguire transazioni sicure. Ora inizieremo a risolvere questi problemi implementando il primo principio del protocollo di pagamento degli agenti: l'architettura basata sui ruoli.
Prima di scrivere qualsiasi codice, cerchiamo di capire perché questo principio è importante.
Principio AP2: separazione dei ruoli
Il problema degli agenti "Fai tutto"
Immagina di assumere una persona che sia il tuo consulente finanziario, contabile e intermediario di investimento. Conveniente? Sì. Sicuro? Assolutamente no. Avrebbe:
- I tuoi obiettivi di investimento (ruolo di consulente)
- Accesso ai tuoi account (ruolo di contabile)
- Autorizzazione a spostare il denaro (ruolo di intermediario)
Se questa persona viene compromessa o commette un errore, tutto è a rischio.
Soluzione di AP2: un agente, un lavoro
AP2 applica il principio di separazione delle competenze per creare confini di trust:
Perché è importante:
- ✅ Raggio di esplosione limitato: se l'agente Shopping viene compromesso, l'autore dell'attacco non può accedere alle credenziali di pagamento
- ✅ Privacy: il fornitore delle credenziali non vede mai la tua conversazione di acquisto
- ✅ Conformità: è più facile soddisfare i requisiti PCI-DSS quando i dati di pagamento sono isolati
- ✅ Responsabilità: responsabilità chiara per ogni passaggio
Modalità di comunicazione degli agenti: lo stato come blocco note condiviso
Poiché gli agenti non possono accedere direttamente ai dati degli altri, comunicano tramite uno stato condiviso. Consideralo come una lavagna su cui tutti gli agenti possono scrivere e leggere:
# 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...
In questo modo manteniamo i limiti di fiducia consentendo la collaborazione.
Il nostro primo agente: l'agente per lo shopping
La responsabilità dell'agente per lo shopping è semplice e mirata:
- Utilizza lo strumento
find_charitiesper eseguire query nel nostro database attendibile - Presentare le opzioni all'utente
- Utilizza lo strumento
save_user_choiceper creare un IntentMandate e salvarlo nello stato - Trasferimento all'agente successivo (il commerciante)
È tutto. Nessuna gestione dei pagamenti, nessuna creazione di carrelli: solo rilevamento e trasferimento.
Vediamo come fare passo dopo passo.
Passaggio 1: aggiungi Input Validation Helper
Quando crei strumenti di produzione, la convalida dell'input è fondamentale. Creiamo una funzione helper che convalidi i dati dell'ente di beneficenza prima di salvarli nello stato.
👉 Apri
charity_advisor/tools/charity_tools.py
In alto vedrai la funzione find_charities (già completata). Scorri verso il basso per trovare:
# MODULE_4_STEP_1_ADD_VALIDATION_HELPER
👉 Sostituisci quella singola riga con:
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, ""
Passaggio 2: aggiungi lo strumento di creazione di IntentMandate
Ora creiamo l'helper che crea la struttura AP2 IntentMandate. Si tratta di una delle tre credenziali verificabili in AP2.
👉 Nello stesso file, trova:
# MODULE_4_STEP_2_ADD_INTENTMANDATE_CREATION_HELPER
👉 Sostituisci quella singola riga con:
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
Passaggio 3: crea lo strumento di trasferimento dello stato con IntentMandate
Ora creiamo lo strumento che crea IntentMandate e lo salva nello stato.
👉 Nello stesso file, scorri verso il basso fino alla sezione
save_user_choice
funzione . Trova:
# MODULE_4_STEP_3_COMPLETE_SAVE_TOOL
👉 Sostituisci quella singola riga con:
# 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"]
}
Passaggio 4: aggiungi lo strumento di formattazione della visualizzazione
Prima di creare l'agente, aggiungiamo un altro helper che formatti i dati dell'ente di beneficenza per una visualizzazione intuitiva.
👉 Scorri per trovare:
# MODULE_4_STEP_4_ADD_FORMATTING_HELPER
👉 Sostituisci quella singola riga con:
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
Passaggio 5: crea l'agente Shopping - importa i componenti
Ora che i nostri strumenti sono completi e robusti, creiamo l'agente che li utilizzerà.
👉 Apri
charity_advisor/shopping_agent/agent.py
Vedrai un modello con commenti segnaposto. Vediamo come fare passo dopo passo.
👉 Trova:
# MODULE_4_STEP_5_IMPORT_COMPONENTS
👉 Sostituisci quella singola riga con:
from google.adk.agents import Agent
from google.adk.tools import FunctionTool
from charity_advisor.tools.charity_tools import find_charities, save_user_choice
Passaggio 6: scrivi le istruzioni per l'agente
L'istruzione è il punto in cui definiamo la descrizione del lavoro e il flusso di lavoro dell'agente. Questo è fondamentale: un'istruzione scritta male porta a un comportamento inaffidabile.
👉 Trova:
# MODULE_4_STEP_6_WRITE_INSTRUCTION
instruction="""""",
👉 Sostituisci queste due righe con:
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.""",
Passaggio 7: aggiungi gli strumenti all'agente
Ora concediamo all'agente l'accesso a entrambi gli strumenti.
👉 Trova:
# MODULE_4_STEP_7_ADD_TOOLS
👉 Sostituisci queste due righe con:
tools=[
FunctionTool(func=find_charities),
FunctionTool(func=save_user_choice)
]
Passaggio 8: verifica l'agente completo
Verifichiamo che tutto sia cablato correttamente.
👉 Il tuo profilo completo
charity_advisor/shopping_agent/agent.py
Ora dovrebbe avere questo aspetto:
"""
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)
]
)
✅ Perfetto. Hai creato un agente conforme ad AP2 e di qualità di produzione con:
- Convalida dell'input
- Creazione corretta di IntentMandate utilizzando i modelli Pydantic AP2
- Output formattato
- Definisci confini chiari per i ruoli
- Istruzioni dettagliate
- Gestione degli errori
Passaggio 9: prova l'agente Shopping
Verifichiamo che il nostro agente funzioni correttamente, crei IntentMandates appropriati e rispetti i suoi limiti.
👉 Nel terminale Cloud Shell, esegui:
adk run charity_advisor/shopping_agent
Viene visualizzato il prompt [user]:.
Test 1: Discovery con Trusted Database
👉 Tipo:
I want to donate to an education charity. What are my options?
Dopo qualche istante, l'agente fornirà una risposta. Grazie al nostro _format_charity_display, i risultati sono formattati in modo ottimale:
Nel nostro database ho trovato tre enti di beneficenza per l'istruzione verificati:
Room to Read (EIN: 77-0479905)
⭐ Valutazione: 4,9/5,0
💰 Efficienza: l'88% dei fondi viene destinato ai programmi
📋 Missione: trasforma la vita di milioni di bambini nelle comunità a basso reddito concentrandosi sull'alfabetizzazione e sull'uguaglianza di genere nell'istruzione.
Teach For America (EIN: 13-3541913)
⭐ Valutazione: 4,7/5,0
💰 Efficienza: l'81% dei fondi viene destinato ai programmi
📋 Missione: si impegna a espandere le opportunità di istruzione per i bambini che vivono in condizioni di difficoltà.
Tech Education Alliance (EIN: 45-2345678)
⭐ Valutazione: 4,8/5,0
💰 Efficienza: il 92% dei fondi viene destinato ai programmi
📋 Missione: porta l'educazione informatica nelle scuole svantaggiate.
Quale ente di beneficenza vuoi sostenere e quanto vuoi donare?
Confrontalo con l'agente semplice del modulo 3, che ti ha fornito risultati di Google non verificati. Questa è la differenza tra rilevamento non attendibile e attendibile.
Test 2: registra la scelta dell'utente e crea IntentMandate
👉 Tipo:
I'll donate $50 to Room to Read.
Dovresti vedere l'agente chiamare lo strumento save_user_choice con i parametri corretti. Grazie alla creazione di IntentMandate utilizzando i modelli AP2, vedrai una risposta più strutturata:
Perfetto! Ho creato un IntentMandate per la tua donazione:
Dettagli dell'intent:
- ID intento: intent_774795905_1730927536
- Importo: 50 $ a Room to Read (EIN: 77-0479905)
- Scade: 2024-11-07T15:32:16Z (tra 1 ora)
Questo IntentMandate acquisisce la tua intenzione di donare e include vincoli per garantire che la transazione venga elaborata in modo sicuro. Ora inoltro la richiesta al nostro sistema di elaborazione dei pagamenti sicuro per creare un'offerta formale e completare la donazione.
Dietro le quinte, sono successe diverse cose:
_validate_charity_data()ha verificato gli input_create_intent_mandate()ha utilizzato il modello Pydantic AP2 per creare la struttura:
# 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"
})
- Lo strumento ha scritto SOLO IntentMandate nello stato condiviso:
state["intent_mandate"] = intent_mandate_dict
- Un messaggio di successo con i dettagli dell'intent è stato restituito al LLM
- Il modello LLM ha composto il messaggio utente finale
Questo IntentMandate è ora pronto per essere ritirato dall'agente del commerciante (modulo 5). Il commerciante estrarrà il nome e l'importo dell'ente di beneficenza da IntentMandate e verificherà che non sia scaduto.
Test 3: convalida in azione
Verifichiamo che l'helper di convalida rilevi input errati.
👉 Tipo:
I'll donate -$25 to Room to Read.
L'agente deve rilevare l'importo non valido:
Ho riscontrato un problema: l'importo della donazione deve essere positivo.Ho ricevuto: -25, 0 €
Specifica un importo della donazione positivo e creerò l'IntentMandate per te.
Questo è un esempio di programmazione difensiva in azione. Il nostro helper _validate_charity_data ha impedito l'inserimento di dati non validi nel sistema e la creazione di un IntentMandate non valido.
Test 4: verifica del limite di attendibilità
👉 Prova a chiedere all'agente di elaborare il pagamento:
Now process my credit card payment.
L'agente deve rifiutare, rispettando i limiti del suo ruolo:
Non ho la possibilità di elaborare i pagamenti, non fa parte del mio ruolo. Il mio compito è aiutarti a trovare enti di beneficenza verificati e a creare l'IntentMandate che acquisisce la tua intenzione di donare.
Il tuo IntentMandate è già stato creato e inviato al nostro elaboratore di pagamenti sicuro. L'agente del commerciante creerà un'offerta formale (CartMandate), quindi il fornitore di credenziali gestirà il pagamento effettivo con il tuo consenso esplicito.
Questo è il limite di attendibilità in azione. L'agente sa di non essere autorizzato a gestire i dati di pagamento e le sue istruzioni lo guidano esplicitamente a spiegarlo agli utenti, insegnando loro anche il concetto di IntentMandate.
👉 Premi
Ctrl+C
per uscire al termine del test.
Cosa hai appena creato
Hai implementato correttamente il primo elemento dell'architettura AP2 con la creazione corretta di IntentMandate utilizzando i modelli Pydantic di AP2.
Concetti fondamentali padroneggiati
✅ Architettura basata sui ruoli:
- Ogni agente ha un compito chiaramente definito
- Gli agenti comunicano tramite uno stato condiviso, non tramite accesso diretto
- I limiti di trust riducono l'impatto della compromissione
✅ IntentMandate (AP2 Credential #1):
- Creato utilizzando i modelli Pydantic AP2 ufficiali per la convalida
- Acquisizione strutturata dell'intenzione dell'utente
- Include la scadenza per la sicurezza (impedisce gli attacchi di replay)
- Specifica i vincoli (commercianti, rimborsabilità, conferma)
- Descrizione in linguaggio naturale per gli esseri umani
- Leggibile automaticamente per gli agenti
- Modello convalidato prima della conversione in dizionario
✅ Stato come ricordo condiviso:
tool_context.stateè il "blocco note" a cui possono accedere tutti gli agenti- Scrittura nello stato = rendere disponibili le credenziali verificabili
- Lettura dallo stato = utilizzo e convalida delle credenziali
- Gli agenti downstream estraggono ciò di cui hanno bisogno dalle credenziali
✅ FunctionTool:
- Converte le funzioni Python in strumenti richiamabili da LLM
- Si basa su docstring e suggerimenti sui tipi per la comprensione dell'LLM
- Gestisce automaticamente la chiamata
- Componibilità degli strumenti: strumenti piccoli e mirati > strumenti monolitici
✅ Istruzioni per l'agente:
- Guida passo passo al flusso di lavoro
- Limiti espliciti ("NON...")
- Specifiche dei parametri per evitare errori
- Definizioni tecniche (che cos'è IntentMandate)
- Gestione dei casi limite (cosa dire quando…)
Passaggi successivi
Nel modulo successivo, creeremo l'agente commerciante per ricevere l'IntentMandate e creare la seconda credenziale verificabile: CartMandate.
L'agente Shopping ha creato un IntentMandate che acquisisce l'intenzione dell'utente con scadenza. Ora abbiamo bisogno di un agente che legga questa credenziale, ne convalidi la scadenza e crei un'offerta formale firmata che reciti: "Io, il commerciante, onorerò questo prezzo e consegnerò questi beni".
Creiamo l'agente Merchant e vediamo la seconda credenziale AP2 in azione.
5. Creazione dell'agente commerciante - Offerte vincolanti e CartMandate

Dalla scoperta all'impegno
Nel modulo precedente, hai creato l'agente Shopping, uno specialista che trova enti di beneficenza verificati e crea un IntentMandate che acquisisce l'intenzione dell'utente. Ora abbiamo bisogno di un agente che riceva questa Intenzione di mandato e crei un'offerta formale e vincolante.
È qui che entra in gioco il secondo principio chiave di AP2: credenziali verificabili tramite CartMandate.
Principio AP2: CartMandate e offerte vincolanti
Perché abbiamo bisogno di un ruolo commerciante
Nel modulo 4, l'agente Shopping ha creato un IntentMandate e l'ha salvato nello stato:
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"
}
Ma questa è solo l'intenzione dell'utente. Prima di poter elaborare qualsiasi pagamento, abbiamo bisogno di:
- Una struttura dell'offerta formale che i sistemi di pagamento comprendono
- Prova che il commerciante rispetterà questo prezzo
- Un impegno vincolante che non può essere modificato a metà transazione
- Convalida che l'intent non sia scaduto
Questo è il lavoro dell'agente del mercante.
Che cos'è un CartMandate?
Un CartMandate è il termine utilizzato da AP2 per indicare un "carrello degli acquisti digitale" che funge da offerta vincolante. È strutturato in base allo standard PaymentRequest di W3C, il che significa che:
- Gli elaboratori dei pagamenti di tutto il mondo riconoscono il formato
- Contiene tutti i dettagli delle transazioni in modo standardizzato
- Può essere firmato crittograficamente per dimostrarne l'autenticità
Consideralo come un preventivo scritto di un appaltatore:
- ❌ Verbale: "Sì, posso fare questo lavoro per circa 50 dollari"
- ✅ Preventivo scritto: costi dettagliati, totale, firma, data
Il preventivo scritto è vincolante. CartMandate è l'equivalente digitale.
La struttura di un CartMandate
Un CartMandate in AP2 ha una struttura nidificata specifica:
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
}
Tre componenti principali:
1. contents: il wrapper del carrello contenente:
- ID carrello e scadenza
- Nome del commerciante
- W3C PaymentRequest
2. payment_request (all'interno dei contenuti) - Che cosa viene acquistato:
- method_data: Tipi di pagamento accettati
- Dettagli: articoli e totale
- opzioni: Spedizione, requisiti per i dati del pagatore
3. merchant_authorization: firma crittografica
Firme del commerciante: prova dell'impegno
La firma del commerciante è fondamentale. Dimostra:
- Questa offerta proviene da un commerciante autorizzato
- Il commerciante si impegna a rispettare questo prezzo esatto
- L'offerta non è stata manomessa dalla sua creazione
In produzione, si tratta di una firma crittografica che utilizza PKI (Public Key Infrastructure) o JWT (JSON Web Tokens). Per il nostro workshop didattico, simuleremo questa situazione 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]}"
La nostra missione: creare l'agente commerciante
L'agente del commerciante:
- Read the IntentMandate from state (what Shopping Agent wrote)
- Verifica che l'intent non sia scaduto
- Estrai il nome dell'ente di beneficenza, l'importo e altri dettagli.
- Crea una struttura PaymentRequest conforme al W3C utilizzando i modelli Pydantic AP2
- Avvolgilo nel CartMandate di AP2 con scadenza
- Aggiungere una firma commerciante simulata
- Scrivi CartMandate per indicare lo stato del provider di credenziali (modulo successivo)
Vediamo come fare passo dopo passo.
Passaggio 1: aggiungi Expiry Validation Helper
Innanzitutto, configuriamo il file degli strumenti correlati al commerciante e aggiungiamo un helper per convalidare la scadenza dell'intento di addebito.
👉 Apri
charity_advisor/tools/merchant_tools.py
Aggiungiamo la convalida della scadenza:
👉 Trova:
# MODULE_5_STEP_1_ADD_EXPIRY_VALIDATION_HELPER
👉 Sostituisci quella singola riga con:
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}"
Passaggio 2: aggiungi lo strumento di generazione della firma
Ora creiamo un helper che generi la firma simulata del commerciante.
👉 Trova:
# MODULE_5_STEP_2_ADD_SIGNATURE_HELPER
👉 Sostituisci quella singola riga con:
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
Passaggio 3A: crea la firma e la configurazione dello strumento
Ora iniziamo a creare lo strumento principale. Lo creeremo in modo incrementale in quattro passaggi secondari. Innanzitutto, la firma della funzione e la configurazione iniziale.
👉 Trova:
# MODULE_5_STEP_3A_CREATE_TOOL_SIGNATURE
👉 Sostituisci quella singola riga con:
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
Passaggio 3B: aggiungi la logica di convalida
Ora aggiungiamo la logica per leggere e convalidare l'IntentMandate utilizzando i modelli Pydantic AP2 ed estraiamo i dati di cui abbiamo bisogno.
👉 Trova:
# MODULE_5_STEP_3B_ADD_VALIDATION_LOGIC
👉 Sostituisci quella singola riga con:
# 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
Passaggio 3C: crea la struttura CartMandate
Ora creiamo la struttura PaymentRequest conforme a W3C e la inseriamo in AP2 CartMandate utilizzando i modelli Pydantic.
👉 Trova:
# MODULE_5_STEP_3C_CREATE_CARTMANDATE_STRUCTURE
👉 Sostituisci quella singola riga con:
# 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
Passaggio 3D: aggiungi la firma e salva nello stato
Infine, firmiamo CartMandate utilizzando il nostro modello Pydantic e salviamolo nello stato per l'agente successivo.
👉 Trova:
# MODULE_5_STEP_3D_ADD_SIGNATURE_AND_SAVE
👉 Sostituisci quella singola riga con:
# 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
}
Passaggio 4: crea l'agente commerciante - importa i componenti
Ora creiamo l'agente che utilizzerà questo strumento.
👉 Apri
charity_advisor/merchant_agent/agent.py
Vedrai un modello con segnaposto. Iniziamo importando ciò che ci serve.
👉 Trova:
# MODULE_5_STEP_4_IMPORT_COMPONENTS
👉 Sostituisci quella singola riga con:
from google.adk.agents import Agent
from google.adk.tools import FunctionTool
from charity_advisor.tools.merchant_tools import create_cart_mandate
Passaggio 5: scrivi le istruzioni per l'agente Merchant
Ora scriviamo l'istruzione che indica all'agente quando e come utilizzare il suo strumento.
👉 Trova:
# MODULE_5_STEP_5_WRITE_INSTRUCTION
instruction="""""",
👉 Sostituisci queste due righe con:
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.""",
Passaggio 6: aggiungi gli strumenti all'agente Merchant
👉 Trova:
# MODULE_5_STEP_6_ADD_TOOLS
tools=[],
👉 Sostituisci queste due righe con:
tools=[
FunctionTool(func=create_cart_mandate)
],
Passaggio 7: verifica l'agente commerciante completo
Verifichiamo che tutto sia cablato correttamente.
👉 Il tuo profilo completo
charity_advisor/merchant_agent/agent.py
Ora dovrebbe avere questo aspetto:
"""
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."""
)
✅ Checkpoint: ora hai un agente commerciante completo con la corretta creazione di AP2 CartMandate utilizzando i modelli Pydantic.
Passaggio 8: testa l'agente commerciante
Ora verifichiamo che il nostro agente crei correttamente CartMandates con le firme e con la convalida della scadenza.
Configurazione del test: esegui lo script di test
👉 Nel terminale Cloud Shell, esegui:
python scripts/test_merchant.py
Output previsto:
======================================================================
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
======================================================================
Test 2: verifica la conformità al W3C
Verifichiamo che la nostra struttura CartMandate sia pienamente conforme agli standard AP2 e W3C PaymentRequest.
👉 Esegui lo script di convalida:
python scripts/validate_cartmandate.py
Output previsto:
======================================================================
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
======================================================================
Cosa hai appena creato
Hai implementato correttamente CartMandate di AP2 utilizzando i modelli Pydantic per la struttura corretta, la convalida della scadenza e le firme del commerciante.
Concetti fondamentali padroneggiati
✅ CartMandate (AP2 Credential #2):
- Creato utilizzando i modelli Pydantic AP2 ufficiali
- Struttura AP2 con wrapper dei contenuti
- W3C PaymentRequest nidificata all'interno
- Scadenza del carrello (inferiore all'intenzione)
- Firma del commerciante per l'impegno vincolante
- La convalida del modello garantisce la conformità alle specifiche
✅ Convalida della scadenza:
- Reading IntentMandate from state
- Convalida della struttura con
IntentMandate.model_validate() - Analisi dei timestamp ISO 8601
- Confronto con l'ora attuale
- Funzionalità di sicurezza che impedisce l'elaborazione obsoleta
✅ Firma del commerciante:
- Dimostra autenticità e impegno
- Generato dal modello Pydantic convalidato
- Utilizza
model_dump(mode='json')per la rappresentazione canonica - Simulato con SHA-256 per l'istruzione
- La produzione utilizza PKI/JWT
- Segni del modello di contenuti, non dizionari
✅ W3C PaymentRequest:
- Creato utilizzando il modello Pydantic PaymentRequest di AP2
- Standard di settore per i dati di pagamento
- Nidificato all'interno della struttura AP2
- Contiene method_data, dettagli, opzioni
- Consente l'interoperabilità
✅ Credential Chain con modelli:
- Shopping → IntentMandate (convalidato)
- Merchant reads IntentMandate → CartMandate (entrambi i modelli convalidati)
- Il provider di credenziali leggerà CartMandate → PaymentMandate
- Ogni passaggio convalida le credenziali precedenti utilizzando Pydantic
✅ Sviluppo basato su modelli:
- Convalida dell'input tramite
model_validate() - Costruzione type-safe
- Serializzazione automatica tramite
model_dump() - Pattern pronti per la produzione
Passaggi successivi
Nel prossimo modulo creeremo il fornitore di credenziali per elaborare i pagamenti in modo sicuro.
L'agente del commerciante ha creato un'offerta vincolante con scadenza utilizzando i modelli AP2. Ora abbiamo bisogno di un agente che legga CartMandate, ottenga il consenso dell'utente ed esegua il pagamento.
Creiamo il provider di credenziali e completiamo la catena di credenziali AP2.
6. Creazione del fornitore di credenziali - Esecuzione di pagamenti sicuri

Dall'offerta vincolante all'esecuzione del pagamento
Nel modulo 5, hai creato l'agente commerciante, uno specialista che legge i mandati di intento, verifica che non siano scaduti e crea mandati del carrello vincolanti con le firme del commerciante. Ora abbiamo bisogno di un agente che riceva il CartMandate ed esegua il pagamento effettivo.
È qui che entra in gioco il terzo e ultimo principio di AP2: l'esecuzione sicura dei pagamenti tramite PaymentMandate.
Principio AP2: mandato di pagamento ed esecuzione del pagamento
Perché abbiamo bisogno di un ruolo di fornitore di credenziali
Nel modulo 5, l'agente commerciante ha creato un CartMandate e l'ha salvato nello stato:
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"
}
Ma questa è solo un'offerta vincolante. Prima che il pagamento possa essere eseguito, abbiamo bisogno di:
- Convalida che il carrello non sia scaduto
- Consenso dell'utente a procedere con il pagamento
- Una credenziale che autorizza l'esecuzione del pagamento
- Elaborazione effettiva dei pagamenti (o simulazione per il nostro workshop)
Questo è il compito del fornitore di credenziali.
Che cos'è un PaymentMandate?
Un PaymentMandate è il termine di AP2 per l'autorizzazione finale che consente l'esecuzione del pagamento. È la terza e ultima credenziale verificabile nella catena AP2.
Pensa alle tre credenziali come a una procedura di firma di un contratto:
- IntentMandate: "Mi interessa acquistare questo prodotto" (lettera di intenti)
- CartMandate: "Io, il commerciante, offro di vendere a questo prezzo" (preventivo scritto)
- PaymentMandate: "Ti autorizzo ad addebitare il mio metodo di pagamento" (contratto firmato)
Il pagamento può essere eseguito solo dopo che sono presenti tutte e tre le credenziali.
Struttura di un PaymentMandate
Un PaymentMandate in AP2 ha una struttura specifica:
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"
}
Componenti chiave:
1. payment_mandate_contents: il wrapper di autorizzazione contenente:
- payment_mandate_id: identificatore univoco
- payment_details_id: rimanda a CartMandate
- user_consent: indica se l'utente ha approvato
- importo: importo del pagamento (estratto da CartMandate)
2. agent_present: indica se si tratta di un flusso human-in-the-loop
3. timestamp: data e ora in cui è stata creata l'autorizzazione
La nostra missione: creare il provider di credenziali
Il provider di credenziali:
- Leggi il CartMandate dallo stato (ciò che ha scritto l'agente del commerciante)
- Verifica che il carrello non sia scaduto utilizzando i modelli Pydantic AP2
- Estrai i dettagli di pagamento dalla struttura nidificata
- Crea un PaymentMandate con il consenso dell'utente utilizzando i modelli AP2
- Simula l'elaborazione del pagamento (in produzione, chiamerebbe l'API di pagamento reale)
- Scrivi PaymentMandate e il risultato del pagamento nello stato
Vediamo come fare passo dopo passo.
Passaggio 1: aggiungi l'helper per la convalida della scadenza del carrello
Innanzitutto, creiamo un helper che convalidi che CartMandate non sia scaduto, proprio come abbiamo convalidato la scadenza di IntentMandate nel modulo 5.
👉 Apri
charity_advisor/tools/payment_tools.py
Aggiungiamo la convalida della scadenza:
👉 Trova:
# MODULE_6_STEP_1_ADD_CART_EXPIRY_VALIDATION_HELPER
👉 Sostituisci quella singola riga con:
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}"
Passaggio 2: aggiungi PaymentMandate Creation Helper
Ora creiamo un helper che crea la struttura PaymentMandate utilizzando i modelli Pydantic AP2 ufficiali.
👉 Trova:
# MODULE_6_STEP_2_ADD_PAYMENT_MANDATE_CREATION_HELPER
👉 Sostituisci quella singola riga con:
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
Passaggio 3A: crea la firma e la configurazione dello strumento
Ora iniziamo a creare lo strumento principale in modo incrementale. Innanzitutto, la firma della funzione e la configurazione iniziale.
👉 Trova:
# MODULE_6_STEP_3A_CREATE_TOOL_SIGNATURE
👉 Sostituisci quella singola riga con:
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
Passaggio 3B: convalida CartMandate
Ora aggiungiamo la logica per leggere, convalidare CartMandate utilizzando i modelli Pydantic AP2 e controllare la scadenza.
👉 Trova:
# MODULE_6_STEP_3B_VALIDATE_CARTMANDATE
👉 Sostituisci quella singola riga con:
# 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
Passaggio 3C: estrai i dettagli di pagamento dalla struttura nidificata
Ora esploriamo il modello CartMandate convalidato per estrarre i dati di pagamento di cui abbiamo bisogno.
👉 Trova:
# MODULE_6_STEP_3C_EXTRACT_PAYMENT_DETAILS
👉 Sostituisci quella singola riga con:
# 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
Passaggio 3D: crea PaymentMandate e simula il pagamento
Infine, creiamo PaymentMandate utilizzando il nostro helper basato su Pydantic, simuliamo l'elaborazione dei pagamenti e salviamo tutto nello stato.
👉 Trova:
# MODULE_6_STEP_3D_CREATE_PAYMENTMANDATE_AND_SIMULATE
👉 Sostituisci quella singola riga con:
# 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"]
}
Passaggio 4: crea l'agente del fornitore di credenziali - Importa componenti
Ora creiamo l'agente che utilizza questo strumento.
👉 Apri
charity_advisor/credentials_provider/agent.py
Vedrai un modello con segnaposto. Iniziamo importando ciò che ci serve.
👉 Trova:
# MODULE_6_STEP_4_IMPORT_COMPONENTS
👉 Sostituisci quella singola riga con:
from google.adk.agents import Agent
from google.adk.tools import FunctionTool
from charity_advisor.tools.payment_tools import create_payment_mandate
Passaggio 5: scrivi l'istruzione del provider di credenziali
Ora scriviamo l'istruzione che guida l'agente.
👉 Trova:
# MODULE_6_STEP_5_WRITE_INSTRUCTION
instruction="""""",
👉 Sostituisci queste due righe con:
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""",
Passaggio 6: aggiungi Strumenti al fornitore di credenziali
👉 Trova:
# MODULE_6_STEP_6_ADD_TOOLS
tools=[],
👉 Sostituisci queste due righe con:
tools=[
FunctionTool(func=create_payment_mandate)
],
Passaggio 7: verifica il provider di credenziali completo
Verifichiamo che tutto sia cablato correttamente.
👉 Il tuo profilo completo
charity_advisor/credentials_provider/agent.py
Ora dovrebbe avere questo aspetto:
"""
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"""
)
✅ Checkpoint: ora hai un fornitore di credenziali completo con la lettura corretta di CartMandate e la creazione di PaymentMandate utilizzando i modelli Pydantic AP2.
Passaggio 8: prova il provider di credenziali
Ora verifichiamo che il nostro agente elabori correttamente i pagamenti e completi la catena di credenziali.
👉 Nel terminale Cloud Shell, esegui:
python scripts/test_credentials_provider.py
Output previsto:
======================================================================
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
======================================================================
Passaggio 9: testa la pipeline completa con tre agenti
Ora proviamo tutti e tre gli agenti insieme.
👉 Esegui il test della pipeline completa:
python scripts/test_full_pipeline.py
Output previsto:
======================================================================
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
======================================================================
Questa è la catena completa di credenziali AP2 in azione.
Ogni agente:
- Legge una credenziale dallo stato
- Lo convalida utilizzando i modelli Pydantic (struttura + controllo della scadenza)
- Crea la credenziale successiva utilizzando i modelli AP2
- Scrive allo stato per l'agente successivo
Cosa hai appena creato
Hai completato correttamente la catena di credenziali a tre agenti AP2 con la corretta convalida della struttura utilizzando i modelli Pydantic e la simulazione di pagamento.
Concetti fondamentali padroneggiati
✅ PaymentMandate (AP2 Credential #3):
- Creato utilizzando i modelli Pydantic AP2 ufficiali
- Credenziale finale che autorizza l'esecuzione del pagamento
- Link a CartMandate tramite payment_details_id
- Registra il consenso dell'utente e il timestamp
- Contiene l'importo del pagamento estratto da CartMandate
- Include il flag agent_present per human-in-the-loop
- La convalida del modello garantisce la conformità alle specifiche
✅ Lettura da CartMandate:
- Convalida la struttura con
CartMandate.model_validate() - Accesso agli attributi con controllo del tipo:
cart_model.contents.payment_request.details.total.amount - Informazioni sul wrapper AP2 e sulla separazione standard W3C
- Estrai in modo sicuro merchant_name, amount e currency dal modello
- Pydantic rileva automaticamente gli errori di struttura
✅ Convalida scadenza carrello:
- Accetta il modello Pydantic
CartMandateconvalidato - Legge da
cart.contents.cart_expiry(accesso agli attributi) - Funzionalità di sicurezza che impedisce l'elaborazione di carrelli obsoleti
- Durata più breve (15 minuti) rispetto all'intento (1 ora)
✅ Simulazione di pagamento:
- Simulazione didattica dell'elaboratore dei pagamenti reale
- Genera l'ID transazione
- Registra payment_result nello stato
- Chiaramente contrassegnato come simulazione (flag simulation: True)
✅ Completa la catena AP2 con i modelli:
- Tre agenti, tre credenziali, tre convalide Pydantic
- Ogni agente convalida la struttura delle credenziali precedenti utilizzando i modelli
- Ogni credenziale è collegata alla precedente per la traccia di controllo
- I trasferimenti basati sullo stato mantengono la separazione dei ruoli
- Sicurezza dei tipi in tutta la catena
✅ Sviluppo basato su modelli:
- Convalida dell'input tramite
model_validate() - Costruzione type-safe con modelli nidificati
- Serializzazione automatica tramite
model_dump(mode='json') - Pattern pronti per la produzione fin dall'inizio
Passaggi successivi
Nel modulo successivo, creeremo l'agente orchestratore che coordina tutti e tre gli agenti specializzati.
Hai creato tre potenti agenti specializzati utilizzando i modelli Pydantic AP2. Ora creiamo il direttore d'orchestra che li coordina in un'esperienza di donazione senza interruzioni.
Creiamo l'agente di orchestrazione e vediamo il sistema completo in azione.
7. Orchestrazione: unire tutti gli elementi
Dagli specialisti a un'esperienza senza interruzioni
Nei moduli precedenti, hai creato tre agenti specializzati:
- Agente Shopping: trova enti benefici, crea IntentMandate
- Agente commerciante: crea CartMandate da IntentMandate
- Fornitore di credenziali: crea PaymentMandate, elabora il pagamento
Questi agenti rientrano naturalmente in due fasi:
- Fase 1 (Shopping): conversazione in più turni per trovare e selezionare l'ente di beneficenza
- Fase 2 (elaborazione): esecuzione atomica della creazione dell'offerta e del pagamento
Ma al momento dovresti orchestrare manualmente queste fasi.
È qui che brillano i pattern di orchestrazione di ADK.
Principio AP2: l'orchestrazione applica i limiti di trust
Perché l'orchestrazione è importante per la sicurezza
L'orchestrazione non riguarda solo la comodità, ma anche l'applicazione di limiti di attendibilità tramite l'architettura.
Senza orchestrazione:
# 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 orchestrazione:
# 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 pipeline sequenziale garantisce:
- ✅ IntentMandate creato prima di CartMandate
- ✅ CartMandate creato prima dell'elaborazione del pagamento
- ✅ Ogni agente viene eseguito nel proprio contesto isolato
- ✅ Lo stato avanza nella catena di credenziali
La nostra missione: creare il sistema completo
Creeremo due livelli:
Livello 1: la pipeline di elaborazione (SequentialAgent)
- Cavi insieme Commerciante → Credenziali
- Viene eseguito automaticamente in sequenza dopo la selezione dell'ente di beneficenza
- Esecuzione atomica dell'offerta e del pagamento
Livello 2: l'orchestratore principale (agente rivolto agli utenti)
- Personalità amichevole
- Delegati a shopping_agent per la selezione dell'ente di beneficenza
- Delega alla pipeline di elaborazione dopo la creazione di IntentMandate
- Gestisce le transizioni di conversazione e di fase
Questo approccio a due livelli corrisponde al flusso naturale:
- Fase di acquisto: conversazione a più turni (l'utente naviga, fa domande, decide)
- Fase di elaborazione: esecuzione atomica (offerta → pagamento)
Creiamo entrambi.
Passaggio 1: importa i componenti di orchestrazione
Innanzitutto, configuriamo il file di orchestrazione con le importazioni necessarie.
👉 Apri
charity_advisor/agent.py
Iniziamo con le importazioni:
👉 Trova:
# MODULE_7_STEP_1_IMPORT_COMPONENTS
👉 Sostituisci quella singola riga con:
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
Passaggio 2: crea la pipeline di elaborazione
Ora creiamo la pipeline che esegue la creazione dell'offerta e l'elaborazione dei pagamenti in modo atomico.
👉 Trova:
# MODULE_7_STEP_2_CREATE_SEQUENTIAL_PIPELINE
👉 Sostituisci queste due righe con:
# 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
]
)
Passaggio 3A: crea la configurazione dell'agente principale
Ora creiamo l'agente rivolto agli utenti che orchestra entrambe le fasi. Lo faremo in tre parti: configurazione (3A), istruzioni (3B) e subagenti (3C).
👉 Trova:
# MODULE_7_STEP_3A_CREATE_ROOT_AGENT_SETUP
👉 Sostituisci quella singola riga con:
# 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
Passaggio 3B: scrivi l'istruzione dell'agente principale
Ora aggiungiamo l'istruzione che guida il comportamento del consulente dell'ente di beneficenza in entrambe le fasi.
👉 Trova:
# MODULE_7_STEP_3B_WRITE_ROOT_AGENT_INSTRUCTION
👉 Sostituisci quella singola riga con:
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
Passaggio 3C: aggiungi i subagenti
Infine, concediamo all'advisor dell'ente di beneficenza l'accesso sia all'agente di shopping sia alla pipeline di elaborazione e chiudiamo la definizione dell'agente.
👉 Trova:
# MODULE_7_STEP_3C_ADD_ROOT_AGENT_SUBAGENTS
👉 Sostituisci quella singola riga con:
sub_agents=[
shopping_agent,
donation_processing_pipeline
]
)
Passaggio 4: verifica il sistema completo
Verifichiamo che l'orchestrazione sia cablata correttamente.
👉 Il tuo profilo completo
charity_advisor/agent.py
Ora dovrebbe avere questo aspetto:
"""
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
]
)
(Facoltativo) Passaggio 5: rafforza la sicurezza con i callback di convalida (vai al passaggio 7)

SequentialAgent garantisce l'ordine di esecuzione, ma cosa succede se:
- L'agente Shopping non riesce a creare IntentMandate
- Un'ora dopo l'acquisto (l'intenzione scade)
- Lo stato viene danneggiato o cancellato
- Qualcuno tenta di chiamare direttamente il commerciante, bypassando Shopping
I callback aggiungono l'applicazione dell'architettura: convalidano i prerequisiti prima che un agente inizi la chiamata LLM. Si tratta di una difesa in profondità: gli strumenti convalidano durante l'esecuzione, i callback convalidano prima dell'esecuzione.
Aggiungiamo i callback di convalida ai nostri agenti Merchant e Credentials Provider.
Passaggio 5A: aggiungi la convalida del commerciante - Importa i tipi di callback
Innanzitutto, aggiungiamo le importazioni necessarie per i callback.
👉 Apri
charity_advisor/merchant_agent/agent.py
All'inizio del file, dopo le importazioni esistenti, aggiungi:
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__)
Passaggio 5B: crea la funzione di convalida dell'intent
Ora creiamo una funzione di callback che convalida l'IntentMandate prima dell'esecuzione di Merchant Agent.
👉 In
charity_advisor/merchant_agent/agent.py
, aggiungi questa funzione PRIMA di
merchant_agent = Agent(...)
definizione:
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
Passaggio 5C: allega il richiamo all'agente del commerciante
Ora colleghiamo il callback all'agente.
👉 In
charity_advisor/merchant_agent/agent.py
, modifica il
merchant_agent = Agent(...)
definizione:
Trova questa riga nella definizione dell'agente:
merchant_agent = Agent(
name="MerchantAgent",
model="gemini-2.5-flash",
description="Creates formal, signed CartMandates for charity donations following W3C PaymentRequest standards.",
Aggiungi questa riga subito dopo
description
line:
before_agent_callback=validate_intent_before_merchant,
La definizione dell'agente ora dovrebbe essere simile a questa:
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="""..."""
)
Passaggio 6: aggiungi la convalida del fornitore di credenziali (facoltativo, vai al passaggio 7)
Stesso pattern: aggiungiamo la convalida per il passaggio del pagamento.
Passaggio 6A: importa i tipi di richiamata
👉 Apri
charity_advisor/credentials_provider/agent.py
All'inizio del file, dopo le importazioni esistenti, aggiungi:
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__)
Passaggio 6B: crea la funzione di convalida del carrello
👉 In
charity_advisor/credentials_provider/agent.py
, aggiungi questa funzione PRIMA di
credentials_provider = Agent(...)
definizione:
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
Passaggio 6C: collega il callback al provider di credenziali
👉 In
charity_advisor/credentials_provider/agent.py
, modifica il
credentials_provider = Agent(...)
definizione:
Trova questa riga nella definizione dell'agente:
credentials_provider = Agent(
name="CredentialsProvider",
model="gemini-2.5-flash",
description="Securely processes payments by creating PaymentMandates and executing transactions with user consent.",
Aggiungi questa riga subito dopo
description
line:
before_agent_callback=validate_cart_before_payment,
La definizione dell'agente ora dovrebbe essere simile a questa:
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="""..."""
)
Passaggio 7: esegui il test con la GUI web dell'ADK
Ora testiamo il sistema protetto completo con i callback di convalida attivi.
👉 Nel terminale Cloud Shell, esegui:
adk web
Dovresti visualizzare un output simile al seguente:
+-----------------------------------------------------------------------------+
| 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)
👉 Poi, per accedere alla GUI web dell'ADK dal browser:
Dall'icona Anteprima web (a forma di occhio o quadrato con una freccia) nella barra degli strumenti di Cloud Shell (di solito in alto a destra), seleziona Cambia porta. Nella finestra popup, imposta la porta su 8000 e fai clic su "Cambia e visualizza anteprima". Cloud Shell aprirà una nuova scheda del browser che mostra la GUI web dell'ADK.

👉 Seleziona il tuo agente dal menu a discesa:
Nell'interfaccia utente web dell'ADK, in alto vedrai un menu a discesa. Seleziona charity_advisor dall'elenco.

Vedrai l'interfaccia web dell'ADK con:
- Riquadro della chat: a sinistra, per la conversazione
- Pannello Trace: sul lato destro, per l'osservabilità (lo utilizzeremo nel modulo 9)
Test 1: completa il flusso di donazione (caso normale)
👉 Nell'interfaccia di chat, digita:
I want to donate to an education charity
Guarda l'intero flusso:


Cosa sta succedendo (visibile nel riquadro della traccia a destra):
1. Advisor delegates to ShoppingAgent:
- ShoppingAgent cerca enti di beneficenza per l'istruzione
- Mostra tre opzioni verificate con i dettagli
2. Interagisci con ShoppingAgent (potrebbero essere necessari più turni):
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 e firma l'intent
- Restituisce la conferma con l'ID intent.
4. L'advisor passa alla fase di elaborazione:
Perfetto! Elaborazione della tua donazione di 50 $a Room to Read in corso…
5. DonationProcessingPipeline activates:
- Il callback del commerciante convalida IntentMandate (✓ superato) ← NOVITÀ
- L'agente del commerciante crea CartMandate con firma
- Il callback delle credenziali convalida CartMandate (✓ superato) ← NOVITÀ
- Il fornitore di credenziali prepara il pagamento
6. Elaborazione dei pagamenti:
- Il provider di credenziali crea PaymentMandate
- Simula l'elaborazione dei pagamenti
- ID transazione di reso
7. Advisor summarizes:
Perfetto! La tua donazione è stata elaborata correttamente. 🎉
Details:
- Importo: 50 €
- Organizzazione non profit: Room to Read (EIN: 77-0479905)
- ID transazione: txn_a3f7b2c8d9e1f4a2
Test 2: verifica gli errori di intercettazione dei callback (test avanzato facoltativo)
Vuoi vedere i callback in azione che rilevano gli errori? Dovresti corrompere manualmente lo stato (debug avanzato), ma in produzione i callback intercetterebbero:
- Lo strumento Shopping Agent non funziona → Blocchi di callback del commerciante: "Errore: impossibile creare il carrello…"
- Pass di 2 ore → Blocchi di callback del commerciante: "Errore: l'intent è scaduto..."
- Il carrello scade → Callback delle credenziali blocca: "Errore: il carrello è scaduto (limite di 15 minuti)..."
Questi casi limite ora sono applicati a livello di architettura dai callback di convalida.
Cosa hai appena creato
Hai orchestrato correttamente tre agenti specializzati in un sistema fluido e affidabile con convalida dell'architettura.
Passaggi successivi
Ora hai completato la parte tecnica principale della creazione di agenti affidabili:
Hai creato un sistema completo e affidabile che applica localmente la catena di credenziali. Ora rendiamolo accessibile agli utenti reali tramite l'implementazione della produzione e attiviamo la traccia di responsabilità che rende possibile il modulo 9.
Eseguiamo il deployment dell'agente rafforzato su Google Cloud.
8. Deployment

Il tuo sistema di donazioni affidabile è ora completo di tre agenti specializzati che lavorano a livello locale:
ma viene eseguito solo sulla macchina di sviluppo. Per rendere questo sistema utile agli utenti reali e per acquisire le tracce di responsabilità che dimostrano l'affidabilità, devi implementarlo in produzione.
Questo modulo ti guida nel deployment dell'agente su Google Cloud con l'osservabilità abilitata fin dal primo giorno. Il flag --trace_to_cloud che utilizzerai durante il deployment è ciò che rende possibile la traccia di responsabilità nel modulo 9.
Informazioni sulle opzioni di deployment
L'ADK supporta più target di deployment. Ognuno ha caratteristiche diverse per complessità, gestione delle sessioni, scalabilità e costi:
Fattore | Attività locali ( | Motore agente | Cloud Run |
complessità | Minimo | Bassa | Media |
Persistenza della sessione | Solo in memoria (perso al riavvio) | Gestito da Vertex AI (automatico) | Cloud SQL (PostgreSQL) o in memoria |
Infrastruttura | Nessuno (solo macchina di sviluppo) | Completamente gestito | Container + database facoltativo |
Avvio a freddo | N/D | 100-500ms | 100-2000ms |
Scalabilità | Singola istanza | Automatico | Automatico (a zero) |
Modello di costo | Senza costi (elaborazione locale) | Basato sul calcolo | Basata sulle richieste + livello senza costi |
UI Support | Sì (integrato) | No (solo API) | Sì (tramite il flag |
Configurazione dell'osservabilità | Visualizzatore tracce locale | Automatico con | Richiede il flag |
Ideale per | Sviluppo e test | Agenti di produzione | Agenti di produzione |
Consiglio:per questo sistema di donazioni affidabile, ti consigliamo di utilizzare Agent Engine come deployment di produzione principale perché fornisce:
- Infrastruttura completamente gestita (nessun container da gestire)
- Persistenza della sessione integrata tramite
VertexAiSessionService - Scalabilità automatica senza avvii a freddo
- Deployment semplificato (non sono richieste conoscenze di Docker)
- Integrazione immediata di Cloud Trace
Opzione aggiuntiva: Google Kubernetes Engine (GKE)
Per gli utenti avanzati che richiedono il controllo a livello di Kubernetes, il networking personalizzato o l'orchestrazione multi-servizio, è disponibile il deployment GKE. Questa opzione offre la massima flessibilità, ma richiede maggiore esperienza operativa (gestione dei cluster, manifest, service account).
Il deployment di GKE non è trattato in questo codelab, ma è documentato in dettaglio nella guida al deployment di ADK GKE.
Prerequisiti
1. Configurazione del progetto Google Cloud
Devi avere un progetto Google Cloud con la fatturazione abilitata. Se non ne hai uno:
- Crea un progetto: Google Cloud Console
- Abilita fatturazione: Abilita fatturazione
- Prendi nota dell'ID progetto (non del nome o del numero del progetto).
2. Riautenticazione (facoltativa)
Autenticati con Google Cloud:
gcloud auth application-default login
gcloud config set project YOUR_PROJECT_ID
Sostituisci YOUR_PROJECT_ID con l'ID del tuo progetto Google Cloud effettivo.
Verifica l'autenticazione:
gcloud config get-value project
# Should output: YOUR_PROJECT_ID
3. Variabili di ambiente
Utilizza questi comandi per compilare automaticamente il file .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}"
Dovresti vedere:
STAGING_BUCKET=gs://your-actual-project-id-staging
Note importanti:
- Sostituisci
YOUR_PROJECT_IDcon l'ID progetto effettivo (o utilizza i comandi riportati sopra). - Per
GOOGLE_CLOUD_LOCATION, utilizza una regione supportata. - Il bucket di staging verrà creato automaticamente se non esiste quando esegui lo script di deployment
4. Abilita le API richieste
Il processo di deployment richiede l'abilitazione di diverse API Google Cloud. Esegui questo comando per abilitarle:
gcloud services enable \
aiplatform.googleapis.com \
storage.googleapis.com \
cloudbuild.googleapis.com \
cloudtrace.googleapis.com \
compute.googleapis.com
Questo comando attiva:
- API AI Platform: per i modelli Agent Engine e Vertex AI
- API Cloud Storage: per il bucket di staging
- API Cloud Build: per la creazione di container (Cloud Run)
- API Cloud Trace: per le tracce di osservabilità e responsabilità
- API Compute Engine: per la gestione dei service account
Passaggio 1: comprendi l'infrastruttura di deployment
Il progetto include uno script di deployment unificato (deploy.sh) che gestisce tutte le modalità di deployment.
👉 (Facoltativo) Rivedi lo script di deployment:
cat deploy.sh
Lo script fornisce tre modalità di deployment:
./deploy.sh local- Esegui localmente con l'archiviazione in memoria./deploy.sh agent-engine- Esegui il deployment su Vertex AI Agent Engine (consigliato)./deploy.sh cloud-run- Esegui il deployment in Cloud Run con UI facoltativa
Come funziona dietro le quinte:
Per il deployment di Agent Engine, lo script esegue:
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
Per il deployment di Cloud Run, esegue:
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
Il flag --trace_to_cloud è fondamentale per entrambi i tipi di deployment: consente l'integrazione di Cloud Trace per la traccia di responsabilità che esplorerai nel modulo 9.
Passaggio 2: prepara il wrapper del motore dell'agente
Agent Engine richiede un punto di ingresso specifico che esegue il wrapping dell'agente per il runtime gestito. Questo file è stato creato per te.
👉 Rivedi
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
)
Perché è necessario questo file:
- Agent Engine richiede che l'agente sia racchiuso in un oggetto
AdkApp - Il parametro
enable_tracing=Trueattiva automaticamente l'integrazione di Cloud Trace - Questo wrapper viene a cui fa riferimento la CLI ADK durante il deployment
- Configura
VertexAiSessionServiceper la persistenza automatica della sessione
Passaggio 3: esegui il deployment in Agent Engine (CONSIGLIATO)
Agent Engine è il deployment di produzione consigliato per il tuo sistema di donazioni affidabile, perché fornisce un'infrastruttura completamente gestita con persistenza della sessione integrata.
Esegui il deployment
Dalla radice del progetto:
chmod +x deploy.sh
./deploy.sh agent-engine
Fasi di deployment
Guarda lo script eseguire queste fasi:
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
Questo processo richiede 5-10 minuti, in quanto pacchettizza l'agente ed esegue il deployment nell'infrastruttura Vertex AI.
Salvare l'ID motore dell'agente
Al termine del deployment:
✅ 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
Aggiorna immediatamente il file .env:
echo "AGENT_ENGINE_ID=7917477678498709504" >> .env
Che cosa è stato eseguito il deployment
Il deployment di Agent Engine ora include:
✅ Tutti e tre gli agenti (Shopping, Merchant, Credenziali) in esecuzione nel runtime gestito
✅ Logica completa della catena di credenziali (Intent → Carrello → Mandati di pagamento)
✅ Meccanismo di consenso dell'utente con flusso di lavoro di conferma
✅ Persistenza automatica della sessione tramite VertexAiSessionService
✅ Infrastruttura di scalabilità automatica gestita da Google
✅ Integrazione di Cloud Trace per una visibilità completa
Passaggio 4: testa l'agente di cui è stato eseguito il deployment
Aggiornamento dell'ambiente
Verifica che il tuo .env includa l'ID motore dell'agente:
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
Esegui lo script di test
Il progetto include uno script di test specifico per le implementazioni di Agent Engine.
👉 Esegui il test:
python scripts/test_deployed_agent.py
Risultato previsto
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
Elenco di controllo per la verifica
Dopo il test, verifica:
✅ L'agente risponde alle query
✅ Tutti e tre gli agenti vengono eseguiti in sequenza (Shopping → Commerciante → Credenziali)
✅ Il meccanismo di consenso si attiva (richiesta di conferma)
✅ La sessione persiste tra le richieste
✅ Nessun errore di autenticazione
✅ Nessun timeout di connessione
Se riscontri errori:
- Controlla che le variabili di ambiente siano impostate correttamente
- Verifica che le API siano abilitate:
gcloud services list --enabled - Controllare i log di Agent Engine nella console Vertex AI
- Verifica che il file
agent_engine_app.pyesista nella cartellacharity_advisor
(Facoltativo) Passaggio 5: esegui il deployment in Cloud Run
Anche se Agent Engine è consigliato per un deployment di produzione semplificato, Cloud Run offre un maggiore controllo e supporta la GUI web dell'ADK. Questa sezione è facoltativa.
Quando utilizzare Cloud Run
Scegli Cloud Run se hai bisogno di:
- L'interfaccia utente web dell'ADK per l'interazione utente
- Controllo completo sull'ambiente container
- Configurazioni personalizzate del database
- Integrazione con i servizi Cloud Run esistenti
Esegui il deployment
chmod +x deploy.sh
./deploy.sh cloud-run
Quali sono le differenze?
Lo script:
- Crea un container Docker con il codice dell'agente
- Crea un database Cloud SQL PostgreSQL (se necessario)
- Configurare la connessione al database
- Esegui il deployment con la UI web dell'ADK abilitata
Il deployment richiede 10-15 minuti a causa del provisioning di Cloud SQL.
Gestione delle sessioni:
- Utilizza
DatabaseSessionServiceanzichéVertexAiSessionService - Richiede le credenziali del database in
.env(o generate automaticamente) - Lo stato viene mantenuto nel database PostgreSQL
Supporto dell'interfaccia utente:
- UI web disponibile all'indirizzo:
https://charity-advisor-xyz.a.run.app
Test del deployment di Cloud Run
Se hai eseguito il deployment in Cloud Run con --with_ui, puoi eseguire il test direttamente nel browser:
- Vai all'URL del servizio (fornito nell'output del deployment).
- Visualizzerai l'interfaccia web dell'ADK. Seleziona il tuo agente dal menu a discesa.
- Avvia una donazione di prova:
I want to donate $50 to a children's education charity
- Osserva il flusso di esecuzione:
- ShoppingAgent trova enti di beneficenza e salva la tua intenzione
- MerchantAgent crea il mandato per il carrello
- CredentialsProvider crea il mandato di pagamento e richiede la conferma
- Dopo la conferma, il pagamento viene elaborato
- Verifica che la risposta includa:
- Consigli sulle organizzazioni non profit
- Richiesta di conferma
- Messaggio di riuscita dopo l'approvazione
Risoluzione dei problemi
Problemi comuni
Problema: ERROR: GOOGLE_CLOUD_PROJECT is not set
Soluzione:assicurati che il file .env abbia l'ID progetto corretto:
GOOGLE_CLOUD_PROJECT=your-actual-project-id
Problema: bucket di staging non creato automaticamente
Soluzione: lo script deve creare automaticamente il bucket. In caso contrario, crealo manualmente:
gsutil mb -p $GOOGLE_CLOUD_PROJECT -l $GOOGLE_CLOUD_LOCATION $STAGING_BUCKET
Riepilogo
Hai completato correttamente le seguenti operazioni:
✅ Compreso l'infrastruttura di deployment fornita da deploy.sh
✅ Esaminata la configurazione del wrapper Agent Engine
✅ Eseguito il deployment del sistema di donazioni affidabile in Agent Engine (consigliato)
✅ Attivata l'integrazione di Cloud Trace con --trace_to_cloud
✅ Verificato che l'agente sia accessibile e funzionale
✅ Creata la base per i percorsi di responsabilità nel modulo 9
Nel modulo successivo vedrai esattamente cosa sblocca questo flag: visibilità completa di ogni donazione, ogni momento di consenso e ogni passaggio della catena di credenziali.
9. Osservabilità


Nel modulo 1, hai appreso un problema fondamentale: quando un agente AI gestisce denaro, come si fa a dimostrare cosa è successo?
Un utente potrebbe rivendicare:
- "Non ho mai scelto questa organizzazione benefica!"
- "Non ho autorizzato questo pagamento!"
- "Il sistema mi ha addebitato un importo senza il mio consenso."
In un sistema di AI tradizionale a scatola nera, non avresti modo di dimostrare il contrario. Ma il tuo sistema di donazioni affidabile è diverso. Nel modulo 8, hai eseguito il deployment con il flag --trace_to_cloud, il che significa che ogni donazione ora crea una traccia di controllo completa e a prova di manomissione in Cloud Trace.
Questo modulo ti insegna a leggere queste tracce e a utilizzarle come prove. Scoprirai come:
- Navigare in Esplora tracce di Cloud Trace per trovare le tracce di produzione
- Leggi la visualizzazione a cascata per comprendere il flusso di esecuzione
- Trova la catena di credenziali (Intent → Carrello → Mandati di pagamento)
- Individuare i momenti di consenso con la prova del timestamp
- Utilizzare le tracce per la risoluzione delle controversie
- Esportare le tracce per la conformità e gli audit
È questo che distingue i sistemi affidabili da quelli capaci ma opachi: la capacità di dimostrare cosa è successo con precisione forense.
Informazioni su tracce e intervalli
Prima di visualizzare le tracce in Cloud Trace, devi capire cosa stai guardando.
Che cos'è una traccia?
Una traccia è la cronologia completa della gestione di una singola richiesta da parte dell'agente. Acquisisce tutto ciò che accade dall'invio di una query da parte di un utente fino alla ricezione della risposta finale.
Ogni traccia mostra:
- Durata totale della richiesta
- Tutte le operazioni eseguite
- Come si relazionano tra loro le operazioni (relazioni padre-figlio)
- Quando è iniziata e terminata ogni operazione
- Stato di riuscita o non riuscita
Per l'agente di beneficenza:una traccia equivale a un flusso di donazione completo da "Voglio fare una donazione" a "Pagamento riuscito".
Che cos'è uno span?
Uno span rappresenta una singola unità di lavoro all'interno di una traccia. Considera gli span come i componenti di base di una traccia.
Tipi di intervallo comuni nel tuo sistema di donazioni:
Tipo di intervallo | Cosa rappresenta | Esempio |
| Esecuzione di un agente |
|
| Richiesta a un modello linguistico |
|
| Esecuzione della funzione dello strumento |
|
| Lettura dalla memoria della sessione | Recupero di |
| Scrittura nella memoria della sessione | Memorizzazione di |
Ogni intervallo contiene:
- Nome:l'operazione che rappresenta
- Durata (ora di inizio → ora di fine)
- Attributi:metadati come input dello strumento, risposte del modello, conteggi dei token
- Stato: operazione riuscita (
OK) o errore (ERROR) - Relazioni principale-secondario:quali operazioni hanno attivato quali
Come gli intervalli formano una traccia
Gli intervalli sono nidificati l'uno dentro l'altro per mostrare la causalità:
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!]
Questa gerarchia mostra esattamente cosa è successo e in quale ordine. Puoi notare che il mandato di pagamento è stato creato dopo il mandato del carrello, che è stato creato dopo che l'utente ha selezionato un ente di beneficenza.
Passaggio 1: accedi a Cloud Trace Explorer
Ora visualizziamo le tracce effettive dell'agente di cui hai eseguito il deployment.
Vai a Cloud Trace
- Apri la console Google Cloud: console.cloud.google.com
- Seleziona il tuo progetto dal menu a discesa in alto (dovrebbe essere preselezionato se ci hai lavorato)
- Vai a Cloud Trace Explorer:
- Nella barra laterale sinistra, scorri fino alla sezione Osservabilità.
- Fai clic su Trace.
- In alternativa, utilizza il link diretto: console.cloud.google.com/traces/list
Cosa stai guardando
Trace Explorer mostra un elenco di tutte le tracce del tuo progetto:
Colonna | Informazioni riportate |
Richiesta | Metodo HTTP ed endpoint (per le richieste API) |
Ora di inizio | Quando è iniziata la richiesta |
Latenza | Durata totale della richiesta |
Span | Numero di operazioni nella traccia |
Ogni riga rappresenta una richiesta completa all'agente di cui è stato eseguito il deployment.
Generare tracce di test (se necessario)
Se non vedi ancora tracce, l'elenco potrebbe essere vuoto perché:
- Non sono ancora state effettuate richieste all'agente di cui è stato eseguito il deployment
- Le tracce vengono visualizzate 1-2 minuti dopo una richiesta
Generare una traccia di test:
Se hai eseguito il deployment in Cloud Run con UI, visita l'URL del servizio e completa una donazione nel browser.
Se hai eseguito il deployment in Agent Engine, esegui lo script di test del modulo 8:
python scripts/test_deployed_agent.py
Attendi 1-2 minuti, poi aggiorna la pagina di Cloud Trace Explorer. Ora dovresti vedere le tracce.
Filtra tracce
Utilizza le opzioni di filtro in alto per trovare tracce specifiche:
- Intervallo di tempo:se necessario, passa da "Ultima ora" a "Ultime 24 ore".
- Latenza minima / Latenza massima:filtra le richieste lente
- Filtro richieste: cerca in base a operazioni specifiche (ad es. "DonationPipeline")
Per questo modulo, concentrati sulle tracce con durate più lunghe (> 5 secondi), in quanto rappresentano flussi di donazione completi con l'esecuzione di tutti e tre gli agenti.
Passaggio 2: esamina un flusso di donazione completo
Fai clic su una traccia qualsiasi nell'elenco per aprire la visualizzazione a cascata. È qui che trascorrerai la maggior parte del tempo ad analizzare il comportamento degli agenti.
Informazioni sulla visualizzazione a cascata
La visualizzazione a cascata è un diagramma di Gantt che mostra la sequenza temporale di esecuzione completa:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
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]
Leggere il grafico
Ogni barra rappresenta un intervallo:
- Posizione orizzontale:quando è iniziato
- Durata:quanto tempo è stato necessario
- Rientro:mostra le relazioni padre-figlio
- Colore:in genere blu per il normale funzionamento, rosso per gli errori
Osservazioni chiave di questa traccia di esempio:
✅ Durata totale: 8,2 secondi
✅ Esecuzione sequenziale: ShoppingAgent è stato completato prima dell'avvio di MerchantAgent
✅ MerchantAgent è stato completato
prima
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
Questa visualizzazione mostra immediatamente dove viene trascorso il tempo e in quale ordine vengono eseguite le operazioni.
Fai clic su un intervallo per i dettagli
Fai clic sullo span invocation (lo span radice in alto). Nel riquadro a destra, vedrai gli attributi dettagliati:
{
"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"
}
Questi attributi forniscono il contesto dell'intera richiesta.
Passaggio 3: trova la catena di certificati
Il tuo sistema attendibile utilizza una catena di credenziali per dimostrare l'autorizzazione in ogni passaggio:
IntentMandate (User chose charity)
↓
CartMandate (Merchant created cart, signed IntentMandate)
↓
PaymentMandate (Payment provider created payment, signed CartMandate)
Troviamo ogni mandato nella traccia.
Trovare l'IntentMandate
Fai clic sullo span execute_tool: save_user_choice (in ShoppingAgent).
Nel riquadro degli attributi vedrai:
{
"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..."
}
}
Ciò dimostra che:
- ✅ L'utente ha selezionato "Save the Children"
- ✅ L'importo era di 50 $
- ✅ Scelta registrata alle ore 15:30:12 UTC
- ✅ La firma è stata generata (in produzione, sarebbe crittografica)
L'IntentMandate è ora nello stato della sessione e disponibile per gli agenti successivi.
Trovare il CartMandate
Fai clic sul tag execute_tool: create_cart_mandate (in MerchantAgent).
Nel riquadro degli attributi:
{
"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"
}
}
Ciò dimostra che:
- ✅ MerchantAgent ha ricevuto l'IntentMandate (l'input lo mostra)
- ✅ Il carrello è stato creato con l'ID "cart_7893"
- ✅ La firma del carrello fa riferimento alla firma del mandato di intento (collegamento a catena).
- ✅ Created at 15:30:14 UTC (2.4 seconds after intent)
CartMandate ora fa riferimento a IntentMandate, formando la catena.
Trovare il PaymentMandate
Fai clic sul tag execute_tool: create_payment_mandate (in CredentialsProvider).
Nel riquadro degli attributi:
{
"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"
}
}
Ciò dimostra la catena completa:
- ✅ CredentialsProvider ha ricevuto CartMandate (l'input lo mostra)
- ✅ Il pagamento fa riferimento alla firma CartMandate (link a catena).
- ✅ Era necessaria la conferma (
confirmation_required: true) - ✅ Utente confermato alle ore 15:30:17 UTC
- ✅ Il sistema ha atteso 29,2 secondi la decisione dell'utente
- ✅ Il pagamento è stato creato dopo la conferma (timestamp: 15:30:47)
Visualizzare la catena
La traccia dimostra che la catena di credenziali è stata eseguita correttamente:
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...)
Ogni mandato fa riferimento alla firma di quello precedente. Si tratta di una prova di manomissione: puoi verificare la catena controllando che le firme corrispondano.
Passaggio 4: analizza le prestazioni e i colli di bottiglia
Cloud Trace non si limita a dimostrare cosa è successo, ma ti mostra dove viene impiegato il tempo in modo che tu possa ottimizzare.
Identificare il percorso critico
Nella visualizzazione a cascata, cerca gli intervalli più lunghi nello stack verticale. Questi rappresentano i colli di bottiglia del rendimento.
Dalla traccia di esempio:
Total: 8.2 seconds
Breakdown:
- ShoppingAgent: 2.1s (26%)
- MerchantAgent: 1.8s (22%)
- CredentialsProvider: 4.0s (49%) ← Bottleneck
- Other overhead: 0.3s (3%)
Informazioni critiche:CredentialsProvider rappresenta il 49% del tempo totale. Perché?
Visualizza in dettaglio lo span CredentialsProvider:
CredentialsProvider: 4.0s
- call_llm: 0.8s (20%)
- create_payment_mandate: 3.0s (75%) ← User consent wait
- Other: 0.2s (5%)
Il ritardo di 3 secondi è previsto ed è un buon segno: l'utente sta riflettendo prima di confermare. Non si tratta di un problema di rendimento, ma di una prova di un consenso ponderato.
Monitoraggio dei costi LLM
Fai clic su un intervallo call_llm per visualizzare l'utilizzo dei token:
{
"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
}
Utilizza questa funzionalità per:
- Monitorare il costo per richiesta (token × prezzo del modello)
- Identificare prompt inutilmente lunghi
- Confrontare il rendimento dei modelli (Flash e Pro)
- Ottimizzare per la latenza rispetto alla qualità
Esempio di calcolo:
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
Questa visibilità granulare ti aiuta a prendere decisioni basate sui dati in merito alla selezione del modello.
Confronto tra tracce
Filtra più tracce e confronta le durate:
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)
Approfondimento:la maggior parte della variazione deriva dal tempo di decisione dell'utente, non dalle prestazioni del sistema. L'esecuzione dell'agente principale (senza consenso) è coerente a circa 5 secondi.
Indica che il sistema funziona in modo affidabile.
Per i sistemi di produzione, configura gli avvisi per rilevare i problemi prima che gli utenti si lamentino.
Avviso sulle percentuali di errore elevate
Crea un avviso quando più del 5% delle tracce contiene errori:
- Vai a Cloud Monitoring.
- Fai clic su "Avvisi" → "Crea criterio".
- Configura:
Resource: Cloud Trace Span Metric: Span error count Condition: Rate > 5% over 5 minutes Notification: Email your-team@example.com
Avviso sulla latenza elevata
Crea un avviso quando la latenza p95 supera i 15 secondi:
Resource: Cloud Trace
Metric: Span duration (95th percentile)
Condition: > 15000ms for 5 minutes
Notification: PagerDuty
In questo modo, il peggioramento delle prestazioni viene rilevato prima che influisca sull'esperienza utente.
Avviso relativo al consenso mancante
Crea un avviso se qualsiasi pagamento viene elaborato senza conferma:
Resource: Cloud Trace Span
Filter: tool.name="create_payment_mandate" AND tool.confirmation_required!=true
Condition: Any match
Notification: Critical alert to security team
Si tratta di un rilevatore di violazioni della sicurezza: se si attiva, significa che il meccanismo di consenso presenta gravi problemi.
Cosa hai imparato
Grazie a Cloud Trace, ora sai come:
✅ Naviga in Cloud Trace Explorer per trovare le tracce di produzione
✅ Leggi le visualizzazioni a cascata per vedere il flusso di esecuzione completo
✅ Traccia la catena di credenziali tramite IntentMandate → CartMandate → PaymentMandate ✅ Utilizza le tracce come prova per la risoluzione delle controversie
✅ Analizza le prestazioni per identificare i colli di bottiglia
✅ Monitora i costi degli LLM a un livello granulare
La differenza che fa
Confronta due sistemi che gestiscono lo stesso reclamo "Non ho mai autorizzato questo pagamento":
Sistema senza osservabilità
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.
Questo è il potere delle tracce di responsabilità. Passi da "fidati di noi" a "ti mostriamo esattamente cosa è successo".
Passaggi successivi
Ora hai completato la parte tecnica principale della creazione di agenti affidabili:
✅ Moduli 1-6: progettazione di un'architettura affidabile (ruoli, credenziali, consenso)
✅ Modulo 7: orchestrazione di flussi di lavoro complessi (SequentialAgent)
✅ Modulo 8: deployment con osservabilità abilitata
✅ Modulo 9: apprendimento della lettura e dell'utilizzo dei percorsi di responsabilità
L'architettura che hai creato (separazione dei ruoli, catene di credenziali, meccanismi di consenso, osservabilità completa) viene trasferita direttamente ai sistemi di produzione che gestiscono denaro reale, dati reali e conseguenze reali.
10. Il tuo percorso
Cosa hai creato
Hai iniziato questo workshop con una domanda: "Come faccio a creare agenti AI di cui posso fidarmi per gestire i miei soldi?"
Ora hai la risposta.
Dove hai iniziato (Modulo 3):
simple_agent = Agent(
model="gemini-2.5-flash",
instruction="Find charities and donate",
tools=[google_search]
)
Dove ti trovi ora (Modulo 10):
- ✅ Tre agenti specializzati con separazione dei ruoli
- ✅ Tre credenziali verificabili (intento → carrello → mandati di pagamento)
- ✅ Completa la catena di credenziali con la convalida della scadenza a ogni passaggio
- ✅ Meccanismo di consenso esplicito con prova del timestamp
- ✅ Deployment in produzione in Agent Engine con osservabilità
- ✅ Traccia di responsabilità completa in Cloud Trace
- ✅ Prove forensi per la risoluzione delle controversie
Workshop e produzione: il divario
Il tuo sistema mostra l'architettura e i pattern corretti, ma utilizza semplificazioni didattiche che devono essere aggiornate per denaro reale e utenti reali.
Ecco esattamente cosa è stato semplificato e cosa richiede la produzione:
Componente | Implementazione del workshop | Requisiti di produzione |
Signatures | Hash SHA-256 ( | Firme crittografiche reali che utilizzano PKI o JWT con chiavi private |
Elaborazione dei pagamenti | Resi simulati (flag | Integrazione con API di pagamento reali (Stripe, PayPal, Square) |
Autenticazione utente | Fiducia implicita (non è richiesto l'accesso) | OAuth 2.0, WebAuthn o gestione delle sessioni |
Gestione dei secret | Variabili di ambiente nel file | Google Secret Manager o Cloud KMS con crittografia |
Database delle organizzazioni non profit | File JSON simulato con 9 enti di beneficenza | Integrazione API live (ricerca di organizzazioni esenti da imposte dell'IRS, API Charity Navigator) |
Gestione degli errori | Try-catch di base con messaggi di errore | Logica di nuovi tentativi con backoff esponenziale, interruttori di sicurezza, code di messaggi non recapitabili |
Test | Verifica manuale tramite script | Suite di test unitari/di integrazione/E2E completa con CI/CD |
Persistenza della sessione | In memoria (locale) o automatico (Agent Engine) | Database di produzione con backup e ripristino di emergenza |
Limitazione di frequenza | Nessuno (ambiente didattico) | Limiti di frequenza per utente, limitazione basata sull'IP, rilevamento di abusi |
Modelli architetturali chiave che hai padroneggiato
I pattern che hai imparato in questo workshop sono pattern di produzione. Non dubitare di loro.
Separazione dei ruoli (principio 1 di AP2)
Ogni agente ha UN compito chiaro e vede SOLO ciò di cui ha bisogno. Se un agente viene compromesso, l'attaccante non può accedere ai dati degli altri agenti. In questo modo, la portata dell'attacco viene limitata.
Sistemi di produzione che utilizzano questo servizio:elaborazione dei pagamenti, flussi di lavoro dei documenti, catene di approvazione, moduli in più passaggi con porte di convalida.
Credenziali verificabili (principio AP2 n. 2)
Ogni credenziale ha una data di scadenza, fa riferimento alla credenziale precedente e richiede la convalida prima del passaggio successivo. In questo modo viene creata una catena di audit antimanomissione.
Valore di produzione:prova completa di cosa è successo, quando e in quale ordine. Essenziale per la risoluzione delle controversie e la conformità normativa.
Consenso esplicito (principio n. 3 di AP2)
Timestamp che dimostra che l'utente ha approvato l'azione. Non può essere contestato.
Valore di produzione:requisito legale per le transazioni finanziarie. Protegge sia l'utente che l'azienda.
Orchestrazione sequenziale (pattern ADK)
Impone l'ordine di esecuzione corretto. Impedisce di saltare i passaggi. Garantisce che ogni agente veda l'output dell'agente precedente.
Valore di produzione:ideale per i sistemi human-in-the-loop in cui gli utenti si aspettano risultati immediati. Questo è il pattern giusto per i flussi di donazione, le procedure di pagamento e le catene di approvazione.
Osservabilità completa (OpenTelemetry + Cloud Trace)
Ogni decisione, chiamata di strumenti, momento di consenso e trasferimento delle credenziali acquisito automaticamente.
Valore produttivo:prove forensi per le contestazioni. Dati di ottimizzazione del rendimento. Audit trail di conformità. Esegui il debug dei problemi di produzione con precisione.
Risorse per l'apprendimento continuo
Documentazione ADK:
AP2 e standard correlati:
Servizi Google Cloud:
Esegui la pulizia delle risorse
Per evitare addebiti continui, elimina il deployment:
Agent Engine: segui i passaggi descritti nella documentazione di Agent Engine
Cloud Run (se è stato eseguito il deployment):
gcloud run services delete charity-advisor \
--region=$GOOGLE_CLOUD_LOCATION
Bucket di archiviazione:
gsutil -m rm -r gs://$GOOGLE_CLOUD_PROJECT-staging
gsutil -m rm -r gs://$GOOGLE_CLOUD_PROJECT-artifacts
Il tuo percorso continua
Hai iniziato con una semplice domanda e hai creato una risposta completa. Hai acquisito le competenze di base per creare agenti AI affidabili. Questi pattern vengono trasferiti a qualsiasi dominio in cui gli agenti AI gestiscono operazioni sensibili: transazioni finanziarie, decisioni sanitarie, documenti legali, operazioni della supply chain.
Il trasferimento dei principi. Il modello di attendibilità funziona.
Ora crea qualcosa di affidabile. ❤️
