Stack de agentes do Google em ação: ADK, A2A e MCP no Google Cloud

1. O que você vai aprender

Olá! Hoje, vamos embarcar em uma jornada muito legal. Vamos começar pensando em uma plataforma popular de eventos sociais, a InstaVibe. Embora seja bem-sucedido, sabemos que, para alguns usuários, o planejamento real de atividades em grupo pode parecer uma tarefa difícil. Imagine tentar descobrir o que todos os seus amigos gostam, procurar opções de eventos ou locais e, por fim, coordenar tudo. É muita coisa! É exatamente aí que podemos introduzir a IA e, mais especificamente, os agentes inteligentes, para fazer uma diferença real.

A ideia é criar um sistema em que esses agentes possam lidar com o trabalho pesado, como "ouvir" de forma inteligente para entender as preferências do usuário e dos amigos e, em seguida, sugerir atividades fantásticas e personalizadas de forma proativa. Nosso objetivo é transformar o planejamento social no InstaVibe em algo simples e agradável. Para começar a criar esses assistentes inteligentes, precisamos estabelecer uma base sólida com as ferramentas certas.

Este é o conceito que você vai encontrar:

Página do título

Fundamentos com o ADK do Google:domine os conceitos básicos para criar seu primeiro agente inteligente usando o Kit de Desenvolvimento de Agente (ADK) do Google. Entenda os componentes essenciais, o ciclo de vida do agente e como aproveitar as ferramentas integradas do framework de maneira eficaz.

Como ampliar os recursos dos agentes com o Protocolo de contexto de modelo (MCP): aprenda a equipar seus agentes com ferramentas e contexto personalizados, permitindo que eles realizem tarefas especializadas e acessem informações específicas. Apresentar o conceito do protocolo de contexto de modelo (MCP). Você vai aprender a configurar um servidor MCP para fornecer esse contexto.

Design de interações e orquestração de agentes:vá além dos agentes únicos para entender a orquestração de agentes. Projete padrões de interação que vão de fluxos de trabalho sequenciais simples a cenários complexos que envolvem loops, lógica condicional e processamento paralelo. Apresente o conceito de subagentes na estrutura do ADK para gerenciar tarefas modulares.

Como criar sistemas multiagentes colaborativos:descubra como arquitetar sistemas em que vários agentes colaboram para alcançar metas complexas. Aprenda e implemente o protocolo de comunicação agente-para-agente (A2A), estabelecendo uma maneira padronizada para que agentes distribuídos (que podem estar em execução em máquinas ou serviços diferentes) interajam de maneira confiável.

Produção de agentes no Google Cloud:faça a transição dos aplicativos de agente de ambientes de desenvolvimento para a nuvem. Conheça as práticas recomendadas para arquitetar e implantar sistemas multiagentes escalonáveis e robustos no Google Cloud Platform (GCP). Saiba como aproveitar os serviços do GCP, como o Cloud Run, e conheça os recursos do mais recente Google Agent Engine para hospedar e gerenciar seus agentes.

2. Arquitetura

Planejamento de redes sociais com tecnologia de IA usando o InstaVibe

O que é monitoramento de mídia social?

O social listening é o processo de monitorar conversas digitais em plataformas como redes sociais, fóruns e sites de notícias para entender o que as pessoas estão dizendo sobre um tema, uma marca ou um setor. Ele oferece insights valiosos sobre o sentimento público, tendências e necessidades dos usuários. Neste workshop, vamos usar esse conceito em um sistema baseado em agentes.

Você está na equipe da InstaVibe

Imagine que você trabalha na "InstaVibe", uma startup de sucesso com uma plataforma de eventos sociais voltada para jovens adultos. As coisas estão indo bem, mas, como muitas empresas de tecnologia, sua equipe enfrenta a pressão dos investidores para inovar usando IA. Internamente, você também notou um segmento de usuários que não estão interagindo tanto quanto outros. Talvez eles tenham menos inclinação a iniciar atividades em grupo ou acham o processo de planejamento desafiador. Para sua empresa, isso significa menor fidelidade à plataforma entre esse importante grupo de usuários.

A pesquisa da sua equipe sugere que a assistência baseada em IA pode melhorar significativamente a experiência desses usuários. A ideia é simplificar o processo de planejamento de saídas sociais, sugerindo atividades relevantes com base nos interesses do usuário e dos amigos dele. A pergunta que você e seus colegas enfrentam é: como os agentes de IA podem automatizar as tarefas de descoberta de interesses, pesquisa de atividades e possível coordenação inicial, que geralmente consomem muito tempo?

Uma solução baseada em agentes (conceito de protótipo)

Você propõe desenvolver um recurso de protótipo com tecnologia de um sistema multiagente. Confira um detalhamento conceitual:

Caso de uso

  • Agente de criação de perfil social: usa técnicas de escuta social para analisar conexões, interações e tendências públicas mais amplas relacionadas às preferências do usuário. O objetivo é identificar interesses em comum e características de atividades adequadas (por exemplo, preferências por encontros mais tranquilos, hobbies específicos).
  • Agente de planejamento de eventos: usando os insights do agente de criação de perfis sociais, esse agente pesquisa recursos on-line para eventos, locais ou ideias específicas que se alinham aos critérios identificados (como local, interesses).
  • Agente de interação da plataforma (usando o MCP): esse agente usa o plano finalizado do agente de planejamento de atividades. A principal função dele é interagir diretamente com a plataforma InstaVibe usando uma ferramenta MCP (protocolo de contexto de modelo) predefinida. Essa ferramenta oferece ao agente a capacidade específica de criar uma sugestão de evento e uma postagem descrevendo o plano.
  • Agente orquestrador: atua como coordenador central. Ele recebe a solicitação inicial do usuário na plataforma InstaVibe, entende o objetivo geral (por exemplo, "planeje um evento para mim e meus amigos") e delega tarefas específicas aos agentes especializados apropriados em uma sequência lógica. Ele gerencia o fluxo de informações entre os agentes e garante que o resultado final seja entregue ao usuário.

Principais elementos e tecnologias arquitetônicas

Arquitetura

Google Cloud Platform (GCP):

  • Vertex AI:
    • Modelos do Gemini: oferece acesso aos modelos de linguagem grandes (LLMs) mais avançados do Google, como o Gemini, que impulsionam as capacidades de raciocínio e tomada de decisões dos nossos agentes.
    • Vertex AI Agent Engine: um serviço gerenciado usado para implantar, hospedar e dimensionar nosso agente orquestrador, simplificando a produção e abstraindo as complexidades da infraestrutura.
  • Cloud Run: uma plataforma sem servidor para implantar aplicativos em contêineres. Usamos isso para:
    • Hospede o principal aplicativo da Web do InstaVibe.
    • Implante agentes individuais ativados para A2A (Planner, Social Profiling, Platform Interaction) como microsserviços independentes.
    • Execute o servidor de ferramentas do MCP, disponibilizando as APIs internas do InstaVibe para os agentes.
  • Spanner: um banco de dados relacional totalmente gerenciado, distribuído globalmente e de grande consistência. Neste workshop, vamos aproveitar os recursos do BigQuery como um banco de dados de gráficos usando a DDL GRAPH e os recursos de consulta para:
    • Modelar e armazenar relacionamentos sociais complexos (usuários, amizades, participação em eventos, postagens).
    • Permitir consultas eficientes dessas relações para os agentes de criação de perfil social.
  • Artifact Registry: um serviço totalmente gerenciado para armazenar, gerenciar e proteger imagens de contêiner.
  • Cloud Build: um serviço que executa seus builds no Google Cloud. Usamos esse serviço para criar automaticamente imagens de contêiner do Docker com base no código-fonte do agente e do aplicativo.
  • Cloud Storage: usado por serviços como o Cloud Build para armazenar artefatos de build e pelo Agent Engine para necessidades operacionais.
  • Frameworks e protocolos principais do agente:
    • Kit de desenvolvimento de agentes (ADK) do Google: o framework principal para:
      • Definir a lógica principal, o comportamento e os conjuntos de instruções para agentes inteligentes individuais.
      • Gerenciar ciclos de vida, estado e memória do agente (estado da sessão de curto prazo e conhecimento de longo prazo).
      • Integração de ferramentas (como a Pesquisa Google ou ferramentas personalizadas) que os agentes podem usar para interagir com o mundo.
      • Orquestração de fluxos de trabalho de vários agentes, incluindo execução sequencial, em loop e paralela de subagentes.
    • Protocolo de comunicação entre agentes (A2A): um padrão aberto que permite:
      • Comunicação e colaboração diretas e padronizadas entre diferentes agentes de IA, mesmo que estejam sendo executados como serviços separados ou em máquinas diferentes.
      • Agentes para descobrir os recursos uns dos outros (usando cards de agente) e delegar tarefas. Isso é fundamental para que nosso agente do Orchestrator interaja com os agentes especializados do Planner, Social e Platform.
    • Biblioteca Python A2A (a2a-python): a biblioteca concreta usada para fazer com que nossos agentes do ADK falem o protocolo A2A. Ele fornece os componentes do lado do servidor necessários para:
      • Expor nossos agentes como servidores compatíveis com A2A.
      • Processar automaticamente a exibição do "Card do agente" para descoberta.
      • Receber e gerenciar solicitações de tarefas recebidas de outros agentes (como o Orchestrator).
    • Protocolo de Contexto de Modelo (MCP): um padrão aberto que permite aos agentes:
      • Conectar e usar ferramentas, fontes de dados e sistemas externos de maneira padronizada.
      • Nosso agente de interação da plataforma usa um cliente MCP para se comunicar com um servidor MCP, que, por sua vez, expõe ferramentas para interagir com as APIs atuais da plataforma InstaVibe.
  • Ferramentas de depuração:
    • A2A Inspector: o A2A Inspector é uma ferramenta de depuração baseada na Web usada em todo este workshop para se conectar, inspecionar e interagir com nossos agentes habilitados para A2A. Embora não faça parte da arquitetura de produção final, é uma parte essencial do nosso fluxo de trabalho de desenvolvimento. Ele oferece:
      • Visualizador de card do agente: para buscar e validar as funcionalidades públicas de um agente.
      • Interface do chat ao vivo: para enviar mensagens diretamente a um agente implantado para teste imediato.
      • Console de depuração: para ver as mensagens JSON-RPC brutas trocadas entre o inspetor e o agente.
  • Modelos de linguagem (LLMs): o "cérebro" do sistema:
    • Modelos do Gemini do Google: usamos versões como gemini-2.0-flash. Esses modelos são escolhidos para:
      • Raciocínio avançado e capacidade de seguir instruções: a capacidade de entender comandos complexos, seguir instruções detalhadas e raciocinar sobre tarefas os torna adequados para impulsionar a tomada de decisões dos agentes.
      • Uso de ferramentas (chamada de função): os modelos do Gemini são excelentes para determinar quando e como usar as ferramentas fornecidas pelo ADK, permitindo que os agentes coletem informações ou realizem ações.
      • Eficiência (modelos Flash): as variantes "flash" oferecem um bom equilíbrio entre desempenho e custo-benefício, adequadas para muitas tarefas de agentes interativos que exigem respostas rápidas.

Precisa de créditos do Google Cloud?

3. Antes de começar

👉Clique em Ativar o Cloud Shell na parte de cima do console do Google Cloud (é o ícone em forma de terminal na parte de cima do painel do Cloud Shell), Cloud Shell

👉Clique no botão "Abrir Editor" (parece uma pasta aberta com um lápis). Isso vai abrir o editor de código do Cloud Shell na janela. Um explorador de arquivos vai aparecer no lado esquerdo. Cloud Shell

👉Clique no botão Fazer login no Cloud Code na barra de status inferior, conforme mostrado. Autorize o plug-in conforme instruído. Se a barra de status mostrar Cloud Code – sem projeto, clique na opção e escolha o projeto do Google Cloud com que você quer trabalhar no menu suspenso "Selecionar um projeto do Google Cloud". Cloud Shell

👉 Encontre seu 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.

Cloud Shell

👉Abra o terminal no IDE da nuvem, Cloud Shell

👉💻 No terminal, verifique se você já está autenticado e se o projeto está definido como seu ID do projeto usando o seguinte comando:

gcloud auth list

👉💻 Clone o projeto instavibe-bootstrap do GitHub:

git clone -b adk-1.2.1-a2a-0.2.7 https://github.com/weimeilin79/instavibe-bootstrap.git
chmod +x ~/instavibe-bootstrap/init.sh
chmod +x ~/instavibe-bootstrap/set_env.sh

Entender a estrutura do projeto

Antes de começar a criar, vamos entender o layout do projeto instavibe-bootstrap que você acabou de clonar. Isso vai ajudar você a saber onde encontrar e editar arquivos durante o workshop.

instavibe-bootstrap/
├── agents/
   ├── orchestrate/
   ├── planner/
   ├── platform_mcp_client/
   └── social/
├── instavibe/
   ├── static/
   └── templates/
├── tools/
   └── instavibe/
├── utils/
├── init.sh
└── set_env.sh

Confira um resumo dos principais diretórios:

  • agents/: é o coração do nosso sistema de IA. Cada subdiretório (planner/, social/, etc.) contém o código-fonte de um agente inteligente específico.
    • agent.py: dentro da pasta de cada agente, esse é o arquivo principal em que a lógica do agente.
    • a2a_server.py: esse arquivo encapsula o agente do ADK com um servidor de agente para agente (A2A).
    • Dockerfile: define como criar a imagem do contêiner para implantar o agente no Cloud Run ou no Agent Engine.
  • instavibe/: esse diretório contém todo o código-fonte do aplicativo da Web InstaVibe.
  • tools/: esse diretório é para criar ferramentas externas que nossos agentes podem usar.
    • instavibe/ contém o servidor do protocolo de contexto de modelo (MCP).

Essa estrutura modular separa o aplicativo da Web dos vários componentes de IA, facilitando o gerenciamento, o teste e a implantação de todo o sistema.

👉💻 Execute o script de inicialização:

O script vai pedir que você insira o ID do projeto do Google Cloud.

Insira o ID do projeto do Google Cloud que você encontrou na última etapa quando solicitado pelo script init.sh:

cd ~/instavibe-bootstrap
./init.sh

👉💻 Defina o ID do projeto necessário:

gcloud config set project $(cat ~/project_id.txt) --quiet

👉💻 Execute o comando a seguir para ativar as APIs do Google Cloud necessárias:

gcloud services enable  run.googleapis.com \
                        cloudfunctions.googleapis.com \
                        cloudbuild.googleapis.com \
                        artifactregistry.googleapis.com \
                        spanner.googleapis.com \
                        apikeys.googleapis.com \
                        iam.googleapis.com \
                        compute.googleapis.com \
                        aiplatform.googleapis.com \
                        cloudresourcemanager.googleapis.com \
                        maps-backend.googleapis.com

👉💻 Defina todas as variáveis de ambiente necessárias:

export PROJECT_ID=$(gcloud config get project)
export PROJECT_NUMBER=$(gcloud projects describe ${PROJECT_ID} --format="value(projectNumber)")
export SERVICE_ACCOUNT_NAME=$(gcloud compute project-info describe --format="value(defaultServiceAccount)")
export SPANNER_INSTANCE_ID="instavibe-graph-instance"
export SPANNER_DATABASE_ID="graphdb"
export GOOGLE_CLOUD_PROJECT=$(gcloud config get project)
export GOOGLE_GENAI_USE_VERTEXAI=TRUE
export GOOGLE_CLOUD_LOCATION="us-central1"

Como configurar permissões

👉💻 Conceda permissões. No terminal, execute :

gcloud projects add-iam-policy-binding $PROJECT_ID \
  --member="serviceAccount:$SERVICE_ACCOUNT_NAME" \
  --role="roles/spanner.admin"

# Spanner Database User
gcloud projects add-iam-policy-binding $PROJECT_ID \
  --member="serviceAccount:$SERVICE_ACCOUNT_NAME" \
  --role="roles/spanner.databaseUser"

# Artifact Registry Admin
gcloud projects add-iam-policy-binding $PROJECT_ID \
  --member="serviceAccount:$SERVICE_ACCOUNT_NAME" \
  --role="roles/artifactregistry.admin"

# Cloud Build Editor
gcloud projects add-iam-policy-binding $PROJECT_ID \
  --member="serviceAccount:$SERVICE_ACCOUNT_NAME" \
  --role="roles/cloudbuild.builds.editor"

# Cloud Run Admin
gcloud projects add-iam-policy-binding $PROJECT_ID \
  --member="serviceAccount:$SERVICE_ACCOUNT_NAME" \
  --role="roles/run.admin"

# IAM Service Account User
gcloud projects add-iam-policy-binding $PROJECT_ID \
  --member="serviceAccount:$SERVICE_ACCOUNT_NAME" \
  --role="roles/iam.serviceAccountUser"

# Vertex AI User
gcloud projects add-iam-policy-binding $PROJECT_ID \
  --member="serviceAccount:$SERVICE_ACCOUNT_NAME" \
  --role="roles/aiplatform.user"

# Logging Writer (to allow writing logs)
gcloud projects add-iam-policy-binding $PROJECT_ID \
  --member="serviceAccount:$SERVICE_ACCOUNT_NAME" \
  --role="roles/logging.logWriter"


gcloud projects add-iam-policy-binding $PROJECT_ID \
  --member="serviceAccount:$SERVICE_ACCOUNT_NAME" \
  --role="roles/logging.viewer"


👉 Valide o resultado no console do IAMCloud Shell

👉💻 Execute os comandos a seguir no terminal para criar um repositório do Artifact Registry. Todas as imagens do Docker para nossos agentes, o servidor MCP e o aplicativo InstaVibe são armazenadas aqui antes da implantação no Cloud Run ou no Agent Engine.

export REPO_NAME="introveally-repo"
gcloud artifacts repositories create $REPO_NAME \
  --repository-format=docker \
  --location=us-central1 \
  --description="Docker repository for InstaVibe workshop"

Configurar a plataforma de mapas para chaves de API

Para usar os serviços do Google Maps no seu aplicativo InstaVibe, crie uma chave de API e restrinja o uso dela de acordo com suas necessidades.

👉 Em uma nova guia, acesse APIs e serviços > Credenciais. Na página "Credenciais", clique no botão + CRIAR CREDENCIAIS na parte de cima. Selecione "Chave de API" no menu suspenso. texto alternativo

👉 Uma caixa de diálogo vai aparecer mostrando a chave de API recém-criada. Você vai precisar dele mais tarde para a configuração do aplicativo.

👉 Clique em FECHAR na caixa de diálogo "Chave de API criada".

👉 Sua nova chave de API vai aparecer na lista (por exemplo, "API key 1"). Clique nos três pontos à direita e selecione Editar chave de API para abrir a página "Restringir e renomear chave de API". texto alternativo

👉 No campo "Nome" na parte de cima, mude o nome padrão para: Chave de API da Plataforma Google Maps (🚨🚨IMPORTANTE🚨🚨 Use esse nome!)

Maps Platform API Key

👉 Na seção "Restrições de aplicativo", confira se a opção Nenhuma está selecionada.

👉 Na seção "Restrições de API", selecione o botão de opção "Restringir chave".

👉 Clique no menu suspenso "Selecionar APIs". Na caixa de pesquisa que aparece, digite Maps JavaScript API e selecione na lista. texto alternativo

👉 Clique em "OK".

👉 Clique no botão SALVAR na parte de baixo da página.

Resultado principal

Você criou uma chave de API chamada "Chave de API da Plataforma Google Maps", restringiu o uso dela apenas à "API Maps JavaScript" e garantiu que a API esteja ativada para seu projeto.

4. Configurar banco de dados de gráficos

Antes de criar nossos agentes inteligentes, precisamos de uma maneira de armazenar e entender as conexões avançadas na nossa rede social InstaVibe. É aí que entra um banco de dados de grafos. Ao contrário dos bancos de dados relacionais tradicionais, que armazenam dados em tabelas de linhas e colunas, um banco de dados de grafo é projetado especificamente para representar e consultar dados em termos de nós (como pessoas, eventos ou postagens) e os relacionamentos (arestas) que os conectam (como amizades, participação em eventos ou menções). Essa estrutura é muito útil para aplicativos de redes sociais porque reflete a maneira como as redes sociais do mundo real são estruturadas, tornando intuitivo explorar como diferentes entidades estão interconectadas.

Estamos implementando esse banco de dados de grafos usando o Google Cloud Spanner. Embora o Spanner seja conhecido principalmente como um banco de dados relacional distribuído globalmente e de consistência forte, ele também permite definir e consultar estruturas de gráficos diretamente nas tabelas relacionais.

Isso nos dá os benefícios combinados da escalonabilidade, da consistência transacional e da interface SQL familiar do Spanner, além do poder expressivo das consultas de gráficos para analisar a dinâmica social complexa, crucial para nossos recursos com tecnologia de IA.

👉💻 No terminal do Cloud Shell IDE. Provisione a infraestrutura necessária no Google Cloud. Vamos começar criando uma instância do Spanner, que funciona como um contêiner dedicado para nossos bancos de dados. Quando a instância estiver pronta, vamos criar o banco de dados do Spanner nela, que vai hospedar todas as nossas tabelas e os dados de gráfico do InstaVibe:

. ~/instavibe-bootstrap/set_env.sh

gcloud spanner instances create $SPANNER_INSTANCE_ID \
  --config=regional-us-central1 \
  --description="GraphDB Instance InstaVibe" \
  --processing-units=100 \
  --edition=ENTERPRISE

gcloud spanner databases create $SPANNER_DATABASE_ID \
  --instance=$SPANNER_INSTANCE_ID \
  --database-dialect=GOOGLE_STANDARD_SQL

👉💻 Conceder acesso de leitura/gravação do Spanner à conta de serviço padrão

echo "Granting Spanner read/write access to ${SERVICE_ACCOUNT_NAME} for database ${SPANNER_DATABASE_ID}..."

gcloud spanner databases add-iam-policy-binding ${SPANNER_DATABASE_ID} \
  --instance=${SPANNER_INSTANCE_ID} \
  --member="serviceAccount:${SERVICE_ACCOUNT_NAME}" \
  --role="roles/spanner.databaseUser" \
  --project=${PROJECT_ID}

👉💻 Agora. Vamos configurar um ambiente virtual Python, instalar os pacotes Python necessários e, em seguida, configurar o esquema do banco de dados de gráficos no Spanner, carregar dados iniciais e executar o script setup.py.

. ~/instavibe-bootstrap/set_env.sh
cd ~/instavibe-bootstrap
python -m venv env
source env/bin/activate
pip install -r requirements.txt
cd instavibe
python setup.py

👉 Em uma nova guia do navegador, acesse o console do Google Cloud, navegue até Spanner e confira uma lista das suas instâncias do Spanner. Clique em instavibe-graph-instance. instância do Spanner 👉 Na página de visão geral da instância, você vai encontrar uma lista de bancos de dados nela. Clique em graphdbspanner db.

👉 No painel de navegação à esquerda do banco de dados, clique em Spanner Studio spanner studio.

👉 No editor de consultas (guia "Consulta sem título"), cole a seguinte consulta do Graph SQL. Essa consulta vai encontrar todos os nós "Person" e as relações diretas de amizade com outros nós "Person". Clique em EXECUTAR para ver o resultado.

Graph SocialGraph
MATCH result_paths = ((p:Person)-[f:Friendship]-(friend:Person))
RETURN SAFE_TO_JSON(result_paths) AS result_paths

gráfico do Spanner

👉 No mesmo editor de consultas, substitua a DDL anterior para encontrar pessoas que participaram do mesmo evento, o que implica uma conexão indireta por uma atividade compartilhada.

Graph SocialGraph
MATCH result_paths =  (p1:Person)-[:Attended]->(e:Event)<-[:Attended]-(p2:Person)
WHERE p1.person_id < p2.person_id
RETURN SAFE_TO_JSON(result_paths) AS result_paths

gráfico do Spanner

👉 Essa consulta explora um tipo diferente de conexão, em que as pessoas mencionadas em postagens escritas por amigos de uma pessoa específica executam a seguinte consulta no editor de consultas.

Graph SocialGraph
MATCH result_paths =  (user:Person {name: "Alice"})-[:Friendship]-(friend:Person)-[:Wrote]->(post:Post)-[:Mentioned]->(mentioned_person:Person)
WHERE user <> mentioned_person AND friend <> mentioned_person -- Avoid self-mentions or friend mentioning themselves in their own post if not intended
RETURN SAFE_TO_JSON(result_paths) AS result_paths

gráfico do Spanner

Essas consultas oferecem apenas uma visão geral do poder de usar o Spanner como um banco de dados de gráficos para nosso aplicativo InstaVibe. Ao modelar nossos dados sociais como um gráfico interconectado, permitimos uma análise sofisticada de relacionamentos e atividades, o que será fundamental para que nossos agentes de IA entendam o contexto do usuário, descubram interesses e, por fim, ofereçam assistência inteligente no planejamento social.

Agora que nossa estrutura de dados fundamental está no lugar e foi testada, vamos nos concentrar no aplicativo InstaVibe atual com que nossos agentes vão interagir.

5. Estado atual do InstaVibe

Para entender onde nossos agentes de IA se encaixam, primeiro precisamos implantar e executar o aplicativo da Web InstaVibe atual. Esse aplicativo fornece a interface do usuário e as funcionalidades básicas que se conectam ao banco de dados de gráficos do Spanner que já configuramos.

página inicial

O aplicativo InstaVibe usa o Google Maps para mostrar os locais dos eventos de forma visual nas páginas de detalhes. Para ativar essa funcionalidade, o aplicativo precisa da chave de API que criamos anteriormente. O script a seguir vai recuperar a string de chave real usando o nome de exibição que atribuímos ("Chave de API da Plataforma Google Maps").

página do evento

👉💻 Volte para o Cloud Shell IDE. Execute o script abaixo. Depois, verifique com atenção a saída para garantir que a GOOGLE_MAPS_API_KEY mostrada corresponda à chave criada e copiada do console do Google Cloud anteriormente.

. ~/instavibe-bootstrap/set_env.sh
export KEY_DISPLAY_NAME="Maps Platform API Key"

GOOGLE_MAPS_KEY_ID=$(gcloud services api-keys list \
  --project="${PROJECT_ID}" \
  --filter="displayName='${KEY_DISPLAY_NAME}'" \
  --format="value(uid)" \
  --limit=1)

GOOGLE_MAPS_API_KEY=$(gcloud services api-keys get-key-string "${GOOGLE_MAPS_KEY_ID}" \
    --project="${PROJECT_ID}" \
    --format="value(keyString)")

echo "${GOOGLE_MAPS_API_KEY}" > ~/mapkey.txt

echo "Retrieved GOOGLE_MAPS_API_KEY: ${GOOGLE_MAPS_API_KEY}"

resultado principal

👉💻 Agora, vamos criar a imagem do contêiner para o aplicativo Web InstaVibe e enviá-la para o repositório do Artifact Registry.

. ~/instavibe-bootstrap/set_env.sh

cd ~/instavibe-bootstrap/instavibe/
export IMAGE_TAG="latest"
export APP_FOLDER_NAME="instavibe"
export IMAGE_NAME="instavibe-webapp"
export IMAGE_PATH="${REGION}-docker.pkg.dev/${PROJECT_ID}/${REPO_NAME}/${IMAGE_NAME}:${IMAGE_TAG}"
export SERVICE_NAME="instavibe"

gcloud builds submit . \
  --tag=${IMAGE_PATH} \
  --project=${PROJECT_ID}

👉💻 Implante a nova imagem do web app InstaVibe no Cloud Run

. ~/instavibe-bootstrap/set_env.sh

cd ~/instavibe-bootstrap/instavibe/
export IMAGE_TAG="latest"
export APP_FOLDER_NAME="instavibe"
export IMAGE_NAME="instavibe-webapp"
export IMAGE_PATH="${REGION}-docker.pkg.dev/${PROJECT_ID}/${REPO_NAME}/${IMAGE_NAME}:${IMAGE_TAG}"
export SERVICE_NAME="instavibe"

gcloud run deploy ${SERVICE_NAME} \
  --image=${IMAGE_PATH} \
  --platform=managed \
  --region=${REGION} \
  --allow-unauthenticated \
  --set-env-vars="SPANNER_INSTANCE_ID=${SPANNER_INSTANCE_ID}" \
  --set-env-vars="SPANNER_DATABASE_ID=${SPANNER_DATABASE_ID}" \
  --set-env-vars="APP_HOST=0.0.0.0" \
  --set-env-vars="APP_PORT=8080" \
  --set-env-vars="GOOGLE_CLOUD_LOCATION=${REGION}" \
  --set-env-vars="GOOGLE_CLOUD_PROJECT=${PROJECT_ID}" \
  --set-env-vars="GOOGLE_MAPS_API_KEY=${GOOGLE_MAPS_API_KEY}" \
  --project=${PROJECT_ID} \
  --min-instances=1

Com a implantação concluída, os registros do Cloud Run vão mostrar o URL público do aplicativo InstaVibe em execução.

URL

Você também pode encontrar esse URL navegando até a seção Cloud Run no Console do Google Cloud e selecionando o serviço instavibe. ListaURL

Abra esse URL no navegador da Web agora para conhecer a plataforma básica do InstaVibe. Confira as postagens, os eventos e as conexões de usuários gerados pelo banco de dados de grafo que configuramos.

Agora que o aplicativo de destino está em execução, vamos começar a criar o primeiro agente inteligente para melhorar as funcionalidades dele.

6. Agente básico,planejador de eventos com ADK

Framework ADK

Introdução ao framework ADK do Google: agora que nossa base (o app e o banco de dados InstaVibe) está definida, podemos começar a criar nosso primeiro agente inteligente usando o Agent Development Kit (ADK) do Google.

O Agent Development Kit (ADK) é um framework flexível e modular criado especificamente para desenvolver e implantar agentes de IA. O princípio de design é fazer com que o desenvolvimento de agentes pareça mais com o desenvolvimento de software tradicional, com o objetivo de facilitar significativamente a criação, a implantação e a organização de arquiteturas de agentes que podem lidar com tudo, desde tarefas simples e de propósito único até fluxos de trabalho complexos e multiagente.

Basicamente, o ADK gira em torno do conceito de um Agent, que encapsula instruções, configuração (como o modelo de linguagem escolhido, por exemplo, Gemini) e um conjunto de Tools que ele pode usar para realizar ações ou coletar informações.

06-agent.png

Nosso agente inicial será um "Organizador de eventos". O objetivo principal é receber pedidos de passeios sociais dos usuários (especificando local, datas e interesses) e gerar sugestões criativas e personalizadas. Para garantir que as sugestões sejam relevantes e baseadas em informações atuais (como eventos específicos que acontecem naquele fim de semana), vamos usar uma das ferramentas integradas do ADK: a Pesquisa Google. Isso permite que o agente baseie as respostas em resultados da Web em tempo real, buscando os detalhes mais recentes sobre locais, eventos e atividades que correspondem aos critérios do usuário.

👉📝 De volta ao IDE do Cloud Shell, em ~/instavibe-bootstrap/agents/planner/agent.py, adicione o seguinte comando e instrução para criar o agente:

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

root_agent = Agent(
    name="planner_agent",
    model="gemini-2.0-flash",
    description="Agent tasked with generating creative and fun dating plan suggestions",
    instruction="""

        You are a specialized AI assistant tasked with generating creative and fun plan suggestions.

        Request:
        For the upcoming weekend, specifically from **[START_DATE_YYYY-MM-DD]** to **[END_DATE_YYYY-MM-DD]**, in the location specified as **[TARGET_LOCATION_NAME_OR_CITY_STATE]** (if latitude/longitude are provided, use these: Lat: **[TARGET_LATITUDE]**, Lon: **[TARGET_LONGITUDE]**), please generate a distinct dating plan suggestions.

        Constraints and Guidelines for Suggestions:
        1.  Creativity & Fun: Plans should be engaging, memorable, and offer a good experience for a date.
        2.  Budget: All generated plans should aim for a moderate budget (conceptually "$$"), meaning they should be affordable yet offer good value, without being overly cheap or extravagant. This budget level should be *reflected in the choice of activities and venues*, but **do not** explicitly state "Budget: $$" in the `plan_description`.
        3.  Interest Alignment:
               Consider the following user interests: **[COMMA_SEPARATED_LIST_OF_INTERESTS, e.g., outdoors, arts & culture, foodie, nightlife, unique local events, live music, active/sports]**. Tailor suggestions specifically to these where possible. The plan should *embody* these interests.
               Fallback: If specific events or venues perfectly matching all listed user interests cannot be found for the specified weekend, you should create a creative and fun generic dating plan that is still appealing, suitable for the location, and adheres to the moderate budget. This plan should still sound exciting and fun, even if it's more general.
        4.  Current & Specific: Prioritize finding specific, current events, festivals, pop-ups, or unique local venues operating or happening during the specified weekend dates. If exact current events cannot be found, suggest appealing evergreen options or implement the fallback generic plan.
        5.  Location Details: For each place or event mentioned within a plan, you MUST provide its name, precise latitude, precise longitude, and a brief, helpful description.
        6.  Maximum Activities: The plan must contain a maximum of 3 distinct activities.

        RETURN PLAN in MARKDOWN FORMAT 
    """,
    tools=[google_search]
)

E esse é o nosso primeiro agente definido. Uma das melhores coisas sobre o ADK é a natureza intuitiva e as ferramentas úteis que ele oferece. Uma delas é a interface de desenvolvimento do ADK, que permite testar seu agente de forma interativa e ver as respostas dele em tempo real.

👉💻 Vamos começar. Os comandos a seguir vão iniciar a interface de desenvolvimento do ADK:

. ~/instavibe-bootstrap/set_env.sh
source ~/instavibe-bootstrap/env/bin/activate
cd  ~/instavibe-bootstrap/agents
sed -i "s|^\(O\?GOOGLE_CLOUD_PROJECT\)=.*|GOOGLE_CLOUD_PROJECT=${PROJECT_ID}|" ~/instavibe-bootstrap/agents/planner/.env
adk web

Depois de executar os comandos, você vai ver uma saída no terminal indicando que o servidor da Web do ADK foi iniciado, 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 de desenvolvimento do ADK no navegador:

No ícone Visualização da Web (geralmente um olho ou um quadrado com uma seta) na barra de ferramentas do Cloud Shell (geralmente no canto superior direito), 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 ou janela do navegador mostrando a interface de desenvolvimento do ADK.

visualização na Web

Depois que a interface de desenvolvimento do ADK for aberta no navegador: no menu suspenso no canto superior direito da interface, selecione planner como o agente com que você quer interagir. Agora, na caixa de diálogo de chat à direita, tente dar uma tarefa ao seu agente. Por exemplo, converse com o agente:

Search and plan something in Seattle for me this weekend
This weekend and I enjoy food and anime

Sugerir uma data (sua preferência)

July 12 2025

O agente vai processar sua solicitação e fornecer um plano com base nos resultados da Pesquisa Google.

adk dev ui

Interagir com um agente é uma coisa, mas como saber se ele está se comportando de maneira consistente como esperado, principalmente quando fazemos mudanças?

Os métodos tradicionais de teste de software geralmente não são adequados para agentes de IA devido à natureza generativa e não determinista deles. Para diminuir a distância entre uma demonstração legal e um agente de produção confiável, é essencial ter uma estratégia de avaliação sólida. Ao contrário de simplesmente verificar a saída final de um modelo generativo, avaliar um agente geralmente envolve analisar o processo de tomada de decisões e a capacidade de usar ferramentas ou seguir instruções corretamente em vários cenários. O ADK oferece recursos para ajudar nisso.

Avaliação

👉 Na interface de desenvolvimento do ADK, clique na guia "Eval" na navegação à esquerda. Você vai encontrar um arquivo de teste pré-carregado chamado plan_eval. Esse arquivo contém entradas e critérios predefinidos para testar nosso agente de planejamento.

👉 Selecione um cenário, como "boston", e clique no botão Executar avaliação. Na janela pop-up que aparece, diminua a pontuação de correspondência para 0,3 e clique em "Iniciar".

Pontuação de correspondência

Isso vai executar o agente com a entrada de teste e verificar se a saída atende às expectativas definidas. Assim, você pode testar a performance do seu agente de forma sistemática.

adk dev ui evaluation

👉 Agora, vamos ver o que acontece com um limite mais restrito. Selecione o cenário "nyc" e clique em Executar avaliação novamente. Desta vez, deixe a pontuação de correspondência no valor padrão (Pontuação de correspondência da resposta: 0,7) e clique em "Iniciar". Você vai notar que o resultado é "Falha". Isso é esperado, já que a saída criativa do agente não corresponde perfeitamente à resposta "ideal" predefinida.

adk dev ui evaluation fail

👉 Para entender por que ele falhou, clique no ícone de falha na linha "nyc". A interface agora mostra uma comparação lado a lado da resposta real do agente e da resposta esperada do caso de teste. Essa visualização é essencial para a depuração, permitindo que você veja exatamente onde a saída do agente divergiu e refine as instruções de acordo.

Depois de explorar a interface e a avaliação, volte ao terminal do Cloud Shell Editor e pressione Ctrl+C para interromper a interface de desenvolvimento do ADK.

Embora a saída de texto livre seja um bom começo, para que aplicativos como o InstaVibe usem facilmente as sugestões de um agente, os dados estruturados (como JSON) são muito mais práticos. Vamos modificar nosso agente para retornar o plano dele em um formato JSON consistente.

👉📝 No ~/instavibe-bootstrap/agents/planner/agent.py, encontre a linha que atualmente diz RETURN PLAN in MARKDOWN FORMAT na string de instrução do agente. Substitua essa linha pela seguinte estrutura JSON detalhada:

Return your response *exclusively* as a single JSON object. This object should contain a top-level key, "fun_plans", which holds a plan objects. Each plan object in the list must strictly adhere to the following structure:

        --json--
        {
          "plan_description": "A summary of the overall plan, consisting of **exactly three sentences**. Craft these sentences in a friendly, enthusiastic, and conversational tone, as if you're suggesting this awesome idea to a close friend. Make it sound exciting and personal, highlighting the positive aspects and appeal of the plan without explicitly mentioning budget or listing interest categories.",
          "locations_and_activities": [
              {
              "name": "Name of the specific place or event",
              "latitude": 0.000000,  // Replace with actual latitude
              "longitude": 0.000000, // Replace with actual longitude
              "description": "A brief description of this place/event, why it's suitable for the date, and any specific details for the weekend (e.g., opening hours, event time)."
              }
              // Add more location/activity objects here if the plan involves multiple stops/parts
          ]
        }

Agora que você atualizou as instruções do agente para solicitar especificamente a saída JSON, vamos verificar a mudança.

👉💻 Reinicie a interface de desenvolvimento do ADK usando o mesmo comando de antes:

. ~/instavibe-bootstrap/set_env.sh
source ~/instavibe-bootstrap/env/bin/activate
cd  ~/instavibe-bootstrap/agents
adk web

Atualize a guia se ela já estiver aberta. Ou siga as mesmas etapas anteriores para abrir a interface de desenvolvimento do ADK no navegador (pela visualização da Web do Cloud Shell na porta 8000). Depois que a interface for carregada, verifique se o agente de planejamento está selecionado.

👉 Desta vez, vamos fazer uma solicitação diferente. Na caixa de diálogo do chat, digite:

Plan an event Boston this weekend with art and coffee

Analise a resposta do agente com cuidado. Em vez de uma resposta de texto puramente conversacional, agora você vai receber uma resposta formatada estritamente como um objeto JSON, correspondendo à estrutura definida nas instruções (contendo fun_plans, plan_description, locations_and_activities etc.). Isso confirma que o agente agora pode produzir uma saída estruturada adequada para uso programático pelo nosso aplicativo InstaVibe.

adk dev ui json

Depois de confirmar a saída JSON, volte ao terminal do Cloud Shell e pressione Ctrl+C para interromper a interface de desenvolvimento do ADK.

Componentes do ADK

Embora a interface de desenvolvimento do ADK seja ótima para testes interativos, muitas vezes precisamos executar nossos agentes de maneira programática, talvez como parte de um aplicativo maior ou serviço de back-end. Para entender como isso funciona, vamos analisar alguns conceitos principais do ADK relacionados ao tempo de execução e ao gerenciamento de contexto.

Para ter conversas significativas com várias rodadas, os agentes precisam entender o contexto, ou seja, lembrar do que foi dito e feito para manter a continuidade. O ADK oferece maneiras estruturadas de gerenciar esse contexto usando Session, State e Memory:

  • Sessão:quando um usuário começa a interagir com um agente, uma sessão é criada. Pense nele como o contêiner de uma única conversa específica. Ele contém um ID exclusivo, o histórico de interações (eventos), os dados de trabalho atuais (estado) e metadados, como o último horário de atualização.
  • Estado:é a memória de trabalho de curto prazo do agente em uma única sessão. É um dicionário mutável em que o agente pode armazenar informações temporárias necessárias para concluir a tarefa atual (por exemplo, preferências do usuário coletadas até o momento, resultados intermediários de chamadas de ferramentas).
  • Memória:representa o potencial do agente para recordar informações a longo prazo em diferentes sessões ou acessar bases de conhecimento externas. Enquanto a sessão e o estado lidam com a conversa imediata, a memória (geralmente gerenciada por um MemoryService) permite que um agente recupere informações de interações passadas ou fontes de dados estruturadas, oferecendo um contexto de conhecimento mais amplo. Observação: nosso cliente simples usa serviços na memória para facilitar, ou seja, a memória/o estado só persistem enquanto o script é executado.
  • Evento:cada interação em uma sessão (mensagem do usuário, resposta do agente, solicitação de uso da ferramenta, resultado da ferramenta, mudança de estado, erro) é registrada como um evento imutável. Isso cria um registro cronológico, essencialmente o histórico de transcrição e ações da conversa.

Então, como eles são gerenciados quando um agente é executado? Essa é a função do Runner.

  • Runner: o Runner é o mecanismo de execução principal fornecido pelo ADK. Você define o agente e as ferramentas que ele usa, e o Runner organiza o processo de atendimento ao pedido de um usuário. Ele gerencia a sessão, processa o fluxo de eventos, atualiza o estado, invoca o modelo de linguagem subjacente, coordena as chamadas de ferramentas e pode interagir com o MemoryService. Imagine que ele é o maestro, garantindo que todas as partes diferentes funcionem juntas corretamente.

Podemos usar o Runner para executar nosso agente como um aplicativo Python independente, completamente independente da interface de desenvolvimento.

Vamos criar um script do cliente simples para invocar nosso agente de planejamento de maneira programática.

👉📝 No arquivo ~/instavibe-bootstrap/agents/planner/planner_client.py, adicione o seguinte código Python abaixo das importações atuais. Em planner_client.py, abaixo das importações, adicione o seguinte:

async def async_main():
  session_service = InMemorySessionService()

  session = await session_service.create_session(
      state={}, app_name='planner_app', user_id='user_dc'
  )

  query = "Plan Something for me in San Francisco this weekend on wine and fashion "
  print(f"User Query: '{query}'")
  content = types.Content(role='user', parts=[types.Part(text=query)])

  root_agent = agent.root_agent
  runner = Runner(
        app_name='planner_app',
        agent=root_agent,
        session_service=session_service,
  )
  print("Running agent...")
  events_async =  runner.run_async(
    session_id=session.id, user_id=session.user_id, new_message=content
  )

  async for event in events_async:
    print(f"Event received: {event}")


if __name__ == '__main__':
  try:
    asyncio.run(async_main())
  except Exception as e:
    print(f"An error occurred: {e}")

Esse código configura serviços na memória para gerenciamento de sessões e artefatos (mantendo a simplicidade para este exemplo), cria uma sessão, define uma consulta do usuário, configura o Runner com nosso agente e executa o agente de forma assíncrona, imprimindo cada evento gerado durante a execução.

👉💻 Agora, execute este script do cliente no seu terminal:

. ~/instavibe-bootstrap/set_env.sh
source ~/instavibe-bootstrap/env/bin/activate
cd  ~/instavibe-bootstrap/agents
python -m planner.planner_client

👀 Observe a saída. Em vez de apenas o plano JSON final, você vai ver a estrutura detalhada de cada objeto de evento gerado durante o fluxo de execução do agente. Isso inclui o evento de mensagem inicial do usuário, possíveis eventos relacionados a chamadas de ferramentas (como a Pesquisa Google) e, por fim, o evento de resposta do modelo que contém o plano JSON. Esse fluxo de eventos detalhado é muito útil para depurar e entender o processamento gradual que acontece no ambiente de execução do ADK.

Running agent...
Event received: content=Content(parts=[Part(video_metadata=None, thought=None, code_execution_result=None, executable_code=None, file_data=None, function_call=None, function_response=None, inline_data=None, text='```json\n{\n "fun_plans": [\n  {\n   "plan_description": "Embark on a stylish adventure through Hayes Valley, 
...(turncated)
, offering a variety of fashion styles to browse and enjoy."\n    }\n   ]\n  }\n ]\n}\n```')], role='model') grounding_metadata=GroundingMetadata(grounding_chunks=[GroundingChunk(retrieved_context=None, web=GroundingChunkWeb(domain='islands.com', title='islands.com', uri='http
...(turncated)
QyTpPV7jS6wUt-Ix7GuP2mC9J4eY_8Km6Vv44liF9cb2VSs='))], grounding_supports=[GroundingSupport(confide
...(turncated)
>\n', sdk_blob=None), web_search_queries=['..e']) partial=None turn_complete=None error_code=None error_message=None interrupted=None custom_metadata=None invocation_id='e-04d97b8b-9021-47a5-ab41-17b5cbb4bf03' author='location_search_agent' actions=EventActions(skip_summarization=None, state_delta={}, artifact_delta={}, transfer_to_agent=None, escalate=None, requested_auth_configs={}) long_running_tool_ids=None branch=None id='CInHdkKw' timestamp=1746978846.232674

Se o script for executado continuamente ou ficar travado, talvez seja necessário interrompê-lo manualmente pressionando Ctrl+C.

7. Agente de interação com a plataforma: interage com o servidor MCP.

Embora o ADK ajude a estruturar nossos agentes, eles geralmente precisam interagir com sistemas ou APIs externos para realizar ações no mundo real.

Protocolo de Contexto de Modelo (MCP)

O Protocolo de contexto de modelo (MCP) é um padrão aberto criado para padronizar como aplicativos de IA, como agentes, se conectam a fontes de dados, ferramentas e sistemas externos. O objetivo é resolver o problema de precisar de integrações personalizadas para cada combinação de aplicativo de IA e fonte de dados, oferecendo uma interface universal. O MCP usa uma arquitetura cliente-servidor em que os clientes MCP, que residem em aplicativos de IA (hosts), gerenciam conexões com servidores MCP. Esses servidores são programas externos que expõem funcionalidades específicas, como acesso a dados locais, interação com serviços remotos via APIs ou fornecimento de comandos predefinidos. Isso permite que os modelos de IA acessem informações atuais e realizem tarefas além do treinamento inicial. Essa estrutura permite que os modelos de IA descubram e interajam com recursos externos de maneira padronizada, tornando as integrações mais simples e escalonáveis.

Criar e implantar o servidor MCP do InstaVibe

07-mcp-server.png

Nossos agentes vão precisar interagir com a plataforma InstaVibe.Especificamente, para criar postagens e registrar eventos usando as APIs atuais da plataforma. O aplicativo InstaVibe já expõe essas funcionalidades por endpoints HTTP padrão:

Endpoint

URL

Método HTTP

Descrição

Criar postagem

api/posts

POSTAR

Endpoint da API para adicionar uma nova postagem. Espera corpo JSON:
{"author_name": "...", "text": "...", "sentiment": "..." (optional)}

Criar evento

api/events

POSTAR

Endpoint da API para adicionar um novo evento e os participantes dele (esquema simplificado).
Espera corpo JSON: { "event_name": "...", "description": "...", "event_date": "YYYY-MM-DDTHH:MM:SSZ", "locations": [ {"name": "...", "description": "...", "latitude": 0.0, "longitude": 0.0, "address": "..."} ], "attendee_names": ["...", "..."] }

Para disponibilizar essas funcionalidades aos nossos agentes via MCP, primeiro precisamos criar funções simples do Python que atuem como wrappers dessas chamadas de API. Essas funções vão processar a lógica de solicitação HTTP.

👉 Primeiro, vamos implementar a função de wrapper para criar uma postagem. Abra o arquivo ~/instavibe-bootstrap/tools/instavibe/instavibe.py e substitua o comentário #REPLACE ME CREATE POST pelo seguinte código Python:

def create_post(author_name: str, text: str, sentiment: str, base_url: str = BASE_URL):
    """
    Sends a POST request to the /posts endpoint to create a new post.

    Args:
        author_name (str): The name of the post's author.
        text (str): The content of the post.
        sentiment (str): The sentiment associated with the post (e.g., 'positive', 'negative', 'neutral').
        base_url (str, optional): The base URL of the API. Defaults to BASE_URL.

    Returns:
        dict: The JSON response from the API if the request is successful.
              Returns None if an error occurs.

    Raises:
        requests.exceptions.RequestException: If there's an issue with the network request (e.g., connection error, timeout).
    """
    url = f"{base_url}/posts"
    headers = {"Content-Type": "application/json"}
    payload = {
        "author_name": author_name,
        "text": text,
        "sentiment": sentiment
    }

    try:
        response = requests.post(url, headers=headers, json=payload)
        response.raise_for_status()  # Raise an exception for bad status codes (4xx or 5xx)
        print(f"Successfully created post. Status Code: {response.status_code}")
        return response.json()
    except requests.exceptions.RequestException as e:
        print(f"Error creating post: {e}")
        # Optionally re-raise the exception if the caller needs to handle it
        # raise e
        return None
    except json.JSONDecodeError:
        print(f"Error decoding JSON response from {url}. Response text: {response.text}")
        return None

👉📝 Em seguida, vamos criar a função wrapper para a API de criação de eventos. No mesmo arquivo ~/instavibe-bootstrap/tools/instavibe/instavibe.py, substitua o comentário #REPLACE ME CREATE EVENTS por este código:

def create_event(event_name: str, description: str, event_date: str, locations: list, attendee_names: list[str], base_url: str = BASE_URL):
    """
    Sends a POST request to the /events endpoint to create a new event registration.

    Args:
        event_name (str): The name of the event.
        description (str): The detailed description of the event.
        event_date (str): The date and time of the event (ISO 8601 format recommended, e.g., "2025-06-10T09:00:00Z").
        locations (list): A list of location dictionaries. Each dictionary should contain:
                          'name' (str), 'description' (str, optional),
                          'latitude' (float), 'longitude' (float),
                          'address' (str, optional).
        attendee_names (list[str]): A list of names of the people attending the event.
        base_url (str, optional): The base URL of the API. Defaults to BASE_URL.

    Returns:
        dict: The JSON response from the API if the request is successful.
              Returns None if an error occurs.

    Raises:
        requests.exceptions.RequestException: If there's an issue with the network request (e.g., connection error, timeout).
    """
    url = f"{base_url}/events"
    headers = {"Content-Type": "application/json"}
    payload = {
        "event_name": event_name,
        "description": description,
        "event_date": event_date,
        "locations": locations,
        "attendee_names": attendee_names,
    }

    try:
        response = requests.post(url, headers=headers, json=payload)
        response.raise_for_status()  # Raise an exception for bad status codes (4xx or 5xx)
        print(f"Successfully created event registration. Status Code: {response.status_code}")
        return response.json()
    except requests.exceptions.RequestException as e:
        print(f"Error creating event registration: {e}")
        # Optionally re-raise the exception if the caller needs to handle it
        # raise e
        return None
    except json.JSONDecodeError:
        print(f"Error decoding JSON response from {url}. Response text: {response.text}")
        return None

Como você pode ver, essas funções são wrappers simples das APIs InstaVibe atuais. Esse padrão é útil se você já tem APIs para seus serviços. É fácil expor a funcionalidade delas como ferramentas para agentes criando esses wrappers.

Implementação do servidor MCP

Agora que temos as funções Python que executam as ações (chamando as APIs do InstaVibe), precisamos criar o componente do servidor MCP. Esse servidor vai expor essas funções como "ferramentas" de acordo com o padrão MCP, permitindo que clientes MCP (como nossos agentes) as descubram e invoquem.

Um servidor MCP geralmente implementa duas funcionalidades principais:

  • list_tools: responsável por permitir que o cliente descubra as ferramentas disponíveis no servidor, fornecendo metadados como nomes, descrições e parâmetros obrigatórios, geralmente definidos usando o esquema JSON.
  • call_tool: processa a execução de uma ferramenta específica solicitada pelo cliente, recebendo o nome e os argumentos dela e realizando a ação correspondente, como no nosso caso, interagindo com uma API.

Os servidores MCP são usados para dar aos modelos de IA acesso a dados e ações do mundo real, permitindo tarefas como enviar e-mails, criar tarefas em sistemas de gerenciamento de projetos, pesquisar bancos de dados ou interagir com vários softwares e serviços da Web. Embora as implementações iniciais geralmente se concentrem em servidores locais que se comunicam via entrada/saída (stdio) padrão por simplicidade, principalmente em ambientes de desenvolvimento ou "estúdio", a mudança para servidores remotos que utilizam protocolos como HTTP com eventos enviados pelo servidor (SSE) faz mais sentido para uma adoção mais ampla e casos de uso corporativos.

A arquitetura remota, apesar da camada adicional de comunicação de rede, oferece vantagens significativas: permite que vários clientes de IA compartilhem o acesso a um único servidor, centraliza o gerenciamento e as atualizações de ferramentas, aumenta a segurança mantendo dados sensíveis e chaves de API no lado do servidor em vez de distribuídos por várias máquinas cliente em potencial e desacopla o modelo de IA das especificidades da integração do sistema externo, tornando todo o ecossistema mais escalonável, seguro e fácil de manter do que exigir que cada instância de IA gerencie as próprias integrações diretas.

07-mcp-server.png

Vamos implementar nosso servidor MCP usando HTTP e eventos enviados pelo servidor (SSE, na sigla em inglês) para comunicação, o que é adequado para execuções de ferramentas potencialmente longas e cenários empresariais.

👉📝 Primeiro, vamos implementar o endpoint list_tools. Abra o arquivo ~/instavibe-bootstrap/tools/instavibe/mcp_server.py e substitua o comentário #REPLACE ME - LIST TOOLS pelo código a seguir. :

@app.list_tools()
async def list_tools() -> list[mcp_types.Tool]:
  """MCP handler to list available tools."""
  # Convert the ADK tool's definition to MCP format
  mcp_tool_schema_event = adk_to_mcp_tool_type(event_tool)
  mcp_tool_schema_post = adk_to_mcp_tool_type(post_tool)
  print(f"MCP Server: Received list_tools request. \n MCP Server: Advertising tool: {mcp_tool_schema_event.name} and {mcp_tool_schema_post}")
  return [mcp_tool_schema_event,mcp_tool_schema_post]

Essa função define as ferramentas (create_event, create_post) e informa os clientes conectados sobre elas.

👉📝 Em seguida, implemente o endpoint call_tool, que processa as solicitações de execução reais dos clientes. No mesmo arquivo ~/instavibe-bootstrap/tools/instavibe/mcp_server.py, substitua o comentário #REPLACE ME - CALL TOOLS por este código.

@app.call_tool()
async def call_tool(
    name: str, arguments: dict
) -> list[mcp_types.TextContent | mcp_types.ImageContent | mcp_types.EmbeddedResource]:
  """MCP handler to execute a tool call."""
  print(f"MCP Server: Received call_tool request for '{name}' with args: {arguments}")

  # Look up the tool by name in our dictionary
  tool_to_call = available_tools.get(name)
  if tool_to_call:
    try:
      adk_response = await tool_to_call.run_async(
          args=arguments,
          tool_context=None, # No ADK context available here
      )
      print(f"MCP Server: ADK tool '{name}' executed successfully.")
      
      response_text = json.dumps(adk_response, indent=2)
      return [mcp_types.TextContent(type="text", text=response_text)]

    except Exception as e:
      print(f"MCP Server: Error executing ADK tool '{name}': {e}")
      # Creating a proper MCP error response might be more robust
      error_text = json.dumps({"error": f"Failed to execute tool '{name}': {str(e)}"})
      return [mcp_types.TextContent(type="text", text=error_text)]
  else:
      # Handle calls to unknown tools
      print(f"MCP Server: Tool '{name}' not found.")
      error_text = json.dumps({"error": f"Tool '{name}' not implemented."})
      return [mcp_types.TextContent(type="text", text=error_text)]

Essa função recebe o nome e os argumentos da ferramenta, encontra a função wrapper correspondente em Python que definimos anteriormente, a executa e retorna o resultado.

👉💻 Com a lógica do servidor MCP definida, agora precisamos empacotá-la como um contêiner. No terminal, execute o script a seguir para criar a imagem do Docker usando o Cloud Build:

. ~/instavibe-bootstrap/set_env.sh

cd ~/instavibe-bootstrap/tools/instavibe

export IMAGE_TAG="latest"
export MCP_IMAGE_NAME="mcp-tool-server"
export IMAGE_PATH="${REGION}-docker.pkg.dev/${PROJECT_ID}/${REPO_NAME}/${MCP_IMAGE_NAME}:${IMAGE_TAG}"
export SERVICE_NAME="mcp-tool-server"
export INSTAVIBE_BASE_URL=$(gcloud run services list --platform=managed --region=us-central1 --format='value(URL)' | grep instavibe)/api

gcloud builds submit . \
  --tag=${IMAGE_PATH} \
  --project=${PROJECT_ID}

👉💻 Implante a imagem como um serviço no Google Cloud Run.

. ~/instavibe-bootstrap/set_env.sh

cd ~/instavibe-bootstrap/tools/instavibe

export IMAGE_TAG="latest"
export MCP_IMAGE_NAME="mcp-tool-server"
export IMAGE_PATH="${REGION}-docker.pkg.dev/${PROJECT_ID}/${REPO_NAME}/${MCP_IMAGE_NAME}:${IMAGE_TAG}"
export SERVICE_NAME="mcp-tool-server"
export INSTAVIBE_BASE_URL=$(gcloud run services list --platform=managed --region=us-central1 --format='value(URL)' | grep instavibe)/api

gcloud run deploy ${SERVICE_NAME} \
  --image=${IMAGE_PATH} \
  --platform=managed \
  --region=${REGION} \
  --allow-unauthenticated \
  --set-env-vars="INSTAVIBE_BASE_URL=${INSTAVIBE_BASE_URL}" \
  --set-env-vars="APP_HOST=0.0.0.0" \
  --set-env-vars="APP_PORT=8080" \
  --set-env-vars="GOOGLE_GENAI_USE_VERTEXAI=TRUE" \
  --set-env-vars="GOOGLE_CLOUD_LOCATION=${REGION}" \
  --set-env-vars="GOOGLE_CLOUD_PROJECT=${PROJECT_ID}" \
  --project=${PROJECT_ID} \
  --min-instances=1

👉💻 Depois que a implantação for concluída, o servidor MCP vai estar em execução e acessível por um URL público. Precisamos capturar esse URL para que nosso agente (atuando como um cliente do MCP) saiba onde se conectar.

export MCP_SERVER_URL=$(gcloud run services list --platform=managed --region=us-central1 --format='value(URL)' | grep mcp-tool-server)/sse

Agora, o serviço mcp-tool-server também vai aparecer como "Em execução" na seção Cloud Run do console do Google Cloud.

Cloud Run

Com o servidor MCP implantado e o URL capturado, agora podemos implementar o agente que vai atuar como um cliente MCP e usar as ferramentas expostas por esse servidor.

8. Agente de interação com a plataforma (usando o MCP)

Cliente do MCP: o cliente do MCP é um componente que reside em um aplicativo ou agente de IA, atuando como a interface entre o modelo de IA e um ou mais servidores do MCP. Na nossa implementação, esse cliente será integrado diretamente ao nosso agente. A principal função desse cliente é se comunicar com servidores MCP para descobrir ferramentas disponíveis usando a função list_tools e, em seguida, solicitar a execução de ferramentas específicas usando a função call_tool, transmitindo os argumentos necessários fornecidos pelo modelo de IA ou pelo agente que orquestra a chamada.

Cliente MCP

Agora vamos criar o agente que atua como o cliente do MCP. Esse agente, executado no framework ADK, será responsável por se comunicar com o mcp-tool-server que acabamos de implantar.

👉 Primeiro, precisamos modificar a definição do agente para buscar dinamicamente as ferramentas do nosso servidor MCP em execução. Em agents/platform_mcp_client/agent.py, substitua #REPLACE ME - FETCH TOOLS pelo seguinte:

"""Gets tools from the File System MCP Server."""
  tools =  MCPToolset(
      connection_params=SseServerParams(url=MCP_SERVER_URL, headers={})
  )

Esse código usa o método MCPToolset.from_server para se conectar ao MCP_SERVER_URL (que definimos como uma variável de ambiente anteriormente) e recuperar a lista de ferramentas disponíveis.

Em seguida, precisamos instruir a definição do agente do ADK a usar essas ferramentas buscadas dinamicamente.

👉 Em agents/platform_mcp_client/agent.py, substitua #REPLACE ME - SET TOOLs pelo seguinte:

  tools=[tools],

👉💻 Agora, vamos testar esse agente localmente usando a interface de desenvolvimento do ADK para ver se ele consegue se conectar corretamente ao servidor MCP e usar as ferramentas para interagir com o aplicativo InstaVibe em execução.

. ~/instavibe-bootstrap/set_env.sh
source ~/instavibe-bootstrap/env/bin/activate
export MCP_SERVER_URL=$(gcloud run services list --platform=managed --region=us-central1 --format='value(URL)' | grep mcp-tool-server)/sse

cd  ~/instavibe-bootstrap/agents
sed -i "s|^\(O\?GOOGLE_CLOUD_PROJECT\)=.*|GOOGLE_CLOUD_PROJECT=${PROJECT_ID}|" ~/instavibe-bootstrap/agents/platform_mcp_client/.env
sed -i "s|^\(O\?MCP_SERVER_URL\)=.*|MCP_SERVER_URL=${MCP_SERVER_URL}|" ~/instavibe-bootstrap/agents/platform_mcp_client/.env
adk web

Abra a interface de desenvolvimento do ADK no navegador novamente (usando a visualização da Web do Cloud Shell na porta 8000). Desta vez, no menu suspenso no canto superior direito, selecione o agente platform_mcp_client.

Vamos testar a ferramenta create_post. Na caixa de diálogo de chat, insira o seguinte pedido:

Create a post saying "Y'all I just got the cutest lil void baby 😭✨ Naming him Abyss bc he's deep, mysterious, and lowkey chaotic 🔥🖤 #VoidCat #NewRoomie" I'm Julia

Postagem da interface de desenvolvimento do ADK

O agente precisa processar isso, identificar a necessidade de usar a ferramenta "create_post" e se comunicar com o servidor MCP, que por sua vez chama a API InstaVibe.

👉 Etapa de verificação: depois que o agente confirmar a ação, abra ou atualize a guia em que o aplicativo InstaVibe está sendo executado. A nova postagem de "Julia" vai aparecer no feed principal.

Postagem do InstaVibe

👉💻 Execute este script em um terminal separado para receber o link do Instavibe, se necessário:

gcloud run services list --platform=managed --region=us-central1 --format='value(URL)' | grep instavibe

👉📝 Agora, vamos testar a ferramenta create_event. Insira a seguinte solicitação de várias linhas na caixa de diálogo do chat:

Hey, can you set up an event for Hannah and George and me, and I'm Julia? Let's call it 'Mexico City Culinary & Art Day'.
here are more info
  {"event_name": "Mexico City Culinary & Art Day",
  "description": "A vibrant day in Mexico City for Hannah and George, starting with lunch at one of the city's best taco spots in the hip Condesa neighborhood, followed by an inspiring afternoon exploring the Museo Soumaya's stunning art collection.",
  "event_date": "2025-10-17T12:00:00-06:00",
  "locations": [
    {
      "name": "El Tizoncito",
      "description": "Considered one of the original creators of tacos al pastor, El Tizoncito offers a legendary taco experience in the heart of Condesa. Their flavorful meats, house salsas, and casual vibe make it a must-visit for foodies.",
      "latitude": 19.412179,
      "longitude": -99.171308,
      "address": "Av. Tamaulipas 122, Hipódromo, Cuauhtémoc, 06100 Ciudad de México, CDMX, Mexico"
    },
    {
      "name": "Museo Soumaya",
      "description": "An architectural icon in Mexico City, Museo Soumaya houses over 66,000 works of art, including pieces by Rodin, Dalí, and Rivera. The striking silver structure is a cultural landmark and a visual feast inside and out.",
      "latitude": 19.440056,
      "longitude": -99.204281,
      "address": "Plaza Carso, Blvd. Miguel de Cervantes Saavedra 303, Granada, Miguel Hidalgo, 11529 Ciudad de México, CDMX, Mexico"
    }
  ],
  "attendee_names": ["Hannah", "George", Julia],
}

De novo, o agente precisa usar a ferramenta adequada pelo servidor MCP. Na guia "Eventos", clique no evento individual para ver um trace detalhado e gradual da execução.

Evento da interface do ADK Dev

👉 Etapa de verificação: volte ao aplicativo InstaVibe em execução e navegue até a seção "Eventos" (ou equivalente). O evento "Dia da arte e da culinária na Cidade do México" vai aparecer.

Evento do InstaVibe

Isso demonstra com sucesso como o MCP permite que nosso agente aproveite ferramentas externas (neste caso, as APIs do InstaVibe) de maneira padronizada.

Depois de verificar as duas ações, volte ao terminal do Cloud Shell e pressione Ctrl+C para interromper a interface de desenvolvimento do ADK.

9. Agente de fluxo de trabalho e vários agentes no ADK

Até agora, nossos agentes podem planejar passeios e interagir com a plataforma. No entanto, um planejamento verdadeiramente personalizado exige entender o círculo social do usuário. Para usuários ocupados que talvez não acompanhem de perto as atividades dos amigos, reunir esse contexto manualmente é difícil. Para resolver isso, vamos criar um agente de criação de perfis sociais que usa nosso banco de dados de gráficos do Spanner para analisar atividades e interesses de amigos, permitindo sugestões mais personalizadas.

Agente de criação de perfil social

Primeiro, precisamos de ferramentas para que o agente acesse os dados do gráfico.

👉📝 Adicione as seguintes funções Python ao final do arquivo ~/instavibe-bootstrap/agents/social/instavibe.py:

def get_person_attended_events(person_id: str)-> list[dict]:
    """
    Fetches events attended by a specific person using Graph Query.
    Args:
       person_id (str): The ID of the person whose posts to fetch.
    Returns: list[dict] or None.
    """
    if not db_instance: return None

    graph_sql = """
        Graph SocialGraph
        MATCH (p:Person)-[att:Attended]->(e:Event)
        WHERE p.person_id = @person_id
        RETURN e.event_id, e.name, e.event_date, att.attendance_time
        ORDER BY e.event_date DESC
    """
    params = {"person_id": person_id}
    param_types_map = {"person_id": param_types.STRING}
    fields = ["event_id", "name", "event_date", "attendance_time"]

    results = run_graph_query( graph_sql, params=params, param_types=param_types_map, expected_fields=fields)

    if results is None: return None

    for event in results:
        if isinstance(event.get('event_date'), datetime):
            event['event_date'] = event['event_date'].isoformat()
        if isinstance(event.get('attendance_time'), datetime):
            event['attendance_time'] = event['attendance_time'].isoformat()
    return results

def get_person_id_by_name( name: str) -> str:
    """
    Fetches the person_id for a given name using SQL.

    Args:
       name (str): The name of the person to search for.

    Returns:
        str or None: The person_id if found, otherwise None.
                     Returns the ID of the *first* match if names are duplicated.
    """
    if not db_instance: return None

    sql = """
        SELECT person_id
        FROM Person
        WHERE name = @name
        LIMIT 1 -- Return only the first match in case of duplicate names
    """
    params = {"name": name}
    param_types_map = {"name": param_types.STRING}
    fields = ["person_id"]

    # Use the standard SQL query helper
    results = run_sql_query( sql, params=params, param_types=param_types_map, expected_fields=fields)

    if results: # Check if the list is not empty
        return results[0].get('person_id') # Return the ID from the first dictionary
    else:
        return None # Name not found


def get_person_posts( person_id: str)-> list[dict]:
    """
    Fetches posts written by a specific person using Graph Query.

    Args:
        person_id (str): The ID of the person whose posts to fetch.


    Returns:
        list[dict] or None: List of post dictionaries with ISO date strings,
                           or None if an error occurs.
    """
    if not db_instance: return None

    # Graph Query: Find the specific Person node, follow 'Wrote' edge to Post nodes
    graph_sql = """
        Graph SocialGraph
        MATCH (author:Person)-[w:Wrote]->(post:Post)
        WHERE author.person_id = @person_id
        RETURN post.post_id, post.author_id, post.text, post.sentiment, post.post_timestamp, author.name AS author_name
        ORDER BY post.post_timestamp DESC
    """
    # Parameters now include person_id and limit
    params = {
        "person_id": person_id
    }
    param_types_map = {
        "person_id": param_types.STRING
    }
    # Fields returned remain the same
    fields = ["post_id", "author_id", "text", "sentiment", "post_timestamp", "author_name"]

    results = run_graph_query(graph_sql, params=params, param_types=param_types_map, expected_fields=fields)

    if results is None:
        return None

    # Convert datetime objects to ISO format strings
    for post in results:
        if isinstance(post.get('post_timestamp'), datetime):
            post['post_timestamp'] = post['post_timestamp'].isoformat()

    return results


def get_person_friends( person_id: str)-> list[dict]:
    """
    Fetches friends for a specific person using Graph Query.
    Args:
        person_id (str): The ID of the person whose posts to fetch.
    Returns: list[dict] or None.
    """
    if not db_instance: return None

    graph_sql = """
        Graph SocialGraph
        MATCH (p:Person {person_id: @person_id})-[f:Friendship]-(friend:Person)
        RETURN DISTINCT friend.person_id, friend.name
        ORDER BY friend.name
    """
    params = {"person_id": person_id}
    param_types_map = {"person_id": param_types.STRING}
    fields = ["person_id", "name"]

    results = run_graph_query( graph_sql, params=params, param_types=param_types_map, expected_fields=fields)

    return results

Agora, vamos falar sobre como estruturar nosso agente. Analisar os perfis de vários amigos e resumir as descobertas envolve várias etapas. Esse é um cenário perfeito para usar os recursos multiagente do ADK, especificamente os agentes de fluxo de trabalho.

No ADK do Google, um agente de fluxo de trabalho não executa tarefas por conta própria, mas orquestra outros agentes, chamados de subagentes. Isso permite um design modular, dividindo problemas complexos em componentes especializados. O ADK oferece tipos de fluxo de trabalho integrados, como

  • Sequencial (etapa por etapa)
  • Paralela (execução simultânea)
  • e Loop (execução repetida)

Agente de criação de perfil social

Para nossa tarefa de criação de perfil social, o design usa um agente de loop para criar um fluxo de trabalho iterativo. A intenção é processar uma pessoa por vez: profile_agent coleta dados, summary_agent atualiza a análise e check_agent determina se devemos repetir o loop.

Vamos definir os subagentes necessários para esse fluxo de trabalho.

👉📝 Em ~/instavibe-bootstrap/agents/social/agent.py, substitua #REPLACE FOR profile_agent pelo seguinte:

profile_agent = LlmAgent(
    name="profile_agent",
    model="gemini-2.5-flash",
    description=(
        "Agent to answer questions about the this person social profile. Provide the person's profile using their name, make sure to fetch the id before getting other data."
    ),
    instruction=(
        "You are a helpful agent to answer questions about the this person social profile. You'll be given a list of names, provide the person's profile using their name, make sure to fetch the id before getting other data. Get one person at a time, start with the first one on the list, and skip if already provided. return this person's result"
    ),
    tools=[get_person_posts,get_person_friends,get_person_id_by_name,get_person_attended_events],
)

Em seguida, o agente usa as informações de perfil coletadas (acumuladas em iterações de loop) e gera o resumo final, identificando pontos em comum se várias pessoas foram analisadas.

👉📝 No mesmo ~/instavibe-bootstrap/agents/social/agent.py, substitua #REPLACE FOR summary_agent pelo seguinte:

summary_agent = LlmAgent(
    name="summary_agent",
    model="gemini-2.5-flash",
    description=(
        "Generate a comprehensive social summary as a single, cohesive paragraph. This summary should cover the activities, posts, friend networks, and event participation of one or more individuals. If multiple profiles are analyzed, the paragraph must also identify and integrate any common ground found between them."
    ),
    instruction=(
        """
        Your primary task is to synthesize social profile information into a single, comprehensive paragraph.

            **Input Scope & Default Behavior:**
            *   If specific individuals are named by the user, focus your analysis on them.
            *   **If no individuals are specified, or if the request is general, assume the user wants an analysis of *all relevant profiles available in the current dataset/context*.**

            **For each profile (whether specified or determined by default), you must analyze:**

            1.  **Post Analysis:**
                *   Systematically review their posts (e.g., content, topics, frequency, engagement).
                *   Identify recurring themes, primary interests, and expressed sentiments.

            2.  **Friendship Relationship Analysis:**
                *   Examine their connections/friends list.
                *   Identify key relationships, mutual friends (especially if comparing multiple profiles), and the general structure of their social network.

            3.  **Event Participation Analysis:**
                *   Investigate their past (and if available, upcoming) event participation.
                *   Note the types of events, frequency of attendance, and any notable roles (e.g., organizer, speaker).

            **Output Generation (Single Paragraph):**

            *   **Your entire output must be a single, cohesive summary paragraph.**
                *   **If analyzing a single profile:** This paragraph will detail their activities, interests, and social connections based on the post, friend, and event analysis.
                *   **If analyzing multiple profiles:** This paragraph will synthesize the key findings regarding posts, friends, and events for each individual. Crucially, it must then seamlessly integrate or conclude with an identification and description of the common ground found between them (e.g., shared interests from posts, overlapping event attendance, mutual friends). The aim is a unified narrative within this single paragraph.

            **Key Considerations:**
            *   Base your summary strictly on the available data.
            *   If data for a specific category (posts, friends, events) is missing or sparse for a profile, you may briefly acknowledge this within the narrative if relevant.
                """
        ),
    output_key="summary"
)

Precisamos de uma maneira de determinar quando o loop deve parar, ou seja, quando todos os perfis solicitados forem resumidos.

👉📝 No mesmo ~/instavibe-bootstrap/agents/social/agent.py, substitua #REPLACE FOR check_agent pelo seguinte:

check_agent = LlmAgent(
    name="check_agent",
    model="gemini-2.5-flash",
    description=(
        "Check if everyone's social profile are summarized and has been generated. Output 'completed' or 'pending'."
    ),
    output_key="summary_status"
)

Adicionamos uma verificação programática simples (CheckCondition) que analisa explicitamente o summary_status armazenado no State, que é retornado por check_agent e informa ao agente de loop se ele deve continuar (escalate=False) ou parar (escalate=True).

👉📝 No mesmo ~/instavibe-bootstrap/agents/social/agent.py, substitua #REPLACE FOR CheckCondition localizado na parte de cima do arquivo pelo seguinte:

class CheckCondition(BaseAgent):
    async def _run_async_impl(self, ctx: InvocationContext) -> AsyncGenerator[Event, None]:
        #log.info(f"Checking status: {ctx.session.state.get("summary_status", "fail")}")
        log.info(f"Summary: {ctx.session.state.get("summary")}")

        status = ctx.session.state.get("summary_status", "fail").strip()
        is_done = (status == "completed")

        yield Event(author=self.name, actions=EventActions(escalate=is_done))

Estado e callbacks para resultados de loop

No ADK do Google, State é um conceito crucial que representa a memória ou os dados de trabalho de um agente durante a execução. É essencialmente um contexto persistente que contém as informações necessárias para um agente manter em diferentes etapas, chamadas de ferramentas ou interações. Esse estado pode armazenar resultados intermediários, informações do usuário, parâmetros para ações subsequentes ou qualquer outro dado que o agente precise lembrar ao longo de uma tarefa.

No nosso cenário, à medida que o agente do Loop itera, o summary_agent e o check_agent armazenam as saídas (summary e summary_status) no estado do agente. Isso permite que as informações persistam em várias iterações. No entanto, o Loop Agent não retorna automaticamente o resumo final do estado quando termina.

Agente de criação de perfil social

Os callbacks no ADK permitem injetar lógica personalizada para ser executada em pontos específicos durante o ciclo de vida de um agente ou em resposta a determinados eventos, como a conclusão de uma chamada de ferramenta ou antes de o agente terminar a execução. Eles oferecem uma maneira de personalizar o comportamento do agente e processar os resultados de forma dinâmica.

Vamos usar um after_agent_callback que é executado quando o loop termina (porque CheckCondition foi escalonado). Esse callback modify_output_after_agent recupera o resumo final do estado e o formata como a mensagem de saída final do agente.

Retornar chamada

👉📝 No mesmo ~/instavibe-bootstrap/agents/social/agent.py, substitua #REPLACE FOR modify_output_after_agent pelo seguinte:

def modify_output_after_agent(callback_context: CallbackContext) -> Optional[types.Content]:

    agent_name = callback_context.agent_name
    invocation_id = callback_context.invocation_id
    current_state = callback_context.state.to_dict()
    current_user_content = callback_context.user_content
    print(f"[Callback] Exiting agent: {agent_name} (Inv: {invocation_id})")
    print(f"[Callback] Current summary_status: {current_state.get("summary_status")}")
    print(f"[Callback] Current Content: {current_user_content}")

    status = current_state.get("summary_status").strip()
    is_done = (status == "completed")
    # Retrieve the final summary from the state

    final_summary = current_state.get("summary")
    print(f"[Callback] final_summary: {final_summary}")
    if final_summary and is_done and isinstance(final_summary, str):
        log.info(f"[Callback] Found final summary, constructing output Content.")
        # Construct the final output Content object to be sent back
        return types.Content(role="model", parts=[types.Part(text=final_summary.strip())])
    else:
        log.warning("[Callback] No final summary found in state or it's not a string.")
        # Optionally return a default message or None if no summary was generated
        return None

Definir o agente de loop raiz

Por fim, definimos o LoopAgent principal. Ele orquestra os subagentes em sequência em cada iteração de loop (profile_agent -> summary_agent -> check_agent -> CheckCondition). Ela vai repetir essa sequência até max_iterations vezes ou até que CheckCondition sinalize a conclusão. O after_agent_callback garante que o resumo final seja retornado.

👉📝 No mesmo ~/instavibe-bootstrap/agents/social/agent.py, substitua #REPLACE FOR root_agent pelo seguinte:

root_agent = LoopAgent(
    name="InteractivePipeline",
    sub_agents=[
        profile_agent,
        summary_agent,
        check_agent,
        CheckCondition(name="Checker")
    ],
    description="Find everyone's social profile on events, post and friends",
    max_iterations=10,
    after_agent_callback=modify_output_after_agent
)

Vamos testar esse fluxo de trabalho multiagente usando a interface de desenvolvimento do ADK.

👉💻 Inicie o servidor da Web do ADK:

. ~/instavibe-bootstrap/set_env.sh
source ~/instavibe-bootstrap/env/bin/activate
cd  ~/instavibe-bootstrap/agents
sed -i "s|^\(O\?GOOGLE_CLOUD_PROJECT\)=.*|GOOGLE_CLOUD_PROJECT=${PROJECT_ID}|" ~/instavibe-bootstrap/agents/social/.env
adk web

Abra a interface de desenvolvimento do ADK (porta 8000 via Visualização da Web). No menu suspenso do agente (canto superior direito), selecione o agente Social.

👉 Agora, peça para ele criar um perfil de várias pessoas. Na caixa de diálogo do chat, digite:

Tell me about Mike and Bob

Depois que o agente responder (o que pode levar um pouco mais de tempo devido ao loop e às várias chamadas de LLM), não olhe apenas para a saída final do chat. Acesse a guia "Eventos" no painel esquerdo da interface de desenvolvimento do ADK.

👉 Etapa de verificação: na guia "Eventos", você vai encontrar um rastreamento detalhado e gradual da execução. 09-01-adk-dev-ui.png

Depois de observar como o agente invoca cada subagente, em que você espera que o fluxo vá de profile_agent -> summary_agent -> check_agent, Checker em cada iteração. Mas, na prática, vemos a poderosa "auto-otimização" do agente em ação.

Como o modelo subjacente vê toda a solicitação (por exemplo, "crie um perfil de Mike e Bob"), ele geralmente escolhe o caminho mais eficiente, reunindo todos os dados necessários em uma única rodada consolidada em vez de iterar várias vezes. É possível conferir as entradas, saídas e estados de cada etapa, incluindo as chamadas de ferramentas feitas pelo profile_agent.

09-02-ui-graph.png

e o status é atualizado por check_agent e CheckCondition. 09-03-ui-state.png

Esse rastreamento visual é muito útil para entender e depurar como o fluxo de trabalho multiagente opera até que o resumo final seja gerado e retornado pelo callback.

Depois de analisar a resposta do chat e o rastreamento de eventos, volte ao terminal do Cloud Shell e pressione Ctrl+C para interromper a interface de desenvolvimento do ADK.

10. Comunicação entre agentes (A2A)

Até agora, criamos agentes especializados, mas eles operam isoladamente ou em um fluxo de trabalho predefinido na mesma máquina. Para criar sistemas multiagentes verdadeiramente distribuídos e colaborativos, precisamos de uma maneira para que os agentes, que podem ser executados como serviços separados, se descubram e se comuniquem de maneira eficaz. É aí que entra o protocolo de agente para agente (A2A).

O protocolo A2A é um padrão aberto projetado especificamente para comunicação interoperável entre agentes de IA. Enquanto o MCP se concentra na interação entre agente e ferramenta, o A2A se concentra na interação entre agentes. Com ele, os agentes podem:

  • Descobrir: encontre outros agentes e saiba mais sobre as funcionalidades deles com os cards padronizados.
  • Comunicar: troque mensagens e dados com segurança.
  • Colaborar: delegue tarefas e coordene ações para alcançar metas complexas.

O protocolo A2A facilita essa comunicação por mecanismos como "cards de agente", que os agentes podem usar para anunciar suas capacidades e informações de conexão.

10-05-agent-card

O A2A usa padrões da Web conhecidos (HTTP, SSE, JSON-RPC) e geralmente emprega um modelo cliente-servidor em que um agente (cliente) envia tarefas para outro (agente/servidor remoto). Essa padronização é fundamental para criar sistemas modulares e escalonáveis em que agentes desenvolvidos de forma independente podem trabalhar juntos.

Como ativar o A2A para agentes do InstaVibe

Para tornar nossos agentes atuais do Planner, de interação com a plataforma e de redes sociais acessíveis a outros agentes via A2A, precisamos encapsular cada um deles com um componente do servidor A2A. Este servidor vai:

  • Expor um card do agente: disponibilize uma descrição padrão das capacidades do agente por um endpoint HTTP.
  • Ouvir tarefas(mensagens de solicitação): aceita solicitações de tarefas recebidas de outros agentes (clientes A2A) de acordo com o protocolo A2A.
  • Gerenciar a execução de tarefas(mensagens de solicitação): transfere as tarefas recebidas para a lógica do agente ADK subjacente para processamento.

Agente de planejamento (A2A ativado)

all-agent-planner

Vamos começar adicionando a camada de servidor A2A ao nosso agente do Planner.

Defina a lógica de inicialização do servidor A2A. Esse código define o AgentCard (a descrição pública do agente), configura o A2AServer e o inicia, vinculando-o ao PlatformAgentExecutor.

👉📝 Adicione o seguinte código ao final de ~/instavibe-bootstrap/agents/planner/a2a_server.py:

class PlannerAgent:
    """An agent to help user planning a event with its desire location."""
    SUPPORTED_CONTENT_TYPES = ["text", "text/plain"]

    def __init__(self):
        self._agent = self._build_agent()
        self.runner = Runner(
            app_name=self._agent.name,
            agent=self._agent,
            artifact_service=InMemoryArtifactService(),
            session_service=InMemorySessionService(),
            memory_service=InMemoryMemoryService(),
        )
        capabilities = AgentCapabilities(streaming=True)
        skill = AgentSkill(
            id="event_planner",
            name="Event planner",
            description="""
            This agent generates multiple fun plan suggestions tailored to your specified location, dates, and interests,
            all designed for a moderate budget. It delivers detailed itineraries,
            including precise venue information (name, latitude, longitude, and description), in a structured JSON format.
            """,
            tags=["instavibe"],
            examples=["What about Bostona MA this weekend?"],
        )
        self.agent_card = AgentCard(
            name="Event Planner Agent",
            description="""
            This agent generates multiple fun plan suggestions tailored to your specified location, dates, and interests,
            all designed for a moderate budget. It delivers detailed itineraries,
            including precise venue information (name, latitude, longitude, and description), in a structured JSON format.
            """,
            url=f"{PUBLIC_URL}",
            version="1.0.0",
            defaultInputModes=PlannerAgent.SUPPORTED_CONTENT_TYPES,
            defaultOutputModes=PlannerAgent.SUPPORTED_CONTENT_TYPES,
            capabilities=capabilities,
            skills=[skill],
        )

    def get_processing_message(self) -> str:
        return "Processing the planning request..."

    def _build_agent(self) -> LlmAgent:
        """Builds the LLM agent for the night out planning agent."""
        return agent.root_agent


if __name__ == '__main__':
    try:
        plannerAgent = PlannerAgent()

        request_handler = DefaultRequestHandler(
            agent_executor=PlannerAgentExecutor(plannerAgent.runner,plannerAgent.agent_card),
            task_store=InMemoryTaskStore(),
        )

        server = A2AStarletteApplication(
            agent_card=plannerAgent.agent_card,
            http_handler=request_handler,
        )
        logger.info(f"Attempting to start server with Agent Card: {plannerAgent.agent_card.name}")
        logger.info(f"Server object created: {server}")

        uvicorn.run(server.build(), host='0.0.0.0', port=port)
    except Exception as e:
        logger.error(f"An error occurred during server startup: {e}")
        exit(1)

👉💻 Vamos testar rapidamente se o servidor A2A inicia corretamente no local e mostra o cartão do agente. Execute o seguinte comando no primeiro terminal:

. ~/instavibe-bootstrap/set_env.sh
source ~/instavibe-bootstrap/env/bin/activate
cd ~/instavibe-bootstrap/agents/
python -m planner.a2a_server

👉 Agora, abra outra janela do terminal. Clique no sinal de adição (+) no painel do terminal. dois terminais

👉💻 Use curl para solicitar o card do agente no servidor em execução local:

curl http://localhost:10003/.well-known/agent.json | jq

Você vai ver a representação JSON do AgentCard que definimos, confirmando que o servidor está em execução e anunciando o agente do Planner.

10-02-planner-a2a.png

Volte ao primeiro terminal (onde o servidor está em execução) e pressione Ctrl+C para interromper.

👉💻 Com a lógica do servidor A2A adicionada, agora podemos criar a imagem do contêiner.

Criar e implantar o agente do Planner

. ~/instavibe-bootstrap/set_env.sh

cd ~/instavibe-bootstrap/agents

# Set variables specific to the PLANNER agent
export IMAGE_TAG="latest"
export AGENT_NAME="planner"
export IMAGE_NAME="planner-agent"
export IMAGE_PATH="${REGION}-docker.pkg.dev/${PROJECT_ID}/${REPO_NAME}/${IMAGE_NAME}:${IMAGE_TAG}"
export SERVICE_NAME="planner-agent"
export PUBLIC_URL="https://planner-agent-${PROJECT_NUMBER}.${REGION}.run.app"

echo "Building ${AGENT_NAME} agent..."
gcloud builds submit . \
  --config=cloudbuild-build.yaml \
  --project=${PROJECT_ID} \
  --region=${REGION} \
  --substitutions=_AGENT_NAME=${AGENT_NAME},_IMAGE_PATH=${IMAGE_PATH}

echo "Image built and pushed to: ${IMAGE_PATH}"

👉💻 Implante o agente do Planner no Cloud Run.

. ~/instavibe-bootstrap/set_env.sh

cd ~/instavibe-bootstrap/agents

# Set variables specific to the PLANNER agent
export IMAGE_TAG="latest"
export AGENT_NAME="planner"
export IMAGE_NAME="planner-agent"
export IMAGE_PATH="${REGION}-docker.pkg.dev/${PROJECT_ID}/${REPO_NAME}/${IMAGE_NAME}:${IMAGE_TAG}"
export SERVICE_NAME="planner-agent"
export PUBLIC_URL="https://planner-agent-${PROJECT_NUMBER}.${REGION}.run.app"


gcloud run deploy ${SERVICE_NAME} \
  --image=${IMAGE_PATH} \
  --platform=managed \
  --region=${REGION} \
  --set-env-vars="A2A_HOST=0.0.0.0" \
  --set-env-vars="A2A_PORT=8080" \
  --set-env-vars="GOOGLE_GENAI_USE_VERTEXAI=TRUE" \
  --set-env-vars="GOOGLE_CLOUD_LOCATION=${REGION}" \
  --set-env-vars="GOOGLE_CLOUD_PROJECT=${PROJECT_ID}" \
  --set-env-vars="PUBLIC_URL=${PUBLIC_URL}" \
  --allow-unauthenticated \
  --project=${PROJECT_ID} \
  --min-instances=1

Vamos verificar se o serviço implantado está em execução e veiculando o cartão do agente corretamente na nuvem usando o A2A Inspector.

👉 No ícone "Visualização da Web" na barra de ferramentas do Cloud Shell, selecione "Alterar porta". Defina a porta como 8081 e clique em "Change and Preview". Uma nova guia do navegador será aberta com a interface do A2A Inspector.

10-08-web-preview.png

👉💻 No terminal, acesse o URL do seu agente planejador implantado:

export PLANNER_AGENT_URL=$(gcloud run services list --platform=managed --region=us-central1 --format='value(URL)' | grep planner-agent)
echo ${PLANNER_AGENT_URL}

👉💻 Copie o URL de saída.

👉 Na interface do inspetor de A2A, cole o URL no campo "URL do agente" e clique em "Conectar".

👀 Os detalhes do cartão e o JSON do agente vão aparecer na guia "Card do agente", confirmando uma conexão bem-sucedida.

10-03-planner-a2a.png

👉 Clique na guia "Chat" no inspetor de A2A. É aqui que você pode interagir diretamente com o agente implantado. Envie uma mensagem para testar a capacidade de planejamento dele. Exemplo:

Plan something for me in Boston MA this weekend, and I enjoy classical music

👀 Para inspecionar a comunicação bruta, clique no balão da sua mensagem e depois no balão da resposta do agente na janela de chat. Ao clicar em cada uma delas, a mensagem JSON-RPC 2.0 completa enviada ou recebida será exibida, o que é muito útil para depuração.

Mantenha a guia "Inspetor de A2A" à mão. NÃO feche! Vamos usá-lo novamente em alguns instantes para testar os outros dois agentes.

10-06-a2a-inspector.png

Agente de interação com a plataforma (A2A ativado)

all-agent-platform

Em seguida, vamos repetir o processo para o agente de interação da plataforma (aquele que usa o MCP).

👉📝 Defina a configuração do servidor A2A, incluindo o AgentCard exclusivo, no final de ~/instavibe-bootstrap/agents/platform_mcp_client/a2a_server.py:

class PlatformAgent:
  """An agent that post event and post to instavibe."""

  SUPPORTED_CONTENT_TYPES = ["text", "text/plain"]

  def __init__(self):
    self._agent = self._build_agent()
    self.runner = Runner(
        app_name=self._agent.name,
        agent=self._agent,
        artifact_service=InMemoryArtifactService(),
        session_service=InMemorySessionService(),
        memory_service=InMemoryMemoryService(),
    )
    capabilities = AgentCapabilities(streaming=True)
    skill = AgentSkill(
            id="instavibe_posting",
            name="Post social post and events on instavibe",
            description="""
            This "Instavibe" agent helps you create posts (identifying author, text, and sentiment – inferred if unspecified) and register
            for events (gathering name, date, attendee). It efficiently collects required information and utilizes dedicated tools
            to perform these actions on your behalf, ensuring a smooth sharing experience.
            """,
            tags=["instavibe"],
            examples=["Create a post for me, the post is about my cute cat and make it positive, and I'm Alice"],
        )
    self.agent_card = AgentCard(
            name="Instavibe Posting Agent",
            description="""
            This "Instavibe" agent helps you create posts (identifying author, text, and sentiment – inferred if unspecified) and register
            for events (gathering name, date, attendee). It efficiently collects required information and utilizes dedicated tools
            to perform these actions on your behalf, ensuring a smooth sharing experience.
            """,
            url=f"{PUBLIC_URL}",
            version="1.0.0",
            defaultInputModes=PlatformAgent.SUPPORTED_CONTENT_TYPES,
            defaultOutputModes=PlatformAgent.SUPPORTED_CONTENT_TYPES,
            capabilities=capabilities,
            skills=[skill],
        )


  def get_processing_message(self) -> str:
      return "Processing the social post and event request..."

  def _build_agent(self) -> LlmAgent:
    """Builds the LLM agent for the Processing the social post and event request."""
    return agent.root_agent


if __name__ == '__main__':
    try:
        platformAgent = PlatformAgent()

        request_handler = DefaultRequestHandler(
            agent_executor=PlatformAgentExecutor(platformAgent.runner,platformAgent.agent_card),
            task_store=InMemoryTaskStore(),
        )

        server = A2AStarletteApplication(
            agent_card=platformAgent.agent_card,
            http_handler=request_handler,
        )

        uvicorn.run(server.build(), host='0.0.0.0', port=port)
    except Exception as e:
        logger.error(f"An error occurred during server startup: {e}")
        exit(1)

Agente de redes sociais (A2A ativado)

all-agent-social

Por fim, vamos ativar o A2A para nosso agente de criação de perfil social.

👉📝 Defina a configuração do servidor A2A e o AgentCard no final de ~/instavibe-bootstrap/agents/social/a2a_server.py:

class SocialAgent:
  """An agent that handles social profile analysis."""

  SUPPORTED_CONTENT_TYPES = ["text", "text/plain"]

  def __init__(self):
    self._agent = self._build_agent()
    self.runner = Runner(
        app_name=self._agent.name,
        agent=self._agent,
        artifact_service=InMemoryArtifactService(),
        session_service=InMemorySessionService(),
        memory_service=InMemoryMemoryService(),
    )
    capabilities = AgentCapabilities(streaming=True)
    skill = AgentSkill(
                id="social_profile_analysis",
                name="Analyze Instavibe social profile",
                description="""
                Using a provided list of names, this agent synthesizes Instavibe social profile information by analyzing posts, friends, and events.
                It delivers a comprehensive single-paragraph summary for individuals, and for groups, identifies commonalities in their social activities
                and connections based on profile data.
                """,
                tags=["instavibe"],
                examples=["Can you tell me about Bob and Alice?"],
    )
    self.agent_card = AgentCard(
                name="Social Profile Agent",
                description="""
                Using a provided list of names, this agent synthesizes Instavibe social profile information by analyzing posts, friends, and events.
                It delivers a comprehensive single-paragraph summary for individuals, and for groups, identifies commonalities in their social activities
                and connections based on profile data.
                """,
                url=f"{PUBLIC_URL}",
                version="1.0.0",
                defaultInputModes=self.SUPPORTED_CONTENT_TYPES,
                defaultOutputModes=self.SUPPORTED_CONTENT_TYPES,
                capabilities=capabilities,
                skills=[skill],
    )

  def get_processing_message(self) -> str:
      return "Processing the social profile analysis request..."

  def _build_agent(self) -> LoopAgent:
    """Builds the LLM agent for the social profile analysis agent."""
    return agent.root_agent

if __name__ == '__main__':
    try:
        socialAgent = SocialAgent()

        request_handler = DefaultRequestHandler(
            agent_executor=SocialAgentExecutor(socialAgent.runner,socialAgent.agent_card),
            task_store=InMemoryTaskStore(),
        )

        server = A2AStarletteApplication(
            agent_card=socialAgent.agent_card,
            http_handler=request_handler,
        )

        uvicorn.run(server.build(), host='0.0.0.0', port=port)
    except Exception as e:
        logger.error(f"An error occurred during server startup: {e}")
        exit(1)

Criar e implantar os agentes de interação com a plataforma e de redes sociais

Esses agentes precisam de acesso ao Spanner. Portanto, verifique se as variáveis de ambiente SPANNER_INSTANCE_ID, SPANNER_DATABASE_ID e MCP_SERVER_URL foram transmitidas corretamente durante a implantação.

👉💻 Crie e implante no Cloud Run com o Cloud Build:

. ~/instavibe-bootstrap/set_env.sh
cd ~/instavibe-bootstrap/agents
export MCP_SERVER_URL=$(gcloud run services list --platform=managed --region=us-central1 --format='value(URL)' | grep mcp-tool-server)/sse


gcloud builds submit . \
  --config=cloudbuild.yaml \
  --project="${PROJECT_ID}" \
  --region="${REGION}" \
  --substitutions=\
_PROJECT_ID="${PROJECT_ID}",\
_PROJECT_NUMBER="${PROJECT_NUMBER}",\
_REGION="${REGION}",\
_REPO_NAME="${REPO_NAME}",\
_SPANNER_INSTANCE_ID="${SPANNER_INSTANCE_ID}",\
_SPANNER_DATABASE_ID="${SPANNER_DATABASE_ID}",\
_MCP_SERVER_URL="${MCP_SERVER_URL}"

👉💻 No terminal, acesse o URL do agente da plataforma implantado:

export PLATFORM_MPC_CLIENT_URL=$(gcloud run services list --platform=managed --region=us-central1 --format='value(URL)' | grep platform-mcp-client)
echo $PLATFORM_MPC_CLIENT_URL

👉💻 Copie o URL de saída.

👉 Na interface do inspetor de A2A, cole o URL no campo "URL do agente" e clique em "Conectar".

👀 Os detalhes do cartão e o JSON do agente vão aparecer na guia "Card do agente", confirmando uma conexão bem-sucedida.

10-05-platform-a2a.png

👉 Clique na guia "Chat" no inspetor de A2A. É aqui que você pode interagir diretamente com o agente implantado. Envie uma mensagem para testar a capacidade dele de criar postagens:

Create a post for me, the post says 'Paws, purrs, and ocean views 🐾☕🌊. Spent my morning at the Morning Seaside Cat Café, where every sip comes with a side of snuggles and sea breeze.' and make it positive, and I'm Oscar.

👀 Para inspecionar a comunicação bruta, clique no balão da sua mensagem e depois no balão da resposta do agente na janela de chat. Ao clicar em cada uma delas, a mensagem JSON-RPC 2.0 completa enviada ou recebida será exibida, o que é muito útil para depuração.

👉💻 No terminal, acesse o URL do seu agente de redes sociais implantado:

export SOCIAL_AGENT_URL=$(gcloud run services list --platform=managed --region=us-central1 --format='value(URL)' | grep social-agent)
echo $SOCIAL_AGENT_URL

👉💻 Copie o URL de saída.

👉 Na interface do inspetor de A2A, cole o URL no campo "URL do agente" e clique em "Conectar".

👀 Os detalhes do cartão e o JSON do agente vão aparecer na guia "Card do agente", confirmando uma conexão bem-sucedida.

10-04-social-a2a.png

👉 Clique na guia "Chat" no inspetor de A2A. É aqui que você pode interagir diretamente com o agente implantado. Envie uma mensagem para analisar os perfis de usuário do seu banco de dados:

Can you tell me about both Ian and Kevin's profile, what are their common interests?

👀 Para inspecionar a comunicação bruta, clique no balão da sua mensagem e depois no balão da resposta do agente na janela de chat. Ao clicar em cada uma delas, a mensagem JSON-RPC 2.0 completa enviada ou recebida será exibida, o que é muito útil para depuração.

👉 Ótimo, terminamos de inspecionar todos os nossos agentes. Agora você pode fechar a guia do A2A Inspector.

11. Agente do orquestrador (cliente A2A)

Agora temos três agentes especializados (Planner, Platform, Social) em execução como serviços independentes e com A2A no Cloud Run. A última parte é o agente do Orchestrator. Esse agente vai atuar como o coordenador central ou cliente A2A. Ele vai receber solicitações do usuário, descobrir quais agentes remotos são necessários para atender à solicitação (possivelmente em sequência) e usar o protocolo A2A para delegar tarefas a esses agentes remotos. Neste workshop, vamos executar o agente do Orchestrator localmente usando a interface de desenvolvimento do ADK.

all-agent-orchestrator

Primeiro, vamos melhorar a lógica do Orchestrator para processar o registro dos agentes remotos que ele descobre. Armazena os detalhes da conexão dos cards de agente buscados durante a inicialização.

👉📝 Em ~/instavibe-bootstrap/agents/orchestrate/agent.py, substitua #REPLACE ME REG AGENT CARD por:

async with httpx.AsyncClient(timeout=30) as client:
            for i, address in enumerate(REMOTE_AGENT_ADDRESSES):
                log.info(f"--- STEP 3.{i}: Attempting connection to: {address} ---")
                try:
                    card_resolver = A2ACardResolver(client, address)
                    card = await card_resolver.get_agent_card()
                    
                    remote_connection = RemoteAgentConnections(agent_card=card, agent_url=address)
                    self.remote_agent_connections[card.name] = remote_connection
                    self.cards[card.name] = card
                    log.info(f"--- STEP 5.{i}: Successfully stored connection for {card.name} ---")

                except Exception as e:
                    log.error(f"--- CRITICAL FAILURE at STEP 4.{i} for address: {address} ---")
                    log.error(f"--- The hidden exception type is: {type(e).__name__} ---")
                    log.error(f"--- Full exception details and traceback: ---", exc_info=True)

Em seguida, defina a ferramenta para o próprio agente do Orchestrator no ADK.

  • send_message (a função A2A para delegar trabalho).

👉📝 Substitua #REPLACE ME CREATE AGENT em ~/instavibe-bootstrap/agents/orchestrate/agent.py por:

def create_agent(self) -> Agent:
        """Synchronously creates the ADK Agent object."""
        return Agent(
            model="gemini-2.5-flash",
            name="orchestrate_agent",
            instruction=self.root_instruction,
            before_agent_callback=self.before_agent_callback,
            description=("Orchestrates tasks for child agents."),
            tools=[self.send_message], 
        )

A lógica principal do Orchestrator está nas instruções, que informam como usar o A2A.

👉📝 Substitua #REPLACE ME INSTRUCTIONS em ~/instavibe-bootstrap/agents/orchestrate/agent.py por este método de geração de instruções:

def root_instruction(self, context: ReadonlyContext) -> str:
        current_agent = self.check_active_agent(context)
        return f"""
                You are an expert AI Orchestrator. Your primary responsibility is to intelligently interpret user requests, break them down into a logical plan of discrete actions, and delegate each action to the most appropriate specialized remote agent using the send_message function. You do not perform the tasks yourself but manage their assignment, sequence, and critically, their outcomes.
                    **Core Directives & Decision Making:**

                    *   **Understand User Intent & Complexity:**
                        *   Carefully analyze the user's request to determine the core task(s) they want to achieve. Pay close attention to keywords and the overall goal.
                        *   Identify if the request requires a single agent or a sequence of actions from multiple agents. For example, "Analyze John Doe's profile and then create a positive post about his recent event attendance" would require two agents in sequence.

                    *   **Task Planning & Sequencing (for Multi-Step Requests):**
                        *   Before delegating, outline the clear sequence of agent tasks.
                        *   Identify dependencies. If Task B requires output from Task A, execute them sequentially. If tasks are independent (like creating a post and then creating an event), execute them one after the other as separate delegations.
                        *   Agent Reusability: An agent's completion of one task does not make it unavailable. If a user's plan involves multiple, distinct actions that fall under the same agent's expertise (e.g., create a post, then create an event), you must call that same agent again for the subsequent task.

                    *   **Task Delegation & Management (using `send_message`):**
                        *   **Delegation:** Use `send_message` to assign actionable tasks to the selected remote agent. Your `send_message` call MUST include:
                            *   The `remote_agent_name` you've selected.
                            *   The `user_request` or all necessary parameters extracted from the user's input, formatted in a way the target agent will understand.
                        *   **Contextual Awareness for Remote Agents:** If a remote agent repeatedly requests user confirmation or seems to lack context, assume it lacks access to the full conversation history. In such cases, enrich your `send_message` with all necessary contextual information relevant to that specific agent from the conversation history.
                        *   **Sequential Task Execution:**
                            *   After a preceding task completes (indicated by the agent's response or a success signal), gather any necessary output from it.
                            *   Then, use `send_message` for the next agent in the sequence, providing it with the user's original relevant intent and any necessary data obtained from the previous agent's task.
                        *   **Active Agent Prioritization:** If an active agent is already engaged and the user's request is related to its current task, route subsequent related requests directly to that agent by providing updated context via `send_message`.
                    
                    
                    **Critical Success Verification:**

                    *   You **MUST** wait for the tool_output after every send_message call before taking any further action.
                    *   Your decision to proceed to the next task in a sequence **MUST** be based entirely on a confirmation of success from the tool_output of the previous task.
                    *   If a tool call fails, returns an error, or the tool_output is ambiguous, you MUST STOP the sequence. Your next action is to report the exact failure or ambiguity to the user.
                    *   DO NOT assume a task was successful. Do not invent success messages like "The event has been created." Only state that a task is complete if the tool's response explicitly says so.
                    
                    **Communication with User:**

                    *   **Transparent Communication:** Always present the complete and detailed response from the remote agent to the user. Do not summarize or filter unless explicitly instructed.
                    *   When you delegate a task (or the first task in a sequence), clearly inform the user which remote agent is handling it.
                    *   For multi-step requests, you can optionally inform the user of the planned sequence (e.g., "Okay, first I'll ask the 'Social Profile Agent' to analyze the profile, and then I'll have the 'Instavibe Posting Agent' create the post.").
                    *   If waiting for a task in a sequence to complete, you can inform the user (e.g., "The 'Social Profile Agent' is currently processing. I'll proceed with the post once that's done.").
                    *   **User Confirmation Relay:** If a remote agent asks for confirmation, and the user has not already provided it, just make up something.
                    *   If the user's request is ambiguous, if necessary information is missing for any agent in the sequence, or if you are unsure about the plan, just make up something.

                    **Important Reminders:**

                    *   **Autonomous Agent Engagement:** Never seek user permission before engaging with remote agents. If multiple agents are required to fulfill a request, connect with them directly without requesting user preference or confirmation.
                    *   **Focused Information Sharing:** Provide remote agents with only relevant contextual information. Avoid extraneous details that are not directly pertinent to their task.
                    *   **No Redundant Confirmations:** Do not ask remote agents for confirmation of information or actions they have already processed or committed to.
                    *   **Tool Reliance:** Strictly rely on your available tools, primarily `send_message`, to address user requests. Do not generate responses based on assumptions. If information is insufficient, request clarification from the user.
                    *   **Prioritize Recent Interaction:** Focus primarily on the most recent parts of the conversation when processing requests, while maintaining awareness of the overall goal for multi-step tasks.
                    *   Always prioritize selecting the correct agent(s) based on their documented purpose.
                    *   Ensure all information required by the chosen remote agent is included in the `send_message` call, including outputs from previous agents if it's a sequential task.

                    Agents:
                    {self.agents}

                    Current agent: {current_agent['active_agent']}`
                """

Como testar o orquestrador e o sistema A2A completo

Agora, vamos testar todo o sistema. Vamos executar o Orchestrator localmente usando a interface de desenvolvimento do ADK, e ele vai se comunicar com os agentes Planner, Platform e Social em execução remotamente no Cloud Run.

👉💻 Primeiro, verifique se a variável de ambiente REMOTE_AGENT_ADDRESSES contém os URLs separados por vírgula dos seus agentes implantados com A2A ativado. Em seguida, defina as variáveis de ambiente necessárias para o agente do Orchestrator e inicie a interface de desenvolvimento do ADK:

. ~/instavibe-bootstrap/set_env.sh
source ~/instavibe-bootstrap/env/bin/activate

export PLATFORM_MPC_CLIENT_URL=$(gcloud run services list --platform=managed --region=us-central1 --format='value(URL)' | grep platform-mcp-client)
export PLANNER_AGENT_URL=$(gcloud run services list --platform=managed --region=us-central1 --format='value(URL)' | grep planner-agent)
export SOCIAL_AGENT_URL=$(gcloud run services list --platform=managed --region=us-central1 --format='value(URL)' | grep social-agent)

export REMOTE_AGENT_ADDRESSES=${PLANNER_AGENT_URL},${PLATFORM_MPC_CLIENT_URL},${SOCIAL_AGENT_URL}

cd  ~/instavibe-bootstrap/agents
sed -i "s|^\(O\?REMOTE_AGENT_ADDRESSES\)=.*|REMOTE_AGENT_ADDRESSES=${REMOTE_AGENT_ADDRESSES}|" ~/instavibe-bootstrap/agents/orchestrate/.env
adk web

👉 Abra a interface de desenvolvimento do ADK. Para isso, mude a porta de volta para 8000 usando a visualização na Web.

10-08-web-preview.png

👉 No menu suspenso de agentes, selecione o agente orchestrate.

👉 Agora, dê a ele uma tarefa complexa que exija a coordenação de vários agentes remotos. Teste este primeiro exemplo, que envolve o agente social e depois o agente de planejamento:

You are an expert event planner for a user named  Diana.
    Your task is to design a fun and personalized event.

    Here are the details for the plan:
    - Friends to invite: Ian, Nora
    - Desired date: "2025-10-15"
    - Location idea or general preference: "Chicago"

    Your process should be:
    1. Analyze the provided friend names. If you have access to a tool to get their InstaVibe profiles or summarized interests, please use it.
    2. Based on their potential interests (or general good taste if profiles are unavailable), create a tailored plan for the outing, check if you have access to any event planner tools.
    3. Ensure the plan includes the original `planned_date`.

    The user wants a comprehensive plan that includes:
    - The list of invited friends.
    - A catchy and descriptive name for the event.
    - The exact planned date for the event.
    - A summary of what the group will do.
    - Specific recommended spots (e.g., restaurants, bars, activity venues) with their names, (if possible, approximate latitude/longitude for mapping, and address), and a brief description of why it fits the plan.
    - A short, exciting message that {Diana} can send to {Ian, Nora} to get them excited about the event.

Orquestrar

Observe a interação na janela de chat da interface de desenvolvimento do ADK. Preste atenção às respostas do Orchestrator. Ele precisa informar a qual agente remoto está delegando tarefas (por exemplo, "Ok, vou perguntar ao agente de perfil social sobre Ian e Nora primeiro...").

Além disso, verifique a guia "Eventos" na interface para conferir as chamadas de ferramentas (send_message) feitas aos URLs dos agentes remotos.

Enviar tarefa

👉 Agora, tente um segundo exemplo que envolva o agente de integração da plataforma diretamente:

Hey, can you register an event on Instavibe for Laura and Charlie? Let's call it 'Vienna Concert & Castles Day'.
here are more info
"event_name": "Vienna Concert & Castles Day",
  "description": "A refined and unforgettable day in Vienna with Laura and Charlie. The day begins with a guided tour of the magnificent Schönbrunn Palace, showcasing imperial architecture and history. In the evening, enjoy a classical music concert in one of Vienna's most iconic concert halls.",
  "event_date": "2025-10-14T10:00:00+02:00",
  "locations": [
    {
      "name": "Schönbrunn Palace",
      "description": "A UNESCO World Heritage Site and former imperial summer residence, Schönbrunn Palace offers opulent rooms, beautiful baroque gardens, and a glimpse into the life of the Habsburg monarchy. Visitors can stroll the grounds or take a guided historical tour.",
      "latitude": 48.184516,
      "longitude": 16.312222,
      "address": "Schönbrunner Schloßstraße 47, 1130 Wien, Austria"
    },
    {
      "name": "Musikverein Vienna",
      "description": "Home to the world-renowned Vienna Philharmonic, the Musikverein is one of the finest concert halls in the world. Its 'Golden Hall' is famous for its acoustics and ornate design. Attendees can enjoy a powerful classical concert in an unforgettable setting.",
      "latitude": 48.200132,
      "longitude": 16.373777,
      "address": "Musikvereinsplatz 1, 1010 Wien, Austria"
    }
  ],
  "attendee_names": ["Laura", "Charlie", "Oscar"] And I am Oscar

Monitore o chat e a guia "Eventos" novamente. O Orchestrator precisa identificar a necessidade de criar um evento e delegar a tarefa (com todos os detalhes fornecidos) ao "Agente de integração da plataforma". Também é possível clicar no botão Trace para analisar os tempos de resposta da consulta e as operações executadas. Evento &quot;send&quot;

Em seguida, verifique se o evento aparece no aplicativo Web InstaVibe. Evento do InstaVibe

Isso demonstra a implementação bem-sucedida de um sistema multiagente usando o ADK e o protocolo A2A, em que um orquestrador central delega tarefas a agentes remotos especializados.

Não se esqueça de interromper a interface de desenvolvimento do ADK (Ctrl+C no terminal) quando terminar de testar.

12. Agent Engine e chamada remota do InstaVibe

Até agora, executamos nossos agentes especializados no Cloud Run e testamos o Orchestrator localmente usando a interface de desenvolvimento do ADK. Para um cenário de produção, precisamos de um ambiente robusto, escalonável e gerenciado para hospedar nossos agentes. É aí que entra o Google Vertex AI Agent Engine.

O Agent Engine é um serviço totalmente gerenciado na Vertex AI criado especificamente para implantar e dimensionar agentes de IA. Ele abstrai o gerenciamento de infraestrutura, a segurança e o overhead operacional, permitindo que os desenvolvedores (especialmente aqueles menos familiarizados com ambientes complexos de nuvem) se concentrem na lógica e nos recursos do agente em vez de gerenciar servidores. Ele oferece um ambiente de execução dedicado otimizado para cargas de trabalho agênticas.

Agora vamos implantar nosso agente do Orchestrator no Agent Engine. Observação: o mecanismo de implantação mostrado abaixo usa um script personalizado (agent_engine_app.py) fornecido nos materiais do workshop, já que as ferramentas oficiais de implantação direta do ADK para o Agent Engine ainda podem estar em desenvolvimento. Esse script processa o empacotamento e a implantação do agente do Orchestrator, configurado com os endereços necessários do agente remoto.

Execute o comando a seguir para implantar o agente do Orchestrator no Agent Engine. Verifique se a variável de ambiente REMOTE_AGENT_ADDRESSES (que contém os URLs dos agentes do Planner, da plataforma e de redes sociais no Cloud Run) ainda está definida corretamente na seção anterior.

👉💻 Vamos implantar o agente do Orchestrate no Agent Engine. Observação: esta é minha própria implementação da implantação. O ADK tem uma CLI para ajudar na implantação. Vou atualizar isso depois que o BYO-SA for implementado.

cd ~/instavibe-bootstrap/agents/
. ~/instavibe-bootstrap/set_env.sh

gcloud projects add-iam-policy-binding $PROJECT_ID \
    --member="serviceAccount:service-$PROJECT_NUMBER@gcp-sa-aiplatform-re.iam.gserviceaccount.com" \
    --role="roles/viewer"


source ~/instavibe-bootstrap/env/bin/activate
export PLATFORM_MPC_CLIENT_URL=$(gcloud run services list --platform=managed --region=us-central1 --format='value(URL)' | grep platform-mcp-client)
export PLANNER_AGENT_URL=$(gcloud run services list --platform=managed --region=us-central1 --format='value(URL)' | grep planner-agent)
export SOCIAL_AGENT_URL=$(gcloud run services list --platform=managed --region=us-central1 --format='value(URL)' | grep social-agent)

export REMOTE_AGENT_ADDRESSES=${PLANNER_AGENT_URL},${PLATFORM_MPC_CLIENT_URL},${SOCIAL_AGENT_URL}
sed -i "s|^\(O\?REMOTE_AGENT_ADDRESSES\)=.*|REMOTE_AGENT_ADDRESSES=${REMOTE_AGENT_ADDRESSES}|" ~/instavibe-bootstrap/agents/orchestrate/.env

adk deploy agent_engine \
--display_name "orchestrate-agent" \
--project $GOOGLE_CLOUD_PROJECT \
--region $GOOGLE_CLOUD_LOCATION \
--staging_bucket gs://$GOOGLE_CLOUD_PROJECT-agent-engine \
--trace_to_cloud \
--requirements_file orchestrate/requirements.txt \
orchestrate

Agora que o Orchestrator está hospedado na plataforma gerenciada do Agent Engine, nosso aplicativo Web InstaVibe precisa se comunicar com ele. Em vez de interagir pela interface de desenvolvimento do ADK, o web app fará chamadas remotas para o endpoint do Agent Engine.

10-agent-remote.png

Primeiro, precisamos modificar o código do aplicativo InstaVibe para inicializar o cliente do Agent Engine usando o ID exclusivo do agente do Orchestrator implantado. Esse ID é necessário para segmentar a instância de agente correta na plataforma.

👉📝 Abra ~/instavibe-bootstrap/instavibe/introvertally.py e substitua o #REPLACE ME initiate agent_engine pelo código a seguir. Isso recupera o ID do Agent Engine de uma variável de ambiente (que vamos definir em breve) e recebe um objeto cliente:

ORCHESTRATE_AGENT_ID = os.environ.get('ORCHESTRATE_AGENT_ID')
agent_engine = agent_engines.get(ORCHESTRATE_AGENT_ID)

O fluxo de usuários planejado no InstaVibe envolve duas interações com o agente: primeiro, gerar o plano recomendado e, segundo, pedir que o usuário confirme antes que o agente poste o evento na plataforma.

Como o web app InstaVibe (em execução no Cloud Run) e o agente do Orchestrator (em execução no Agent Engine) agora são serviços separados, o web app precisa fazer chamadas remotas para o endpoint do Agent Engine para interagir com o agente.

👉📝 Vamos atualizar o código que faz a chamada inicial para gerar a recomendação de plano. No mesmo arquivo introvertally.py, substitua #REPLACE ME Query remote agent get plan pelo snippet a seguir, que usa o cliente agent_engine para enviar a solicitação do usuário:

agent_engine.stream_query(
                user_id=user_id,
                message=prompt_message,
            )

👉📝 Em seguida, atualize o código que processa a confirmação do usuário (por exemplo, quando ele clica em "Confirmar plano"). Isso envia uma mensagem de acompanhamento para a mesma conversa no Agent Engine, instruindo o Orchestrator a postar o evento (que ele vai delegar ao agente de integração da plataforma). Substitua #REPLACE ME Query remote agent for confirmation por confirmação em introvertally.py com:

agent_engine.stream_query(
            user_id=agent_session_user_id,
            message=prompt_message,
        )

As rotas do aplicativo da Web precisam de acesso a essas funções. Verifique se as funções necessárias de introvertally.py estão importadas no arquivo de rotas do Flask.

👉📝 Em cd ~/instavibe-bootstrap/instavibe/ally_routes.py, primeiro vamos apontar para a instância e substituir # REPLACE ME TO ADD IMPORT pelo seguinte:

from introvertally import call_agent_for_plan, post_plan_event

👉📝 Adicione o recurso de protótipo ao InstaVibe. Em ~/instavibe-bootstrap/instavibe/templates/base.html, substitua <!–REPLACE_ME_LINK_TO_INTROVERT_ALLY–> pelo seguinte:

            <li class="nav-item">
              <a class="nav-link" href="{{ url_for('ally.introvert_ally_page') }}">Introvert Ally</a>
            </li>

Antes de reimplantar o app InstaVibe, precisamos do Resource ID específico do agente do Orchestrator que implantamos no Agent Engine.

No momento, a recuperação programática usando gcloud pode ser limitada. Por isso, vamos usar um script auxiliar do Python (temp-endpoint.py fornecido no workshop) para buscar o ID e armazená-lo em uma variável de ambiente.

👉💻 Execute os comandos a seguir para executar o script. O script vai capturar o ID do endpoint do Agent Engine e conceder as permissões necessárias à conta de serviço padrão do mecanismo de agente. Observação: o script está configurado para usar a conta de serviço padrão porque ela não pode ser modificada pelo usuário no momento.

. ~/instavibe-bootstrap/set_env.sh
cd ~/instavibe-bootstrap/instavibe/
source ~/instavibe-bootstrap/env/bin/activate
python temp-endpoint.py
export ORCHESTRATE_AGENT_ID=$(cat temp_endpoint.txt)
echo "ORCHESTRATE_AGENT_ID set to: ${ORCHESTRATE_AGENT_ID}"

ID do endpoint do Agent Engine

Por fim, precisamos reimplantar o aplicativo da Web InstaVibe com o código atualizado e a nova variável de ambiente ORCHESTRATE_AGENT_ID para que ele saiba como se conectar ao nosso agente em execução no Agent Engine.

👉💻 Os comandos a seguir recriam a imagem do aplicativo InstaVibe e implantam a nova versão no Cloud Run:

. ~/instavibe-bootstrap/set_env.sh

cd ~/instavibe-bootstrap/instavibe/

export IMAGE_TAG="latest"
export APP_FOLDER_NAME="instavibe"
export IMAGE_NAME="instavibe-webapp"
export IMAGE_PATH="${REGION}-docker.pkg.dev/${PROJECT_ID}/${REPO_NAME}/${IMAGE_NAME}:${IMAGE_TAG}"
export SERVICE_NAME="instavibe"

echo "Building ${APP_FOLDER_NAME} webapp image..."
gcloud builds submit . \
  --tag=${IMAGE_PATH} \
  --project=${PROJECT_ID}

echo "Deploying ${SERVICE_NAME} to Cloud Run..."

gcloud run deploy ${SERVICE_NAME} \
  --image=${IMAGE_PATH} \
  --platform=managed \
  --region=${REGION} \
  --allow-unauthenticated \
  --set-env-vars="SPANNER_INSTANCE_ID=${SPANNER_INSTANCE_ID}" \
  --set-env-vars="SPANNER_DATABASE_ID=${SPANNER_DATABASE_ID}" \
  --set-env-vars="APP_HOST=0.0.0.0" \
  --set-env-vars="APP_PORT=8080" \
  --set-env-vars="GOOGLE_CLOUD_LOCATION=${REGION}" \
  --set-env-vars="GOOGLE_CLOUD_PROJECT=${PROJECT_ID}" \
  --set-env-vars="GOOGLE_MAPS_API_KEY=${GOOGLE_MAPS_API_KEY}" \
  --set-env-vars="ORCHESTRATE_AGENT_ID=${ORCHESTRATE_AGENT_ID}" \
  --project=${PROJECT_ID} \
  --min-instances=1 \
  --cpu=2 \
  --memory=2Gi

Com a implantação final concluída, acesse o URL do aplicativo InstaVibe em outra guia do navegador.

Teste da experiência completa do InstaVibe com tecnologia de IA

O recurso "InstaVibe Ally" já está disponível. Ele é alimentado pelo nosso sistema multiagente orquestrado pelo Vertex AI Agent Engine e se comunica por A2A.

12-02-new.png

Clique em "InstaVibe Ally" e peça para ele planejar um evento.

12-03-introvertally.png

Observe o registro de atividades à direita enquanto os agentes trabalham. Isso pode levar de 90 a 120 segundos. Quando o plano aparecer, revise-o e clique em "Confirmar este plano" para continuar com a postagem.

12-04-confirm.png

O orquestrador vai instruir o agente da plataforma a criar a postagem e o evento no InstaVibe. 12-05-posting.png

Confira a nova postagem e o evento na página inicial do InstaVibe. 12-06-instavibe.png

A página do evento vai refletir os detalhes gerados pelo agente.

12-07-event.png

Analisar o desempenho com o Cloud Trace

O processo pode levar algum tempo. O Vertex AI Agent Engine se integra ao Cloud Trace, permitindo analisar a latência do nosso sistema multiagente.

Acesse Traces no console do Google Cloud, selecione agent_run[orchestrate_agent] no intervalo. Você vai ver alguns intervalos. Clique neles.

12-08-trace.png

Nos detalhes do rastreamento, você pode identificar quais partes levaram mais tempo. Por exemplo, as chamadas ao agente do Planner podem apresentar maior latência devido à fundamentação da pesquisa e à geração complexa. 12-09-plan.png

Da mesma forma, ao criar a postagem e o evento, você pode ver o tempo gasto pelo Orchestrator processando dados e preparando chamadas de ferramentas para o agente da plataforma. 12-10-post.png

Ao analisar esses rastreamentos, você entende e otimiza a performance do seu sistema de agente.

celebrate.png

Parabéns! Você criou, implantou e testou com sucesso um sistema sofisticado de IA multiagente usando o ADK, o A2A, o MCP e os serviços do Google Cloud. Você aprendeu sobre orquestração de agentes, uso de ferramentas, gerenciamento de estado e implantação na nuvem, criando um recurso funcional com tecnologia de IA para o InstaVibe. Parabéns por concluir o workshop!

13. Limpeza

Para evitar cobranças contínuas na sua conta do Google Cloud, exclua os recursos criados durante este workshop. Os comandos a seguir ajudam a remover a instância do Spanner, os serviços do Cloud Run, o repositório do Artifact Registry, a chave de API, o mecanismo de agente da Vertex AI e as permissões do IAM associadas.

Importante:

  • Verifique se você está executando esses comandos no mesmo projeto do Google Cloud usado no workshop.
  • Se você fechou o terminal do Cloud Shell, algumas variáveis de ambiente, como $PROJECT_ID, $SPANNER_INSTANCE_ID etc., podem não estar definidas. Você precisará exportá-las novamente, como fez durante a configuração do workshop, ou substituir as variáveis nos comandos abaixo pelos valores reais.
  • Esses comandos vão excluir seus recursos de forma permanente. Verifique antes de executar se você tem outros dados importantes neste projeto.

👉💻 Execute os scripts a seguir para fazer a limpeza.

Redefinir variáveis de ambiente

. ~/instavibe-bootstrap/set_env.sh

Excluir mecanismo do agente:

cd ~/instavibe-bootstrap/utils
source ~/instavibe-bootstrap/env/bin/activate
export ORCHESTRATE_AGENT_ID=$(cat ~/instavibe-bootstrap/instavibe/temp_endpoint.txt)
echo "ORCHESTRATE_AGENT_ID set to: ${ORCHESTRATE_AGENT_ID}"
python remote_delete.py
deactivate
echo "Vertex AI Agent Engine deletion initiated."

Exclua os serviços do Cloud Run:

# InstaVibe Web Application
gcloud run services delete instavibe --platform=managed --region=${REGION} --project=${PROJECT_ID} --quiet

# MCP Tool Server
gcloud run services delete mcp-tool-server --platform=managed --region=${REGION} --project=${PROJECT_ID} --quiet

# Planner Agent (A2A Server)
gcloud run services delete planner-agent --platform=managed --region=${REGION} --project=${PROJECT_ID} --quiet

# Platform MCP Client Agent (A2A Server)
gcloud run services delete platform-mcp-client --platform=managed --region=${REGION} --project=${PROJECT_ID} --quiet

# Social Agent (A2A Server)
gcloud run services delete social-agent --platform=managed --region=${REGION} --project=${PROJECT_ID} --quiet

echo "Cloud Run services deletion initiated."

Interromper e remover o contêiner do Docker do A2A Inspector

docker rm --force a2a-inspector

Excluir instância do Spanner:

echo "Deleting Spanner instance: ${SPANNER_INSTANCE_ID}..."
gcloud spanner instances delete ${SPANNER_INSTANCE_ID} --project=${PROJECT_ID} --quiet
echo "Spanner instance deletion initiated."

Exclua o repositório do Artifact Registry:

echo "Deleting Artifact Registry repository: ${REPO_NAME}..."
gcloud artifacts repositories delete ${REPO_NAME} --location=${REGION} --project=${PROJECT_ID} --quiet
echo "Artifact Registry repository deletion initiated."

Remover papéis da conta de serviço:

echo "Removing roles from service account: $SERVICE_ACCOUNT_NAME in project $PROJECT_ID"

# Remove Project-level roles for default service account
gcloud projects remove-iam-policy-binding $PROJECT_ID \
  --member="serviceAccount:$SERVICE_ACCOUNT_NAME" \
  --role="roles/spanner.admin"

gcloud projects remove-iam-policy-binding $PROJECT_ID \
  --member="serviceAccount:$SERVICE_ACCOUNT_NAME" \
  --role="roles/spanner.databaseUser"

gcloud projects remove-iam-policy-binding $PROJECT_ID \
  --member="serviceAccount:$SERVICE_ACCOUNT_NAME" \
  --role="roles/artifactregistry.admin"

gcloud projects remove-iam-policy-binding $PROJECT_ID \
  --member="serviceAccount:$SERVICE_ACCOUNT_NAME" \
  --role="roles/cloudbuild.builds.editor"

gcloud projects remove-iam-policy-binding $PROJECT_ID \
  --member="serviceAccount:$SERVICE_ACCOUNT_NAME" \
  --role="roles/run.admin"

gcloud projects remove-iam-policy-binding $PROJECT_ID \
  --member="serviceAccount:$SERVICE_ACCOUNT_NAME" \
  --role="roles/iam.serviceAccountUser"

gcloud projects remove-iam-policy-binding $PROJECT_ID \
  --member="serviceAccount:$SERVICE_ACCOUNT_NAME" \
  --role="roles/aiplatform.user"

gcloud projects remove-iam-policy-binding $PROJECT_ID \
  --member="serviceAccount:$SERVICE_ACCOUNT_NAME" \
  --role="roles/logging.logWriter"

gcloud projects remove-iam-policy-binding $PROJECT_ID \
  --member="serviceAccount:$SERVICE_ACCOUNT_NAME" \
  --role="roles/logging.viewer"


echo "All specified roles have been removed."

Excluir arquivos do workshop local:

echo "Removing local workshop directory ~/instavibe-bootstrap..."
rm -rf ~/instavibe-bootstrap
rm -rf ~/a2a-inspector
rm -f ~/mapkey.txt
rm -f ~/project_id.txt
echo "Local directory removed."

Limpeza