top of page

Treinamento Amazon EKS NVRx reduz a recuperação de falhas de GPU de minutos para segundos

17 de set.
15 min de leitura

O Amazon EKS integrou o NVIDIA NVRx a uma pilha de treinamento reproduzível que recuperou falhas de GPU injetadas em aproximadamente 10 a 17 segundos. O design de treinamento Amazon EKS NVRx também manteve a eficiência de checkpoint acima de 99% em testes selecionados com 16 a 64 GPUs H100. Esses resultados desafiam uma suposição custosa: uma recuperação confiável precisa começar reiniciando contêineres ou reconstruindo todo o job do Kubernetes.

O sistema combina PyTorch Fully Sharded Data Parallel, ou FSDP, com três recursos distintos do NVRx. O checkpoint assíncrono transfere as gravações de armazenamento para fora do loop de treinamento. A reinicialização no processo reconstrói o estado distribuído sem substituir o processo Python. O componente ft_launcher inicia novos workers dentro do job existente após falhas mais graves.

A disputa importante, portanto, não é entre a AWS e outro provedor de nuvem. É entre recuperação orientada pela aplicação e recuperação baseada apenas na infraestrutura. O Kubernetes continua responsável pelo agendamento e por falhas no nível do nó, mas o NVRx trata falhas mais próximas do processo de treinamento. A AWS afirma que essa divisão reduz drasticamente o tempo que GPUs saudáveis e caras passam aguardando um par que falhou.

O treinamento Amazon EKS NVRx leva a recuperação para dentro do job

A mudança central é que a falha de um worker deixa de precisar se tornar um evento completo do ciclo de vida do contêiner.

A AWS e a NVIDIA construíram o ambiente de referência em torno do Amazon EKS, grupos de nós de GPU autogerenciados e PyTorch FSDP. Seu benchmark publicado usou instâncias p5.48xlarge, cada uma com oito GPUs NVIDIA H100 e 80 GB de memória.

O cluster testado escalou de dois para oito nós, ou de 16 para 64 GPUs. Cada instância também disponibilizava 32 interfaces Elastic Fabric Adapter para comunicação de alta largura de banda. O Amazon FSx for Lustre forneceu armazenamento compartilhado de checkpoints entre os pods de treinamento.

NVRx, abreviação de NVIDIA Resiliency Extension, é um pacote Python que adiciona componentes de recuperação e checkpoint a workloads do PyTorch. Ele não exige um fork do PyTorch, kernels personalizados ou recompilação. As equipes podem adicionar seus recursos de forma independente, em vez de substituir todo o framework de treinamento.

Esse design modular importa porque desempenho de checkpoint e recuperação de falhas são problemas diferentes. Um workload pode precisar de salvamentos mais rápidos sem recuperação de processo. Outro pode precisar de proteção contra falhas de processo enquanto mantém sua implementação de checkpoint atual.

A arquitetura de referência trata cada camada de acordo com o escopo da falha. A reinicialização no processo lida com exceções e travamentos de comunicação que mantêm o interpretador Python ativo. O ft_launcher lida com eventos como SIGKILL, encerramento por falta de memória e alguns travamentos no nível do sistema operacional.

O Kubernetes continua sendo a camada externa para falhas que removem um nó inteiro. Essa abordagem se assemelha a um conjunto de zonas de recuperação aninhadas. Cada mecanismo intervém apenas quando a falha ultrapassa o limite da camada abaixo dele.

Os pods de treinamento usam Serviços Kubernetes sem cabeçalho para descoberta de pares. Os workers encontram uns aos outros por DNS, em vez de endereços IP fixos. Esse arranjo ajuda workers substitutos a retornarem sem exigir que operadores reescrevam a configuração do job.

A AWS descreveu anteriormente o treinamento distribuído elástico no EKS usando ferramentas do PyTorch. O trabalho com NVRx estreita ainda mais o ciclo de recuperação. Ele se concentra em manter um job ativo produtivo quando ranks individuais falham, param ou desaparecem.

Essa distinção cria a tensão central do artigo. O Kubernetes pode restaurar a infraestrutura, mas a recuperação de infraestrutura não possui conhecimento detalhado sobre o estado do modelo, grupos de processos e o momento dos checkpoints. O NVRx leva essas decisões para a aplicação de treinamento.

Checkpoints bloqueantes consumiam cerca de 40% do tempo total

O primeiro problema de desempenho não era a computação de GPU. Era o tempo que cada rank passava aguardando os dados de checkpoint chegarem ao armazenamento.

Um checkpoint síncrono pausa o treinamento até que o estado necessário do modelo e do otimizador tenha sido gravado. Em um job FSDP distribuído, essa pausa afeta todos os ranks participantes. As GPUs saudáveis continuam alocadas, mas não realizam computação direta ou retropropagação durante a gravação.

A AWS informou que o checkpoint síncrono produziu apenas 57% a 61% de eficiência de treinamento em seus testes de escalabilidade. As gravações no armazenamento levaram cerca de 275 segundos, e essa duração permaneceu amplamente constante entre 16 e 64 GPUs. Adicionar computação, portanto, não eliminou a pausa limitada pelo armazenamento.

O checkpoint assíncrono do NVRx altera o caminho de gravação. O processo de treinamento prepara o estado na CPU e passa o trabalho a um processo persistente em segundo plano. O processo principal então retorna à próxima etapa de treinamento enquanto a E/S de armazenamento continua.

A implementação usa TorchAsyncCheckpoint e seu método async_save(). Antes de iniciar outro salvamento, ou antes de sair, a aplicação finaliza a operação pendente. Essa coordenação impede que um checkpoint inacabado colida silenciosamente com o próximo.

Os dicionários de estado local do FSDP fortalecem o design. Cada rank grava seu próprio fragmento, evitando uma operação all-gather e o gargalo de gravação de um único rank zero. A documentação do FSDP do PyTorch descreve o modelo mais amplo de fragmentação que distribui parâmetros entre os workers participantes.

Em um intervalo de checkpoint de 1.000 etapas, a AWS afirma que o checkpoint assíncrono NVRx alcançou 99,2% de eficiência de treinamento em dois nós. A eficiência chegou a 99,8% em oito nós. A comparação síncrona alcançou 60,3% em oito nós.

Esses números são resultados de benchmark relatados pelo fornecedor, não garantias para todos os modelos ou configurações de armazenamento. Ainda assim, o mecanismo por trás deles é direto. Se a computação durar mais que a gravação no armazenamento, a operação em segundo plano pode se encaixar quase inteiramente por trás do trabalho útil de treinamento.

O limite ficou claro quando a AWS aumentou a frequência dos checkpoints. Em oito nós e um checkpoint a cada 100 etapas, a eficiência síncrona caiu para 14,7%. A eficiência assíncrona também caiu, mas permaneceu mais alta, em 29,6%.

O motivo foi o timing. Cem etapas de treinamento levaram aproximadamente 280 segundos, enquanto a gravação do checkpoint levou cerca de 275 segundos. Quase não havia janela de computação disponível para ocultar a próxima operação de armazenamento.

Esse é o limite real do checkpoint assíncrono NVRx. A E/S assíncrona pode ocultar uma gravação por trás da computação, mas não pode tornar o armazenamento infinitamente rápido. Quando os salvamentos chegam tão rapidamente quanto o sistema de arquivos consegue concluí-los, a fila acaba exercendo pressão.

Mesmo com essa restrição, o recurso muda a forma como as equipes podem escolher intervalos de checkpoint. Sistemas síncronos incentivam menos checkpoints porque cada salvamento impõe tempo ocioso visível. Salvamentos pouco frequentes aumentam, então, a quantidade de treinamento perdida após uma falha.

O checkpoint assíncrono reduz essa compensação. As equipes podem salvar com mais frequência quando há computação suficiente entre as gravações. Um intervalo mais curto reduz a distância de rollback, enquanto a E/S sobreposta preserva mais do investimento em GPU.

O resultado não é simplesmente uma API de checkpoint mais rápida. É um equilíbrio diferente entre eficiência em estado estável e progresso recuperável. Esse equilíbrio se torna mais valioso à medida que os jobs se alongam e envolvem componentes mais sujeitos a falhas.

A reinicialização no processo desafia o modelo de recuperação apenas com Kubernetes

O caminho de recuperação mais rápido preserva o processo Python e reconstrói apenas os recursos distribuídos danificados pela falha.

Na implementação de referência, o NVRx envolve a função principal de treinamento com um controlador de reinicialização no processo. Se ocorrer uma exceção compatível, o wrapper interrompe a tentativa ativa e prepara outra chamada. O processo Python externo permanece ativo durante toda essa sequência.

O NVRx primeiro encerra o grupo de processos distribuídos do PyTorch danificado. Ele pode coletar rastros do gravador de voo, interromper os backends NCCL e destruir o grupo inválido. NCCL é a biblioteca de comunicação da NVIDIA para operações coletivas entre GPUs.

As verificações de integridade então examinam os recursos associados a cada rank. Essas verificações podem abranger a GPU, conexões NVLink, interfaces de rede e falhas repetidas de rank. Um controlador de tentativas limita as reinicializações e determina quantos ranks ativos precisam sobreviver.

O sistema reorganiza os ranks sobreviventes em um grupo contíguo e inicia um novo rendezvous. A função de treinamento envolvida recria seu modelo FSDP, carrega o checkpoint mais recente e retoma o trabalho. O interpretador Python e os objetos fora da função envolvida permanecem disponíveis.

Essa técnica é direcionada a falhas leves. Exemplos incluem exceções não tratadas da aplicação e travamentos de NCCL que o watchdog pode detectar. Ela não pressupõe que uma exceção Python surgirá de forma confiável em todas as chamadas nativas bloqueadas.

Em vez disso, um watchdog de progresso registra atividade entre operações de bytecode Python. Uma thread de monitoramento separada verifica o estado compartilhado e pode solicitar uma reinicialização quando um rank deixa de progredir. O sistema então coordena a interrupção entre os workers participantes.

A AWS comparou essa abordagem com o ft_launcher e a recuperação básica do Kubernetes. O experimento usou dois nós p5.48xlarge, 16 GPUs H100 e Llama 3.1 8B sob FSDP. O job executou 2.000 etapas e salvou a cada 500 etapas.

Os pesquisadores injetaram cinco falhas determinísticas em cada execução usando a mesma programação. A reinicialização no processo do NVRx recuperou cada falha em cerca de 10 segundos, sem reiniciar contêineres. A AWS mediu 31% de goodput de treinamento e 87% de goodput de infraestrutura.

O goodput de treinamento mede o tempo que produz progresso de treinamento válido. O goodput de infraestrutura mede o tempo em que a infraestrutura alocada permanece operacional e disponível. A diferença entre eles inclui trabalho que não avança o modelo, incluindo rollback e carregamento de checkpoints.

A recuperação básica do Kubernetes levou cerca de 270 segundos por falha injetada. Ela produziu 11,5% de goodput de treinamento e 35,8% de goodput de infraestrutura no experimento relatado. Isso torna a comparação maior do que uma otimização de inicialização de contêineres.

Segundo a AWS, um rank que falhou acionou timeouts de comunicação nos ranks sobreviventes. Os pods então reiniciaram fora de sincronia, causando ciclos repetidos de timeout e comportamento de CrashLoopBackOff. O orquestrador restaurou contêineres sem entender como o grupo de treinamento distribuído precisava se recuperar em conjunto.

A recuperação orientada pela aplicação tem acesso a esse contexto ausente. Ela sabe quando o progresso parou, qual grupo de processos se tornou inválido e qual checkpoint pode reiniciar o treinamento. O Kubernetes vê o estado dos pods e a integridade dos nós, mas não a semântica completa de uma etapa de treinamento FSDP.

Isso não torna a recuperação do Kubernetes desnecessária. Um nó inativo não pode preservar seu interpretador, estado CUDA ou processos locais. A substituição de nós ainda pertence à camada do cluster, e os workers recuperados continuam exigindo dados de checkpoint persistentes.

A pressão recai sobre equipes que dependem de reinicializações de pods como sua única política de tolerância a falhas. Essa abordagem continua simples, mas sua janela de recuperação pode desperdiçar um tempo substancial de aceleradores. Clusters maiores amplificam o custo porque uma falha pode deixar muitos workers saudáveis ociosos.

A tolerância a falhas do NVRx separa falhas leves e graves

Nenhum mecanismo único de reinicialização cobre todas as falhas, portanto a tolerância a falhas do NVRx separa a recuperação que preserva o processo da substituição de workers.

O componente ft_launcher lida com falhas que a reinicialização no processo não consegue superar. Entre elas estão SIGKILL, encerramentos por falta de memória e falhas que não deixam um interpretador Python utilizável. Ele substitui o torchrun, mantendo conceitos conhecidos de rendezvous.

Cada rank de treinamento cria um RankMonitorClient após a inicialização distribuída. O cliente envia heartbeats durante o treinamento. Servidores de monitoramento por rank comparam esses sinais com timeouts configurados para condições normais e de inicialização inicial.

A configuração da AWS usou um timeout de heartbeat de rank de 900 segundos. Seu timeout de heartbeat inicial era de 1.200 segundos, permitindo mais tempo para o primeiro carregamento do modelo. Um intervalo de monitoramento de cinco segundos controlava a frequência com que o launcher verificava o status dos workers.

Esses valores são exemplos de configuração, e não recomendações universais. Um timeout de heartbeat deve exceder o maior atraso legítimo entre sinais. Se for curto demais, checkpoints lentos ou a inicialização do modelo podem parecer workers com falha.

Quando um worker morre ou deixa de responder, o ft_launcher encerra os workers restantes. Ele recupera a memória da GPU, executa outro rendezvous e inicia processos novos dentro do mesmo job. Os novos workers restauram o estado a partir do checkpoint mais recente.

O guia do launcher mostra o mesmo padrão geral na stack NeMo RL da NVIDIA. Essa integração mais ampla sugere que o NVRx foi concebido como uma camada de resiliência reutilizável, e não como uma ferramenta exclusiva do EKS.

A AWS mediu cerca de 17 segundos de recuperação por falha injetada com o ft_launcher. A execução reportada atingiu 25,5% de goodput de treinamento e 85,9% de goodput de infraestrutura. Isso foi mais lento do que a recuperação no processo, mas muito mais rápido do que a referência de 270 segundos do Kubernetes.

A diferença reflete quanto estado cada método preserva. A reinicialização no processo mantém o interpretador e o processo externo ativos. O ft_launcher precisa criar novos workers, inicializar o estado distribuído, reconstruir o modelo e recarregar um checkpoint.

O carregamento de checkpoints pode dominar o intervalo de recuperação em escalas maiores. A criação mais rápida de processos não elimina a necessidade de ler shards do modelo e do otimizador. Portanto, a vazão do sistema de arquivos compartilhado continua sendo parte do projeto de tolerância a falhas.

A arquitetura de treinamento NVRx no Amazon EKS usou um sistema de arquivos FSx for Lustre SCRATCH_2 na mesma Availability Zone que seus nós de GPU. Esse posicionamento buscou reduzir a latência de leitura de checkpoints. Todos os workers podiam alcançar o mesmo estado persistido após a recuperação.

O checkpoint assíncrono do NVRx é ortogonal aos dois caminhos de reinicialização. Ele reduz o tempo ocioso relacionado a gravações e controla quanto progresso fica em risco. A camada de reinicialização determina a rapidez com que os workers retornam após uma falha.

Essa separação oferece mais escolhas aos operadores, mas também adiciona trabalho de política. Eles precisam decidir quais exceções podem acionar a recuperação no processo, quantas tentativas são seguras e quais verificações de saúde devem remover um rank. Também precisam definir timeouts de heartbeat e rendezvous.

A NVIDIA classifica o projeto NVRx como experimental e em desenvolvimento ativo. Sua documentação alerta que recursos e interfaces podem mudar. Equipes de produção devem tratar a seleção de versão e os testes de atualização como parte do plano de resiliência.

A AWS usou o NVRx 0.4.1 para reproduzir seu benchmark. A publicação recomenda a versão 0.6.0 com configuração atualizada do launcher para uma implantação atual. Essa distinção de versão é importante porque sistemas de tolerância a falhas ficam diretamente nos caminhos críticos de inicialização e recuperação.

Um mecanismo de recuperação com falha pode ser pior do que nenhuma automação se reiniciar repetidamente um job irrecuperável. Limites de tentativas, tamanho mínimo do world e contadores de falhas evitam loops ilimitados. Os operadores ainda precisam de alertas que diferenciem uma recuperação bem-sucedida de uma falha recorrente.

O Resultado de 99% Tem Limites Importantes

O benchmark sustenta um mecanismo robusto, mas não estabelece eficiência de 99% para toda carga de trabalho de treinamento distribuído.

A AWS testou uma configuração principal de modelo, Llama 3.1 8B com PyTorch FSDP, em instâncias p5 baseadas em H100. Usou rede EFA e armazenamento FSx for Lustre. Diferentes tamanhos de modelo, caminhos de armazenamento, formatos de checkpoint e durações de etapa alterarão a janela de sobreposição.

O número de 99% se aplica à eficiência de checkpoints assíncronos em intervalos selecionados. Ele não descreve o goodput de ponta a ponta sob falhas repetidas. Nos testes com falhas injetadas, o goodput de treinamento permaneceu em 31% para recuperação no processo e 25,5% para ft_launcher.

Esses resultados menores não contradizem as medições de checkpoint. Eles respondem a uma pergunta diferente. A eficiência assíncrona mede a sobrecarga de checkpoint durante o treinamento normal, enquanto o goodput inclui injeção de falhas, rollback, carregamento e outras tarefas de recuperação.

A frequência de checkpoints também tem um limite inevitável. Com um checkpoint a cada 100 etapas, o treinamento assíncrono alcançou 29,6% de eficiência, e não 99%. O sistema de armazenamento ficou ocupado quase continuamente porque as durações de computação e gravação eram semelhantes.

A pressão de memória também merece atenção. O checkpoint assíncrono prepara dados fora da operação imediata da GPU e atribui a um processo em segundo plano a responsabilidade pelas gravações. As equipes devem medir memória de CPU, profundidade de fila e backlog de armazenamento com seus dicionários de estado reais.

A cobertura de recuperação é outro limite. A reinicialização no processo não ajuda quando o sistema operacional encerra o worker ou o nó desaparece. O ft_launcher pode substituir um processo morto, mas ainda depende do job, da rede do cluster, do serviço de rendezvous e do armazenamento de checkpoints.

A perda de nós continua sendo uma preocupação do Kubernetes. Uma interrupção regional de armazenamento ou um checkpoint corrompido pode derrotar todas as camadas de recuperação simultaneamente. A arquitetura reduz o custo de várias falhas comuns, mas não elimina dependências compartilhadas.

A detecção de falhas também pode criar falsos positivos. Uma fase longa de compilação, uma pausa no carregamento de dados ou uma interrupção do sistema de arquivos pode exceder um timeout de heartbeat agressivo. O launcher então reiniciaria workers saudáveis e descartaria progresso válido.

As equipes precisam de dados de timeout específicos da carga de trabalho antes de ativar a recuperação automática. Elas devem registrar os maiores intervalos de inicialização do modelo, checkpoint, validação e entrada de dados. Os testes devem incluir falhas durante essas fases, não apenas falhas dentro de uma etapa regular de treinamento.

O benchmark da AWS usou falhas determinísticas injetadas. Essa abordagem permite comparações repetíveis, mas as falhas de produção são menos organizadas. Clusters reais podem enfrentar simultaneamente degradação de rede, lentidão de armazenamento, problemas térmicos e travamentos de processos.

A implementação de referência oferece às equipes um ponto de partida útil para reproduzir a configuração. A reprodução em diferentes famílias de instâncias e escalas de modelo determinará quão portáteis são as vantagens reportadas.

A complexidade operacional é a troca final. A stack inclui Jobs do Kubernetes, descoberta de pares baseada em DNS, recursos EFA, armazenamento compartilhado, wrappers NVRx, clientes de monitoramento e múltiplas camadas de timeout. Cada componente cria outra superfície de configuração.

Essa complexidade ainda pode se justificar quando uma grande frota de GPUs passa minutos ociosa após a falha de um rank. No entanto, jobs menores podem aceitar uma estratégia mais simples de reinicialização de pods. O cálculo relevante é o custo de recuperação multiplicado pela frequência de falhas, e não o prestígio do benchmark.

As equipes que avaliam o design devem acompanhar tanto o goodput de treinamento quanto o de infraestrutura. A utilização de GPU sozinha pode parecer saudável enquanto o modelo recarrega repetidamente checkpoints antigos. Um dashboard útil deve mostrar etapas concluídas, distância de rollback, atualidade do checkpoint e causa da reinicialização.

As organizações de engenharia também precisam de registros duráveis desses experimentos. Uma base de conhecimento de engenharia pesquisável pode conectar alterações de timeout, rastros de falhas e resultados de benchmark. Esse contexto ajuda as equipes a evitar repetir configurações de recuperação que falharam.

O Que Observar Após o Benchmark NVRx do Amazon EKS

As próximas evidências devem mostrar se o design mantém sua vantagem em modelos maiores, falhas reais e versões em evolução do NVRx.

O primeiro sinal é a reprodução independente além de 64 GPUs H100. A AWS testou a escalabilidade de dois a oito nós, mas a frequência de falhas e os custos de coordenação aumentam com o tamanho do cluster. Resultados em centenas de aceleradores revelariam melhor os limites de rendezvous e de carregamento de checkpoints.

Um teste maior deve reportar mais do que o tempo médio de recuperação. A distribuição importa porque recuperações raras de cinco minutos podem dominar a economia de uma execução longa. Os relatórios devem incluir latência de cauda, tentativas de reinicialização malsucedidas e progresso perdido por incidente.

O segundo sinal é a adoção em grandes frameworks de treinamento. O NVRx já se conecta a cargas de trabalho baseadas em PyTorch e aparece na stack de software mais ampla da NVIDIA. Mais integrações nativas reduziriam a quantidade de código personalizado de wrapper e launcher que as equipes precisam manter.

A adoção por frameworks fortaleceria o argumento de que a tolerância a falhas do NVRx pode se tornar uma camada padrão de aplicação. Integrações fragmentadas o enfraqueceriam, especialmente se cada framework exigir lógica diferente de timeout, checkpoint e rendezvous.

O terceiro sinal é a evidência de falhas de produção não controladas. A injeção determinística de falhas é necessária para comparação, mas incidentes em campo testam combinações que laboratórios raramente reproduzem. Os operadores devem publicar taxas de recuperação para erros de GPU, travamentos de NCCL, encerramentos por OOM e perda de nós separadamente.

Uma recuperação bem-sucedida deve significar mais do que reiniciar um processo. O job precisa restaurar um estado válido, continuar produzindo atualizações corretas e evitar corrupção silenciosa de checkpoints. A convergência do modelo após recuperações repetidas merece o mesmo escrutínio que a velocidade de recuperação.

Para equipes que consideram agora o treinamento NVRx no Amazon EKS, o primeiro passo prático é um benchmark paralelo controlado. Use o modelo real, o tamanho de checkpoint, o sistema de arquivos e a duração de etapa reais. Compare salvamentos síncronos, salvamentos assíncronos, recuperação pelo launcher e recuperação apenas pelo Kubernetes sob um mesmo cronograma de falhas.

Em seguida, ajuste os intervalos de checkpoint com base nos tempos observados de computação e armazenamento. Se um checkpoint levar quase tanto tempo quanto o intervalo entre salvamentos, a sobreposição assíncrona continuará incompleta. Se a computação fornecer uma janela maior, a eficiência reportada de 99% se torna mais plausível.

As políticas de recuperação devem começar com limites conservadores de tentativas. Registre cada motivo de reinicialização e preserve rastros de diagnóstico. Um job que falha repetidamente no mesmo rank ou checkpoint precisa de escalonamento, e não de um loop infinito de recuperação.

O trabalho da AWS e da NVIDIA torna uma conclusão difícil de ignorar. A confiabilidade do treinamento distribuído não pode continuar sendo apenas uma preocupação de infraestrutura. A aplicação entende o progresso, a validade de checkpoints e o estado do grupo de processos de maneiras que um orquestrador não entende.

O Amazon EKS ainda fornece a base essencial de agendamento e substituição de nós. O NVRx adiciona uma resposta mais rápida dentro desse limite. Juntos, oferecem um caminho crível de ciclos de recuperação de quatro minutos para reinicializações na escala de segundos em várias classes importantes de falhas.

A questão em aberto agora é operacional, e não conceitual. As equipes conseguem reproduzir esses ganhos com seus próprios modelos, sistemas de armazenamento e padrões reais de falha? Esse é o teste que deve orientar a próxima implantação de treinamento NVRx no Amazon EKS.

 
 

Comece grátis

Um assistente de IA local-first com gestão de conhecimento pessoal

Para oferecer uma experiência de IA melhor,

atualmente, o remio é compatível apenas com Windows 10+ (x64) e M-Chip Macs.

Seu parceiro de IA no trabalho
Faça mais com o remio

Planeje. Crie. Entregue.
Tudo em um só lugar.

bottom of page