{"slug": "nao-achei-um-framework-go-production-ready-para-agentes-de-ia-entao-construi-um", "title": "Não achei um framework Go production-ready para agentes de IA. Então construí um.", "summary": "A Go developer built and open-sourced **eywa**, a production-ready framework for conversational AI agents, after finding no existing Go solutions suitable for real-world traffic and concurrency. The framework uses a hexagonal architecture with strict domain/infrastructure separation, where business logic defines interfaces (called \"ports\") for distributed locking, LLM providers, and other infrastructure concerns. Eywa is released under MIT license at v1.0.0, designed to handle concurrent webhook events and provide observability that Python-based AI frameworks lack in production.", "body_md": "*Como construí um framework Go para agentes de IA em produção — e as decisões de arquitetura que realmente importam.*\n\nQuinze anos escrevendo software profissionalmente e nunca open sourcei uma linha de código.\n\nNão é que eu não quisesse. É que é assim que funciona quando você constrói dentro de empresa — o código é deles, os problemas são específicos do domínio deles, e quando você finalmente tinha algo que poderia abstrair pra algo útil, já tinha passado pra próxima crise.\n\nDois meses atrás decidi mudar isso.\n\nEstava construindo um agente de IA conversacional em Go. Precisava de um framework. Fui procurar e encontrei... Python. Mais Python. Uns repos Go abandonados em 2023. E muito \"é só envolver o SDK da OpenAI\" — conselho que funciona bem até você ter tráfego real e o agente começar a responder duas vezes para a mesma mensagem.\n\nNada production-ready. Nada com arquitetura de verdade. Nada que eu pudesse passar para um time e falar *isso aguenta*.\n\nEntão construí. O resultado é o **eywa** — framework Go para agentes de IA conversacionais, arquitetura hexagonal, v1.0.0, licença MIT, open source.\n\nO nome vem de Avatar — Eywa é a rede neural que conecta todos os seres vivos em Pandora. A metáfora encaixou: um sistema que conecta LLMs, canais, memória e ferramentas num organismo só, onde cada parte percebe e responde ao ambiente.\n\nAqui o que aprendi construindo.\n\nO ecossistema de IA vive em Python. LangChain, LlamaIndex, CrewAI — tudo Python. Se você está prototipando ou rodando notebooks, faz sentido total.\n\nMas se você está rodando algo em produção com escala de verdade — usuários reais mandando mensagens reais e você precisando de observabilidade, controle de concorrência e algo que não cai às 3 da manhã — Go é uma história completamente diferente.\n\nGo te dá:\n\n`go test -race`\n\n) — que vai encontrar bugs que Python nem vêOs frameworks Python assumem que você vai ter uma requisição por vez ou vai tratar concorrência via filas fora do framework. Quando você está lidando com webhooks de WhatsApp em escala — múltiplos eventos do mesmo usuário chegando com milissegundos de diferença — essa suposição quebra.\n\nO princípio central do eywa é que o domínio de negócio não pode ter absolutamente nenhum conhecimento de infraestrutura.\n\nSem import do SDK da OpenAI no código de domínio. Sem chamadas ao Redis. Sem queries no MongoDB. Só interfaces — o que o eywa chama de **ports**.\n\nO domínio define o que precisa. A infraestrutura implementa. O wiring acontece na inicialização.\n\nAqui o port do Bond — o lock distribuído:\n\n```\ntype Bond interface {\n    AcquireLock(ctx context.Context, key string, ttl time.Duration) (bool, error)\n    ReleaseLock(ctx context.Context, key string) error\n    ExtendLock(ctx context.Context, key string, ttl time.Duration) error\n}\n```\n\nO domínio sabe que pode adquirir e liberar locks. Não sabe que a implementação usa Redis Redlock por baixo. Em testes, você injeta um no-op. Em produção, injeta o adapter Redis.\n\nMesmo padrão para o Oracle — a abstração de LLM:\n\n```\ntype OracleRequest struct {\n    Model         string\n    SystemPrompt  string\n    Messages      []OracleMessage\n    Temperature   float64\n    MaxTokens     int\n    Tools         []OracleTool\n    UseTools      bool\n    Attachments   []LLMAttachment\n}\n```\n\nO domínio manda um `OracleRequest`\n\n. Se vai para Anthropic, OpenAI, Gemini, Bedrock ou VertexAI é detalhe de infraestrutura. Troca provider na inicialização. Roda múltiplos ao mesmo tempo. O domínio não sabe e não precisa saber.\n\nIsso não é over-engineering. É o que torna o sistema testável, manutenível e sobrevivível quando o próximo provider de LLM lançar e todo mundo quiser trocar.\n\nUma coisa onde investi pesado: nomenclatura. Não só nomes limpos de variável — um vocabulário de domínio consistente que todo pedaço do código usa.\n\nSim, os nomes são intencionais. Queria um vocabulário de domínio consistente em vez de \"Manager\", \"Service\", \"Handler\" e \"Util\" — que não dizem nada sobre o que o componente realmente faz no contexto de um agente de IA.\n\n| Nome | O que é |\n|---|---|\nWeave |\nMotor de runtime — orquestra tudo por evento |\nSpirit |\nConfiguração do agente — LLM, tools, system prompt, comportamento |\nPulse |\nEvento de entrada — uma mensagem recebida de um canal |\nOracle |\nAbstração de LLM — manda prompt, recebe resposta |\nBond |\nLock distribuído — evita respostas duplicadas concorrentes |\nVoice |\nAdapter de saída — manda resposta de volta pro canal |\nScout |\nEnriquecimento de contexto — roda antes da chamada ao LLM |\nLore |\nRAG — geração aumentada por recuperação |\nImprint |\nInjeção de memória de longo prazo |\nVigil |\nHuman-in-the-loop — pausa o agente para resposta humana |\nRite |\nApproval workflow — barra ações por trás de confirmação humana |\nConduit |\nAdapter cliente MCP (Model Context Protocol) |\n\nQuando seu código fala `bond.AcquireLock(...)`\n\nem vez de `redisLock.Lock(...)`\n\n, você para de pensar em infraestrutura e começa a pensar no domínio. Terminologia é design.\n\nCenário que acontece em produção e quase nenhum framework trata:\n\nUsuário manda mensagem no WhatsApp. Webhook dispara. Seu agente começa a processar — chamada ao LLM em andamento, 800ms dentro dela.\n\nUsuário fica impaciente e manda a mesma mensagem de novo. Segundo webhook dispara.\n\nAgora você tem duas goroutines processando o contexto do mesmo usuário simultaneamente. A primeira termina, escreve a resposta e atualiza a memória. A segunda termina, escreve *outra* resposta usando estado de memória stale, sobrescrevendo a primeira atualização.\n\nO usuário recebe duas respostas. A memória está inconsistente. Você criou uma race condition no nível da aplicação.\n\nIsso é o Bond.\n\nAntes do Weave processar qualquer Pulse, ele adquire um lock distribuído com chave no session ID do usuário. Se o lock já está em posse de outra goroutine, o evento é descartado. Só um processamento ativo por usuário, sempre.\n\nO contrato é preciso: `AcquireLock`\n\nretorna `(false, nil)`\n\nquando o lock está ocupado (caso esperado), e `(false, error)`\n\nsó para falhas de infraestrutura. Essa distinção importa — o chamador trata os dois de forma diferente.\n\nO pipeline que roda a cada Pulse antes da chamada ao LLM:\n\n```\nPulse → Scouts → Pathfinder → Spirit → Oracle → Actions → Voice\n```\n\nScouts são etapas sequenciais de enriquecimento de contexto. Leem de sistemas externos e injetam conhecimento no Pulse antes do modelo ver qualquer coisa.\n\n```\ntype Scout interface {\n    GetName() string\n    Harvest(ctx context.Context, event *entities.Pulse) error\n    IsApplicable(event *entities.Pulse) bool\n}\n```\n\nA decisão crítica de design: **Scouts são fail-open**.\n\nUm Scout que retorna erro é logado. O pipeline continua sem os dados dele. A chamada ao LLM acontece assim mesmo.\n\nPor quê? Porque se um Scout está batendo num CRM para enriquecer o contexto do usuário, e o CRM está lento naquela manhã, você não quer que seu agente inteiro pare de responder. Você quer que continue funcionando com menos contexto, graciosamente.\n\nO Weave inteiro é montado na inicialização com um builder fluente:\n\n```\nweave, err := eywa.NewWeaveBuilder(ctx).\n    WithRepositories(spiritRepo, memoryRepo, echoRepo, chronicleRepo).\n    WithBond(bond).\n    WithActionRegistry(eywa.NewActionRegistry()).\n    WithScoutRegistry(eywa.NewScoutRegistry()).\n    AddOracle(eywaopenai.NewOracle(apiKey)).\n    WithConfig(config).\n    Build()\n```\n\nMongoDB para configuração de Spirits e histórico de conversa. Redis para lock distribuído e memória em andamento. OpenAI como Oracle. Tudo injetado — nada global.\n\nPara adicionar Anthropic como provider adicional:\n\n```\nAddOracle(eywaopenai.NewOracle(openaiKey)).\nAddOracle(eywaanthropic.NewOracle(anthropicKey)).\n```\n\nSpirits definem qual provider usam. O OracleFactory seleciona o correto em runtime.\n\nO framework inteiro é distribuído como 19 módulos Go independentes:\n\n```\ngithub.com/wmulabs/eywa                     # core\ngithub.com/wmulabs/eywa/fiber               # adapter HTTP\ngithub.com/wmulabs/eywa/mongo               # repositórios MongoDB\ngithub.com/wmulabs/eywa/redis               # Bond + memória Redis\ngithub.com/wmulabs/eywa/mcp                 # cliente MCP (Conduit)\ngithub.com/wmulabs/eywa/providers/anthropic\ngithub.com/wmulabs/eywa/providers/openai\ngithub.com/wmulabs/eywa/providers/gemini\ngithub.com/wmulabs/eywa/providers/bedrock\ngithub.com/wmulabs/eywa/providers/vertexai\ngithub.com/wmulabs/eywa/providers/weaviate\ngithub.com/wmulabs/eywa/providers/qdrant\ngithub.com/wmulabs/eywa/providers/pgvector\ngithub.com/wmulabs/eywa/providers/pinecone\ngithub.com/wmulabs/eywa/channels/whatsapp\ngithub.com/wmulabs/eywa/gcp/cloudtasks\ngithub.com/wmulabs/eywa/gcp/gcs\ngithub.com/wmulabs/eywa/gcp/gemini\n```\n\nSe você não usa Bedrock, não importa. Não recebe as dependências dele no `go.sum`\n\n. Não recebe a superfície de segurança dele. Desenvolvedor Go liga pra isso.\n\nAntes de chamar isso de v1.0, fiz uma revisão de segurança séria:\n\n`file://`\n\n, `ftp://`\n\n`io.LimitReader`\n\n`subtle.ConstantTimeCompare`\n\n— sem timing attacks`go test -race`\n\nNada disso é animador. Tudo importa.\n\neywa está em v1.0.0. Estável, hardened, documentado.\n\nSe você está construindo agentes de IA em Go — ou quer construir mas não achava nada sério o suficiente pra basear um sistema de produção — adoraria seu feedback.\n\nPull requests bem-vindos. Issues bem-vindas. Crítica direta bem-vinda.\n\nTalvez o mundo não precisasse de mais um framework de IA. Mas definitivamente precisava de mais engenharia nele.", "url": "https://wpnews.pro/news/nao-achei-um-framework-go-production-ready-para-agentes-de-ia-entao-construi-um", "canonical_source": "https://dev.to/wmoraes/-nao-achei-um-framework-go-production-ready-para-agentes-de-ia-entao-construi-um-1d6a", "published_at": "2026-05-29 05:13:54+00:00", "updated_at": "2026-05-29 05:42:37.214192+00:00", "lang": "en", "topics": ["ai-agents", "ai-tools", "ai-infrastructure", "large-language-models"], "entities": ["eywa", "Go", "Python", "LangChain", "LlamaIndex", "CrewAI", "OpenAI", "Avatar"], "alternates": {"html": "https://wpnews.pro/news/nao-achei-um-framework-go-production-ready-para-agentes-de-ia-entao-construi-um", "markdown": "https://wpnews.pro/news/nao-achei-um-framework-go-production-ready-para-agentes-de-ia-entao-construi-um.md", "text": "https://wpnews.pro/news/nao-achei-um-framework-go-production-ready-para-agentes-de-ia-entao-construi-um.txt", "jsonld": "https://wpnews.pro/news/nao-achei-um-framework-go-production-ready-para-agentes-de-ia-entao-construi-um.jsonld"}}