Budowanie wiarygodnych agentów organizacji charytatywnych za pomocą pakietu ADK i AP2 od Google

1. Budowanie zaufania, aby zachęcić do hojności

baner

Moment inspiracji

Telefon zawibruje. Widzisz artykuł o skutecznym programie nauki czytania, który pomaga dzieciom z grup defaworyzowanych. Czujesz silną potrzebę zaangażowania się. Otwierasz przeglądarkę i wyszukujesz „darowizny na programy wspierające umiejętność czytania i pisania u dzieci”.

wyszukiwarka google

Pojawią się setki wyników.

Klikasz pierwszy link. Witryna wygląda profesjonalnie. Przewijasz w dół do informacji finansowych. „Koszty administracyjne: 28%”. Wstrzymujesz. Tylko 72 centy z każdego przekazanego dolara trafią na finansowanie programu. Czy to dobrze? Nie masz pewności.

Spróbuj w innej organizacji. Nigdy o nich nie słyszałeś(-aś). Czy są one legalne? Szybkie wyszukiwanie prowadzi do niekończących się wyników. Znajdujesz wątek na Reddicie sprzed dwóch lat, w którym jeden z użytkowników twierdzi: „To oszustwo, moja darowizna nigdy nie dotarła do celu”. Inna osoba gorąco ich broni: „Oni wykonują prawdziwą pracę w terenie!”. Niepewność jest paraliżująca.

Pół godziny później wciąż przeglądasz sprzeczne opinie, oceny efektywności i dokumenty IRS, ale nadal nie udało Ci się przekazać darowizny. Pierwotny impuls hojności został zastąpiony przez trudności związane z badaniami. Karta pozostaje otwarta przez kilka dni, przypominając o dobrym zamiarze, dopóki jej nie zamkniesz.

To nie jest osobista porażka, tylko awaria systemu

Ta funkcja jest uniwersalna. Chęć pomocy jest duża, ale proces jest pełen przeszkód, które powodują wahanie i wątpliwości:

  • ❌ Trudności w badaniu: każda organizacja charytatywna wymaga własnego dochodzenia.
  • ❌ Weryfikacja zaufania: trudno odróżnić organizacje o wysokiej skuteczności od tych nieefektywnych, a nawet od oszustw.
  • ❌ Paraliż decyzyjny: przytłaczająca liczba opcji prowadzi do zmęczenia decyzyjnego.
  • Utrata impetu: emocjonalna motywacja do przekazania darowizny maleje wraz ze wzrostem obciążenia logistycznego.

To utrudnienie wiąże się z ogromnymi kosztami w rzeczywistości. Darczyńcy indywidualni w Stanach Zjednoczonych przekazują ogromne kwoty – według raportu Giving USA 2024 w 2023 roku darczyńcy indywidualni przekazali łącznie około 374 miliardy dolarów. Badania pokazują jednak, że bariery w przekazywaniu darowizn – w tym koszty wyszukiwania, opory psychologiczne i ograniczenia czasowe – znacznie zmniejszają kwotę, która trafia do organizacji charytatywnych. Badania z udziałem milionów darczyńców wykazały, że nawet niewielkie utrudnienia w procesie przekazywania darowizn online uniemożliwiają ludziom realizację ich zamiarów charytatywnych.

To miliardy dolarów w postaci darowizn, które nigdy nie trafiają do organizacji, które ich potrzebują.

The Vision

Wyobraź sobie inne wrażenia. Zamiast 30-minutowej sesji wyszukiwania wystarczy, że powiesz:

„Chcę przekazać 50 zł na program nauki czytania i pisania dla dzieci. Znajdź wysoko ocenianą, wydajną i zweryfikowaną organizację charytatywną”.

W ciągu kilku sekund otrzymasz odpowiedź, która wzbudzi Twoje zaufanie:

karta wyników organizacji charytatywnej,

Taką obietnicę składa agent AI do przekazywania darowizn. Aby jednak zrealizować tę wizję, musimy rozwiązać podstawowy problem: gdy autonomiczny agent AI zarządza pieniędzmi, zaufanie nie jest opcjonalne, ale stanowi podstawę jego działania.

  • Jak możemy udowodnić, co użytkownik autoryzował?
  • Kto ponosi odpowiedzialność za błędy?
  • Jak zapewniamy darczyńcom, organizacjom charytatywnym i sieciom płatniczym pewność, że warto wziąć udział w programie?

Twoja misja na dziś

W ramach tych warsztatów stworzysz takiego godnego zaufania agenta, łącząc 2 zaawansowane technologie:

Google Agent Development Kit (ADK)

Agent Payments Protocol (AP2)

Role

Fabryka do tworzenia agentów AI klasy produkcyjnej

Architektoniczny plan budowania zaufania w transakcjach AI

Co zapewnia

• Platforma do orkiestracji wielu agentów
• Wbudowane funkcje zabezpieczeń, takie jak potwierdzenie narzędzia
• Gotowe do wdrożenia w środowisku produkcyjnym funkcje obserwacji i śledzenia
• Prosty interfejs Pythona do złożonych zachowań agentów

• Granice zabezpieczeń oparte na rolach
• Weryfikowalne poświadczenia cyfrowe (mandaty)
• Kryptograficzny dowód zgody
• Pełne ścieżki audytu zapewniające odpowiedzialność

Więcej informacji

Dokumentacja pakietu ADK

Protokół AP2

Co utworzysz

architektura

Po zakończeniu tych warsztatów będziesz mieć:

✅ System wielu agentów z wyspecjalizowanymi rolami:

  • Asystent zakupów, który znajduje zweryfikowane organizacje charytatywne
  • Agent sprzedawcy, który tworzy wiążące oferty darowizn
  • Dostawca danych logowania, który bezpiecznie przetwarza płatności
  • Orchestrator, który koordynuje cały przepływ.

✅ 3 rodzaje weryfikowalnych danych logowania:

  • IntentMandate: „Znajdź organizację charytatywną zajmującą się edukacją”
  • CartMandate: „50 PLN na rzecz Room to Read, podpisane przez sprzedawcę”
  • PaymentMandate: „Process via simulated payment” (Przetwórz za pomocą symulowanej płatności)

✅ Bezpieczeństwo w każdej warstwie:

  • Granice zaufania oparte na rolach
  • Wyraźna zgoda użytkownika

✅ Pełny rejestr kontrolny:

  • Każda decyzja jest śledzona
  • Każda zarejestrowana zgoda
  • Każde przekazanie jest widoczne

🔒 Ważne: to bezpieczne środowisko edukacyjne

Chcesz budować zaufanie?

W następnym module skonfigurujemy środowisko programistyczne i utworzymy pierwszego agenta AI. Szybko dowiesz się, dlaczego proste agenty nie są godne zaufania, a potem spędzisz resztę warsztatów na nauce, jak to naprawić.

Zacznijmy od zrozumienia problemu z pierwszej ręki.

2. Przygotowywanie obszaru roboczego

Podstawa zaufanych agentów

Zanim stworzymy naszego agenta AI Giving Agent, musimy przygotować czyste, spójne i prawidłowo skonfigurowane środowisko deweloperskie. Ten moduł to skoncentrowany krok, który ma na celu zapewnienie, że wszystkie niezbędne narzędzia i usługi są dostępne.

Po pomyślnym zakończeniu tej konfiguracji możesz w pełni skupić się na ekscytującej pracy nad tworzeniem logiki agenta w kolejnych modułach, nie martwiąc się o problemy z konfiguracją.

Dostęp do Cloud Shell

Najpierw otworzymy Cloud Shell, czyli terminal w przeglądarce z zainstalowanym pakietem Google Cloud SDK i innymi niezbędnymi narzędziami.

Potrzebujesz środków w Google Cloud?

Kliknij Aktywuj Cloud Shell u góry konsoli Google Cloud (jest to ikona terminala na pasku nawigacyjnym w prawym górnym rogu).

Cloud Shell

Znajdź identyfikator projektu Google Cloud:

  • Otwórz konsolę Google Cloud: https://console.cloud.google.com
  • Wybierz projekt, którego chcesz użyć w tych warsztatach, z menu u góry strony.
  • Identyfikator projektu jest wyświetlany na karcie Informacje o projekcie w panelu identyfikator projektu.

Po otwarciu Cloud Shell sprawdź, czy masz uwierzytelnienie:

# Check that you are logged in
gcloud auth list

Twoje konto powinno być widoczne jako (ACTIVE).

Konfigurowanie projektu

Teraz skonfiguruj projekt Google Cloud i włącz niezbędne interfejsy API.

Ustawianie identyfikatora projektu

# 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)"

Włącz wymagane interfejsy API

Twoi agenci potrzebują dostępu do kilku usług Google Cloud:

gcloud services enable \
    aiplatform.googleapis.com \
    secretmanager.googleapis.com \
    cloudtrace.googleapis.com

Może to potrwać 1–2 minuty. Zobaczysz:

Operation "operations/..." finished successfully.

Co zapewniają te interfejsy API:

  • aiplatform.googleapis.com: dostęp do modeli Gemini na potrzeby rozumowania agenta.
  • secretmanager.googleapis.com: bezpieczne przechowywanie kluczy interfejsu API (sprawdzona metoda w środowisku produkcyjnym).
  • cloudtrace.googleapis.com: widoczność ścieżki odpowiedzialności

Klonowanie kodu startowego

Pobierz repozytorium warsztatowe ze wszystkimi kodami szablonów i zasobami:

git clone https://github.com/ayoisio/adk-ap2-charity-agents
cd adk-ap2-charity-agents
git checkout codelab

Sprawdźmy, co mamy:

ls -la

Zobaczysz, że:

  • charity_advisor/ – miejsce, w którym będziemy tworzyć agentów i narzędzia.
  • scripts/ – skrypty pomocnicze do testowania i weryfikacji
  • deploy.sh – skrypt pomocniczy do wdrożenia
  • setup.py – skrypt pomocniczy do instalacji modułu
  • .env.template – plik zmiennych środowiskowych

Konfigurowanie środowiska Pythona

Teraz utworzymy izolowane środowisko Pythona dla naszego projektu.

Tworzenie i aktywowanie środowiska wirtualnego

# Create the virtual environment
python3 -m venv venv

# Activate it
source venv/bin/activate

✅ Weryfikacja: w prompcie powinien się teraz wyświetlać prefiks (venv).

Instalowanie zależności

pip install -r charity_advisor/requirements.txt
pip install -e .

Spowoduje to zainstalowanie:

  • google-adk: framework Agent Development Kit
  • google-cloud-aiplatform: integracja z Vertex AI i Gemini
  • ap2: Agent Payments Protocol SDK (z GitHub)
  • python-dotenv: zarządzanie zmiennymi środowiskowymi

Flaga -e umożliwia importowanie modułów adk_ap2_charity_agents z dowolnego miejsca.

Konfigurowanie pliku środowiska

Utwórz konfigurację na podstawie szablonu:

# 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

Zobaczysz, że:

GOOGLE_CLOUD_PROJECT=your-actual-project-id

Weryfikacja

Uruchom skrypt weryfikacyjny, aby sprawdzić, czy wszystko jest poprawnie skonfigurowane:

python scripts/verify_setup.py

Powinny pojawić się same zielone znaczniki wyboru:

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

Rozwiązywanie problemów

Co dalej?

Środowisko jest już w pełni przygotowane. Wiesz już, jak:

  • ✅ Skonfigurowano projekt Google Cloud
  • ✅ Wymagane interfejsy API włączone
  • ✅ Zainstalowano biblioteki ADK i AP2
  • ✅ Kod szablonu gotowy do modyfikacji

W następnym module utworzysz pierwszego agenta AI za pomocą kilku wierszy kodu i dowiesz się, dlaczego prości agenci nie są godni zaufania w przypadku transakcji finansowych.

3. Twój pierwszy agent i odkrywanie luki w zaufaniu

baner

Od pomysłu do interakcji

W poprzednim module przygotowaliśmy środowisko programistyczne. Teraz zaczyna się ciekawa praca. Stworzymy i uruchomimy pierwszego agenta, nadamy mu pierwszą funkcję i przy okazji odkryjemy podstawowe wyzwania, które musimy rozwiązać, aby był naprawdę godny zaufania.

Ten moduł to zdjęcie „przed” – moment, który pokazuje, dlaczego budowanie godnych zaufania agentów wymaga czegoś więcej niż tylko zapewnienia LLM dostępu do narzędzi.

Krok 1. Sprawdź agenta startowego

Najpierw przyjrzyjmy się szablonowi pierwszego agenta. Zawiera on podstawową strukturę z symbolami zastępczymi, które uzupełnimy w następnych krokach.

👉 Otwórz plik

charity_advisor/simple_agent/agent.py

w edytorze.

Zobaczysz:

"""
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=[]
)

Zwróć uwagę, że komentarze zastępcze są zgodne z wzorcem: MODULE_3_STEP_X_DESCRIPTION. Zastąpimy te znaczniki, aby stopniowo tworzyć agenta.

Krok 2. Zaimportuj wymagane komponenty

Zanim utworzymy instancję klasy Agent lub użyjemy narzędzia google_search, musimy zaimportować je do pliku.

👉 Znajdź:

# MODULE_3_STEP_2_IMPORT_COMPONENTS

👉 Zastąp ten wiersz tym tekstem:

from google.adk.agents import Agent
from google.adk.tools import google_search

Teraz w naszym pliku są dostępne klasa Agent i narzędzie google_search.

Krok 3. Napisz instrukcję dla agenta

Instrukcja to „opis stanowiska” agenta – informuje model LLM, kiedy i jak używać narzędzi. Napiszmy taki, który poprowadzi agenta w wyszukiwaniu informacji o organizacjach charytatywnych.

👉 Znajdź:

# MODULE_3_STEP_3_WRITE_INSTRUCTION
instruction="""""",

👉 Zastąp te 2 wiersze tymi:

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.""",

Krok 4. Dodaj narzędzie wyszukiwania

Agent bez narzędzi to tylko rozmówca. Nadajmy agentowi pierwszą funkcję: możliwość wyszukiwania w internecie.

👉 Znajdź:

# MODULE_3_STEP_4_ADD_TOOLS
tools=[]

👉 Zastąp te 2 wiersze tymi:

tools=[google_search]

Krok 5. Weryfikacja kompletnego agenta

Zanim zaczniemy testować, sprawdźmy, czy wszystkie elementy są na swoim miejscu.

👉 Twój pełny

charity_advisor/simple_agent/agent.py

plik powinien wyglądać dokładnie tak:

"""
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]
)

Krok 6. Testowanie agenta – ujawnianie luk w zaufaniu

Agent jest już w pełni skonfigurowany, więc przetestujmy go i przeanalizujmy jego działanie. W tym miejscu odkrywamy, dlaczego proste modele nie są wiarygodne w przypadku podejmowania decyzji finansowych.

Test 1. Problem z wykrywaniem

👉 W terminalu Cloud Shell uruchom to polecenie:

adk run charity_advisor/simple_agent

Powinny się wyświetlić dane wyjściowe podobne do tych:

INFO:google.adk.agents:Loading agent from charity_advisor/simple_agent
INFO:google.adk.agents:Agent 'SimpleAgent' ready

[user]:

Prompt [user]: czeka teraz na Twoje dane.

👉 W wierszu [user] wpisz:

Can you find me a verified, highly-rated charity for children's literacy?

👉 Naciśnij Enter i sprawdź odpowiedź.

Po chwili agent zsyntetyzuje wyniki wyszukiwania i wygeneruje odpowiedź podobną do tej:

Z wyszukiwania w internecie wynika, że do cenionych organizacji charytatywnych zajmujących się umiejętnością czytania i pisania u dzieci należą Reading Is FundamentalRoom to Read. Do weryfikacji statusu i ocen organizacji często polecane są źródła takie jak Charity Navigator i GuideStar. Znalazłem też kilka dyskusji online, m.in. na forach takich jak Reddit, gdzie użytkownicy dzielą się osobistymi doświadczeniami związanymi z różnymi mniejszymi, lokalnymi programami nauki czytania i pisania.

Przeanalizujmy to. Czy pracownik rozwiązał nasz problem?

❌ Nie. Doskonale odzwierciedla ludzkie doświadczenie, które opisaliśmy w module 1. Udało mu się zautomatyzować proces „googlowania” i przekazać nam z powrotem problem „paraliżu analitycznego”.

W ten sposób ujawnia się pierwsza luka w zaufaniu: brak wiarygodnych danych.

Agent przeszukuje otwarty internet, co oznacza, że:

  • ✅ Szybko znalazł wyniki (lepsze wrażenia użytkowników).
  • ❌ Łączy wysoko oceniane organizacje z dyskusjami na Reddicie (niewiarygodne źródła).
  • ❌ Nie odróżnia zweryfikowanych organizacji charytatywnych od potencjalnych oszustw (brak weryfikacji).
  • ❌ Prosi nas o zweryfikowanie informacji, które właśnie podał (przerzucając na nas ten obowiązek).

Test 2. Problem z wykonaniem

Teraz przejdźmy do drugiego, kluczowego testu. W odpowiedzi na prompt [user]: spróbuj przekazać darowiznę:

Okay, please donate $50 to Room to Read for me.

Odpowie on przeprosinami, przyznając się do swoich ograniczeń:

Rozumiemy, że chcesz przekazać darowiznę. To wspaniale! Jestem jednak asystentem ds. badań i nie mam możliwości obsługi transakcji finansowych ani przetwarzania płatności. Aby przekazać darowiznę, musisz bezpośrednio wejść na oficjalną stronę organizacji Room to Read.

To drugi, równie ważny moment „Aha!”.

Nie tylko nie można ufać agentowi w kwestii znalezienia odpowiedniej organizacji charytatywnej, ale też nie można mu jeszcze powierzyć zadania przekazania darowizny.

👉 Naciśnij

Ctrl+C

, aby wyjść po zakończeniu testowania.

Wizualizacja dwóch luk

problem z zaufaniem,

Czego się właśnie dowiedziałeś(-aś)

W tym module udało Ci się utworzyć i wyposażyć pierwszego agenta AI. W ten sposób odkryliśmy 2 podstawowe wyzwania związane z budowaniem wiarygodnego systemu.

Opanowane kluczowe pojęcia

Klasa agenta:

  • Podstawowy element składowy ADK
  • Łączy rozumowanie LLM (mózg) z narzędziami (rękami)
  • Skonfigurowany z modelem, instrukcjami i narzędziami

Struktura oparta na folderach:

  • Każdy agent znajduje się w osobnym folderze.
  • Pakiet ADK szuka agent_folder/agent.py
  • Bieganie z adk run agent_folder

Lista narzędzi:

  • Definiuje możliwości agenta
  • Model LLM decyduje, kiedy i jak używać narzędzi
  • Może zawierać wiele narzędzi do różnych działań.

Prompt z instrukcjami:

  • Określa zachowanie agenta, podobnie jak opis stanowiska.
  • Określa rolę, aktywatory, działania i format wyjściowy
  • Kluczowe dla niezawodnego korzystania z narzędzia

Problem z zaufaniem:

  • Przerwa w odkrywaniu: niezweryfikowane źródła o różnej jakości.
  • Brak możliwości wykonania: brak funkcji zabezpieczeń, brak zgody użytkownika, brak ścieżki audytu.

Co dalej

W następnym module zaczniemy tworzyć rozwiązanie, wdrażając architekturę AP2 opartą na rolach.

Utwórzmy pierwszego agenta i zobaczmy, jak działa podział ról.

4. Tworzenie agenta zakupów – wykrywanie na podstawie ról

baner

Podstawa zaufania: rozdział ról

W ostatnim module dowiedzieliśmy się, że prosty agent ogólnego przeznaczenia zawodzi na 2 frontach: nie zapewnia wiarygodnego wyszukiwania i nie może przeprowadzać bezpiecznych transakcji. Zacznijmy rozwiązywać te problemy, wdrażając pierwszą zasadę protokołu płatności dla agentów: architekturę opartą na rolach.

Zanim napiszemy kod, dowiedzmy się, dlaczego ta zasada jest ważna.

Zasada AP2: rozdzielenie ról

Problem z agentami, którzy „robią wszystko”

Wyobraź sobie, że zatrudniasz jedną osobę, która będzie Twoim doradcą finansowym, księgowym i brokerem inwestycyjnym. Wygodne? Tak. Bezpieczne? Zdecydowanie nie. Mogą one:

  • Twoje cele inwestycyjne (rola doradcy)
  • Dostęp do Twoich kont (rola księgowego)
  • Uprawnienia do przenoszenia środków (rola brokera)

Jeśli ta osoba zostanie przejęta lub popełni błąd, wszystko będzie zagrożone.

Rozwiązanie AP2: jeden agent, jedno zadanie

AP2 stosuje zasadę rozdzielenia odpowiedzialności, aby tworzyć granice zaufania:

architektura

Dlaczego to jest ważne:

  • ✅ Ograniczony zasięg: jeśli agent zakupowy zostanie przejęty, atakujący nie będzie mieć dostępu do danych uwierzytelniających płatności.
  • ✅ Prywatność: dostawca danych logowania nigdy nie widzi Twojej rozmowy o zakupach.
  • ✅ Zgodność: łatwiej spełnić wymagania PCI-DSS, gdy dane dotyczące płatności są odseparowane.
  • ✅ Odpowiedzialność: jasne określenie odpowiedzialności za każdy etap.

Sposób komunikacji agentów: stan jako wspólny notatnik

Agenty nie mają bezpośredniego dostępu do danych innych agentów, więc komunikują się za pomocą stanu udostępnionego. Można ją traktować jako tablicę, na której wszyscy agenci mogą pisać i z której mogą odczytywać informacje:

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

W ten sposób zachowujemy granice zaufania, a jednocześnie umożliwiamy współpracę.

Nasz pierwszy agent: agent zakupowy

Odpowiedzialność Agenta zakupów jest prosta i skupiona na:

  1. Użyj narzędzia find_charities, aby wysłać zapytanie do naszej zaufanej bazy danych.
  2. Prezentowanie opcji użytkownikowi
  3. Użyj narzędzia save_user_choice, aby utworzyć element IntentMandate i zapisać go w stanie.
  4. Przekazanie sprawy następnemu agentowi (sprzedawcy)

To wszystko. Brak obsługi płatności i tworzenia koszyka – tylko wykrywanie i przekazywanie.

Zbudujmy go krok po kroku.

Krok 1. Dodaj narzędzie Input Validation Helper

Podczas tworzenia narzędzi produkcyjnych kluczowe znaczenie ma weryfikacja danych wejściowych. Utwórzmy funkcję pomocniczą, która sprawdza dane organizacji charytatywnej przed zapisaniem ich w stanie.

👉 Otwórz

charity_advisor/tools/charity_tools.py

U góry zobaczysz funkcję find_charities (już ukończoną). Przewiń w dół, aby znaleźć:

# MODULE_4_STEP_1_ADD_VALIDATION_HELPER

👉 Zastąp ten wiersz tym tekstem:

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, ""

Krok 2. Dodaj narzędzie IntentMandate Creation Helper

Teraz utwórzmy funkcję pomocniczą, która tworzy strukturę AP2 IntentMandate. Jest to jeden z 3 rodzajów weryfikowalnych danych logowania w AP2.

👉 W tym samym pliku znajdź:

# MODULE_4_STEP_2_ADD_INTENTMANDATE_CREATION_HELPER

👉 Zastąp ten wiersz tym tekstem:

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

Krok 3. Utwórz narzędzie do przekazywania stanu za pomocą IntentMandate

Teraz utwórzmy narzędzie, które tworzy obiekt IntentMandate i zapisuje go w stanie.

👉 W tym samym pliku przewiń w dół do sekcji

save_user_choice

funkcja . Znajdź:

# MODULE_4_STEP_3_COMPLETE_SAVE_TOOL

👉 Zastąp ten wiersz tym tekstem:

    # 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"]
    }

Krok 4. Dodaj narzędzie Display Formatting Helper

Zanim utworzymy agenta, dodajmy jeszcze jeden element pomocniczy, który będzie formatować dane organizacji charytatywnych w sposób przyjazny dla użytkownika.

👉 Przewiń, aby znaleźć:

# MODULE_4_STEP_4_ADD_FORMATTING_HELPER

👉 Zastąp ten wiersz tym tekstem:

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

Krok 5. Tworzenie agenta zakupowego – importowanie komponentów

Gdy nasze narzędzia są już gotowe i działają bez zarzutu, możemy utworzyć agenta, który będzie z nich korzystać.

👉 Otwórz

charity_advisor/shopping_agent/agent.py

Zobaczysz szablon z komentarzami zastępczymi. Zbudujmy go krok po kroku.

👉 Znajdź:

# MODULE_4_STEP_5_IMPORT_COMPONENTS

👉 Zastąp ten wiersz tym tekstem:

from google.adk.agents import Agent
from google.adk.tools import FunctionTool
from charity_advisor.tools.charity_tools import find_charities, save_user_choice

Krok 6. Napisz instrukcję dla agenta

Instrukcja to miejsce, w którym definiujemy opis stanowiska i przepływ pracy agenta. Jest to bardzo ważne, ponieważ źle napisana instrukcja prowadzi do nieprzewidywalnych działań.

👉 Znajdź:

# MODULE_4_STEP_6_WRITE_INSTRUCTION
instruction="""""",

👉 Zastąp te 2 wiersze tymi:

    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.""",

Krok 7. Dodaj narzędzia do agenta

Teraz przyznajmy agentowi dostęp do obu narzędzi.

👉 Znajdź:

# MODULE_4_STEP_7_ADD_TOOLS

👉 Zastąp te 2 wiersze tymi:

    tools=[
        FunctionTool(func=find_charities),
        FunctionTool(func=save_user_choice)
    ]

Krok 8. Weryfikacja ukończonego agenta

Sprawdźmy, czy wszystko jest prawidłowo podłączone.

👉 Twój pełny

charity_advisor/shopping_agent/agent.py

powinien teraz wyglądać tak:

"""
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)
    ]
)

Świetnie! Utworzono agenta zgodnego z AP2, który jest gotowy do wdrożenia w środowisku produkcyjnym i ma:

  • Weryfikacja danych wejściowych
  • Prawidłowe tworzenie obiektu IntentMandate za pomocą modeli Pydantic AP2
  • Sformatowane dane wyjściowe
  • Jasne granice ról
  • Szczegółowe instrukcje
  • Obsługa błędów

Krok 9. Przetestuj agenta zakupowego

Sprawdźmy, czy nasz agent działa prawidłowo, tworzy odpowiednie obietnice dotyczące intencji i przestrzega swoich ograniczeń.

👉 W terminalu Cloud Shell uruchom:

adk run charity_advisor/shopping_agent

Pojawi się wiersz poleceń [user]:.

Test 1. Odkrywanie za pomocą zaufanej bazy danych

👉 Typ:

I want to donate to an education charity. What are my options?

Po chwili agent udzieli odpowiedzi. Dzięki _format_charity_display pomocnikowi wyniki są pięknie sformatowane:

W naszej bazie danych znaleźliśmy 3 zweryfikowane organizacje charytatywne zajmujące się edukacją:

Room to Read (EIN: 77-0479905)
⭐ Ocena: 4,9/5,0
💰 Skuteczność: 88% środków jest przeznaczanych na programy
📋 Misja: zmienia życie milionów dzieci w społecznościach o niskich dochodach, koncentrując się na umiejętności czytania i pisania oraz równości płci w edukacji.

Teach For America (EIN: 13-3541913)
⭐ Ocena: 4,7/5,0
💰 Skuteczność: 81% środków jest przeznaczanych na programy
📋 Misja: zwiększanie możliwości edukacyjnych dzieci z trudnych środowisk.

Tech Education Alliance (EIN: 45-2345678)
⭐ Ocena: 4,8/5,0
💰 Skuteczność: 92% środków jest przeznaczanych na programy
📋 Misja: zapewnia edukację informatyczną w szkołach o ograniczonych możliwościach.

Którą organizację charytatywną chcesz wesprzeć i jaką kwotę chcesz przekazać?

Porównaj to z prostym agentem z modułu 3, który wyświetlał niezweryfikowane wyniki Google. To jest różnica między wykrywaniem niezaufanym a zaufanym.

Test 2. Zapisywanie wyboru użytkownika i tworzenie obiektu IntentMandate

👉 Typ:

I'll donate $50 to Room to Read.

Powinien on wywołać narzędzie save_user_choice z prawidłowymi parametrami. Dzięki tworzeniu IntentMandate za pomocą modeli AP2 zobaczysz bardziej ustrukturyzowaną odpowiedź:

Super! Utworzyliśmy dla Ciebie dokument IntentMandate dotyczący darowizny:

Szczegóły intencji:

  • Identyfikator intencji: intent_774795905_1730927536
  • Kwota: 50,00 USD na rzecz organizacji Room to Read (EIN: 77-0479905)
  • Wygasa: 2024-11-07T15:32:16Z (za godzinę)

Ten dokument IntentMandate odzwierciedla Twoją intencję przekazania darowizny i zawiera ograniczenia, które zapewniają bezpieczne przetwarzanie transakcji. Przekazuję teraz tę informację do naszego bezpiecznego procesora płatności, aby utworzyć formalną ofertę i dokończyć przekazywanie darowizny.

W tle wydarzyło się kilka rzeczy:

  1. _validate_charity_data() zweryfikowano dane wejściowe.
  2. _create_intent_mandate() użył modelu AP2 Pydantic do utworzenia struktury:
   # 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"
   })
  1. Narzędzie zapisało w stanie udostępnionym TYLKO element IntentMandate:
   state["intent_mandate"] = intent_mandate_dict
  1. Do LLM została zwrócona wiadomość o sukcesie zawierająca szczegóły intencji.
  2. Model LLM utworzył końcową wiadomość dla użytkownika.

Ten dokument IntentMandate jest gotowy do odebrania przez przedstawiciela sprzedawcy (moduł 5). Sprzedawca wyodrębni z dokumentu IntentMandate nazwę organizacji charytatywnej i kwotę oraz sprawdzi, czy dokument nie wygasł.

Test 3. Weryfikacja w praktyce

Sprawdźmy, czy nasz pomocnik weryfikacji wykrywa nieprawidłowe dane wejściowe.

👉 Typ:

I'll donate -$25 to Room to Read.

Agent powinien wychwycić nieprawidłową kwotę:

Wystąpił problem: kwota darowizny musi być dodatnia, otrzymano: $-25.0

Podaj kwotę darowizny (musi być dodatnia), a ja utworzę dla Ciebie IntentMandate.

To jest przykład programowania defensywnego w praktyce. Nasz _validate_charity_datapomocnik zapobiegł wprowadzeniu do systemu nieprawidłowych danych i utworzeniu nieprawidłowego obiektu IntentMandate.

Test 4. Weryfikacja granicy zaufania

👉 Poproś agenta o przetworzenie płatności:

Now process my credit card payment.

Agent powinien odmówić, przestrzegając zakresu swojej roli:

Nie mam możliwości przetwarzania płatności – nie należy to do moich obowiązków. Moim zadaniem jest pomóc Ci znaleźć zweryfikowane organizacje charytatywne i utworzyć dokument IntentMandate, który odzwierciedla Twoje intencje dotyczące darowizny.

Twój dokument IntentMandate został już utworzony i przekazany do naszego bezpiecznego procesora płatności. Agent sprzedawcy utworzy formalną ofertę (CartMandate), a dostawca danych logowania zajmie się płatnością za Twoją wyraźną zgodą.

To jest granica zaufania w działaniu. Agent wie, że nie ma uprawnień do obsługi danych dotyczących płatności, a jego instrukcja wyraźnie nakazuje mu wyjaśnienie tego użytkownikom, a także zapoznanie ich z koncepcją IntentMandate.

👉 Naciśnij

Ctrl+C

, aby wyjść po zakończeniu testowania.

Co właśnie zostało utworzone

Udało Ci się wdrożyć pierwszy element architektury AP2 z prawidłowym tworzeniem obiektu IntentMandate za pomocą modeli Pydantic AP2.

Opanowane kluczowe pojęcia

✅ Architektura oparta na rolach:

  • Każdy agent ma jedno jasno określone zadanie.
  • Agenci komunikują się za pomocą stanu udostępnionego, a nie bezpośredniego dostępu.
  • Granice zaufania ograniczają wpływ naruszenia bezpieczeństwa

IntentMandate (AP2 Credential #1):

  • Utworzone przy użyciu oficjalnych modeli Pydantic AP2 na potrzeby weryfikacji
  • Ustrukturyzowane rejestrowanie intencji użytkownika
  • Obejmuje wygaśnięcie zabezpieczeń (zapobiega atakom typu replay)
  • Określa ograniczenia (sprzedawcy, możliwość zwrotu środków, potwierdzenie)
  • Opis w języku naturalnym dla ludzi
  • Czytelne dla agentów
  • Model weryfikowany przed przekształceniem w słownik

✅ Stan jako wspomnienie udostępnione:

  • tool_context.state to „notatnik”, do którego mają dostęp wszyscy agenci.
  • Zapisywanie w stanie = udostępnianie weryfikowalnych danych logowania
  • Odczyt ze stanu = wykorzystywanie i weryfikowanie danych logowania
  • Agenty podrzędne wyodrębniają z danych logowania potrzebne informacje.

FunctionTool:

  • Konwertuje funkcje Pythona na narzędzia wywoływane przez LLM
  • Opiera się na docstringach i wskazówkach dotyczących typów, aby model LLM mógł je zrozumieć.
  • Automatycznie obsługuje wywołanie
  • Komponowanie narzędzi: małe, wyspecjalizowane narzędzia > narzędzia monolityczne

Instrukcje dla pracowników obsługi klienta:

  • Wskazówki dotyczące przepływu pracy krok po kroku
  • Wyraźne granice („NIE...”)
  • Specyfikacje parametrów zapobiegające błędom
  • Definicje techniczne (co to jest IntentMandate)
  • Obsługa przypadków skrajnych (co powiedzieć, gdy…)

Co dalej

W następnym module utworzymy agenta sprzedawcy, który będzie odbierać IntentMandate i tworzyć drugie poświadczenie weryfikowalne: CartMandate.

Agent zakupowy utworzył obiekt IntentMandate, który rejestruje zamiar użytkownika z datą ważności. Teraz potrzebujemy agenta, który odczyta te dane logowania, sprawdzi, czy nie wygasły, i utworzy formalną, podpisaną ofertę o treści: „Ja, sprzedawca, zobowiązuję się do utrzymania tej ceny i dostarczenia tych towarów”.

Utwórzmy agenta sprzedawcy i zobaczmy, jak działa drugi rodzaj danych logowania AP2.

5. Tworzenie agenta sprzedawcy – oferty wiążące i CartMandate

baner

Od odkrywania do zaangażowania

W poprzednim module utworzyliśmy agenta zakupów – specjalistę, który wyszukuje zweryfikowane organizacje charytatywne i tworzy obiekt IntentMandate, który rejestruje zamiary użytkownika. Teraz potrzebujemy agenta, który otrzyma ten dokument i utworzy formalną, wiążącą ofertę.

W tym miejscu pojawia się druga kluczowa zasada AP2: weryfikowalne dane logowania za pomocą CartMandate.

Zasada AP2: CartMandate i oferty wiążące

Dlaczego potrzebujemy roli sprzedawcy

W module 4 agent zakupowy utworzył obiekt IntentMandate i zapisał go w stanie:

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"
}

To jednak tylko zamiar użytkownika. Zanim będziemy mogli przetworzyć płatność, musimy mieć:

  • formalną strukturę oferty, którą rozumieją systemy płatności;
  • dowód na to, że sprzedawca zaakceptuje tę cenę.
  • Zobowiązanie wiążące, którego nie można zmienić w trakcie transakcji.
  • Sprawdzenie, czy intencja nie wygasła.

To zadanie agenta sprzedawcy.

Co to jest CartMandate?

CartMandate to termin używany przez AP2 na określenie „cyfrowego koszyka”, który służy jako wiążąca oferta. Jest ona zgodna ze standardem W3C PaymentRequest, co oznacza, że:

  • Format jest rozpoznawany przez firmy obsługujące płatności na całym świecie.
  • Zawiera wszystkie szczegóły transakcji w standardowy sposób.
  • Może być podpisany kryptograficznie, aby potwierdzić autentyczność.

Można to porównać do pisemnej wyceny od wykonawcy:

  • ❌ Słownie: „Tak, mogę wykonać to zadanie za około 50 złotych”.
  • ✅ Pisemna wycena: wyszczególnione koszty, suma, podpis, data.

Pisemna wycena jest wiążąca. Dokument CartMandate jest cyfrowym odpowiednikiem tego dokumentu.

zamiar dodania do koszyka

Struktura CartMandate

Obiekt CartMandate w AP2 ma określoną strukturę zagnieżdżoną:

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
}

3 główne elementy:

1. contents – kontener koszyka zawierający:

  • Identyfikator koszyka i data ważności
  • Nazwa sprzedawcy
  • W3C PaymentRequest

2. payment_request (w treści) – co jest kupowane:

  • method_data: akceptowane formy płatności
  • szczegóły: produkty i suma,
  • opcje: wymagania dotyczące dostawy i informacji o płatniku;

3. merchant_authorization – podpis kryptograficzny

Podpisy sprzedawców: dowód zobowiązania

Podpis sprzedawcy jest niezbędny. Potwierdza to:

  • Ta oferta pochodzi od autoryzowanego sprzedawcy
  • Sprzedawca zobowiązuje się do honorowania tej dokładnej ceny.
  • od momentu utworzenia oferta nie została zmieniona;

W środowisku produkcyjnym byłby to podpis kryptograficzny z użyciem infrastruktury klucza publicznego (PKI) lub tokenów sieciowych JSON (JWT). Na potrzeby warsztatów edukacyjnych zasymulujemy to za pomocą haszu 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]}"

Nasza misja: stworzenie agenta sprzedawcy

Przedstawiciel handlowy:

  1. Odczytanie IntentMandate ze stanu (tego, co napisał agent Zakupów)
  2. Sprawdź, czy intencja nie wygasła.
  3. Wyodrębnij nazwę organizacji charytatywnej, kwotę i inne szczegóły.
  4. Tworzenie struktury PaymentRequest zgodnej ze standardem W3C za pomocą modeli Pydantic AP2
  5. Zapakuj go w CartMandate interfejsu AP2 z datą ważności.
  6. Dodawanie symulowanego podpisu sprzedawcy
  7. Napisz dokument CartMandate, aby określić stan dostawcy danych uwierzytelniających (następny moduł)

Zbudujmy go krok po kroku.

Krok 1. Dodaj narzędzie Expiry Validation Helper

Najpierw skonfigurujmy plik narzędzi związanych z merchantem i dodajmy pomocnika do sprawdzania ważności IntentMandate.

👉 Otwórz

charity_advisor/tools/merchant_tools.py

Dodajmy weryfikację daty ważności:

👉 Znajdź:

# MODULE_5_STEP_1_ADD_EXPIRY_VALIDATION_HELPER

👉 Zastąp ten wiersz tym tekstem:

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}"

Krok 2. Dodaj narzędzie Signature Generation Helper

Teraz utwórzmy funkcję pomocniczą, która będzie generować symulowany podpis sprzedawcy.

👉 Znajdź:

# MODULE_5_STEP_2_ADD_SIGNATURE_HELPER

👉 Zastąp ten wiersz tym tekstem:

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

Krok 3A. Utwórz podpis narzędzia i skonfiguruj je

Teraz zacznijmy tworzyć główne narzędzie. Utworzymy go stopniowo w 4 podkrokach. Najpierw podpis funkcji i konfiguracja początkowa.

👉 Znajdź:

# MODULE_5_STEP_3A_CREATE_TOOL_SIGNATURE

👉 Zastąp ten wiersz tym tekstem:

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

Krok 3B. Dodaj logikę weryfikacji

Teraz dodajmy logikę odczytywania i weryfikowania dokumentu IntentMandate za pomocą modeli Pydantic AP2 oraz wyodrębniania potrzebnych danych.

👉 Znajdź:

# MODULE_5_STEP_3B_ADD_VALIDATION_LOGIC

👉 Zastąp ten wiersz tym tekstem:

    # 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

Krok 3C. Tworzenie struktury CartMandate

Teraz zbudujmy strukturę PaymentRequest zgodną ze standardem W3C i umieśćmy ją w AP2 CartMandate za pomocą modeli Pydantic.

👉 Znajdź:

# MODULE_5_STEP_3C_CREATE_CARTMANDATE_STRUCTURE

👉 Zastąp ten wiersz tym tekstem:

    # 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

Krok 3D. Dodaj podpis i zapisz w stanie

Na koniec podpiszmy CartMandate za pomocą naszego modelu Pydantic i zapiszmy go w stanie dla następnego agenta.

👉 Znajdź:

# MODULE_5_STEP_3D_ADD_SIGNATURE_AND_SAVE

👉 Zastąp ten wiersz tym tekstem:

    # 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
    }

Krok 4. Tworzenie agenta sprzedawcy – importowanie komponentów

Teraz utwórzmy agenta, który będzie korzystać z tego narzędzia.

👉 Otwórz

charity_advisor/merchant_agent/agent.py

Zobaczysz szablon ze znacznikami zastępczymi. Zacznijmy od zaimportowania tego, czego potrzebujemy.

👉 Znajdź:

# MODULE_5_STEP_4_IMPORT_COMPONENTS

👉 Zastąp ten wiersz tym tekstem:

from google.adk.agents import Agent
from google.adk.tools import FunctionTool
from charity_advisor.tools.merchant_tools import create_cart_mandate

Krok 5. Napisz instrukcję dla agenta sprzedawcy

Teraz napiszemy instrukcję, która powie agentowi, kiedy i jak ma używać narzędzia.

👉 Znajdź:

# MODULE_5_STEP_5_WRITE_INSTRUCTION
instruction="""""",

👉 Zastąp te 2 wiersze tymi:

    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.""",

Krok 6. Dodaj narzędzia do Merchant Agent

👉 Znajdź:

# MODULE_5_STEP_6_ADD_TOOLS
tools=[],

👉 Zastąp te 2 wiersze tymi:

    tools=[
        FunctionTool(func=create_cart_mandate)
    ],

Krok 7. Sprawdź agenta Complete Merchant Agent

Sprawdźmy, czy wszystko jest prawidłowo podłączone.

👉 Twój pełny

charity_advisor/merchant_agent/agent.py

powinien teraz wyglądać tak:

"""
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."""
)

Punkt kontrolny: masz teraz kompletnego agenta sprzedawcy z prawidłowym tworzeniem AP2 CartMandate za pomocą modeli Pydantic.

Krok 8. Przetestuj agenta sprzedawcy

Teraz sprawdźmy, czy nasz agent prawidłowo tworzy obciążenia koszyka z podpisami i weryfikuje datę ważności.

Konfiguracja testu: uruchamianie skryptu testowego

👉 W terminalu Cloud Shell uruchom:

python scripts/test_merchant.py

Oczekiwane dane wyjściowe:

======================================================================
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. Weryfikacja zgodności ze standardami W3C

Sprawdźmy, czy struktura CartMandate jest w pełni zgodna ze standardami AP2 i W3C PaymentRequest.

👉 Uruchom skrypt weryfikacji:

python scripts/validate_cartmandate.py

Oczekiwane dane wyjściowe:

======================================================================
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
======================================================================

Co właśnie zostało utworzone

Udało Ci się wdrożyć CartMandate interfejsu AP2 za pomocą modeli Pydantic, aby zapewnić prawidłową strukturę, weryfikację daty ważności i podpisy sprzedawcy.

Opanowane kluczowe pojęcia

CartMandate (AP2 Credential #2):

  • Utworzono przy użyciu oficjalnych modeli Pydantic AP2
  • Struktura AP2 z elementem opakowującym treści
  • W3C PaymentRequest zagnieżdżony w
  • Wygasanie koszyka (krótsze niż intencja)
  • Podpis sprzedawcy potwierdzający zobowiązanie
  • Weryfikacja modelu zapewnia zgodność ze specyfikacją

✅ Weryfikacja daty ważności:

  • Reading IntentMandate from state
  • Sprawdzanie struktury za pomocą IntentMandate.model_validate()
  • Parsowanie sygnatur czasowych w formacie ISO 8601
  • Porównywanie z bieżącym czasem
  • Funkcja zabezpieczeń zapobiegająca nieaktualnemu przetwarzaniu

Podpis sprzedawcy:

  • Potwierdza autentyczność i zaangażowanie
  • Wygenerowano na podstawie zweryfikowanego modelu Pydantic
  • Używa model_dump(mode='json') do reprezentacji kanonicznej
  • Symulacja z SHA-256 dla celów edukacyjnych
  • W użyciu wersja produkcyjna PKI/JWT
  • Podpisuje model treści, a nie słowniki.

✅ W3C PaymentRequest:

  • Utworzono przy użyciu modelu Pydantic PaymentRequest interfejsu AP2
  • Standard branżowy dotyczący danych o płatnościach
  • Zagnieżdżone w strukturze AP2
  • Zawiera method_data, details, options
  • Umożliwia współdziałanie

Łańcuchy danych logowania z modelami:

  • Zakupy → IntentMandate (zweryfikowane)
  • Sprzedawca odczytuje IntentMandate → CartMandate (oba modele są weryfikowane)
  • Dostawca danych uwierzytelniających odczyta CartMandate → PaymentMandate
  • Każdy krok weryfikuje poprzednie dane logowania za pomocą Pydantic.

✅ Tworzenie oparte na modelu:

  • Sprawdzanie poprawności danych wejściowych za pomocą model_validate()
  • Konstrukcja bezpieczna pod względem typów
  • Automatyczna serializacja za pomocą model_dump()
  • Wzorce gotowe do wykorzystania w środowisku produkcyjnym

Co dalej

W następnym module utworzymy dostawcę danych uwierzytelniających, który będzie bezpiecznie przetwarzać płatności.

Agent sprzedawcy utworzył wiążącą ofertę z datą ważności za pomocą modeli AP2. Teraz potrzebujemy agenta, który odczyta CartMandate, uzyska zgodę użytkownika i zrealizuje płatność.

Utwórzmy dostawcę danych logowania i dokończmy łańcuch danych logowania AP2.

6. Tworzenie dostawcy danych logowania – bezpieczne wykonywanie płatności

baner

Od wiążącej oferty do realizacji płatności

W module 5 utworzyliśmy agenta sprzedawcy – specjalistę, który odczytuje IntentMandates, sprawdza, czy nie wygasły, i tworzy wiążące CartMandates z podpisami sprzedawcy. Teraz potrzebujemy agenta, który otrzyma ten obiekt CartMandate i zrealizuje płatność.

W tym miejscu pojawia się trzecia i ostatnia zasada AP2: bezpieczne wykonywanie płatności za pomocą PaymentMandate.

Zasada AP2: PaymentMandate & Payment Execution

Dlaczego potrzebujemy roli dostawcy danych logowania

W module 5 agent sprzedawcy utworzył obiekt CartMandate i zapisał go w stanie:

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"
}

Ale to tylko wiążąca oferta. Zanim będziemy mogli zrealizować płatność, musimy:

  • Sprawdzenie, czy koszyk nie wygasł
  • Zgoda użytkownika na kontynuowanie płatności
  • dane logowania, które autoryzują wykonanie płatności;
  • rzeczywiste przetwarzanie płatności (lub symulacja na potrzeby naszych warsztatów);

To zadanie dostawcy danych logowania.

Co to jest PaymentMandate?

PaymentMandate to termin używany przez AP2 na określenie ostatecznej autoryzacji, która umożliwia wykonanie płatności. Jest to trzecie i ostatnie weryfikowalne poświadczenie w łańcuchu AP2.

Pomyśl o tych 3 rodzajach danych logowania jak o procesie podpisywania umowy:

  • IntentMandate: „Chcę kupić ten produkt” (list intencyjny)
  • CartMandate: „Ja, sprzedawca, oferuję sprzedaż w tej cenie” (pisemna oferta)
  • PaymentMandate: „Upoważniam Cię do obciążenia mojej formy płatności” (podpisana umowa)

Płatność można zrealizować dopiero po utworzeniu wszystkich 3 rodzajów danych logowania.

pełny łańcuch danych logowania,

Struktura dokumentu PaymentMandate

Dokument PaymentMandate w AP2 ma określoną strukturę:

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"
}

Kluczowe komponenty:

1. payment_mandate_contents – otoczka autoryzacji zawierająca:

  • payment_mandate_id: unikalny identyfikator
  • payment_details_id: link do obiektu CartMandate
  • user_consent: czy użytkownik wyraził zgodę
  • amount: kwota płatności (wyodrębniona z dokumentu CartMandate);

2. agent_present – czy jest to proces z udziałem człowieka.

3. timestamp – czas utworzenia autoryzacji.

Nasza misja: stworzenie dostawcy danych logowania

Dostawca danych logowania:

  1. Odczytanie CartMandate ze stanu (tego, co napisał agent sprzedawcy)
  2. Sprawdzanie, czy koszyk nie wygasł, za pomocą modeli Pydantic AP2
  3. Wyodrębnianie szczegółów płatności z zagnieżdżonej struktury
  4. Tworzenie obiektu PaymentMandate za zgodą użytkownika za pomocą modeli AP2
  5. Symulowanie przetwarzania płatności (w środowisku produkcyjnym wywoływany byłby prawdziwy interfejs API płatności)
  6. Zapisywanie PaymentMandate i wyniku płatności w stanie

Zbudujmy go krok po kroku.

Krok 1. Dodaj narzędzie Cart Expiry Validation Helper

Najpierw utwórzmy funkcję pomocniczą, która sprawdza, czy CartMandate nie wygasł – tak jak w module 5 sprawdzaliśmy wygaśnięcie IntentMandate.

👉 Otwórz

charity_advisor/tools/payment_tools.py

Dodajmy weryfikację daty ważności:

👉 Znajdź:

# MODULE_6_STEP_1_ADD_CART_EXPIRY_VALIDATION_HELPER

👉 Zastąp ten wiersz tym tekstem:

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}"

Krok 2. Dodaj PaymentMandate Creation Helper

Teraz utwórzmy funkcję pomocniczą, która będzie tworzyć strukturę PaymentMandate za pomocą oficjalnych modeli Pydantic AP2.

👉 Znajdź:

# MODULE_6_STEP_2_ADD_PAYMENT_MANDATE_CREATION_HELPER

👉 Zastąp ten wiersz tym tekstem:

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

Krok 3A. Utwórz podpis narzędzia i skonfiguruj je

Teraz zacznijmy stopniowo tworzyć główne narzędzie. Najpierw podpis funkcji i konfiguracja początkowa.

👉 Znajdź:

# MODULE_6_STEP_3A_CREATE_TOOL_SIGNATURE

👉 Zastąp ten wiersz tym tekstem:

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

Krok 3B. Sprawdź poprawność CartMandate

Teraz dodajmy logikę odczytywania i weryfikowania CartMandate za pomocą modeli AP2 Pydantic oraz sprawdzania daty ważności.

👉 Znajdź:

# MODULE_6_STEP_3B_VALIDATE_CARTMANDATE

👉 Zastąp ten wiersz tym tekstem:

    # 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

Krok 3C. Wyodrębnianie danych do płatności z zagnieżdżonej struktury

Teraz przejdźmy do zweryfikowanego modelu CartMandate, aby wyodrębnić potrzebne szczegóły płatności.

👉 Znajdź:

# MODULE_6_STEP_3C_EXTRACT_PAYMENT_DETAILS

👉 Zastąp ten wiersz tym tekstem:

    # 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

Krok 3D. Utwórz PaymentMandate i zasymuluj płatność

Na koniec utwórzmy PaymentMandate za pomocą naszego narzędzia opartego na Pydantic, zasymulujmy przetwarzanie płatności i zapiszmy wszystko w stanie.

👉 Znajdź:

# MODULE_6_STEP_3D_CREATE_PAYMENTMANDATE_AND_SIMULATE

👉 Zastąp ten wiersz tym tekstem:

    # 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"]
    }

Krok 4. Utwórz agenta dostawcy danych logowania – zaimportuj komponenty

Teraz utwórzmy agenta, który będzie korzystać z tego narzędzia.

👉 Otwórz

charity_advisor/credentials_provider/agent.py

Zobaczysz szablon ze znacznikami zastępczymi. Zacznijmy od zaimportowania tego, czego potrzebujemy.

👉 Znajdź:

# MODULE_6_STEP_4_IMPORT_COMPONENTS

👉 Zastąp ten wiersz tym tekstem:

from google.adk.agents import Agent
from google.adk.tools import FunctionTool
from charity_advisor.tools.payment_tools import create_payment_mandate

Krok 5. Napisz instrukcję dostawcy danych logowania

Teraz napiszmy instrukcję, która będzie kierować agentem.

👉 Znajdź:

# MODULE_6_STEP_5_WRITE_INSTRUCTION
instruction="""""",

👉 Zastąp te 2 wiersze tymi:

    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""",

Krok 6. Dodaj narzędzia do dostawcy danych logowania

👉 Znajdź:

# MODULE_6_STEP_6_ADD_TOOLS
tools=[],

👉 Zastąp te 2 wiersze tymi:

    tools=[
        FunctionTool(func=create_payment_mandate)
    ],

Krok 7. Weryfikacja dostawcy pełnych danych logowania

Sprawdźmy, czy wszystko jest prawidłowo podłączone.

👉 Twój pełny

charity_advisor/credentials_provider/agent.py

powinien teraz wyglądać tak:

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

Punkt kontrolny: masz teraz kompletny dostawcę danych logowania z prawidłowym odczytywaniem upoważnienia do koszyka i tworzeniem upoważnienia do płatności za pomocą modeli Pydantic AP2.

Krok 8. Przetestuj dostawcę danych logowania

Sprawdźmy teraz, czy nasz agent prawidłowo przetwarza płatności i uzupełnia łańcuch danych logowania.

👉 W terminalu Cloud Shell uruchom:

python scripts/test_credentials_provider.py

Oczekiwane dane wyjściowe:

======================================================================
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
======================================================================

Krok 9. Przetestuj pełną ścieżkę z 3 agentami

Teraz przetestujmy wszystkie 3 agenty działające razem.

👉 Uruchom pełny test potoku:

python scripts/test_full_pipeline.py

Oczekiwane dane wyjściowe:

======================================================================
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
======================================================================

To kompletny łańcuch poświadczeń AP2 w działaniu.

Każdy agent:

  1. Odczytuje dane logowania ze stanu
  2. Sprawdza go za pomocą modeli Pydantic (struktura + sprawdzenie daty ważności).
  3. Tworzy kolejne dane logowania za pomocą modeli AP2
  4. Zapisuje stan dla następnego agenta

Co właśnie zostało utworzone

Udało Ci się utworzyć łańcuch poświadczeń AP2 z 3 agentami z prawidłową weryfikacją struktury za pomocą modeli Pydantic i symulacji płatności.

Opanowane kluczowe pojęcia

✅ PaymentMandate (AP2 Credential #3):

  • Utworzono przy użyciu oficjalnych modeli Pydantic AP2
  • Ostateczne dane logowania autoryzujące wykonanie płatności
  • Linki do CartMandate za pomocą identyfikatora payment_details_id
  • Rejestruje zgodę użytkownika i sygnaturę czasową
  • Zawiera kwotę płatności wyodrębnioną z obiektu CartMandate.
  • Zawiera flagę agent_present dla procesu z udziałem człowieka
  • Weryfikacja modelu zapewnia zgodność ze specyfikacją

✅ Odczytywanie z dokumentu CartMandate:

  • Sprawdzanie struktury za pomocą narzędzia CartMandate.model_validate()
  • Bezpieczny typowo dostęp do atrybutów: cart_model.contents.payment_request.details.total.amount
  • Różnice między otoką AP2 a separacją standardu W3C
  • Bezpieczne wyodrębnianie z modelu informacji merchant_name, amount i currency
  • Pydantic automatycznie wykrywa błędy struktury

✅ Weryfikacja wygaśnięcia koszyka:

  • Akceptuje zweryfikowany CartMandate model Pydantic
  • Odczytuje z cart.contents.cart_expiry (dostęp do atrybutu)
  • Funkcja zabezpieczeń zapobiegająca przetwarzaniu nieaktualnych koszyków
  • krótszy czas trwania (15 minut) niż czas trwania intencji (1 godzina);

✅ Symulacja płatności:

  • Symulacja edukacyjna rzeczywistej firmy obsługującej płatności
  • Generuje identyfikator transakcji
  • Rejestruje payment_result w stanie
  • Wyraźnie oznaczony jako symulacja (flaga symulacji: True)

✅ Ukończ łańcuch AP2 z modelami:

  • 3 agenty, 3 dane logowania, 3 weryfikacje Pydantic
  • Każdy agent weryfikuje strukturę poprzednich danych logowania za pomocą modeli.
  • Każde dane logowania są połączone z poprzednimi w celu utworzenia ścieżki audytu.
  • Przekazywanie zadań na podstawie stanu zachowuje podział ról
  • Zabezpieczenia wpisywania w całym łańcuchu

✅ Tworzenie oparte na modelu:

  • Sprawdzanie poprawności danych wejściowych za pomocą model_validate()
  • Konstrukcja bezpieczna pod względem typów z modelami zagnieżdżonymi
  • Automatyczna serializacja za pomocą model_dump(mode='json')
  • Wzorce gotowe do użytku produkcyjnego od samego początku

Co dalej

W następnym module utworzymy agenta aranżera, który koordynuje pracę wszystkich 3 agentów specjalistów.

Utworzono 3 zaawansowane specjalistyczne agenty przy użyciu modeli AP2 Pydantic. Teraz zbudujmy element, który będzie koordynować te działania, aby zapewnić bezproblemowe przekazywanie darowizn.

Zbudujmy aranżera i zobaczmy, jak działa cały system.

7. Administracja – wszystko w jednym miejscu

potok sekwencyjny,

Od specjalistów do bezproblemowej obsługi

W poprzednich modułach utworzyliśmy 3 wyspecjalizowane agenty:

  • Agent zakupowy: wyszukuje organizacje charytatywne i tworzy IntentMandate.
  • Agent sprzedawcy: tworzy CartMandate na podstawie IntentMandate.
  • Dostawca danych uwierzytelniających: tworzy PaymentMandate, przetwarza płatność.

Te agenty dzielą się na 2 fazy:

  • Etap 1. (zakupy): wieloetapowa rozmowa w celu znalezienia i wybrania organizacji charytatywnej.
  • Etap 2. (Przetwarzanie): atomowe wykonanie tworzenia oferty i płatności

Obecnie musisz ręcznie koordynować te fazy.

W takich sytuacjach przydają się wzorce orkiestracji ADK.

Zasada AP2: orkiestracja wymusza granice zaufania

Dlaczego orkiestracja ma znaczenie dla bezpieczeństwa

Orkiestracja to nie tylko wygoda, ale też wymuszanie granic zaufania za pomocą architektury.

Bez orkiestracji:

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

Z odpowiednią aranżacją przekazu:

# 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

Potok sekwencyjny gwarantuje:

  • ✅ IntentMandate utworzono przed CartMandate
  • ✅ CartMandate utworzony przed przetworzeniem płatności
  • ✅ Każdy agent działa w izolowanym kontekście.
  • ✅ Stan jest przekazywany dalej w łańcuchu danych logowania

Nasza misja: stworzenie kompletnego systemu

Utworzymy 2 warstwy:

Warstwa 1. Potok przetwarzania (SequentialAgent)

  • Przewody razem Sprzedawca → Dane logowania
  • Uruchamia się automatycznie po wybraniu organizacji charytatywnej
  • Atomowe wykonanie oferty i płatności

Warstwa 2. Główny aranżer (agent widoczny dla użytkownika)

  • przyjazne usposobienie,
  • Przekazuje wybór organizacji charytatywnej do usługi shopping_agent
  • Przekazuje do potoku przetwarzania po utworzeniu IntentMandate
  • Obsługuje rozmowy i przejścia między fazami

Takie dwuwarstwowe podejście jest zgodne z naturalnym przepływem informacji:

  • Etap zakupów: wieloetapowa rozmowa (użytkownik przegląda produkty, zadaje pytania i podejmuje decyzję).
  • Etap przetwarzania: wykonanie atomowe (oferta → płatność)

Stwórzmy oba.

Krok 1. Zaimportuj komponenty orkiestracji

Najpierw skonfigurujmy plik orkiestracji z niezbędnymi importami.

👉 Otwórz

charity_advisor/agent.py

Zacznijmy od importów:

👉 Znajdź:

# MODULE_7_STEP_1_IMPORT_COMPONENTS

👉 Zastąp ten wiersz tym tekstem:

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

Krok 2. Utwórz potok przetwarzania

Teraz utwórzmy potok, który będzie atomowo uruchamiać tworzenie oferty i przetwarzanie płatności.

👉 Znajdź:

# MODULE_7_STEP_2_CREATE_SEQUENTIAL_PIPELINE

👉 Zastąp te 2 wiersze tymi:

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

Krok 3A. Utwórz konfigurację agenta głównego

Teraz utwórzmy agenta, z którego będą korzystać użytkownicy i który będzie koordynować obie fazy. Omówimy to w 3 częściach: konfiguracja (3A), instrukcje (3B) i podagenci (3C).

👉 Znajdź:

# MODULE_7_STEP_3A_CREATE_ROOT_AGENT_SETUP

👉 Zastąp ten wiersz tym tekstem:

# 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

Krok 3B. Napisz instrukcję dla głównego agenta

Teraz dodajmy instrukcję, która będzie kierować zachowaniem doradcy ds. organizacji charytatywnych w obu fazach.

👉 Znajdź:

# MODULE_7_STEP_3B_WRITE_ROOT_AGENT_INSTRUCTION

👉 Zastąp ten wiersz tym tekstem:

    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

Krok 3C. Dodaj podagenta

Na koniec przyznajmy doradcy ds. organizacji charytatywnych dostęp do agenta zakupowego i potoku przetwarzania, a potem zamknijmy definicję agenta.

👉 Znajdź:

# MODULE_7_STEP_3C_ADD_ROOT_AGENT_SUBAGENTS

👉 Zastąp ten wiersz tym tekstem:

    sub_agents=[
        shopping_agent,
        donation_processing_pipeline
    ]
)

Krok 4. Sprawdź kompletny system

Sprawdźmy, czy orkiestracja jest prawidłowo skonfigurowana.

👉 Twój pełny

charity_advisor/agent.py

powinien teraz wyglądać tak:

"""
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
    ]
)

Krok 5. Zwiększ bezpieczeństwo za pomocą wywołań zwrotnych weryfikacji (opcjonalnie – przejdź do kroku 7)

wywołania zwrotne,

SequentialAgent gwarantuje kolejność wykonywania, ale co się stanie, jeśli:

  • Agent zakupów nie działa (nie utworzono elementu IntentMandate)
  • Godzina mija między zakupami a Merchant (zamiar wygasa)
  • Stan ulega uszkodzeniu lub wyczyszczeniu
  • Użytkownik próbuje zadzwonić bezpośrednio do sprzedawcy, pomijając Zakupy Google

Wywołania zwrotnedodają wymuszanie architektury – sprawdzają wymagania wstępne, zanim agent rozpocznie wywołanie LLM. Jest to obrona warstwowa: narzędzia weryfikują podczas wykonywania, a wywołania zwrotne weryfikują przed wykonaniem.

Dodajmy wywołania zwrotne weryfikacji do agentów Merchant i Credentials Provider.

Krok 5A. Dodaj weryfikację sprzedawcy – importowanie typów wywołań zwrotnych

Najpierw dodajmy importy potrzebne do wywołań zwrotnych.

👉 Otwórz

charity_advisor/merchant_agent/agent.py

U góry pliku, po istniejących instrukcjach importu, dodaj:

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

Krok 5B. Utwórz funkcję weryfikacji intencji

Teraz utwórzmy funkcję wywołania zwrotnego, która będzie weryfikować IntentMandate przed uruchomieniem Merchant Agent.

👉 W

charity_advisor/merchant_agent/agent.py

, dodaj tę funkcję PRZED

merchant_agent = Agent(...)

definicja:

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

Krok 5C. Dołączanie funkcji oddzwaniania do agenta Merchant

Teraz połączmy oddzwanianie z pracownikiem obsługi klienta.

👉 W

charity_advisor/merchant_agent/agent.py

, zmień

merchant_agent = Agent(...)

definicja:

Znajdź w definicji agenta ten wiersz:

merchant_agent = Agent(
    name="MerchantAgent",
    model="gemini-2.5-flash",
    description="Creates formal, signed CartMandates for charity donations following W3C PaymentRequest standards.",

Dodaj ten wiersz bezpośrednio po

description

linia:

    before_agent_callback=validate_intent_before_merchant,

Definicja agenta powinna teraz wyglądać tak:

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="""..."""
)

Krok 6. Dodaj weryfikację dostawcy danych logowania (opcjonalnie – przejdź do kroku 7)

Ten sam wzorzec – dodajmy weryfikację na etapie płatności.

Krok 6A. Importowanie typów połączeń zwrotnych

👉 Otwórz

charity_advisor/credentials_provider/agent.py

U góry pliku, po istniejących instrukcjach importu, dodaj:

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

Krok 6B. Utwórz funkcję weryfikacji koszyka

👉 W

charity_advisor/credentials_provider/agent.py

, dodaj tę funkcję PRZED

credentials_provider = Agent(...)

definicja:

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

Krok 6C. Dołączanie wywołania zwrotnego do dostawcy danych logowania

👉 W

charity_advisor/credentials_provider/agent.py

, zmień

credentials_provider = Agent(...)

definicja:

Znajdź w definicji agenta ten wiersz:

credentials_provider = Agent(
    name="CredentialsProvider",
    model="gemini-2.5-flash",
    description="Securely processes payments by creating PaymentMandates and executing transactions with user consent.",

Dodaj ten wiersz bezpośrednio po

description

linia:

    before_agent_callback=validate_cart_before_payment,

Definicja agenta powinna teraz wyglądać tak:

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="""..."""
)

Krok 7. Testowanie za pomocą interfejsu internetowego ADK

Teraz przetestujmy cały wzmocniony system z aktywnymi wywołaniami zwrotnymi weryfikacji.

👉 W terminalu Cloud Shell uruchom:

adk web

Powinny się wyświetlić dane wyjściowe podobne do tych:

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

👉 Następnie, aby uzyskać dostęp do interfejsu internetowego ADK w przeglądarce:

Na pasku narzędzi Cloud Shell (zwykle w prawym górnym rogu) kliknij ikonę Podgląd w przeglądarce (wygląda jak oko lub kwadrat ze strzałką) i wybierz Zmień port. W wyskakującym okienku ustaw port na 8000 i kliknij „Zmień i wyświetl podgląd”. Cloud Shell otworzy nową kartę przeglądarki z interfejsem internetowym ADK.

webpreview

👉 Wybierz agenta z menu:

W interfejsie ADK u góry zobaczysz menu. Na liście wybierz charity_advisor.

agent-select

Zobaczysz interfejs internetowy ADK z tymi elementami:

  • Panel czatu: po lewej stronie, do prowadzenia rozmowy.
  • Panel śledzenia: po prawej stronie, do obserwacji (użyjemy go w module 9)

Test 1. Przejdź proces przekazywania darowizny (przypadek normalny)

👉 W interfejsie czatu wpisz:

I want to donate to an education charity

Zobacz cały proces:

adk web shopping agent

Potok przetwarzania darowizn w ADK

Co się dzieje (widoczne w panelu śledzenia po prawej stronie):

1. Doradca przekazuje uprawnienia do ShoppingAgent:

  • ShoppingAgent wyszukuje organizacje charytatywne zajmujące się edukacją
  • Wyświetla 3 zweryfikowane opcje ze szczegółami.

2. Interakcja z ShoppingAgent (może wymagać kilku tur):

User: "Tell me more about Room to Read"
Shopping: [explains mission and impact]
User: "I'll donate $50 to Room to Read"

3. ShoppingAgent tworzy IntentMandate:

  • Tworzy i podpisuje intencję
  • Zwraca potwierdzenie z identyfikatorem intencji.

4. Przejście doradcy do etapu przetwarzania:

Super! Przetwarzanie darowizny w wysokości 50 USD na rzecz organizacji Room to Read…

5. Aktywacja DonationProcessingPipeline:

  • Wywołanie zwrotne sprzedawcy weryfikuje IntentMandate (✓ passed) ← NOWOŚĆ
  • Agent sprzedawcy tworzy CartMandate z podpisem
  • Wywołanie zwrotne danych uwierzytelniających weryfikuje CartMandate (✓ passed) ← NOWOŚĆ
  • Dostawca danych uwierzytelniających przygotowuje płatność

6. Procesy płatności:

  • Dostawca danych uwierzytelniających tworzy PaymentMandate
  • Symuluje przetwarzanie płatności
  • Identyfikator transakcji zwrotu

7. Doradca podsumowuje:

Super! Twoja darowizna została zrealizowana. 🎉

Szczegóły:

  • Kwota: 50,00 USD
  • Organizacja charytatywna: Room to Read (EIN: 77-0479905)
  • Identyfikator transakcji: txn_a3f7b2c8d9e1f4a2

Test 2. Sprawdź, czy wywołania zwrotne wykrywają błędy (opcjonalny test zaawansowany)

Chcesz zobaczyć, jak funkcje zwrotne działają w praktyce i wykrywają błędy? Musisz ręcznie uszkodzić stan (zaawansowane debugowanie), ale w środowisku produkcyjnym wywołania zwrotne wykryją:

  • Narzędzie Shopping Agent nie działa → Blokady wywołania zwrotnego sprzedawcy: „Error: Cannot create cart...”
  • Po 2 godzinach → wywołanie zwrotne sprzedawcy blokuje: „Error: Intent expired...”
  • Koszyk wygasa → Wywołania zwrotne danych logowania: „Błąd: koszyk wygasł (limit 15 min)...”

Te przypadki brzegowe są teraz wymuszane architektonicznie przez wywołania zwrotne weryfikacji.

Co właśnie zostało utworzone

Udało Ci się zintegrować 3 wyspecjalizowane modele w jeden spójny i wiarygodny system z walidacją architektury.

Co dalej

Ukończono już techniczną część tworzenia wiarygodnych agentów:

Masz już kompletny, godny zaufania system, który lokalnie egzekwuje łańcuch danych logowania. Teraz udostępnimy go prawdziwym użytkownikom w ramach wdrożenia produkcyjnego i włączymy ścieżkę odpowiedzialności, która umożliwia realizację modułu 9.

Wdróżmy wzmocnionego agenta w Google Cloud.

8. Wdrożenie

baner

Twój godny zaufania system darowizn jest teraz kompletny i obejmuje 3 wyspecjalizowane agenty działające lokalnie:

Działa ona jednak tylko na komputerze deweloperskim. Aby ten system był przydatny dla prawdziwych użytkowników i aby rejestrować ścieżki odpowiedzialności, które potwierdzają wiarygodność, musisz wdrożyć go w środowisku produkcyjnym.

W tym module dowiesz się, jak wdrożyć agenta w Google Cloud z włączoną od samego początku funkcją dostrzegalności. Flaga --trace_to_cloud, której użyjesz podczas wdrażania, umożliwia ścieżkę odpowiedzialności w module 9.

Omówienie opcji wdrażania

ADK obsługuje wiele miejsc docelowych wdrożenia. Każda z nich ma inne cechy pod względem złożoności, zarządzania sesjami, skalowania i kosztów:

Czynnik

Lokalne (adk web)

Silnik agenta

Cloud Run

Złożoność

Minimalny

Niski

Średni

Trwałość sesji

Tylko w pamięci (utracone po ponownym uruchomieniu)

Zarządzane przez Vertex AI (automatyczne)

Cloud SQL (PostgreSQL) lub w pamięci

Infrastruktura

Brak (tylko na urządzeniu deweloperskim)

Usługa w pełni zarządzana

Kontener + opcjonalna baza danych

Uruchomienie „na zimno”

Nie dotyczy

100–500 ms

100–2000 ms

Skalowanie

Pojedyncza instancja

Automatycznie

Automatyczne (do zera)

Model kosztów

Bezpłatne (obliczenia lokalne)

Na podstawie zasobów obliczeniowych

Na podstawie żądań + poziom bezpłatny

Obsługa interfejsu

Tak (wbudowany)

Nie (tylko interfejs API)

Tak (za pomocą flagi --with_ui)

Konfiguracja dostrzegalności

Lokalna przeglądarka śladów

Automatyczne z --trace_to_cloud

Wymaga flagi --trace_to_cloud

Najlepsze zastosowania

Programowanie i testowanie

Agenci produkcyjni

Agenci produkcyjni

Rekomendacja: w przypadku tego godnego zaufania systemu darowizn zalecamy używanie Agent Engine jako głównego wdrożenia produkcyjnego, ponieważ zapewnia ono:

  • W pełni zarządzana infrastruktura (bez konieczności zarządzania kontenerami)
  • Wbudowana trwałość sesji za pomocą VertexAiSessionService
  • Automatyczne skalowanie bez uruchamiania „na zimno”
  • Uproszczone wdrażanie (nie wymaga wiedzy o Dockerze)
  • Gotowa integracja z Cloud Trace

Dodatkowa opcja: Google Kubernetes Engine (GKE)

Dla zaawansowanych użytkowników, którzy potrzebują kontroli na poziomie Kubernetes, niestandardowej sieci lub orkiestracji wielu usług, dostępna jest implementacja GKE. Ta opcja zapewnia maksymalną elastyczność, ale wymaga większej wiedzy operacyjnej (zarządzanie klastrami, manifesty, konta usługi).

Wdrożenie GKE nie jest objęte tymi ćwiczeniami, ale jest w pełni udokumentowane w przewodniku po wdrażaniu ADK w GKE.

Wymagania wstępne

1. Konfigurowanie projektu Google Cloud

Musisz mieć projekt w Google Cloud z włączonymi płatnościami. Jeśli nie masz konta:

  1. Tworzenie projektu: Google Cloud Console
  2. Włącz płatności: Włącz płatności
  3. Zapisz identyfikator projektu (nie nazwę ani numer projektu).

2. Ponowne uwierzytelnianie (opcjonalnie)

Uwierzytelnij się w Google Cloud:

gcloud auth application-default login
gcloud config set project YOUR_PROJECT_ID

Zastąp YOUR_PROJECT_ID identyfikatorem Twojego projektu Google Cloud.

Potwierdź uwierzytelnianie:

gcloud config get-value project
# Should output: YOUR_PROJECT_ID

3. Zmienne środowiskowe

Aby automatycznie wypełnić plik .env, użyj tych poleceń:

# 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}"

Zobaczysz, że:

STAGING_BUCKET=gs://your-actual-project-id-staging

Ważne informacje:

  • Zastąp YOUR_PROJECT_ID identyfikatorem projektu (lub użyj powyższych poleceń).
  • W przypadku GOOGLE_CLOUD_LOCATION użyj obsługiwanego regionu.
  • Podczas uruchamiania skryptu wdrażania tymczasowy zasobnik zostanie utworzony automatycznie, jeśli nie istnieje.

4. Włącz wymagane interfejsy API

Proces wdrażania wymaga włączenia kilku interfejsów Google Cloud API. Aby je włączyć, uruchom to polecenie:

gcloud services enable \
    aiplatform.googleapis.com \
    storage.googleapis.com \
    cloudbuild.googleapis.com \
    cloudtrace.googleapis.com \
    compute.googleapis.com

To polecenie umożliwia:

  • AI Platform API – w przypadku modeli Agent Engine i Vertex AI
  • Cloud Storage API – w przypadku zasobnika tymczasowego
  • Cloud Build API – do tworzenia kontenerów (Cloud Run).
  • Cloud Trace API – do śledzenia widoczności i odpowiedzialności.
  • Compute Engine API – do zarządzania kontami usługi.

Krok 1. Zapoznaj się z infrastrukturą wdrożenia

Projekt zawiera ujednolicony skrypt wdrażania (deploy.sh), który obsługuje wszystkie tryby wdrażania.

👉 Sprawdź skrypt wdrożenia (opcjonalnie):

cat deploy.sh

Skrypt udostępnia 3 tryby wdrażania:

  • ./deploy.sh local – uruchamianie lokalne z pamięcią w pamięci
  • ./deploy.sh agent-engine – Wdróż w silniku agenta Vertex AI (zalecane)
  • ./deploy.sh cloud-run – wdrożenie w Cloud Run z opcjonalnym interfejsem

Jak to działa:

W przypadku wdrożenia silnika agenta skrypt wykonuje te czynności:

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

W przypadku wdrożenia Cloud Run wykonuje te czynności:

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

Flaga --trace_to_cloud ma kluczowe znaczenie w przypadku obu typów wdrożenia – umożliwia integrację Cloud Trace na potrzeby ścieżki odpowiedzialności, którą poznasz w module 9.

Krok 2. Przygotuj otokę Agent Engine

Agent Engine wymaga określonego punktu wejścia, który opakowuje agenta na potrzeby zarządzanego środowiska wykonawczego. Ten plik został utworzony dla Ciebie.

👉 Sprawdź

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
)

Dlaczego ten plik jest potrzebny:

  • Agent Engine wymaga, aby agent był opakowany w obiekt AdkApp.
  • Parametr enable_tracing=True automatycznie włącza integrację z Cloud Trace.
  • Ten moduł jest przywoływany przez interfejs wiersza poleceń ADK podczas wdrażania.
  • Konfiguruje VertexAiSessionService pod kątem automatycznego utrwalania sesji.

Agent Engine to zalecane wdrożenie produkcyjne dla Twojego godnego zaufania systemu darowizn, ponieważ zapewnia w pełni zarządzaną infrastrukturę z wbudowaną trwałością sesji.

Uruchamianie wdrożenia

W katalogu głównym projektu:

chmod +x deploy.sh
./deploy.sh agent-engine

Etapy wdrażania

Obserwuj, jak skrypt wykonuje te fazy:

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

Ten proces trwa 5–10 minut, ponieważ pakuje agenta i wdraża go w infrastrukturze Vertex AI.

Zapisywanie identyfikatora silnika agenta

Po pomyślnym wdrożeniu:

✅ 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

Natychmiast zaktualizuj plik .env:

echo "AGENT_ENGINE_ID=7917477678498709504" >> .env

Co zostało wdrożone

Wdrożenie Agent Engine obejmuje teraz:

✅ Wszystkie 3 agenty (Shopping, Merchant, Credentials) działające w zarządzanym środowisku wykonawczym
✅ Pełny łańcuch danych logowania (Intent → Cart → Payment mandates)
✅ Mechanizm zgody użytkownika z procesem potwierdzania
✅ Automatyczne utrwalanie sesji za pomocą VertexAiSessionService
✅ Automatyczne skalowanie infrastruktury zarządzanej przez Google
✅ Integracja z Cloud Trace zapewniająca pełną widoczność

Krok 4. Przetestuj wdrożonego agenta

Aktualizowanie środowiska

Sprawdź, czy .env zawiera identyfikator Agent Engine:

AGENT_ENGINE_ID=7917477678498709504  # From deployment output
GOOGLE_CLOUD_PROJECT=your-project-id
GOOGLE_CLOUD_LOCATION=us-central1
STAGING_BUCKET=gs://your-project-id-staging

Uruchamianie scenariusza testowania

Projekt zawiera skrypt testowy przeznaczony specjalnie do wdrożeń Agent Engine.

👉 Przeprowadź test:

python scripts/test_deployed_agent.py

Oczekiwane dane wyjściowe

Testing Agent Engine deployment...
Project: your-project-id
Location: us-central1
Agent Engine ID: 7917477678498709504
Endpoint: https://us-central1-aiplatform.googleapis.com/v1/...

Creating session...
✓ Session created: 4857885913439920384

Sending donation query...
✓ Response received:
  Event 1: I'll help you donate $50 to a children's education charity...
  Event 2: Here are some highly-rated children's education charities...
  Event 3: Which charity would you like to support?...

✅ Test completed successfully!

Session ID: 4857885913439920384

This donation generated a trace in Cloud Trace.
View it in Module 9: Observability

To view traces:
https://console.cloud.google.com/traces/list?project=your-project-id

Lista kontrolna weryfikacji

Po przeprowadzeniu testów sprawdź:

✅ Agent odpowiada na zapytania.
✅ Wszyscy 3 agenci działają po kolei (Zakupy → Sprzedawca → Dane logowania).
✅ Mechanizm zgody jest aktywowany (wymagana jest potwierdzenie).
✅ Sesja jest utrzymywana w przypadku kolejnych żądań.
✅ Brak błędów uwierzytelniania.
✅ Brak przekroczenia limitu czasu połączenia.

Jeśli napotkasz błędy:

  • Sprawdź, czy zmienne środowiskowe są prawidłowo ustawione
  • Sprawdź, czy interfejsy API są włączone: gcloud services list --enabled
  • Sprawdzanie logów silnika agenta w konsoli Vertex AI
  • Sprawdź, czy plik agent_engine_app.py znajduje się w folderze charity_advisor

Krok 5. Wdróż w Cloud Run (opcjonalny)

Agent Engine jest zalecany w przypadku uproszczonego wdrożenia produkcyjnego, ale Cloud Run zapewnia większą kontrolę i obsługuje interfejs ADK. Ta sekcja jest opcjonalna.

Kiedy używać Cloud Run

Wybierz Cloud Run, jeśli potrzebujesz:

  • Interfejs internetowy ADK do interakcji z użytkownikiem
  • Pełna kontrola nad środowiskiem kontenera
  • Niestandardowe konfiguracje bazy danych
  • Integracja z istniejącymi usługami Cloud Run

Uruchamianie wdrożenia

chmod +x deploy.sh
./deploy.sh cloud-run

Co się zmieniło:

Skrypt automatycznie:

  • Tworzenie kontenera Dockera z kodem agenta
  • Utwórz bazę danych Cloud SQL PostgreSQL (w razie potrzeby).
  • Konfigurowanie połączenia z bazą danych
  • Wdrażanie z włączonym interfejsem internetowym ADK

Wdrożenie zajmuje 10–15 minut ze względu na obsługę administracyjną Cloud SQL.

Zarządzanie sesjami:

  • Używa DatabaseSessionService zamiast VertexAiSessionService
  • Wymaga danych logowania do bazy danych w .env (lub wygenerowanych automatycznie)
  • Stan jest przechowywany w bazie danych PostgreSQL

Obsługa interfejsu:

  • Interfejs internetowy dostępny pod adresem: https://charity-advisor-xyz.a.run.app

Testowanie wdrożenia Cloud Run

Jeśli wdrożysz aplikację w Cloud Run za pomocą --with_ui, możesz ją przetestować bezpośrednio w przeglądarce:

  1. Otwórz adres URL usługi (podany w danych wyjściowych wdrożenia).
  2. Zobaczysz interfejs internetowy ADK. Wybierz agenta z menu.
  3. Rozpocznij testową darowiznę:
   I want to donate $50 to a children's education charity
  1. Obserwuj przepływ wykonania:
    • ShoppingAgent znajduje organizacje charytatywne i zapisuje Twoje intencje
    • MerchantAgent tworzy polecenie zapłaty za koszyk
    • Dostawca danych logowania tworzy upoważnienie do płatności i prosi o potwierdzenie
    • Po potwierdzeniu płatność zostanie przetworzona
  2. Sprawdź, czy odpowiedź zawiera:
    • Rekomendacje dotyczące organizacji charytatywnych
    • Prośba o potwierdzenie
    • Komunikat o sukcesie po zatwierdzeniu

Rozwiązywanie problemów

Typowe problemy

Problem: ERROR: GOOGLE_CLOUD_PROJECT is not set

Rozwiązanie: sprawdź, czy plik .env zawiera prawidłowy identyfikator projektu:

GOOGLE_CLOUD_PROJECT=your-actual-project-id

Problem: zasobnik tymczasowy nie został utworzony automatycznie

Rozwiązanie: skrypt powinien automatycznie utworzyć zasobnik. Jeśli nie, utwórz go ręcznie:

gsutil mb -p $GOOGLE_CLOUD_PROJECT -l $GOOGLE_CLOUD_LOCATION $STAGING_BUCKET

Podsumowanie

Udało Ci się:

✅ Zapoznaj się z infrastrukturą wdrożenia udostępnianą przez deploy.sh
. ✅ Sprawdź konfigurację otoki Agent Engine.
✅ Wdróż w Agent Engine system zaufanych darowizn (zalecane).
✅ Włącz integrację Cloud Trace z --trace_to_cloud
. ✅ Sprawdź, czy agent jest dostępny i działa prawidłowo.
✅ W module 9 utwórz podstawy ścieżek odpowiedzialności.

W następnym module dowiesz się, co dokładnie odblokowuje ten tag: pełną widoczność każdej darowizny, każdego momentu uzyskania zgody i każdego kroku w łańcuchu danych logowania.

9. Dostrzegalność

baner

ślad grafu

W części 1 przedstawiliśmy podstawowy problem: gdy agent AI zarządza pieniędzmi, jak udowodnić, co się stało?

Użytkownik może zgłosić:

  • „Nigdy nie wybrałem tej organizacji charytatywnej!”
  • „Nie autoryzuję tej płatności!”
  • „System obciążył mnie bez mojej zgody!”

W tradycyjnym systemie AI typu „czarna skrzynka” nie byłoby możliwości udowodnienia, że jest inaczej. Jednak Twój godny zaufania system darowizn działa inaczej. W module 8 wdrożyliśmy flagę --trace_to_cloud, co oznacza, że każda darowizna tworzy teraz w Cloud Trace pełną, odporną na manipulacje ścieżkę audytu.

Z tego modułu dowiesz się, jak odczytywać te ślady i wykorzystywać je jako dowody. Poznasz następujące funkcje:

  • Znajdowanie logów czasu produkcji w eksploratorze Cloud Trace
  • Analizowanie widoku kaskadowego w celu zrozumienia przepływu wykonania
  • Znajdź ciąg danych uwierzytelniających (Intent → Cart → Payment mandates)
  • Znajdowanie momentów uzyskania zgody dzięki dowodom w postaci sygnatur czasowych
  • Używanie śladów do rozwiązywania sporów
  • Eksportowanie śladów na potrzeby zgodności i kontroli

To odróżnia wiarygodne systemy od tych, które są wprawdzie skuteczne, ale nieprzejrzyste: możliwość udowodnienia, co się stało, z dokładnością kryminalistyczną.

Informacje o śladach i zakresach

Zanim zaczniesz wyświetlać ślady w Cloud Trace, musisz wiedzieć, na co patrzysz.

Co to jest ślad?

Log czasu to pełna oś czasu obsługi pojedynczego żądania przez agenta. Obejmuje wszystko od momentu wysłania zapytania przez użytkownika do momentu dostarczenia ostatecznej odpowiedzi.

Każdy ślad zawiera:

  • Łączny czas trwania żądania
  • wszystkie operacje, które zostały wykonane;
  • Jak operacje są ze sobą powiązane (relacje nadrzędne i podrzędne)
  • Kiedy rozpoczęła się i zakończyła każda operacja
  • Stan powodzenia lub niepowodzenia

W przypadku przedstawiciela organizacji charytatywnej: 1 ślad = 1 pełny proces przekazywania darowizny od „Chcę przekazać darowiznę” do „Płatność zrealizowana”.

Co to jest zakres?

Zakres to pojedyncza jednostka pracy w ramach śladu. Spany to elementy składowe śladu.

Typowe zakresy w systemie darowizn:

Rodzaj zakresu

Co to oznacza

Przykład

agent_run

Uruchamianie agenta

ShoppingAgent.run, MerchantAgent.run

call_llm

Prośba do modelu językowego

gemini-2.5-flash prośba o wybranie organizacji charytatywnej

execute_tool

Wykonanie funkcji narzędzia

find_charities, create_payment_mandate

state_read

Odczytywanie z pamięci sesji

Pobieranie intent_mandate ze stanu

state_write

Zapisywanie w pamięci sesji

Zapisywanie cart_mandate w stanie

Każdy zakres zawiera:

  • Nazwa: co reprezentuje ta operacja.
  • Czas trwania (od godziny rozpoczęcia do godziny zakończenia)
  • Atrybuty: metadane, takie jak dane wejściowe narzędzia, odpowiedzi modelu i liczba tokenów.
  • Stan: powodzenie (OK) lub błąd (ERROR)
  • Relacje nadrzędne i podrzędne: które operacje wywołały które

Jak zakresy tworzą ślad

Zakresy są zagnieżdżone w sobie, aby pokazywać związek przyczynowo-skutkowy:

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

Ta hierarchia pokazuje dokładnie, co się wydarzyło i w jakiej kolejności. Widać, że upoważnienie do płatności zostało utworzone po upoważnieniu do koszyka, które zostało utworzone po wybraniu przez użytkownika organizacji charytatywnej.

Krok 1. Otwórz Eksplorator Cloud Trace

Teraz wyświetlmy rzeczywiste ślady z wdrożonego agenta.

  1. Otwórz konsolę Google Cloud: console.cloud.google.com
  2. Wybierz projekt z menu u góry (powinien być wstępnie wybrany, jeśli w nim pracujesz).
  3. Otwórz Eksplorator Cloud Trace:

Co widzisz

Eksplorator śladów wyświetla listę wszystkich śladów z Twojego projektu:

Kolumna

Co zawiera

Żądanie

Metoda HTTP i punkt końcowy (w przypadku żądań API)

Godzina rozpoczęcia

Kiedy rozpoczęło się żądanie

Opóźnienie

Łączny czas trwania żądania

Spans

Liczba operacji w śladzie

Każdy wiersz reprezentuje jedno pełne żądanie wysłane do wdrożonego agenta.

Generowanie śladów testowych (w razie potrzeby)

Jeśli nie widzisz jeszcze żadnych śladów, lista może być pusta, ponieważ:

  • Do wdrożonego agenta nie wysłano jeszcze żadnych próśb
  • Ślady pojawiają się w ciągu 1–2 minut od wysłania żądania.

Generowanie śladu testowego:

Jeśli wdrożysz aplikację w Cloud Run za pomocą interfejsu, otwórz adres URL usługi i dokonaj darowizny w przeglądarce.

Jeśli wdrożono w Agent Engine, uruchom skrypt testowy z modułu 8:

python scripts/test_deployed_agent.py

Poczekaj 1–2 minuty, a potem odśwież stronę Eksploratora Cloud Trace. Powinny być teraz widoczne ślady.

Filtrowanie logów czasu

Aby znaleźć konkretne ślady, użyj opcji filtrowania u góry:

  • Zakres czasu: w razie potrzeby zmień „Ostatnia godzina” na „Ostatnie 24 godziny”.
  • Minimalny czas oczekiwania / maksymalny czas oczekiwania: filtruj powolne żądania.
  • Filtr żądania: wyszukuj według konkretnych operacji (np. „DonationPipeline”)

W tym module skup się na śladach o dłuższym czasie trwania (powyżej 5 sekund), ponieważ reprezentują one pełne ścieżki darowizn z udziałem wszystkich 3 agentów.

Krok 2. Sprawdź pełny proces przekazywania darowizny

Kliknij dowolny ślad na liście, aby otworzyć widok kaskadowy. W tym miejscu będziesz spędzać najwięcej czasu na analizowaniu zachowań agentów.

Omówienie widoku kaskadowego

Widok kaskadowy to wykres Gantta przedstawiający pełną oś czasu wykonania:

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
              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]

Interpretowanie wykresu

Każdy słupek reprezentuje zakres:

  • Pozycja pozioma: kiedy się zaczął
  • Długość: czas trwania
  • Wcięcie: pokazuje relacje nadrzędny-podrzędny.
  • Kolor: zwykle niebieski w przypadku normalnego działania, czerwony w przypadku błędów.

Najważniejsze obserwacje z tego przykładowego śledzenia:

✅ Łączny czas trwania: 8,2 s
✅ Kolejne wykonanie: ShoppingAgent zakończył działanie przed rozpoczęciem działania MerchantAgent
✅ MerchantAgent zakończył działanie

przed

Uruchomiono CredentialsProvider
Najdłużej trwała operacja związana z uzyskaniem zgody: 3,0 s w przypadku create_payment_mandate (ponieważ oczekiwała na potwierdzenie użytkownika)
Wywołania LLM są widoczne: każdy agent wysłał 1 żądanie do Gemini
Wywołania narzędzi są rejestrowane: wszystkie 6 narzędzi zostało wykonanych

Ta wizualizacja od razu pokazuje gdzie jest spędzany czasw jakiej kolejności są wykonywane operacje.

Kliknij zakres, aby wyświetlić szczegóły

Kliknij zakres invocation (główny zakres u góry). W panelu po prawej stronie zobaczysz szczegółowe atrybuty:

{
  "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"
}

Te atrybuty dostarczają kontekstu całego żądania.

Krok 3. Znajdź łańcuch danych logowania

Zaufany system używa łańcucha danych logowania, aby potwierdzić autoryzację na każdym etapie:

IntentMandate (User chose charity)
    ↓
CartMandate (Merchant created cart, signed IntentMandate)
    ↓
PaymentMandate (Payment provider created payment, signed CartMandate)

Znajdźmy w śladzie każdy mandat.

Znajdowanie dokumentu IntentMandate

Kliknij zakres execute_tool: save_user_choice (w sekcji ShoppingAgent).

W panelu atrybutów zobaczysz:

{
  "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..."
  }
}

To dowodzi, że:

  • ✅ Użytkownik wybrał organizację „Save the Children”
  • ✅ Kwota wynosiła 50 PLN
  • ✅ Wybór został zarejestrowany o 15:30:12 czasu UTC
  • ✅ Podpis został wygenerowany (w środowisku produkcyjnym byłby to podpis kryptograficzny)

Obiekt IntentMandate jest teraz w stanie sesji i jest dostępny dla kolejnych agentów.

Znajdowanie dokumentu CartMandate

Kliknij zakres execute_tool: create_cart_mandate (w sekcji MerchantAgent).

W panelu atrybutów:

{
  "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"
  }
}

To dowodzi, że:

  • ✅ MerchantAgent otrzymał IntentMandate (dane wejściowe to potwierdzają).
  • ✅ Utworzono koszyk o identyfikatorze „cart_7893”
  • ✅ Podpis koszyka odwołuje się do podpisu IntentMandate (połączonego łańcuchem).
  • ✅ Utworzono o 15:30:14 UTC (2,4 sekundy po zamiarze)

Obecnie CartMandate odwołuje się do IntentMandate, tworząc łańcuch.

Znajdowanie PaymentMandate

Kliknij zakres execute_tool: create_payment_mandate (w sekcji CredentialsProvider).

W panelu atrybutów:

{
  "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"
  }
}

To potwierdza cały łańcuch:

  • ✅ CredentialsProvider otrzymał CartMandate (dane wejściowe to pokazują)
  • ✅ Płatność odnosi się do podpisu CartMandate (link do łańcucha).
  • ✅ Wymagane było potwierdzenie (confirmation_required: true)
  • ✅ Użytkownik potwierdził o 15:30:17 UTC
  • System czekał 29,2 sekundy na decyzję użytkownika
  • ✅ Płatność została utworzona po potwierdzeniu (sygnatura czasowa: 15:30:47)

Wizualizacja łańcucha

Ślad potwierdza, że łańcuch danych logowania został wykonany prawidłowo:

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

Każdy mandat odnosi się do podpisu poprzedniego. Jest to zabezpieczenie przed manipulacją – możesz zweryfikować łańcuch, sprawdzając, czy podpisy są zgodne.

Krok 4. Analizowanie wydajności i wąskich gardeł

Cloud Trace nie tylko pokazuje, co się stało, ale też gdzie upływa czas, dzięki czemu możesz przeprowadzić optymalizację.

Określanie ścieżki krytycznej

W widoku kaskadowym poszukaj najdłuższych przedziałów w pionowym stosie. Są to wąskie gardła wydajności.

Z naszego przykładowego śladu:

Total: 8.2 seconds

Breakdown:
  - ShoppingAgent:         2.1s (26%)
  - MerchantAgent:         1.8s (22%)
  - CredentialsProvider:   4.0s (49%)   Bottleneck
  - Other overhead:        0.3s (3%)

Kluczowe informacje: CredentialsProvider odpowiada za 49% całkowitego czasu. Dlaczego?

Przejdź do zakresu CredentialsProvider:

CredentialsProvider: 4.0s
  - call_llm:              0.8s (20%)
  - create_payment_mandate: 3.0s (75%)   User consent wait
  - Other:                 0.2s (5%)

3-sekundowe opóźnienie jest oczekiwane i pożądane – użytkownik zastanawia się przed potwierdzeniem. Nie jest to problem z wydajnością, ale dowód na przemyślaną zgodę.

Śledzenie kosztów LLM

Kliknij dowolny zakres call_llm, aby zobaczyć wykorzystanie tokenów:

{
  "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
}

Wykorzystaj go, aby:

  • Śledzenie kosztu żądania (tokeny × cena modelu)
  • Identyfikowanie niepotrzebnie długich promptów
  • Porównywanie skuteczności modeli (Flash i Pro)
  • Optymalizacja pod kątem opóźnienia lub jakości

Przykładowe obliczenie:

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

Szczegółowy wgląd w dane pomaga podejmować decyzje dotyczące wyboru modelu na podstawie danych.

Porównywanie śladów

Filtrowanie wielu śladów i porównywanie czasu trwania:

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)

Statystyka: większość różnic wynika z czasu podejmowania decyzji przez użytkowników, a nie z wydajności systemu. Czas wykonywania podstawowego agenta (bez zgody użytkownika) jest stały i wynosi około 5 sekund.

Oznacza to, że system działa niezawodnie.

W przypadku systemów produkcyjnych skonfiguruj alerty, aby wykrywać problemy, zanim zaczną zgłaszać je użytkownicy.

Alerty o wysokiej częstotliwości występowania błędów

Utwórz alert, gdy ponad 5% śladów zawiera błędy:

  1. Otwórz Cloud Monitoring.
  2. Kliknij „Alerty” → „Utwórz zasadę”.
  3. Skonfiguruj:
    Resource: Cloud Trace Span
    Metric: Span error count
    Condition: Rate > 5% over 5 minutes
    Notification: Email your-team@example.com
    

Alert o dużym opóźnieniu

Utwórz alert, gdy 95 centyl czasu oczekiwania przekroczy 15 sekund:

Resource: Cloud Trace
Metric: Span duration (95th percentile)
Condition: > 15000ms for 5 minutes
Notification: PagerDuty

Pozwala to wykryć pogorszenie wydajności, zanim wpłynie ono na wygodę użytkowników.

Utwórz alert, jeśli jakakolwiek płatność zostanie przetworzona bez potwierdzenia:

Resource: Cloud Trace Span
Filter: tool.name="create_payment_mandate" AND tool.confirmation_required!=true
Condition: Any match
Notification: Critical alert to security team

Jest to detektor naruszeń zasad bezpieczeństwa – jeśli zostanie uruchomiony, oznacza to, że z mechanizmem uzyskiwania zgody jest coś nie tak.

Czego się nauczysz

Dzięki Cloud Trace wiesz już, jak:

✅ Poruszaj się po Eksploratorze Cloud Trace, aby znaleźć ślady produkcji.
✅ Odczytuj widoki kaskadowe, aby zobaczyć pełny przepływ wykonania.
✅ Śledź łańcuch danych logowania w kolejności IntentMandate → CartMandate → PaymentMandate. ✅ Używaj śladów jako dowodów w rozwiązywaniu sporów.
✅ Analizuj wydajność, aby identyfikować wąskie gardła.
✅ Śledź koszty LLM na poziomie szczegółowym.

Różnica, jaką to robi

Porównaj 2 systemy obsługujące tę samą skargę „Nigdy nie wyraziłem(-am) na to zgody”:

System bez dostrzegalności

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.

System z 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.

Na tym polega siła ścieżek odpowiedzialności. Przechodzisz od „zaufaj nam” do „pokażemy Ci dokładnie, co się stało”.

Co dalej

Ukończono już techniczną część tworzenia wiarygodnych agentów:

Moduły 1–6: zaprojektowano godną zaufania architekturę (role, dane logowania, zgoda użytkowników)
Moduł 7: zorganizowano złożone przepływy pracy (SequentialAgent)
Moduł 8: wdrożono z włączoną obserwacją
Moduł 9: nauczono się odczytywać i wykorzystywać ścieżki odpowiedzialności

Zbudowana przez Ciebie architektura – rozdzielenie ról, łańcuchy danych logowania, mechanizmy uzyskiwania zgody, pełna widoczność – jest bezpośrednio przenoszona do systemów produkcyjnych obsługujących prawdziwe pieniądze, rzeczywiste dane i rzeczywiste konsekwencje.

10. Twoja dalsza podróż

Co utworzysz

Ten warsztat zaczęliśmy od pytania: „Jak stworzyć agentów AI, którym można powierzyć pieniądze?”

Teraz znasz odpowiedź.

Where you started (Module 3):

simple_agent = Agent(
    model="gemini-2.5-flash",
    instruction="Find charities and donate",
    tools=[google_search]
)

Gdzie jesteś teraz (moduł 10):

  • ✅ 3 wyspecjalizowanych agentów z podziałem ról
  • ✅ 3 weryfikowalne dane logowania (zamiar → koszyk → upoważnienia do płatności)
  • ✅ Sprawdzanie ważności łańcucha danych logowania na każdym etapie
  • ✅ Mechanizm uzyskiwania wyraźnej zgody z dowodem w postaci sygnatury czasowej
  • ✅ Wdrożenie produkcyjne w Agent Engine z możliwością obserwacji
  • ✅ Pełna ścieżka odpowiedzialności w Cloud Trace
  • ✅ Dowody kryminalistyczne do rozwiązywania sporów

Warsztat a produkcja: różnica

Twój system ma prawidłową architekturę i wzorce, ale korzysta z uproszczeń edukacyjnych, które należy ulepszyć, aby można było używać prawdziwych pieniędzy i prawdziwych użytkowników.

Oto co zostało uproszczone i czego wymaga produkcja:

Komponent

Wdrożenie warsztatów

Wymagania dotyczące produkcji

Podpisy

Identyfikatory SHA-256 (SIG_abc123) na potrzeby demonstracji

Prawdziwe podpisy kryptograficzne z użyciem infrastruktury kluczy publicznych lub tokenów JWT z kluczami prywatnymi

Przetwarzanie płatności

Symulowane zwroty (flaga simulation: True)

Integracja z interfejsami API prawdziwych systemów płatności (Stripe, PayPal, Square)

Uwierzytelnianie użytkowników

Zaufanie domyślne (nie wymaga logowania)

OAuth 2.0, WebAuthn lub zarządzanie sesjami

Zarządzanie obiektami tajnymi

Zmienne środowiskowe w pliku .env

Google Secret Manager lub Cloud KMS z szyfrowaniem

Baza danych organizacji charytatywnych

Przykładowy plik JSON z 9 organizacjami charytatywnymi

Integracja interfejsu Live API (wyszukiwarka organizacji zwolnionych z podatku IRS, interfejs Charity Navigator API)

Obsługa błędów

Podstawowa konstrukcja try-catch z komunikatami o błędach

Logika ponawiania z wykładniczym czasem do ponowienia, wyłącznikami obwodu i kolejkami niedostarczonych wiadomości

Testowanie

Ręczna weryfikacja za pomocą skryptów

Kompleksowy zestaw testów jednostkowych, integracyjnych i E2E z CI/CD

Trwałość sesji

W pamięci (lokalnie) lub automatycznie (Agent Engine)

Baza danych produkcyjnych z kopiami zapasowymi i odtwarzaniem awaryjnym

Ograniczanie liczby żądań

Brak (środowisko edukacyjne)

Limity liczby żądań na użytkownika, ograniczanie na podstawie adresu IP, wykrywanie nadużyć

Kluczowe wzorce architektoniczne, które opanowałeś(-aś)

Wzorce, których nauczysz się podczas tych warsztatów, są wzorcami produkcyjnymi. Nie wątp w nie.

Rozdział ról (zasada AP2 nr 1)

Każdy agent ma JEDNO konkretne zadanie i widzi TYLKO to, czego potrzebuje. Jeśli jeden agent zostanie przejęty, atakujący nie będzie mieć dostępu do danych innych agentów. Ogranicza to zasięg wybuchu.

Systemy produkcyjne, które z niego korzystają: przetwarzanie płatności, przepływy pracy związane z dokumentami, łańcuchy zatwierdzania, formularze wieloetapowe z bramkami weryfikacyjnymi.

Weryfikowalne dane logowania (zasada 2 AP2)

Każde poświadczenie ma czas wygaśnięcia, odwołuje się do poprzedniego poświadczenia i wymaga weryfikacji przed przejściem do następnego kroku. Tworzy to odporny na manipulacje łańcuch kontrolny.

Wartość produkcyjna: pełny dowód na to, co się wydarzyło, kiedy i w jakiej kolejności. Niezbędne do rozwiązywania sporów i zachowania zgodności z przepisami.

Dowód w postaci sygnatury czasowej, że użytkownik zatwierdził działanie. Nie można zakwestionować.

Wartość produkcji: wymóg prawny w przypadku transakcji finansowych. Ochrona zarówno użytkownika, jak i firmy.

Sekwencyjna administracja (wzorzec ADK)

Wymusza prawidłową kolejność wykonywania. Zapobiega pomijaniu kroków. Gwarantuje, że każdy agent zobaczy dane wyjściowe poprzedniego agenta.

Wartość produkcyjna: idealna w przypadku systemów z udziałem człowieka, w których użytkownicy oczekują natychmiastowych wyników. To odpowiedni wzorzec w przypadku procesów przekazywania darowizn, płatności i łańcuchów zatwierdzania.

Pełna widoczność (OpenTelemetry + Cloud Trace)

Każda decyzja, wywołanie narzędzia, moment uzyskania zgody i przekazanie danych logowania są rejestrowane automatycznie.

Wartość produkcyjna: dowody w sprawach spornych. dane optymalizacji skuteczności, Rejestry kontrolne zgodności. Precyzyjnie debuguj problemy w środowisku produkcyjnym.

Materiały do dalszej nauki

Dokumentacja pakietu ADK:

AP2 i powiązane standardy:

Usługi Google Cloud:

Czyszczenie zasobów

Aby uniknąć dalszych opłat, usuń wdrożenie:

Agent Engine: postępuj zgodnie z instrukcjami w dokumentacji Agent Engine.

Cloud Run (jeśli jest wdrożona):

gcloud run services delete charity-advisor \
    --region=$GOOGLE_CLOUD_LOCATION

Zasobniki na dane:

gsutil -m rm -r gs://$GOOGLE_CLOUD_PROJECT-staging
gsutil -m rm -r gs://$GOOGLE_CLOUD_PROJECT-artifacts

Twoja podróż trwa dalej

Zaczęliśmy od prostego pytania i stworzyliśmy pełną odpowiedź. Opanowałeś(-aś) podstawowe wzorce dotyczące wiarygodnych agentów AI. Te wzorce można przenieść na dowolną domenę, w której agenci AI obsługują operacje wymagające zachowania poufności – transakcje finansowe, decyzje dotyczące opieki zdrowotnej, dokumenty prawne czy operacje w łańcuchu dostaw.

Zasady zostaną przeniesione. Model zaufania działa.

Teraz możesz stworzyć coś godnego zaufania. ❤️

baner