9.6 KiB
Agentes do framework com A2A assíncrono
O que foi implementado
O agente criado a partir de templates/agent_template_backend pode expor o
protocolo nativo e A2A no mesmo processo. O Agent Gateway também oferece a
mesma fachada A2A. As duas fachadas usam o SDK oficial e suas operações de
Tasks/SSE, com interoperabilidade A2A 1.0 e 0.3.
Consumidor A2A ── Gateway A2A (Task)
├── agente nativo (/gateway/message)
└── agente framework A2A (Task remota)
Consumidor A2A ── agente framework A2A diretamente
O envio imediato devolve uma Task antes de terminar a operação de negócio.
A execução continua após a resposta HTTP e também após a desconexão do SSE.
O consumidor pode consultar o resultado ou se reconectar à tarefa em andamento.
Os endpoints nativos conservam seu comportamento; /gateway/message continua
aguardando o resultado, e o SSE nativo permanece separado do SSE A2A.
Habilitar no agente construído com o framework
Na raiz do framework, instale a dependência opcional:
python -m pip install -e 'libs/agent_framework[a2a]' -r templates/agent_template_backend/requirements.txt
No ambiente do backend:
ENABLE_A2A=true
A2A_TASK_MODE=true
A2A_PUBLIC_URL=http://localhost:8000
A2A_TASK_TIMEOUT_SECONDS=240
Inicie o backend com suas configurações normais. Por exemplo, após preparar o
.env e os arquivos de configuração do agente:
cd templates/agent_template_backend
python -m uvicorn app.main:app --host 0.0.0.0 --port 8000
O endereço público real deve ser usado em A2A_PUBLIC_URL. A configuração de
exemplo está em .env.a2a.example. Há também uma imagem opcional, construída
na raiz do framework:
docker build -f templates/agent_template_backend/Dockerfile.a2a -t framework-agent-a2a .
Os perfis de agents.yaml são publicados como skills no Agent Card:
GET /.well-known/agent-card.json. Use message.metadata.agent_id para escolher
um perfil; sem ele, o backend mantém sua seleção padrão.
Registrar esse agente no Agent Gateway
backends:
pedidos_a2a:
protocol: a2a
a2a_version: '1.0'
url: http://backend-pedidos:8000
a2a_rpc_path: /a2a
description: Consulta e acompanhamento de pedidos
keywords: [pedido, entrega, rastreamento]
default_agent_id: pedidos
No gateway, habilite ENABLE_A2A=true, A2A_TASK_MODE=true e configure seu próprio
A2A_PUBLIC_URL. Para uma execução mais longa, ajuste os limites, por exemplo:
A2A_TASK_TIMEOUT_SECONDS=300 e BACKEND_TIMEOUT_SECONDS=270, com o timeout do
backend em 240 segundos. A duração efetiva é limitada pelo menor prazo aplicável.
O gateway devolve sua própria Task imediatamente e consulta a Task remota até
conclusão ou solicitação de input/autenticação. Eventos de status remoto são
publicados como a2a.backend.status. Os IDs local e remoto podem ser distintos.
O resultado nativo inclui metadata.a2a_task_id e metadata.a2a_context_id para
correlação; o consumidor externo continua acompanhando o ID emitido pelo gateway.
Envio assíncrono A2A 1.0
Envie um POST /a2a com Content-Type: application/json e
A2A-Version: 1.0:
{
"jsonrpc": "2.0",
"id": "request-1",
"method": "SendMessage",
"params": {
"configuration": {"returnImmediately": true},
"message": {
"messageId": "message-1",
"contextId": "conversation-123",
"role": "ROLE_USER",
"parts": [{"text": "Consulte meu pedido"}],
"metadata": {"agent_id": "pedidos", "tenant_id": "default"}
}
}
}
A resposta contém result.task.id, contextId e um estado inicial como
TASK_STATE_SUBMITTED ou TASK_STATE_WORKING. Guarde esse ID e consulte:
{
"jsonrpc": "2.0",
"id": "request-2",
"method": "GetTask",
"params": {"id": "ID_DA_TAREFA"}
}
O resultado está em result. Quando status.state for TASK_STATE_COMPLETED,
o conteúdo final estará em artifacts[].parts. Há uma parte de texto e uma
parte data com a resposta original do framework.
Sem returnImmediately: true, o envio aguarda o estado final ou interrompido,
retornando uma Task no mesmo formato. Para manter o comportamento da primeira
entrega A2A, que retornava Message, configure A2A_TASK_MODE=false; nesse modo,
streaming fica desabilitado e não há Tasks locais.
Streaming e reconexão
Para enviar e acompanhar, use SendStreamingMessage no lugar de SendMessage
com os mesmos parâmetros. A resposta tem Content-Type: text/event-stream.
Cada campo data: contém uma resposta JSON-RPC. Os eventos contêm:
- A Task inicial.
- Atualizações de status: submitted/working e eventos de progresso.
- O artefato de resposta.
- O estado completed, failed, canceled, input-required ou auth-required.
O backend do framework publica marcos como flow.start, session.upserted,
workflow.started, workflow.completed e message.responded, quando o fluxo
passa por essas etapas. Eles aparecem em
result.statusUpdate.metadata.framework_event, com os dados correspondentes
em framework_payload. Isso não implica transmissão de tokens do LLM.
Para se reconectar a uma Task em andamento:
{
"jsonrpc": "2.0",
"id": "request-3",
"method": "SubscribeToTask",
"params": {"id": "ID_DA_TAREFA"}
}
A reconexão entrega o estado atual e os eventos seguintes; não há promessa de
replay de todos os eventos perdidos. Se a tarefa já terminou, consulte GetTask.
Cancelamento, falhas e continuidade
CancelTask, com params.id, cancela a execução local em andamento. O SDK
interrompe a coroutine e registra TASK_STATE_CANCELED. Isso não desfaz efeitos
já confirmados em ferramentas ou sistemas externos. Quando o gateway está
consultando um agente remoto, parar a tarefa local não garante cancelar a
operação que já foi aceita pelo serviço remoto.
Exceções e expiração de A2A_TASK_TIMEOUT_SECONDS resultam em
TASK_STATE_FAILED, consultável depois, com mensagem genérica. Detalhes internos
permanecem nos logs do servidor.
Para uma interação que exige nova entrada, um handler pode retornar
metadata.a2a_task_state: "input-required" ou "auth-required". O consumidor
envia outra mensagem com o mesmo taskId e contextId. A tarefa é retomada;
uma tarefa concluída não pode ser reexecutada. O adaptador de saída preserva a
continuidade de tarefas remotas nesses estados.
Os workflows nativos que não declaram esse estado concluem uma Task por turno;
a conversa continua com uma nova Task e o mesmo contextId, usando as sessões
e memórias já existentes no framework.
Equivalentes A2A 0.3
Use A2A-Version: 0.3, role: "user" e os discriminadores kind de 0.3.
| A2A 1.0 | A2A 0.3 |
|---|---|
SendMessage + returnImmediately: true |
message/send + blocking: false |
SendStreamingMessage |
message/stream |
GetTask |
tasks/get |
SubscribeToTask |
tasks/resubscribe |
CancelTask |
tasks/cancel |
Em 0.3, result contém diretamente a Task, sem o envelope task. Os estados
usam submitted, working, completed, failed, etc.
Armazenamento e autenticação
O padrão usa o TaskStore em memória do SDK. Tarefas e resultados são perdidos
quando o processo reinicia. Para múltiplos workers/réplicas, use afinidade de
requisições ao mesmo processo durante a execução ou configure armazenamento e
coordenação compartilhados. Um TaskStore persistente, sozinho, não torna a
execução retomável após falha. As funções install_a2a e install_backend_a2a
aceitam task_store para integração com um armazenamento do SDK.
O backend opcional reutiliza AGENT_AUTH_*; o gateway usa
AGENT_GATEWAY_AUTH_*. A identidade autenticada pelo middleware define o dono
das Tasks, incluindo consultas e cancelamento. Usuários anônimos compartilham
o escopo anônimo. tenant_id em metadata continua sendo dado do pedido, não
credencial nem isolamento de autorização. Clientes que compartilham uma mesma
credencial/principal compartilham o escopo de tarefas.
A2A_SECURITY_SCHEMES e A2A_SECURITY documentam os mecanismos no Agent Card,
como descrito no guia geral. SSE está habilitado no modo de Tasks;
notificações push e gRPC continuam fora desta implementação.
Validação
A suíte inclui envio imediato enquanto o handler permanece bloqueado, polling,
streaming com progresso e resultado, cancelamento, falhas, timeout, retomada,
separação entre identidades autenticadas e gateway chamando um backend A2A real
em aplicação local. Há também testes HTTP em 127.0.0.1 que verificam a chegada
do primeiro evento antes da conclusão e reconexão após desconexão.
python -m pip install pytest pytest-asyncio uvicorn
PYTHONPATH=libs/agent_framework/src pytest \
tests/test_a2a_interoperability.py tests/test_a2a_async_tasks.py \
tests/test_a2a_http_streaming.py \
tests/unit/test_sse.py tests/unit/test_sse_replay_dedup.py \
tests/unit/test_authentication.py tests/unit/test_authentication_policies.py -q
Os testes não acessam OCI nem serviços de negócio externos. Em ambientes sem permissão para abrir portas locais, execute o teste HTTP em um ambiente que permita bind em loopback.
Especificação oficial: https://a2a-protocol.org/latest/specification/ SDK oficial: https://github.com/a2aproject/a2a-python
Frontend pronto para A2A
A interface apps/agent_frontend_a2a consome este protocolo diretamente, com
Tasks, streaming, consulta e cancelamento. Consulte
Frontend A2A para a demonstração sem OCI
e os comandos de execução com o backend ou Gateway. O frontend nativo continua
em apps/agent_frontend.