JetBrains AI
Supercharge your tools with AI-powered features inside many JetBrains products
Koog × A2A: desenvolvimento de agentes de IA conectados no Kotlin
Se você já tiver tentado desenvolver um sistema de vários agentes de IA, provavelmente encontrou este problema. Ele começa bastante simples: você tem um agente escrevendo postagens de blog, outro revisando essas postagens e talvez um terceiro sugerindo ou gerando imagens. Todos eles, isoladamente, são eficazes. Mas fazê-los trabalharem juntos? É aqui que as coisas podem começar a dar errado.
Cada agente fala sua própria “língua”: um usa uma interface de API diferente, outro tem seu próprio formato de mensagens e cada um pode ter requisitos específicos de autenticação. Fazê-los se comunicarem significa escrever código específico de integração para cada conexão. Em vez de se concentrar em tornar os seus agentes mais inteligentes, rápidos ou úteis, você fica ocupado com a criação de conexões entre eles.
O que faz o A2A: a camada de comunicação entre agentes
É aqui que entra o protocolo Agent2Agent (A2A).
Com o A2A, os seus agentes podem se comunicar diretamente através de um protocolo padronizado, que funciona como um tradutor universal para o seu ecossistema de IA. O agente que escreve os seus blogs passa conteúdo de forma transparente para o seu revisor, que aciona o seu gerador de imagens, enquanto o revisor retorna correções e o gerador de imagens solicita esclarecimentos sobre o estilo. Tudo isso é orquestrado através de uma camada unificada de comunicação.
Em vez de gerenciar dezenas de conexões ponto a ponto, o A2A fornece:
- Conectividade plug-and-play: Os agentes descobrem uns aos outros e se conectam automaticamente.
- Mensagens padronizadas: Um formato unificado, um protocolo claro e nenhuma dor de cabeça com traduções.
- Orquestração incorporada: Defina os fluxos de trabalho uma única vez e deixe o A2A cuidar da coordenação.
- Escalabilidade sem complexidade: Adicione ou reutilize agentes sem reescrever as conexões já existentes.
O resultado? Você ocupa o seu tempo melhorando os recursos dos seus agentes e não depurando as conversas entre eles. E o melhor é que você pode implementar os seus agentes na linguagem ou framework que preferir. Para usuários da JVM, o Koog é uma das melhores escolhas e na versão 0.5.0, ele se integra de forma transparente com o ecossistema do A2A.
O que faz o Koog: o mecanismo de orquestração interna
O Koog é um framework baseado em Kotlin para desenvolver agentes de IA visando JVM, Android, iOS, WebAssembly e aplicativos de navegador. Ele é excelente para:
- Gerenciar fluxos de trabalho complexos: Projete estratégias baseadas em grafos, com suporte a loops, branches, fallbacks e execução de branches paralelos.
- Componentes prontos para usar: Desfrute dos nós incorporados ao Koog para chamar LLMs e ferramentas externas, resumir o histórico de mensagens e executar estratégias inteiras.
- Orquestração de ferramentas: Transforme qualquer função no seu código em uma ferramenta que o seu agente de IA pode utilizar, sequencialmente ou até em paralelo.
- Integração nativa com MCP: Conecte-se de forma transparente a qualquer servidor de MCP, usando o Kotlin MCP SDK.
- Suporte a memória e armazenamento: Suporte a memória de agentes e fluxos de trabalho de RAG (Retrieval-Augmented Generation), com gerenciamento eficiente de contextos.
- Tolerância a falhas: Novas tentativas, criação de checkpoints, mecanismos de recuperação e persistência de estados, tudo isso incorporado para garantir uma execução confiável.
- Observabilidade: Tratamento completo de eventos de agentes, gravação de logs e suporte a OpenTelemetry, incorporando integrações com o Langfuse e o W&B Weave.
Em resumo, o Koog é excelente para desenvolver agentes de IA confiáveis.
Por que combinar o Koog com o A2A
O Koog e o A2A trabalham em camadas diferentes da pilha de agentes de IA. Quando usados juntos, eles se complementam e preenchem as lacunas.
O Koog já cuida das partes mais difíceis da orquestração de IA necessária para o uso corporativo no mundo real.
O A2A junta a peça que faltava: ele possibilita que os agentes do Koog se comuniquem com quaisquer outros agentes compatíveis com A2A no seu ecossistema. Em vez de criarem integrações específicas para cada serviço externo, os seus fluxos de trabalho de IA no Koog podem descobrir e usar outros agentes automaticamente.
O resultado é uma combinação perfeita: os fluxos de trabalho avançados do Koog tornam-se tarefas do A2A, que qualquer agente pode solicitar, enquanto os agentes do Koog aproveitam todo o poder do ecossistema do A2A. E como o Koog é executado no back-end, no próprio dispositivo e em ambientes de navegador, você pode entregar IA interconectada de forma mais ampla e eficaz do que nunca.
Como isso é possível? Vamos ver!
O protocolo A2A
O protocolo A2A define os componentes essenciais para a comunicação entre agentes:
- Descoberta de agentes através de fichas de agentes padronizadas (documentos em JSON que descrevem capacidades).
- Formatos de mensagens para solicitações e respostas, com esquemas consistentes.
- Gerenciamento do ciclo de vida das tarefas, com estados claros: solicitada → em processamento → concluída/falhada.
- Camadas de transporte, como JSON-RPC, gRPC e REST.
- Esquemas de segurança usando OAuth2-padrão, chaves de API e tokens de JWT.
- Tratamento de erros com códigos de erro padronizados.

Fichas de agentes: cartões de visita digitais
Cada agente no ecossistema do A2A publica suas capacidades através de uma “ficha de agente” — um arquivo padrão JSON, hospedado em alguma URL, como /.conhecida/ficha-de-agente.json, no domínio do agente. A ficha de agente funciona como um cartão de visita digital, possibilitando que outros agentes descubram os serviços que ele fornece.
Tipicamente, uma ficha de agente contém:
- Informações básicas, tais como o nome do agente, a descrição e a versão.
- Habilidades: O que o agente pode fazer (por exemplo, redigir documentos, revisar textos, analisar dados ou gerar imagens).
- Endpoints: Como se comunicar com o agente.
- Outras informações opcionais: Capacidades habilitadas, autenticação e outras.
Esse mecanismo de descoberta elimina a necessidade de trabalho manual de integração. Quando um agente precisa de uma habilidade específica, simplesmente verifica a ficha do agente relevante para compreender como interagir com aquele serviço.
No Koog, as fichas de agentes são definidas usando classes de dados do Kotlin:
val agentCard = AgentCard(
name = "Redator de blogs",
description = "Agente de IA que cria postagens e artigos de alta qualidade para blogs",
url = "https://api.blog-writer.com/a2a/v1",
version = "1.0.0",
capabilities = AgentCapabilities(streaming = true),
defaultInputModes = listOf("text/plain"),
defaultOutputModes = listOf("text/markdown"),
skills = listOf(
AgentSkill(
id = "redator-de-postagens",
name = "Redação de postagens de blog",
description = "Gere postagens envolventes de blog sobre qualquer assunto",
tags = listOf("writing", "content", "blog"),
examples = listOf("Escreva uma postagem sobre tendências em IA")
)
)
)
Mensagens universais: um único padrão simples
O A2A usa um formato de mensagens único e padronizado para toda a comunicação entre agentes. Essa simplicidade é poderosa: em vez de aprenderem dezenas de APIs diferentes, os agentes só precisam compreender um único padrão de comunicação.
Todas as interações seguem o mesmo fluxo:
- Enviar uma mensagem com a solicitação de uma tarefa e os parâmetros.
- Receber resultados imediatos ou uma tarefa para acompanhar.
- Obter atualizações através de canais em tempo real, no caso de operações mais longas.
Esta abordagem universal significa que para adicionar novas capacidades de agentes, não é preciso mudar o protocolo de comunicação. Quer você esteja solicitando que um agente resuma um texto ou gere um relatório complexo, a estrutura da mensagem continua consistente.
No Koog, criar e enviar uma mensagem é simples e direto, usando objetos e protocolos já implementados:
val message = Message(
role = Role.User,
parts = listOf(
TextPart("Escreva uma postagem de blog sobre o futuro dos agentes de IA")
),
contextId = "blog-project-456"
)
val request = Request(
data = MessageSendParams(
message = message,
configuration = MessageConfiguration(
blocking = false, // Obter primeira resposta
historyLength = 5 // Incluir contexto
)
)
)
val response = client.sendMessage(request)
O formato das mensagens tem suporte a conteúdo formatado, através de diferentes tipos Part, incluindo TextPart para conteúdo em texto simples, FilePart para arquivos anexados e DataPart para dados estruturados em JSON.
Essa estrutura unificada significa que os agentes do Koog podem se comunicar com qualquer agente compatível com A2A, seja para processamento de textos, análise de arquivos ou transformações complexas de dados.
Ciclo de vida das tarefas: fluxos de trabalho inteligentes
O A2A gerencia diferentes tipos de tarefas de forma inteligente, com base na complexidade e na duração:
Mensagens imediatas: Operações simples, como formatação de textos ou cálculos rápidos, que retornam resultados diretamente na resposta da IA. Não é necessário aguardá-las, nem acompanhá-las.
Tarefas de execução longa: Operações complexas, como análise de documentos ou fluxos de trabalho com várias etapas. Essas operações são agendadas e retornam uma tarefa. O agente solicitante pode então monitorar o andamento e coletar os resultados da tarefa, quando ela estiver concluída.
Atualizações em tempo real: No caso de operações muito demoradas, eventos enviados pelo servidor (SSE) fornecem atualizações do andamento em tempo real. Isso mantém os agentes informados, sem precisar de polling constante.
class BlogWriterExecutor : AgentExecutor {
override suspend fun execute(
context: RequestContext,
eventProcessor: SessionEventProcessor
) {
val task = Task(
contextId = context.contextId,
status = TaskStatus(
state = TaskState.Submitted,
message = Message(
role = Role.Agent,
parts = listOf(TextPart("Solicitação para redigir um blog recebida")),
contextId = context.contextId,
taskId = context.taskId,
)
)
)
eventProcessor.sendTaskEvent(task)
...
}
}
Segurança incorporada: apenas padrões do setor
O A2A não reinventa a segurança. Em vez disso, ele usa padrões comprovados e amplamente adotados, como OAuth2, chaves de API e HTTPS-padrão.
Esta abordagem significa que os desenvolvedores não precisam aprender novos esquemas de autenticação. Se você compreende a segurança moderna de APIs de Web, já compreende a segurança do A2A. O sistema herda todas as ferramentas, melhores práticas e auditorias de segurança que vêm junto com esses padrões consagrados.
"securitySchemes": {
"google": {
"openIdConnectUrl": "https://accounts.google.com/.well-known/openid-configuration",
"type": "openIdConnect"
}
}
class AuthorizedA2AServer(
agentExecutor: AgentExecutor,
agentCard: AgentCard,
agentCardExtended: AgentCard? = null,
taskStorage: TaskStorage = InMemoryTaskStorage(),
messageStorage: MessageStorage = InMemoryMessageStorage(),
private val authService: AuthService, // Serviço responsável pela autenticação
) : A2AServer(
agentExecutor = agentExecutor,
agentCard = agentCard,
agentCardExtended = agentCardExtended,
taskStorage = taskStorage,
messageStorage = messageStorage,
) {
private suspend fun authenticateAndAuthorize(
ctx: ServerCallContext,
requiredPermission: String
): AuthenticatedUser {
val token = ctx.headers["Authorization"]?.firstOrNull()
?: throw A2AInvalidParamsException("Token de autorização faltando")
val user = authService.authenticate(token)
?: throw A2AInvalidParamsException("Token de autorização inválido")
if (requiredPermission !in user.permissions) {
throw A2AUnsupportedOperationException("Permissões insuficientes")
}
return user
}
override suspend fun onSendMessage(
request: Request,
ctx: ServerCallContext
): Response {
val user = authenticateAndAuthorize(ctx, requiredPermission = "send_message")
// Passagem de dados do usuário ao executor do agente através do estado do contexto
val enrichedCtx = ctx.copy(
state = ctx.state + (AuthStateKeys.USER to user)
)
// Delegação à implementação de nível superior com conteúdo formatado
return super.onSendMessage(request, enrichedCtx)
}
// o resto dos métodos incorporadoa do A2A
// ...
}
Como integrar os agentes do Koog com o A2A
O framework Koog já tem incorporados tanto o cliente quanto o servidor de A2A. Isto significa que os agentes do Koog podem se comunicar com outros agentes compatíveis com A2A e, ao mesmo tempo, também se tornam passíveis de serem descobertos pelo mundo exterior. Veja um exemplo simples, demonstrando como você pode implementar isso.
Como incorporar agentes do Koog a servidores de A2A
Primeiro, defina uma estratégia para o agente. O Koog oferece conversores convenientes (toKoogMessage, toA2AMessage), que fazem transformações transparentes entre os formatos de mensagens do Koog e do A2A, eliminando a necessidade de serialização manual. Nós especializados, como nodeA2ASendMessage, cuidam desse processo de troca de mensagens, tornando os fluxos de trabalho de comunicação simples e diretos de implementar:
fun blogpostWritingStrategy() = strategy("blogpost-writer-strategy") {
val blogpostRequest by node { input ->
val userMessage = input.toKoogMessage().content
llm.writeSession {
user {
+"Escreva uma postagem de blog com base na solicitação do usuário"
+xml {
tag("user_request") {
+userMessage
}
}
}
requestLLM().toA2AMessage()
}
}
val sendMessage by nodeA2ARespondMessage()
nodeStart then blogpostRequest then sendMessage then nodeFinish
}
Depois, defina o próprio agente. Depois que você instalar o recurso A2AServer, o seu agente se tornará passível de ser descoberto e ficará acessível a outros no ecossistema, possibilitando a criação de redes sofisticadas, onde agentes especializados colaboram uns com os outros de forma transparente.
fun createBlogpostWritingAgent(
requestContext: RequestContext,
eventProcessor: SessionEventProcessor
): AIAgent {
// Obtenção das mensagens já existentes para o contexto da comunicação atual
val messageHistory = requestContext.messageStorage.getAll().map { it.toKoogMessage() }
val agentConfig = AIAgentConfig(
prompt = prompt("blogpost") {
system("Você é um agente para escrever postagens de blog")
messages(messageHistory)
},
model = GoogleModels.Gemini2_5Flash,
maxAgentIterations = 5
)
return agent = AIAgent(
promptExecutor = MultiLLMPromptExecutor(
LLMProvider.Google to GoogleLLMClient(System.getenv("GOOGLE_API_KEY")),
),
strategy = blogpostWritingStrategy(),
agentConfig = agentConfig
) {
install(A2AAgentServer) {
this.context = requestContext
this.eventProcessor = eventProcessor
}
handleEvents {
onAgentFinished { ctx ->
// Atualização do contexto da comunicação atual com a resposta do agente
val resultMessge = ctx.result as A2AMessage
requestContext.messageStorage.save(resultMessge)
}
}
}
}
Em terceiro lugar, precisamos incorporar o agente ao executor e depois definir um servidor.
class BlogpostAgentExecutor : AgentExecutor {
override suspend fun execute(
context: RequestContext,
eventProcessor: SessionEventProcessor
) {
createBlogpostWritingAgent(context, eventProcessor)
.run(context.params.message)
}
}
val a2aServer = A2AServer(
agentExecutor = BlogpostAgentExecutor(),
agentCard = agentCard,
)
A etapa final é definir um transporte do servidor e executar o servidor.
val transport = HttpJSONRPCServerTransport(
requestHandler = a2aServer
)
transport.start(
engineFactory = Netty,
port = 8080,
path = "/a2a",
wait = true,
agentCard = agentCard,
agentCardPath = A2AConsts.AGENT_CARD_WELL_KNOWN_PATH
)
Agora o seu agente está pronto para atender a solicitações!
Como invocar outros agentes compatíveis com A2A a partir de um agente do Koog
Primeiramente, você precisa configurar um cliente de A2A e conectá-lo, para ele buscar uma ficha de agente.
val agentUrl = "https://example.com"
val cardResolver = UrlAgentCardResolver(
baseUrl = agentUrl,
path = A2AConsts.AGENT_CARD_WELL_KNOWN_PATH,
)
val transport = HttpJSONRPCClientTransport(
url = agentUrl,
)
val a2aClient = A2AClient(
transport = transport,
agentCardResolver = cardResolver
)
// Inicialização do cliente e busca da ficha
a2aClient.connect()
Depois, você pode usar nodeA2ASendMessage ou nodeA2ASendMessageStreaming na sua estratégia, para invocar esses clientes e receber uma mensagem ou resposta a uma tarefa.
val agentId = "agent_id"
val agent = AIAgent(
promptExecutor = MultiLLMPromptExecutor(
LLMProvider.Google to GoogleLLMClient(System.getenv("GOOGLE_API_KEY")),
),
strategy = strategy("a2a") {
val nodePrepareRequest by node<String, A2AClientRequest> { input ->
A2AClientRequest(
agentId = agentId,
callContext = ClientCallContext.Default,
params = MessageSendParams(
message = A2AMessage(
messageId = Uuid.random().toString(),
role = Role.User,
parts = listOf(
TextPart(input)
)
)
)
)
}
val nodeA2A by nodeA2AClientSendMessage(agentId)
val nodeProcessResponse by node {
// Process event
when (it) {
is A2AMessage -> it.parts
.filterIsInstance()
.joinToString(separator = "n") { it.text }
is Task -> it.artifacts
.orEmpty()
.flatMap { it.parts }
.filterIsInstance()
.joinToString(separator = "n") { it.text }
}
}
nodeStart then nodePrepareRequest then nodeA2A then nodeProcessResponse then nodeFinish
},
agentConfig = agentConfig
) {
install(A2AAgentClient) {
this.a2aClients = mapOf(agentId to client)
}
}
agent.run("Escreva uma postagem de blog sobre a integração do A2A com o Koog")
Próximos passos
Para se aprofundar mais no Koog e no A2A, confira estes materiais úteis:
Artigo original em inglês por: