10 pontos por GN⁺ 2025-06-11 | 2 comentários | Compartilhar no WhatsApp
  • Com o aumento recente da adoção de agentes de IA, cresce a tendência de usar stacks híbridas baseadas na linguagem Go
  • Agentes têm como características longos tempos de execução, alto custo e esperas frequentes de entrada/saída
  • Go oferece um modelo de concorrência de alto desempenho, com goroutines leves, mecanismo centralizado de cancelamento e mensageria baseada em canais
  • A biblioteca padrão é ampla, e com a ferramenta de profiling pprof é fácil rastrear vazamentos de memória e de threads
  • Ainda assim, Go também tem limitações, como falta de ecossistema de machine learning, desempenho máximo não tão excepcional e menor suporte de terceiros em comparação com outras linguagens

O que é um agente

  • Um agente executa em um loop repetitivo e se refere a um processo capaz de decidir por conta própria a próxima etapa de execução
  • Em vez de seguir um caminho pré-definido como um workflow, ele determina se deve encerrar com base em condições (por exemplo, “testes aprovados”) ou em um número máximo de iterações
  • Em serviços reais, agentes executam por longos períodos, de segundos a horas, e têm alto custo devido a chamadas de LLM, controle de navegador e similares
  • Como precisam processar a entrada do usuário (ou de outros agentes), há muito tempo de espera de I/O (entrada/saída)

Por que a linguagem Go é adequada para agentes

Concorrência de alto desempenho

  • As goroutines do Go conseguem executar simultaneamente milhares ou dezenas de milhares de threads leves usando apenas 2 KB de memória
  • Cada goroutine aproveita múltiplos núcleos para processamento paralelo, permitindo operar sem dificuldade até agentes em estado de I/O ou espera
  • A comunicação baseada em canais (Channel) implementa sincronização por passagem de mensagens em vez de compartilhamento de memória (minimizando o uso de Mutex)
  • Isso é adequado para agentes que trocam mensagens de forma assíncrona e gerenciam estado

Mecanismo centralizado de cancelamento

  • Ao usar context.Context do Go, a maioria das bibliotecas e APIs suporta sinais de cancelamento, o que torna interromper a execução muito simples
  • Enquanto Node.js e Python misturam vários padrões de cancelamento, Go permite cancelar e liberar recursos com segurança de forma consistente

Biblioteca padrão rica

  • Go tem uma biblioteca padrão ampla e cobre praticamente todas as áreas, como HTTP/web, arquivos e I/O de rede
  • Todo I/O parte do pressuposto de operações bloqueantes dentro de goroutines, permitindo escrever a lógica de negócio de forma linear (straight-line)
  • Em Python, diferentes padrões de concorrência, como asyncio, threads e processos, coexistem e tornam tudo mais complexo

Ferramentas de profiling e diagnóstico

  • Com ferramentas integradas do Go, como pprof, é possível rastrear em tempo real vazamentos de memória e vazamentos de goroutines (threads)
  • Isso é uma vantagem para diagnosticar problemas de vazamento que podem surgir em agentes de longa duração e execução concorrente

Ótimo suporte de LLM para programação

  • Graças à sintaxe simples e à rica biblioteca padrão, LLMs escrevem bem código em estilo idiomático de Go
  • Como há pouca dependência de frameworks, o LLM precisa se preocupar menos com versões ou padrões específicos

Limitações do Go

  • O ecossistema e as bibliotecas de terceiros são mais fracos que os de Python e TypeScript
  • Não é adequado para implementação direta de machine learning (há limites de desempenho e suporte)
  • Quando é necessário desempenho máximo, Rust e C++ são opções melhores
  • Para desenvolvedores mais tolerantes com tratamento de erros, pode ser um pouco incômodo

2 comentários

 
codemasterkimc 2025-06-11

Melhor que Java, mas Rust é melhor que Go :)

 
GN⁺ 2025-06-11
Comentários no Hacker News
  • Destaca que, na maioria dos sistemas de agentes, o maior fator de latência acaba sendo a chamada ao LLM. As vantagens mencionadas no artigo não favorecem uma linguagem específica; na maior parte dos casos, o que existe são longos tempos de espera, uso de recursos caros, entrada de usuários ou de outros agentes e muito tempo de espera por I/O. Por essas características, argumenta-se que, mais do que velocidade ou eficiência de execução no servidor, a grande vantagem está no vasto ecossistema de bibliotecas e suporte de IA de linguagens como Python. Há quem aponte a necessidade de lidar com asyncio ou bibliotecas de multithreading em Python, mas a impressão é que o desenvolvimento de agentes não é tão difícil, e como alguém já desenvolveu fluxos de trabalho relacionados, é fácil começar

    • Pela experiência, ao construir agentes em Go, uma grande vantagem é que os padrões de concorrência e de gerenciamento de backpressure já são bem estabelecidos. Agentes geralmente envolvem transações com serviços externos lentos, e os padrões de concorrência de Go são muito úteis nesse tipo de trabalho. Claro que a linguagem em si não importa tanto, e JavaScript parece ser a mais usada. Ainda assim, para geração de código, parece haver uma boa sinergia entre Go e LLMs

    • Go se diferencia de Python por oferecer excelente tratamento de concorrência e facilidade de deploy. Em Go, basta distribuir um binário estático, o que elimina os problemas de ambiente e dependências comuns no Python

    • Agentes cumprem o papel de camada de orquestração, e Go, Erlang e Node parecem especialmente adequados para isso. Não é obrigatório ter uma grande quantidade de bibliotecas relacionadas a IA; tarefas intensivas em I/O podem ser abstraídas atrás de interfaces de ferramentas específicas de domínio, permitindo criar subsistemas na linguagem necessária

  • Go tende a não oferecer grande vantagem nesse tipo de carga de trabalho, já que a maior parte do tempo é gasta esperando por I/O. O sistema de tipos de Go é limitado, e muitos recursos nativos em linguagens modernas exigem contornos em Go. TypeScript é uma excelente linguagem de cola para IA, e junto com Python tem suporte de bibliotecas muito bom. A preferência por TypeScript em vez de Python vem do fato de seu sistema de tipos ser muito mais poderoso e maduro. Python também está melhorando rapidamente. A afirmação de que é muito difícil interromper tarefas de longa duração em Node.js e Python parece não ter base sólida. A maioria das ferramentas já oferece esse recurso, e as principais linguagens continuam sendo Python e JS

    • Se desempenho for realmente necessário, a preferência é escrever módulos nativos em C++ ou Rust, que são mais rápidos que V8. Não há necessidade de ir até Go
  • Foram feitos experimentos com Elixir e frameworks de agentes baseados em BEAM, e a combinação de BEAM com SQLite parece ser, neste momento, a mais ideal para agentes. É possível trocar agentes com segurança sem redeploy da aplicação, e a concorrência do BEAM sobra para esse tipo de tarefa. Também é muito fácil implementar agentes baseados em estado ou efêmeros. No futuro, a ideia é construir agentes-base em Python, TypeScript e Rust, além de criar servidores MCP para viabilizar o desenvolvimento de agentes complexos conforme a preferência por cada linguagem

    • Recomenda o projeto Extism e o SDK de Elixir. Com essa combinação, é possível criar serviços centrais, roteamento e passagem de mensagens em Elixir, aproveitando as vantagens de BEAM/OTP, além de embutir, como plugins, agentes em forma de módulos Wasm pequenos e leves escritos em outras linguagens
      Extism
      SDK de Elixir

    • Fica a dúvida se houve algum motivo para escolher SQLite em vez do mnesia, o armazenamento de dados embutido do BEAM
      docs do mnesia

  • A maior parte do tempo de um agente é gasta esperando respostas do LLM e chamando serviços externos (API, DB). O impacto de desempenho do runtime da linguagem, na prática, é quase nulo. Se houver um recurso de linguagem realmente importante para desempenho e escalabilidade de agentes, ele provavelmente seria o desempenho de serialização e desserialização de JSON

    • Por isso, parece melhor usar uma linguagem como TypeScript, que lida com JSON nativamente. O sistema de tipos de TypeScript também é muito mais poderoso que o de Go

    • Pela experiência, além das chamadas ao LLM, o item mais caro em agentes é a resolução de conflitos em edições assíncronas (merge, diff, patch). Esse trabalho também pode ser delegado a bibliotecas de baixo nível, mas é um problema tão difícil de otimizar quanto a serialização

  • Isso lembra este guia de construção de agentes da ampcode.com. Em Python, por causa das características dinâmicas da linguagem, é natural usar decorators para transformar métodos diretamente em chamadas de ferramenta, gerar listas por iteração de funções de ferramenta e converter rapidamente para JSON Schema. Já uma arquitetura em que múltiplos gatilhos externos (por exemplo, entrada do usuário, e-mails do Gmail, mensagens do Slack etc.) disparam novas execuções de agentes pareceu muito mais intuitiva em Go, usando channels e loops for com switch. Em Python, seria preciso montar várias filas e threads separadamente, o que complica bastante

  • Seguindo a lógica do artigo, Elixir seria ideal para agentes

  • O sistema de tipos limitado e insuficiente de Go o torna inadequado para quase qualquer aplicação. Na prática, o maior defeito de Go seria a própria linguagem. Os aspectos extracódigo é que acabam tornando Go tolerável

    • Após anos programando em Go, há concordância de que o sistema de tipos tem muitos problemas. No campo de LLM, parece que quase todo mundo usa Python ou JavaScript. A ideia é que todos deveriam migrar para linguagens modernas, mas Go talvez ainda seja uma escolha menos ruim que os imports e os problemas de pacotes bagunçados de Python/JavaScript

    • Gostaria de ouvir de forma concreta como as limitações do sistema de tipos de Go atrapalham a criação de agentes

    • Na prática, Go recebeu um sistema de tipos estático porque não se encontrou outra maneira de atingir os requisitos de desempenho. Faz mais sentido vê-lo como uma linguagem usada de forma parecida com linguagens dinamicamente tipadas, e o problema vem de uma interpretação equivocada dos objetivos de design da linguagem. É possível argumentar que linguagens dinamicamente tipadas são inadequadas em geral, mas o uso ativo de Python, Erlang, Elixir e outras linguagens dinâmicas sugere justamente que tipagem dinâmica combina melhor com esse domínio de problema

    • Múltiplos valores de retorno não se compõem bem, o suporte a erros é melhor do que exceções, mas muito verboso, channels são fáceis de usar errado e o tipo enum é decepcionante. Ainda assim, interfaces funcionam surpreendentemente bem, e o sistema de empacotamento é bem fluido. Ao aprender Rust, ficou claro que a estrutura de arquivos é muito mais complexa que em Go. Como a linguagem é simples, também é fácil criar vários linters e ferramentas de geração de código. A manutenção de longo prazo de código Go inspira menos preocupação do que Python/JS

    • Seria ótimo se existisse um dialeto de LISP/Scheme bem mantido que compilasse para Go

  • Em processos demorados e caros de executar, uma desvantagem é que, se o processo morrer, todo o trabalho se perde. Serializar o estado em um banco de dados durante a espera pode ser mais seguro, mas não parece haver uma linguagem em que isso seja fácil de implementar. Escrever máquinas de estado baseadas em checkpoint não é simples

    • Explica que máquinas de estado baseadas em checkpoint são um recurso central de plataformas como Hatchet(hatchet.run) e Temporal(temporal.io). Elas armazenam o histórico de execução de funções dentro do workflow e, quando há uma interrupção, reproduzem automaticamente esse histórico. Argumenta-se que histórico de progresso por saída é muito mais eficiente do que um memory snapshot. (Fundador da Hatchet)

    • Goroutines, threads e cadeias de longa execução acabam, no fim, sendo divididas em unidades atômicas de trabalho, e a serialização de estado é indispensável. Isso atende requisitos como recuperação de falhas, rastreamento de erros, reconsulta de resultados e distribuição em múltiplos nós. O framework Oban(github.com/oban-bg/oban) (Elixir) segue essa abordagem, e também é recomendado o artigo do Oban que enfatiza a importância da persistência de tarefas assíncronas. (Autor do Oban)

    • Está em desenvolvimento uma biblioteca de agentes baseada em golang, e a impressão é que, com logging suficiente, sempre será possível restaurar o estado do agente. Sabendo apenas o timestamp e a execução (run) pai, já dá para montar a árvore de execuções filha/ramificada. Sessões podem ser gerenciadas com mapas e banco de dados, e reconstruídas quando necessário. Em vez de manter objetos individuais em memória, objetos stateless são buscados por id em mapas, enquanto ações anteriores, etapas e contexto ficam em objetos de estado. A consistência de agente/workflow também é resolvida gerenciando os resultados por hash. Por enquanto, só agentes/ferramentas básicos foram implementados, e lógica de logging, restauração e cancelamento ainda não foi desenvolvida

    • Temporal é bastante útil para checkpointing de processos longos e é neutro em relação à linguagem

    • Também houve reflexão sobre filas de trabalho, incluindo a possibilidade de criar uma fila rudimentar em Postgres. As vantagens seriam distribuir a carga entre servidores, evitar perda de tarefas quando um processo termina e obter melhor visibilidade. Em contrapartida, a complexidade do código pode aumentar muito, o que torna difícil desenhar uma arquitetura simples

  • Engenheiros de IA evitam JavaScript com todas as forças. O fim do TensorFlow for Swift marcou o fim da diversidade de linguagens em IA

    • A aversão a JavaScript não seria algo restrito a engenheiros de IA. Mesmo para alguém que programa em JS há mais de 30 anos, isso faz sentido

    • JS é uma linguagem muito ruim, e levá-la para o backend foi um erro. TypeScript, no fim das contas, não resolve os problemas subjacentes do JS. A preferência é evitar JS ou TS e usar alternativas como Go, Rust, Python, Ruby, Elixir, F# etc.

    • Fica a curiosidade sobre por que JS seria especialmente bom para agentes

  • Há a sensação de que o campo de ML precisa de um modelo de concorrência melhor. Já se tentou fazer ML com Go, mas a falta de suporte de bibliotecas e a dependência de chamadas externas via gRPC ou wrappers tornam isso praticamente inviável. Python tem limitações, e C++ é verboso demais, o que prejudica a produtividade