Como criar agentes de caridade confiáveis com o ADK e a AP2 do Google

1. Construir confiança para despertar a generosidade

banner

O momento de inspiração

O smartphone vibra. Você lê uma notícia sobre um programa de alfabetização bem-sucedido que ajuda crianças de comunidades carentes a aprender a ler. Você sente uma forte vontade de contribuir. Abra o navegador e pesquise "doações para programas de alfabetização infantil".

Pesquisa Google

Centenas de resultados aparecem.

Você clica no primeiro link. O site parece profissional. Role a tela para baixo até as informações financeiras. "Custos administrativos: 28%". Você pausa. Apenas 72 centavos de cada dólar doado vão financiar o programa. Isso é bom? Você não tem certeza.

Tente outra organização. Você nunca ouviu falar deles. Eles são legítimos? Uma pesquisa rápida leva você a um buraco sem fim. Você encontra uma conversa no Reddit de dois anos atrás em que um usuário afirma: "É um golpe, minha doação nunca chegou a lugar nenhum". Outro defende com paixão: "Eles estão no local fazendo trabalho de verdade!" A ambiguidade é paralisante.

Trinta minutos depois, você está em um labirinto de avaliações conflitantes, classificações de eficiência e registros do IRS, e ainda não fez a doação. A faísca inicial de generosidade foi substituída pelo atrito da pesquisa. A guia fica aberta por alguns dias, um pequeno lembrete de uma boa intenção, até que você a feche.

Isso não é uma falha pessoal, mas sim do sistema

Essa experiência é universal. A vontade de ajudar é grande, mas o processo é cheio de obstáculos que causam hesitação e dúvida:

  • Dificuldade na pesquisa:cada instituição de caridade exige uma investigação própria.
  • Verificação de confiança:é difícil distinguir organizações altamente eficazes de ineficientes ou até mesmo golpes.
  • Paralisia de análise:um número excessivo de opções leva à fadiga de decisão.
  • Perda de motivação:a motivação emocional para doar diminui à medida que o fardo logístico aumenta.

Essa fricção tem um custo impressionante no mundo real. As doações individuais nos Estados Unidos são imensas. Em 2023, os doadores individuais contribuíram com aproximadamente US$374 bilhões, de acordo com o relatório Giving USA 2024. No entanto, uma pesquisa demonstra que as barreiras para doação, incluindo custos de pesquisa, atrito psicológico e restrições de tempo, reduzem significativamente o valor que chega às causas beneficentes. Estudos envolvendo milhões de doadores descobriram que até mesmo pequenas dificuldades no processo de doação on-line impedem que as pessoas cumpram suas intenções de caridade.

Isso representa bilhões de dólares em doações que nunca chegam às causas que precisam delas.

A visão

Imagine uma experiência diferente. Em vez de uma sessão de pesquisa de 30 minutos, basta dizer:

"Quero doar R $50 para um programa de alfabetização infantil. Encontre uma instituição de caridade eficiente, verificada e com boas avaliações."

Em segundos, você recebe uma resposta que aumenta a confiança:

card de resultado de caridade

Essa é a promessa de um agente de doação de IA. Mas, para realizar essa visão, precisamos resolver um desafio fundamental: quando um agente autônomo de IA lida com dinheiro, a confiança não é opcional, ela é a base de tudo.

  • Como podemos provar o que um usuário autorizou?
  • Quem é responsável se um erro for cometido?
  • Como podemos dar aos doadores, instituições de caridade e redes de pagamento a confiança para participar?

Sua missão hoje

Neste workshop, você vai criar esse agente confiável combinando duas tecnologias poderosas:

Google Agent Development Kit (ADK)

Protocolo de pagamentos do agente (AP2)

Cargo

A fábrica para criar agentes de IA de nível de produção

O blueprint arquitetônico para confiança em transações de IA

O que ele oferece

• Framework para orquestração multiagente
• Recursos de segurança integrados, como confirmação de ferramentas
• Observabilidade e rastreamento prontos para produção
• Interface simples em Python para comportamentos complexos de agentes

• Limites de segurança baseados em função
• Credenciais digitais verificáveis (mandatos)
• Prova criptográfica de consentimento
• Trilhas de auditoria completas para responsabilização

Saiba mais

Documentação do ADK

Protocolo AP2

O que você vai criar

arquitetura

Ao final deste workshop, você terá criado:

Um sistema multiagente com funções especializadas:

  • Um agente de compras que encontra instituições de caridade verificadas
  • Um agente do comerciante que cria ofertas de doação vinculadas
  • Um provedor de credenciais que processa pagamentos com segurança
  • Um orquestrador que coordena todo o fluxo

Três tipos de credenciais verificáveis:

  • IntentMandate: "Encontre uma instituição de caridade educacional"
  • CartMandate: "$50 to Room to Read, signed by merchant"
  • PaymentMandate: "Processar via pagamento simulado"

Segurança em todas as camadas:

  • Limites de confiança com base em papéis
  • Consentimento explícito do usuário

Uma trilha de auditoria completa:

  • Cada decisão rastreável
  • Todos os consentimentos registrados
  • Todas as transferências visíveis

🔒 Importante: este é um ambiente de aprendizado seguro

Tudo pronto para criar confiança?

No próximo módulo, vamos configurar seu ambiente de desenvolvimento e criar seu primeiro agente de IA. Você vai descobrir rapidamente por que os agentes simples não são confiáveis e, depois, passar o restante do workshop aprendendo a corrigir isso.

Vamos começar entendendo o problema em primeira mão.

2. Preparação do seu espaço de trabalho

A Fundação para Agentes Confiáveis

Antes de criar nosso agente de doações de IA, precisamos preparar um ambiente de desenvolvimento limpo, consistente e configurado corretamente. Este módulo é uma etapa focada para garantir que todas as ferramentas e serviços necessários estejam disponíveis.

Ao concluir essa configuração, você pode se concentrar totalmente no trabalho interessante de criar a lógica do agente nos próximos módulos, sem se preocupar com problemas de configuração.

Acessar o Cloud Shell

Primeiro, vamos abrir o Cloud Shell, que é um terminal baseado em navegador com o SDK do Google Cloud e outras ferramentas essenciais pré-instaladas.

Precisa de créditos do Google Cloud?

Clique em Ativar o Cloud Shell na parte de cima do console do Google Cloud. É o ícone de terminal na barra de navegação no canto superior direito.

Cloud Shell

Encontre o ID do projeto do Google Cloud:

  • Abra o console do Google Cloud: https://console.cloud.google.com
  • Selecione o projeto que você quer usar neste workshop no menu suspenso na parte de cima da página.
  • O ID do projeto é exibido no card "Informações do projeto" no painel project id

Quando o Cloud Shell abrir, verifique se você está autenticado:

# Check that you are logged in
gcloud auth list

Sua conta vai aparecer como (ACTIVE).

Configurar seu projeto

Agora vamos configurar seu projeto do Google Cloud e ativar as APIs necessárias.

Definir o ID do projeto

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

Ativar APIs obrigatórias

Seus agentes precisam de acesso a vários serviços do Google Cloud:

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

Isso leva de 1 a 2 minutos. Você vai ver:

Operation "operations/..." finished successfully.

O que essas APIs oferecem:

  • aiplatform.googleapis.com: acesso aos modelos do Gemini para o raciocínio do agente
  • secretmanager.googleapis.com: armazenamento seguro para chaves de API (prática recomendada de produção)
  • cloudtrace.googleapis.com: capacidade de observação para nossa trilha de responsabilidade.

Clonar o código inicial

Extraia o repositório do workshop com todos os recursos e códigos de modelo:

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

Vamos verificar o que temos:

ls -la

Você verá:

  • charity_advisor/: onde vamos criar nossos agentes e ferramentas
  • scripts/: scripts auxiliares para teste e verificação
  • deploy.sh: script auxiliar para implantação
  • setup.py: script auxiliar para instalação de módulos
  • .env.template: arquivo de variáveis de ambiente

Configurar o ambiente Python

Agora vamos criar um ambiente Python isolado para nosso projeto.

Criar e ativar o ambiente virtual

# Create the virtual environment
python3 -m venv venv

# Activate it
source venv/bin/activate

Verificação: seu comando agora vai mostrar o prefixo (venv).

Instalar dependências

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

Isso instala:

  • google-adk: a estrutura do Kit de Desenvolvimento de Agente
  • google-cloud-aiplatform: integração da Vertex AI e do Gemini
  • ap2: SDK do protocolo de pagamentos do agente (do GitHub).
  • python-dotenv: gerenciamento de variáveis de ambiente

A flag -e permite importar módulos adk_ap2_charity_agents de qualquer lugar.

Configurar arquivo de ambiente

Crie sua configuração com base no modelo:

# 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

Você verá:

GOOGLE_CLOUD_PROJECT=your-actual-project-id

Verificação

Execute o script de verificação para garantir que tudo esteja configurado corretamente:

python scripts/verify_setup.py

Você vai ver todas as marcas de seleção verdes:

======================================================================
SETUP VERIFICATION
======================================================================

✓ Python version: 3.11.x
✓ google-adk: 1.17.0
✓ google-cloud-aiplatform: 1.111.0+
✓ ap2: 0.1.0
✓ python-dotenv: 1.0.0+
✓ .env file found and contains project ID
✓ Google Cloud project configured: your-project-id

✓ Mock charity database found
✓ Agent templates ready
✓ All directories present

======================================================================
✓ Setup complete! You are ready to build trustworthy agents.
======================================================================

Solução de problemas

A seguir

Seu ambiente está totalmente preparado. Você fez o seguinte:

  • ✅ Projeto do Google Cloud configurado
  • ✅ APIs necessárias ativadas
  • ✅ Bibliotecas do ADK e do AP2 instaladas
  • ✅ Código do modelo pronto para modificação

No próximo módulo, você vai criar seu primeiro agente de IA com algumas linhas de código e descobrir por que agentes simples não são confiáveis ao lidar com transações financeiras.

3. Seu primeiro agente e como descobrir a falta de confiança

banner

Da ideia à interação

No módulo anterior, preparamos nosso ambiente de desenvolvimento. Agora começa o trabalho de verdade. Vamos criar e executar nosso primeiro agente, dar a ele sua primeira capacidade e, ao fazer isso, descobrir os desafios fundamentais que precisamos resolver para torná-lo realmente confiável.

Este módulo é sua foto de "antes": o momento que revela por que a criação de agentes confiáveis exige mais do que apenas dar a um LLM acesso a ferramentas.

Etapa 1: analisar o agente inicial

Primeiro, vamos analisar o modelo do nosso primeiro agente. Ele contém uma estrutura básica com marcadores de posição que vamos preencher nas próximas etapas.

👉 Abra o arquivo .

charity_advisor/simple_agent/agent.py

no editor.

Você vai ver:

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

Observe que os comentários do marcador de posição seguem um padrão: MODULE_3_STEP_X_DESCRIPTION. Vamos substituir esses marcadores para criar nosso agente de forma progressiva.

Etapa 2: importar os componentes necessários

Antes de instanciar a classe Agent ou usar a ferramenta google_search, precisamos importá-las para nosso arquivo.

👉 Encontre:

# MODULE_3_STEP_2_IMPORT_COMPONENTS

👉 Substitua essa linha única por:

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

Agora, a classe Agent e a ferramenta google_search estão disponíveis no nosso arquivo.

Etapa 3: escrever a instrução do agente

A instrução é a "descrição do trabalho" do agente. Ela informa ao LLM quando e como usar as ferramentas. Vamos escrever um que oriente nosso agente a pesquisar informações sobre instituições de caridade.

👉 Encontre:

# MODULE_3_STEP_3_WRITE_INSTRUCTION
instruction="""""",

👉 Substitua essas duas linhas por:

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

Etapa 4: adicionar a ferramenta de pesquisa

Um agente sem ferramentas é apenas um interlocutor. Vamos dar ao nosso agente a primeira capacidade: pesquisar na Web.

👉 Encontre:

# MODULE_3_STEP_4_ADD_TOOLS
tools=[]

👉 Substitua essas duas linhas por:

tools=[google_search]

Etapa 5: verificar o agente completo

Vamos confirmar se todas as peças estão no lugar antes de testar.

👉 Seu

charity_advisor/simple_agent/agent.py

agora vai ficar exatamente assim:

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

Etapa 6: testar o agente e expor as lacunas de confiança

Agora que o agente está totalmente configurado, vamos testá-lo e analisar o comportamento dele. É aqui que descobrimos por que agentes simples não são confiáveis ao lidar com decisões financeiras.

Teste 1: o problema de descoberta

👉 No terminal do Cloud Shell, execute o comando a seguir:

adk run charity_advisor/simple_agent

A saída será semelhante a esta:

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

[user]:

O comando [user]: está aguardando sua entrada.

👉 No prompt [user]:, digite:

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

👉 Pressione Enter e observe a resposta.

Depois de um momento, o agente vai sintetizar os resultados da pesquisa em uma resposta como esta:

Com base em uma pesquisa na Web, algumas instituições de caridade bem avaliadas para alfabetização infantil parecem ser Reading Is Fundamental (link em inglês) e Room to Read (link em inglês). Fontes como Charity Navigator e GuideStar são recomendadas para verificar o status e as classificações. Também encontrei várias discussões on-line, incluindo em fóruns como o Reddit, em que os usuários compartilham experiências pessoais com vários programas de alfabetização locais menores.

Vamos analisar isso. O representante resolveu nosso problema?

Não.Ele replicou perfeitamente a experiência humana que descrevemos no Módulo 1. Ele automatizou com sucesso o processo de "pesquisar no Google" e devolveu o problema da "paralisia por análise" para nós.

Isso expõe a primeira lacuna de confiança: a falta de dados confiáveis.

O agente está pesquisando na Web aberta, o que significa:

  • ✅ Ele encontrou resultados rapidamente (melhor experiência do usuário)
  • ❌ Ele mistura organizações bem avaliadas com discussões do Reddit (fontes não confiáveis).
  • ❌ Não consegue distinguir entre instituições de caridade verificadas e possíveis golpes (sem verificação)
  • ❌ Ele está pedindo para nós verificarmos as informações que acabou de fornecer (transferindo a responsabilidade de volta)

Teste 2: o problema de execução

Agora, vamos para o segundo teste crucial. No prompt [user]:, tente concluir a doação:

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

O agente vai responder com um pedido de desculpas, admitindo sua limitação:

Entendi que você quer fazer uma doação, o que é ótimo! No entanto, sou assistente de pesquisa e não tenho capacidade para lidar com transações financeiras ou processar pagamentos. Para fazer uma doação, acesse diretamente o site oficial da Room to Read.

Esse é o segundo momento "Aha!", igualmente importante.

O agente não é confiável para encontrar a instituição de caridade certa nem para fazer a doação.

👉 Pressione

Ctrl+C

para sair quando terminar o teste.

As duas lacunas visualizadas

problema de confiança

O que você acabou de aprender

Neste módulo, você criou e equipou seu primeiro agente de IA. Ao fazer isso, você descobriu os dois desafios fundamentais de criar um sistema confiável.

Conceitos principais dominados

A classe Agent:

  • Elemento básico principal do ADK
  • Combina o raciocínio do LLM (cérebro) com ferramentas (mãos)
  • Configurado com modelo, instrução e ferramentas

Estrutura com base em pastas:

  • Cada agente fica em uma pasta própria
  • O ADK procura agent_folder/agent.py
  • Correr com adk run agent_folder

A lista de ferramentas:

  • Define os recursos do agente
  • O LLM decide quando e como usar as ferramentas
  • Pode conter várias ferramentas para diferentes ações

O comando de instrução:

  • Orienta o comportamento do agente como uma descrição de cargo
  • Especifica função, acionadores, ações e formato de saída
  • Essencial para o uso confiável de ferramentas

O problema da confiança:

  • Lacuna de descoberta: fontes não verificadas, qualidade variada
  • Lacuna de execução: sem recursos seguros, sem consentimento, sem registro de auditoria

A seguir

No próximo módulo, vamos começar a criar a solução implementando a arquitetura baseada em papéis do AP2.

Vamos criar o primeiro agente e ver a separação de funções em ação.

4. Como criar o agente de compras: descoberta com base em papéis

banner

A base de confiança: separação de papéis

No último módulo, você descobriu que um agente simples e de uso geral falha em duas frentes: não consegue fornecer descobertas confiáveis nem executar transações seguras. Agora vamos começar a resolver esses problemas implementando o primeiro princípio do Protocolo de pagamentos do agente: arquitetura baseada em função.

Antes de escrever qualquer código, vamos entender por que esse princípio é importante.

Princípio AP2: separação de funções

O problema com agentes que "fazem tudo"

Imagine que você contrate uma pessoa para ser seu consultor financeiro, contador e corretor de investimentos. Conveniente? Sim. Seguro? De jeito nenhum. Elas teriam:

  • Suas metas de investimento (função de consultor)
  • Acesso às suas contas (função de contador)
  • Autoridade para movimentar seu dinheiro (função de corretor)

Se essa pessoa for comprometida ou cometer um erro, tudo estará em risco.

Solução do AP2: um agente, um job

O AP2 aplica o princípio da separação de conceitos para criar limites de confiança:

arquitetura

Por que isso é importante:

  • Raio de explosão limitado: se o agente de compras for comprometido, o invasor não poderá acessar as credenciais de pagamento.
  • Privacidade: o provedor de credenciais nunca vê sua conversa de compras.
  • Compliance: é mais fácil atender aos requisitos do PCI DSS quando os dados de pagamento estão isolados.
  • Responsabilidade: responsabilidade clara por cada etapa

Como os agentes se comunicam: estado como bloco de notas compartilhado

Como os agentes não podem acessar diretamente os dados uns dos outros, eles se comunicam por um estado compartilhado. Pense nisso como um quadro branco em que todos os agentes podem escrever e ler:

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

É assim que mantemos os limites de confiança e permitimos a colaboração.

Nosso primeiro agente: o Shopping Agent

A responsabilidade do agente de compras é simples e focada:

  1. Use a ferramenta find_charities para consultar nosso banco de dados confiável
  2. Apresentar opções ao usuário
  3. Use a ferramenta save_user_choice para criar um IntentMandate e salvar no estado.
  4. Transferir para o próximo agente (o comerciante)

É isso. Sem processamento de pagamentos, sem criação de carrinho: apenas descoberta e transferência.

Vamos criar isso etapa por etapa.

Etapa 1: adicionar o Input Validation Helper

Ao criar ferramentas de produção, a validação de entrada é fundamental. Vamos criar uma função auxiliar que valida os dados da instituição de caridade antes de salvá-los no estado.

👉 Abrir

charity_advisor/tools/charity_tools.py

A função find_charities (já concluída) vai aparecer na parte de cima. Role a tela para baixo e encontre:

# MODULE_4_STEP_1_ADD_VALIDATION_HELPER

👉 Substitua essa linha única por:

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

Etapa 2: adicionar o auxiliar de criação do IntentMandate

Agora vamos criar o auxiliar que cria a estrutura IntentMandate do AP2. Esta é uma das três credenciais verificáveis na AP2.

👉 No mesmo arquivo, encontre:

# MODULE_4_STEP_2_ADD_INTENTMANDATE_CREATION_HELPER

👉 Substitua essa linha única por:

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

Etapa 3: criar a ferramenta de transferência de estado com o IntentMandate

Agora vamos criar a ferramenta que cria o IntentMandate e o salva no estado.

👉 No mesmo arquivo, role a tela para baixo até o

save_user_choice

. Localizar:

# MODULE_4_STEP_3_COMPLETE_SAVE_TOOL

👉 Substitua essa linha única por:

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

Etapa 4: adicionar o auxiliar de formatação de display

Antes de criar o agente, vamos adicionar mais um auxiliar que formata os dados de instituições de caridade para uma exibição fácil de usar.

👉 Role a tela para encontrar:

# MODULE_4_STEP_4_ADD_FORMATTING_HELPER

👉 Substitua essa linha única por:

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

Etapa 5: criar o agente do Shopping: importar componentes

Agora que nossas ferramentas estão completas e robustas, vamos criar o agente que vai usá-las.

👉 Abrir

charity_advisor/shopping_agent/agent.py

Você vai encontrar um modelo com comentários de marcador de posição. Vamos criar isso etapa por etapa.

👉 Encontre:

# MODULE_4_STEP_5_IMPORT_COMPONENTS

👉 Substitua essa linha única por:

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

Etapa 6: escrever a instrução do agente

A instrução é onde definimos a descrição do trabalho e o fluxo de trabalho do agente. Isso é fundamental: uma instrução mal escrita leva a um comportamento não confiável.

👉 Encontre:

# MODULE_4_STEP_6_WRITE_INSTRUCTION
instruction="""""",

👉 Substitua essas duas linhas por:

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

Etapa 7: adicionar ferramentas ao agente

Agora vamos dar ao agente acesso às duas ferramentas.

👉 Encontre:

# MODULE_4_STEP_7_ADD_TOOLS

👉 Substitua essas duas linhas por:

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

Etapa 8: verificar seu agente completo

Vamos verificar se tudo está conectado corretamente.

👉 Seu

charity_advisor/shopping_agent/agent.py

vai ficar assim:

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

Perfeito! Você criou um agente de qualidade de produção compatível com AP2 com:

  • Validação de entrada
  • Criação adequada de IntentMandate usando modelos Pydantic AP2
  • Saída formatada
  • Limites claros de função
  • Instruções detalhadas
  • Tratamento de erros

Etapa 9: testar o agente de compras

Vamos verificar se nosso agente funciona corretamente, cria IntentMandates adequados e respeita os limites.

👉 No terminal do Cloud Shell, execute:

adk run charity_advisor/shopping_agent

O prompt [user]: vai aparecer.

Teste 1: descoberta com o Trusted Database

👉 Tipo:

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

Em alguns instantes, o agente vai dar uma resposta. Graças ao nosso auxiliar _format_charity_display, os resultados são formatados de maneira elegante:

Encontrei três instituições de caridade educacionais verificadas no nosso banco de dados:

Room to Read (EIN: 77-0479905)
⭐ Avaliação: 4,9/5,0
💰 Eficiência: 88% dos fundos são destinados a programas
📋 Missão: transformar a vida de milhões de crianças em comunidades de baixa renda, focando em alfabetização e igualdade de gênero na educação.

Teach For America (EIN: 13-3541913)
⭐ Avaliação: 4,7/5,0
💰 Eficiência: 81% dos fundos são destinados a programas
📋 Missão: trabalha para ampliar as oportunidades educacionais de crianças em situação de adversidade.

Tech Education Alliance (EIN: 45-2345678)
⭐ Avaliação: 4,8/5,0
💰 Eficiência: 92% dos fundos são destinados a programas
📋 Missão: levar o ensino de ciência da computação a escolas carentes.

Qual instituição de caridade você quer apoiar e quanto quer doar?

Compare isso ao agente simples do módulo 3, que forneceu resultados não verificados do Google. Essa é a diferença entre a descoberta não confiável e a confiável.

Teste 2: gravar a escolha do usuário e criar IntentMandate

👉 Tipo:

I'll donate $50 to Room to Read.

O agente vai chamar a ferramenta save_user_choice com os parâmetros corretos. Graças à criação do IntentMandate usando modelos AP2, você vai notar uma resposta mais estruturada:

Perfeito! Criei um IntentMandate para sua doação:

Detalhes da intenção:

  • ID do intent: intent_774795905_1730927536
  • Valor: US$ 50,00 para a Room to Read (EIN: 77-0479905)
  • Expira em 2024-11-07T15:32:16Z (em 1 hora)

Essa IntentMandate captura sua intenção de doação e inclui restrições para garantir que a transação seja processada com segurança. Agora estou encaminhando isso para nosso processador de pagamentos seguro para criar uma oferta formal e concluir sua doação.

Nos bastidores, várias coisas aconteceram:

  1. _validate_charity_data() verificou as entradas
  2. O _create_intent_mandate() usou o modelo AP2 Pydantic para criar a estrutura:
   # 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. A ferramenta gravou APENAS o IntentMandate no estado compartilhado:
   state["intent_mandate"] = intent_mandate_dict
  1. Uma mensagem de sucesso com detalhes da intenção foi retornada ao LLM
  2. O LLM criou a mensagem final do usuário

Essa IntentMandate está pronta para ser retirada pelo agente do comerciante (módulo 5). O comerciante vai extrair o nome e o valor da instituição de caridade do IntentMandate e validar se ele não expirou.

Teste 3: validação em ação

Vamos verificar se o auxiliar de validação detecta entradas incorretas.

👉 Tipo:

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

O agente precisa detectar o valor inválido:

Encontrei um problema: o valor da doação precisa ser positivo.Recebi: $-25, 0

Especifique um valor de doação positivo para que eu possa criar o IntentMandate.

Essa é a programação defensiva em ação. Nosso auxiliar _validate_charity_data impediu que dados inválidos entrassem no sistema e criassem um IntentMandate malformado.

Teste 4: verificar o limite de confiança

👉 Peça ao agente para processar o pagamento:

Now process my credit card payment.

O agente precisa recusar, respeitando o limite da função:

Não tenho capacidade para processar pagamentos. Isso não faz parte da minha função. Meu trabalho é ajudar você a encontrar instituições de caridade verificadas e criar o IntentMandate, que captura sua intenção de doação.

Seu IntentMandate já foi criado e transmitido ao nosso processador de pagamentos seguro. O agente do comerciante vai criar uma oferta formal (CartMandate), e o provedor de credenciais vai processar o pagamento com seu consentimento explícito.

Esse é o limite de confiança em ação. O agente sabe que não está autorizado a processar dados de pagamento, e a instrução o orienta explicitamente a explicar isso aos usuários e ensinar sobre o conceito de IntentMandate.

👉 Pressione

Ctrl+C

para sair quando terminar o teste.

O que você acabou de criar

Você implementou com sucesso a primeira parte da arquitetura do AP2 com a criação adequada de IntentMandate usando modelos Pydantic do AP2.

Conceitos principais dominados

Arquitetura com base em papéis:

  • Cada agente tem uma função claramente definida
  • Os agentes se comunicam por estado compartilhado, não por acesso direto
  • Os limites de confiança limitam o impacto de uma invasão

IntentMandate (AP2 Credential #1):

  • Criado usando modelos oficiais do Pydantic AP2 para validação
  • Captura estruturada da intenção do usuário
  • Inclui validade para segurança (evita ataques de repetição)
  • Especifica restrições (comerciantes, reembolsabilidade, confirmação)
  • Descrição em linguagem natural para humanos
  • Legível por máquina para agentes
  • Modelo validado antes da conversão em dicionário

Estado como memória compartilhada:

  • tool_context.state é o "bloco de notas" que todos os agentes podem acessar
  • Gravar no estado = disponibilizar credenciais verificáveis
  • Leitura do estado = consumo e validação de credenciais
  • Os agentes downstream extraem o que precisam das credenciais

FunctionTool:

  • Converte funções Python em ferramentas que podem ser chamadas por LLMs
  • Depende de docstrings e dicas de tipo para a compreensão do LLM
  • Processa a invocação automaticamente
  • Capacidade de composição de ferramentas: ferramentas pequenas e focadas > monolíticas

Instruções para agentes:

  • Orientações detalhadas para o fluxo de trabalho
  • Limites explícitos ("NÃO faça...")
  • Especificações de parâmetros para evitar erros
  • Definições técnicas (o que é IntentMandate)
  • Tratamento de casos extremos (o que dizer quando...)

A seguir

No próximo módulo, vamos criar o Agente do comerciante para receber o IntentMandate e criar a segunda credencial verificável: CartMandate.

O agente de compras criou um IntentMandate que captura a intenção do usuário com validade. Agora precisamos de um agente para ler essa credencial, validar se ela não expirou e criar uma oferta formal e assinada que diga: "Eu, o comerciante, vou honrar esse preço e entregar esses produtos".

Vamos criar o agente do comerciante e conferir a segunda credencial AP2 em ação.

5. Como criar o agente do comerciante: ofertas vinculadas e CartMandate

banner

Da descoberta ao comprometimento

No módulo anterior, você criou o Shopping Agent, um especialista que encontra instituições de caridade verificadas e cria um IntentMandate que captura a intenção do usuário. Agora precisamos de um agente para receber essa IntentMandate e criar uma oferta formal e vinculativa.

É aqui que entra o segundo princípio fundamental do AP2: credenciais verificáveis pelo CartMandate.

Princípio da AP2: CartMandate e ofertas vinculadas

Por que precisamos de uma função de comerciante

No módulo 4, o agente do Shopping criou um IntentMandate e o salvou no estado:

state["intent_mandate"] = {
    "natural_language_description": "Donate $50 to Room to Read",
    "merchants": ["Room to Read"],
    "amount": 50.0,
    "intent_expiry": "2024-11-07T15:32:16Z"
}

Mas isso é apenas a intenção do usuário. Antes de processar qualquer pagamento, precisamos de:

  • Uma estrutura de oferta formal que os sistemas de pagamento entendem
  • Prova de que o comerciante vai honrar esse preço
  • Um compromisso vinculativo que não pode ser alterado no meio da transação.
  • Validação de que a intenção não expirou

Essa é a função do agente do comerciante.

O que é um CartMandate?

Um CartMandate é o termo do AP2 para um "carrinho de compras digital" que serve como uma oferta vinculante. Ele é estruturado de acordo com o padrão PaymentRequest do W3C, o que significa:

  • Os processadores de pagamentos do mundo todo reconhecem o formato
  • Ele contém todos os detalhes da transação de maneira padronizada.
  • Ela pode ser assinada criptograficamente para provar a autenticidade.

Pense nisso como um orçamento por escrito de um contratado:

  • ❌ Verbal: "Sim, posso fazer esse trabalho por uns cinquenta reais"
  • ✅ Cotação por escrito: custos detalhados, total, assinatura, data

O orçamento por escrito é vinculativo. O CartMandate é o equivalente digital.

intenção de adicionar ao carrinho

A estrutura de um CartMandate

Um CartMandate no AP2 tem uma estrutura aninhada específica:

cart_mandate = {
    "contents": {  # ← AP2 wrapper
        "id": "cart_xyz123",
        "cart_expiry": "2024-11-07T15:47:16Z",
        "merchant_name": "Room to Read",
        "user_cart_confirmation_required": False,
        
        "payment_request": {  # ← W3C PaymentRequest nested inside
            "method_data": [...],
            "details": {...},
            "options": {...}
        }
    },
    "merchant_authorization": "SIG_a3f7b2c8"  # ← Merchant signature
}

Três componentes principais:

1. contents: o wrapper do carrinho que contém:

  • ID e validade do carrinho
  • Nome do comerciante
  • O PaymentRequest do W3C

2. payment_request (dentro do conteúdo) – O que está sendo comprado:

  • method_data: Payment types accepted
  • detalhes: itens e total
  • opções: frete, requisitos de informações do pagador

3. merchant_authorization: assinatura criptográfica

Assinaturas do comerciante: comprovante de compromisso

A assinatura do comerciante é essencial. Ele prova:

  • Esta oferta veio de um comerciante autorizado
  • O comerciante se compromete a honrar esse preço exato.
  • A oferta não foi adulterada desde a criação

Em produção, essa seria uma assinatura criptográfica usando PKI (infraestrutura de chave pública) ou JWT (JSON Web Tokens). Para nosso workshop educacional, vamos simular isso com um hash SHA-256.

# Production (real signature):
signature = sign_with_private_key(cart_data, merchant_private_key)

# Workshop (simulated signature):
cart_hash = hashlib.sha256(cart_json.encode()).hexdigest()
signature = f"SIG_{cart_hash[:16]}"

Nossa missão: criar o agente do comerciante

O agente do comerciante vai:

  1. Ler o IntentMandate do estado (o que o agente do Shopping escreveu)
  2. Verifique se a intenção não expirou.
  3. Extraia o nome da instituição de caridade, o valor e outros detalhes
  4. Criar uma estrutura PaymentRequest compatível com W3C usando modelos Pydantic do AP2
  5. Encapsule em CartMandate do AP2 com validade
  6. Adicionar uma assinatura simulada do comerciante
  7. Escreva o CartMandate para o estado do provedor de credenciais (próximo módulo)

Vamos criar isso etapa por etapa.

Etapa 1: adicionar o auxiliar de validação de expiração

Primeiro, vamos configurar o arquivo de ferramentas relacionadas ao comerciante e adicionar um auxiliar para validar o vencimento do IntentMandate.

👉 Abrir

charity_advisor/tools/merchant_tools.py

Vamos adicionar a validação de expiração:

👉 Encontre:

# MODULE_5_STEP_1_ADD_EXPIRY_VALIDATION_HELPER

👉 Substitua essa linha única por:

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

Etapa 2: adicionar o Signature Generation Helper

Agora vamos criar um auxiliar que gera a assinatura simulada do comerciante.

👉 Encontre:

# MODULE_5_STEP_2_ADD_SIGNATURE_HELPER

👉 Substitua essa linha única por:

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

Etapa 3A: criar a assinatura e a configuração da ferramenta

Agora vamos começar a criar a ferramenta principal. Vamos criar isso de forma incremental em quatro subetapas. Primeiro, a assinatura da função e a configuração inicial.

👉 Encontre:

# MODULE_5_STEP_3A_CREATE_TOOL_SIGNATURE

👉 Substitua essa linha única por:

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

Etapa 3B: adicionar lógica de validação

Agora vamos adicionar a lógica para ler e validar o IntentMandate usando modelos Pydantic do AP2 e extrair os dados necessários.

👉 Encontre:

# MODULE_5_STEP_3B_ADD_VALIDATION_LOGIC

👉 Substitua essa linha única por:

    # 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

Etapa 3C: criar a estrutura CartMandate

Agora vamos criar a estrutura PaymentRequest compatível com o W3C e encapsulá-la no AP2 CartMandate usando modelos Pydantic.

👉 Encontre:

# MODULE_5_STEP_3C_CREATE_CARTMANDATE_STRUCTURE

👉 Substitua essa linha única por:

    # 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

Etapa 3D: adicionar assinatura e salvar no estado

Por fim, vamos assinar o CartMandate usando nosso modelo Pydantic e salvá-lo no estado para o próximo agente.

👉 Encontre:

# MODULE_5_STEP_3D_ADD_SIGNATURE_AND_SAVE

👉 Substitua essa linha única por:

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

Etapa 4: criar o agente do comerciante: importar componentes

Agora vamos criar o agente que vai usar essa ferramenta.

👉 Abrir

charity_advisor/merchant_agent/agent.py

Você vai ver um modelo com marcadores de posição. Vamos começar importando o que precisamos.

👉 Encontre:

# MODULE_5_STEP_4_IMPORT_COMPONENTS

👉 Substitua essa linha única por:

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

Etapa 5: escrever a instrução do agente do comerciante

Agora vamos escrever a instrução que diz ao agente quando e como usar a ferramenta.

👉 Encontre:

# MODULE_5_STEP_5_WRITE_INSTRUCTION
instruction="""""",

👉 Substitua essas duas linhas por:

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

Etapa 6: adicionar ferramentas ao agente do Merchant

👉 Encontre:

# MODULE_5_STEP_6_ADD_TOOLS
tools=[],

👉 Substitua essas duas linhas por:

    tools=[
        FunctionTool(func=create_cart_mandate)
    ],

Etapa 7: verificar o agente de comerciante completo

Vamos confirmar se tudo está conectado corretamente.

👉 Seu

charity_advisor/merchant_agent/agent.py

vai ficar assim:

"""
Merchant Agent - Creates W3C-compliant CartMandates with merchant signatures.
This agent acts as our "Contract Creator."
"""

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


merchant_agent = Agent(
    name="MerchantAgent",
    model="gemini-2.5-flash",
    description="Creates formal, signed CartMandates for charity donations following W3C PaymentRequest standards.",
    tools=[
        FunctionTool(func=create_cart_mandate)
    ],
    instruction="""You are a merchant specialist responsible for creating formal, signed offers (CartMandates).

Your workflow:

1. Read the IntentMandate from shared state.
   The IntentMandate was created by the Shopping Agent and contains:
   - merchants: List of merchant names
   - amount: Donation amount
   - charity_ein: Tax ID
   - intent_expiry: When the intent expires

2. Use the create_cart_mandate tool to create a W3C PaymentRequest-compliant CartMandate.
   This tool will:
   - Validate the IntentMandate hasn't expired (CRITICAL security check)
   - Extract the charity name and amount from the IntentMandate
   - Create a structured offer with payment methods, transaction details, and merchant info
   - Generate a merchant signature to prove authenticity
   - Save the CartMandate to state for the payment processor

3. After creating the CartMandate, inform the user:
   - That you've created a formal, signed offer
   - The cart ID
   - When the cart expires (15 minutes)
   - That you're passing it to the secure payment processor

IMPORTANT BOUNDARIES:
- Your ONLY job is creating signed CartMandates from valid IntentMandates
- You do NOT process payments
- You do NOT see the user's payment methods or credentials
- You do NOT interact with payment networks
- You MUST validate that the IntentMandate hasn't expired before creating a cart
- After calling create_cart_mandate, your work is done

WHAT IS A CARTMANDATE:
A CartMandate is a binding commitment that says:
"I, the merchant, commit to accepting $X for this charity donation, and I prove it with my signature."

This commitment is structured using the W3C PaymentRequest standard and includes:
- Payment methods accepted (card, bank transfer)
- Transaction details (amount, charity name)
- Cart expiry (15 minutes from creation)
- Merchant signature (proof of commitment)

This is the second of three verifiable credentials in our secure payment system."""
)

Checkpoint: agora você tem um agente do comerciante completo com a criação adequada do CartMandate AP2 usando modelos Pydantic.

Etapa 8: testar o agente do comerciante

Agora vamos verificar se o agente cria corretamente CartMandates com assinaturas e valida o vencimento.

Configuração do teste: executar o script de teste

👉 No terminal do Cloud Shell, execute:

python scripts/test_merchant.py

Resposta esperada:

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

Teste 2: verificar a conformidade com o W3C

Vamos validar se nossa estrutura CartMandate está totalmente em conformidade com os padrões AP2 e W3C PaymentRequest.

👉 Execute o script de validação:

python scripts/validate_cartmandate.py

Resposta esperada:

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

O que você acabou de criar

Você implementou com sucesso o CartMandate da AP2 usando modelos Pydantic para estrutura adequada, validação de expiração e assinaturas do comerciante.

Conceitos principais dominados

CartMandate (credencial AP2 nº 2):

  • Criado usando modelos oficiais do Pydantic AP2
  • Estrutura AP2 com wrapper de conteúdo
  • W3C PaymentRequest aninhado em
  • Expiração do carrinho (menor que a intenção)
  • Assinatura do comerciante para compromisso vinculativo
  • A validação do modelo garante a conformidade com a especificação

Validação de expiração:

  • Lendo IntentMandate do estado
  • Validando a estrutura com IntentMandate.model_validate()
  • Analisar carimbos de data/hora ISO 8601
  • Comparação com a hora atual
  • Recurso de segurança que impede o processamento desatualizado

Assinatura do comerciante:

  • Prova autenticidade e compromisso
  • Gerado de um modelo Pydantic validado
  • Usa model_dump(mode='json') para representação canônica
  • Simulado com SHA-256 para educação
  • A produção usa PKI/JWT
  • Assina o modelo de conteúdo, não os dicionários.

PaymentRequest do W3C:

  • Criado usando o modelo PaymentRequest Pydantic do AP2
  • Padrão do setor para dados de pagamento
  • Aninhado na estrutura AP2
  • Contém method_data, details, options
  • Permite a interoperabilidade

Cadeia de credenciais com modelos:

  • Shopping → IntentMandate (validado)
  • Merchant lê IntentMandate → CartMandate (ambos os modelos validados)
  • O provedor de credenciais vai ler CartMandate → PaymentMandate
  • Cada etapa valida a credencial anterior usando o Pydantic

Desenvolvimento orientado a modelos:

  • Validação de entrada via model_validate()
  • Construção com segurança de tipos
  • Serialização automática via model_dump()
  • Padrões prontos para produção

A seguir

No próximo módulo, vamos criar o provedor de credenciais para processar pagamentos com segurança.

O agente do comerciante criou uma oferta vinculativa com validade usando modelos AP2. Agora precisamos de um agente para ler esse CartMandate, receber o consentimento do usuário e executar o pagamento.

Vamos criar o provedor de credenciais e concluir a cadeia de credenciais do AP2.

6. Como criar o provedor de credenciais: execução de pagamento seguro

banner

Da oferta vinculada à execução do pagamento

No módulo 5, você criou o agente do comerciante, um especialista que lê IntentMandates, valida se eles não expiraram e cria CartMandates vinculados com assinaturas do comerciante. Agora precisamos de um agente para receber esse CartMandate e executar o pagamento real.

É aqui que entra em jogo o terceiro e último princípio da AP2: execução segura de pagamentos com PaymentMandate.

Princípio AP2: PaymentMandate e PaymentExecution

Por que precisamos de uma função de provedor de credenciais

No módulo 5, o agente do comerciante criou um CartMandate e o salvou no estado:

state["cart_mandate"] = {
    "contents": {
        "id": "cart_abc123",
        "cart_expiry": "2025-11-07:15:47:16Z",
        "payment_request": {
            "details": {
                "total": {
                    "amount": {"currency": "USD", "value": "50.00"}
                }
            }
        }
    },
    "merchant_authorization": "SIG_a3f7b2c8"
}

Mas essa é apenas uma oferta vinculativa. Antes de executar o pagamento, precisamos de:

  • Validação de que o carrinho não expirou
  • O usuário consente em prosseguir com o pagamento
  • Uma credencial que autoriza a execução do pagamento
  • Processamento de pagamento real (ou simulação para nosso workshop)

Essa é a função do provedor de credenciais.

O que é um PaymentMandate?

Um PaymentMandate é o termo da AP2 para a autorização final que permite a execução do pagamento. É a terceira e última credencial verificável na cadeia AP2.

Pense nas três credenciais como um processo de assinatura de contrato:

  • IntentMandate: "Tenho interesse em comprar isso" (Carta de intenção)
  • CartMandate: "Eu, o comerciante, ofereço a venda a este preço" (citação escrita)
  • PaymentMandate: "Autorizo você a cobrar na minha forma de pagamento" (contrato assinado)

O pagamento só pode ser feito depois que todas as três credenciais existirem.

cadeia de credenciais completa

Estrutura de um PaymentMandate

Um PaymentMandate no AP2 tem uma estrutura específica:

payment_mandate = {
    "payment_mandate_contents": {  # ← AP2 wrapper
        "payment_mandate_id": "payment_xyz123",
        "payment_details_id": "cart_abc123",  # Links to CartMandate
        "user_consent": True,
        "consent_timestamp": "2025-11-07T15:48:00Z",
        "amount": {
            "currency": "USD",
            "value": "50.00"
        },
        "merchant_name": "Room to Read"
    },
    "agent_present": True,  # Human-in-the-loop flow
    "timestamp": "2025-11-07T15:48:00Z"
}

Principais componentes:

1. payment_mandate_contents: o wrapper de autorização que contém:

  • payment_mandate_id: identificador exclusivo
  • payment_details_id: vincula de volta ao CartMandate
  • user_consent: se o usuário aprovou
  • amount: valor do pagamento (extraído de CartMandate)

2. agent_present: indica se é um fluxo human-in-the-loop.

3. timestamp: quando a autorização foi criada.

Nossa missão: criar o provedor de credenciais

O provedor de credenciais vai:

  1. Ler o CartMandate do estado (o que o agente do comerciante escreveu)
  2. Validar se o carrinho não expirou usando modelos Pydantic do AP2
  3. Extrair detalhes de pagamento da estrutura aninhada
  4. Criar um PaymentMandate com consentimento do usuário usando modelos AP2
  5. Simular o processamento de pagamentos (em produção, chamaria a API de pagamento real)
  6. Gravar o PaymentMandate e o resultado do pagamento no estado

Vamos criar isso etapa por etapa.

Etapa 1: adicionar o auxiliar de validação de expiração do carrinho

Primeiro, vamos criar um auxiliar que valide se o CartMandate não expirou, assim como validamos o vencimento do IntentMandate no Módulo 5.

👉 Abrir

charity_advisor/tools/payment_tools.py

Vamos adicionar a validação de expiração:

👉 Encontre:

# MODULE_6_STEP_1_ADD_CART_EXPIRY_VALIDATION_HELPER

👉 Substitua essa linha única por:

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

Etapa 2: adicionar o PaymentMandate Creation Helper

Agora vamos criar um auxiliar que cria a estrutura PaymentMandate usando modelos Pydantic oficiais do AP2.

👉 Encontre:

# MODULE_6_STEP_2_ADD_PAYMENT_MANDATE_CREATION_HELPER

👉 Substitua essa linha única por:

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

Etapa 3A: criar a assinatura e a configuração da ferramenta

Agora vamos começar a criar a ferramenta principal de forma incremental. Primeiro, a assinatura da função e a configuração inicial.

👉 Encontre:

# MODULE_6_STEP_3A_CREATE_TOOL_SIGNATURE

👉 Substitua essa linha única por:

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

Etapa 3B: validar CartMandate

Agora vamos adicionar a lógica para ler e validar o CartMandate usando modelos Pydantic AP2 e verificar a validade.

👉 Encontre:

# MODULE_6_STEP_3B_VALIDATE_CARTMANDATE

👉 Substitua essa linha única por:

    # 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

Etapa 3C: extrair detalhes de pagamento de uma estrutura aninhada

Agora, vamos navegar pelo modelo CartMandate validado para extrair os detalhes de pagamento necessários.

👉 Encontre:

# MODULE_6_STEP_3C_EXTRACT_PAYMENT_DETAILS

👉 Substitua essa linha única por:

    # 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

Etapa 3D: criar PaymentMandate e simular pagamento

Por fim, vamos criar o PaymentMandate usando nosso auxiliar baseado em Pydantic, simular o processamento de pagamentos e salvar tudo no estado.

👉 Encontre:

# MODULE_6_STEP_3D_CREATE_PAYMENTMANDATE_AND_SIMULATE

👉 Substitua essa linha única por:

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

Etapa 4: criar o agente provedor de credenciais: importar componentes

Agora vamos criar o agente que usa essa ferramenta.

👉 Abrir

charity_advisor/credentials_provider/agent.py

Você vai ver um modelo com marcadores de posição. Vamos começar importando o que precisamos.

👉 Encontre:

# MODULE_6_STEP_4_IMPORT_COMPONENTS

👉 Substitua essa linha única por:

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

Etapa 5: escrever a instrução do provedor de credenciais

Agora vamos escrever a instrução que orienta o agente.

👉 Encontre:

# MODULE_6_STEP_5_WRITE_INSTRUCTION
instruction="""""",

👉 Substitua essas duas linhas por:

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

Etapa 6: adicionar ferramentas ao provedor de credenciais

👉 Encontre:

# MODULE_6_STEP_6_ADD_TOOLS
tools=[],

👉 Substitua essas duas linhas por:

    tools=[
        FunctionTool(func=create_payment_mandate)
    ],

Etapa 7: verificar o provedor de credenciais completo

Vamos confirmar se tudo está conectado corretamente.

👉 Seu

charity_advisor/credentials_provider/agent.py

vai ficar assim:

"""
Credentials Provider Agent - Handles payment processing with user consent.
This agent acts as our "Payment Processor."
"""

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


credentials_provider = Agent(
    name="CredentialsProvider",
    model="gemini-2.5-flash",
    description="Securely processes payments by creating PaymentMandates and executing transactions with user consent.",
    tools=[
        FunctionTool(func=create_payment_mandate)
    ],
    instruction="""You are a payment specialist responsible for securely processing payments with user consent.

Your workflow:

1. Read the CartMandate from shared state.
   The CartMandate was created by the Merchant Agent and has this structure:
   - contents: AP2 wrapper containing:
     - id: Cart identifier
     - cart_expiry: When the cart expires
     - merchant_name: Who is receiving payment
     - payment_request: W3C PaymentRequest with transaction details
   - merchant_authorization: Merchant's signature

2. Extract payment details from the nested structure:
   - Navigate: cart_mandate["contents"]["payment_request"]["details"]["total"]["amount"]
   - This gives you the currency and value

3. **IMPORTANT - Two-Turn Conversational Confirmation Pattern:**
   Before calling create_payment_mandate, you MUST:
   - Present the payment details clearly to the user
   - Ask explicitly: "I'm ready to process a payment of $X to [Charity Name]. Do you want to proceed with this donation?"
   - WAIT for the user's explicit confirmation (e.g., "yes", "proceed", "confirm")
   - ONLY call create_payment_mandate AFTER receiving explicit confirmation
   - If user says "no" or "cancel", DO NOT call the tool

4. After user confirms, use the create_payment_mandate tool to:
   - Validate the CartMandate hasn't expired (CRITICAL security check)
   - Create a PaymentMandate (the third AP2 credential)
   - Simulate payment processing
   - Record the transaction result

5. After processing, inform the user:
   - That payment was processed successfully (this is a simulation)
   - The transaction ID
   - The amount and merchant
   - That this completes the three-agent AP2 credential chain

IMPORTANT BOUNDARIES:
- Your ONLY job is creating PaymentMandates and processing payments
- You do NOT discover charities (that's Shopping Agent's job)
- You do NOT create offers (that's Merchant Agent's job)
- You MUST validate that the CartMandate hasn't expired before processing
- You MUST get explicit user confirmation before calling create_payment_mandate
- In production, this consent mechanism would be even more robust

WHAT IS A PAYMENTMANDATE:
A PaymentMandate is the final credential that authorizes payment execution. It:
- Links to the CartMandate (proving the merchant's offer)
- Records user consent
- Contains payment details extracted from the CartMandate
- Enables the actual payment transaction

This is the third and final verifiable credential in our secure payment system.

THE COMPLETE AP2 CREDENTIAL CHAIN:
1. Shopping Agent creates IntentMandate (user's intent)
2. Merchant Agent reads IntentMandate, creates CartMandate (merchant's binding offer)
3. You read CartMandate, get user confirmation, create PaymentMandate (authorized payment execution)

Each credential:
- Has an expiry time (security feature)
- Links to the previous credential
- Is validated before the next step
- Creates an auditable chain of trust"""
)

Checkpoint: agora você tem um provedor de credenciais completo com leitura adequada de CartMandate e criação de PaymentMandate usando modelos AP2 Pydantic.

Etapa 8: testar o provedor de credenciais

Agora vamos verificar se o agente processa os pagamentos corretamente e conclui a cadeia de credenciais.

👉 No terminal do Cloud Shell, execute:

python scripts/test_credentials_provider.py

Resposta esperada:

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

Etapa 9: testar o pipeline completo de três agentes

Agora vamos testar os três agentes trabalhando juntos!

👉 Execute o teste completo do pipeline:

python scripts/test_full_pipeline.py

Resposta esperada:

======================================================================
THREE-AGENT PIPELINE TEST (AP2 CREDENTIAL CHAIN)
======================================================================

[1/3] SHOPPING AGENT - Finding charity and creating IntentMandate...
----------------------------------------------------------------------
✓ IntentMandate created
  - Intent ID: intent_774799058_1730927536
  - Description: Donate $75.00 to Room to Read
  - Merchant: Room to Read
  - Amount: $75.0
  - Expires: 2025-11-07T16:32:16Z

[2/3] MERCHANT AGENT - Reading IntentMandate and creating CartMandate...
----------------------------------------------------------------------
✓ CartMandate created
  - ID: cart_3b4c5d6e7f8a
  - Expires: 2025-11-07T15:47:16Z
  - Signature: SIG_a3f7b2c8d9e1f4a2

[3/3] CREDENTIALS PROVIDER - Creating PaymentMandate and processing...
----------------------------------------------------------------------
NOTE: In the web UI, this would show a confirmation dialog
      For this test, consent is automatically granted
✓ Payment processed (SIMULATED)
  - Transaction ID: txn_a3f7b2c8d9e1f4a2
  - Amount: $75.0
  - Status: completed

======================================================================
COMPLETE AP2 CREDENTIAL CHAIN
======================================================================

✓ Credential 1: IntentMandate (User's Intent)
  - Intent ID: intent_774799058_1730927536
  - Description: Donate $75.00 to Room to Read
  - Expiry: 2025-11-07T16:32:16Z

✓ Credential 2: CartMandate (Merchant's Offer)
  - Cart ID: cart_3b4c5d6e7f8a
  - Cart Expiry: 2025-11-07T15:47:16Z
  - Merchant Signature: SIG_a3f7b2c8d9e1f4a2

✓ Credential 3: PaymentMandate (Payment Execution)
  - Payment Mandate ID: payment_3b4c5d6e7f8a
  - Linked to Cart: cart_3b4c5d6e7f8a
  - Agent Present: True

✓ Transaction Result:
  - Transaction ID: txn_a3f7b2c8d9e1f4a2
  - Simulation: True

======================================================================
✅ COMPLETE PIPELINE TEST PASSED
======================================================================

Esta é a cadeia completa de credenciais do AP2 em ação!

Cada representante:

  1. Lê uma credencial do estado.
  2. Valida usando modelos Pydantic (estrutura + verificação de validade)
  3. Cria a próxima credencial usando modelos AP2.
  4. Grava no estado para o próximo agente

O que você acabou de criar

Você concluiu a cadeia de credenciais de três agentes da AP2 com validação de estrutura adequada usando modelos Pydantic e simulação de pagamento.

Conceitos principais dominados

PaymentMandate (AP2 Credential #3):

  • Criado usando modelos oficiais do Pydantic AP2
  • Credencial final que autoriza a execução do pagamento
  • Vincula-se a CartMandate usando payment_details_id
  • Registra o consentimento e o carimbo de data/hora do usuário.
  • Contém o valor do pagamento extraído de CartMandate.
  • Inclui a flag "agent_present" para human-in-the-loop
  • A validação do modelo garante a conformidade com a especificação

Leitura da CartMandate:

  • Validar a estrutura com CartMandate.model_validate()
  • Acesso a atributos com segurança de tipo: cart_model.contents.payment_request.details.total.amount
  • Entenda a diferença entre o wrapper AP2 e a separação padrão do W3C
  • Extrair merchant_name, amount, currency com segurança do modelo
  • O Pydantic detecta erros de estrutura automaticamente

Validação da expiração do carrinho:

  • Aceita o modelo Pydantic CartMandate validado
  • Lê de cart.contents.cart_expiry (acesso a atributos)
  • Recurso de segurança que impede o processamento de carrinho desatualizado.
  • Duração menor (15 minutos) do que a intenção (1 hora)

Simulação de pagamento:

  • Simulação educacional de um processador de pagamentos real.
  • Gera o ID da transação.
  • Registra payment_result no estado
  • Claramente marcada como simulação (flag "simulation: True")

Conclua a cadeia AP2 com modelos:

  • Três agentes, três credenciais, três validações do Pydantic
  • Cada agente valida a estrutura das credenciais anteriores usando modelos
  • Cada credencial é vinculada à anterior para trilha de auditoria
  • As transferências baseadas em estado mantêm a separação de papéis
  • Segurança de tipo em toda a cadeia

Desenvolvimento orientado a modelos:

  • Validação de entrada via model_validate()
  • Construção com segurança de tipos usando modelos aninhados
  • Serialização automática via model_dump(mode='json')
  • Padrões prontos para produção desde o início

A seguir

No próximo módulo, vamos criar o agente orquestrador, que coordena todos os três agentes especializados.

Você criou três agentes especialistas poderosos usando modelos Pydantic do AP2. Agora vamos criar o condutor que orquestra tudo isso em uma experiência de doação integrada.

Vamos criar o orquestrador e ver o sistema completo em ação.

7. Orquestração: reunindo tudo

pipeline sequencial

De especialistas a uma experiência perfeita

Nos módulos anteriores, você criou três agentes especializados:

  • Agente de compras: encontra instituições de caridade e cria IntentMandate.
  • Agente do comerciante: cria CartMandate com base em IntentMandate.
  • Provedor de credenciais: cria PaymentMandate e processa o pagamento.

Esses agentes se dividem naturalmente em duas fases:

  • Fase 1 (Shopping): conversa em várias etapas para encontrar e selecionar uma instituição de caridade
  • Fase 2 (processamento): execução atômica da criação e do pagamento da oferta

Mas, no momento, você teria que orquestrar essas fases manualmente.

É aqui que os padrões de orquestração do ADK brilham.

Princípio AP2: a orquestração aplica limites de confiança

Por que a orquestração é importante para a segurança

A orquestração não é apenas uma questão de conveniência, mas também de impor limites de confiança por meio da arquitetura.

Sem orquestração:

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

Com orquestração:

# 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

O pipeline sequencial garante:

  • ✅ IntentMandate criado antes de CartMandate
  • ✅ CartMandate criado antes do processamento do pagamento
  • ✅ Cada agente é executado em um contexto isolado
  • ✅ O estado flui pela cadeia de credenciais

Nossa missão: criar o sistema completo

Vamos criar duas camadas:

Camada 1: o pipeline de processamento (SequentialAgent)

  • Conecta o comerciante → Credenciais
  • Executa automaticamente em sequência depois que a instituição de caridade é selecionada
  • Execução atômica de oferta e pagamento

Camada 2: o orquestrador raiz (agente voltado ao usuário)

  • Personalidade amigável
  • Delega a shopping_agent para seleção de instituições de caridade
  • Delega ao pipeline de processamento depois que o IntentMandate é criado.
  • Processa conversas e transições de fase.

Essa abordagem de duas camadas corresponde ao fluxo natural:

  • Fase de compras: conversa em várias etapas (o usuário navega, faz perguntas e decide)
  • Fase de processamento: execução atômica (oferta → pagamento)

Vamos criar os dois.

Etapa 1: importar componentes de orquestração

Primeiro, vamos configurar o arquivo de orquestração com as importações necessárias.

👉 Abrir

charity_advisor/agent.py

Vamos começar com as importações:

👉 Encontre:

# MODULE_7_STEP_1_IMPORT_COMPONENTS

👉 Substitua essa linha única por:

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

Etapa 2: criar o pipeline de processamento

Agora vamos criar o pipeline que executa a criação de ofertas e o processamento de pagamentos de forma atômica.

👉 Encontre:

# MODULE_7_STEP_2_CREATE_SEQUENTIAL_PIPELINE

👉 Substitua essas duas linhas por:

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

Etapa 3A: criar a configuração do agente raiz

Agora vamos criar o agente voltado ao usuário que orquestra as duas fases. Vamos criar isso em três partes: configuração (3A), instrução (3B) e subagentes (3C).

👉 Encontre:

# MODULE_7_STEP_3A_CREATE_ROOT_AGENT_SETUP

👉 Substitua essa linha única por:

# 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

Etapa 3B: escrever a instrução do agente raiz

Agora, vamos adicionar a instrução que orienta o comportamento do consultor de caridade nas duas fases.

👉 Encontre:

# MODULE_7_STEP_3B_WRITE_ROOT_AGENT_INSTRUCTION

👉 Substitua essa linha única por:

    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

Etapa 3C: adicionar os subagentes

Por fim, vamos dar ao consultor de caridade acesso ao agente de compras e ao pipeline de processamento e fechar a definição do agente.

👉 Encontre:

# MODULE_7_STEP_3C_ADD_ROOT_AGENT_SUBAGENTS

👉 Substitua essa linha única por:

    sub_agents=[
        shopping_agent,
        donation_processing_pipeline
    ]
)

Etapa 4: verificar o sistema completo

Vamos confirmar se a orquestração está conectada corretamente.

👉 Seu

charity_advisor/agent.py

vai ficar assim:

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

Etapa 5: reforçar com callbacks de validação (opcional, pule para a etapa 7)

callbacks

O SequentialAgent garante a ordem de execução, mas e se:

  • O agente de compras falha silenciosamente (IntentMandate nunca criado)
  • Uma hora se passa entre o Shopping e o Merchant (a intenção expira)
  • O estado é corrompido ou limpo
  • Alguém tenta ligar diretamente para o comerciante, sem passar pelo Shopping

Callbacks adicionam aplicação arquitetônica: eles validam os pré-requisitos antes mesmo de um agente iniciar a chamada de LLM. Essa é a defesa em profundidade: as ferramentas validam durante a execução, e os callbacks validam antes da execução.

Vamos adicionar callbacks de validação aos nossos agentes de provedor de credenciais e comerciante.

Etapa 5A: adicionar validação do comerciante: importar tipos de callback

Primeiro, vamos adicionar as importações necessárias para callbacks.

👉 Abrir

charity_advisor/merchant_agent/agent.py

Na parte de cima do arquivo, depois das importações atuais, adicione:

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

Etapa 5B: criar a função de validação de intent

Agora, vamos criar uma função de callback que valide o IntentMandate antes da execução do Merchant Agent.

👉 Em

charity_advisor/merchant_agent/agent.py

, adicione esta função ANTES do

merchant_agent = Agent(...)

definition:

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

Etapa 5C: anexar o callback ao agente do comerciante

Agora vamos conectar o callback ao agente.

👉 Em

charity_advisor/merchant_agent/agent.py

, modifique o

merchant_agent = Agent(...)

definition:

Encontre esta linha na definição do agente:

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

Adicione esta linha logo após o

description

linha:

    before_agent_callback=validate_intent_before_merchant,

A definição do agente agora vai ficar assim:

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

Etapa 6: adicionar a validação do provedor de credenciais (opcional, pule para a etapa 7)

Mesmo padrão: vamos adicionar validação para a etapa de pagamento.

Etapa 6A: importar tipos de callback

👉 Abrir

charity_advisor/credentials_provider/agent.py

Na parte de cima do arquivo, depois das importações atuais, adicione:

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

Etapa 6B: criar a função de validação do carrinho

👉 Em

charity_advisor/credentials_provider/agent.py

, adicione esta função ANTES do

credentials_provider = Agent(...)

definition:

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

Etapa 6C: anexar callback ao provedor de credenciais

👉 Em

charity_advisor/credentials_provider/agent.py

, modifique o

credentials_provider = Agent(...)

definition:

Encontre esta linha na definição do agente:

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

Adicione esta linha logo após o

description

linha:

    before_agent_callback=validate_cart_before_payment,

A definição do agente agora vai ficar assim:

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

Etapa 7: testar com a interface da Web do ADK

Agora vamos testar o sistema completo com callbacks de validação ativos.

👉 No terminal do Cloud Shell, execute:

adk web

A saída será semelhante a esta:

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

👉 Em seguida, para acessar a interface da Web do ADK no navegador:

No ícone Visualização na Web (parece um olho ou um quadrado com uma seta) na barra de ferramentas do Cloud Shell (geralmente na parte superior direita), selecione Alterar porta. Na janela pop-up, defina a porta como 8000 e clique em Alterar e visualizar. O Cloud Shell vai abrir uma nova guia do navegador mostrando a interface da Web do ADK.

webpreview

👉 Selecione seu agente no menu suspenso:

Na interface da Web do ADK, você vai encontrar um menu suspenso na parte de cima. Selecione charity_advisor na lista.

agent-select

A interface da Web do ADK vai aparecer com:

  • Painel do chat: lado esquerdo, para conversas
  • Painel de rastreamento: lado direito, para observabilidade (vamos usar isso no módulo 9)

Teste 1: concluir o fluxo de doação (caso normal)

👉 Na interface do chat, digite:

I want to donate to an education charity

Confira o fluxo completo:

agente de compras da Web do ADK

pipeline de processamento de doações da Web do ADK

O que está acontecendo (visível no painel de rastreamento à direita):

1. O Advisor delega ao ShoppingAgent:

  • O ShoppingAgent pesquisa instituições de caridade de educação
  • Mostra três opções verificadas com detalhes

2. Você interage com o ShoppingAgent (pode levar várias rodadas):

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

3. O ShoppingAgent cria o IntentMandate:

  • Cria e assina a intent.
  • Retorna a confirmação com o ID da intent

4. O consultor passa para a fase de processamento:

Perfeito! Processando sua doação de US $50 para a Room to Read…

5. O DonationProcessingPipeline é ativado:

  • O callback do comerciante valida o IntentMandate (✓ aprovado) ← NOVO!
  • O agente do comerciante cria um CartMandate com assinatura
  • O callback de credenciais valida o CartMandate (✓ aprovado) ← NOVO!
  • O provedor de credenciais prepara o pagamento

6. Processos de pagamento:

  • O provedor de credenciais cria PaymentMandate
  • Simula o processamento de pagamentos.
  • ID da transação de devolução

7. O Advisor resume:

Perfeito! Sua doação foi processada. 🎉

Detalhes:

  • Valor: US$ 50,00
  • Organização sem fins lucrativos: Room to Read (EIN: 77-0479905)
  • ID da transação: txn_a3f7b2c8d9e1f4a2

Teste 2: verificar se os callbacks detectam falhas (teste avançado opcional)

Quer ver os callbacks em ação detectando erros? Você precisaria corromper manualmente o estado (depuração avançada), mas, em produção, os callbacks detectariam:

  • Falha na ferramenta do agente de compras → Bloqueios de callback do comerciante: "Erro: não é possível criar carrinho..."
  • 2 horas se passam → Bloqueios de callback do comerciante: "Erro: a intenção expirou..."
  • O carrinho expira → Bloqueios de callback de credenciais: "Erro: o carrinho expirou (limite de 15 minutos)..."

Esses casos extremos agora são aplicados arquiteturalmente pelos seus callbacks de validação.

O que você acabou de criar

Você orquestrou três agentes especializados em um sistema integrado e confiável com validação arquitetônica.

A seguir

Você concluiu o núcleo técnico da criação de agentes confiáveis:

Você criou um sistema completo e confiável localmente, aplicando a cadeia de credenciais. Agora vamos disponibilizar para usuários reais com a implantação de produção e ativar o rastreamento de responsabilidade que torna o módulo 9 possível.

Vamos implantar seu agente reforçado no Google Cloud.

8. Implantação

banner

Seu sistema de doações confiável agora está completo com três agentes especializados trabalhando localmente:

mas ele só é executado na sua máquina de desenvolvimento. Para tornar esse sistema útil para usuários reais e capturar os rastros de responsabilidade que comprovam a confiabilidade, é necessário implantá-lo na produção.

Este módulo mostra como implantar seu agente no Google Cloud com a observabilidade ativada desde o primeiro dia. A flag --trace_to_cloud que você vai usar durante a implantação é o que torna possível o rastreamento de responsabilidade no módulo 9.

Como entender as opções de implantação

O ADK é compatível com vários destinos de implantação. Cada um tem características diferentes de complexidade, gerenciamento de sessão, escalonamento e custo:

Fator

Local (adk web)

Mecanismo do agente

Cloud Run

Complexidade

Mínimo

Baixo

Médio

Persistência da sessão

Somente na memória (perdido na reinicialização)

Gerenciada pela Vertex AI (automática)

Cloud SQL (PostgreSQL) ou na memória

Infraestrutura

Nenhum (somente máquina de desenvolvimento)

Totalmente gerenciado

Contêiner + banco de dados opcional

Inicialização a frio

N/A

100 a 500 ms

100 a 2.000 ms

Escalonamento

Instância única

Automático

Automático (para zero)

Modelo de custo

Sem custo financeiro (computação local)

Baseado em computação

Baseada em solicitações + nível sem custo financeiro

Suporte à interface

Sim (integrado)

Não (somente API)

Sim (com a sinalização --with_ui)

Configuração da observabilidade

Visualizador de rastros local

Automático com --trace_to_cloud

Requer a flag --trace_to_cloud

Ideal para

Desenvolvimento e teste

Agentes de produção

Agentes de produção

Recomendação:para esse sistema de doação confiável, recomendamos o Agent Engine como sua principal implantação de produção porque ele oferece:

  • Infraestrutura totalmente gerenciada (sem contêineres para gerenciar)
  • Persistência de sessão integrada via VertexAiSessionService
  • Escalonamento automático sem inicializações a frio
  • Implantação simplificada (não é necessário ter conhecimento do Docker)
  • Integração imediata do Cloud Trace

Opção adicional: Google Kubernetes Engine (GKE)

Para usuários avançados que precisam de controle no nível do Kubernetes, rede personalizada ou orquestração de vários serviços, o implantação do GKE está disponível. Essa opção oferece o máximo de flexibilidade, mas exige mais experiência operacional (gerenciamento de cluster, manifestos, contas de serviço).

A implantação do GKE não é abordada neste codelab, mas está totalmente documentada no Guia de implantação do GKE do ADK.

Pré-requisitos

1. Configuração do projeto do Google Cloud

Você precisa de um projeto do Google Cloud com o faturamento ativado. Se você não tiver:

  1. Criar um projeto: Console do Google Cloud
  2. Ativar o faturamento: Ativar o faturamento
  3. Anote o ID do projeto (não o nome ou número dele).

2. Reautenticação (opcional)

Faça a autenticação no Google Cloud:

gcloud auth application-default login
gcloud config set project YOUR_PROJECT_ID

Substitua YOUR_PROJECT_ID pelo ID do seu projeto do Google Cloud.

Verifique sua autenticação:

gcloud config get-value project
# Should output: YOUR_PROJECT_ID

3. Variáveis de ambiente

Use estes comandos para preencher automaticamente o arquivo .env:

# Get your current Project ID
PROJECT_ID=$(gcloud config get-value project)
STAGING_BUCKET_VALUE="gs://${PROJECT_ID}-staging"
ENV_FILE=".env"

# Check if STAGING_BUCKET is already set in the .env file
if grep -q "^STAGING_BUCKET=" "${ENV_FILE}"; then
  # If it exists, replace the line
  # The sed command finds the line starting with STAGING_BUCKET= and replaces the entire line.
  # Using | as a delimiter to avoid issues with slashes in the bucket name.
  sed -i "s|^STAGING_BUCKET=.*|STAGING_BUCKET=${STAGING_BUCKET_VALUE}|" "${ENV_FILE}"
  echo "Updated STAGING_BUCKET in ${ENV_FILE}"
else
  # If it doesn't exist, add it to the end of the file
  echo "STAGING_BUCKET=${STAGING_BUCKET_VALUE}" >> "${ENV_FILE}"
  echo "Added STAGING_BUCKET to ${ENV_FILE}"
fi

# Verify it was added or updated correctly
echo "Current STAGING_BUCKET setting:"
grep "^STAGING_BUCKET=" "${ENV_FILE}"

Você verá:

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

Observações importantes:

  • Substitua YOUR_PROJECT_ID pelo ID do projeto real ou use os comandos acima.
  • Para GOOGLE_CLOUD_LOCATION, use uma região compatível
  • O bucket de preparo será criado automaticamente se não existir quando você executar o script de implantação.

4. Ativar APIs obrigatórias

O processo de implantação precisa de várias APIs do Google Cloud ativadas. Execute este comando para ativar:

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

Esse comando ativa:

  • API AI Platform: para modelos do Agent Engine e da Vertex AI
  • API Cloud Storage: para bucket de preparação
  • API Cloud Build: para criação de contêineres (Cloud Run)
  • API Cloud Trace: para observabilidade e rastreamentos de responsabilidade.
  • API Compute Engine: para gerenciamento de contas de serviço

Etapa 1: entender a infraestrutura de implantação

Seu projeto inclui um script de implantação unificado (deploy.sh) que processa todos os modos de implantação.

👉 Revise o script de implantação (opcional):

cat deploy.sh

O script oferece três modos de implantação:

  • ./deploy.sh local: executar localmente com armazenamento na memória
  • ./deploy.sh agent-engine: implante no Vertex AI Agent Engine (recomendado)
  • ./deploy.sh cloud-run: implantação no Cloud Run com interface opcional

Como funciona:

Para a implantação do Agent Engine, o script executa:

adk deploy agent_engine \
  --project=$GOOGLE_CLOUD_PROJECT \
  --region=$GOOGLE_CLOUD_LOCATION \
  --staging_bucket=$STAGING_BUCKET \
  --display_name="Charity Advisor" \
  --trace_to_cloud \
  charity_advisor

Para a implantação do Cloud Run, ele executa:

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

A flag --trace_to_cloud é essencial para os dois tipos de implantação. Ela ativa a integração do Cloud Trace para a trilha de responsabilidade que você vai conhecer no módulo 9.

Etapa 2: preparar o wrapper do mecanismo de agente

O Agent Engine exige um ponto de entrada específico que encapsula seu agente para o ambiente de execução gerenciado. Esse arquivo já foi criado para você.

👉 Revisão

charity_advisor/agent_engine_app.py

:

"""Agent Engine application wrapper.

This file prepares the Charity Advisor agent for deployment to Vertex AI Agent Engine.
"""

from vertexai import agent_engines
from .agent import root_agent

# Wrap the agent in an AdkApp object for Agent Engine deployment
app = agent_engines.AdkApp(
    agent=root_agent,
    enable_tracing=True,  # Enables Cloud Trace integration automatically
)

Por que esse arquivo é necessário:

  • O Agent Engine exige que o agente esteja agrupado em um objeto AdkApp.
  • O parâmetro enable_tracing=True ativa a integração do Cloud Trace automaticamente.
  • Esse wrapper é referenciado pela CLI do ADK durante a implantação.
  • Ele configura VertexAiSessionService para persistência automática de sessão.

O Agent Engine é a implantação de produção recomendada para seu sistema de doações confiável porque oferece infraestrutura totalmente gerenciada com persistência de sessão integrada.

Executar a implantação

Na raiz do projeto:

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

Fases de implantação

Assista o script executar estas fases:

Phase 1: API Enablement
   aiplatform.googleapis.com
   storage.googleapis.com
   cloudbuild.googleapis.com
   cloudtrace.googleapis.com
   compute.googleapis.com

Phase 2: IAM Setup
   Getting project number
   Granting Storage Object Admin
   Granting Vertex AI User
   Granting Cloud Trace Agent

Phase 3: Staging Bucket
   Creating gs://your-project-id-staging (if needed)
   Setting permissions

Phase 4: Validation
   Checking agent.py exists
   Verifying root_agent defined
   Checking agent_engine_app.py exists
   Validating requirements.txt

Phase 5: Build & Deploy
   Packaging agent code
   Uploading to staging bucket
   Creating Agent Engine instance
   Configuring session persistence
   Setting up Cloud Trace integration
   Running health checks

Esse processo leva de 5 a 10 minutos, já que empacota o agente e o implanta na infraestrutura da Vertex AI.

Salvar o ID do Agent Engine

Após a implantação bem-sucedida:

✅ 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

Atualize o arquivo .env imediatamente:

echo "AGENT_ENGINE_ID=7917477678498709504" >> .env

O que foi implantado

Sua implantação do Agent Engine agora inclui:

Todos os três agentes (Shopping, Merchant, Credentials) em execução no ambiente de execução gerenciado
Lógica completa da cadeia de credenciais (intenção → carrinho → autorizações de pagamento)
Mecanismo de consentimento do usuário com fluxo de trabalho de confirmação
Persistência automática de sessão via VertexAiSessionService
✅ Infraestrutura de escalonamento automático gerenciada pelo Google
Integração do Cloud Trace para observabilidade completa

Etapa 4: testar o agente implantado

Atualizar o ambiente

Verifique se o .env inclui o ID do 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

Executar o script de teste

Seu projeto inclui um script de teste específico para implantações do Agent Engine.

👉 Executar o teste:

python scripts/test_deployed_agent.py

Resposta esperada

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

Creating session...
✓ Session created: 4857885913439920384

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

✅ Test completed successfully!

Session ID: 4857885913439920384

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

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

Lista de verificação da confirmação

Depois de testar, verifique:

✅ O agente responde às consultas
✅ Todos os três agentes são executados em sequência (Shopping → Comerciante → Credenciais)
✅ O mecanismo de consentimento é ativado (confirmação solicitada)
✅ A sessão persiste em todas as solicitações
✅ Não há erros de autenticação
✅ Não há tempos limite de conexão

Se você encontrar erros:

  • Verifique se as variáveis de ambiente estão definidas corretamente
  • Verificar se as APIs estão ativadas: gcloud services list --enabled
  • Verifique os registros do Agent Engine no console da Vertex AI
  • Verifique se o arquivo agent_engine_app.py existe na pasta charity_advisor.

Etapa 5: implantar no Cloud Run (opcional)

Embora o Agent Engine seja recomendado para implantação de produção simplificada, o Cloud Run oferece mais controle e é compatível com a interface da Web do ADK. Esta seção é opcional.

Quando usar o Cloud Run

Escolha o Cloud Run se você precisar de:

  • A interface da Web do ADK para interação do usuário
  • Controle total sobre o ambiente do contêiner
  • Configurações personalizadas de banco de dados
  • Integração com serviços atuais do Cloud Run

Executar a implantação

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

O que vai mudar:

O script vai fazer o seguinte automaticamente:

  • Criar um contêiner do Docker com o código do agente
  • Crie um banco de dados do PostgreSQL no Cloud SQL (se necessário)
  • Configurar a conexão do banco de dados
  • Implantar com a interface da Web do ADK ativada

A implantação leva de 10 a 15 minutos devido ao provisionamento do Cloud SQL.

Gerenciamento de sessão:

  • Usa DatabaseSessionService em vez de VertexAiSessionService
  • Requer credenciais de banco de dados em .env (ou geradas automaticamente)
  • O estado é mantido no banco de dados PostgreSQL

Suporte à interface:

  • Interface da Web disponível em: https://charity-advisor-xyz.a.run.app

Como testar a implantação do Cloud Run

Se você fez a implantação no Cloud Run com --with_ui, teste diretamente no navegador:

  1. Navegue até o URL do serviço (fornecido na saída da implantação).
  2. A interface da Web do ADK vai aparecer. Selecione seu agente no menu suspenso.
  3. Faça uma doação de teste:
   I want to donate $50 to a children's education charity
  1. Observe o fluxo de execução:
    • O ShoppingAgent encontra instituições de caridade e salva sua intenção
    • O MerchantAgent cria o mandato do carrinho
    • O CredentialsProvider cria um mandato de pagamento e pede confirmação
    • Depois que você confirmar, o pagamento será processado
  2. Verifique se a resposta inclui:
    • Recomendações de instituições de caridade
    • Pedido de confirmação
    • Mensagem de sucesso após a aprovação

Solução de problemas

Problemas comuns

Problema: ERROR: GOOGLE_CLOUD_PROJECT is not set

Solução:verifique se o arquivo .env tem o ID do projeto correto:

GOOGLE_CLOUD_PROJECT=your-actual-project-id

Problema:o bucket de preparo não é criado automaticamente

Solução:o script cria o bucket automaticamente. Se não, crie manualmente:

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

Resumo

Você fez o seguinte:

✅ Entendeu a infraestrutura de implantação fornecida por deploy.sh
✅ Analisou a configuração do wrapper do Agent Engine
✅ Implantou seu sistema de doações confiável no Agent Engine (recomendado)
✅ Ativou a integração do Cloud Trace com --trace_to_cloud
✅ Verificou se o agente está acessível e funcional
✅ Criou a base para rastreamentos de responsabilidade no módulo 9

No próximo módulo, você vai saber exatamente o que essa flag desbloqueia: visibilidade completa de cada doação, cada momento de consentimento e cada etapa da cadeia de credenciais.

9. Observabilidade

banner

rastreamento de grafo

No módulo 1, você aprendeu sobre um problema fundamental: quando um agente de IA lida com dinheiro, como provar o que aconteceu?

Um usuário pode reivindicar:

  • "Eu nunca escolhi essa instituição de caridade!"
  • "Não autorizei esse pagamento!"
  • "O sistema fez uma cobrança sem minha autorização!"

Em um sistema tradicional de IA de caixa preta, não há como provar o contrário. Mas seu sistema de doação confiável é diferente. No módulo 8, você fez a implantação com a flag --trace_to_cloud, o que significa que cada doação agora cria um rastreamento de auditoria completo e inviolável no Cloud Trace.

Este módulo ensina a ler esses rastreamentos e usá-los como evidência. O que você vai aprender:

  • Navegar pelo Cloud Trace Explorer para encontrar rastreamentos de produção
  • Leia a visualização em cascata para entender o fluxo de execução
  • Encontre a cadeia de credenciais (Intent → Cart → Payment mandates)
  • Localizar momentos de consentimento com prova de carimbo de data/hora
  • Usar rastreamentos para resolução de disputas
  • Exportar rastreamentos para compliance e auditorias

É isso que separa os sistemas confiáveis dos capazes, mas opacos: a capacidade de provar o que aconteceu com precisão forense.

Noções básicas sobre rastreamentos e períodos

Antes de visualizar traces no Cloud Trace, é necessário entender o que você está vendo.

O que é um rastreamento?

Um trace é a linha do tempo completa do seu agente processando uma única solicitação. Ele captura tudo desde quando um usuário envia uma consulta até que a resposta final seja entregue.

Cada rastreamento mostra:

  • Duração total da solicitação
  • Todas as operações executadas
  • Como as operações se relacionam (relações pai-filho)
  • Quando cada operação começou e terminou
  • Status de sucesso ou falha

Para seu agente de caridade:um rastreamento = um fluxo de doação completo, desde "Quero fazer uma doação" até "Pagamento concluído".

O que é um período?

Um intervalo representa uma única unidade de trabalho em um rastreamento. Pense nos intervalos como os elementos básicos de um rastreamento.

Tipos de períodos comuns no seu sistema de doações:

Tipo de período

O que ele representa

Exemplo

agent_run

Execução de um agente

ShoppingAgent.run, MerchantAgent.run

call_llm

Solicitação a um modelo de linguagem

gemini-2.5-flash solicitação de seleção de instituição de caridade

execute_tool

Execução da função da ferramenta

find_charities, create_payment_mandate

state_read

Leitura da memória da sessão

Como recuperar intent_mandate do estado

state_write

Gravar na memória da sessão

Armazenar cart_mandate no estado

Cada intervalo contém:

  • Nome:qual operação isso representa
  • Quanto tempo levou (horário de início → horário de término)
  • Atributos:metadados como entradas de ferramentas, respostas do modelo, contagens de tokens
  • Status:sucesso (OK) ou erro (ERROR)
  • Relacionamentos pai-filho:quais operações acionaram quais

Como os períodos formam um rastreamento

Os intervalos são aninhados para mostrar a causalidade:

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

Essa hierarquia mostra exatamente o que aconteceu e em que ordem. Você pode ver que a autorização de pagamento foi criada depois da autorização do carrinho, que foi depois que o usuário selecionou uma instituição de caridade.

Etapa 1: acessar o Cloud Trace Explorer

Agora vamos ver os rastreamentos reais do seu agente implantado.

  1. Abra o console do Google Cloud: console.cloud.google.com
  2. Selecione seu projeto no menu suspenso na parte de cima. Ele já vai estar selecionado se você estiver trabalhando nele.
  3. Navegue até o Explorador do Cloud Trace:

O que você está vendo

O Trace Explorer mostra uma lista de todos os traces do seu projeto:

Coluna

O que ele mostra

Solicitação

Método HTTP e endpoint (para solicitações de API)

Horário de início

Quando a solicitação começou

Latência

Duração total da solicitação

Intervalos

Número de operações no rastreamento

Cada linha representa uma solicitação completa ao agente implantado.

Gerar rastreamentos de teste (se necessário)

Se você ainda não encontrar nenhum rastreamento, a lista pode estar vazia porque:

  • Nenhum pedido foi feito ao seu agente implantado
  • Os traces levam de 1 a 2 minutos para aparecer após uma solicitação

Gerar um rastreamento de teste:

Se você fez a implantação no Cloud Run com a interface, acesse o URL do serviço e faça uma doação no navegador.

Se você fez a implantação no Agent Engine, execute o script de teste do Módulo 8:

python scripts/test_deployed_agent.py

Aguarde 1 a 2 minutos e atualize a página do Explorador do Cloud Trace. Agora você vai ver os rastreamentos.

Filtrar rastreamentos

Use as opções de filtro na parte de cima para encontrar traces específicos:

  • Período:mude de "Última hora" para "Últimas 24 horas", se necessário.
  • Latência mínima / Latência máxima:filtre solicitações lentas.
  • Filtro de solicitação:pesquise por operações específicas (por exemplo, "DonationPipeline")

Para este módulo, concentre-se em rastreamentos com durações mais longas (>5 segundos), já que eles representam fluxos de doação completos com a execução dos três agentes.

Etapa 2: analisar um fluxo de doação completo

Clique em qualquer rastreamento na lista para abrir a visualização em cascata. É aqui que você vai passar a maior parte do tempo analisando o comportamento do agente.

Noções básicas sobre a visualização em cascata

A visualização em cascata é um gráfico de Gantt que mostra a linha do tempo completa da execução:

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

Como ler o gráfico

Cada barra representa um período:

  • Posição horizontal:quando começou
  • Duração:quanto tempo levou
  • Recuo:mostra relações pai-filho
  • Cor:geralmente azul para normal e vermelho para erros

Principais observações deste exemplo de rastreamento:

Duração total:8,2 segundos
Execução sequencial:o ShoppingAgent foi concluído antes do início do MerchantAgent
O MerchantAgent foi concluído

before

CredentialsProvider started
Consent was the longest operation: 3.0 seconds for create_payment_mandate (because it waited for user confirmation)
LLM calls are visible: Each agent made one Gemini request
Tool calls are captured: All six tools executed successfully

Essa visualização mostra imediatamente onde o tempo é gasto e em que ordem as operações foram executadas.

Clique em um período para ver detalhes

Clique no intervalo invocation (o intervalo raiz na parte de cima). No painel à direita, você vai encontrar atributos detalhados:

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

Esses atributos fornecem contexto sobre toda a solicitação.

Etapa 3: encontrar a cadeia de credenciais

Seu sistema confiável usa uma cadeia de credenciais para comprovar a autorização em cada etapa:

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

Vamos encontrar cada mandato no rastreamento.

Como encontrar o IntentMandate

Clique no intervalo execute_tool: save_user_choice (em ShoppingAgent).

No painel de atributos, você vai encontrar:

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

Isso prova que:

  • ✅ O usuário selecionou "Save the Children"
  • ✅ O valor era de US $50
  • ✅ A escolha foi registrada às 15:30:12 UTC
  • ✅ A assinatura foi gerada (em produção, ela seria criptográfica)

O IntentMandate agora está no estado da sessão e disponível para agentes subsequentes.

Como encontrar o CartMandate

Clique no intervalo execute_tool: create_cart_mandate (em MerchantAgent).

No painel de atributos:

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

Isso prova que:

  • ✅ O MerchantAgent recebeu o IntentMandate (a entrada mostra isso)
  • ✅ O carrinho foi criado com o ID "cart_7893"
  • ✅ A assinatura do carrinho faz referência à assinatura do IntentMandate (link de corrente).
  • ✅ Criado às 15:30:14 UTC (2,4 segundos após a intenção)

O CartMandate agora faz referência ao IntentMandate, formando a cadeia.

Como encontrar o PaymentMandate

Clique no intervalo execute_tool: create_payment_mandate (em CredentialsProvider).

No painel de atributos:

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

Isso prova a cadeia completa:

  • ✅ O CredentialsProvider recebeu o CartMandate (a entrada mostra isso)
  • ✅ O pagamento faz referência à assinatura do CartMandate (link de corrente).
  • Confirmação obrigatória (confirmation_required: true)
  • Usuário confirmou às 15:30:17 UTC
  • O sistema esperou 29,2 segundos pela decisão do usuário
  • ✅ O pagamento foi criado após a confirmação (carimbo de data/hora: 15:30:47)

Como visualizar a cadeia

O rastreamento prova que a cadeia de credenciais foi executada corretamente:

15:30:12 UTC  IntentMandate created (signature: a3f7...)
                  
15:30:14 UTC  CartMandate created (references: a3f7...)
                  
15:30:17 UTC  User consent requested
                  
15:30:47 UTC  PaymentMandate created (references: e8f2...)

Cada mandato faz referência à assinatura do anterior. Ela é inviolável. Para verificar a cadeia, confira se as assinaturas correspondem.

Etapa 4: analisar a performance e os gargalos

O Cloud Trace não apenas prova o que aconteceu, mas também mostra onde o tempo é gasto para que você possa otimizar.

Identificar o caminho crítico

Na visualização em cascata, procure os períodos mais longos na pilha vertical. Eles representam seus gargalos de performance.

No nosso exemplo de rastreamento:

Total: 8.2 seconds

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

Insight crítico:CredentialsProvider representa 49% do tempo total. Por quê?

Detalhe o intervalo "CredentialsProvider":

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

O atraso de 3 segundos é esperado e bom: é o usuário deliberando antes de confirmar. Isso não é um problema de performance, mas sim uma prova de consentimento consciente.

Rastreamento de custos de LLM

Clique em qualquer intervalo call_llm para conferir o uso de tokens:

{
  "llm.model": "gemini-2.5-flash",
  "llm.usage.prompt_tokens": 487,
  "llm.usage.completion_tokens": 156,
  "llm.usage.total_tokens": 643,
  "llm.response_time_ms": 1243
}

Use isso para:

  • Acompanhar o custo por solicitação (tokens × preços do modelo)
  • Identificar comandos desnecessariamente longos
  • Comparar o desempenho do modelo (Flash x Pro)
  • Otimizar para latência x qualidade

Exemplo de cálculo:

Gemini 2.5 Flash pricing (as of Nov 2024):
  Input:  $0.075 per 1M tokens
  Output: $0.30 per 1M tokens

This request:
  Input:  487 tokens × $0.075 / 1M = $0.000037
  Output: 156 tokens × $0.30 / 1M  = $0.000047
  Total:                            = $0.000084 (~$0.00008)

For 10,000 donations/month:
  10,000 × 3 agents × $0.00008 = $2.40/month in LLM costs

Essa visibilidade granular ajuda você a tomar decisões orientadas por dados sobre a seleção de modelos.

Comparar entre rastreamentos

Filtrar vários rastreamentos e comparar durações:

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)

Insight:a maior parte da variação vem do tempo de decisão do usuário, não do desempenho do sistema. A execução do agente principal (sem consentimento) é consistente em cerca de 5 segundos.

Isso indica que o sistema está funcionando de maneira confiável.

Para sistemas de produção, configure alertas para detectar problemas antes que os usuários reclamem.

Alerta sobre altas taxas de erros

Crie um alerta quando mais de 5% dos rastreamentos contiverem erros:

  1. Navegue até o Cloud Monitoring.
  2. Clique em AlertasCriar política.
  3. Configurar:
    Resource: Cloud Trace Span
    Metric: Span error count
    Condition: Rate > 5% over 5 minutes
    Notification: Email your-team@example.com
    

Alerta de alta latência

Crie um alerta quando a latência p95 exceder 15 segundos:

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

Isso detecta a degradação da performance antes que ela afete a experiência do usuário.

Crie um alerta se algum pagamento for processado sem confirmação:

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

Esse é um detector de violação de segurança. Se ele for acionado, algo está muito errado com seu mecanismo de consentimento.

O que você aprendeu

Com o Cloud Trace, agora você sabe como:

Navegue pelo Explorador do Cloud Trace para encontrar rastreamentos de produção
Leia as visualizações em cascata para conferir o fluxo de execução completo
Rastreie a cadeia de credenciais por IntentMandate → CartMandate → PaymentMandate ✅ Use rastreamentos como evidências para resolução de disputas
Analise a performance para identificar gargalos
Acompanhe os custos de LLM em um nível granular

A diferença que isso faz

Compare dois sistemas que lidam com a mesma reclamação "Nunca autorizei isso!":

Sistema sem observabilidade

User: "I never authorized that $50 donation!"
You:  "Our logs show the transaction completed successfully."
User: "But I didn't approve it!"
You:  "The system requires confirmation before processing."
User: "I never saw any confirmation!"
You:  "..." [no way to prove what happened]

Result: Refund issued, trust lost, user never returns.

Sistema com o 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.

Esse é o poder dos rastros de responsabilidade. Você passa de "confie em nós" para "vamos mostrar exatamente o que aconteceu".

A seguir

Você concluiu o núcleo técnico da criação de agentes confiáveis:

Módulos 1 a 6:projetou uma arquitetura confiável (funções, credenciais, consentimento)
Módulo 7:orquestrou fluxos de trabalho complexos (SequentialAgent)
Módulo 8:fez a implantação com a observabilidade ativada
Módulo 9:aprendeu a ler e usar rastros de responsabilidade

A arquitetura que você criou (separação de funções, cadeias de credenciais, mecanismos de consentimento, observabilidade completa) é transferida diretamente para sistemas de produção que lidam com dinheiro, dados e consequências reais.

10. Sua jornada

O que você criou

Você começou este workshop com uma pergunta: "Como criar agentes de IA em que eu possa confiar para lidar com dinheiro?"

Agora você tem a resposta.

Onde você começou (Módulo 3):

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

Onde você está agora (Módulo 10):

  • ✅ Três agentes especializados com separação de funções
  • ✅ Três credenciais verificáveis (intenção → carrinho → autorizações de pagamento)
  • ✅ Cadeia de credenciais completa com validação de expiração em cada etapa
  • ✅ Mecanismo de consentimento explícito com prova de carimbo de data/hora
  • ✅ Implantação de produção no Agent Engine com capacidade de observação
  • ✅ Rastreamento de responsabilidade completo no Cloud Trace
  • ✅ Evidências forenses para resolução de disputas

Workshop x produção: a lacuna

Seu sistema demonstra a arquitetura e os padrões corretos, mas usa simplificações educacionais que precisam ser atualizadas para dinheiro e usuários reais.

Confira exatamente o que foi simplificado e o que a produção exige:

Componente

Implementação do workshop

Requisitos de produção

Assinaturas

Hashes SHA-256 (SIG_abc123) para demonstração

Assinaturas criptográficas reais usando PKI ou JWT com chaves privadas

Processamento de pagamentos

Retornos simulados (flag simulation: True)

Integração com APIs de pagamento reais (Stripe, PayPal, Square)

Autenticação de usuário

Confiança implícita (não é necessário fazer login)

OAuth 2.0, WebAuthn ou gerenciamento de sessão

Gerenciamento de secrets

Variáveis de ambiente no arquivo .env

Google Secret Manager ou Cloud KMS com criptografia

Banco de dados de instituições de caridade

Arquivo JSON simulado com nove instituições de caridade

Integração de API ativa (pesquisa de organizações isentas de tributos do IRS, API Charity Navigator)

Tratamento de erros

Try-catch básico com mensagens de erro

Lógica de repetição com espera exponencial, disjuntores, filas de mensagens não entregues

Testes

Verificação manual por scripts

Conjunto abrangente de testes de unidade/integração/E2E com CI/CD

Persistência da sessão

Na memória (local) ou automática (Agent Engine)

Banco de dados de produção com backups e recuperação de desastres

Limitação de taxa

Nenhum (ambiente educacional)

Limites de taxa por usuário, limitação com base em IP, detecção de abuso

Principais padrões de arquitetura que você aprendeu

Os padrões que você aprendeu neste workshop são padrões de produção. Não duvide delas.

Separação de funções (princípio nº 1 da AP2)

Cada agente tem UMA função clara e vê APENAS o que precisa. Se um agente for comprometido, o invasor não poderá acessar os dados de outros agentes. Isso limita o raio de impacto.

Sistemas de produção que usam isso:processamento de pagamentos, fluxos de trabalho de documentos, cadeias de aprovação, formulários de várias etapas com portões de validação.

Credenciais verificáveis (princípio nº 2 da AP2)

Cada credencial tem um tempo de expiração, faz referência à credencial anterior e exige validação antes da próxima etapa. Isso cria uma cadeia de auditoria à prova de violação.

Valor de produção:prova completa do que aconteceu, quando e em que ordem. Essencial para resolução de disputas e conformidade regulatória.

Prova de carimbo de data/hora de que o usuário aprovou a ação. Não pode ser contestado.

Valor de produção:requisito legal para transações financeiras. Protege o usuário e a empresa.

Orquestração sequencial (padrão ADK)

Força a ordem de execução correta. Evita que você pule etapas. Garante que cada agente veja a saída do agente anterior.

Valor de produção:perfeito para sistemas human-in-the-loop em que os usuários esperam resultados imediatos. Esse é o padrão certo para fluxos de doação, processos de finalização de compra e cadeias de aprovação.

Observabilidade completa (OpenTelemetry + Cloud Trace)

Todas as decisões, chamadas de ferramentas, momentos de consentimento e transferências de credenciais capturados automaticamente.

Valor de produção:evidências forenses para disputas. Dados de otimização de performance. Trilhas de auditoria de compliance. Depure problemas de produção com precisão.

Recursos para aprendizado contínuo

Documentação do ADK:

AP2 e padrões relacionados:

Serviços do Google Cloud:

Limpar recursos

Para evitar cobranças contínuas, exclua a implantação:

Agent Engine:siga as etapas na documentação do Agent Engine.

Cloud Run (se implantado):

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

Buckets de armazenamento:

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

Sua jornada continua

Você começou com uma pergunta simples e criou uma resposta completa. Você aprendeu os padrões básicos para agentes de IA confiáveis. Esses padrões são transferidos para qualquer domínio em que os agentes de IA lidam com operações sensíveis: transações financeiras, decisões de saúde, documentos jurídicos, operações da cadeia de suprimentos.

Os princípios são transferidos. O modelo de confiança funciona.

Agora, crie algo confiável! ❤️

banner