Pular para o conteúdo principal

Conectando Google Gemini Enterprise via A2A

O protocolo A2A (Agent-to-Agent) permite que agentes externos recebam mensagens de orquestradores como o Google Gemini Enterprise. Neste guia, você vai criar um HTTP Listener no Data² que funciona como endpoint A2A e recebe mensagens enviadas por usuários do Gemini Enterprise.

Protocolo em Draft — Implementação limitada no Gemini Enterprise

O protocolo A2A está na versão 0.3 (draft). O Google Gemini Enterprise implementa uma versão parcial e bastante limitada desse protocolo. Nem todos os recursos do spec oficial funcionam. Este guia documenta o que realmente funciona na prática com o Gemini Enterprise hoje.


Pré-requisitos

  • Acesso ao Google Gemini Enterprise Console com permissão para criar agentes A2A.
  • Um projeto no Data² Builder.

Passo 1: Criar o HTTP Listener no Data²

O HTTP Listener vai ser o endpoint que o Gemini Enterprise chamará sempre que um usuário enviar uma mensagem ao agente.

  1. No Builder, dê dois cliques no fundo, selecione a aba Rush e depois HTTP Listener.
  2. O Data² vai gerar uma referência automaticamente para o listener.
  3. Copie a URL gerada — você vai precisar dela no próximo passo.
tip

A URL do HTTP Listener aparece no painel do componente. Clique no botão ao lado da URL para copiá-la.


Passo 2: Registrar o agente no Gemini Enterprise

Acesse a seção de agentes do seu app no console:

https://console.cloud.google.com/gemini-enterprise/locations/global/engines/SEU_APP_ID/agentic/agents

Clique em Adicionar e na tela seguinte escolha Agente personalizado via A2A.

Na próxima tela — Importar agente — cole o JSON do agent card no campo de texto e clique em Avançar.

O Agent Card

O agent card descreve o seu agente: nome, URL do endpoint, capacidades e skills. Cole este JSON no campo "JSON do card do agente", substituindo a URL pela copiada no passo anterior:

{
"protocolVersion": "0.3.0",
"name": "Meu Agente Data²",
"description": "Agente que processa mensagens do Gemini Enterprise via Data² HTTP Listener.",
"url": "https://seu-app.data2.link/rush/SEU_HTTP_LISTENER",
"version": "1.0.0",
"capabilities": {
"streaming": false,
"pushNotifications": false
},
"defaultInputModes": ["text/plain"],
"defaultOutputModes": ["text/plain"],
"skills": [
{
"id": "chat",
"name": "Chat",
"description": "Responde perguntas e executa tarefas via linguagem natural."
}
]
}
info

streaming: false é o correto para o Gemini Enterprise — ele não suporta streaming. O campo skills é obrigatório e deve ter pelo menos um item.

Após colar o JSON, clique em Visualizar detalhes do agente para confirmar que o card foi lido corretamente, depois em Avançar para configurar as Autorizações (opcional — veja a seção de autenticação mais adiante).

caution

O Gemini Enterprise armazena uma cópia estática do agent card. Se a URL do HTTP Listener mudar, você precisa remover e re-registrar o agente.


Passo 3: Entender o payload recebido

O Gemini Enterprise envia mensagens no formato JSON-RPC 2.0 com method: "message/send" via POST para a URL do seu HTTP Listener.

O que é o contextId e como persistir uma sessão

O contextId é o identificador da sessão de conversa. Ele agrupa todas as mensagens de um mesmo thread — é o equivalente ao "ID da conversa" no contexto do A2A.

  • Na primeira mensagem, o Gemini não envia contextId. Cabe ao seu agente gerar um e retorná-lo na resposta.
  • Nas mensagens seguintes do mesmo thread, o Gemini envia o contextId que você retornou antes.

O Gemini não mantém histórico por conta própria. Se precisar de contexto entre mensagens (por exemplo, para passar o histórico da conversa para um modelo de IA), você precisa armazenar e recuperar esse estado no seu lado.

Como fazer isso no Data²:

  1. Crie um DataSet para armazenar as sessões — por exemplo, com os campos contextId (string, chave da busca) e history (texto ou JSON com o histórico acumulado).
  2. No handler do HTTP Listener, ao receber uma mensagem:
    • Leia body.params.message.contextId.
    • Se não houver contextId → crie um novo ID e insira um registro no DataSet com o histórico inicial.
    • Se houver contextId → busque o registro no DataSet por esse ID e recupere o histórico.
  3. Processe a mensagem (com o histórico em mãos, se necessário).
  4. Atualize o registro no DataSet com a nova mensagem e resposta adicionadas ao histórico.
  5. Retorne a resposta com o contextId — seja o recém-criado ou o que chegou na requisição.

Primeira mensagem (sem contextId)

A primeira mensagem de uma conversa não contém contextId:

{
"id": "49506c0d-ba88-482a-9ac8-33141ded6e8f",
"jsonrpc": "2.0",
"method": "message/send",
"params": {
"configuration": {
"acceptedOutputModes": [],
"blocking": true
},
"message": {
"kind": "message",
"messageId": "cfc94f40-2d44-439c-a098-155e64b025be",
"parts": [
{
"kind": "text",
"text": "Olá, preciso de ajuda!"
}
],
"role": "user"
},
"metadata": {}
}
}

Os campos mais relevantes dentro de body:

CampoDescrição
idID da requisição JSON-RPC — deve ser espelhado na resposta
params.message.parts[0].textTexto da mensagem do usuário
params.message.messageIdID único da mensagem
params.message.contextIdID da sessão — ausente na primeira mensagem

Mensagens seguintes (com contextId)

A partir da segunda mensagem na mesma thread, o Gemini envia o contextId que você retornou na primeira resposta:

{
"id": "6b9ca6b5-d633-4933-9778-ff2341f009c2",
"jsonrpc": "2.0",
"method": "message/send",
"params": {
"configuration": {
"acceptedOutputModes": [],
"blocking": true
},
"message": {
"contextId": "minha-sessao-123",
"kind": "message",
"messageId": "6d8ec61b-fe19-44d3-ae95-01264c8dfe01",
"parts": [
{
"kind": "text",
"text": "E aí, continuando..."
}
],
"role": "user"
},
"metadata": {}
}
}

Use o contextId para identificar a sessão e manter o histórico da conversa.


Passo 4: Autenticação

Ao registrar o agente, você pode configurar um mecanismo de autenticação para que o Gemini Enterprise injete credenciais nas requisições que envia ao seu listener. Quando isso está configurado, cada requisição chega com um header authorization: Bearer <token>:

{
"authorization": "Bearer ya29.a0ATkoCc79HA07MBCYf_TxY6W4...",
"content-type": "application/json",
"user-agent": "Google",
"host": "seu-app.data2.link",
...
}

O token é um OAuth2 access token do Google que representa o usuário autenticado no Gemini Enterprise. Ele é gerado via fluxo OAuth2 com consentimento do usuário — não é um token de serviço.

O que fazer com o token

Dentro do handler do seu HTTP Listener, você pode acessar o token via headers.authorization. A partir daí, as opções são:

  • Ignorar — se o seu agente não precisa de autenticação, você pode simplesmente não validar o token.
  • Repassar para uma API do Google Cloud que o usuário autorizou (ex: Google Sheets, Drive, etc.).
  • Introspectar — chamar https://oauth2.googleapis.com/tokeninfo?access_token=TOKEN para obter a identidade do usuário (email, sub), mas isso tem custo de latência.
Limitação: um único token Bearer

O Gemini Enterprise injeta exatamente um header Authorization. Se o seu listener precisar de uma credencial própria (ex: autenticação no próprio Data²) e ao mesmo tempo repassar o token do usuário para outra API, você terá que lidar com isso manualmente — não dá para ter dois tokens Bearer simultaneamente.


Passo 5: Configurar o handler para responder

A resposta deve seguir o formato A2A. O campo mais importante é o contextId:

  • Se a mensagem não tinha contextIdcrie um novo e retorne-o.
  • Se a mensagem já tinha contextIdrepita o mesmo na resposta.

Estrutura da resposta

{
"jsonrpc": "2.0",
"id": "<mesmo id da requisição recebida>",
"result": {
"id": "<id do task — pode ser qualquer string única>",
"contextId": "<id da sessão>",
"status": {
"state": "completed"
},
"artifacts": [
{
"artifactId": "<id do artifact>",
"name": "response",
"parts": [
{
"text": "Sua resposta aqui. Markdown é suportado."
}
]
}
]
}
}

Estados possíveis do status.state

EstadoDescrição
completedTarefa concluída com sucesso
failedOcorreu um erro
workingEm progresso (apenas em streaming — não usado aqui)
input-requiredAgente precisa de mais informações do usuário

Limitações conhecidas do Gemini Enterprise

O Gemini Enterprise implementa uma versão bastante restrita do protocolo A2A. O que não funciona ou tem suporte limitado:

  • Streaming (message/stream) — não suportado; o Gemini envia sempre blocking: true.
  • Push notifications — não implementado no contexto do Gemini Enterprise.
  • /.well-known/agent.json — o Gemini não faz discovery automático; o agent card é enviado manualmente no cadastro.
  • tasks/get e tasks/cancel — não utilizados; cada mensagem é tratada de forma síncrona e independente.
  • acceptedOutputModes — enviado sempre vazio ([]); ignorado na prática.
  • Histórico no payload — o Gemini não envia histórico da conversa; você precisa manter o contexto no seu lado usando o contextId.
  • Estados intermediáriosworking e input-required não são suportados na prática; todo processamento deve ser síncrono.

Tipos de artifacts — Markdown e imagens por URL

O spec A2A define vários tipos de parts dentro de um artifact: text, file (com URI ou bytes em base64), data (JSON estruturado), entre outros. No Gemini Enterprise, a grande maioria não funciona.

Na prática, o que funciona de forma confiável é:

1. text com conteúdo Markdown — o Gemini Enterprise renderiza o Markdown diretamente na interface: tabelas, listas, negrito, títulos, blocos de código e emojis são suportados.

"parts": [
{
"text": "## Resultado\n\nAqui vai sua resposta em **Markdown**."
}
]

2. Imagens via file com URI — imagens funcionam usando o tipo file com uma URL pública:

"parts": [
{
"kind": "file",
"file": {
"mimeType": "image/png",
"uri": "https://sua-url.com/imagem.png"
}
}
]

Evite usar file com bytes (base64), data ou múltiplos parts por artifact — o comportamento é imprevisível ou simplesmente ignorado.


Fluxo resumido

Usuário → Gemini Enterprise → POST /seu-http-listener

body.params.message.parts[0].text
body.params.message.contextId (se houver)
headers.authorization (se auth configurada)

Handler processa mensagem

Retorna JSON-RPC com contextId

Usuário ← Gemini Enterprise ← Exibe artifacts[0].parts[0].text

Na primeira mensagem, crie e retorne um contextId. Nas mensagens seguintes, o Gemini envia o mesmo contextId para você identificar a sessão.