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.
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.
- No Builder, dê dois cliques no fundo, selecione a aba Rush e depois HTTP Listener.
- O Data² vai gerar uma referência automaticamente para o listener.
- Copie a URL gerada — você vai precisar dela no próximo passo.
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."
}
]
}
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).
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
contextIdque 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²:
- Crie um DataSet para armazenar as sessões — por exemplo, com os campos
contextId(string, chave da busca) ehistory(texto ou JSON com o histórico acumulado). - 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.
- Leia
- Processe a mensagem (com o histórico em mãos, se necessário).
- Atualize o registro no DataSet com a nova mensagem e resposta adicionadas ao histórico.
- 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:
| Campo | Descrição |
|---|---|
id | ID da requisição JSON-RPC — deve ser espelhado na resposta |
params.message.parts[0].text | Texto da mensagem do usuário |
params.message.messageId | ID único da mensagem |
params.message.contextId | ID 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=TOKENpara obter a identidade do usuário (email, sub), mas isso tem custo de latência.
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
contextId→ crie um novo e retorne-o. - Se a mensagem já tinha
contextId→ repita 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
| Estado | Descrição |
|---|---|
completed | Tarefa concluída com sucesso |
failed | Ocorreu um erro |
working | Em progresso (apenas em streaming — não usado aqui) |
input-required | Agente 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 sempreblocking: 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/getetasks/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ários —
workingeinput-requirednã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.