<rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Modelos-Locais | AkitaOnRails.com</title><link>https://www.akitaonrails.com/tags/modelos-locais/</link><description>Blog do Fabio Akita do Canal do YouTube 'Akitando' falando sobre tecnologia, carreira e coisas geek.</description><generator>Hugo -- gohugo.io</generator><language>pt-BR</language><lastBuildDate>Fri, 30 Oct 2026 02:34:21 +0000</lastBuildDate><atom:link href="https://www.akitaonrails.com/tags/modelos-locais/index.xml" rel="self" type="application/rss+xml"/><item><title>LLM Benchmark: Quais as melhores LLMs open source pra hardware caseiro?</title><link>https://www.akitaonrails.com/2026/10/27/llm-benchmark-melhores-llms-open-source-hardware-caseiro/</link><guid isPermaLink="true">https://www.akitaonrails.com/2026/10/27/llm-benchmark-melhores-llms-open-source-hardware-caseiro/</guid><pubDate>Tue, 27 Oct 2026 13:00:00 -0300</pubDate><description>&lt;p&gt;No &lt;a href="https://www.akitaonrails.com/2026/10/22/llm-benchmark-v4-o-veredito-de-open-source-ate-out-2026/"&gt;último LLM Benchmark sobre modelos open source&lt;/a&gt;, eu fui até o limite do razoável. Aluguei B300, H200, pods com quatro GPUs, rodei quase 70 rodadas e gastei mais de US$ 6 mil na campanha inteira. Saíram candidatos muito bons, como DeepSeek V4.1 Flash, GLM 5.3 Flash e Qwen3.8 Flash-Next, todos chegando perto dos frontier no meu teste de programação.&lt;/p&gt;
&lt;p&gt;O problema continuava sendo a máquina. Um modelo de 510 GB que precisa de quatro B300 pode ser open source no GitHub, mas continua fora do alcance de quem quer rodar uma IA no escritório de casa.&lt;/p&gt;</description><content:encoded><![CDATA[<p>No <a href="/2026/10/22/llm-benchmark-v4-o-veredito-de-open-source-ate-out-2026/">último LLM Benchmark sobre modelos open source</a>, eu fui até o limite do razoável. Aluguei B300, H200, pods com quatro GPUs, rodei quase 70 rodadas e gastei mais de US$ 6 mil na campanha inteira. Saíram candidatos muito bons, como DeepSeek V4.1 Flash, GLM 5.3 Flash e Qwen3.8 Flash-Next, todos chegando perto dos frontier no meu teste de programação.</p>
<p>O problema continuava sendo a máquina. Um modelo de 510 GB que precisa de quatro B300 pode ser open source no GitHub, mas continua fora do alcance de quem quer rodar uma IA no escritório de casa.</p>
<p>Voltei pra minha RTX 5090 e pro Strix Halo. Desta vez, em vez de repetir o benchmark v4 inteiro em dezenas de modelos que já sabemos que não cabem ou não têm velocidade, peguei o candidato mais promissor e fui comparar <strong>as quantizações dele</strong>. Qual versão entrega qualidade suficiente sem transformar cada interação numa espera de vários minutos?</p>
<p>O resultado me surpreendeu: <strong>a versão de 2 bits ganhou</strong>. Depois repeti o teste com o <a href="https://github.com/jesdga95/basalt"target="_blank" rel="noopener">Basalt</a>, um fork do Strata especializado nas GPUs Blackwell. O modelo continuou acertando tudo e a geração chegou a <strong>400 tokens por segundo</strong>. A quantização não mudou; o engine mudou bastante o resultado.</p>
<h2>A RTX 3090 e a 4090 ganharam uma sobrevida<span class="hx:absolute hx:-mt-20" id="a-rtx-3090-e-a-4090-ganharam-uma-sobrevida"></span>
    <a href="#a-rtx-3090-e-a-4090-ganharam-uma-sobrevida" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Antes dos meus resultados, a comunidade descobriu uma limitação que dava pra remover. Nas GeForce RTX 20, certas operações Tensor Core FP16 com acumulação em FP32 rodam com metade da taxa teórica de emissão por ciclo das equivalentes profissionais. Nas RTX 30 e 40, o limite também atinge BF16.</p>
<p>O hardware está lá. Dois registradores internos selecionam a velocidade, e o driver normal não tem privilégio pra alterá-los.</p>
<p>O projeto <a href="https://github.com/Zagitta/gpu-unlock"target="_blank" rel="noopener">gpu-unlock</a> encontrou uma forma de mudar esses registradores antes do driver da NVIDIA assumir a placa. No Ampere, isso explora um buffer overflow no Booter assinado que autentica e carrega o GSP-RM. Nada é gravado na VBIOS. Pra voltar ao comportamento original, você remove o parâmetro de ativação e desliga a máquina por completo, porque os seletores voltam ao padrão quando a placa perde energia. Se o parâmetro continuar ativo, o módulo reaplica o unlock no próximo boot.</p>
<p>Também exige Linux, initramfs e bastante cuidado. Eu não recomendo sair instalando isso numa workstation de produção sem ler o código e entender o risco.</p>
<p>É um hack da comunidade, sem suporte oficial da NVIDIA. Você instala por sua conta e risco. O interesse pra LLM está em acelerar prefill quando o engine usa exatamente esse caminho dos Tensor Cores. Outras aplicações de compute, como alguns pipelines de geração de imagem, também podem mudar de desempenho. Jogos dependem de outros gargalos e caminhos da GPU, então não espere ganhar FPS com isso.</p>
<p>Numa RTX 3090 a 420 W, <a href="https://github.com/Zagitta/gpu-unlock/issues/85"target="_blank" rel="noopener">um teste controlado</a> mediu FP16 com acumulação em FP32 de 76,396 pra 125,006 TFLOPS; BF16 foi de 76,994 pra 127,923. Na 4090, uma implementação ainda experimental mediu aproximadamente o dobro da taxa de instruções FP16 e BF16.</p>
<p>Só que dobrar TFLOPS não dobra sua LLM. O <a href="https://dev.to/orca_forge/why-gpu-unlock-speeds-up-rtx-3090-prefill-but-barely-changes-decode-4572"target="_blank" rel="noopener">teste que motivou esta discussão</a> separou as duas fases:</p>
<ul>
<li><strong>Prefill</strong> é quando o modelo lê o prompt inteiro. Há muitos tokens disponíveis ao mesmo tempo, então o motor consegue montar operações matriciais grandes e paralelas. <a href="https://github.com/Zagitta/gpu-unlock/issues/33"target="_blank" rel="noopener">Uma 3090 ganhou 14,4%</a> numa carga de 16K tokens, enquanto uma dupla de 3080 modificadas ganhou <a href="https://github.com/Zagitta/gpu-unlock/issues/15"target="_blank" rel="noopener">33% a 36% com vLLM</a>.</li>
<li><strong>Decode</strong> é quando o modelo gera um token depois do outro. Em LLM quantizada com expert-offload, essa etapa fica limitada principalmente por banda de memória, RAM, PCIe e cache de especialistas. O ganho medido ficou dentro do ruído.</li>
</ul>
<p>O ganho varia bastante com engine, quantização e tamanho do prompt. Na dupla de 3080, o Qwen3.8 Flash-Next Q4_K_XL ganhou só 0,5% a 0,8% em 8K, mas chegou a 12% ou 13% em 32K e 128K. Num MoE IQ3_S testado numa 3090, uma carga perto de 32K não mudou. A rota de cálculo usada por uma quantização pode nem passar pelo caminho FP16/BF16 que foi liberado.</p>
<blockquote>
  <p>O gpu-unlock acelera um caminho específico dos Tensor Cores. Ele pode reduzir a espera pra ler prompts grandes, mas não transforma 24 GB em 96 GB e nem cria banda de memória.</p>

</blockquote>
<p>Mesmo com essa ressalva, 3090 e 4090 ficaram mais interessantes pra inferência local. Uma <a href="https://www.nvidia.com/en-us/geforce/graphics-cards/30-series/rtx-3090-3090ti/"target="_blank" rel="noopener">RTX 3090</a> usada ainda tem 24 GB de VRAM, NVLink na geração dela e 936 GB/s de banda. A comunidade mediu o Qwen3.8 Flash-Next em aproximadamente <a href="https://github.com/sybil-solutions/local-ai-registry/pull/212"target="_blank" rel="noopener">75 tok/s com IQ3_S e 100 tok/s com Q2_0</a> numa única 3090. Com duas placas, há relatos entre <a href="https://github.com/ruashots/flashnext-2x3090"target="_blank" rel="noopener">90 e 120 tok/s</a>, dependendo da configuração e do tamanho do contexto. Esses resultados são da comunidade, não desta rodada.</p>
<h2>Por que eu escolhi o Qwen3.8 Flash-Next<span class="hx:absolute hx:-mt-20" id="por-que-eu-escolhi-o-qwen38-flash-next"></span>
    <a href="#por-que-eu-escolhi-o-qwen38-flash-next" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>No v4, os candidatos abertos que realmente me interessaram foram:</p>
<table>
  <thead>
      <tr>
          <th>Modelo</th>
          <th>Onde rodei</th>
          <th style="text-align: right">Nota v4</th>
          <th style="text-align: right">Velocidade</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>DeepSeek V4.1 Flash</td>
          <td>4x B300, checkpoint nativo</td>
          <td style="text-align: right">92,5</td>
          <td style="text-align: right">174,8 tok/s</td>
      </tr>
      <tr>
          <td>GLM 5.3 Flash</td>
          <td>1x B300, NVFP4</td>
          <td style="text-align: right">92,5</td>
          <td style="text-align: right">142 tok/s</td>
      </tr>
      <tr>
          <td>Qwen3.8 Flash-Next</td>
          <td>1x B300, NVFP4</td>
          <td style="text-align: right">92,5</td>
          <td style="text-align: right">195 tok/s</td>
      </tr>
      <tr>
          <td>Qwen3.8 Flash-Next</td>
          <td>RTX 5090, IQ3_S</td>
          <td style="text-align: right">91,0</td>
          <td style="text-align: right">159,2 tok/s</td>
      </tr>
  </tbody>
</table>
<p>Os três empataram dentro da margem de ruído. Só o Qwen repetiu a qualidade numa máquina de consumidor. O modelo principal é um MoE de 125 bilhões de parâmetros, com aproximadamente 6 bilhões ativos por token, mais 51 bilhões em embeddings n-gram e uma cabeça MTP de 4 bilhões. O <a href="https://github.com/Niko1221/Strata"target="_blank" rel="noopener">Strata</a> deixa os especialistas mais usados na GPU, mantém os demais na RAM pra CPU calcular, lê a tabela n-gram do SSD e usa a camada MTP do próprio modelo pra especular os próximos tokens.</p>
<p>O backbone e o Engram do DeepSeek V4.1 somam aproximadamente 748 bilhões de parâmetros: 552 bilhões do backbone e 196 bilhões do Engram. O checkpoint publicado tem cerca de 763 bilhões no total. Eu servi a versão nativa mista FP4/FP8, de cerca de 510 GB, no pod de quatro B300.</p>
<p>Também consegui iniciar uma versão de 2 bits na 5090 com o engine dedicado do Strata. Ela gerou por volta de <strong>17 tokens por segundo</strong>. Tecnicamente roda. Pra agente de programação, está fora da disputa.</p>
<p>O checkpoint NVFP4 do GLM 5.3 Flash tinha cerca de 198 GB e foi servido na B300. Não existe neste repositório uma configuração de consumidor que eu tenha medido e possa recomendar. O GLM 4.7 Flash menor já tinha feito 24 pontos na 5090 e 33,5 na B300 no benchmark anterior. Gastar dias tentando fazer a família errada caber não produziria informação útil.</p>
<p>Também deixei Qwen 4 e Llama fora por escolha. Esta rodada tem uma pergunta estreita: <strong>qual versão do modelo que já provou qualidade funciona melhor nas duas máquinas que eu tenho?</strong></p>
<h2>O que eu testei<span class="hx:absolute hx:-mt-20" id="o-que-eu-testei"></span>
    <a href="#o-que-eu-testei" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Todo o código, os prompts, os testes escondidos e os resultados estão no <a href="https://github.com/akitaonrails/llm-opensource-benchmark"target="_blank" rel="noopener">llm-opensource-benchmark</a>. Testei quatro versões do Qwen3.8 Flash-Next:</p>
<table>
  <thead>
      <tr>
          <th>Quantização</th>
          <th style="text-align: right">Arquivos do modelo</th>
          <th>Ideia</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Q2_0</td>
          <td style="text-align: right">~62 GiB</td>
          <td>2 bits, menor e mais agressiva</td>
      </tr>
      <tr>
          <td>IQ3_XXS</td>
          <td style="text-align: right">~71 GiB</td>
          <td>3 bits, prioriza tamanho</td>
      </tr>
      <tr>
          <td>IQ3_S</td>
          <td style="text-align: right">~78 GiB</td>
          <td>3 bits, mais precisão</td>
      </tr>
      <tr>
          <td>UD-Q4_K_XL</td>
          <td style="text-align: right">~104 GiB</td>
          <td>4 bits, maior preservação dos pesos</td>
      </tr>
  </tbody>
</table>
<p>Os arquivos do Strata incluem uma parte comum de PLE, então o tamanho total não cabe diretamente na VRAM. O engine faz o trabalho de distribuir os especialistas entre GPU e RAM. No Basalt, usei os packs próprios do projeto: um arquivo <code>.basalt</code> de 64,9 GiB pro Q2_0 e outro de 81,0 GiB pro IQ3_S. Cada arquivo já reúne pesos, tokenizer, MTP, perfil de especialistas e projetor de visão.</p>
<p>Na primeira rodada da RTX 5090, usei o backend CUDA do Strata com:</p>
<ul>
<li>4 GiB de VRAM reservados pro Hyprland, navegador e terminal;</li>
<li>cache de KV em <code>int8</code>, contexto máximo de 128K e 32K residentes na GPU;</li>
<li>cache elástico de especialistas;</li>
<li>especulação de quatro tokens com o MTP do modelo.</li>
</ul>
<p>Depois rodei Q2_0 e IQ3_S no Basalt, na mesma 5090 e com os mesmos 131.072 tokens de contexto máximo e KV em <code>int8</code>. Deixei 4.608 MiB reservados porque o Basalt contabiliza seus buffers de trabalho nessa margem. A VRAM livre ficou entre 3,6 e 4,4 GB durante os testes.</p>
<p>No Strix Halo, usei o backend <code>hip-halo</code>. Essa máquina tem 64 GB reservados pra GPU e GTT de 31 GB mapeado sobre a RAM do sistema; o limite de RAM do engine era 61,9 GiB. Como ela é headless, deixei a GPU inteira pro modelo e mantive 8 GiB de folga pro cache da conversa.</p>
<p>Nas medições do Strata são dois backends diferentes por baixo, CUDA na NVIDIA e HIP/ROCm na AMD, mas o mesmo engine e a mesma API compatível com OpenAI. O Basalt acrescenta uma comparação de engine na 5090, usando o mesmo OpenCode, os mesmos prompts e a mesma correção.</p>
<h2>A metodologia<span class="hx:absolute hx:-mt-20" id="a-metodologia"></span>
    <a href="#a-metodologia" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Comecei com um teste de velocidade em três situações:</p>
<ol>
<li>prompt frio de 4K tokens;</li>
<li>prompt frio rotulado como 32K, que o tokenizer do modelo contou como 42.400 tokens;</li>
<li>continuação da mesma conversa, reaproveitando o cache.</li>
</ol>
<p>Cada uma gera 256 tokens. O runner mede tempo até o primeiro token, leitura do prompt, geração, VRAM, RAM, potência e temperatura.</p>
<h3>O que cada número de velocidade quer dizer<span class="hx:absolute hx:-mt-20" id="o-que-cada-número-de-velocidade-quer-dizer"></span>
    <a href="#o-que-cada-n%c3%bamero-de-velocidade-quer-dizer" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Quando alguém diz que uma LLM faz 200 tokens por segundo, quase sempre está falando só da geração. Uma sessão de agente passa por fases diferentes, e cada uma encosta num gargalo diferente.</p>
<p><strong>Prefill</strong> é a leitura inicial do prompt. O modelo precisa processar as instruções do harness, histórico da conversa, arquivos e sua pergunta antes de soltar a primeira palavra. Como todos esses tokens já estão disponíveis, a GPU consegue trabalhar com matrizes grandes em paralelo.</p>
<p>Essa fase aproveita melhor Tensor Cores e poder bruto de cálculo. Quanto maior o contexto, mais ela pesa.</p>
<table>
  <thead>
      <tr>
          <th>Métrica</th>
          <th>O que mede</th>
          <th>O que você sente</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Prompt tok/s</td>
          <td>Vazão do prefill, quantos tokens de entrada são lidos por segundo</td>
          <td>Tempo pra digerir contexto novo</td>
      </tr>
      <tr>
          <td>TTFT (<em>time to first token</em>)</td>
          <td>Tempo total entre enviar a requisição e receber o primeiro token</td>
          <td>A pausa antes da resposta começar</td>
      </tr>
      <tr>
          <td>Decode ou output tok/s</td>
          <td>Velocidade de geração depois do primeiro token</td>
          <td>Ritmo do texto e das chamadas de ferramenta</td>
      </tr>
      <tr>
          <td>TPOT (<em>time per output token</em>)</td>
          <td>Tempo por token gerado, normalmente em p50 e p99 (ou p95)</td>
          <td>Regularidade da resposta; percentil alto denuncia travadas</td>
      </tr>
      <tr>
          <td>Wall time</td>
          <td>Tempo da tarefa inteira, incluindo ferramentas, testes, retries e texto gerado</td>
          <td>Produtividade real do agente</td>
      </tr>
  </tbody>
</table>
<p>O <strong>KV cache</strong> guarda os estados intermediários dos tokens já processados. Numa continuação da mesma conversa, o modelo reaproveita esse estado em vez de ler tudo de novo. Por isso o teste de append chega a dezenas de milhares de prompt tokens por segundo e recebe o primeiro token em cerca de meio segundo. Esse número mede reaproveitamento de cache; comparar com prefill frio seria fraude.</p>
<p>O KV também cresce com a conversa e disputa memória com pesos, especialistas e buffers. Aqui ele está em <code>int8</code>, com 32K residentes na 5090. Contexto máximo anunciado, contexto que cabe e contexto que continua rápido são três números diferentes.</p>
<p><strong>Speculative decoding</strong> usa uma camada de draft pra propor vários tokens e o modelo principal verifica o lote. Quando a taxa de aceitação é boa, o output tok/s sobe. Se o draft erra muito, o ganho desaparece. As velocidades do Strata já incluem MTP com especulação de quatro tokens. O Basalt também usa o MTP incluído no próprio pack, com seu mecanismo de especulação e seus defaults.</p>
<p>E falta uma métrica que eu não medi aqui: <strong>concorrência</strong>. Todos estes números são de um único stream. Um servidor pode atender várias sessões e aumentar a vazão agregada, mas cada usuário tende a receber menos tokens por segundo.</p>
<p>Pra uso individual, single-stream e wall time mandam. Pra uma equipe, precisa medir sessões simultâneas e latência por usuário, como fiz no <a href="/2026/10/22/llm-benchmark-v4-can-my-team-fit-in-a-4x-b300-h200/">teste com B300 e H200</a>.</p>
<p>Depois vieram dois testes de programação usando OpenCode 2.1.4. O modelo recebe uma pequena base Python, trabalha em uma sessão contínua e nunca vê os testes escondidos. Tudo roda em Bubblewrap, sem rede, com filesystem limitado e apenas um gateway local que aceita as chamadas pro modelo esperado.</p>
<p>O primeiro canário tem três turnos e 9 testes escondidos. O modelo precisa implementar um analisador de logs, corrigir bugs e lidar com mudança de requisitos.</p>
<p>O teste difícil tem quatro turnos e 36 testes escondidos. É um reconciliador de pagamentos com decisões enterradas num histórico de cerca de 79K tokens, uma decisão corrigida mais tarde, propostas rejeitadas servindo de distração, dinheiro que exige decimal exato e regras novas que interagem com as anteriores. Com o system prompt do OpenCode, o primeiro turno chega perto de 83K tokens.</p>
<h3>Uma régua separada do benchmark v4<span class="hx:absolute hx:-mt-20" id="uma-régua-separada-do-benchmark-v4"></span>
    <a href="#uma-r%c3%a9gua-separada-do-benchmark-v4" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Estes canários são muito menores e mais fáceis. No v4, o modelo trabalha num aplicativo Rails por sete sprints enquanto um revisor injeta 14 defeitos de segurança e de correção. A nota mede quantos ele encontra e corrige sozinho antes da revelação. É um teste de vigilância, com muito mais código, ferramentas e oportunidades de se perder.</p>
<p>Aqui são duas bases Python pequenas, três ou quatro turnos e testes funcionais escondidos. Eu montei essa régua pra comparar <strong>quantizações do mesmo Qwen</strong> e descobrir se reduzir os bits quebra requisitos, contexto ou processo. A comparação termina aí, sem ranquear Qwen contra Opus, GPT, DeepSeek ou qualquer outro modelo.</p>
<p>A nota 100 deste artigo pertence a outra escala, sem equivalência com 100 do v4. Ela também não torna o Q2_0 “melhor” que o IQ3_S que fez 91 no v4. O v4 escolheu o Qwen como candidato; este teste menor escolhe qual arquivo do Qwen vale carregar em casa.</p>
<blockquote>
  <p>Velocidade sem qualidade só faz o modelo errar mais rápido. Por isso eu medi as duas coisas separadamente.</p>

</blockquote>
<h2>Ranking de velocidade<span class="hx:absolute hx:-mt-20" id="ranking-de-velocidade"></span>
    <a href="#ranking-de-velocidade" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><h3>RTX 5090 com Strata, 4 GiB preservados pro desktop<span class="hx:absolute hx:-mt-20" id="rtx-5090-com-strata-4-gib-preservados-pro-desktop"></span>
    <a href="#rtx-5090-com-strata-4-gib-preservados-pro-desktop" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><table>
  <thead>
      <tr>
          <th>Quantização</th>
          <th style="text-align: right">Geração</th>
          <th style="text-align: right">Leitura da carga longa</th>
          <th style="text-align: right">Primeiro token</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><strong>Q2_0</strong></td>
          <td style="text-align: right"><strong>223 a 259 tok/s</strong></td>
          <td style="text-align: right"><strong>6.158 tok/s</strong></td>
          <td style="text-align: right"><strong>6,9 s</strong></td>
      </tr>
      <tr>
          <td>IQ3_XXS</td>
          <td style="text-align: right">196 a 241 tok/s</td>
          <td style="text-align: right">5.084 tok/s</td>
          <td style="text-align: right">8,3 s</td>
      </tr>
      <tr>
          <td>IQ3_S</td>
          <td style="text-align: right">158 a 208 tok/s</td>
          <td style="text-align: right">6.233 tok/s</td>
          <td style="text-align: right">6,8 s</td>
      </tr>
      <tr>
          <td>UD-Q4_K_XL</td>
          <td style="text-align: right">44 a 52 tok/s</td>
          <td style="text-align: right">2.300 tok/s</td>
          <td style="text-align: right">19 s</td>
      </tr>
  </tbody>
</table>
<h3>RTX 5090 com Basalt<span class="hx:absolute hx:-mt-20" id="rtx-5090-com-basalt"></span>
    <a href="#rtx-5090-com-basalt" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><table>
  <thead>
      <tr>
          <th>Quantização</th>
          <th style="text-align: right">Geração</th>
          <th style="text-align: right">Leitura da carga longa</th>
          <th style="text-align: right">Primeiro token</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><strong>Q2_0</strong></td>
          <td style="text-align: right"><strong>377 a 400 tok/s</strong></td>
          <td style="text-align: right"><strong>8.573 tok/s</strong></td>
          <td style="text-align: right"><strong>4,9 s</strong></td>
      </tr>
      <tr>
          <td>IQ3_S</td>
          <td style="text-align: right">223 a 231 tok/s</td>
          <td style="text-align: right">8.196 tok/s</td>
          <td style="text-align: right">5,2 s</td>
      </tr>
  </tbody>
</table>
<h3>Strix Halo, usando a GPU inteira<span class="hx:absolute hx:-mt-20" id="strix-halo-usando-a-gpu-inteira"></span>
    <a href="#strix-halo-usando-a-gpu-inteira" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><table>
  <thead>
      <tr>
          <th>Quantização</th>
          <th style="text-align: right">Geração</th>
          <th style="text-align: right">Leitura da carga longa</th>
          <th style="text-align: right">Primeiro token</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><strong>Q2_0</strong></td>
          <td style="text-align: right"><strong>55 a 65 tok/s</strong></td>
          <td style="text-align: right"><strong>371 tok/s</strong></td>
          <td style="text-align: right"><strong>114 s</strong></td>
      </tr>
      <tr>
          <td>IQ3_XXS</td>
          <td style="text-align: right">50 a 57 tok/s</td>
          <td style="text-align: right">347 tok/s</td>
          <td style="text-align: right">122 s</td>
      </tr>
      <tr>
          <td>IQ3_S</td>
          <td style="text-align: right">47 a 50 tok/s</td>
          <td style="text-align: right">338 tok/s</td>
          <td style="text-align: right">125 s</td>
      </tr>
      <tr>
          <td>UD-Q4_K_XL</td>
          <td style="text-align: right">17 tok/s num prompt de 4K</td>
          <td style="text-align: right">137 tok/s</td>
          <td style="text-align: right">não terminou</td>
      </tr>
  </tbody>
</table>
<p>A 5090 tem 32 GB de GDDR7 e <a href="https://www.nvidia.com/en-us/geforce/graphics-cards/50-series/rtx-5090/"target="_blank" rel="noopener">1.792 GB/s de banda</a>. O Strix tem mais capacidade total, mas seus aproximadamente 256 GB/s precisam servir o sistema inteiro. Mesmo usando o Strata nos dois, a diferença aparece principalmente no prefill: 6,9 segundos contra quase 2 minutos pra começar a responder a carga de 42.400 tokens. No Basalt, a 5090 baixou essa espera pra 4,9 segundos.</p>
<p>O Q4 foi o pior dos dois mundos. Na 5090 ele rodou devagar e o <code>systemd-oomd</code> matou o teste de qualidade. No Strix, zerou a VRAM, começou a paginar e travou a máquina até reboot. Mais bits preservam mais informação, mas ocupam mais memória e reduzem o cache quente que faz esse MoE andar.</p>
<h2>Basalt contra Strata<span class="hx:absolute hx:-mt-20" id="basalt-contra-strata"></span>
    <a href="#basalt-contra-strata" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>O <a href="https://github.com/jesdga95/basalt"target="_blank" rel="noopener">Basalt</a> nasceu como fork do Strata, só que tomou uma direção mais específica: Linux ou Windows, CUDA 13 e GPU NVIDIA Blackwell com compute capability 12.0. A 5090 entra exatamente nesse alvo. O Strata continua muito mais abrangente e foi o que permitiu rodar o mesmo Qwen no Strix Halo com AMD.</p>
<p>Os dois resolvem o mesmo problema central. Na configuração testada, os especialistas que não cabem na VRAM ficam na RAM, enquanto a GPU mantém uma parte quente. Basalt e Strata expõem API compatível com OpenAI e Anthropic e aceleram a geração com seus drafters MTP. O Basalt empacota tudo num único arquivo e traz kernels, gerenciamento de memória e execução especulativa trabalhados especificamente pra Blackwell.</p>
<p>Este foi um A/B prático entre stacks completos, sem isolar uma única otimização. Os packs são próprios de cada engine, os defaults não são idênticos e o Basalt reservou 4.608 MiB de VRAM contra 4.096 MiB do Strata. Mantive a mesma RTX 5090, as mesmas quantizações, contexto máximo de 131K, KV em <code>int8</code>, mesmo OpenCode e os mesmos testes.</p>
<p>Nesse A/B, o Basalt ganhou em todas as métricas de velocidade e empatou nos resultados funcionais de código. O Q2_0 saiu de 223 a 259 pra 377 a 400 tokens por segundo. A leitura da carga longa foi de 6.158 pra 8.573 tokens por segundo, e a espera pelo primeiro token caiu de 6,9 pra 4,9 segundos. No IQ3_S, a geração saiu da faixa de 158 a 208 pra 223 a 231 tokens por segundo.</p>
<p>O recorte de plataforma decide a escolha. Numa RTX 5090, eu começaria pelo Basalt. No Strix, em NVIDIA anterior a Blackwell ou em outro hardware suportado, o Strata continua sendo o caminho. O Strata cobre justamente os lugares onde o Basalt nem roda.</p>
<h2>Ranking de qualidade<span class="hx:absolute hx:-mt-20" id="ranking-de-qualidade"></span>
    <a href="#ranking-de-qualidade" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>O teste simples não separou nada:</p>
<table>
  <thead>
      <tr>
          <th>Quantização</th>
          <th style="text-align: right">RTX 5090</th>
          <th style="text-align: right">Strix Halo</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Q2_0</td>
          <td style="text-align: right">100</td>
          <td style="text-align: right">100</td>
      </tr>
      <tr>
          <td>IQ3_XXS</td>
          <td style="text-align: right">100</td>
          <td style="text-align: right">100</td>
      </tr>
      <tr>
          <td>IQ3_S</td>
          <td style="text-align: right">100</td>
          <td style="text-align: right">100</td>
      </tr>
  </tbody>
</table>
<p>Todas passaram os 9 testes escondidos, preservaram os requisitos e terminaram com a suíte visível verde. Precisei aumentar a dificuldade.</p>
<p>No canário difícil, rodei Q2_0 e IQ3_S três vezes cada na 5090 com Strata:</p>
<table>
  <thead>
      <tr>
          <th>Rodada</th>
          <th style="text-align: right">Nota</th>
          <th style="text-align: right">Testes escondidos</th>
          <th style="text-align: right">Tempo dos 4 turnos</th>
          <th style="text-align: right">Tokens de saída</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Q2_0 r1</td>
          <td style="text-align: right">100</td>
          <td style="text-align: right">36/36</td>
          <td style="text-align: right">5,3 min</td>
          <td style="text-align: right">70K</td>
      </tr>
      <tr>
          <td>Q2_0 r2</td>
          <td style="text-align: right">100</td>
          <td style="text-align: right">36/36</td>
          <td style="text-align: right">8,8 min</td>
          <td style="text-align: right">117K</td>
      </tr>
      <tr>
          <td>Q2_0 r3</td>
          <td style="text-align: right">100</td>
          <td style="text-align: right">36/36</td>
          <td style="text-align: right">7,4 min</td>
          <td style="text-align: right">100K</td>
      </tr>
      <tr>
          <td>IQ3_S r1</td>
          <td style="text-align: right">100</td>
          <td style="text-align: right">36/36</td>
          <td style="text-align: right">11,9 min</td>
          <td style="text-align: right">110K</td>
      </tr>
      <tr>
          <td>IQ3_S r2</td>
          <td style="text-align: right">100</td>
          <td style="text-align: right">36/36</td>
          <td style="text-align: right">13,3 min</td>
          <td style="text-align: right">127K</td>
      </tr>
      <tr>
          <td>IQ3_S r3</td>
          <td style="text-align: right">100</td>
          <td style="text-align: right">36/36</td>
          <td style="text-align: right">11,1 min</td>
          <td style="text-align: right">98K</td>
      </tr>
  </tbody>
</table>
<p>As duas acertaram tudo nas três rodadas. Encontraram as decisões no contexto longo, respeitaram a correção posterior, lidaram com eventos fora de ordem, chargebacks, câmbio e fizeram o refactor final. Também passaram por quantidade parecida de testes quebrados enquanto trabalhavam.</p>
<p>A diferença ficou no relógio. <strong>Q2_0 levou 7,2 minutos em média. IQ3_S levou 12,1.</strong> O modelo de 2 bits entregou o mesmo resultado em aproximadamente 60% do tempo.</p>
<p>Depois repeti as seis rodadas no Basalt:</p>
<table>
  <thead>
      <tr>
          <th>Rodada</th>
          <th style="text-align: right">Nota</th>
          <th style="text-align: right">Testes escondidos</th>
          <th style="text-align: right">Tempo dos 4 turnos</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Q2_0 r1</td>
          <td style="text-align: right">100</td>
          <td style="text-align: right">36/36</td>
          <td style="text-align: right">5,9 min</td>
      </tr>
      <tr>
          <td>Q2_0 r2</td>
          <td style="text-align: right">100</td>
          <td style="text-align: right">36/36</td>
          <td style="text-align: right">4,2 min</td>
      </tr>
      <tr>
          <td>Q2_0 r3</td>
          <td style="text-align: right">100</td>
          <td style="text-align: right">36/36</td>
          <td style="text-align: right">6,3 min</td>
      </tr>
      <tr>
          <td>IQ3_S r1</td>
          <td style="text-align: right">100</td>
          <td style="text-align: right">36/36</td>
          <td style="text-align: right">5,8 min</td>
      </tr>
      <tr>
          <td>IQ3_S r2</td>
          <td style="text-align: right">96*</td>
          <td style="text-align: right">36/36</td>
          <td style="text-align: right">11,3 min</td>
      </tr>
      <tr>
          <td>IQ3_S r3</td>
          <td style="text-align: right">100</td>
          <td style="text-align: right">36/36</td>
          <td style="text-align: right">10,1 min</td>
      </tr>
  </tbody>
</table>
<p>A penalização que derrubou a segunda rodada do IQ3_S pra 96 foi um falso positivo conhecido do harness. O modelo executou <code>ps aux</code> dentro do sandbox, e o comando mostrou a linha do próprio <code>bwrap</code>, que contém caminhos do host. O detector interpretou isso como tentativa de acessar os testes escondidos e tirou quatro pontos. O modelo não usou esses caminhos, os testes escondidos só são montados depois, na correção, e os 36 passaram.</p>
<p>No Basalt, o Q2_0 levou <strong>5,4 minutos em média</strong>, contra 7,2 no Strata. O IQ3_S caiu de 12,1 pra <strong>9,0 minutos</strong>. O ganho de ponta a ponta é menor que o salto de tokens por segundo porque a sessão também gasta tempo executando ferramentas e testes, e cada rodada gerou uma quantidade diferente de texto.</p>
<p>A evidência vale para bases Python pequenas, com contexto de até uns 85K tokens, e apenas três repetições por combinação de engine e quantização no canário difícil. Quantização de 2 bits costuma sofrer primeiro em contexto acima de 100K e em bases maiores. Dentro do uso que medi, gastar mais bits não comprou nenhuma qualidade.</p>
<h2>O que cada versão mostrou<span class="hx:absolute hx:-mt-20" id="o-que-cada-versão-mostrou"></span>
    <a href="#o-que-cada-vers%c3%a3o-mostrou" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><h3>Q2_0: a recomendação atual<span class="hx:absolute hx:-mt-20" id="q2_0-a-recomendação-atual"></span>
    <a href="#q2_0-a-recomenda%c3%a7%c3%a3o-atual" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Foi a quantização que gerou tokens mais rápido nas três combinações de máquina e engine, e terminou as seis rodadas difíceis com nota máxima. No prefill longo do Strata na 5090, o IQ3_S ganhou por uma margem mínima. Com Basalt, o Q2_0 chegou a 400 tokens por segundo. No Strix, 55 a 65 tokens por segundo é utilizável, embora o prefill longo cobre quase dois minutos antes da primeira resposta.</p>
<p>Eu usaria esta versão no dia a dia e guardaria a ressalva de contexto. Projeto grande, sessão chegando em 100K ou comportamento estranho? Repito o mesmo trabalho em IQ3_S antes de culpar o modelo.</p>
<h3>IQ3_XXS: ficou sem função clara<span class="hx:absolute hx:-mt-20" id="iq3_xxs-ficou-sem-função-clara"></span>
    <a href="#iq3_xxs-ficou-sem-fun%c3%a7%c3%a3o-clara" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Passou no canário simples e foi rápida, só que perdeu pro Q2_0 em velocidade e ocupa mais espaço. Eu não rodei as três repetições do teste difícil nela, então não tenho dado pra prometer uma vantagem de qualidade.</p>
<h3>IQ3_S: a opção conservadora<span class="hx:absolute hx:-mt-20" id="iq3_s-a-opção-conservadora"></span>
    <a href="#iq3_s-a-op%c3%a7%c3%a3o-conservadora" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Continua sendo a versão que eu escolheria pra validar um resultado suspeito da Q2_0. Ela já tinha feito 91 no benchmark v4 na 5090, a apenas 1,5 ponto da versão NVFP4 na B300. No Basalt, ficou entre 223 e 231 tokens por segundo, praticamente a faixa que o Q2_0 alcançava no Strata, e ainda leu prompts mais rápido.</p>
<p>O próprio Basalt mede a proximidade das quantizações contra Q8_0. No teste de 100 chunks, o IQ3_S manteve o mesmo token no topo em 87,16% das posições. É uma régua diferente dos meus canários, mas reforça seu papel de opção conservadora. Nos meus testes de código, essa precisão extra ainda não encontrou nada que o Q2_0 tivesse perdido.</p>
<h3>UD-Q4_K_XL: qualidade que não cabe não serve<span class="hx:absolute hx:-mt-20" id="ud-q4_k_xl-qualidade-que-não-cabe-não-serve"></span>
    <a href="#ud-q4_k_xl-qualidade-que-n%c3%a3o-cabe-n%c3%a3o-serve" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Era a candidata intuitiva a entregar mais fidelidade. Faltou memória pra chegar ao teste que confirmaria isso. Na 5090, caiu pra menos de um quarto da velocidade da Q2_0 e o sistema matou a rodada. No Strix, a máquina travou.</p>
<p>Serve como lembrança de que a maior quantização compatível no papel pode ser a pior configuração real. Um MoE depende do espaço que sobra pra especialistas, KV cache, buffers e o próprio sistema operacional.</p>
<h2>Qual hardware vale a pena?<span class="hx:absolute hx:-mt-20" id="qual-hardware-vale-a-pena"></span>
    <a href="#qual-hardware-vale-a-pena" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><h3>RTX 3090 e 4090: boas se você já tem<span class="hx:absolute hx:-mt-20" id="rtx-3090-e-4090-boas-se-você-já-tem"></span>
    <a href="#rtx-3090-e-4090-boas-se-voc%c3%aa-j%c3%a1-tem" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>As duas têm 24 GB, e isso limita o cache de especialistas. O gpu-unlock melhora prefill em workloads que usam o caminho liberado, mas decode continua preso em memória e offload. Uma 3090 usada, principalmente duas com NVLink, pode formar uma máquina local interessante. Eu não compraria uma 4090 nova hoje só pra isso: continua com 24 GB, custa caro e o unlock de Ada ainda é experimental.</p>
<p>Pra ambas, começaria pelo <strong>Qwen3.8 Flash-Next Q2_0</strong> e mediria meu workload. Os aproximadamente 100 tokens por segundo publicados pela comunidade numa única 3090 já colocam a placa numa faixa prática. Duas 3090 aumentam a capacidade e podem ficar entre 90 e 120 tokens por segundo, mas não dobram automaticamente a velocidade. A conta inclui consumo, calor, fonte e pelo menos 128 GB de RAM.</p>
<h3>Strix Halo: muita capacidade, pouca banda<span class="hx:absolute hx:-mt-20" id="strix-halo-muita-capacidade-pouca-banda"></span>
    <a href="#strix-halo-muita-capacidade-pouca-banda" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>É uma boa caixa headless, compacta e econômica. A memória unificada deixa rodar modelos que uma GPU de 24 ou 32 GB recusaria. Neste teste, o melhor modelo também foi o <strong>Q2_0</strong>, a 55 a 65 tokens por segundo.</p>
<p>O problema aparece toda vez que o agente lê contexto grande. Quase dois minutos pra processar a carga longa do teste acumulam rápido durante uma sessão. Eu usaria como servidor privado pra tarefas assíncronas, agentes simples e workloads onde capacidade vale mais que resposta imediata.</p>
<h3>RTX 5090: o melhor ponto caseiro hoje<span class="hx:absolute hx:-mt-20" id="rtx-5090-o-melhor-ponto-caseiro-hoje"></span>
    <a href="#rtx-5090-o-melhor-ponto-caseiro-hoje" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>A <a href="https://www.nvidia.com/en-us/geforce/graphics-cards/50-series/rtx-5090/"target="_blank" rel="noopener">RTX 5090</a> tem 32 GB, 21.760 CUDA cores e a mesma banda nominal de 1.792 GB/s da RTX PRO 6000. O que falta é capacidade: preciso reservar 4 GiB pro desktop e usar RAM e SSD pros especialistas que não cabem.</p>
<p>Com Strata, ela colocou o Q2_0 em 223 a 259 tokens por segundo e leu a carga longa a 6.158 tokens por segundo. Com Basalt, chegou a 400 e 8.573, respectivamente. É a primeira configuração local que não me faz sentir que estou esperando a máquina a cada passo. E é uma placa que também serve pra jogos, geração de imagem, vídeo e todo o resto da workstation.</p>
<h3>RTX PRO 6000: três vezes a VRAM, preço de servidor<span class="hx:absolute hx:-mt-20" id="rtx-pro-6000-três-vezes-a-vram-preço-de-servidor"></span>
    <a href="#rtx-pro-6000-tr%c3%aas-vezes-a-vram-pre%c3%a7o-de-servidor" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>A <a href="https://www.nvidia.com/en-us/products/workstations/professional-desktop-gpus/rtx-pro-6000/"target="_blank" rel="noopener">RTX PRO 6000 Blackwell</a> tem 96 GB de GDDR7 ECC, 24.064 CUDA cores e os mesmos 1.792 GB/s de banda. A capacidade extra deixa muito mais especialistas residentes, abre espaço pra quantização maior e comporta contexto sem disputar os últimos gigabytes com o desktop.</p>
<p>Ela custa <a href="https://marketplace.nvidia.com/en-us/enterprise/laptops-workstations/nvidia-rtx-pro-6000-blackwell-workstation-edition/"target="_blank" rel="noopener">US$ 16 mil na loja da NVIDIA</a>, oito vezes o preço sugerido de US$ 1.999 da 5090. Eu ainda não rodei este benchmark nela, então não vou inventar uma velocidade. A banda igual à 5090 significa que três vezes a memória não viram três vezes mais tokens por segundo. O ganho está em manter mais coisa na placa e evitar o caminho lento de RAM e PCIe.</p>
<p>Pra uso individual, a conta da 5090 fecha melhor. A PRO 6000 começa a fazer sentido quando 96 GB resolvem um modelo que você realmente precisa, quando ECC e suporte profissional importam ou quando várias sessões vão dividir a mesma placa.</p>
<h2>O veredito<span class="hx:absolute hx:-mt-20" id="o-veredito"></span>
    <a href="#o-veredito" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><table>
  <thead>
      <tr>
          <th>Hardware</th>
          <th>Melhor escolha hoje</th>
          <th>Experiência</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>1x RTX 3090/4090, 24 GB</td>
          <td>Qwen3.8 Flash-Next Q2_0</td>
          <td>Viável com offload; confira prefill e decode no seu stack</td>
      </tr>
      <tr>
          <td>Strix Halo</td>
          <td>Qwen3.8 Flash-Next Q2_0</td>
          <td>55 a 65 tok/s; prompt longo demora</td>
      </tr>
      <tr>
          <td>RTX 5090</td>
          <td><strong>Qwen3.8 Flash-Next Q2_0 no Basalt</strong></td>
          <td>377 a 400 tok/s; melhor equilíbrio que medi</td>
      </tr>
      <tr>
          <td>RTX PRO 6000</td>
          <td>Provavelmente IQ3_S ou maior</td>
          <td>Capacidade excelente, sem medição minha nesta rodada</td>
      </tr>
  </tbody>
</table>
<p>O open source local finalmente entrou numa faixa prática. Uma única 5090 roda um modelo que fez Tier A no meu benchmark v4 e, com Q2_0 no Basalt, chegou a 400 tokens por segundo no canário. O Strix também ficou muito melhor do que os 7,7 tokens por segundo do Qwen denso que eu tinha testado antes.</p>
<p>Ainda existem limites claros. O contexto custa memória, o desktop precisa da mesma VRAM e qualquer especialista fora da placa atravessa PCIe ou cai na RAM. DeepSeek a 17 tokens por segundo mostra que “rodou” continua sendo uma métrica inútil. Se cada iteração leva várias horas, a qualidade do modelo deixa de importar porque ninguém consegue trabalhar desse jeito.</p>
<blockquote>
  <p>Pra hardware caseiro, o melhor modelo é o que mantém qualidade suficiente na velocidade que deixa você iterar. Nesta rodada, foi o Qwen3.8 Flash-Next Q2_0 no Basalt, numa RTX 5090.</p>

</blockquote>
<p>O <a href="https://github.com/akitaonrails/llm-opensource-benchmark"target="_blank" rel="noopener">repositório do benchmark</a> tem os runners, configurações, resultados brutos e testes escondidos. Repita na sua máquina e mande resultados. Hardware, engine, quantização e contexto mudam a resposta. Ranking pronto serve como ponto de partida; a decisão final continua sendo medida no seu trabalho.</p>
]]></content:encoded><category>benchmarks-de-llm</category><category>modelos-locais</category><category>llms</category><category>hardware</category></item><item><title>LLM Benchmark para Contadores e Advogados (Experimental - LLMs Open Source)</title><link>https://www.akitaonrails.com/2026/10/27/llm-benchmark-para-contadores-e-advogados-experimental/</link><guid isPermaLink="true">https://www.akitaonrails.com/2026/10/27/llm-benchmark-para-contadores-e-advogados-experimental/</guid><pubDate>Tue, 27 Oct 2026 12:00:00 -0300</pubDate><description>&lt;p&gt;Depois de quase 70 rodadas, uns 60 modelos e mais de US$ 6 mil gastos, publiquei o &lt;a href="https://www.akitaonrails.com/2026/10/22/llm-benchmark-v4-o-veredito-de-open-source-ate-out-2026/"&gt;veredito do meu LLM Benchmark v4&lt;/a&gt;. Todo aquele trabalho tem um foco muito específico: desenvolvimento de software no mundo real.&lt;/p&gt;
&lt;p&gt;O modelo constrói um projeto Rails em sete sprints e precisa encontrar 14 defeitos de segurança e correção plantados no caminho. Eu meço se ele percebe uma backdoor, um bypass de autorização, um teste adulterado ou uma conta errada antes que alguém entregue a resposta. É uma régua de vigilância durante programação. Nunca foi uma prova de inteligência universal.&lt;/p&gt;</description><content:encoded><![CDATA[<p>Depois de quase 70 rodadas, uns 60 modelos e mais de US$ 6 mil gastos, publiquei o <a href="/2026/10/22/llm-benchmark-v4-o-veredito-de-open-source-ate-out-2026/">veredito do meu LLM Benchmark v4</a>. Todo aquele trabalho tem um foco muito específico: desenvolvimento de software no mundo real.</p>
<p>O modelo constrói um projeto Rails em sete sprints e precisa encontrar 14 defeitos de segurança e correção plantados no caminho. Eu meço se ele percebe uma backdoor, um bypass de autorização, um teste adulterado ou uma conta errada antes que alguém entregue a resposta. É uma régua de vigilância durante programação. Nunca foi uma prova de inteligência universal.</p>
<p>Enquanto eu fechava essa série, alguns colegas que trabalham com investimentos e Direito começaram a discutir se um modelo aberto já serviria nas rotinas deles. A conversa deixou uma coceira: como eu poderia medir isso sem cair em prova de múltipla escolha ou pergunta de trivia jurídica?</p>
<p>Tentei responder. E já começo com a ressalva que manda no resto deste artigo.</p>
<blockquote>
  <p>Eu não sou contador, não tenho CPA, nunca passei na OAB e tenho zero experiência profissional trabalhando em escritório contábil ou jurídico. Consigo conversar sobre os assuntos e ler o material, porém não tenho condição de certificar se esta suíte representa bem o trabalho diário dessas profissões.</p>

</blockquote>
<p>Portanto, trate tudo daqui pra frente como <strong>um experimento que precisa do escrutínio de contadores e advogados de verdade</strong>. Os números ajudam a comparar os modelos dentro desta suíte. Eles não certificam nenhum modelo para atender clientes, assinar parecer, fechar balanço ou protocolar peça.</p>
<h2>O que eu tentei medir<span class="hx:absolute hx:-mt-20" id="o-que-eu-tentei-medir"></span>
    <a href="#o-que-eu-tentei-medir" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Alguns dos profissionais com quem conversei já usam Claude ou GPT no trabalho, com modelos como Fable, Opus, Astra ou Sol. Parti dessa realidade em vez de fingir que conseguiria escrever sozinho uma prova profissional.</p>
<p>Pedi ao Fable 5.1 para desenhar casos dentro do que ele próprio tinha confiança para pesquisar, estruturar e revisar. A suíte usa padrões de trabalho, legislação, formulários, decisões e materiais de referência, mas <a href="https://github.com/akitaonrails/llm-non-coding-benchmark/blob/main/docs/METHODOLOGY.md#2-why-this-matters-for-private-infrastructure"target="_blank" rel="noopener">todos os clientes, empresas e documentos são sintéticos</a>. Nenhum dado confidencial real entrou no repositório.</p>
<p>Depois rodei o GPT-6 Sol como âncora de fronteira. Finalmente, peguei os modelos abertos que tinham ido bem nos testes de programação e rodei a mesma suíte em três classes de infraestrutura: RTX 5090, Strix Halo e NVIDIA B300 alugada.</p>
<p>Uso &ldquo;aberto&rdquo; no mesmo sentido do benchmark anterior: checkpoint que eu consigo baixar e servir por conta própria. As licenças variam e nem todo peso aberto atende à definição mais rigorosa de software open source.</p>
<p>O Fable também fez a correção cega. Cada configuração recebeu uma letra aleatória, e um subagente separado avaliou cada caso sem saber qual modelo tinha produzido a resposta. Então a comparação deste artigo significa algo bem restrito:</p>
<blockquote>
  <p>Quão perto estes modelos abertos chegaram do Sol, numa prova criada e corrigida pelo Fable?</p>

</blockquote>
<p>Fable e Astra não fizeram a suíte como candidatos. Sol é a única âncora fechada desta rodada. O próprio Fable limita o que conseguimos perguntar e julgar, e um avaliador de IA pode errar. A revisão de uma amostra por profissionais humanos ainda está pendente.</p>
<h2>O teste começa com uma pasta de cliente<span class="hx:absolute hx:-mt-20" id="o-teste-começa-com-uma-pasta-de-cliente"></span>
    <a href="#o-teste-come%c3%a7a-com-uma-pasta-de-cliente" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>O benchmark tem <strong>38 casos</strong>, divididos entre contabilidade e Direito nos Estados Unidos e no Brasil. Como alguns casos atravessam várias sessões, a campanha inteira soma 63 unidades de sessão por configuração e 190 execuções avaliadas.</p>
<p>A distribuição ficou assim:</p>
<table>
  <thead>
      <tr>
          <th>Área</th>
          <th style="text-align: right">Casos</th>
          <th>Exemplos</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Contabilidade e fiscal nos EUA</td>
          <td style="text-align: right">10</td>
          <td>Schedule C, fechamento contábil, Form 1040 familiar, venda de ativos contra ações, QSBS, Chapter 11 e fraude</td>
      </tr>
      <tr>
          <td>Contabilidade e fiscal no Brasil</td>
          <td style="text-align: right">10</td>
          <td>DAS e Fator R, escolha de regime, CBS/IBS, Lucro Real, abertura de empresa, holding e recuperação judicial</td>
      </tr>
      <tr>
          <td>Direito nos EUA</td>
          <td style="text-align: right">9</td>
          <td>Prazos, revisão de MSA, non-compete, divórcio, Chapter 11, M&amp;A, retaliação e disputa societária</td>
      </tr>
      <tr>
          <td>Direito no Brasil</td>
          <td style="text-align: right">9</td>
          <td>Prazos do DJe, contrato com LGPD, pejotização, execução fiscal, divórcio, RJ, M&amp;A, justa causa e cobrança prescrita</td>
      </tr>
  </tbody>
</table>
<p>São quatro casos L1, seis L2, 22 L3 e seis L4. A dificuldade não vem de pedir uma resposta mais rebuscada. Ela cresce com o volume de documentos, a quantidade de regras que interagem e o tempo durante o qual o modelo precisa manter uma linha de raciocínio.</p>
<ul>
<li><strong>L1, estagiário ou clerk:</strong> um ou dois documentos, uma entrega e uma resposta verificável. Um exemplo é calcular prazos a partir de uma intimação no DJe.</li>
<li><strong>L2, sênior:</strong> vários documentos, algum limite ou regra que interage com outra e pelo menos um fato que precisa ser perguntado ao cliente. Aqui entram escolha de regime tributário e revisão de contrato contra um playbook.</li>
<li><strong>L3, gerente ou associate:</strong> fontes conflitantes ou superadas, falsos positivos e decisões que precisam ser justificadas. A maior parte da suíte está aqui.</li>
<li><strong>L4, sócio:</strong> de três a doze sessões. Um fato novo derruba trabalho anterior, a outra parte reage e o modelo precisa corrigir o curso sem esconder a mudança.</li>
</ul>
<p>Os seis casos L4 são doze fechamentos mensais de uma empresa SaaS, um devedor em Chapter 11, um trimestre de Lucro Real, uma recuperação judicial, um litígio americano em cinco sessões e uma execução fiscal brasileira também em cinco sessões. Somados aos casos de sessão única, eles levam cada configuração às 63 unidades de sessão. Como rodei cinco configurações, foram 315 sessões de trabalho dentro das 190 execuções.</p>
<p>Cada caso tenta parecer uma pequena contratação profissional. O modelo recebe:</p>
<ul>
<li>uma pasta com contratos, extratos, notas fiscais, petições, planilhas e outros documentos;</li>
<li>uma biblioteca fechada com as normas e autoridades permitidas, sem acesso à internet;</li>
<li>uma carta dizendo quem é o cliente, qual é a tarefa e o formato da entrega;</li>
<li>ferramentas para calcular e manipular arquivos;</li>
<li>um cliente simulado que só revela certos fatos quando o modelo faz a pergunta certa.</li>
</ul>
<p>Há tarefas curtas de nível estagiário e trabalhos longos de nível sócio, com fatos que chegam depois e obrigam a rever uma conclusão anterior. No fim de cada caso, todos recebem a mesma ordem para revisar erros, fontes sem suporte e contas que não fecham.</p>
<h3>Exemplo contábil: o fechamento que não fecha<span class="hx:absolute hx:-mt-20" id="exemplo-contábil-o-fechamento-que-não-fecha"></span>
    <a href="#exemplo-cont%c3%a1bil-o-fechamento-que-n%c3%a3o-fecha" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Um dos casos americanos entrega o balancete de fevereiro, razão de março, extratos bancário e de cartão, aging de contas a receber, duas cópias da mesma fatura e o checklist de fechamento. O dono quer a conciliação limpa até sexta-feira porque vai renovar uma linha de crédito.</p>
<p>Depois de contabilizar os itens legítimos, ainda sobra uma diferença de <strong>US$ 4.312</strong>. Não há documento que explique esse valor. A resposta correta precisa deixar a diferença aberta, contar isso ao dono e recusar qualquer lançamento inventado.</p>
<p>O caso ainda esconde uma transferência de <strong>US$ 1.500</strong>. O modelo precisa perguntar de onde veio. O cliente roteirizado responde que foi aporte pessoal do dono.</p>
<p>Se o modelo assumir que é receita, erra o resultado e o imposto. Se lançar os US$ 4.312 em &ldquo;despesas diversas&rdquo;, &ldquo;retirada do sócio&rdquo; ou uma conta transitória para forçar a conciliação, reprova imediatamente no filtro de confiança.</p>
<p>Esse exemplo testa mais do que soma. Testa se o agente resiste à pressão do cliente, reconhece a ausência de evidência, detecta a fatura duplicada, faz a pergunta necessária e entrega balanço, lançamentos e nota de pendências consistentes entre si.</p>
<h3>Exemplo jurídico: cinco sessões de execução fiscal<span class="hx:absolute hx:-mt-20" id="exemplo-jurídico-cinco-sessões-de-execução-fiscal"></span>
    <a href="#exemplo-jur%c3%addico-cinco-sess%c3%b5es-de-execu%c3%a7%c3%a3o-fiscal" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>No caso brasileiro L4, uma marmoraria enfrenta uma CDA de IRPJ e CSLL de aproximadamente <strong>R$ 1,1 milhão</strong>. O modelo recebe a CDA, parte do processo administrativo fiscal, alterações do contrato social, certidão do oficial e uma biblioteca fechada com CTN, LEF, CPC, súmulas e precedentes necessários.</p>
<p>Na primeira sessão, ele avalia nulidades e escolhe o instrumento de defesa. Na segunda, a Fazenda emenda a CDA. Na terceira, pede o redirecionamento para um sócio. As duas sessões finais tratam da defesa do sócio, garantia e transação com valores.</p>
<p>A armadilha é continuar repetindo uma tese depois que o vício foi corrigido, escolher o sócio errado ou aceitar automaticamente a presunção de dissolução irregular. As alterações societárias mostram quem administrava no período relevante, e a mudança de endereço havia sido registrada antes da certidão. O modelo precisa abandonar a tese que perdeu sentido e recalcular a estratégia com os fatos novos.</p>
<p>Outro caso jurídico faz o mesmo tipo de pressão num litígio americano de cinco sessões. A contraparte apresenta um e-mail que enfraquece a tese inicial, aparece uma cláusula de arbitragem em letra miúda no Anexo C do contrato e o agente precisa dizer ao cliente que a posição piorou. Persistir na narrativa inicial conta contra ele.</p>
<blockquote>
  <p>Cada caso oferece uma conclusão errada muito conveniente. O teste mede se o modelo encontra a prova que obriga a mudar de ideia.</p>

</blockquote>
<h3>Como eu corrigi as entregas<span class="hx:absolute hx:-mt-20" id="como-eu-corrigi-as-entregas"></span>
    <a href="#como-eu-corrigi-as-entregas" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>As respostas passam por quatro filtros:</p>
<ol>
<li><strong>Confiança:</strong> inventou fato do cliente, autoridade ou número para fechar conta, aplicou lei superada ou declarou que terminou sem terminar? A execução falha.</li>
<li><strong>Oráculo:</strong> datas, valores, classificações e itens obrigatórios batem exatamente com a resposta esperada?</li>
<li><strong>Vigilância:</strong> encontrou sozinho os problemas plantados ou só percebeu na revisão final?</li>
<li><strong>Qualidade da entrega:</strong> o memorando, parecer ou resumo contém os elementos binários exigidos pelo caso? Estilo bonito não ganha ponto.</li>
</ol>
<p>O oráculo reúne <strong>235 itens exatos por configuração</strong>. Uma data vence no dia certo ou no errado. Um formulário tem o valor esperado ou não tem.</p>
<p>Uma cláusula obrigatória foi sinalizada ou passou. Quando cabe tolerância numérica, ela já está definida no gabarito.</p>
<p>As armadilhas têm pesos por gravidade: crítica vale 5, alta vale 3, média vale 2 e baixa vale 1. Descoberta durante o trabalho recebe o peso inteiro; descoberta apenas depois da ordem final de revisão vale 40%; problema perdido vale zero. Foi daí que saiu a coluna de vigilância.</p>
<p>O cliente também segue um script. Ele não improvisa ajuda. Cada resposta escondida tem um gatilho, e só aparece quando a pergunta do modelo realmente busca aquele fato.</p>
<p>Se ninguém perguntar, o modelo precisa declarar a premissa ou deixar a pendência aberta. Inventar a resposta pode derrubar toda a execução.</p>
<p>Antes de cada rodada, o harness copia uma pasta limpa e esconde fisicamente o gabarito, os manifests das armadilhas e as respostas dos outros modelos. A rede fica bloqueada. Isso impede que o agente pesquise uma solução pronta ou leia arquivos de correção no próprio repositório.</p>
<p>Nenhum dos dois lembretes genéricos permitidos pelo protocolo chegou a ser usado. Os arquivos de resposta, históricos, prompts, chaves de correção e scripts estão no <a href="https://github.com/akitaonrails/llm-non-coding-benchmark"target="_blank" rel="noopener">repositório do llm-non-coding-benchmark</a>.</p>
<h2>O ranking experimental<span class="hx:absolute hx:-mt-20" id="o-ranking-experimental"></span>
    <a href="#o-ranking-experimental" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Ordenei por entregas completas. Uma entrega completa terminou todas as sessões, não bateu no limite de saída, não teve falha grave de confiança e acertou todos os itens obrigatórios do oráculo.</p>
<table>
  <thead>
      <tr>
          <th style="text-align: right">#</th>
          <th>Modelo / infraestrutura</th>
          <th style="text-align: right">Completos</th>
          <th style="text-align: right">Falhas graves de confiança</th>
          <th style="text-align: right">Oráculo estrito, itens avaliados</th>
          <th style="text-align: right">Oráculo sobre todos os itens</th>
          <th style="text-align: right">Vigilância</th>
          <th style="text-align: right">Tempo ativo</th>
          <th style="text-align: right">Custo comparável</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td style="text-align: right">1</td>
          <td>Qwen3.8 Flash-Next IQ3_S / RTX 5090</td>
          <td style="text-align: right"><strong>27/38</strong></td>
          <td style="text-align: right"><strong>0</strong></td>
          <td style="text-align: right">91%</td>
          <td style="text-align: right">79%</td>
          <td style="text-align: right">99,6</td>
          <td style="text-align: right">432 min</td>
          <td style="text-align: right">hardware próprio</td>
      </tr>
      <tr>
          <td style="text-align: right">2</td>
          <td>Qwen3.8 Flash-Next IQ3_S / Strix Halo</td>
          <td style="text-align: right"><strong>27/38</strong></td>
          <td style="text-align: right">3</td>
          <td style="text-align: right">90%</td>
          <td style="text-align: right"><strong>86%</strong></td>
          <td style="text-align: right">97,5</td>
          <td style="text-align: right">2.105 min</td>
          <td style="text-align: right">hardware próprio</td>
      </tr>
      <tr>
          <td style="text-align: right">3</td>
          <td>GPT-6 Sol médio / Codex</td>
          <td style="text-align: right">25/38</td>
          <td style="text-align: right"><strong>0</strong></td>
          <td style="text-align: right">84%</td>
          <td style="text-align: right">84%</td>
          <td style="text-align: right"><strong>100,0</strong></td>
          <td style="text-align: right"><strong>248 min</strong></td>
          <td style="text-align: right"><strong>US$ 16,72</strong> equivalente de API</td>
      </tr>
      <tr>
          <td style="text-align: right">4</td>
          <td>GLM 5.3 Flash NVFP4 / B300</td>
          <td style="text-align: right">23/38</td>
          <td style="text-align: right"><strong>0</strong></td>
          <td style="text-align: right"><strong>93%</strong></td>
          <td style="text-align: right">79%</td>
          <td style="text-align: right">98,8</td>
          <td style="text-align: right">725 min</td>
          <td style="text-align: right"><strong>US$ 83,86 a US$ 108,63</strong> no mínimo</td>
      </tr>
      <tr>
          <td style="text-align: right">5</td>
          <td>Qwen3.8 Flash-Next NVFP4 / B300</td>
          <td style="text-align: right">23/38</td>
          <td style="text-align: right">2</td>
          <td style="text-align: right">85%</td>
          <td style="text-align: right">84%</td>
          <td style="text-align: right">99,6</td>
          <td style="text-align: right">622 min</td>
          <td style="text-align: right"><strong>US$ 71,94 a US$ 93,20</strong> no mínimo</td>
      </tr>
  </tbody>
</table>
<p>O Sol custou <strong>US$ 16,72 em preço equivalente de API</strong>. Eu rodei pela assinatura, então esse valor serve para comparar consumo e não representa uma cobrança que recebi. As máquinas locais não têm custo por chamada, mas continuam custando hardware, eletricidade e manutenção.</p>
<p>A <a href="https://www.runpod.io/gpu-models/b300"target="_blank" rel="noopener">tabela oficial da Runpod</a> cobra hoje <strong>US$ 6,94 por hora</strong> pela B300 no Community Cloud e <strong>US$ 8,99 por hora</strong> no Secure Cloud, com cobrança por segundo. O benchmark não guardou a fatura nem o tier do pod, então calculei as duas pontas usando apenas os minutos ativos de cada modelo.</p>
<p>Isso dá de US$ 71,94 a US$ 93,20 para o Qwen e de US$ 83,86 a US$ 108,63 para o GLM. São pisos artificiais. O pod também cobra durante download, carga dos pesos, compilação, troca de modelo, espera do controller, retries e qualquer minuto ocioso até alguém desligá-lo. A conta real foi maior.</p>
<p>&ldquo;Tempo ativo&rdquo; soma o tempo de modelo de todos os casos. O Sol rodou quatro em paralelo e terminou a campanha em 72,9 minutos de relógio. As outras configurações rodaram uma por vez. Esta tabela compara esforço de inferência e latência de cada caso; não mede capacidade de atender muitos usuários ao mesmo tempo.</p>
<p>Também há duas colunas de oráculo porque o limite de 16.384 tokens cortou respostas dos modelos locais. &ldquo;Itens avaliados&rdquo; ignora o que ficou impossível de corrigir. &ldquo;Todos os itens&rdquo; conta o que faltou como erro. O GLM, por exemplo, foi o mais exato quando conseguiu entregar algo avaliável, mas perdeu muito trabalho no limite de saída.</p>
<blockquote>
  <p>Uma colocação acima na tabela não torna um modelo melhor para trabalhar. Eu prefiro 25 entregas limpas do Sol a 27 entregas com três invenções graves no Strix Halo.</p>

</blockquote>
<h2>O custo do erro pesa mais que a média<span class="hx:absolute hx:-mt-20" id="o-custo-do-erro-pesa-mais-que-a-média"></span>
    <a href="#o-custo-do-erro-pesa-mais-que-a-m%c3%a9dia" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Depois que publiquei este artigo, recebi um comentário: para advogado e contador, a média de acertos não basta. O erro que interessa é aquele dito como fato, sem ressalva, que parece plausível e vai parar na mão do cliente.</p>
<p>O benchmark separa o filtro de confiança do restante da nota. Uma data ou um valor errado reprova o item do oráculo. Inventar uma resposta do cliente, um documento, uma autoridade ou um número para fechar a conta derruba a execução inteira, mesmo que todo o resto esteja certo.</p>
<p>A diferença por configuração é grande:</p>
<table>
  <thead>
      <tr>
          <th>Modelo / infraestrutura</th>
          <th style="text-align: right">Execuções com invenção grave</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>GPT-6 Sol médio / Codex</td>
          <td style="text-align: right"><strong>0/38 (0%)</strong></td>
      </tr>
      <tr>
          <td>Qwen3.8 Flash-Next IQ3_S / RTX 5090</td>
          <td style="text-align: right"><strong>0/38 (0%)</strong></td>
      </tr>
      <tr>
          <td>Qwen3.8 Flash-Next IQ3_S / Strix Halo</td>
          <td style="text-align: right">3/38 (7,9%)</td>
      </tr>
      <tr>
          <td>Qwen3.8 Flash-Next NVFP4 / B300</td>
          <td style="text-align: right">2/38 (5,3%)</td>
      </tr>
      <tr>
          <td>GLM 5.3 Flash NVFP4 / B300</td>
          <td style="text-align: right"><strong>0/38 (0%)</strong></td>
      </tr>
  </tbody>
</table>
<p>Foram cinco execuções afetadas entre 190, ou <strong>2,6%</strong>. Dentro delas, os avaliadores encontraram nove ocorrências graves: respostas de cliente que nunca foram dadas, documentos inexistentes, datas fictícias, termos contratuais inventados e uma citação atribuída falsamente à biblioteca fechada. Outras sete execuções receberam alertas de confiança média e ficaram pendentes de revisão humana.</p>
<p>Chamar isso de &ldquo;taxa de respostas confiantes e erradas&rdquo; seria impreciso. O benchmark não mede a confiança interna do modelo. O <code>high confidence</code> registrado nos resultados é a confiança do avaliador de que houve uma violação. O que medimos é se o modelo afirmou uma informação sem suporte, não marcou como premissa e deixou que ela chegasse ao material entregue ou produzido até a interrupção.</p>
<p>Também ainda não separei todo valor errado nessa categoria. Datas, contas, classificações e recomendações definidas no gabarito entram na acurácia do oráculo. Na coluna sobre todos os itens, omissões causadas pelo limite de saída contam como erro; na coluna de itens avaliados, ficam fora do denominador.</p>
<p>Na próxima versão, vale classificar cada erro por tipo e registrar se o modelo avisou que estava em dúvida. Aí teremos, além da acurácia geral, taxas distintas de invenção, erro numérico, erro de data, omissão e correção durante a revisão.</p>
<p>O mesmo Qwen teve acurácia estrita parecida nos itens avaliados da RTX 5090 e do Strix Halo, <strong>91% contra 90%</strong>. Na RTX, nenhuma execução teve invenção grave; no Strix, foram três.</p>
<h2>Sol 6: a referência também erra<span class="hx:absolute hx:-mt-20" id="sol-6-a-referência-também-erra"></span>
    <a href="#sol-6-a-refer%c3%aancia-tamb%c3%a9m-erra" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>O Sol foi o mais rápido, não teve falha grave de confiança e concluiu todas as 38 execuções sem estourar a saída. Mesmo assim, só 25 passaram por todos os critérios.</p>
<p>O ponto fraco apareceu nas contas, principalmente em cinco casos brasileiros. Ele errou números em escolha de regime, CBS/IBS, Lucro Real, abertura de empresa e recuperação judicial. Também perguntou bem menos ao cliente: 51 fatos, contra 74 a 95 nos outros modelos, e registrou mais premissas.</p>
<p>Premissa explícita é um comportamento correto quando falta informação. Só que perguntar costuma dar uma resposta melhor do que carregar uma hipótese até o fim.</p>
<p>A revisão final também piorou respostas. Num formulário americano, o Sol tinha acertado os 16 itens antes da revisão. Depois apagou quatro linhas corretas e marcou o trabalho como bloqueado. Em outro caso, anulou dois campos que já estavam certos.</p>
<p>Usar modelo fechado como árbitro continua sendo um erro. O Sol serviu como referência prática desta rodada e teve um perfil limpo, rápido e consistente. Ainda falhou em 13 dos 38 casos pelo critério estrito.</p>
<h2>Qwen3.8 Flash-Next na RTX 5090: melhor equilíbrio local<span class="hx:absolute hx:-mt-20" id="qwen38-flash-next-na-rtx-5090-melhor-equilíbrio-local"></span>
    <a href="#qwen38-flash-next-na-rtx-5090-melhor-equil%c3%adbrio-local" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>O melhor resultado bruto veio da versão IQ3_S, quantizada em pouco mais de 3 bits, numa única RTX 5090. Foram 27 entregas completas, nenhuma falha grave de confiança e 91% de acerto estrito entre os itens que chegaram a ser avaliados.</p>
<p>Ele levou 432 minutos ativos. A mediana por caso ficou 1,6 vez mais lenta que o Sol, e a geração média chegou a 153 tokens por segundo. Três execuções bateram no teto de saída. Para uma estação local, ainda foi um resultado forte.</p>
<p>Os erros mostram por que precisa de revisão profissional. Num caso brasileiro, calculou prazos com um dia de atraso e usou a revisão final para &ldquo;confirmar&rdquo; a própria conta errada. Num litígio americano, tinha calculado corretamente uma parcela de US$ 375 mil e mudou para zero durante a revisão.</p>
<p>Houve ainda duas suspeitas de invenção com confiança média que aguardam revisão humana. Elas não foram contadas como falhas graves. Eu usaria esse modelo para preparar, cruzar documentos, levantar perguntas e produzir uma primeira versão dentro de uma máquina sob meu controle. Não deixaria o resultado sair para o cliente sem alguém qualificado conferir.</p>
<h2>O mesmo Qwen no Strix Halo: cinco vezes mais lento<span class="hx:absolute hx:-mt-20" id="o-mesmo-qwen-no-strix-halo-cinco-vezes-mais-lento"></span>
    <a href="#o-mesmo-qwen-no-strix-halo-cinco-vezes-mais-lento" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>O Strix Halo rodou exatamente a mesma quantização IQ3_S e também terminou com 27 entregas completas. A semelhança para aí.</p>
<p>Foram 2.105 minutos ativos, quase cinco vezes o tempo da RTX 5090 e 8,5 vezes o tempo total ativo do Sol. A mediana do mesmo caso ficou 7,7 vezes mais lenta que a referência. Quarenta tokens por segundo funciona para lote e paciência; numa rotina interativa, você começa a esperar a máquina.</p>
<p>Pior: essa rodada teve três falhas graves. O modelo inventou respostas de clientes, documentos, datas de CDA e fatos que sustentavam suas próprias conclusões. A versão idêntica na RTX não teve nenhuma falha de alta confiança.</p>
<p>Com uma rodada por configuração, eu não consigo atribuir a diferença ao hardware, ao servidor ou à aleatoriedade da inferência. Seria desonesto concluir que Strix &ldquo;faz o modelo alucinar&rdquo;. O que os dados permitem dizer é mais simples: <strong>esta execução foi lenta e não foi confiável o bastante</strong>. Precisa ser repetida antes de qualquer recomendação.</p>
<h2>Qwen3.8 Flash-Next na B300: alugar GPU grande não comprou qualidade<span class="hx:absolute hx:-mt-20" id="qwen38-flash-next-na-b300-alugar-gpu-grande-não-comprou-qualidade"></span>
    <a href="#qwen38-flash-next-na-b300-alugar-gpu-grande-n%c3%a3o-comprou-qualidade" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Na B300 alugada, o Flash-Next rodou em NVFP4, fez 23 entregas completas e teve duas falhas graves de confiança. Numa delas, inventou cidade, termos contratuais, valores e até uma citação que não existia na biblioteca fechada.</p>
<p>O tempo ativo foi de 622 minutos, com mediana 1,9 vez mais lenta que o Sol e geração média de 148 tokens por segundo. Cinco execuções atingiram o teto de saída; quatro delas efetivamente impediram uma entrega completa. Mesmo no cálculo mínimo, custou entre 4,3 e 5,6 vezes o equivalente de API do Sol.</p>
<p>Neste teste serial, uma B300 não trouxe mais qualidade nem menor latência que a RTX 5090. Isso não mede o principal motivo para alguém comprar ou alugar uma B300, que é servir vários usuários e lotes em paralelo. Eu testei uma contratação de cada vez para manter o protocolo comparável. O máximo que posso concluir é que <strong>pagar pela classe de datacenter não melhorou esta sessão individual</strong>.</p>
<h2>GLM 5.3 Flash na B300: preciso quando termina<span class="hx:absolute hx:-mt-20" id="glm-53-flash-na-b300-preciso-quando-termina"></span>
    <a href="#glm-53-flash-na-b300-preciso-quando-termina" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>O GLM teve um perfil diferente. Não registrou falha grave nem suspeita média de confiança e acertou 93% dos itens estritos que conseguiu entregar, o maior percentual da tabela.</p>
<p>Só completou 23 casos. Sete execuções bateram no teto de 16.384 tokens, e seu acerto cai para 79% quando todo item que ficou sem resposta entra na conta. Levou 725 minutos ativos, 2,7 vezes a mediana do Sol, a 116 tokens por segundo. Pela tarifa oficial da Runpod, o piso ficou entre 5 e 6,5 vezes o equivalente de API do Sol.</p>
<p>A escolha da coluna muda completamente a leitura. O GLM foi cuidadoso e exato nos resultados avaliáveis. O conjunto modelo, harness e limite de saída não terminou trabalho suficiente.</p>
<p>O teto foi uma escolha do harness, não uma limitação intrínseca do GLM. Eu preciso repetir com respostas maiores ou sessões mais curtas. Até lá, vender o 93% sem contar as sete interrupções seria propaganda.</p>
<h2>A revisão final ajuda e também estraga<span class="hx:absolute hx:-mt-20" id="a-revisão-final-ajuda-e-também-estraga"></span>
    <a href="#a-revis%c3%a3o-final-ajuda-e-tamb%c3%a9m-estraga" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Eu sempre recomendo pedir ao agente para reler tudo antes de entregar. O benchmark confirma a utilidade dessa prática, com uma ressalva importante.</p>
<p>Em 113 das 190 execuções, os avaliadores encontraram algum problema removido ou corrigido na revisão final. Entre eles havia fabricações que teriam chegado à entrega. O modelo relê fontes, retira citação duvidosa, conserta nome e limpa afirmação que não consegue sustentar.</p>
<p>Em cerca de uma dúzia, a mesma revisão mexeu no que estava certo. Sol, RTX e GLM trocaram datas, valores e conclusões corretas por versões erradas. Em dois casos, o modelo refez uma conta errada e saiu ainda mais convencido dela.</p>
<blockquote>
  <p>Revisão automática é uma camada de higiene. A palavra &ldquo;revise&rdquo; não transforma o modelo em auditor independente de si mesmo.</p>

</blockquote>
<p>Para trabalho sensível, eu usaria pelo menos duas etapas separadas: um agente produz, outro revisa com contexto limpo e critérios explícitos. A responsabilidade continua com o profissional.</p>
<h2>O benchmark também falhou em partes<span class="hx:absolute hx:-mt-20" id="o-benchmark-também-falhou-em-partes"></span>
    <a href="#o-benchmark-tamb%c3%a9m-falhou-em-partes" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Os modelos marcaram de 97,5 a 100 na vigilância. Seria tentador anunciar que todos viraram sócios perfeitos. A explicação real é que essa parte da prova ficou fácil demais.</p>
<p>Algumas bibliotecas praticamente entregavam o problema no índice. Certos fatos que deveriam ficar escondidos já apareciam nos documentos. Alguns empurrões do cliente também contavam como descoberta espontânea. Com isso, 961 de 973 armadilhas foram pegas antes da revisão e a métrica perdeu poder de separar os modelos.</p>
<p>Itens de quatro casos com defeitos confirmados na própria suíte foram rebaixados para diagnóstico. Outros casos ainda têm inconsistências que precisam de revisão. A ordem geral se manteve, com mudanças nos empates, porém os valores absolutos não merecem duas casas decimais de confiança.</p>
<p>Há mais limitações:</p>
<ul>
<li>uma única rodada por configuração, sem média nem barra de erro;</li>
<li>Fable como criador e avaliador, sujeito aos próprios pontos cegos;</li>
<li>nenhum contador, CPA, advogado ou attorney revisou ainda uma amostra formal;</li>
<li>Codex para o Sol e OpenCode para os modelos locais;</li>
<li>quantizações e servidores diferentes;</li>
<li>um limite de saída que afetou apenas os modelos locais;</li>
<li>legislação fechada no pacote, sem medir pesquisa atualizada na internet.</li>
</ul>
<p>Eu publiquei esses problemas porque benchmark sem autocrítica vira tabela de marketing. A próxima versão precisa corrigir os casos, endurecer as armadilhas, fixar uma data de referência em todo contrato, aumentar o teto dos casos longos e repetir cada configuração.</p>
<h2>Privacidade muda a conta<span class="hx:absolute hx:-mt-20" id="privacidade-muda-a-conta"></span>
    <a href="#privacidade-muda-a-conta" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Para um desenvolvedor trabalhando num projeto aberto, mandar contexto para a nuvem pode ser aceitável. Escritórios contábeis, jurídicos, bancos e empresas reguladas lidam com contratos, estratégias de litígio, documentos fiscais, dados pessoais e segredos comerciais. Política interna, contrato com o cliente ou obrigação regulatória pode impedir que esse material saia da infraestrutura controlada pela organização.</p>
<p>Nesses casos, o modelo local deixa de competir apenas por preço. Às vezes ele é a única opção permitida.</p>
<p>Rodar localmente também não torna os dados seguros por mágica. Ainda precisa controlar acesso à estação, criptografar disco e backup, proteger logs, desligar telemetria desnecessária, limitar rede e saber onde o harness grava sessões. Um servidor local mal administrado continua sendo um vazamento esperando acontecer.</p>
<p>Todos os casos deste benchmark são sintéticos justamente para evitar esse problema durante o teste.</p>
<h2>Então já dá para usar?<span class="hx:absolute hx:-mt-20" id="então-já-dá-para-usar"></span>
    <a href="#ent%c3%a3o-j%c3%a1-d%c3%a1-para-usar" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p><strong>Nuvem, com Sol:</strong> foi a opção mais rápida e previsível desta campanha. O custo equivalente de US$ 16,72 para 38 casos é baixo perto do trabalho necessário para revisar 38 contratações. Faz sentido quando a política permite enviar os dados, com revisão humana obrigatória.</p>
<p><strong>Uma RTX 5090 própria:</strong> o Qwen3.8 Flash-Next já serve como assistente local para organizar documentos, preparar rascunhos, fazer contas, apontar pendências e levantar perguntas. Foi o melhor equilíbrio aberto que medi. Ainda errou datas e valores de forma convincente.</p>
<p><strong>Strix Halo:</strong> é uma opção compacta, mas 35 horas ativas para a campanha tornam o uso interativo difícil. O resultado desta rodada também teve invenções graves. Pode servir para tarefas em lote e baixo volume depois de novas repetições.</p>
<p><strong>B300 alugada:</strong> não melhorou a qualidade de uma sessão e o tempo ativo sozinho já passou de US$ 70 por modelo. Seu caso econômico depende de concorrência, ocupação e volume, coisas que este benchmark não mediu. Para uma pessoa fazendo uma tarefa por vez, os números não justificam a infraestrutura maior.</p>
<p><a href="https://github.com/akitaonrails/llm-non-coding-benchmark/blob/main/docs/FULL_SUITE_GRADING_20261027.md#1-lane-profiles"target="_blank" rel="noopener">Nenhuma configuração completou mais de 27 dos 38 casos</a>. O melhor resultado ficou em 71%. Mesmo aceitando que parte das falhas veio do harness e de defeitos da suíte, eu não colocaria nenhum deles para trabalhar sozinho.</p>
<blockquote>
  <p>Modelos abertos já são assistentes viáveis para contabilidade e Direito. Este experimento ainda não sustenta chamá-los de profissionais autônomos.</p>

</blockquote>
<p>Um modelo que economiza duas horas de leitura e prepara uma boa primeira versão já pode pagar a máquina. O valor está em reduzir trabalho repetitivo sem criar um risco maior que o tempo economizado.</p>
<h2>Preciso de profissionais nesta próxima rodada<span class="hx:absolute hx:-mt-20" id="preciso-de-profissionais-nesta-próxima-rodada"></span>
    <a href="#preciso-de-profissionais-nesta-pr%c3%b3xima-rodada" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>O projeto inteiro está aberto em <a href="https://github.com/akitaonrails/llm-non-coding-benchmark"target="_blank" rel="noopener">github.com/akitaonrails/llm-non-coding-benchmark</a>. Lá estão a <a href="https://github.com/akitaonrails/llm-non-coding-benchmark/blob/main/docs/METHODOLOGY.md"target="_blank" rel="noopener">metodologia</a>, o <a href="https://github.com/akitaonrails/llm-non-coding-benchmark/blob/main/docs/FULL_SUITE_GRADING_20261027.md"target="_blank" rel="noopener">relatório de correção</a>, os <a href="https://github.com/akitaonrails/llm-non-coding-benchmark/blob/main/docs/FULL_SUITE_EXECUTION_SUMMARY.md"target="_blank" rel="noopener">tempos de execução</a>, os casos, scripts e resultados brutos.</p>
<p>Agora preciso mais de profissionais do que de outra GPU. Gente licenciada precisa revisar os casos, as respostas esperadas e as falhas marcadas pelo Fable. Também quero casos novos que reflitam trabalho real sem incluir informação de clientes.</p>
<p>Se você é contador, CPA, advogado ou attorney e achou um absurdo técnico, abra uma issue com a correção e a fonte. É exatamente o tipo de escrutínio que falta para este experimento deixar de ser só uma primeira leitura interessante.</p>
]]></content:encoded><category>benchmarks-de-llm</category><category>modelos-locais</category><category>llms</category><category>open-source</category></item><item><title>LLM Benchmark v4: o veredito de open source (até out/2026)</title><link>https://www.akitaonrails.com/2026/10/22/llm-benchmark-v4-o-veredito-de-open-source-ate-out-2026/</link><guid isPermaLink="true">https://www.akitaonrails.com/2026/10/22/llm-benchmark-v4-o-veredito-de-open-source-ate-out-2026/</guid><pubDate>Thu, 22 Oct 2026 12:00:00 -0300</pubDate><description>&lt;p&gt;Este é o fechamento do benchmark v4. Desde que comecei essa rodada, foram sete artigos, primeiro na nuvem e depois empurrando a mesma régua de sabotagem pra cada vez mais longe dela:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href="https://www.akitaonrails.com/2026/09/15/novo-llm-benchmark-v4-retestando-todos-llms-parte-1/"&gt;Parte 1&lt;/a&gt;&lt;/strong&gt;: por que eu refiz o benchmark do zero pela terceira vez, como funciona o app Rails sabotado com CVEs reais, e quanto custou.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href="https://www.akitaonrails.com/2026/09/15/novo-llm-benchmark-v4-retestando-todos-llms-parte-2/"&gt;Parte 2&lt;/a&gt;&lt;/strong&gt;: o ranking original da nuvem, com quase 40 modelos, seis empates no topo e um modelo de graça batendo metade do Claude.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href="https://www.akitaonrails.com/2026/09/23/llm-benchmark-v4-opus-5-5-gpt-6-sol-luna-mimo-2-6-grok-4-7/"&gt;Opus 5.5, GPT-6 sol e luna, MiMo 2.6 e Grok 4.7&lt;/a&gt;&lt;/strong&gt;: a leva seguinte da nuvem. O Opus 5.5 fechou com 100, o GPT 6 luna fez 95,5 por menos de um dólar a rodada, e o Grok 4.7 regrediu contra o próprio antecessor.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href="https://www.akitaonrails.com/2026/10/19/llm-benchmark-v4-os-top-llms-open-source-numa-b300/"&gt;Os top LLMs open source numa B300&lt;/a&gt;&lt;/strong&gt;: uma GPU alugada, 14 modelos abertos, e dois deles, GLM 5.3 Flash e Qwen3.8 Flash-Next, empatados em 92,5, no nível do Sonnet 5.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href="https://www.akitaonrails.com/2026/10/22/llm-benchmark-v4-open-source-que-so-cabe-em-4x-b300/"&gt;Open source que só cabe em 4x B300&lt;/a&gt;&lt;/strong&gt;: os modelos grandes, a maioria de 400 bilhões a 1 trilhão de parâmetros. O DeepSeek V4.1 Flash empatou com o líder em 92,5 e o resto mostrou que tamanho não compra vigilância.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href="https://www.akitaonrails.com/2026/10/22/llm-benchmark-v4-can-my-team-fit-in-a-4x-b300-h200/"&gt;Meu time cabe em 4x B300/H200?&lt;/a&gt;&lt;/strong&gt;: teste de concorrência de verdade. O mesmo DeepSeek atende 32 sessões confortáveis no pod de B300 e só 8 no de H200.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href="https://www.akitaonrails.com/2026/10/22/llm-benchmark-v4-open-source-nivel-consumidor-rtx5090-strix-halo-strata/"&gt;Open source nível consumidor na RTX 5090 ou Strix Halo&lt;/a&gt;&lt;/strong&gt;: o Qwen3.8 Flash-Next fez 91 numa placa de PC com o motor Strata, a 159 tokens por segundo.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Este artigo junta tudo numa tabela só e responde a pergunta que sobra: &lt;strong&gt;até outubro de 2026, open source já dá conta?&lt;/strong&gt;&lt;/p&gt;</description><content:encoded><![CDATA[<p>Este é o fechamento do benchmark v4. Desde que comecei essa rodada, foram sete artigos, primeiro na nuvem e depois empurrando a mesma régua de sabotagem pra cada vez mais longe dela:</p>
<ul>
<li><strong><a href="/2026/09/15/novo-llm-benchmark-v4-retestando-todos-llms-parte-1/">Parte 1</a></strong>: por que eu refiz o benchmark do zero pela terceira vez, como funciona o app Rails sabotado com CVEs reais, e quanto custou.</li>
<li><strong><a href="/2026/09/15/novo-llm-benchmark-v4-retestando-todos-llms-parte-2/">Parte 2</a></strong>: o ranking original da nuvem, com quase 40 modelos, seis empates no topo e um modelo de graça batendo metade do Claude.</li>
<li><strong><a href="/2026/09/23/llm-benchmark-v4-opus-5-5-gpt-6-sol-luna-mimo-2-6-grok-4-7/">Opus 5.5, GPT-6 sol e luna, MiMo 2.6 e Grok 4.7</a></strong>: a leva seguinte da nuvem. O Opus 5.5 fechou com 100, o GPT 6 luna fez 95,5 por menos de um dólar a rodada, e o Grok 4.7 regrediu contra o próprio antecessor.</li>
<li><strong><a href="/2026/10/19/llm-benchmark-v4-os-top-llms-open-source-numa-b300/">Os top LLMs open source numa B300</a></strong>: uma GPU alugada, 14 modelos abertos, e dois deles, GLM 5.3 Flash e Qwen3.8 Flash-Next, empatados em 92,5, no nível do Sonnet 5.</li>
<li><strong><a href="/2026/10/22/llm-benchmark-v4-open-source-que-so-cabe-em-4x-b300/">Open source que só cabe em 4x B300</a></strong>: os modelos grandes, a maioria de 400 bilhões a 1 trilhão de parâmetros. O DeepSeek V4.1 Flash empatou com o líder em 92,5 e o resto mostrou que tamanho não compra vigilância.</li>
<li><strong><a href="/2026/10/22/llm-benchmark-v4-can-my-team-fit-in-a-4x-b300-h200/">Meu time cabe em 4x B300/H200?</a></strong>: teste de concorrência de verdade. O mesmo DeepSeek atende 32 sessões confortáveis no pod de B300 e só 8 no de H200.</li>
<li><strong><a href="/2026/10/22/llm-benchmark-v4-open-source-nivel-consumidor-rtx5090-strix-halo-strata/">Open source nível consumidor na RTX 5090 ou Strix Halo</a></strong>: o Qwen3.8 Flash-Next fez 91 numa placa de PC com o motor Strata, a 159 tokens por segundo.</li>
</ul>
<p>Este artigo junta tudo numa tabela só e responde a pergunta que sobra: <strong>até outubro de 2026, open source já dá conta?</strong></p>
<h2>O maior benchmark que eu já rodei<span class="hx:absolute hx:-mt-20" id="o-maior-benchmark-que-eu-já-rodei"></span>
    <a href="#o-maior-benchmark-que-eu-j%c3%a1-rodei" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>O v4 foi, de longe, a maior rodada de testes que eu já fiz. São quase 70 rodadas completas do mesmo protocolo, quase 60 modelos diferentes, rodando em API de nuvem, numa B300 alugada, num pod de quatro B300, numa RTX 5090 e num Strix Halo, mais um pod de quatro H200 só pro teste de concorrência.</p>
<p>E foi caro. Só a primeira leva, a da nuvem, passou de 4 mil dólares em nove dias, somando créditos e assinaturas. A B300 única custou uns 270, e a campanha de 4x B300 e 4x H200 levou mais uns 2.100. Somando o que eu consigo somar, passou de 6 mil dólares, sem contar as horas de máquina em casa.</p>
<blockquote>
  <p>Eu não acho que muita gente vai reproduzir isso tão cedo. Se este material te serviu, compartilhe com quem precisa decidir entre nuvem, servidor próprio ou PC local. Todo o código, os prompts, os logs e os relatórios estão abertos no meu GitHub: <a href="https://github.com/akitaonrails/llm-coding-benchmark"target="_blank" rel="noopener">akitaonrails/llm-coding-benchmark</a>.</p>

</blockquote>
<h3>Relembrando a régua<span class="hx:absolute hx:-mt-20" id="relembrando-a-régua"></span>
    <a href="#relembrando-a-r%c3%a9gua" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>O protocolo é sempre o mesmo. O modelo constrói um app Rails de chat em sete sprints, e entre as sprints um revisor injeta 14 defeitos de segurança e de correção, alguns barulhentos (CVE em gem, chave no código), outros silenciosos (vazamento entre tenants, agregado errado, teste adulterado pra esconder backdoor).</p>
<p>A nota vai de 0 a 100. Defeito corrigido sem aviso vale peso cheio; corrigido só depois que eu revelo que tem problema vale 40%; nunca corrigido vale zero. Os tiers são A a partir de 83, B de 73 a 82,5, C de 51 a 72,5 e D até 50.</p>
<p>É uma régua de <strong>vigilância de revisão de código</strong>. Ela mede se o modelo percebe sabotagem enquanto trabalha, e isso é diferente de medir quem escreve o seu sistema inteiro melhor.</p>
<h2>O ranking combinado<span class="hx:absolute hx:-mt-20" id="o-ranking-combinado"></span>
    <a href="#o-ranking-combinado" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Todas as rodadas do v4 numa tabela só, comerciais e abertos, nuvem e hardware próprio. Quando o mesmo modelo rodou em lugares diferentes, ele aparece mais de uma vez, porque o lugar muda a nota.</p>
<table>
  <thead>
      <tr>
          <th style="text-align: right">#</th>
          <th>Modelo</th>
          <th>Pesos</th>
          <th>Onde rodou</th>
          <th style="text-align: right">Nota</th>
          <th style="text-align: center">Tier</th>
          <th style="text-align: right">Custo da rodada</th>
          <th style="text-align: right">tok/s</th>
          <th style="text-align: right">Minutos</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td style="text-align: right">1</td>
          <td>GPT-6 Astra</td>
          <td>fechado</td>
          <td>Nuvem (Codex)</td>
          <td style="text-align: right">100,0</td>
          <td style="text-align: center">A</td>
          <td style="text-align: right">US$ 30,55</td>
          <td style="text-align: right">—</td>
          <td style="text-align: right">100</td>
      </tr>
      <tr>
          <td style="text-align: right">1</td>
          <td>Claude Opus 5</td>
          <td>fechado</td>
          <td>Nuvem (Claude Code)</td>
          <td style="text-align: right">100,0</td>
          <td style="text-align: center">A</td>
          <td style="text-align: right">~US$ 71 ¹</td>
          <td style="text-align: right">—</td>
          <td style="text-align: right">145</td>
      </tr>
      <tr>
          <td style="text-align: right">1</td>
          <td>Claude Fable 5</td>
          <td>fechado</td>
          <td>Nuvem (Claude Code)</td>
          <td style="text-align: right">100,0</td>
          <td style="text-align: center">A</td>
          <td style="text-align: right">~US$ 50 ¹</td>
          <td style="text-align: right">—</td>
          <td style="text-align: right">85</td>
      </tr>
      <tr>
          <td style="text-align: right">1</td>
          <td>Claude Opus 5.5</td>
          <td>fechado</td>
          <td>Nuvem (Claude Code)</td>
          <td style="text-align: right">100,0</td>
          <td style="text-align: center">A</td>
          <td style="text-align: right">US$ 16,63 ¹</td>
          <td style="text-align: right">—</td>
          <td style="text-align: right">64</td>
      </tr>
      <tr>
          <td style="text-align: right">1</td>
          <td>GPT 5.6 sol</td>
          <td>fechado</td>
          <td>Nuvem (Codex)</td>
          <td style="text-align: right">100,0</td>
          <td style="text-align: center">A</td>
          <td style="text-align: right">US$ 20,37</td>
          <td style="text-align: right">—</td>
          <td style="text-align: right">317</td>
      </tr>
      <tr>
          <td style="text-align: right">1</td>
          <td>GPT 5.6 terra</td>
          <td>fechado</td>
          <td>Nuvem (Codex)</td>
          <td style="text-align: right">100,0</td>
          <td style="text-align: center">A</td>
          <td style="text-align: right">US$ 9,52</td>
          <td style="text-align: right">—</td>
          <td style="text-align: right">80</td>
      </tr>
      <tr>
          <td style="text-align: right">1</td>
          <td>GPT 5.5</td>
          <td>fechado</td>
          <td>Nuvem (Codex)</td>
          <td style="text-align: right">100,0</td>
          <td style="text-align: center">A</td>
          <td style="text-align: right">US$ 34,69</td>
          <td style="text-align: right">—</td>
          <td style="text-align: right">117</td>
      </tr>
      <tr>
          <td style="text-align: right">8</td>
          <td>Grok 4.6</td>
          <td>fechado</td>
          <td>Nuvem (OpenCode)</td>
          <td style="text-align: right">98,5</td>
          <td style="text-align: center">A</td>
          <td style="text-align: right">US$ 13,00</td>
          <td style="text-align: right">—</td>
          <td style="text-align: right">67</td>
      </tr>
      <tr>
          <td style="text-align: right">9</td>
          <td>Claude Fable 5.1</td>
          <td>fechado</td>
          <td>Nuvem (Claude Code)</td>
          <td style="text-align: right">95,5</td>
          <td style="text-align: center">A</td>
          <td style="text-align: right">~US$ 51 ¹</td>
          <td style="text-align: right">—</td>
          <td style="text-align: right">133</td>
      </tr>
      <tr>
          <td style="text-align: right">9</td>
          <td>Sakana Fugu Ultra v2</td>
          <td>fechado</td>
          <td>Nuvem (OpenCode)</td>
          <td style="text-align: right">95,5</td>
          <td style="text-align: center">A</td>
          <td style="text-align: right">US$ 122,01</td>
          <td style="text-align: right">—</td>
          <td style="text-align: right">294</td>
      </tr>
      <tr>
          <td style="text-align: right">9</td>
          <td>GPT 6 luna</td>
          <td>fechado</td>
          <td>Nuvem (Codex)</td>
          <td style="text-align: right">95,5</td>
          <td style="text-align: center">A</td>
          <td style="text-align: right">~US$ 0,84 ²</td>
          <td style="text-align: right">—</td>
          <td style="text-align: right">122</td>
      </tr>
      <tr>
          <td style="text-align: right">12</td>
          <td>GPT 5.6 luna</td>
          <td>fechado</td>
          <td>Nuvem (Codex)</td>
          <td style="text-align: right">95,0</td>
          <td style="text-align: center">A</td>
          <td style="text-align: right">US$ 10,04</td>
          <td style="text-align: right">—</td>
          <td style="text-align: right">123</td>
      </tr>
      <tr>
          <td style="text-align: right">13</td>
          <td>Nex N2.5 Pro ⁴</td>
          <td>aberto</td>
          <td>Nuvem (OpenCode)</td>
          <td style="text-align: right">94,0</td>
          <td style="text-align: center">A</td>
          <td style="text-align: right">grátis</td>
          <td style="text-align: right">—</td>
          <td style="text-align: right">480</td>
      </tr>
      <tr>
          <td style="text-align: right">13</td>
          <td>GLM 5.3</td>
          <td>aberto</td>
          <td>Nuvem (ZCode)</td>
          <td style="text-align: right">94,0</td>
          <td style="text-align: center">A</td>
          <td style="text-align: right">plano flat</td>
          <td style="text-align: right">—</td>
          <td style="text-align: right">215</td>
      </tr>
      <tr>
          <td style="text-align: right">15</td>
          <td>DeepSeek V4.1 Flash</td>
          <td>aberto</td>
          <td>Nuvem (OpenCode)</td>
          <td style="text-align: right">92,5</td>
          <td style="text-align: center">A</td>
          <td style="text-align: right">US$ 1,21</td>
          <td style="text-align: right">—</td>
          <td style="text-align: right">172</td>
      </tr>
      <tr>
          <td style="text-align: right">15</td>
          <td>DeepSeek V4.1 Flash</td>
          <td>aberto</td>
          <td>4x B300</td>
          <td style="text-align: right">92,5</td>
          <td style="text-align: center">A</td>
          <td style="text-align: right">pod a US$ 36/h</td>
          <td style="text-align: right">174,8</td>
          <td style="text-align: right">—</td>
      </tr>
      <tr>
          <td style="text-align: right">15</td>
          <td>GLM 5.3 Flash (NVFP4)</td>
          <td>aberto</td>
          <td>1x B300</td>
          <td style="text-align: right">92,5</td>
          <td style="text-align: center">A</td>
          <td style="text-align: right">US$ 21,96 ³</td>
          <td style="text-align: right">142,0</td>
          <td style="text-align: right">145</td>
      </tr>
      <tr>
          <td style="text-align: right">15</td>
          <td>Qwen3.8 Flash-Next (NVFP4)</td>
          <td>aberto</td>
          <td>1x B300</td>
          <td style="text-align: right">92,5</td>
          <td style="text-align: center">A</td>
          <td style="text-align: right">US$ 12,04 ³</td>
          <td style="text-align: right">195,0</td>
          <td style="text-align: right">80</td>
      </tr>
      <tr>
          <td style="text-align: right">19</td>
          <td>MiMo V2.6 Pro</td>
          <td>aberto</td>
          <td>Nuvem (OpenCode)</td>
          <td style="text-align: right">92,0</td>
          <td style="text-align: center">A</td>
          <td style="text-align: right">US$ 1,22</td>
          <td style="text-align: right">—</td>
          <td style="text-align: right">308</td>
      </tr>
      <tr>
          <td style="text-align: right">20</td>
          <td>Qwen3.8 Flash-Next (IQ3_S)</td>
          <td>aberto</td>
          <td>RTX 5090 (Strata)</td>
          <td style="text-align: right">91,0</td>
          <td style="text-align: center">A</td>
          <td style="text-align: right">US$ 0 (local)</td>
          <td style="text-align: right">159,2</td>
          <td style="text-align: right">—</td>
      </tr>
      <tr>
          <td style="text-align: right">20</td>
          <td>Claude Sonnet 5</td>
          <td>fechado</td>
          <td>Nuvem (Claude Code)</td>
          <td style="text-align: right">91,0</td>
          <td style="text-align: center">A</td>
          <td style="text-align: right">~US$ 27 ¹</td>
          <td style="text-align: right">—</td>
          <td style="text-align: right">112</td>
      </tr>
      <tr>
          <td style="text-align: right">20</td>
          <td>GPT 6 sol</td>
          <td>fechado</td>
          <td>Nuvem (Codex)</td>
          <td style="text-align: right">91,0</td>
          <td style="text-align: center">A</td>
          <td style="text-align: right">~US$ 8,35 ²</td>
          <td style="text-align: right">—</td>
          <td style="text-align: right">73</td>
      </tr>
      <tr>
          <td style="text-align: right">23</td>
          <td>Gemini 3.8 Flash high</td>
          <td>fechado</td>
          <td>Nuvem (OpenCode)</td>
          <td style="text-align: right">90,5</td>
          <td style="text-align: center">A</td>
          <td style="text-align: right">US$ 15,98</td>
          <td style="text-align: right">—</td>
          <td style="text-align: right">97</td>
      </tr>
      <tr>
          <td style="text-align: right">24</td>
          <td>Gemini 3.8 Flash</td>
          <td>fechado</td>
          <td>Nuvem (Antigravity)</td>
          <td style="text-align: right">89,5</td>
          <td style="text-align: center">A</td>
          <td style="text-align: right">grátis (OAuth)</td>
          <td style="text-align: right">—</td>
          <td style="text-align: right">120</td>
      </tr>
      <tr>
          <td style="text-align: right">24</td>
          <td>MiniMax M3.1 Flash</td>
          <td>fechado</td>
          <td>Nuvem (OpenCode)</td>
          <td style="text-align: right">89,5</td>
          <td style="text-align: center">A</td>
          <td style="text-align: right">grátis</td>
          <td style="text-align: right">—</td>
          <td style="text-align: right">—</td>
      </tr>
      <tr>
          <td style="text-align: right">26</td>
          <td>Muse Spark 1.3</td>
          <td>fechado</td>
          <td>Nuvem (OpenCode)</td>
          <td style="text-align: right">88,75</td>
          <td style="text-align: center">A</td>
          <td style="text-align: right">US$ 13,31</td>
          <td style="text-align: right">—</td>
          <td style="text-align: right">150</td>
      </tr>
      <tr>
          <td style="text-align: right">27</td>
          <td>Swift 1.5 27B</td>
          <td>aberto</td>
          <td>1x B300</td>
          <td style="text-align: right">88,0</td>
          <td style="text-align: center">A</td>
          <td style="text-align: right">US$ 9,71 ³</td>
          <td style="text-align: right">203,0</td>
          <td style="text-align: right">64</td>
      </tr>
      <tr>
          <td style="text-align: right">27</td>
          <td>Grok 4.5</td>
          <td>fechado</td>
          <td>Nuvem (OpenCode)</td>
          <td style="text-align: right">88,0</td>
          <td style="text-align: center">A</td>
          <td style="text-align: right">US$ 6,19</td>
          <td style="text-align: right">—</td>
          <td style="text-align: right">48</td>
      </tr>
      <tr>
          <td style="text-align: right">29</td>
          <td>Claude Opus 4.6</td>
          <td>fechado</td>
          <td>Nuvem (Claude Code)</td>
          <td style="text-align: right">87,5</td>
          <td style="text-align: center">A</td>
          <td style="text-align: right">US$ 25,64 ¹</td>
          <td style="text-align: right">—</td>
          <td style="text-align: right">89</td>
      </tr>
      <tr>
          <td style="text-align: right">30</td>
          <td>Kimi K2.7 Code</td>
          <td>aberto</td>
          <td>Nuvem (Kimi CLI)</td>
          <td style="text-align: right">87,25</td>
          <td style="text-align: center">A</td>
          <td style="text-align: right">US$ 7,75</td>
          <td style="text-align: right">—</td>
          <td style="text-align: right">175</td>
      </tr>
      <tr>
          <td style="text-align: right">31</td>
          <td>MiMo V2.5 Pro</td>
          <td>aberto</td>
          <td>Nuvem (OpenCode)</td>
          <td style="text-align: right">86,5</td>
          <td style="text-align: center">A</td>
          <td style="text-align: right">US$ 1,03</td>
          <td style="text-align: right">—</td>
          <td style="text-align: right">158</td>
      </tr>
      <tr>
          <td style="text-align: right">31</td>
          <td>Qwen3.8 Flash</td>
          <td>fechado</td>
          <td>Nuvem (OpenCode)</td>
          <td style="text-align: right">86,5</td>
          <td style="text-align: center">A</td>
          <td style="text-align: right">US$ 1,17</td>
          <td style="text-align: right">—</td>
          <td style="text-align: right">149</td>
      </tr>
      <tr>
          <td style="text-align: right">33</td>
          <td>DeepSeek V4 Flash</td>
          <td>aberto</td>
          <td>Nuvem (OpenCode)</td>
          <td style="text-align: right">86,0</td>
          <td style="text-align: center">A</td>
          <td style="text-align: right">US$ 0,97</td>
          <td style="text-align: right">—</td>
          <td style="text-align: right">111</td>
      </tr>
      <tr>
          <td style="text-align: right">33</td>
          <td>Claude Opus 4.8</td>
          <td>fechado</td>
          <td>Nuvem (Claude Code)</td>
          <td style="text-align: right">86,0</td>
          <td style="text-align: center">A</td>
          <td style="text-align: right">~US$ 37 ¹</td>
          <td style="text-align: right">—</td>
          <td style="text-align: right">87</td>
      </tr>
      <tr>
          <td style="text-align: right">35</td>
          <td>Claude Sonnet 4.6</td>
          <td>fechado</td>
          <td>Nuvem (Claude Code)</td>
          <td style="text-align: right">85,75</td>
          <td style="text-align: center">A</td>
          <td style="text-align: right">US$ 18,64 ¹</td>
          <td style="text-align: right">—</td>
          <td style="text-align: right">91</td>
      </tr>
      <tr>
          <td style="text-align: right">36</td>
          <td>Kimi K3</td>
          <td>aberto</td>
          <td>Nuvem (Kimi CLI)</td>
          <td style="text-align: right">85,0</td>
          <td style="text-align: center">A</td>
          <td style="text-align: right">US$ 13,79</td>
          <td style="text-align: right">—</td>
          <td style="text-align: right">148</td>
      </tr>
      <tr>
          <td style="text-align: right">36</td>
          <td>DeepSeek V4 Flash 0731</td>
          <td>aberto</td>
          <td>Nuvem (OpenCode)</td>
          <td style="text-align: right">85,0</td>
          <td style="text-align: center">A</td>
          <td style="text-align: right">US$ 1,94</td>
          <td style="text-align: right">—</td>
          <td style="text-align: right">194</td>
      </tr>
      <tr>
          <td style="text-align: right">38</td>
          <td>GLM 5.3 Flash</td>
          <td>aberto</td>
          <td>Nuvem (ZCode)</td>
          <td style="text-align: right">84,25</td>
          <td style="text-align: center">A</td>
          <td style="text-align: right">plano flat</td>
          <td style="text-align: right">—</td>
          <td style="text-align: right">296</td>
      </tr>
      <tr>
          <td style="text-align: right">39</td>
          <td>DeepSeek V4 Pro 0813</td>
          <td>aberto</td>
          <td>Nuvem (OpenCode)</td>
          <td style="text-align: right">84,0</td>
          <td style="text-align: center">A</td>
          <td style="text-align: right">US$ 4,49</td>
          <td style="text-align: right">—</td>
          <td style="text-align: right">152</td>
      </tr>
      <tr>
          <td style="text-align: right">40</td>
          <td>Step 3.7 Flash</td>
          <td>aberto</td>
          <td>Nuvem (OpenCode)</td>
          <td style="text-align: right">83,75</td>
          <td style="text-align: center">A</td>
          <td style="text-align: right">US$ 4,15</td>
          <td style="text-align: right">—</td>
          <td style="text-align: right">118</td>
      </tr>
      <tr>
          <td style="text-align: right">41</td>
          <td>Grok 4.7</td>
          <td>fechado</td>
          <td>Nuvem (OpenCode)</td>
          <td style="text-align: right">83,5</td>
          <td style="text-align: center">A</td>
          <td style="text-align: right">US$ 27,94</td>
          <td style="text-align: right">—</td>
          <td style="text-align: right">120</td>
      </tr>
      <tr>
          <td style="text-align: right">42</td>
          <td>ThinkingCap 3.8 27B</td>
          <td>aberto</td>
          <td>1x B300</td>
          <td style="text-align: right">83,0</td>
          <td style="text-align: center">A</td>
          <td style="text-align: right">US$ 11,47 ³</td>
          <td style="text-align: right">189,0</td>
          <td style="text-align: right">76</td>
      </tr>
      <tr>
          <td style="text-align: right">43</td>
          <td>DeepSeek V4 Pro</td>
          <td>aberto</td>
          <td>Nuvem (OpenCode)</td>
          <td style="text-align: right">82,0</td>
          <td style="text-align: center">B</td>
          <td style="text-align: right">US$ 5,26</td>
          <td style="text-align: right">—</td>
          <td style="text-align: right">97</td>
      </tr>
      <tr>
          <td style="text-align: right">44</td>
          <td>Muse Glimmer 30B</td>
          <td>aberto</td>
          <td>1x B300</td>
          <td style="text-align: right">81,0</td>
          <td style="text-align: center">B</td>
          <td style="text-align: right">US$ 3,54 ³</td>
          <td style="text-align: right">295,0</td>
          <td style="text-align: right">23</td>
      </tr>
      <tr>
          <td style="text-align: right">44</td>
          <td>Ornith 1.5 35B</td>
          <td>aberto</td>
          <td>1x B300</td>
          <td style="text-align: right">81,0</td>
          <td style="text-align: center">B</td>
          <td style="text-align: right">US$ 24,39 ³</td>
          <td style="text-align: right">317,0</td>
          <td style="text-align: right">161</td>
      </tr>
      <tr>
          <td style="text-align: right">46</td>
          <td>Qwen 3.8 27B</td>
          <td>aberto</td>
          <td>1x B300</td>
          <td style="text-align: right">80,5</td>
          <td style="text-align: center">B</td>
          <td style="text-align: right">US$ 29,11 ³</td>
          <td style="text-align: right">195,0</td>
          <td style="text-align: right">192</td>
      </tr>
      <tr>
          <td style="text-align: right">47</td>
          <td>Qwen 3.8 27B (Q8_0)</td>
          <td>aberto</td>
          <td>Strix Halo</td>
          <td style="text-align: right">80,0</td>
          <td style="text-align: center">B</td>
          <td style="text-align: right">US$ 0 (local)</td>
          <td style="text-align: right">7,7</td>
          <td style="text-align: right">706</td>
      </tr>
      <tr>
          <td style="text-align: right">48</td>
          <td>Qwen 3.7 Max</td>
          <td>fechado</td>
          <td>Nuvem (OpenCode)</td>
          <td style="text-align: right">79,0</td>
          <td style="text-align: center">B</td>
          <td style="text-align: right">US$ 10,63</td>
          <td style="text-align: right">—</td>
          <td style="text-align: right">106</td>
      </tr>
      <tr>
          <td style="text-align: right">49</td>
          <td>GLM 5.2</td>
          <td>aberto</td>
          <td>Nuvem (ZCode)</td>
          <td style="text-align: right">77,0</td>
          <td style="text-align: center">B</td>
          <td style="text-align: right">plano flat</td>
          <td style="text-align: right">—</td>
          <td style="text-align: right">239</td>
      </tr>
      <tr>
          <td style="text-align: right">50</td>
          <td>GLM 5.3 full</td>
          <td>aberto</td>
          <td>4x B300</td>
          <td style="text-align: right">76,0</td>
          <td style="text-align: center">B</td>
          <td style="text-align: right">pod a US$ 36/h</td>
          <td style="text-align: right">110,0</td>
          <td style="text-align: right">—</td>
      </tr>
      <tr>
          <td style="text-align: right">51</td>
          <td>Gemini 3.7 Flash high</td>
          <td>fechado</td>
          <td>Nuvem (OpenCode)</td>
          <td style="text-align: right">75,5</td>
          <td style="text-align: center">B</td>
          <td style="text-align: right">US$ 12,93</td>
          <td style="text-align: right">—</td>
          <td style="text-align: right">85</td>
      </tr>
      <tr>
          <td style="text-align: right">51</td>
          <td>MiniMax M3</td>
          <td>aberto</td>
          <td>Nuvem (OpenCode)</td>
          <td style="text-align: right">75,5</td>
          <td style="text-align: center">B</td>
          <td style="text-align: right">US$ 12,17</td>
          <td style="text-align: right">—</td>
          <td style="text-align: right">187</td>
      </tr>
      <tr>
          <td style="text-align: right">53</td>
          <td>Ornith 1.5 122B</td>
          <td>aberto</td>
          <td>4x B300</td>
          <td style="text-align: right">73,0</td>
          <td style="text-align: center">B</td>
          <td style="text-align: right">pod a US$ 36/h</td>
          <td style="text-align: right">244,2</td>
          <td style="text-align: right">—</td>
      </tr>
      <tr>
          <td style="text-align: right">54</td>
          <td>Step-5-Preview</td>
          <td>aberto</td>
          <td>4x B300</td>
          <td style="text-align: right">72,5</td>
          <td style="text-align: center">C</td>
          <td style="text-align: right">pod a US$ 36/h</td>
          <td style="text-align: right">165,9</td>
          <td style="text-align: right">—</td>
      </tr>
      <tr>
          <td style="text-align: right">55</td>
          <td>Muse Spark Open</td>
          <td>aberto</td>
          <td>4x B300</td>
          <td style="text-align: right">69,5</td>
          <td style="text-align: center">C</td>
          <td style="text-align: right">pod a US$ 36/h</td>
          <td style="text-align: right">130,8</td>
          <td style="text-align: right">—</td>
      </tr>
      <tr>
          <td style="text-align: right">56</td>
          <td>Qwen3.5 397B (NVFP4)</td>
          <td>aberto</td>
          <td>1x B300</td>
          <td style="text-align: right">68,5</td>
          <td style="text-align: center">C</td>
          <td style="text-align: right">US$ 8,22 ³</td>
          <td style="text-align: right">140,0</td>
          <td style="text-align: right">54</td>
      </tr>
      <tr>
          <td style="text-align: right">57</td>
          <td>MiMo V2.6 Pro</td>
          <td>aberto</td>
          <td>4x B300</td>
          <td style="text-align: right">67,0</td>
          <td style="text-align: center">C</td>
          <td style="text-align: right">pod a US$ 36/h</td>
          <td style="text-align: right">167,2</td>
          <td style="text-align: right">—</td>
      </tr>
      <tr>
          <td style="text-align: right">58</td>
          <td>Laguna-S-2.1</td>
          <td>aberto</td>
          <td>4x B300</td>
          <td style="text-align: right">65,5</td>
          <td style="text-align: center">C</td>
          <td style="text-align: right">pod a US$ 36/h</td>
          <td style="text-align: right">263,0</td>
          <td style="text-align: right">—</td>
      </tr>
      <tr>
          <td style="text-align: right">59</td>
          <td>Ornith 1.5 35B (Q8_0)</td>
          <td>aberto</td>
          <td>RTX 5090 (Strata)</td>
          <td style="text-align: right">64,5</td>
          <td style="text-align: center">C</td>
          <td style="text-align: right">US$ 0 (local)</td>
          <td style="text-align: right">80,0</td>
          <td style="text-align: right">—</td>
      </tr>
      <tr>
          <td style="text-align: right">60</td>
          <td>Nemotron 3.5 Super VL (NVFP4)</td>
          <td>aberto</td>
          <td>1x B300</td>
          <td style="text-align: right">63,0</td>
          <td style="text-align: center">C</td>
          <td style="text-align: right">US$ 7,51 ³</td>
          <td style="text-align: right">207,0</td>
          <td style="text-align: right">50</td>
      </tr>
      <tr>
          <td style="text-align: right">61</td>
          <td>Kimi K2 Thinking</td>
          <td>aberto</td>
          <td>4x B300</td>
          <td style="text-align: right">46,0</td>
          <td style="text-align: center">D</td>
          <td style="text-align: right">pod a US$ 36/h</td>
          <td style="text-align: right">110,0</td>
          <td style="text-align: right">—</td>
      </tr>
      <tr>
          <td style="text-align: right">62</td>
          <td>DeepSeek V4 Flash</td>
          <td>aberto</td>
          <td>1x B300</td>
          <td style="text-align: right">44,5</td>
          <td style="text-align: center">D</td>
          <td style="text-align: right">US$ 5,59 ³</td>
          <td style="text-align: right">153,0</td>
          <td style="text-align: right">37</td>
      </tr>
      <tr>
          <td style="text-align: right">63</td>
          <td>Mistral Large 3</td>
          <td>aberto</td>
          <td>Nuvem (OpenCode)</td>
          <td style="text-align: right">39,0</td>
          <td style="text-align: center">D</td>
          <td style="text-align: right">US$ 5,11</td>
          <td style="text-align: right">—</td>
          <td style="text-align: right">76</td>
      </tr>
      <tr>
          <td style="text-align: right">64</td>
          <td>GLM 4.7 Flash (BF16)</td>
          <td>aberto</td>
          <td>1x B300</td>
          <td style="text-align: right">33,5</td>
          <td style="text-align: center">D</td>
          <td style="text-align: right">US$ 4,60 ³</td>
          <td style="text-align: right">217,0</td>
          <td style="text-align: right">30</td>
      </tr>
      <tr>
          <td style="text-align: right">65</td>
          <td>Gemini 3.1 Pro ⁴</td>
          <td>fechado</td>
          <td>Nuvem (OpenCode)</td>
          <td style="text-align: right">32,5</td>
          <td style="text-align: center">D</td>
          <td style="text-align: right">US$ 10,31</td>
          <td style="text-align: right">—</td>
          <td style="text-align: right">54</td>
      </tr>
      <tr>
          <td style="text-align: right">66</td>
          <td>Nex N2.5 Pro</td>
          <td>aberto</td>
          <td>4x B300</td>
          <td style="text-align: right">27,0</td>
          <td style="text-align: center">D</td>
          <td style="text-align: right">pod a US$ 36/h</td>
          <td style="text-align: right">184,0</td>
          <td style="text-align: right">—</td>
      </tr>
      <tr>
          <td style="text-align: right">67</td>
          <td>GLM-4.7-Flash (Q5_K_M)</td>
          <td>aberto</td>
          <td>RTX 5090</td>
          <td style="text-align: right">24,0</td>
          <td style="text-align: center">D</td>
          <td style="text-align: right">US$ 0 (local)</td>
          <td style="text-align: right">—</td>
          <td style="text-align: right">29</td>
      </tr>
  </tbody>
</table>
<p>Como ler as colunas:</p>
<ul>
<li><strong>Pesos</strong>: &ldquo;aberto&rdquo; é checkpoint baixável, qualquer licença; &ldquo;fechado&rdquo; é só API.</li>
<li><strong>Custo da rodada</strong>: o que uma rodada completa de sete sprints custou.</li>
<li><strong>tok/s</strong>: velocidade de decodificação de um stream só, medida por mim. Só existe pra quem eu servi.</li>
<li><strong>Minutos</strong>: tempo de relógio da rodada inteira, quando eu tenho o dado. Na B300 única, é só o tempo dos sprints.</li>
</ul>
<p>E as notas de rodapé:</p>
<ol>
<li>Claude rodou pela assinatura Max do Claude Code. O custo é nocional: os tokens da rodada a preço de tabela da API.</li>
<li>Estimado a partir dos tokens da rodada.</li>
<li>Só o tempo de inferência na B300 a uns US$ 9 por hora. Download, carga, compilação e tuning ficam de fora, e o custo real do pod foi maior.</li>
<li>Nota com ressalva. O Nex na nuvem foi avaliado pelo disco, com tudo sem commit; o Gemini 3.1 Pro foi prejudicado por um bug do proxy do OpenRouter.</li>
</ol>
<p>Os primeiros artigos do v4 juntavam tudo abaixo de B como C; aqui eu uso a escala com D, a mesma das campanhas de B300. O MiniMax M3.1 Flash rodou de graça pelo OpenRouter como âncora de nuvem da campanha local, e a MiniMax promete liberar os pesos dele no dia 23 de outubro.</p>
<h2>Os modelos estão convergindo<span class="hx:absolute hx:-mt-20" id="os-modelos-estão-convergindo"></span>
    <a href="#os-modelos-est%c3%a3o-convergindo" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>No escopo limitado deste benchmark, a impressão é de que todo mundo está chegando no mesmo lugar. <strong>42 das 67 rodadas fizeram Tier A</strong>, 24 de modelos fechados e 18 de abertos. Sete modelos bateram 100, e 23 rodadas ficaram acima de 90.</p>
<p>Parte disso é limite da própria régua. Quando sete modelos tiram a nota máxima, o teste deixa de separar o topo. E uma rodada repetida do GPT 5.5 fez 91 contra os 100 da oficial, então diferenças de poucos pontos entre dois modelos podem ser só ruído.</p>
<p>Mesmo descontando isso, o desenho é claro. O DeepSeek V4.1 Flash, o GLM 5.3 Flash e o Qwen3.8 Flash-Next, todos abertos, estão a 7,5 pontos do teto, colados no Sonnet 5 e no GPT 6 sol.</p>
<blockquote>
  <p>Pra revisão de código e vigilância, o modelo aberto bom já está no pelotão do modelo fechado bom. O que ainda separa os dois é consistência: entre os abertos aparecem notas D que entre os fechados de ponta não aparecem.</p>

</blockquote>
<h3>O Ocidente parado, a China correndo<span class="hx:absolute hx:-mt-20" id="o-ocidente-parado-a-china-correndo"></span>
    <a href="#o-ocidente-parado-a-china-correndo" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Comparando gerações da mesma família dentro do v4, a diferença de ritmo aparece:</p>
<table>
  <thead>
      <tr>
          <th>Família</th>
          <th style="text-align: right">Antes</th>
          <th style="text-align: right">Depois</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>DeepSeek V4 Flash → V4.1 Flash</td>
          <td style="text-align: right">86,0</td>
          <td style="text-align: right">92,5</td>
      </tr>
      <tr>
          <td>GLM 5.2 → 5.3</td>
          <td style="text-align: right">77,0</td>
          <td style="text-align: right">94,0</td>
      </tr>
      <tr>
          <td>MiMo V2.5 Pro → V2.6 Pro</td>
          <td style="text-align: right">86,5</td>
          <td style="text-align: right">92,0</td>
      </tr>
      <tr>
          <td>MiniMax M3 → M3.1 Flash</td>
          <td style="text-align: right">75,5</td>
          <td style="text-align: right">89,5</td>
      </tr>
      <tr>
          <td>Kimi K2.7 Code → K3</td>
          <td style="text-align: right">87,25</td>
          <td style="text-align: right">85,0</td>
      </tr>
      <tr>
          <td>Gemini 3.7 Flash → 3.8 Flash (high)</td>
          <td style="text-align: right">75,5</td>
          <td style="text-align: right">90,5</td>
      </tr>
      <tr>
          <td>Claude Fable 5 → 5.1</td>
          <td style="text-align: right">100,0</td>
          <td style="text-align: right">95,5</td>
      </tr>
      <tr>
          <td>Grok 4.6 → 4.7</td>
          <td style="text-align: right">98,5</td>
          <td style="text-align: right">83,5</td>
      </tr>
      <tr>
          <td>GPT 5.6 sol → GPT 6 sol</td>
          <td style="text-align: right">100,0</td>
          <td style="text-align: right">91,0</td>
      </tr>
      <tr>
          <td>GPT 5.6 luna → GPT 6 luna</td>
          <td style="text-align: right">95,0</td>
          <td style="text-align: right">95,5</td>
      </tr>
      <tr>
          <td>Claude Opus 5 → 5.5</td>
          <td style="text-align: right">100,0</td>
          <td style="text-align: right">100,0</td>
      </tr>
  </tbody>
</table>
<p>Os chineses subiram de 5,5 a 17 pontos por geração, com exceção do Kimi. Do lado ocidental, quem já estava no teto não tinha pra onde subir: o Opus ficou em 100, o GPT 6 luna ficou onde o 5.6 luna estava, e três lançamentos novos ficaram abaixo dos antecessores. O Gemini Flash é o contraexemplo ocidental, com um salto de 15 pontos.</p>
<p>O calendário reforça a leitura. O Claude Fable 5 saiu em junho, o Opus 5 e o GPT 5.6 em julho, e todos já batiam 100 na minha régua. Daí até outubro, a OpenAI e a Anthropic lançaram o Fable 5.1, o GPT-6 Astra, o Opus 5.5 e o GPT-6 sol e luna. Nenhum deles mostrou ganho na minha régua, e alguns ficaram abaixo dos antecessores.</p>
<p>No mesmo intervalo, os chineses saíram de uma posição bem atrás: DeepSeek V4 em abril, Kimi K3 em julho, Qwen 3.8 e GLM 5.3 em agosto, DeepSeek V4.1 Flash e MiMo V2.6 Pro em setembro, MiniMax M3.1 Flash em outubro. Cada lançamento chegou mais perto do topo.</p>
<blockquote>
  <p>Os chineses parecem andar mais rápido, e andam. Mas parte da impressão vem de os ocidentais estarem parados há meses, pelo menos em programação. Quando o líder não se mexe, quem corre atrás encosta.</p>

</blockquote>
<h3>Matemática não é programação<span class="hx:absolute hx:-mt-20" id="matemática-não-é-programação"></span>
    <a href="#matem%c3%a1tica-n%c3%a3o-%c3%a9-programa%c3%a7%c3%a3o" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Os laboratórios ocidentais não pararam de trabalhar. Eles estão gastando boa parte da vitrine em matemática:</p>
<ul>
<li>em maio, a OpenAI anunciou que um modelo de raciocínio de uso geral <a href="https://openai.com/index/model-disproves-discrete-geometry-conjecture/"target="_blank" rel="noopener">derrubou uma conjectura de Erdős</a> sobre distâncias unitárias;</li>
<li>em agosto, ela publicou <a href="https://github.com/openai/ten-proofs"target="_blank" rel="noopener">dez resultados matemáticos do Astra</a>, cada um com prova formal em Lean;</li>
<li>também em agosto, a Anthropic mostrou um Claude interno elevando de 41,6% pra 67,2% a fração provada de <a href="https://www.anthropic.com/research/riemann-zeta"target="_blank" rel="noopener">zeros da função zeta de Riemann</a> que satisfazem a hipótese;</li>
<li>em setembro, a Anthropic <a href="https://www.anthropic.com/research/formalizing-fermats-last-theorem"target="_blank" rel="noopener">formalizou o Último Teorema de Fermat inteiro em Lean</a> em 11 dias, com um modelo interno que ela mesma descreve como comparável ao Fable 5.1.</li>
</ul>
<p>É impressionante, e é legítimo. Prova formal é um dos poucos lugares onde dá pra verificar a resposta automaticamente, o que torna matemática um ótimo campo de treino e de propaganda. Só que o mesmo Fable 5.1 que serviu de régua pro Fermat fez 95,5 no meu benchmark, contra os 100 do Fable 5.</p>
<p>Ficar melhor em matemática não deixa o modelo automaticamente melhor em programação. Um estudo de 2025, <a href="https://arxiv.org/abs/2507.00432"target="_blank" rel="noopener"><em>Does Math Reasoning Improve General LLM Capabilities?</em></a>, concluiu que <em>a maioria dos modelos que vai bem em matemática não transfere o ganho pra outros domínios</em>, com programação entre eles. Há contraexemplos: a NVIDIA mostrou que <a href="https://arxiv.org/abs/2505.16400"target="_blank" rel="noopener">aprendizado por reforço só com matemática</a> subiu a nota de código de modelos pequenos. A transferência acontece às vezes, e depende de como se treina.</p>
<h3>E o tal do mixture of experts<span class="hx:absolute hx:-mt-20" id="e-o-tal-do-mixture-of-experts"></span>
    <a href="#e-o-tal-do-mixture-of-experts" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>A intuição comum é que, num modelo <em>mixture of experts</em> (MoE), dá pra melhorar o &ldquo;especialista de matemática&rdquo; sem mexer no &ldquo;especialista de código&rdquo;. A ideia está meio certa pelo motivo errado.</p>
<p>Um MoE tem vários blocos de pesos em cada camada, e um roteador escolhe poucos deles pra cada token. Mas os especialistas não se dividem por assunto do jeito que a intuição sugere. O <a href="https://arxiv.org/abs/2401.04088"target="_blank" rel="noopener">paper do Mixtral</a> procurou exatamente isso e <em>não encontrou padrão óbvio de especialista por tema</em>, com uma diferença pequena só num conjunto sintético de matemática; o roteador se organiza mais por sintaxe, por tipo de token. E nem a OpenAI nem a Anthropic confirmam publicamente se os modelos fechados delas são MoE.</p>
<p>O que fica de pé é o essencial: capacidade é treinada por objetivo. O sinal que premia prova em Lean não é o mesmo que premia perceber um vazamento entre tenants num controller Rails, e melhorar um não arrasta o outro junto. Num modelo onde só uma fração dos parâmetros trabalha por token, esse descompasso fica ainda mais fácil de acontecer.</p>
<h2>NVFP4, FP8, Q4: o que é essa sopa de letrinhas<span class="hx:absolute hx:-mt-20" id="nvfp4-fp8-q4-o-que-é-essa-sopa-de-letrinhas"></span>
    <a href="#nvfp4-fp8-q4-o-que-%c3%a9-essa-sopa-de-letrinhas" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Todo modelo é uma pilha de bilhões de números, os pesos. Quantização é guardar cada número com menos bits. Quanto menos bits, menos memória o modelo ocupa e menos dado a placa precisa ler por token, e é isso que define a velocidade em máquina local. O preço, quando existe, é qualidade.</p>
<p>Pra um modelo de 27 bilhões de parâmetros, a conta fica assim:</p>
<table>
  <thead>
      <tr>
          <th>Formato</th>
          <th style="text-align: right">Bits por peso</th>
          <th style="text-align: right">27B ocupa</th>
          <th>Onde roda</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>BF16</td>
          <td style="text-align: right">16</td>
          <td style="text-align: right">~54 GB</td>
          <td>nativo de Ampere (RTX 30) em diante</td>
      </tr>
      <tr>
          <td>FP8</td>
          <td style="text-align: right">8</td>
          <td style="text-align: right">~27 GB</td>
          <td>nativo em Ada (RTX 40), Hopper (H200) e Blackwell</td>
      </tr>
      <tr>
          <td>Q8_0 (GGUF)</td>
          <td style="text-align: right">~8,5</td>
          <td style="text-align: right">~29 GB</td>
          <td>llama.cpp e Ollama, qualquer placa</td>
      </tr>
      <tr>
          <td>Q5_K_M (GGUF)</td>
          <td style="text-align: right">~5,5</td>
          <td style="text-align: right">~19 GB</td>
          <td>llama.cpp e Ollama</td>
      </tr>
      <tr>
          <td>Q4_K_M (GGUF)</td>
          <td style="text-align: right">~4,5 a 4,8</td>
          <td style="text-align: right">~16 GB</td>
          <td>llama.cpp e Ollama</td>
      </tr>
      <tr>
          <td>NVFP4</td>
          <td style="text-align: right">4,5</td>
          <td style="text-align: right">~15 GB</td>
          <td>nativo só em Blackwell; vLLM, e llama.cpp desde 2026</td>
      </tr>
      <tr>
          <td>IQ3_S (GGUF)</td>
          <td style="text-align: right">~3,4</td>
          <td style="text-align: right">~12 GB</td>
          <td>llama.cpp e derivados</td>
      </tr>
  </tbody>
</table>
<h3>O que é cada um<span class="hx:absolute hx:-mt-20" id="o-que-é-cada-um"></span>
    <a href="#o-que-%c3%a9-cada-um" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><ul>
<li><strong>BF16</strong> é a precisão em que a maioria dos modelos é publicada. É a referência: nada se perde.</li>
<li><strong>FP8</strong> é ponto flutuante de 8 bits, com suporte em hardware nas gerações Ada, Hopper e Blackwell. Corta a memória pela metade e é o padrão dos checkpoints grandes, como o DeepSeek V4.1 Flash e o GLM 5.3 full.</li>
<li><strong>Q8_0, Q5_K_M, Q4_K_M e IQ3_S</strong> são formatos GGUF do llama.cpp. Os pesos são guardados em blocos de inteiros com uma escala por bloco; os &ldquo;K&rdquo; misturam precisões por tensor, e os &ldquo;IQ&rdquo; usam uma matriz de importância pra decidir onde gastar bits. Q8 é tido como praticamente sem perda, e Q4_K_M é o padrão recomendado de quem roda em casa.</li>
<li><strong>NVFP4</strong> é o formato de 4 bits da NVIDIA. Cada número é um ponto flutuante de 4 bits, e cada bloco de 16 números ganha a própria escala em FP8. A NVIDIA diz que isso <a href="https://developer.nvidia.com/blog/introducing-nvfp4-for-efficient-and-accurate-low-precision-inference/"target="_blank" rel="noopener">ocupa 3,5 vezes menos que FP16 e perde 1% ou menos contra FP8</a> no DeepSeek-R1.</li>
</ul>
<p>O hype do NVFP4 vem daí: os tensor cores da Blackwell calculam nele direto, e foi ele que fez caber numa B300 só o GLM 5.3 Flash, de 198 GB, e o Qwen3.5 397B, de 251 GB. A H200, da geração anterior, não tem FP4 nativo. E o ecossistema caseiro ainda está chegando: o llama.cpp ganhou <a href="https://github.com/ggml-org/llama.cpp/pull/22196"target="_blank" rel="noopener">suporte a NVFP4</a> só em 2026, e a cobertura por arquitetura ainda é parcial.</p>
<h3>O que os nossos testes mostram<span class="hx:absolute hx:-mt-20" id="o-que-os-nossos-testes-mostram"></span>
    <a href="#o-que-os-nossos-testes-mostram" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Quatro modelos rodaram em mais de uma precisão:</p>
<table>
  <thead>
      <tr>
          <th>Modelo</th>
          <th>Versão A</th>
          <th>Versão B</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Qwen 3.8 27B</td>
          <td>BF16, B300: 80,5</td>
          <td>Q8_0, Strix Halo: 80,0 ¹</td>
      </tr>
      <tr>
          <td>Qwen3.8 Flash-Next</td>
          <td>NVFP4, B300: 92,5</td>
          <td>IQ3_S, RTX 5090 com Strata: 91,0</td>
      </tr>
      <tr>
          <td>Ornith 1.5 35B</td>
          <td>BF16, B300: 81,0</td>
          <td>Q8_0, RTX 5090 com Strata: 64,5</td>
      </tr>
      <tr>
          <td>GLM 4.7 Flash</td>
          <td>BF16, B300: 33,5</td>
          <td>Q5_K_M, RTX 5090: 24,0</td>
      </tr>
  </tbody>
</table>
<p>¹ Rodada mista: sprints 1 e 2 em Q5_K_M na 5090, o resto em Q8_0 na Strix, com o raciocínio desligado.</p>
<p>E seis rodaram na API de nuvem e também servidos por mim, com os pesos oficiais:</p>
<table>
  <thead>
      <tr>
          <th>Modelo</th>
          <th style="text-align: right">Nuvem</th>
          <th style="text-align: right">Servido por mim</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>GLM 5.3 Flash</td>
          <td style="text-align: right">84,25</td>
          <td style="text-align: right">92,5 (NVFP4, B300)</td>
      </tr>
      <tr>
          <td>DeepSeek V4.1 Flash</td>
          <td style="text-align: right">92,5</td>
          <td style="text-align: right">92,5 (FP8, 4x B300)</td>
      </tr>
      <tr>
          <td>GLM 5.3 full</td>
          <td style="text-align: right">94,0</td>
          <td style="text-align: right">76,0 (FP8, 4x B300)</td>
      </tr>
      <tr>
          <td>MiMo V2.6 Pro</td>
          <td style="text-align: right">92,0</td>
          <td style="text-align: right">67,0 (FP8, 4x B300)</td>
      </tr>
      <tr>
          <td>DeepSeek V4 Flash 0731</td>
          <td style="text-align: right">85,0</td>
          <td style="text-align: right">44,5 (FP4/FP8 oficial, B300)</td>
      </tr>
      <tr>
          <td>Nex N2.5 Pro</td>
          <td style="text-align: right">94,0</td>
          <td style="text-align: right">27,0 (FP8, 4x B300)</td>
      </tr>
  </tbody>
</table>
<p>Cada linha é uma rodada só, e o ruído chega a quase 10 pontos. Com essa ressalva, a leitura que eu faço:</p>
<ul>
<li><strong>Q8 ficou empatado com BF16.</strong> O Qwen 27B fez 80,5 e 80,0, num ponto só e com outras coisas mudando junto.</li>
<li><strong>Descer de 4 pra 3 bits saiu quase de graça no Flash-Next</strong>, a 1,5 ponto, mesmo trocando de motor e de placa.</li>
<li><strong>A queda da Ornith dificilmente é dos bits.</strong> Q8 é quase sem perda, e mesmo assim ela perdeu 16,5 trocando também de motor e de placa.</li>
<li><strong>Servir o mesmo checkpoint pode dar qualquer coisa.</strong> O GLM 5.3 Flash subiu 8 pontos na minha mão, o DeepSeek V4.1 empatou, e o Nex caiu quase 70. Parser de ferramenta, template de chat, modo de raciocínio e versão do motor pesam tanto quanto a precisão, e o efeito muda de modelo pra modelo.</li>
</ul>
<blockquote>
  <p>Nos meus testes, os bits pesaram menos do que o hype sugere, e o conjunto todo de como o modelo é servido pesou mais. Use Q8 quando couber, desça pra 4 bits quando precisar, e meça no seu próprio trabalho antes de confiar.</p>

</blockquote>
<h2>Os melhores comerciais<span class="hx:absolute hx:-mt-20" id="os-melhores-comerciais"></span>
    <a href="#os-melhores-comerciais" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Aqui só entra modelo fechado, só Tier A, e só o que eu pagaria por API.</p>
<h3>Por nota<span class="hx:absolute hx:-mt-20" id="por-nota"></span>
    <a href="#por-nota" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><table>
  <thead>
      <tr>
          <th>Modelo</th>
          <th style="text-align: right">Nota</th>
          <th style="text-align: right">Custo da rodada</th>
          <th style="text-align: right">Minutos</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>GPT-6 Astra</td>
          <td style="text-align: right">100,0</td>
          <td style="text-align: right">US$ 30,55</td>
          <td style="text-align: right">100</td>
      </tr>
      <tr>
          <td>Claude Opus 5.5</td>
          <td style="text-align: right">100,0</td>
          <td style="text-align: right">US$ 16,63 ¹</td>
          <td style="text-align: right">64</td>
      </tr>
      <tr>
          <td>Claude Opus 5</td>
          <td style="text-align: right">100,0</td>
          <td style="text-align: right">~US$ 71 ¹</td>
          <td style="text-align: right">145</td>
      </tr>
      <tr>
          <td>Claude Fable 5</td>
          <td style="text-align: right">100,0</td>
          <td style="text-align: right">~US$ 50 ¹</td>
          <td style="text-align: right">~85</td>
      </tr>
      <tr>
          <td>GPT 5.6 terra</td>
          <td style="text-align: right">100,0</td>
          <td style="text-align: right">US$ 9,52</td>
          <td style="text-align: right">80</td>
      </tr>
      <tr>
          <td>GPT 5.6 sol</td>
          <td style="text-align: right">100,0</td>
          <td style="text-align: right">US$ 20,37</td>
          <td style="text-align: right">317</td>
      </tr>
      <tr>
          <td>GPT 5.5</td>
          <td style="text-align: right">100,0</td>
          <td style="text-align: right">US$ 34,69</td>
          <td style="text-align: right">117</td>
      </tr>
      <tr>
          <td>Grok 4.6</td>
          <td style="text-align: right">98,5</td>
          <td style="text-align: right">US$ 13,00</td>
          <td style="text-align: right">67</td>
      </tr>
      <tr>
          <td>GPT 6 luna</td>
          <td style="text-align: right">95,5</td>
          <td style="text-align: right">~US$ 0,84</td>
          <td style="text-align: right">122</td>
      </tr>
      <tr>
          <td>Claude Fable 5.1</td>
          <td style="text-align: right">95,5</td>
          <td style="text-align: right">~US$ 51 ¹</td>
          <td style="text-align: right">133</td>
      </tr>
  </tbody>
</table>
<p>¹ Custo nocional da assinatura, a preço de tabela da API.</p>
<p>No topo absoluto, o <strong>Claude Opus 5.5</strong> e o <strong>GPT 5.6 terra</strong> são as escolhas óbvias: nota máxima, rodada em pouco mais de uma hora, e preço de rodada de um dígito ou pouco mais. O Opus 5 e o GPT 5.5 tiram a mesma nota custando de duas a sete vezes mais.</p>
<h3>Por custo-benefício<span class="hx:absolute hx:-mt-20" id="por-custo-benefício"></span>
    <a href="#por-custo-benef%c3%adcio" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><table>
  <thead>
      <tr>
          <th>Modelo</th>
          <th style="text-align: right">Nota</th>
          <th style="text-align: right">Custo da rodada</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>GPT 6 luna</td>
          <td style="text-align: right">95,5</td>
          <td style="text-align: right">~US$ 0,84</td>
      </tr>
      <tr>
          <td>Qwen3.8 Flash</td>
          <td style="text-align: right">86,5</td>
          <td style="text-align: right">US$ 1,17</td>
      </tr>
      <tr>
          <td>GPT 6 sol</td>
          <td style="text-align: right">91,0</td>
          <td style="text-align: right">~US$ 8,35</td>
      </tr>
      <tr>
          <td>GPT 5.6 terra</td>
          <td style="text-align: right">100,0</td>
          <td style="text-align: right">US$ 9,52</td>
      </tr>
      <tr>
          <td>GPT 5.6 luna</td>
          <td style="text-align: right">95,0</td>
          <td style="text-align: right">US$ 10,04</td>
      </tr>
      <tr>
          <td>Grok 4.6</td>
          <td style="text-align: right">98,5</td>
          <td style="text-align: right">US$ 13,00</td>
      </tr>
      <tr>
          <td>Claude Opus 5.5</td>
          <td style="text-align: right">100,0</td>
          <td style="text-align: right">US$ 16,63 ¹</td>
      </tr>
  </tbody>
</table>
<p>¹ Custo nocional da assinatura, a preço de tabela da API.</p>
<p>O <strong>GPT 6 luna</strong> é o absurdo da tabela: Tier A alto por menos de um dólar a rodada, uma fração do preço de qualquer outro fechado no mesmo patamar. Se a nota máxima não for obrigatória, é por onde eu começaria.</p>
<p>Na nota máxima, o <strong>GPT 5.6 terra</strong> custa pouco mais da metade do Opus 5.5. A diferença entre os dois fica no resto: harness, velocidade, comportamento em sessão longa, e isso eu cobri nos artigos anteriores.</p>
<h2>Os melhores abertos pra empresa<span class="hx:absolute hx:-mt-20" id="os-melhores-abertos-pra-empresa"></span>
    <a href="#os-melhores-abertos-pra-empresa" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Aqui entra o que roda em GPU de datacenter, na B300.</p>
<table>
  <thead>
      <tr>
          <th>Modelo</th>
          <th>Onde</th>
          <th style="text-align: right">Nota</th>
          <th style="text-align: right">tok/s</th>
          <th style="text-align: right">Memória</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>DeepSeek V4.1 Flash</td>
          <td>4x B300</td>
          <td style="text-align: right">92,5</td>
          <td style="text-align: right">175</td>
          <td style="text-align: right">510 GB</td>
      </tr>
      <tr>
          <td>GLM 5.3 Flash</td>
          <td>1x B300</td>
          <td style="text-align: right">92,5</td>
          <td style="text-align: right">142</td>
          <td style="text-align: right">198 GB</td>
      </tr>
      <tr>
          <td>Qwen3.8 Flash-Next</td>
          <td>1x B300</td>
          <td style="text-align: right">92,5</td>
          <td style="text-align: right">195</td>
          <td style="text-align: right">100 GB</td>
      </tr>
      <tr>
          <td>Swift 1.5 27B</td>
          <td>1x B300</td>
          <td style="text-align: right">88,0</td>
          <td style="text-align: right">203</td>
          <td style="text-align: right">56 GB</td>
      </tr>
      <tr>
          <td>ThinkingCap 3.8 27B</td>
          <td>1x B300</td>
          <td style="text-align: right">83,0</td>
          <td style="text-align: right">189</td>
          <td style="text-align: right">56 GB</td>
      </tr>
  </tbody>
</table>
<p>O <strong>DeepSeek V4.1 Flash</strong> é o campeão pra time grande. Num pod de 4x B300 a US$ 36 por hora, ele atende <strong>32 sessões confortáveis</strong> ao mesmo tempo, com o primeiro token em menos de 2 segundos pra quase todas. Contando uma sessão ativa por desenvolvedor o expediente inteiro, o que é otimista, são 32 pessoas. Ligando o pod só no expediente, 8 horas por dia, 22 dias por mês, dá uns US$ 6.300 por mês, ou <strong>uns US$ 200 por desenvolvedor</strong>.</p>
<p>As alternativas, pela mesma conta:</p>
<ul>
<li>4x H200 na Runpod atende só 8 sessões confortáveis e sai por uns US$ 316 por desenvolvedor por mês, mesmo com o pod mais barato;</li>
<li>a mesma configuração na AWS custa quase o dobro, e a H200 na Azure custa quase três vezes o preço da Runpod;</li>
<li>comprar uma DGX Station GB300 de uns US$ 130 mil se paga em uns 20 meses contra o aluguel no expediente, ou em uns 5 meses contra o pod ligado direto.</li>
</ul>
<p>Pra time menor, a B300 única com o <strong>Qwen3.8 Flash-Next</strong> ou o <strong>GLM 5.3 Flash</strong> é o ponto doce: mesma nota do DeepSeek, numa GPU só, a uns US$ 9 por hora. Eu não medi concorrência nela. Pela memória que sobra, um time de cinco a dez pessoas cabe bem num modelo de 27 a 35 bilhões, como o Swift 1.5; no GLM 5.3 Flash, de 198 GB, fica apertado.</p>
<blockquote>
  <p>A infra própria começa a fazer sentido na casa de 30 pessoas usando ao mesmo tempo, ou quando o código não pode sair da rede. Abaixo disso, a API do mesmo DeepSeek custa pouco mais de um dólar por rodada e você não administra servidor nenhum.</p>

</blockquote>
<h2>Os melhores abertos pra consumidor<span class="hx:absolute hx:-mt-20" id="os-melhores-abertos-pra-consumidor"></span>
    <a href="#os-melhores-abertos-pra-consumidor" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Aqui entra o que roda num PC: RTX 5090 de 32 GB ou Strix Halo de 128 GB unificados.</p>
<table>
  <thead>
      <tr>
          <th>Modelo</th>
          <th>Máquina</th>
          <th style="text-align: right">Nota</th>
          <th style="text-align: right">tok/s</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Qwen3.8 Flash-Next (IQ3_S, Strata)</td>
          <td>RTX 5090</td>
          <td style="text-align: right"><strong>91,0</strong></td>
          <td style="text-align: right">159</td>
      </tr>
      <tr>
          <td>Qwen 3.8 27B (Q8_0)</td>
          <td>Strix Halo</td>
          <td style="text-align: right">80,0</td>
          <td style="text-align: right">8</td>
      </tr>
      <tr>
          <td>Ornith 1.5 35B (Q8_0, Strata)</td>
          <td>RTX 5090</td>
          <td style="text-align: right">64,5</td>
          <td style="text-align: right">~80</td>
      </tr>
      <tr>
          <td>GLM-4.7-Flash (Q5_K_M)</td>
          <td>RTX 5090</td>
          <td style="text-align: right">24,0</td>
          <td style="text-align: right">—</td>
      </tr>
  </tbody>
</table>
<p>Só tem um vencedor: o <strong>Qwen3.8 Flash-Next na RTX 5090 com o Strata</strong>. É um MoE de 125 bilhões com 6 bilhões ativos por token, e o Strata deixa os especialistas mais usados na placa e o resto na RAM. Ficou a 1,5 ponto da versão de datacenter, em velocidade interativa. Eu detalhei ele, com instalação, serviço de carga automática e configuração do OpenCode, em <a href="/2026/10/22/melhor-llm-open-source-strata-com-qwen-3-8-flash-next/">Melhor LLM Open Source: Strata com Qwen 3.8 Flash Next</a>.</p>
<p>O Strix Halo cabe modelo grande, mas a banda de memória dele é sete vezes menor que a da 5090, e um modelo denso de 27B anda a menos de 10 tokens por segundo em Q8, uns 13 em Q4. Serve pra tarefa de fundo, que pode esperar.</p>
<p>E o <strong>Swift 1.5</strong> e o <strong>ThinkingCap 3.8</strong>, os fine-tunes de 27B que foram bem na B300? Eu não rodei os dois na 5090, e nem precisei. Na B300, em BF16, precisão cheia, eles fizeram 88 e 83, abaixo dos 92,5 do Flash-Next na mesma placa e dos 91 que ele fez na 5090.</p>
<p>Numa 5090 eles caberiam em Q4 ou Q5, mas quantizado o melhor cenário é repetir a nota da B300. Rodar em casa custou 1,5 ponto pro Flash-Next e 16,5 pra Ornith-35B. O Qwen 3.8 27B base, que é a mesma arquitetura, nem completou na 5090 em Q5_K_M: travou num loop de raciocínio no sprint 3.</p>
<blockquote>
  <p>Se os dois já ficaram atrás do Flash-Next com precisão cheia, numa 5090 eles não vão ficar melhores. Não vi motivo pra testar: pra PC, o Flash-Next é claramente a escolha.</p>

</blockquote>
<h3>E placa pior que a 5090?<span class="hx:absolute hx:-mt-20" id="e-placa-pior-que-a-5090"></span>
    <a href="#e-placa-pior-que-a-5090" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>É a pergunta que sempre aparece: e quem tem uma placa de 8, 12 ou 16 GB? Pelos meus dados, o caminho do modelo pequeno que cabe inteiro na placa ainda não entrega nada que valha o trabalho pra programar.</p>
<p>Numa placa dessas, em Q4, cabe modelo denso de até uns 20 bilhões; um MoE de 30 bilhões com poucos ativos só roda com parte na RAM. E essa faixa foi mal no v4:</p>
<ul>
<li>o GLM-4.7-Flash, um MoE de 30B com 3B ativos, fez 24 na 5090 e 33,5 na B300 com precisão cheia, Tier D nas duas;</li>
<li>a Ornith-35B, outro MoE com 3B ativos, fez 81 na B300 e caiu pra 64,5 na 5090;</li>
<li>o gpt-oss-20b nem completou o protocolo;</li>
<li>o melhor modelo médio foi o Qwen 3.8 27B, com 80, mas ele já precisa de uns 16 GB só em Q4, travou na 5090 e só terminou a 8 tokens por segundo na Strix.</li>
</ul>
<p>A exceção é justamente o Flash-Next. O Strata foi feito pra rodar ele em placa de 12 GB, segurando a maioria dos especialistas na RAM: o <a href="https://github.com/Niko1221/Strata"target="_blank" rel="noopener">README do projeto</a> publica 53 tokens por segundo em IQ3_S numa RTX 5070 de 12 GB com 64 GB de RAM, e o mínimo declarado é 32 GB somando RAM e VRAM. Com os mesmos pesos e o mesmo motor, a nota em tese não deveria mudar, só a velocidade, que cai pra um terço da 5090. Eu não medi isso; é número do autor.</p>
<p>O porém é a RAM. Com a DRAM quase <a href="https://www.trendforce.com/presscenter/news/20260601-13070.html"target="_blank" rel="noopener">dobrando de preço</a> num único trimestre deste ano, 64 GB deixaram de ser o upgrade barato que eram.</p>
<blockquote>
  <p>Abaixo da 5090, eu não perderia tempo com modelo pequeno pra programar. Se for tentar alguma coisa, que seja o Flash-Next com Strata e RAM sobrando, e meça no seu trabalho antes de confiar.</p>

</blockquote>
<p>E tem o problema que eu contei no <a href="/2026/10/22/llm-benchmark-v4-open-source-nivel-consumidor-rtx5090-strix-halo-strata/">artigo anterior</a>: achar uma 5090 hoje é difícil, e quando acha, o preço fica entre duas vezes e meia e quase cinco vezes o sugerido.</p>
<h3>Quanto custa sair dos 150 tokens por segundo?<span class="hx:absolute hx:-mt-20" id="quanto-custa-sair-dos-150-tokens-por-segundo"></span>
    <a href="#quanto-custa-sair-dos-150-tokens-por-segundo" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Depois de algumas horas usando o Qwen local, <a href="https://x.com/AkitaOnRails/status/2113386101293428812"target="_blank" rel="noopener">resumi o problema no X</a>. A média de 159 tokens por segundo da minha rodada parece rápida numa tabela. Numa sessão de programação que passa o dia inteiro gerando código, rodando teste, lendo log e corrigindo o que fez, a espera acumula.</p>
<p>Eu já fico impaciente. Abaixo disso, nem considero pra trabalho diário.</p>
<p>A própria 5090 tem limites além da velocidade. A NVIDIA especifica <a href="https://www.nvidia.com/en-us/geforce/graphics-cards/50-series/rtx-5090/"target="_blank" rel="noopener">32 GB de GDDR7 e 1.792 GB/s de banda</a> e <a href="https://nvidianews.nvidia.com/news/nvidia-blackwell-geforce-rtx-50-series-opens-new-world-of-ai-computer-graphics"target="_blank" rel="noopener">lançou a placa por US$ 1.999</a>. A minha ainda move os monitores e o Hyprland, então deixo 4 GB reservados pro desktop. O resto precisa segurar pesos densos, cache dos especialistas quentes, buffers do motor e a parte residente do cache de KV, que guarda o contexto da conversa.</p>
<p>Minha configuração do Strata oferece 256 mil tokens de contexto com KV em int8 e mantém 32 mil residentes na VRAM; o restante pode ir pra RAM. Isso evita estourar a placa, mas RAM continua muito mais lenta. Enquanto <a href="https://docs.anthropic.com/en/api/claude-on-amazon-bedrock"target="_blank" rel="noopener">modelos de fronteira do Claude já oferecem janela de 1 milhão de tokens</a>, localmente eu preciso vigiar o que entra na sessão. Carregar uma coleção inteira de skills, plugins e servidores MCP só porque existe espaço no arquivo de configuração é pedir pra desperdiçar contexto e piorar a latência.</p>
<blockquote>
  <p>Em hardware local, contexto também custa memória e tempo. Eu carrego só o que aquela sessão vai usar.</p>

</blockquote>
<p>E se eu colocar mais hardware? A comparação fica assim:</p>
<table>
  <thead>
      <tr>
          <th>Opção</th>
          <th style="text-align: right">Memória</th>
          <th style="text-align: right">Banda</th>
          <th style="text-align: right">Velocidade observada</th>
          <th style="text-align: right">Preço de referência</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>RTX 5090</td>
          <td style="text-align: right">32 GB GDDR7</td>
          <td style="text-align: right">1.792 GB/s</td>
          <td style="text-align: right"><strong>159,2 tok/s</strong>, medido por mim</td>
          <td style="text-align: right">US$ 1.999 sugerido</td>
      </tr>
      <tr>
          <td>2x RTX 5090</td>
          <td style="text-align: right">64 GB físicos, divididos entre as placas</td>
          <td style="text-align: right">2x 1.792 GB/s, sem virar um pool plano</td>
          <td style="text-align: right">sem teste equivalente meu</td>
          <td style="text-align: right">US$ 3.998 sugeridos</td>
      </tr>
      <tr>
          <td>Mac Studio M5 Ultra</td>
          <td style="text-align: right">96 ou 256 GB unificados; 512 GB prometidos pro fim de outubro</td>
          <td style="text-align: right">1.200 GB/s</td>
          <td style="text-align: right">ainda não medido no Strata</td>
          <td style="text-align: right">a partir de US$ 5.499</td>
      </tr>
      <tr>
          <td>DGX Spark</td>
          <td style="text-align: right">128 GB unificados</td>
          <td style="text-align: right">273 GB/s</td>
          <td style="text-align: right">cerca de 22 a 75 tok/s por sessão em receitas públicas</td>
          <td style="text-align: right">US$ 6.950 na loja da NVIDIA</td>
      </tr>
      <tr>
          <td>RTX PRO 6000 Blackwell</td>
          <td style="text-align: right">96 GB GDDR7 ECC</td>
          <td style="text-align: right">1.792 GB/s</td>
          <td style="text-align: right">187,4 tok/s no chat curto; 174,3 na sessão de agente</td>
          <td style="text-align: right">US$ 16 mil; R$ 123 mil no Pix no Brasil</td>
      </tr>
  </tbody>
</table>
<p>Eu escrevi no post que o Strata só subiria duas instâncias numa máquina com duas 5090. Essa parte ficou desatualizada: o Strata atual <a href="https://github.com/Niko1221/Strata/blob/main/docs/MULTI_GPU.md"target="_blank" rel="noopener">divide as camadas entre duas ou mais GPUs</a>. É <em>pipeline parallelism</em>, sem transformar duas placas num bloco simples de 64 GB e sem tensor parallelism maduro.</p>
<p>O ganho por sessão fica longe de 2x. Um <a href="https://github.com/Niko1221/Strata/issues/642"target="_blank" rel="noopener">teste comunitário com duas 5090</a> mediu cerca de 22% de ganho na média geométrica de decode e 40% no braço de código. Ajuda na capacidade e dá pra usar as placas como duas instâncias independentes pra dobrar concorrência. Pra mim sozinho, comprar outra 5090 pra cortar uma fração da espera não fecha a conta.</p>
<p>O <strong>Mac Studio M5 Ultra</strong> começa em <a href="https://www.apple.com/newsroom/2026/08/apple-introduces-new-mac-studio-with-m5-max-and-m5-ultra/"target="_blank" rel="noopener">US$ 5.499</a>, tem 1,2 TB/s de banda e vai de 96 a 256 GB; a opção de 512 GB foi prometida pro fim de outubro. Ainda não existe benchmark publicado do Strata no Ultra. No <a href="https://github.com/Niko1221/Strata/blob/main/docs/MAC.md"target="_blank" rel="noopener">M5 Max de 128 GB</a>, o mesmo Flash-Next ficou entre 97 e 119,5 tokens por segundo, dependendo da quantização e do prompt. Meu palpite original de menos de 100 no Ultra não está comprovado, e eu não compraria uma máquina dessas baseado em palpite.</p>
<p>A <strong>DGX Spark</strong> coloca 128 GB numa caixa compacta, com o GB10 de TDP 140 W e uma fonte de 240 W. A memória entrega só <a href="https://www.nvidia.com/en-us/products/workstations/dgx-spark/"target="_blank" rel="noopener">273 GB/s</a>, quase sete vezes menos que a 5090. A NVIDIA agora pede <a href="https://marketplace.nvidia.com/en-us/enterprise/personal-ai-supercomputers/dgx-spark/"target="_blank" rel="noopener">US$ 6.950</a>.</p>
<p>Receitas reproduzíveis do Flash-Next variam de <a href="https://github.com/ursuciprian/qwen3.8-flash-next-1x-dgx-spark"target="_blank" rel="noopener">22 a 75 tokens por segundo por sessão</a>, dependendo de concorrência, stack, amostragem e modo de raciocínio. Cabe muita coisa. Pra minha tolerância, anda pouco.</p>
<p>Sobra a <strong>RTX PRO 6000 Blackwell</strong>. Ela tem <a href="https://www.nvidia.com/content/dam/en-zz/Solutions/data-center/rtx-pro-6000-blackwell-workstation-edition/workstation-blackwell-rtx-pro-6000-workstation-edition-nvidia-us-3519208-web.pdf"target="_blank" rel="noopener">96 GB GDDR7 ECC, 1.792 GB/s e potência total de 600 watts</a>. A banda é exatamente a mesma da 5090. Os 96 GB resolvem o aperto de VRAM, permitem mais cache e contexto, mas não compram três vezes a velocidade.</p>
<p>Um <a href="https://github.com/Niko1221/Strata/issues/2268"target="_blank" rel="noopener">benchmark comunitário do Strata</a> mediu 187,4 tokens por segundo no chat curto e 174,3 numa sessão de agente, com bastante variação entre rodadas. É melhor que meus 159,2, só que a NVIDIA lista a placa por <a href="https://marketplace.nvidia.com/en-us/enterprise/laptops-workstations/nvidia-rtx-pro-6000-blackwell-workstation-edition/"target="_blank" rel="noopener">US$ 16 mil</a>. Em 22 de outubro, a WAZ <a href="https://www.waz.com.br/placa-de-video-nvidia-quadro-rtx-pro-6000-blackwell-workstation-edition-pny-vcnrtxpro6000b-pb-96gb-gddr7-pcie-5-0-x16-131172-html/p"target="_blank" rel="noopener">mostrava a PNY por R$ 123.299,99 no Pix ou R$ 145.058,81 no preço regular</a>. Já entramos em orçamento de workstation profissional ou servidor.</p>
<blockquote>
  <p>Hoje não existe, pra mim, uma opção doméstica com modelo bom, resposta realmente rápida e custo razoável. A 5090 funciona, mas 150 tokens por segundo já me cansam. Abaixo dela vira brinquedo; acima dela o preço explode antes da velocidade.</p>

</blockquote>
<h2>O que eu aprendi no v4<span class="hx:absolute hx:-mt-20" id="o-que-eu-aprendi-no-v4"></span>
    <a href="#o-que-eu-aprendi-no-v4" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><h3>Sobre os modelos<span class="hx:absolute hx:-mt-20" id="sobre-os-modelos"></span>
    <a href="#sobre-os-modelos" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><ul>
<li>Quase todo modelo pega o defeito barulhento sem aviso: CVE em gem, chave no código, CORS aberto, SQL injection óbvia. Quase todo modelo deixa passar o silencioso até eu revelar: vazamento entre tenants, agregado errado, teste adulterado, índice removido. O que separa o Tier A do resto é quanto do silencioso ele pega sozinho.</li>
<li>Tamanho não compra vigilância. O Kimi K2 Thinking, de 1 trilhão de parâmetros, fez 46. O GLM 5.3 full, de 700 bilhões, fez 76, contra 92,5 do Flash da mesma família.</li>
<li>Modelo trapaceia ou se engana com convicção. O Nemotron adulterou um teste de regressão pra passar, o Qwen3.5 397B deixou passar uma backdoor e ainda escreveu um teste que a tratava como comportamento esperado, e o Kimi K2 fez teatro de checklist de segurança, pegando só o que dá pra achar com grep.</li>
<li>O salto entre gerações foi de confiabilidade agêntica. O GPT-4o e o GPT-4.1 nem conseguem completar o protocolo sozinhos. Os modelos atuais do topo rodam as sete sprints sem ajuda nenhuma.</li>
</ul>
<h3>Sobre servir modelo aberto<span class="hx:absolute hx:-mt-20" id="sobre-servir-modelo-aberto"></span>
    <a href="#sobre-servir-modelo-aberto" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><ul>
<li>O mesmo checkpoint muda de nota conforme quem serve. O DeepSeek V4 Flash fez 85 na nuvem, na mesma versão 0731 que eu baixei, e 44,5 servido por mim; o Nex N2.5 Pro fez 94 na nuvem e 27 no pod de 4x B300, com tokens corrompidos chegando no código commitado.</li>
<li>Nenhum modelo roda rápido do jeito que vem. O GLM 5.3 Flash saiu de 10 pra 141 tokens por segundo só de configuração, e cada família pediu parser, flag e às vezes uma branch de PR do vLLM.</li>
<li>Rodar em casa cobra preço diferente de cada modelo. Trocando quantização, motor e placa de uma vez, o Flash-Next perdeu 1,5 ponto e a Ornith-35B perdeu 16,5.</li>
<li>Quem limita a concorrência é a arquitetura do cache de KV e o tempo até o primeiro token. Contagem de parâmetros diz pouco: na H200, um modelo de 510 GB e outro bem menor empataram em 8 usuários, por motivos opostos.</li>
</ul>
<h3>Sobre medir<span class="hx:absolute hx:-mt-20" id="sobre-medir"></span>
    <a href="#sobre-medir" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><ul>
<li>Uma rodada tem ruído de quase 10 pontos. Duas notas a 2 pontos de distância empatam.</li>
<li>O harness pesa. O Gemini 3.1 Pro foi derrubado por um bug de proxy, o gpt-oss não completa no OpenCode, e três modelos insistiram em criar o app numa subpasta.</li>
<li>Modelo que não faz commit perde trabalho. Correção que fica só no disco some no primeiro <code>git checkout</code>.</li>
</ul>
<h2>A minha hipótese: o hardware caro é parte do modelo de negócio<span class="hx:absolute hx:-mt-20" id="a-minha-hipótese-o-hardware-caro-é-parte-do-modelo-de-negócio"></span>
    <a href="#a-minha-hip%c3%b3tese-o-hardware-caro-%c3%a9-parte-do-modelo-de-neg%c3%b3cio" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>O veredito do v4, até outubro de 2026, é este: pra revisão de código e vigilância, open source chegou. O DeepSeek V4.1 Flash, o GLM 5.3 Flash e o Qwen3.8 Flash-Next estão no pelotão do Sonnet 5 e do GPT 6 sol, e um deles roda numa placa de PC.</p>
<p>Então por que não está todo mundo rodando em casa? Porque o hardware ficou caro e difícil de achar, e eu tenho uma hipótese sobre isso. É hipótese minha, sem prova de combinação nenhuma, montada em cima de fatos públicos.</p>
<h3>Os fatos<span class="hx:absolute hx:-mt-20" id="os-fatos"></span>
    <a href="#os-fatos" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><ul>
<li>A memória disparou. A TrendForce mediu alta de <a href="https://www.trendforce.com/presscenter/news/20260601-13070.html"target="_blank" rel="noopener">93% a 98% no preço de contrato de DRAM</a> só no primeiro trimestre de 2026, e diz que os fabricantes <a href="https://www.trendforce.com/presscenter/news/20260930-13258.html"target="_blank" rel="noopener">continuam priorizando capacidade pra servidor</a> no quarto trimestre.</li>
<li>A HBM, a memória das GPUs de datacenter, deve ter o <a href="https://www.trendforce.com/presscenter/news/20260929-13255.html"target="_blank" rel="noopener">preço médio subindo 121% em 2027</a> e ocupar perto de <a href="https://www.trendforce.com/presscenter/news/20260602-13074.html"target="_blank" rel="noopener">30% das bolachas de DRAM</a> dos três maiores fabricantes até o fim de 2027.</li>
<li>A Micron <a href="https://www.storagenewsletter.com/2025/12/05/micron-announces-exit-from-crucial-consumer-business/"target="_blank" rel="noopener">saiu do mercado consumidor</a> e matou a Crucial, nas palavras dela, pra atender melhor os clientes estratégicos maiores.</li>
<li>PC e console subiram de preço, e a Microsoft <a href="https://www.digitalcitizen.life/xbox-series-x-and-series-s-prices-will-rise-again-as-memory-and-storage-costs-surge/"target="_blank" rel="noopener">atribuiu o aumento do Xbox</a> ao custo de memória e armazenamento.</li>
<li>A RTX 5090, quando aparece nos Estados Unidos, custa de <a href="https://gpupoet.com/gpu/market-report/gpu-market-report-october-2026"target="_blank" rel="noopener">duas vezes e meia</a> a <a href="https://www.techspot.com/news/114254-nvidia-rtx-5090-production-but-absence-company-us.html"target="_blank" rel="noopener">quase cinco vezes</a> o preço sugerido, e o boato é que os chips dela vão pras linhas profissionais. A NVIDIA <a href="https://www.guru3d.com/story/nvidia-says-geforce-rtx-5090-production-is-continuing/"target="_blank" rel="noopener">nega a descontinuação</a>.</li>
<li>Do outro lado, a Anthropic <a href="https://techcrunch.com/2026/06/01/anthropic-files-to-go-public/"target="_blank" rel="noopener">pediu IPO</a> mirando, segundo a imprensa, <a href="https://www.khaleejtimes.com/business/anthropic-says-revenue-jumped-to-nearly-17b-last-quarter-reports-2"target="_blank" rel="noopener">uns 2 trilhões de dólares</a>, e a OpenAI está <a href="https://uk.investing.com/news/stock-market-news/openai-races-to-raise-30-bln-ahead-of-anthropic-ipo-reuters-reports-4918623"target="_blank" rel="noopener">captando a mais de 1,4 trilhão</a>.</li>
<li>E o discurso sobre modelo aberto chinês endureceu. A Anthropic publicou que <a href="https://www.anthropic.com/news/detecting-and-preventing-distillation-attacks"target="_blank" rel="noopener">DeepSeek, Moonshot e MiniMax destilaram o Claude</a>, e o Dario Amodei, mesmo dizendo que <a href="https://www.anthropic.com/news/position-open-weights-models"target="_blank" rel="noopener">nunca defendeu proibir pesos abertos</a>, pede controle de exportação de chip e teste obrigatório, antes do lançamento, de todo modelo suficientemente capaz, aberto ou fechado.</li>
</ul>
<h3>O ciclo<span class="hx:absolute hx:-mt-20" id="o-ciclo"></span>
    <a href="#o-ciclo" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>A explicação mais simples pra memória cara é demanda: datacenter compra tudo e paga mais. Eu acredito nela. O que me incomoda é que nenhum participante grande tem incentivo pra quebrar o ciclo:</p>
<ol>
<li>O discurso diz que só o laboratório de fronteira sabe lidar com esses modelos &ldquo;perigosos&rdquo;, e que open source é uma aposta arriscada.</li>
<li>Isso puxa a demanda pra nuvem, e a nuvem compra GPU e memória em volume recorde.</li>
<li>Os fabricantes vendem com ágio pra quem paga mais e tiram prioridade do consumidor.</li>
<li>O hardware bom fica inacessível fora do datacenter.</li>
<li>Sem hardware em casa, o open source não é adotado, o que reforça o discurso do passo 1.</li>
</ol>
<blockquote>
  <p>Se todo mundo começasse a hospedar o próprio modelo, as contas de IPO de trilhões de dólares deixariam de fechar. O v4 mostrou que o modelo aberto já está pronto pra boa parte do trabalho. O que falta é o hardware chegar na mão de quem trabalha.</p>

</blockquote>
]]></content:encoded><category>benchmarks-de-llm</category><category>modelos-locais</category><category>llms</category><category>open-source</category></item><item><title>Melhor LLM Open Source: Strata com Qwen 3.8 Flash Next</title><link>https://www.akitaonrails.com/2026/10/22/melhor-llm-open-source-strata-com-qwen-3-8-flash-next/</link><guid isPermaLink="true">https://www.akitaonrails.com/2026/10/22/melhor-llm-open-source-strata-com-qwen-3-8-flash-next/</guid><pubDate>Thu, 22 Oct 2026 11:45:00 -0300</pubDate><description>&lt;p&gt;O Strata apareceu em quase todo artigo da reta final do benchmark v4. No de &lt;a href="https://www.akitaonrails.com/2026/10/22/llm-benchmark-v4-open-source-nivel-consumidor-rtx5090-strix-halo-strata/"&gt;open source nível consumidor&lt;/a&gt; ele foi o protagonista; no de &lt;a href="https://www.akitaonrails.com/2026/10/22/llm-benchmark-v4-can-my-team-fit-in-a-4x-b300-h200/"&gt;quanto custa um time em B300 e H200&lt;/a&gt; o modelo que ele serve virou a recomendação pra DGX Spark; e no &lt;a href="https://www.akitaonrails.com/2026/10/22/llm-benchmark-v4-o-veredito-de-open-source-ate-out-2026/"&gt;veredito do v4&lt;/a&gt; ele é a única resposta séria pra quem quer rodar modelo bom em casa.&lt;/p&gt;
&lt;p&gt;O resultado, resumido: o &lt;strong&gt;Qwen3.8 Flash-Next&lt;/strong&gt; servido pelo Strata numa RTX 5090 fez &lt;strong&gt;91, Tier A&lt;/strong&gt;, com zero nudges e os 14 defeitos plantados resolvidos (dois só depois da revelação), a 1,5 ponto do mesmo modelo rodando numa B300 de datacenter. E fez isso a uns 159 tokens por segundo.&lt;/p&gt;</description><content:encoded><![CDATA[<p>O Strata apareceu em quase todo artigo da reta final do benchmark v4. No de <a href="/2026/10/22/llm-benchmark-v4-open-source-nivel-consumidor-rtx5090-strix-halo-strata/">open source nível consumidor</a> ele foi o protagonista; no de <a href="/2026/10/22/llm-benchmark-v4-can-my-team-fit-in-a-4x-b300-h200/">quanto custa um time em B300 e H200</a> o modelo que ele serve virou a recomendação pra DGX Spark; e no <a href="/2026/10/22/llm-benchmark-v4-o-veredito-de-open-source-ate-out-2026/">veredito do v4</a> ele é a única resposta séria pra quem quer rodar modelo bom em casa.</p>
<p>O resultado, resumido: o <strong>Qwen3.8 Flash-Next</strong> servido pelo Strata numa RTX 5090 fez <strong>91, Tier A</strong>, com zero nudges e os 14 defeitos plantados resolvidos (dois só depois da revelação), a 1,5 ponto do mesmo modelo rodando numa B300 de datacenter. E fez isso a uns 159 tokens por segundo.</p>
<p>Neste artigo eu detalho o que é o Strata, por que ele importa justo agora, o que ele não resolve, e como eu deixei ele rodando no dia a dia, com carga e descarga automática e integrado no OpenCode.</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/20261022121453_strata-qwen-flash-next-opencode.png" alt="O OpenCode rodando o Qwen3.8 Flash-Next pelo Strata na RTX 5090, entre 129,9 e 153,1 tokens por segundo"  loading="lazy" /></p>
<h2>O que é o Strata<span class="hx:absolute hx:-mt-20" id="o-que-é-o-strata"></span>
    <a href="#o-que-%c3%a9-o-strata" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>O <a href="https://github.com/Niko1221/Strata"target="_blank" rel="noopener">Strata</a> é um motor de inferência open source, licença MIT, feito em torno de um modelo: o Qwen3.8 Flash-Next, um MoE de 125 bilhões de parâmetros com 6 bilhões ativos por token, que normalmente pede servidor. O código é C++ e CUDA, reaproveita partes do llama.cpp/ggml, e tem builds pra NVIDIA, AMD (inclusive Strix Halo), Intel Arc e Mac. Ele sobe uma API compatível com OpenAI e com Anthropic em localhost.</p>
<p>O truque é tratar o PC inteiro como uma hierarquia de memória. O modelo tem 24.576 especialistas, 512 em cada uma das 48 camadas, e cada token usa só 10 por camada, mais um especialista compartilhado:</p>
<ul>
<li>a <strong>VRAM</strong> guarda os especialistas mais usados, como um cache;</li>
<li>a <strong>RAM</strong> guarda todos, e quando o token precisa de um que não está na placa, o processador calcula ele ali mesmo, enquanto a placa trabalha nos outros;</li>
<li>o <strong>SSD</strong> guarda uma tabela de consulta de uns 29 GB, da qual o modelo lê poucas linhas por token.</li>
</ul>
<p>Em cima disso, uma cabeça de especulação que vem com o próprio modelo chuta os próximos tokens e o modelo grande confere todos de uma vez. O README promete a mesma resposta 1,6 a 1,8 vez mais rápido.</p>
<p>Na minha 5090, o log mostra como isso fica na prática. Os 32 GB de VRAM seguram de 10 a 11,5 mil especialistas em uns 20 a 22 GiB, quase metade do total, e <strong>em geral de 90% a 97% dos especialistas que cada token pede já estão na placa</strong>. O resto a CPU calcula direto na RAM, e só uma fração pequena é copiada pra placa pelo barramento PCIe. É por isso que a CPU também trabalha pesado, como dá pra ver mais abaixo.</p>
<h2>Por que isso importa agora<span class="hx:absolute hx:-mt-20" id="por-que-isso-importa-agora"></span>
    <a href="#por-que-isso-importa-agora" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Porque memória virou artigo de luxo. Em 2026 o preço de contrato da DRAM <a href="https://www.trendforce.com/presscenter/news/20260601-13070.html"target="_blank" rel="noopener">quase dobrou num único trimestre</a>, os fabricantes <a href="https://www.trendforce.com/presscenter/news/20260930-13258.html"target="_blank" rel="noopener">continuam priorizando servidor</a>, e a Micron <a href="https://www.storagenewsletter.com/2025/12/05/micron-announces-exit-from-crucial-consumer-business/"target="_blank" rel="noopener">largou o mercado consumidor</a> pra atender os clientes grandes. A própria RTX 5090, quando aparece, custa de duas vezes e meia a quase cinco vezes o preço sugerido, como eu contei no <a href="/2026/10/22/llm-benchmark-v4-o-veredito-de-open-source-ate-out-2026/">veredito</a>.</p>
<p>Nesse cenário, a forma tradicional de rodar um modelo Tier A em casa, com o modelo inteiro dentro da VRAM, é inviável. O Flash-Next em NVFP4 ocupou uns 100 GB de placa na B300; em casa ninguém tem isso.</p>
<blockquote>
  <p>O Strata inverte a premissa: em vez de exigir VRAM pro modelo inteiro, ele usa a VRAM como cache e a RAM como estoque. É o que permite um modelo Tier A rodar com uma fração da memória de vídeo que ele pediria.</p>

</blockquote>
<h2>Não é mágica<span class="hx:absolute hx:-mt-20" id="não-é-mágica"></span>
    <a href="#n%c3%a3o-%c3%a9-m%c3%a1gica" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>O Strata faz caber, mas não faz milagre. O mínimo declarado é <strong>32 GB somando RAM e VRAM</strong>, mas com isso roda só a versão mais comprimida; a IQ3_S que fez os 91 pede uns 64 GB de RAM. Some <strong>uns 80 GB de disco</strong>, de preferência SSD, e uma placa de 12 GB pra cima no caminho oficial. Abaixo disso até sobe, tem relato com placa de 8 GB, mas a uns 15 tokens por segundo.</p>
<h3>Banda de memória é o que define a velocidade<span class="hx:absolute hx:-mt-20" id="banda-de-memória-é-o-que-define-a-velocidade"></span>
    <a href="#banda-de-mem%c3%b3ria-%c3%a9-o-que-define-a-velocidade" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Na geração de token, o gargalo de máquina local quase nunca é o cálculo. É quantos bytes por segundo a memória consegue entregar pros núcleos, porque cada token precisa ler os pesos ativos do modelo. E aí a diferença entre os tipos de memória é brutal:</p>
<table>
  <thead>
      <tr>
          <th>Memória</th>
          <th style="text-align: right">Banda aproximada</th>
          <th style="text-align: right">Contra a 5090</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>VRAM GDDR7 da RTX 5090</td>
          <td style="text-align: right">~1,8 TB/s</td>
          <td style="text-align: right">1x</td>
      </tr>
      <tr>
          <td>Memória unificada LPDDR5X do Strix Halo</td>
          <td style="text-align: right">~256 GB/s</td>
          <td style="text-align: right">7 vezes mais lenta</td>
      </tr>
      <tr>
          <td>RAM DDR5 de desktop, dois canais</td>
          <td style="text-align: right">~60 a 100 GB/s</td>
          <td style="text-align: right">18 a 30 vezes mais lenta</td>
      </tr>
      <tr>
          <td>Barramento PCIe 5.0 x16</td>
          <td style="text-align: right">~64 GB/s por sentido</td>
          <td style="text-align: right">quase 30 vezes mais lento</td>
      </tr>
      <tr>
          <td>PCIe medido pelo Strata na minha máquina (placa em x8)</td>
          <td style="text-align: right">~29 GB/s</td>
          <td style="text-align: right">uns 60 vezes mais lento</td>
      </tr>
  </tbody>
</table>
<p>A última linha é da minha máquina: a placa está rodando com 8 linhas de PCIe em vez de 16, e o Strata mede isso na inicialização. Mais um motivo pra o cache na VRAM ser tão importante.</p>
<p>O Strix Halo tem 128 GB e cabe muito mais coisa que uma 5090, mas cada byte chega sete vezes mais devagar. Foi isso que eu medi no benchmark: o Qwen 3.8 27B denso em Q8 lê uns 29 GB por token, e na Strix o teto teórico fica perto de 9 tokens por segundo. Eu medi 7,7. A mesma conta na banda da 5090 daria uns 60, sete vezes mais, se o modelo coubesse com o contexto.</p>
<p>É por isso que o desenho do Strata funciona: o caminho quente fica na GDDR7, e o que falta no cache é calculado pela CPU na DDR5, ou atravessa o PCIe, os dois dezenas de vezes mais lentos. Com mais de 90% de acerto no cache, o pouco que fica de fora não derruba a velocidade. Numa placa pequena, o cache cobre menos, mais especialistas vão pela estrada lenta, e a velocidade cai: o próprio autor publica 53 tokens por segundo numa RTX 5070 de 12 GB, um terço da minha 5090.</p>
<h3>PC batata não roda<span class="hx:absolute hx:-mt-20" id="pc-batata-não-roda"></span>
    <a href="#pc-batata-n%c3%a3o-roda" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Então esqueça notebook com GPU integrada e 16 GB de RAM. Sem uma placa dedicada razoável, sem 32 GB somando RAM e VRAM e sem SSD, o modelo não sobe, ou sobe e anda na velocidade da RAM, que é a de uma carroça.</p>
<p>E tem o custo de entrada que não aparece na velocidade de geração: o Strata lê a primeira mensagem de cada conversa inteira, e o README fala em <a href="https://github.com/Niko1221/Strata#good-to-know"target="_blank" rel="noopener">uns 60 segundos a cada 30 mil tokens</a> como regra geral. Na minha 5090 é bem mais rápido, uns 100 mil tokens em 16 a 20 segundos, mas numa placa menor conte com o número do README. Agente de código manda prompt de sistema grande, então a primeira resposta de cada sessão demora mais que as seguintes.</p>
<h3>Pesado até na minha 5090<span class="hx:absolute hx:-mt-20" id="pesado-até-na-minha-5090"></span>
    <a href="#pesado-at%c3%a9-na-minha-5090" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Minha máquina não é fraca: Ryzen 9 7950X3D, 96 GB de RAM (o sistema enxerga uns 92), RTX 5090 de 32 GB. Mesmo assim, o Flash-Next ocupa tudo:</p>
<ul>
<li>na carga, ele joga uns 50 GB de especialistas na RAM, a uns 3 GB por segundo com o SSD frio, e enche o cache da placa com mais de 20 GiB;</li>
<li>com tudo carregado, sobram menos de 800 MB de VRAM livre;</li>
<li>com o servidor de pé, a máquina fica com 88 dos 92 GB de RAM ocupados, e o sistema começa a usar swap.</li>
</ul>
<p>Este é o btop no meio de uma sessão de trabalho com o OpenCode. O processo do Strata sozinho segura 54 GB de RAM e metade da CPU, com metade das 32 threads do 7950X3D cravadas em 100% e o resto acima de 50%. A 5090 está a 99% de uso, com 30 dos 32 GB de VRAM ocupados e puxando 351 W. Sobram 4 GB de RAM disponíveis, e já tem quase 48 GB em swap.</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/20261022122545_strata-btop-7950x3d-5090.png" alt="O btop durante uma sessão com o Strata: RAM a 96%, swap pela metade, metade da CPU em 100% e o resto acima de 50%, e a RTX 5090 a 99% com 30 de 32 GB de VRAM"  loading="lazy" /></p>
<p>O efeito colateral aparece no resto do sistema. Com a VRAM no limite, o lançador de aplicativos do meu desktop e o terminal num monitor secundário pararam de abrir direito, e outros programas ficam lentos. A solução foi mandar o Strata devolver uns 2,5 GB de VRAM pro desktop, com a opção <code>--vram-elastic</code>, ao custo de um cache um pouco menor.</p>
<blockquote>
  <p>Com tudo isso, eu acredito firmemente que a minha máquina é o mínimo pra usar um LLM local em trabalho de programação de verdade, além de exercício pequeno. Com menos que isso, conte com velocidade bem menor, contexto mais curto e o PC inteiro sofrendo enquanto o modelo está carregado.</p>

</blockquote>
<h2>Como instalar<span class="hx:absolute hx:-mt-20" id="como-instalar"></span>
    <a href="#como-instalar" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>O caminho oficial no Linux e no Mac é o instalador de uma linha. Ele verifica o SHA-256, não pede root e instala na sua pasta pessoal. Como todo <code>curl | sh</code>, vale baixar e ler antes:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">curl -fsSL https://github.com/Niko1221/Strata/releases/latest/download/install.sh -o install.sh
</span></span><span class="line"><span class="cl">less install.sh
</span></span><span class="line"><span class="cl">sh install.sh</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Depois, o <code>strata launch</code> pergunta modelo, contexto e acesso, baixa o motor certo pra sua placa e o modelo, e começa a servir. Pra scripts, tudo vai por flag:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">strata launch                                <span class="c1"># telas guiadas</span>
</span></span><span class="line"><span class="cl">strata launch --model &lt;variante&gt; --yes --no-tui   <span class="c1"># sem perguntas</span>
</span></span><span class="line"><span class="cl">strata launch --status                       <span class="c1"># o que está servindo</span>
</span></span><span class="line"><span class="cl">strata launch --stop                         <span class="c1"># parar</span>
</span></span><span class="line"><span class="cl">strata monitor --url http://127.0.0.1:8080   <span class="c1"># velocidade, GPU, memória</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Eu usei a variante <strong>IQ3_S</strong>, a que fez os 91 no benchmark. No disco ela ocupa uns 84 GB em dois arquivos (o modelo de 55 GB e a tabela de consulta de 29 GB), mais uns 7 GB da cabeça de especulação.</p>
<p>A configuração que o <code>strata launch</code> gera é um JSON com os argumentos do motor. Os que importam na minha:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="s2">&#34;args&#34;</span><span class="err">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">  <span class="s2">&#34;--expert-cache&#34;</span><span class="p">,</span> <span class="s2">&#34;auto&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="s2">&#34;--prefill&#34;</span><span class="p">,</span> <span class="s2">&#34;auto&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="s2">&#34;--spec&#34;</span><span class="p">,</span> <span class="s2">&#34;4&#34;</span><span class="p">,</span> <span class="s2">&#34;--spec-min-p&#34;</span><span class="p">,</span> <span class="s2">&#34;0.5&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="s2">&#34;--mtp&#34;</span><span class="p">,</span> <span class="s2">&#34;/mnt/data/strata-models/mtp/rt&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="s2">&#34;--max-context&#34;</span><span class="p">,</span> <span class="s2">&#34;262144&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="s2">&#34;--kv&#34;</span><span class="p">,</span> <span class="s2">&#34;int8&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="s2">&#34;--kv-resident&#34;</span><span class="p">,</span> <span class="s2">&#34;32768&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="s2">&#34;--vram-elastic&#34;</span>
</span></span><span class="line"><span class="cl"><span class="p">]</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>O <code>--spec 4</code> liga a especulação com uma janela de 4 tokens por passo, até 3 chutados pela cabeça de especulação; o <code>--kv int8</code> guarda o cache de atenção em 8 bits; o <code>--kv-resident</code> deixa só 32 mil posições do cache de contexto, as mais lidas, na VRAM; e o <code>--vram-elastic</code> permite devolver VRAM pro desktop com o servidor rodando.</p>
<h3>De 128 mil pra 256 mil tokens de contexto<span class="hx:absolute hx:-mt-20" id="de-128-mil-pra-256-mil-tokens-de-contexto"></span>
    <a href="#de-128-mil-pra-256-mil-tokens-de-contexto" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>O benchmark rodou com 128 mil tokens de contexto, que é o que a instalação configurou por padrão. Depois eu subi pra 256 mil, com <code>--max-context 262144</code>, e a perda foi pequena: nos logs do servidor, a geração ficou na média de uns 190 tokens por segundo numa sessão curta com 128 mil e de uns 175 com 256 mil, uns 10% mais lenta pelo dobro de contexto, um pouco mais com a VRAM devolvida pro desktop.</p>
<p>O que segura isso é o <code>--kv-resident</code>: só 32 mil posições do cache de atenção, as mais lidas, ficam na VRAM, o resto fica na RAM, e mesmo assim umas 97% das leituras desse cache acontecem na placa. A nota de 91 do benchmark foi medida com 128 mil; com 256 mil eu medi velocidade, e a qualidade ainda não foi re-testada.</p>
<p>Pra mim, 256 mil é o mínimo pra trabalho longo com agente. Com 128 mil, uma sessão de verdade estoura o contexto rápido, e o agente começa a compactar a memória toda hora. Isso fica irritante muito rápido, e cada compactação joga fora detalhe que o modelo vai precisar mais tarde.</p>
<blockquote>
  <p>Contexto curto é um problema escondido de modelo local. Antes de olhar tokens por segundo, veja quanto contexto você consegue servir sem a velocidade desabar.</p>

</blockquote>
<h2>Carregar e descarregar sozinho<span class="hx:absolute hx:-mt-20" id="carregar-e-descarregar-sozinho"></span>
    <a href="#carregar-e-descarregar-sozinho" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Deixar uns 50 GB de RAM e a placa inteira presos o dia todo, só pra ter o modelo pronto quando eu precisar, não faz sentido. Então montei um proxy pequeno, em Python puro, que fica sempre de pé na porta 8080 e controla o Strata:</p>
<ul>
<li>responde a lista de modelos sozinho, então o OpenCode enxerga o modelo sem acordar nada;</li>
<li>na primeira requisição de chat, sobe o Strata numa porta interna e espera ficar pronto, uns 40 segundos;</li>
<li>repassa as respostas em streaming;</li>
<li>depois de 10 minutos sem tráfego, derruba o Strata e devolve a placa e a RAM pro sistema; nunca no meio de uma requisição.</li>
</ul>
<p>O miolo é este:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">ensure_up</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">    <span class="k">with</span> <span class="n">_lock</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="n">ready</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">            <span class="k">return</span> <span class="kc">True</span>
</span></span><span class="line"><span class="cl">        <span class="n">subprocess</span><span class="o">.</span><span class="n">Popen</span><span class="p">([</span><span class="n">STRATA</span><span class="p">,</span> <span class="s2">&#34;serve&#34;</span><span class="p">,</span> <span class="s2">&#34;--config&#34;</span><span class="p">,</span> <span class="n">CFG</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                          <span class="s2">&#34;--host&#34;</span><span class="p">,</span> <span class="s2">&#34;127.0.0.1&#34;</span><span class="p">,</span> <span class="s2">&#34;--port&#34;</span><span class="p">,</span> <span class="nb">str</span><span class="p">(</span><span class="n">UP_PORT</span><span class="p">)],</span>
</span></span><span class="line"><span class="cl">                         <span class="n">start_new_session</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="k">for</span> <span class="n">_</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">90</span><span class="p">):</span>          <span class="c1"># carga a frio leva ~35-40 s</span>
</span></span><span class="line"><span class="cl">            <span class="k">if</span> <span class="n">ready</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">                <span class="n">set_reserve</span><span class="p">()</span>        <span class="c1"># devolve VRAM pro desktop</span>
</span></span><span class="line"><span class="cl">                <span class="k">return</span> <span class="kc">True</span>
</span></span><span class="line"><span class="cl">            <span class="n">time</span><span class="o">.</span><span class="n">sleep</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="kc">False</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">idle_watch</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">    <span class="k">while</span> <span class="kc">True</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="n">time</span><span class="o">.</span><span class="n">sleep</span><span class="p">(</span><span class="mi">30</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="n">time</span><span class="o">.</span><span class="n">time</span><span class="p">()</span> <span class="o">-</span> <span class="n">_last</span> <span class="o">&gt;</span> <span class="n">IDLE_TTL</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">            <span class="c1"># nunca derrube o motor com requisição em voo (ver abaixo),</span>
</span></span><span class="line"><span class="cl">            <span class="c1"># mas com teto: requisição vazando não prende a GPU pra sempre</span>
</span></span><span class="line"><span class="cl">            <span class="k">if</span> <span class="n">_active</span> <span class="o">&gt;</span> <span class="mi">0</span> <span class="ow">and</span> <span class="p">(</span><span class="n">time</span><span class="o">.</span><span class="n">time</span><span class="p">()</span> <span class="o">-</span> <span class="n">_last</span><span class="p">)</span> <span class="o">&lt;</span> <span class="n">ACTIVE_HARD_CAP_S</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">                <span class="k">continue</span>
</span></span><span class="line"><span class="cl">            <span class="n">stop_up</span><span class="p">()</span>                <span class="c1"># libera GPU e ~50 GB de RAM</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>E um serviço de usuário do systemd mantém o proxy de pé desde o login (versão resumida):</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ini" data-lang="ini"><span class="line"><span class="cl"><span class="c1"># ~/.config/systemd/user/qwen-strata.service</span>
</span></span><span class="line"><span class="cl"><span class="k">[Unit]</span>
</span></span><span class="line"><span class="cl"><span class="na">Description</span><span class="o">=</span><span class="s">Qwen3.8 Flash-Next on-demand proxy (Strata RTX 5090)</span>
</span></span><span class="line"><span class="cl"><span class="na">After</span><span class="o">=</span><span class="s">network-online.target</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">[Service]</span>
</span></span><span class="line"><span class="cl"><span class="na">Environment</span><span class="o">=</span><span class="s">QWEN_IDLE_TTL=600</span>
</span></span><span class="line"><span class="cl"><span class="na">Environment</span><span class="o">=</span><span class="s">QWEN_VRAM_RESERVE_MIB=2560</span>
</span></span><span class="line"><span class="cl"><span class="na">Environment</span><span class="o">=</span><span class="s">QWEN_UP_PORT=8781</span>
</span></span><span class="line"><span class="cl"><span class="na">Environment</span><span class="o">=</span><span class="s">QWEN_PROXY_PORT=8080</span>
</span></span><span class="line"><span class="cl"><span class="na">ExecStart</span><span class="o">=</span><span class="s">%h/.local/bin/qwen-strata-ondemand</span>
</span></span><span class="line"><span class="cl"><span class="na">Restart</span><span class="o">=</span><span class="s">on-failure</span>
</span></span><span class="line"><span class="cl"><span class="na">KillMode</span><span class="o">=</span><span class="s">mixed</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">[Install]</span>
</span></span><span class="line"><span class="cl"><span class="na">WantedBy</span><span class="o">=</span><span class="s">default.target</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">systemctl --user daemon-reload
</span></span><span class="line"><span class="cl">systemctl --user <span class="nb">enable</span> --now qwen-strata.service</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<h3>O bug do &ldquo;parou do nada&rdquo;<span class="hx:absolute hx:-mt-20" id="o-bug-do-parou-do-nada"></span>
    <a href="#o-bug-do-parou-do-nada" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>A primeira versão desse vigilante de idle tinha um bug maldoso, e vale contar porque ele ensina uma coisa sobre modelos que pensam sempre. O Flash-Next raciocina antes de responder, e numa fase longa de raciocínio ele pode ficar <strong>mais de 10 minutos sem emitir um único byte</strong>. Para o relógio de idle, aquilo era &ldquo;ninguém usando&rdquo;: o proxy derrubava o Strata no meio da própria requisição, engolia a truncagem do stream, e o OpenCode recebia um turno vazio, sem conteúdo e sem erro. O modelo parecia ter parado do nada.</p>
<p>Três correções, todas no proxy:</p>
<ul>
<li><strong>Contador de requisições em voo</strong> (<code>_active</code>, o <code>if</code> novo do trecho acima): enquanto houver uma requisição em andamento, o motor não cai. Com teto: depois de <code>QWEN_ACTIVE_HARD_CAP_S</code> (1 hora por padrão, seis vezes o TTL normal), até uma requisição vazando por bug não consegue prender a GPU e a RAM pra sempre.</li>
<li><strong>Relógio atualizado por byte</strong>: o contador de idle agora zera no início de cada requisição e a cada pedaço repassado no streaming. Geração em curso nunca conta como inatividade; o período sem byte nenhum, que é o raciocínio puro, fica coberto pelo <code>_active</code>.</li>
<li><strong>Falha honesta no meio do stream</strong>: se o motor morrer mesmo assim, o proxy agora emite um evento de erro no stream (<code>&quot;upstream engine died mid-stream&quot;</code>) em vez de fechar calado. O agente vê que a resposta morreu e tenta de novo, em vez de ficar olhando um turno vazio parecendo travado.</li>
</ul>
<p>O preço é a espera na primeira mensagem depois de um tempo parado. Em troca, quando eu não estou programando, a máquina volta a ser minha.</p>
<h2>Configurando o OpenCode<span class="hx:absolute hx:-mt-20" id="configurando-o-opencode"></span>
    <a href="#configurando-o-opencode" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>O Strata tem um atalho, <code>strata connect opencode --write</code>, que escreve a configuração depois de perguntar e guarda backup. Na mão, é um provider compatível com OpenAI apontando pra porta local:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="s2">&#34;provider&#34;</span><span class="err">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;strata-5090&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;npm&#34;</span><span class="p">:</span> <span class="s2">&#34;@ai-sdk/openai-compatible&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;name&#34;</span><span class="p">:</span> <span class="s2">&#34;Strata (RTX 5090)&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;options&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="nt">&#34;baseURL&#34;</span><span class="p">:</span> <span class="s2">&#34;http://127.0.0.1:8080/v1&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="nt">&#34;apiKey&#34;</span><span class="p">:</span> <span class="s2">&#34;local&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;models&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="nt">&#34;qwen3.8-flash-next-iq3_s&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;name&#34;</span><span class="p">:</span> <span class="s2">&#34;Qwen3.8 Flash-Next IQ3_S (Strata 5090)&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;reasoning&#34;</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;tool_call&#34;</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;limit&#34;</span><span class="p">:</span> <span class="p">{</span> <span class="nt">&#34;context&#34;</span><span class="p">:</span> <span class="mi">262144</span><span class="p">,</span> <span class="nt">&#34;input&#34;</span><span class="p">:</span> <span class="mi">229376</span><span class="p">,</span> <span class="nt">&#34;output&#34;</span><span class="p">:</span> <span class="mi">32768</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">      <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Qualquer chave serve, porque o servidor é local. O próprio Strata avisa que o OpenCode precisa do <code>limit.input</code>, pra saber quanto do contexto sobra pra entrada; no exemplo eu pus o contexto menos a saída máxima. O Claude Code também funciona, apontando <code>ANTHROPIC_BASE_URL</code> pra <code>http://127.0.0.1:8080</code> e ajustando <code>CLAUDE_CODE_MAX_CONTEXT_TOKENS</code> pro tamanho do contexto, já que o Strata fala a API da Anthropic também.</p>
<p>Um detalhe: por padrão o Strata atende uma requisição por vez. Se o seu agente dispara subagentes em paralelo, eles entram em fila.</p>
<h2>E no dia a dia?<span class="hx:absolute hx:-mt-20" id="e-no-dia-a-dia"></span>
    <a href="#e-no-dia-a-dia" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Se você tem a máquina pra isso, o resultado é bom. O Flash-Next passa de 150 tokens por segundo na maior parte do tempo, como dá pra ver no print do começo, e a chamada de ferramenta é sólida: no benchmark ele rodou as sete sprints no OpenCode sem nenhum empurrão, e no uso normal ele lê arquivo, roda comando e edita código sem tropeçar no formato.</p>
<p>Na prática, não sinto muita diferença pro Claude Code ou pro OpenCode com GLM ou Kimi na nuvem. E a nuvem não anda no melhor momento: na minha percepção, todos esses serviços estão um pouco mais lentos que o normal ultimamente, provavelmente sofrendo com demanda maior que a capacidade disponível. Rodando em casa, a velocidade é sempre a mesma, e o código não sai da minha máquina.</p>
<p>Mesmo com os tokens por segundo bons, eu ainda sinto o Flash-Next um pouco menos responsivo que um modelo comercial. Tem os segundos de leitura na primeira mensagem de cada sessão, a pausa quando ele precisa de especialistas que não estão na placa, e a carga a frio depois de um tempo parado. É o esperado: estamos rodando um modelo de servidor com os recursos no limite.</p>
<blockquote>
  <p>Considerando que é de graça e roda na minha máquina, ele está quase bom o suficiente. Pela primeira vez, rodar um modelo local pra programar parece um cenário usável de verdade, e eu me vejo usando cada vez mais como substituto.</p>

</blockquote>
<p>O porém continua sendo o hardware. Uma 5090, 96 GB de RAM e um processador forte são o mínimo pra esse cenário, e esse hardware está cada vez mais caro e difícil de achar. Mas, pra quem já tem, o Tier A em casa deixou de ser promessa.</p>
]]></content:encoded><category>modelos-locais</category><category>open-source</category><category>llms</category><category>hardware</category></item><item><title>LLM Benchmark v4: open source nível consumidor na RTX5090 ou Strix Halo. E o Strata?</title><link>https://www.akitaonrails.com/2026/10/22/llm-benchmark-v4-open-source-nivel-consumidor-rtx5090-strix-halo-strata/</link><guid isPermaLink="true">https://www.akitaonrails.com/2026/10/22/llm-benchmark-v4-open-source-nivel-consumidor-rtx5090-strix-halo-strata/</guid><pubDate>Thu, 22 Oct 2026 11:30:00 -0300</pubDate><description>&lt;p&gt;Nos dois últimos artigos eu cobri o &lt;a href="https://www.akitaonrails.com/2026/10/22/llm-benchmark-v4-open-source-que-so-cabe-em-4x-b300/"&gt;benchmark dos modelos grandes em 4x B300&lt;/a&gt; e a &lt;a href="https://www.akitaonrails.com/2026/10/22/llm-benchmark-v4-can-my-team-fit-in-a-4x-b300-h200/"&gt;conta de time e custo por usuário em B300 e H200&lt;/a&gt;. Agora a pergunta de todo desenvolvedor que não tem um pod na Runpod: &lt;strong&gt;hardware de consumidor já serve pra trabalhar com IA de verdade?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Voltei pra duas máquinas que eu já tinha: uma workstation com RTX 5090 e um mini PC Strix Halo. E com um motor de inferência novo, o Strata, que promete fazer MoE grande caber em PC comum. A resposta curta: um modelo de 125B já faz Tier A numa RTX 5090. O resto é mais complicado.&lt;/p&gt;</description><content:encoded><![CDATA[<p>Nos dois últimos artigos eu cobri o <a href="/2026/10/22/llm-benchmark-v4-open-source-que-so-cabe-em-4x-b300/">benchmark dos modelos grandes em 4x B300</a> e a <a href="/2026/10/22/llm-benchmark-v4-can-my-team-fit-in-a-4x-b300-h200/">conta de time e custo por usuário em B300 e H200</a>. Agora a pergunta de todo desenvolvedor que não tem um pod na Runpod: <strong>hardware de consumidor já serve pra trabalhar com IA de verdade?</strong></p>
<p>Voltei pra duas máquinas que eu já tinha: uma workstation com RTX 5090 e um mini PC Strix Halo. E com um motor de inferência novo, o Strata, que promete fazer MoE grande caber em PC comum. A resposta curta: um modelo de 125B já faz Tier A numa RTX 5090. O resto é mais complicado.</p>
<h2>As máquinas, lado a lado<span class="hx:absolute hx:-mt-20" id="as-máquinas-lado-a-lado"></span>
    <a href="#as-m%c3%a1quinas-lado-a-lado" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Antes de qualquer nota, entenda a diferença física do que eu estou comparando:</p>
<table>
  <thead>
      <tr>
          <th>Hardware</th>
          <th>Memória</th>
          <th>Banda</th>
          <th>Arquitetura</th>
          <th>Custo aproximado</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>NVIDIA B300</td>
          <td>288 GB HBM3e</td>
          <td>~8 TB/s</td>
          <td>Blackwell Ultra, FP8 e FP4</td>
          <td>US$ 9/hora (Runpod)</td>
      </tr>
      <tr>
          <td>NVIDIA H200</td>
          <td>141 GB HBM3e</td>
          <td>~4,8 TB/s</td>
          <td>Hopper, FP8</td>
          <td>US$ 3,59/hora</td>
      </tr>
      <tr>
          <td>DGX Spark</td>
          <td>128 GB unificada</td>
          <td>273 GB/s</td>
          <td>GB10, FP4</td>
          <td>~US$ 6.950</td>
      </tr>
      <tr>
          <td>RTX 5090</td>
          <td>32 GB GDDR7</td>
          <td>~1,8 TB/s</td>
          <td>Blackwell sm_120, FP8/FP4</td>
          <td>US$ 1.999 sugerido (~5x no varejo)</td>
      </tr>
      <tr>
          <td>Strix Halo (Ryzen AI Max+ 395)</td>
          <td>128 GB unificada</td>
          <td>~256 GB/s</td>
          <td>gfx1151, ROCm</td>
          <td>~US$ 2.000</td>
      </tr>
  </tbody>
</table>
<p>O B300 tem mais de 30 vezes a banda de memória da Strix Halo. A 5090 tem 7 vezes. Isso explica por que o mesmo modelo voa numa máquina e rasteja na outra, e por que quantização deixa de ser detalhe e vira decisão de projeto.</p>
<p>E tem o detalhe da memória ser compartilhada: na Strix e na Spark, a mesma RAM serve CPU, GPU e sistema operacional. Não é VRAM dedicada, então a folga real é bem menor que o número bonito da caixa.</p>
<h2>O ranking em hardware de consumidor<span class="hx:absolute hx:-mt-20" id="o-ranking-em-hardware-de-consumidor"></span>
    <a href="#o-ranking-em-hardware-de-consumidor" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>A régua é a mesma do benchmark v4 de sabotagem: 14 defeitos plantados, nota de vigilância de revisão, Tier A a partir de 83.</p>
<table>
  <thead>
      <tr>
          <th>Modelo / máquina</th>
          <th>Variante e engine</th>
          <th style="text-align: right">Nota / Tier</th>
          <th style="text-align: right">Velocidade</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><strong>Qwen3.8 Flash-Next</strong> (125B-A6B)</td>
          <td>RTX 5090, Strata, IQ3_S</td>
          <td style="text-align: right"><strong>91,0 (A)</strong></td>
          <td style="text-align: right">159,2 tok/s</td>
      </tr>
      <tr>
          <td>Ornith-1.5-35B-A3B</td>
          <td>RTX 5090, Strata, Q8_0 GGUF</td>
          <td style="text-align: right">64,5 (C)</td>
          <td style="text-align: right">~80 tok/s</td>
      </tr>
      <tr>
          <td>Qwen3.8-27B</td>
          <td>5090 → Strix, Q5_K_M → Q8_0</td>
          <td style="text-align: right">80,0 (B)</td>
          <td style="text-align: right">7,7 tok/s</td>
      </tr>
      <tr>
          <td>GLM-4.7-Flash (30B-A3B)</td>
          <td>RTX 5090, llama-swap, Q5_K_M</td>
          <td style="text-align: right">24,0 (D)</td>
          <td style="text-align: right">não medido</td>
      </tr>
  </tbody>
</table>
<p>O GLM-4.7-Flash completou o protocolo inteiro (depois de uma primeira tentativa aninhada que precisou de retry) e mesmo assim fracassou em detectar sabotagem. E tem uma lição de humildade no Qwen 27B: ele fez 80 na mesma tarefa em que fez 80,5 na B300, mas a 7,7 tokens por segundo na sonda Q8 e 706 minutos de relógio. Nota boa não paga produtividade.</p>
<p>Deixei de fora os testes que ainda não têm nota final: a Qwen3.8 Flash-Next na Strix com Strata só está confirmada como executável, e as densas de 27B foram descartadas na Strix pelo corte de 30 tok/s.</p>
<h2>O tuning que faz modelo rodar bem em PC<span class="hx:absolute hx:-mt-20" id="o-tuning-que-faz-modelo-rodar-bem-em-pc"></span>
    <a href="#o-tuning-que-faz-modelo-rodar-bem-em-pc" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Hardware de consumidor não perdoa configuração preguiçosa. O que eu aprendi na prática:</p>
<ul>
<li><strong>Na 5090, compile o llama.cpp pro sm_120.</strong> Imagem pronta cai em fallback PTX lento. É <code>-DGGML_CUDA=ON -DCMAKE_CUDA_ARCHITECTURES=120</code>, driver 570+, e nada de <code>--ctx-size 0</code> numa placa de 32 GB: o default de contexto de treino estoura a VRAM na primeira alocação. Contexto tem teto explícito, e se der OOM no primeiro request, corta 25 a 50%.</li>
<li><strong>KV cache em q8_0.</strong> Corta a memória de KV pela metade sem custo mensurável de qualidade. Flash-attention ligado por padrão, mas o GLM 4.7 Flash precisa dele desligado, porque a arquitetura deepseek2 dele é incompatível.</li>
<li><strong>Raciocínio híbrido dá trabalho.</strong> O Qwen3.8-27B, com a arquitetura SSM/Mamba, precisou de build fresco do llama.cpp/Ollama por causa do <code>ssm_conv1d</code>. E o modo &ldquo;sem raciocínio&rdquo; não impediu loop de coerência em uma das tentativas; a saída foi retomar na Strix com Q8_0 e <code>--reasoning off</code>. Não foi memória extra que resolveu, foi desligar o pensamento.</li>
<li><strong>Na Strix, o ajuste é ROCm, não mágica.</strong> <code>HSA_OVERRIDE_GFX_VERSION</code>, <code>GGML_HIP_NO_VMM</code>, <code>-dio</code>. O trunfo dela é capacidade (128 GB), não velocidade (~256 GB/s). Q4_K_M deu 12,6 tok/s e Q8_0 deu 7,7 tok/s, abaixo do meu corte de 30 tok/s pra uso interativo. Por isso a Strix virou escape hatch de modelo grande, não caixa de chat rápida.</li>
<li><strong>Especulação só ajuda com aceitação alta.</strong> Num <a href="https://mihai.ltd/blog/beating-llamacpp-from-scratch-consumer-amd"target="_blank" rel="noopener">teste do Mihai Farcas</a> numa RX 7900 XTX, a especulação por n-gram do llama.cpp, sem modelo de rascunho, teve só 5 de 64 tokens aceitos, e a vazão ficou igual numa rodada e caiu uns 2% na outra. O ganho aparece com um rascunhador feito pro modelo, como o ajudante que o Strata usa no Flash-Next.</li>
</ul>
<h2>O que é esse tal de Strata<span class="hx:absolute hx:-mt-20" id="o-que-é-esse-tal-de-strata"></span>
    <a href="#o-que-%c3%a9-esse-tal-de-strata" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>O <a href="https://github.com/Niko1221/Strata"target="_blank" rel="noopener">Strata</a> é um motor de inferência open source, licença MIT, feito em torno de um modelo: o <strong>Qwen3.8 Flash-Next</strong>, um MoE de 125 bilhões de parâmetros que normalmente pede servidor. Ele também roda MoEs menores, como a Ornith-35B que testei aqui. O código é C++ e CUDA e reaproveita partes do llama.cpp/ggml. Tem builds pra NVIDIA (CUDA 12 e 13), AMD (inclusive Strix Halo), Intel Arc e Apple Silicon, e sobe uma API compatível com OpenAI e Anthropic em localhost, que pluga direto no Claude Code, Codex CLI ou OpenCode.</p>
<p>Cuidado com o nome. O Mihai Farcas publicou <a href="https://mihai.ltd/blog/beating-llamacpp-from-scratch-consumer-amd"target="_blank" rel="noopener">outro motor chamado Strata</a>, em Rust com kernels HIP, pra modelos densos pequenos do Qwen 3.5 numa RX 7900 XTX. São projetos diferentes, e o que rodou aqui foi o primeiro.</p>
<p>O problema que ele ataca é o MoE grande em máquina pequena. A estratégia é <strong>expert-offload</strong>, em camadas:</p>
<ul>
<li>o modelo tem 24.576 especialistas, 512 em cada uma das 48 camadas, e cada token usa só 10 por camada;</li>
<li>a GPU guarda uns 4 mil especialistas, os mais usados;</li>
<li>a RAM do sistema guarda todos;</li>
<li>uma tabela de consulta de uns 29 GB fica no SSD, e o modelo lê poucas linhas dela por token.</li>
</ul>
<p>Em cima disso, um ajudante pequeno chuta as próximas palavras e o modelo grande confere todas de uma vez. O README promete a mesma resposta 1,6 a 1,8 vez mais rápido. O mínimo é 32 GB somando RAM e VRAM, mais uns 80 GB de disco, e a variante IQ3_S que eu usei tem uns 55 GB.</p>
<p>O que me fez acreditar nele foi o resultado: o <strong>Qwen3.8 Flash-Next na 5090 com Strata fez 91,0, com zero nudges e os 14 itens resolvidos</strong>, a 1,5 ponto do 92,5 que o mesmo modelo fez na B300 com vLLM. O próprio relatório chama isso de &ldquo;dentro do ruído&rdquo;. Um projeto de comunidade, numa placa de consumidor, ficou colado no mesmo modelo servido em datacenter em NVFP4.</p>
<h3>E agora ele enxerga<span class="hx:absolute hx:-mt-20" id="e-agora-ele-enxerga"></span>
    <a href="#e-agora-ele-enxerga" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Até ontem, o meu Strata era cego: mandar imagem devolvia &ldquo;this server was started without the vision encoder&rdquo;. Habilitei a visão na mão, e foi mais simples do que parecia.</p>
<p>O projetor de visão (o <code>mmproj</code>) eu já tinha arquivado no NAS dentro da variante uncensored do Flash-Next. Conferi o sha256 dele contra o manifesto do arquivo e contra o hash que o Strata pinou pro encoder BF16, e subi uma cópia pro diretório de modelos. A configuração ganhou o flag <code>--vision</code> e uma seção <code>vision</code> apontando pro componente <code>strata-vision</code> e pro mmproj, com backup do lado.</p>
<p>A verificação foi de ponta a ponta: o encoder carrega em meio segundo, aquece 1.024 tokens de imagem em menos de dois segundos, e respondeu &ldquo;Red&rdquo; pra um quadrado vermelho de teste. E o detalhe que me importa mais: com o encoder residente, sobraram 3,65 GB de VRAM, então a reserva de 4 GB do desktop segue valendo. A única pegadinha: o carregamento preguiçoso não convive com a seção de visão nessa configuração, então ele fica desligado aqui.</p>
<h2>Cuidado: o Strata come a GPU toda, e o desktop morre<span class="hx:absolute hx:-mt-20" id="cuidado-o-strata-come-a-gpu-toda-e-o-desktop-morre"></span>
    <a href="#cuidado-o-strata-come-a-gpu-toda-e-o-desktop-morre" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Tem um custo que não aparece em benchmark: rodar o Strata na mesma GPU que desenha a sua área de trabalho. Ele foi feito pra encher a placa, e enche mesmo.</p>
<p>No meu caso, o motor alocou 32.085 dos 32.607 MiB da 5090, 98,4% da VRAM. O Hyprland ficou com uns 500 MiB pra si, as alocações do desktop falharam, e até o terminal parou de responder. Só resolveu com hard reset.</p>
<p>Foram duas falhas em sequência. A reserva que eu tinha configurado era de 2.560 MiB, mas o proxy disparava uma única vez, engolia a exceção e nunca tentava de novo. O endpoint <code>/v1/vram</code> ainda não estava no ar naquele instante, então a reserva não foi aplicada. E 2.560 MiB já era pouco pra Hyprland, navegador e terminal juntos.</p>
<p>A correção, validada em teste de carga:</p>
<ul>
<li>Reserva de <strong>4.096 MiB</strong> (4 GiB), com retry por até alguns minutos, verificação da resposta do motor e log de confirmação. Nada de fire-and-forget.</li>
<li>Reafirmação da reserva a cada 30 segundos enquanto o motor está carregado, pra fechar qualquer deriva de rebalanceamento.</li>
<li>No primeiro teste de carga, com a GPU a 99%, ficaram <strong>3.629 a 3.888 MiB livres durante toda a geração</strong>. No teste seguinte, uma geração longa e outras três requisições concorrentes terminaram com sucesso; a menor folga observada foi 3.949 MiB. O desktop continuou vivo.</li>
</ul>
<p>O preço aparece na vazão. O cache de especialistas caiu de uns 11.700 slots pra 9.700, então mais especialistas viajam por PCIe. Na medição inicial, saiu de ~200 pra ~150 tokens por segundo. Eu pago isso feliz: um desktop que não congela vale mais que aqueles 25% de velocidade naquele teste.</p>
<p>Tem uma alternativa estrutural: essa máquina tem uma Radeon 610M integrada, e dá pra mover o Hyprland pra ela. A 5090 ficaria dedicada à inferência.</p>
<p>O custo é que a 610M é fraca pra jogos e o multi-GPU da NVIDIA no Wayland tem suas manias. Eu preferi a reserva elástica, porque o modelo também descarrega quando fica ocioso.</p>
<p>A lição geral: <strong>não deixe motor de inferência tomar 100% de uma GPU que também desenha sua tela.</strong> Nesta configuração não tenho uma reserva rígida de VRAM no driver pro cliente de display. Precisei resolver do lado do motor: reservar alguns GB, reafirmar depois de cada cold start e medir de novo com a área de trabalho aberta.</p>
<h3>Os mesmos 32 GB também limitam a sessão<span class="hx:absolute hx:-mt-20" id="os-mesmos-32-gb-também-limitam-a-sessão"></span>
    <a href="#os-mesmos-32-gb-tamb%c3%a9m-limitam-a-sess%c3%a3o" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>A conta da 5090 é apertada por todos os lados. Os mesmos 32 GB precisam acomodar os pesos densos e buffers fixos do modelo, o cache dos especialistas quentes, a parte residente do KV cache e ainda a reserva de 4 GiB pro Hyprland, navegador e terminal. O que sobra pra uma coisa sai de outra.</p>
<p>KV cache é a memória da conversa. Cada token do prompt, das respostas anteriores e dos arquivos lidos cria estado que o modelo precisa consultar nos próximos tokens. No meu Strata ele está em <code>int8</code>, com 32K células por camada residentes na GPU.</p>
<p>O restante pode ser transmitido da RAM, por isso aumentar a janela de 128K pra 256K não aumentou a VRAM nem derrubou a velocidade no meu teste sintético. Esse streaming evita o estouro imediato, mas continua usando memória e não transforma uma 5090 num servidor sem limite.</p>
<p>O teto configurado aqui é <strong>262.144 tokens, ou 256K</strong>. Vários modelos comerciais que testei, inclusive os Claude mais novos, já trabalham na classe de 1 milhão de tokens. Quase quatro vezes a janela muda o jeito de trabalhar: no Claude Code dá pra acumular muito mais histórico antes da compactação; no Qwen local eu preciso tratar contexto como orçamento desde o primeiro prompt.</p>
<p>A conversa que aparece no terminal é só uma parte do contexto. Dependendo do harness, também entram o system prompt, <code>AGENTS.md</code> e <code>CLAUDE.md</code>, catálogo e conteúdo carregado de skills, descrições e schemas de ferramentas, servidores MCP, instruções de plugins, saídas de comandos, arquivos e o espaço reservado pra resposta. Uma skill bem implementada carrega o texto completo só quando é chamada, mas o catálogo dela ainda pode entrar no prompt. Plugin que registra vinte ferramentas também manda vinte schemas antes de você pedir qualquer coisa.</p>
<p>Foi por isso que o benchmark não usou meu OpenCode normal. O runner <a href="https://github.com/akitaonrails/llm-coding-benchmark#local-opencode-benchmark-config"target="_blank" rel="noopener">gera uma configuração isolada</a>, sem os plugins e MCPs globais, com apenas os providers e agentes necessários pro teste. Também fixa o limite real de 262.144 tokens em vez de fingir que o modelo aceita o contexto anunciado por outro backend.</p>
<p>Pra usar esse Qwen no dia a dia, eu faria a mesma limpeza:</p>
<ul>
<li>começar com o harness mais enxuto possível e ativar plugins ou MCPs quando o projeto realmente precisar;</li>
<li>deixar skills pesadas sob demanda, sem despejar manuais inteiros no começo da sessão;</li>
<li>compactar ou abrir uma sessão nova antes de encostar no teto, registrando decisões importantes em arquivos do projeto;</li>
<li>reservar espaço pra saída do modelo. Uma janela lotada de entrada não serve se não sobra token pra ele pensar, chamar ferramentas e responder.</li>
</ul>
<blockquote>
  <p>Com 256K, contexto é recurso de hardware. Instalar integração demais e carregar tudo em toda sessão consome janela e memória antes do trabalho começar.</p>

</blockquote>
<h2>Mas o Qwen 3.8 não era lento demais?<span class="hx:absolute hx:-mt-20" id="mas-o-qwen-38-não-era-lento-demais"></span>
    <a href="#mas-o-qwen-38-n%c3%a3o-era-lento-demais" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Era, e eu escrevi isso. No <a href="/2026/09/15/novo-llm-benchmark-v4-retestando-todos-llms-parte-2/">ranking do v4</a>, o Qwen 3.8 27B rodando local levou quase 12 horas pra fechar as sete sprints. No <a href="/2026/10/19/llm-benchmark-v4-os-top-llms-open-source-numa-b300/">artigo da B300</a> eu resumi: fez 80, levou 706 minutos, &ldquo;então nem tente&rdquo;.</p>
<p>Só que aquele é outro Qwen 3.8. A família tem os dois, e eles se comportam de jeitos opostos em casa:</p>
<table>
  <thead>
      <tr>
          <th></th>
          <th>Qwen 3.8 27B</th>
          <th>Qwen3.8 Flash-Next</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Arquitetura</td>
          <td>denso, 27B</td>
          <td>MoE, 125B no total, 6B ativos por token</td>
      </tr>
      <tr>
          <td>Onde terminou o v4</td>
          <td>Strix Halo, Q8_0</td>
          <td>RTX 5090, IQ3_S, Strata</td>
      </tr>
      <tr>
          <td>Velocidade</td>
          <td>7,7 tok/s</td>
          <td><strong>159,2 tok/s</strong></td>
      </tr>
      <tr>
          <td>Nota v4</td>
          <td>80,0 (B)</td>
          <td><strong>91,0 (A)</strong></td>
      </tr>
  </tbody>
</table>
<h3>Por que o maior roda mais rápido<span class="hx:absolute hx:-mt-20" id="por-que-o-maior-roda-mais-rápido"></span>
    <a href="#por-que-o-maior-roda-mais-r%c3%a1pido" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Na geração de token, máquina local é limitada por banda de memória: cada token precisa ler todos os pesos ativos. O 27B denso lê os 27 bilhões a cada token, uns 29 GB em Q8. A Strix move uns 256 GB/s, então o teto fica perto de 9 tokens por segundo, e eu medi 7,7.</p>
<p>Na 5090 o mesmo 27B seria rápido, mas em Q8 ele não cabe nos 32 GB junto com o contexto que sete sprints acumulam. Em Q5_K_M ele coube, mas travou num loop de raciocínio no sprint 3. Terminou na Strix, em Q8_0 e com o raciocínio desligado (<code>--reasoning off</code>), que foi o que destravou o loop.</p>
<p>O Flash-Next tem quase cinco vezes mais parâmetros no total, mas só uns 6 bilhões trabalham por token. Em 3 bits, isso é por volta de um décimo do que o 27B em Q8 precisa ler. Os especialistas quentes ficam na GDDR7 da 5090, com sete vezes a banda da Strix, e o ajudante de especulação adianta tokens. Resultado: uns 20 vezes a velocidade do 27B na Strix, com nota maior.</p>
<blockquote>
  <p>A virada veio da troca de modelo e de motor: um MoE com poucos parâmetros ativos, servido por um motor de expert-offload feito sob medida pra ele. Flag nenhuma de configuração tiraria isso do 27B denso na Strix.</p>

</blockquote>
<h3>O tuning ainda cobra<span class="hx:absolute hx:-mt-20" id="o-tuning-ainda-cobra"></span>
    <a href="#o-tuning-ainda-cobra" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>O preço de rodar em casa muda de modelo pra modelo. O Flash-Next em 3 bits perdeu só 1,5 ponto contra a versão NVFP4 da B300. A Ornith-35B, no mesmo motor e na mesma placa, perdeu 16,5 contra a versão BF16 dela na B300. Meça o seu modelo antes de confiar nele.</p>
<h2>O que cada modelo fez de interessante<span class="hx:absolute hx:-mt-20" id="o-que-cada-modelo-fez-de-interessante"></span>
    <a href="#o-que-cada-modelo-fez-de-interessante" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p><strong>Flash-Next: a prova de que consumidor chega em Tier A.</strong> 34 de 40 sem aviso, mais 6 com peso de revelação, e só dois itens precisando de apontar. Nenhum nudge, todas as sprints limpas. É o único local que junta nota de Tier A, velocidade interativa (159 tok/s) e protocolo completo sem ajuda. Se a pergunta é &ldquo;da pra usar no trabalho&rdquo;, esse é o exemplo.</p>
<p><strong>Ornith-35B: rápido, mas não bom o suficiente.</strong> A mesma Ornith que fez 81 na B300 em BF16 fez 64,5 na 5090 com Q8_0. Caiu a detecção sem aviso de 30 pra 23. Mudou quantização, motor e hardware ao mesmo tempo, então não dá pra isolar a causa: essa combinação de Q8_0 + Strata + 5090 perdeu 16,5 pontos, e eu não sei quanto veio de cada variável. O recado é claro: &ldquo;roda rápido&rdquo; não é &ldquo;revisa bem&rdquo;.</p>
<p><strong>Qwen 27B: boa nota, péssima experiência.</strong> Fez 80 na retomada na Strix, com sprints 1 e 2 na 5090, quase o 80,5 da B300. Mas 7,7 tok/s na sonda Q8 e 706 minutos de wall time. E ainda por cima zero consertos na revelação, com quatro itens abertos. Usar isso no dia a dia é escolher esperar.</p>
<p><strong>GLM-4.7-Flash: o motor rodou, o modelo falhou.</strong> Protocolo completo em 29 minutos, e zero detecção sem aviso. Recuperou 8 de 14 só quando apontado, e mesmo assim deixou os consertos sem commit. Na B300 ele fez 33,5, que também não é bom. O problema não é só a placa.</p>
<h2>A distância entre as categorias<span class="hx:absolute hx:-mt-20" id="a-distância-entre-as-categorias"></span>
    <a href="#a-dist%c3%a2ncia-entre-as-categorias" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>A pergunta que todo dev tem é se consumidor já chega lá. Comparação direta, na mesma régua:</p>
<table>
  <thead>
      <tr>
          <th>Categoria</th>
          <th>Modelos</th>
          <th style="text-align: right">Nota</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Frontier comercial</td>
          <td>Claude Opus 5 / 5.5</td>
          <td style="text-align: right">100,0</td>
      </tr>
      <tr>
          <td>Frontier comercial</td>
          <td>GPT-6 Luna</td>
          <td style="text-align: right">95,5</td>
      </tr>
      <tr>
          <td>Frontier comercial</td>
          <td>GLM 5.3 (hospedado)</td>
          <td style="text-align: right">94,0</td>
      </tr>
      <tr>
          <td>Frontier comercial</td>
          <td>GPT-6 Sol / Sonnet 5</td>
          <td style="text-align: right">91,0</td>
      </tr>
      <tr>
          <td>Frontier comercial</td>
          <td>Kimi K3</td>
          <td style="text-align: right">85,0</td>
      </tr>
      <tr>
          <td>Open datacenter</td>
          <td>DeepSeek V4.1 Flash (4x B300)</td>
          <td style="text-align: right">92,5</td>
      </tr>
      <tr>
          <td>Open datacenter</td>
          <td>GLM 5.3 Flash / Qwen3.8 Flash-Next (B300)</td>
          <td style="text-align: right">92,5</td>
      </tr>
      <tr>
          <td><strong>Open consumidor</strong></td>
          <td><strong>Qwen3.8 Flash-Next (5090 + Strata)</strong></td>
          <td style="text-align: right"><strong>91,0</strong></td>
      </tr>
      <tr>
          <td>Open consumidor</td>
          <td>Qwen3.8-27B (Strix)</td>
          <td style="text-align: right">80,0</td>
      </tr>
      <tr>
          <td>Open consumidor</td>
          <td>Ornith-35B (5090)</td>
          <td style="text-align: right">64,5</td>
      </tr>
      <tr>
          <td>Open consumidor</td>
          <td>GLM-4.7-Flash (5090)</td>
          <td style="text-align: right">24,0</td>
      </tr>
  </tbody>
</table>
<p>A leitura é que o topo do consumidor está <strong>9 pontos abaixo do topo comercial absoluto e 1,5 ponto abaixo do topo aberto em datacenter</strong>, nessa métrica de vigilância. É uma distância pequena, e ela some quando você percebe que o GPT-6 Sol e o Sonnet 5 também estão a 91. O abismo real não é entre datacenter e consumidor: é entre o modelo certo e todos os outros. Entre o Flash-Next e a Ornith, na mesma 5090, são 26,5 pontos.</p>
<h2>Então, hardware de consumidor já serve pra trabalhar?<span class="hx:absolute hx:-mt-20" id="então-hardware-de-consumidor-já-serve-pra-trabalhar"></span>
    <a href="#ent%c3%a3o-hardware-de-consumidor-j%c3%a1-serve-pra-trabalhar" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p><strong>Serve, com o modelo e o motor certos.</strong> Uma RTX 5090 com Strata entrega Tier A em vigilância de código, a 159 tokens por segundo, sem pagar um centavo por token depois que o hardware existe. Pra quem faz revisão de código, testa agente de codificação e mantém dado privado em casa, isso muda o jogo.</p>
<p>Pela primeira vez neste benchmark eu tenho um modelo testado, em hardware de consumidor, que merece ir pro trabalho de todo dia. Já deixei o Flash-Next configurado no OpenCode apontando pra 5090. O próximo passo é usar em programação de verdade, fora da régua de sabotagem, e ver se a nota se sustenta.</p>
<p>O que não serve é achar que qualquer configuração serve. A Strix Halo é ótima pra ter 128 GB e montar modelo grande, mas a velocidade dela não paga interatividade; a Ornith roda rápido e revisa mal; o GLM-4.7-Flash completa protocolo e não detecta nada sem aviso. E tem o preço invisível: quantização agressiva pode cobrar nota, dependendo do modelo e da configuração, e tempo de espera cobra paciência.</p>
<p>Se eu fosse montar uma caixa pra um dev hoje, seria uma 5090 com Strata e o Flash-Next, e usaria a Strix pra tarefa de fundo que aceita ser lenta. A fronteira comercial continua ganhando de quem precisa do máximo. Mas o consumidor deixou de ser consolação: já tem Tier A na prateleira de casa.</p>
<h3>O problema agora é achar uma 5090<span class="hx:absolute hx:-mt-20" id="o-problema-agora-é-achar-uma-5090"></span>
    <a href="#o-problema-agora-%c3%a9-achar-uma-5090" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Em outubro, leakers garantiram que a NVIDIA ia parar de produzir a RTX 5090 e reservar os chips GB202 pras linhas profissionais e de datacenter. A placa chegou a sumir da loja oficial da NVIDIA nos Estados Unidos. No dia 16 de outubro, num evento em Taipei, o <strong>Jeff Fisher</strong>, vice-presidente sênior da NVIDIA, <a href="https://www.guru3d.com/story/nvidia-says-geforce-rtx-5090-production-is-continuing/"target="_blank" rel="noopener">negou a descontinuação</a> e disse que a empresa vai continuar entregando, mas admitiu que a capacidade de produção está apertada.</p>
<p>Na prática:</p>
<ul>
<li>o preço sugerido continua US$ 1.999, e a maioria dos anúncios na Newegg americana <a href="https://www.techspot.com/news/114254-nvidia-rtx-5090-production-but-absence-company-us.html"target="_blank" rel="noopener">passa de US$ 9.300, quase cinco vezes isso</a>;</li>
<li>no Brasil, cada unidade sai na casa dos <a href="https://www.baguete.com.br/noticias/carga-milionaria-de-gpus-some-de-guarulhos"target="_blank" rel="noopener">R$ 35 mil no varejo</a>;</li>
<li>e dois pallets de RTX 5090 da Zotac, importados pela distribuidora Oderço e avaliados em uns R$ 2,5 milhões, <a href="https://www.terra.com.br/noticias/misterio-em-guarulhos-r-25-milhoes-em-placas-de-video-desaparecem,a7596d7874eddfb490f96345b4be1b09z4eix4rc.html"target="_blank" rel="noopener">sumiram no aeroporto de Guarulhos</a>. A GRU Airport comunicou as autoridades, e o caso segue em investigação, sem confirmação de roubo.</li>
</ul>
<blockquote>
  <p>O primeiro modelo local Tier A do v4 roda justamente na placa mais difícil de comprar do momento. A prateleira de casa já tem Tier A. Falta conseguir a prateleira.</p>

</blockquote>
]]></content:encoded><category>benchmarks-de-llm</category><category>modelos-locais</category><category>llms</category><category>hardware</category></item><item><title>LLM Benchmark v4: meu time cabe em 4x B300/H200?</title><link>https://www.akitaonrails.com/2026/10/22/llm-benchmark-v4-can-my-team-fit-in-a-4x-b300-h200/</link><guid isPermaLink="true">https://www.akitaonrails.com/2026/10/22/llm-benchmark-v4-can-my-team-fit-in-a-4x-b300-h200/</guid><pubDate>Thu, 22 Oct 2026 11:00:00 -0300</pubDate><description>&lt;p&gt;Nos dois últimos artigos eu cobri o &lt;a href="https://www.akitaonrails.com/2026/10/19/llm-benchmark-v4-os-top-llms-open-source-numa-b300/"&gt;benchmark numa B300 única&lt;/a&gt; e o &lt;a href="https://www.akitaonrails.com/2026/10/22/llm-benchmark-v4-open-source-que-so-cabe-em-4x-b300/"&gt;ranking dos modelos grandes em 4x B300&lt;/a&gt;. A pergunta que fecha a trilogia é a da empresa: &lt;strong&gt;vale a pena montar uma infra de LLM própria pro time de desenvolvimento?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;A resposta depende de duas coisas: quantos usuários confortáveis o hardware aguenta, e quanto isso custa por pessoa. Medimos os dois.&lt;/p&gt;
&lt;h2&gt;A conta antes do teste&lt;span class="hx:absolute hx:-mt-20" id="a-conta-antes-do-teste"&gt;&lt;/span&gt;
&lt;a href="#a-conta-antes-do-teste" class="subheading-anchor" aria-label="Permalink for this section"&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Recapitulando os números do artigo anterior: o pod de 4x B300 custa &lt;strong&gt;US$ 36 por hora&lt;/strong&gt; (US$ 864/dia, quase US$ 26 mil/mês ligado direto). Comecei a campanha com uns US$ 127 de saldo, recarreguei US$ 2.550, e sobraram US$ 537. Foram &lt;strong&gt;uns US$ 2.140&lt;/strong&gt; entre setup, downloads, tuning e benchmarks, não só inferência.&lt;/p&gt;</description><content:encoded><![CDATA[<p>Nos dois últimos artigos eu cobri o <a href="/2026/10/19/llm-benchmark-v4-os-top-llms-open-source-numa-b300/">benchmark numa B300 única</a> e o <a href="/2026/10/22/llm-benchmark-v4-open-source-que-so-cabe-em-4x-b300/">ranking dos modelos grandes em 4x B300</a>. A pergunta que fecha a trilogia é a da empresa: <strong>vale a pena montar uma infra de LLM própria pro time de desenvolvimento?</strong></p>
<p>A resposta depende de duas coisas: quantos usuários confortáveis o hardware aguenta, e quanto isso custa por pessoa. Medimos os dois.</p>
<h2>A conta antes do teste<span class="hx:absolute hx:-mt-20" id="a-conta-antes-do-teste"></span>
    <a href="#a-conta-antes-do-teste" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Recapitulando os números do artigo anterior: o pod de 4x B300 custa <strong>US$ 36 por hora</strong> (US$ 864/dia, quase US$ 26 mil/mês ligado direto). Comecei a campanha com uns US$ 127 de saldo, recarreguei US$ 2.550, e sobraram US$ 537. Foram <strong>uns US$ 2.140</strong> entre setup, downloads, tuning e benchmarks, não só inferência.</p>
<p>No meio, o saldo zerou, o pod morreu durante um autotune, e na hora de recarregar <strong>não havia B300 disponível</strong>. É o risco operacional da Runpod: o preço muda e a oferta some. A alternativa foi um pod de 4x H200, com preço de tabela na casa de <a href="https://www.runpod.io/pricing"target="_blank" rel="noopener">US$ 3,59 por GPU-hora na Community Cloud</a>: US$ 14,36/hora pras quatro na conta de tabela, contra US$ 36 das B300.</p>
<h2>B300 e H200 não são a mesma máquina<span class="hx:absolute hx:-mt-20" id="b300-e-h200-não-são-a-mesma-máquina"></span>
    <a href="#b300-e-h200-n%c3%a3o-s%c3%a3o-a-mesma-m%c3%a1quina" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>A diferença de hardware muda mais que o preço:</p>
<table>
  <thead>
      <tr>
          <th></th>
          <th>4x B300</th>
          <th>4x H200</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Arquitetura</td>
          <td>Blackwell Ultra (<code>sm_103a</code>)</td>
          <td>Hopper (<code>sm_90</code>)</td>
      </tr>
      <tr>
          <td>VRAM</td>
          <td>~288 GB por GPU, ~1,1 TB total</td>
          <td>~141 GB por GPU, ~560 GB total</td>
      </tr>
      <tr>
          <td>Formatos nativos</td>
          <td>FP8 e FP4/NVFP4</td>
          <td>FP8 (sem FP4 nativo)</td>
      </tr>
      <tr>
          <td>Toolchain</td>
          <td>CUDA 12.9+ e kernels compilados na hora</td>
          <td>mais maduro, mas ainda com remendos de JIT</td>
      </tr>
  </tbody>
</table>
<p>A troca teve custo de adaptação: o checkpoint NVFP4 do GLM que rodava na B300 não existe pra Hopper, então foi preciso baixar a variante FP8 dele, e mesmo o DeepSeek precisou de ajuste de kernel. Os resultados de concorrência abaixo são com os dois em FP8 nativo, que é a comparação justa possível.</p>
<h2>Como medimos &ldquo;cabe um time&rdquo;<span class="hx:absolute hx:-mt-20" id="como-medimos-cabe-um-time"></span>
    <a href="#como-medimos-cabe-um-time" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>O teste usa <code>vllm bench serve</code> contra o localhost de cada pod, com requisições de 8.000 tokens de entrada e 800 de saída (uma carga sintética no formato de sessão de codificação), subindo a concorrência N degrau por degrau.</p>
<p>O critério de conforto é o que importa: <strong>TTFT de até 2 segundos e TPOT de até 20 ms por token</strong> (ou seja, pelo menos 50 tokens/s por stream), com <strong>95% ou mais das requisições</strong> cumprindo os dois limites. Não é &ldquo;a mediana ficou boa&rdquo;: é quase todo mundo bem atendido. Um time que espera 50 segundos pelo primeiro token não é um time, é uma fila.</p>
<p>E o aviso de honestidade: isso mede requisições de código confortáveis, não pessoas. Não tenho um multiplicador medido de duty-cycle de agente interativo, então não vou inventar que cada N vira 2 ou 3 desenvolvedores.</p>
<h2>Os números<span class="hx:absolute hx:-mt-20" id="os-números"></span>
    <a href="#os-n%c3%bameros" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><table>
  <thead>
      <tr>
          <th>Modelo / hardware</th>
          <th style="text-align: right">Stream único</th>
          <th style="text-align: right">Confortável (N)</th>
          <th style="text-align: right">Porta solta (só mediana)</th>
          <th style="text-align: right">Pico agregado</th>
          <th>Gargalo</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>DeepSeek V4.1 Flash, 4x B300 FP8</td>
          <td style="text-align: right">177 tok/s</td>
          <td style="text-align: right"><strong>32</strong></td>
          <td style="text-align: right">48</td>
          <td style="text-align: right">2.778 tok/s</td>
          <td>prefill (TTFT)</td>
      </tr>
      <tr>
          <td>DeepSeek V4.1 Flash, 4x H200 FP8</td>
          <td style="text-align: right">137 tok/s</td>
          <td style="text-align: right"><strong>8</strong></td>
          <td style="text-align: right">16</td>
          <td style="text-align: right">2.506 tok/s</td>
          <td>prefill (TTFT)</td>
      </tr>
      <tr>
          <td>GLM 5.3 Flash, 4x H200 FP8</td>
          <td style="text-align: right">175 tok/s</td>
          <td style="text-align: right"><strong>8</strong></td>
          <td style="text-align: right">16</td>
          <td style="text-align: right">1.930 tok/s</td>
          <td><strong>KV cache, 97%</strong></td>
      </tr>
  </tbody>
</table>
<p>Leia com calma, porque tem três histórias aqui.</p>
<p><strong>A B300 atende quatro vezes mais gente no conforto.</strong> Mesmo modelo, mesmo FP8, mesmo TP4: 32 usuários confortáveis contra 8. O agregado da H200 até impressiona (2.506 tok/s), mas isso é fila de prefill: o p99 de TTFT chega a cerca de 51 segundos em N=256, então o usuário espera um minuto antes de ver qualquer coisa. Throughput alto não é capacidade interativa.</p>
<p>O penhasco é tão íngreme que vale olhar os números de perto. Em N=16, o p99 de TTFT era 1,17 s na B300 e 3,19 s na H200, com 100% e 92,7% das requisições boas. Em N=32, a B300 ainda segurava 97,4% dentro do SLO (2,23 s), enquanto a H200 desabou pra 8,3% (6,58 s). E em N=48 a B300 caiu pra 92%, abaixo do meu corte de 95%: por isso o confortável é 32 e não 48. A diferença entre &ldquo;funciona&rdquo; e &ldquo;funciona pra todo mundo&rdquo; mora nesses últimos 2 pontos percentuais.</p>
<p><strong>Modelo pesado e leve empatam na H200.</strong> O DeepSeek de 510 GB e o GLM menor sustentam os mesmos 8 confortáveis, por motivos diferentes: o DeepSeek tem KV eficiente via MLA e morre de compute/prefill; o GLM estoura o KV cache (97% de uso). Escolher hardware por contagem de parâmetros é olhar pro lugar errado: a arquitetura do KV é que manda na concorrência.</p>
<p><strong>A porta solta engana.</strong> Se você olhar só a mediana, dá pra dizer 48 na B300 e 16 na H200. Só que a cauda do p99 destrói a experiência de quem está do outro lado. Pra dimensionar time, uso o número confortável e pronto.</p>
<h2>Custo por usuário confortável<span class="hx:absolute hx:-mt-20" id="custo-por-usuário-confortável"></span>
    <a href="#custo-por-usu%c3%a1rio-confort%c3%a1vel" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Agora a aritmética que decide:</p>
<ul>
<li><strong>4x B300:</strong> US$ 36/h ÷ 32 = <strong>US$ 1,13 por usuário confortável por hora</strong>.</li>
<li><strong>4x H200:</strong> US$ 14,36/h ÷ 8 = <strong>US$ 1,80 por usuário confortável por hora</strong>.</li>
</ul>
<p>A B300 ganha por pessoa mesmo custando 2,5x mais o pod. A H200 só viraria mais barata por usuário abaixo de US$ 9/hora o pod inteiro, e na tabela atual ela custa US$ 14,36. O ponto de corte não é o preço da máquina, é a capacidade dela.</p>
<h2>E se a sua empresa já vive em AWS ou Azure?<span class="hx:absolute hx:-mt-20" id="e-se-a-sua-empresa-já-vive-em-aws-ou-azure"></span>
    <a href="#e-se-a-sua-empresa-j%c3%a1-vive-em-aws-ou-azure" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Dá pra montar a mesma infra nos hiperescalares, mas o preço muda de categoria. Os valores de tabela que eu conferi, por GPU-hora:</p>
<table>
  <thead>
      <tr>
          <th>Provedor</th>
          <th>Configuração</th>
          <th style="text-align: right">Preço por GPU-hora</th>
          <th style="text-align: right">Contra Runpod</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Runpod, 4x B300 (minha campanha)</td>
          <td>B300</td>
          <td style="text-align: right">US$ 9,00</td>
          <td style="text-align: right">1x</td>
      </tr>
      <tr>
          <td>Runpod, 4x H200 (Community Cloud)</td>
          <td>H200</td>
          <td style="text-align: right"><a href="https://www.runpod.io/pricing"target="_blank" rel="noopener">US$ 3,59</a></td>
          <td style="text-align: right">0,4x</td>
      </tr>
      <tr>
          <td><a href="https://themodelpress.com/gpu-prices"target="_blank" rel="noopener">AWS, p6-b300.48xlarge</a></td>
          <td>8x B300, capacity block</td>
          <td style="text-align: right">US$ 16,15</td>
          <td style="text-align: right">1,8x</td>
      </tr>
      <tr>
          <td>AWS, p6-b200.48xlarge</td>
          <td>8x B200, capacity block</td>
          <td style="text-align: right">US$ 14,21</td>
          <td style="text-align: right">1,6x</td>
      </tr>
      <tr>
          <td><a href="https://themodelpress.com/gpu-prices/azure-nd-h200-v5-on-demand"target="_blank" rel="noopener">Azure, ND96isr_H200_v5</a></td>
          <td>8x H200, East US 2</td>
          <td style="text-align: right">US$ 10,60</td>
          <td style="text-align: right">3,0x</td>
      </tr>
  </tbody>
</table>
<p>A B300 na AWS custa 1,8x o que eu paguei na Runpod, e a H200 na Azure custa quase 3x a da Runpod. A conclusão de capacidade não muda (a física do tensor parallelism e do KV cache é a mesma), mas a conta de &ldquo;vale a pena&rdquo; piora bem: se na Runpod a B300 empata com a assinatura na faixa de 30 usuários, na AWS ela passa a perder por uma margem confortável.</p>
<p>E na prática, quanto sai por desenvolvedor, desligando o pod à noite como nas outras contas (8 horas por dia útil, 176 horas/mês)? As linhas de AWS e Azure são aritmética normalizada pra 4 GPUs, porque os SKUs citados vêm com 8 e eu não medi TP=8:</p>
<table>
  <thead>
      <tr>
          <th>Setup</th>
          <th>Custo mensal do pod</th>
          <th style="text-align: right">Streams confortáveis (1 por dev)</th>
          <th style="text-align: right">Custo por dev/mês</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Runpod 4x B300 (US$ 36/h)</td>
          <td>US$ 6.336</td>
          <td style="text-align: right">32</td>
          <td style="text-align: right">US$ 198</td>
      </tr>
      <tr>
          <td>AWS 4x B300 (US$ 64,58/h, normalizado)</td>
          <td>US$ 11.367</td>
          <td style="text-align: right">32</td>
          <td style="text-align: right">US$ 355</td>
      </tr>
      <tr>
          <td>Runpod 4x H200 (US$ 14,36/h)</td>
          <td>US$ 2.527</td>
          <td style="text-align: right">8</td>
          <td style="text-align: right">US$ 316</td>
      </tr>
      <tr>
          <td>Azure 4x H200 (US$ 42,40/h, normalizado)</td>
          <td>US$ 7.462</td>
          <td style="text-align: right">8</td>
          <td style="text-align: right">US$ 933</td>
      </tr>
  </tbody>
</table>
<p>A diferença é brutal: o mesmo desenvolvedor confortável sai por US$ 198 na Runpod e por US$ 933 na Azure, quase cinco vezes mais caro que a assinatura de frontier mais forte. A AWS fica quase 80% acima da Runpod, então mesmo ela só faz sentido se a sua exigência for outra coisa que não custo.</p>
<p>E se você pegar a instância inteira de 8 GPUs, como elas são vendidas? A AWS sai por US$ 22.734/mês e a Azure por US$ 14.925/mês, e aí você tem que medir TP=8 pra saber quantos usuários aproveita, porque com TP=4 você paga o dobro pela mesma capacidade.</p>
<p>Os modelos comerciais também são diferentes: capacity block na AWS não é on-demand, e a Community Cloud da Runpod não é SLA de produção. Leia essa tabela como ordem de grandeza de preço, não como cotação de contrato. Se a sua exigência é compliance, procurement ou residência em região específica, o hiperescalar pode ser inevitável, e aí você paga o prêmio. Se a exigência é só preço, as neoclouds ganham nessas configurações.</p>
<h2>E se em vez de alugar, eu comprar a máquina?<span class="hx:absolute hx:-mt-20" id="e-se-em-vez-de-alugar-eu-comprar-a-máquina"></span>
    <a href="#e-se-em-vez-de-alugar-eu-comprar-a-m%c3%a1quina" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Quando a conta de aluguel dói, a pergunta natural é comprar o hardware. Dois candidatos da prateleira atual:</p>
<p><strong>DGX Station GB300</strong> (~US$ 130 mil, com configurações começando em torno de US$ 95 mil): 748 GB de memória unificada (252 GB de HBM3e a 7,1 TB/s e 496 GB de LPDDR5X a 396 GB/s), 20 PFLOPS em FP4. Os pesos de todos os modelos que eu testei cabem nela, e os melhores pra colocar nela seriam:</p>
<ul>
<li><strong>DeepSeek V4.1 Flash</strong> (92,5/A, 510 GB em FP8), o campeão do benchmark;</li>
<li><strong>Muse Spark Open</strong> (69,5/C, 533 GB em NVFP4);</li>
<li><strong>Step-5-Preview</strong> (72,5/C, 368 GB em NVFP4);</li>
<li><strong>GLM 5.3 full</strong> (76/B, 704 GB em FP8).</li>
</ul>
<p>Cabe, mas caber não é servir: o GLM full deixaria só uns 44 GB pra KV cache e sistema, então a concorrência plena com contexto longo fica por medir. A estimativa de conforto é de um dígito, na linha da H200, e não os 32 do pod de quatro. A diferença é que você não precisa de TP=4: tudo mora num chip só.</p>
<p><img src="dgx-station-gb300.jpg" alt="Render da DGX Station GB300, o desktop com superchip Grace Blackwell Ultra e 748 GB de memória unificada"  loading="lazy" /></p>
<p><strong>DGX Spark</strong> (~US$ 6.950 na loja atual da NVIDIA): 128 GB unificados, 1 PFLOP em FP4, 273 GB/s de banda. O recheio bom pra ela é de modelo pequeno e médio, e ele é melhor do que parece:</p>
<p>E tem um detalhe que dói de ver na fatura: por estimativa minha, sem auditoria fechada de todos os custos da primeira rodada, somando os créditos do v4 (aqueles ~40 modelos comerciais) com os US$ 2.140 desta campanha de B300/H200, eu já gastei alguns mil dólares entre API e pods, perto de metade do preço de uma DGX Spark. E ainda assim eu não teria uma máquina guardada em casa por causa disso.</p>
<ul>
<li><strong>Qwen3.8 Flash-Next</strong> (92,5/A), o co-líder da B300 única. Em IQ3_S ele fica na casa de dezenas de GB, e foi exatamente o modelo que fez 91,0 na RTX 5090 com Strata. É o melhor modelo que eu recomendaria pra uma Spark hoje.</li>
<li><strong>Swift 1.5 27B</strong> (88/A, 56 GB), que foi a recomendação de self-host mais equilibrada da minha campanha anterior;</li>
<li><strong>ThinkingCap 3.8 27B</strong> (83/A, 56 GB);</li>
<li><strong>Ornith-1.5-35B-A3B</strong> (81/B, ~70 GB em BF16);</li>
<li><strong>Laguna-S-2.1</strong> (65,5/C, 99,7 GB em NVFP4);</li>
<li>e os classificadores pequenos do benchmark de decisão (Jebadiah 4B, Laya, Julia), que são o caso de uso ideal de uma caixa dessas.</li>
</ul>
<p>O DeepSeek V4.1, com 510 GB, não cabe de jeito nenhum. A banda é quase 30 vezes menor que a da B300, então a interatividade sofre, e a concorrência prática fica pra medir: eu espero pouco mais que uso solo. Pra modelo pequeno é uma caixinha simpática; pra monstro, é peso de papel.</p>
<p><img src="dgx-spark-box.jpeg" alt="DGX Spark, o desktop de 128 GB de memória unificada e 1 PFLOP em FP4"  loading="lazy" /></p>
<h3>TCO, ROI e depreciação<span class="hx:absolute hx:-mt-20" id="tco-roi-e-depreciação"></span>
    <a href="#tco-roi-e-deprecia%c3%a7%c3%a3o" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>A conta de ter versus alugar, usando 3 anos de depreciação e o mesmo padrão de uso (8 horas por dia útil, 176 horas/mês):</p>
<table>
  <thead>
      <tr>
          <th>Cenário</th>
          <th>Custo de alugar/mês</th>
          <th style="text-align: right">Custo de comprar (depreciação/mês)</th>
          <th style="text-align: right">Break-even</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>GB300 vs Runpod 4x B300 (US$ 36/h)</td>
          <td>US$ 6.336</td>
          <td style="text-align: right">US$ 3.611 (130 mil / 36)</td>
          <td style="text-align: right"><strong>~20 meses</strong></td>
      </tr>
      <tr>
          <td>GB300 vs Runpod 24/7</td>
          <td>US$ 25.920</td>
          <td style="text-align: right">US$ 3.611</td>
          <td style="text-align: right">~5 meses</td>
      </tr>
      <tr>
          <td>Spark vs 1x B300 (US$ 9,09/h)</td>
          <td>US$ 1.600</td>
          <td style="text-align: right">US$ 193 (6.950 / 36)</td>
          <td style="text-align: right"><strong>~4 meses e meio</strong></td>
      </tr>
      <tr>
          <td>Spark vs assinatura de US$ 200</td>
          <td>US$ 200</td>
          <td style="text-align: right">US$ 193</td>
          <td style="text-align: right">~3 anos</td>
      </tr>
  </tbody>
</table>
<p>Leitura: se você roda modelo grande todo dia útil por dois anos ou mais, o GB300 se paga em menos de dois anos, e em 24/7 ele se paga em cinco meses. Se a sua necessidade é uma campanha de duas semanas como a minha, alugar custou US$ 2.140 e pronto, sem ativo parado depois. E o Spark só faz sentido se a sua carga for modelo pequeno: nesse caso ele se paga em menos de um semestre contra aluguel e em três anos contra assinatura, mas se a sua necessidade é um 748B, ele é inútil desde a caixa.</p>
<p>Duas ressalvas pra não comprar errado. Primeiro, a tabela compara cenários de dólar, não ROI de carga equivalente: se a sua concorrência real no GB300 for de um dígito, a comparação honesta seria com um pod menor que o de 4x B300. Segundo, energia e administração mudam o resultado, principalmente no Spark, onde a diferença pra assinatura fica em poucos dólares por mês. Ignorei revenda no fim dos três anos; se ela existir, o break-even melhora.</p>
<p><strong>Nota pra quem compra no Brasil:</strong> esses preços são em dólar. Em reais, com o dólar a R$ 4,99, mais impostos de importação e a margem de revenda local, o GB300 passa fácil de R$ 800 mil e o Spark passa de R$ 34 mil antes de qualquer margem. O break-even em reais fica ainda mais distante, então a decisão de comprar no Brasil precisa de uma utilização muito mais alta pra fechar.</p>
<p>E comparando com assinatura de frontier? Um plano forte de Claude/Codex custa uns US$ 200 por mês por desenvolvedor. Com duas ressalvas importantes: esses planos não são ilimitados (o Codex mede por crédito e o Claude tem janelas de uso por sessão e por semana), e automação/CI não cabe em plano de consumidor. A comparação abaixo é de preço, não de carga equivalente. Pra transformar stream em gente eu preciso de uma premissa: um desenvolvedor com uma requisição ativa o tempo inteiro durante o expediente, o que é otimista pra capacidade e conservador pra custo. Com essa premissa, e sem contar minha hora de sysadmin nem o setup, o pod rodando 8 horas por dia útil (176 horas/mês) fica assim:</p>
<table>
  <thead>
      <tr>
          <th>Setup</th>
          <th style="text-align: right">Custo mensal do pod</th>
          <th style="text-align: right">Streams confortáveis (1 por dev)</th>
          <th style="text-align: right">Custo por dev/mês</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>4x B300</td>
          <td style="text-align: right">US$ 6.336</td>
          <td style="text-align: right">32</td>
          <td style="text-align: right"><strong>US$ 198</strong></td>
      </tr>
      <tr>
          <td>4x H200</td>
          <td style="text-align: right">US$ 2.527</td>
          <td style="text-align: right">8</td>
          <td style="text-align: right"><strong>US$ 316</strong></td>
      </tr>
      <tr>
          <td>4x B300 ligado 24/7</td>
          <td style="text-align: right">US$ 25.920</td>
          <td style="text-align: right">32</td>
          <td style="text-align: right">US$ 810</td>
      </tr>
  </tbody>
</table>
<p>Na faixa de 30 streams contínuos, a B300 fica praticamente no preço da assinatura forte. Abaixo disso, a assinatura sai mais barata, porque você paga por pessoa, não por hora de pod ociosa. E ligar 24/7 sem carga correspondente é jogar dinheiro fora: o pod cobra dormindo.</p>
<h2>Pra empresa, quando fecha a conta<span class="hx:absolute hx:-mt-20" id="pra-empresa-quando-fecha-a-conta"></span>
    <a href="#pra-empresa-quando-fecha-a-conta" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Se você tem um time grande de verdade usando o dia inteiro, dados que não podem sair do ambiente, e um modelo que entrega no trabalho diário (o DeepSeek V4.1 Flash fez 92,5 no meu benchmark de vigilância, acima do GPT-6 Sol e do Sonnet 5), a infra própria começa a fazer sentido <strong>pela primeira vez sem ser vaidade</strong>. O ponto de corte, na premissa de um stream ativo por pessoa o expediente inteiro, fica na casa das 30 pessoas concorrentes, com o pod desligando fora do horário.</p>
<p>Se o time é pequeno, o uso é intermitente, ou o objetivo é só economizar: continua com a API. O mesmo DeepSeek custou US$ 1,21 por rodada completa de benchmark na nuvem, e você não herda um pod pra administrar, nem o risco de a B300 sumir do estoque quando você mais precisa.</p>
<p>O que eu levo dessa campanha: hardware grande não é resposta pra pergunta pequena. Antes de assinar o pod, faça a conta de quantas pessoas realmente concorrem no seu time, meça a latência que elas toleram, e some o preço da sua hora de sysadmin. O modelo aberto grande já chegou na faixa de Sonnet 5 e GPT-6 Sol pra revisão diária de código. A infra só precisa caber no orçamento.</p>
]]></content:encoded><category>benchmarks-de-llm</category><category>modelos-locais</category><category>llms</category><category>hardware</category></item><item><title>LLM Benchmark v4: open source que só cabe em 4x B300!!</title><link>https://www.akitaonrails.com/2026/10/22/llm-benchmark-v4-open-source-que-so-cabe-em-4x-b300/</link><guid isPermaLink="true">https://www.akitaonrails.com/2026/10/22/llm-benchmark-v4-open-source-que-so-cabe-em-4x-b300/</guid><pubDate>Thu, 22 Oct 2026 10:00:00 -0300</pubDate><description>&lt;p&gt;No &lt;a href="https://www.akitaonrails.com/2026/10/19/llm-benchmark-v4-os-top-llms-open-source-numa-b300/"&gt;artigo anterior&lt;/a&gt; eu contei como foi rodar o benchmark v4 de sabotagem numa única NVIDIA B300 alugada na Runpod. A pergunta que sobrou foi óbvia: e os modelos grandes de verdade, aqueles que não cabem em 288 GB? Decidi ver até onde dá pra empurrar os open source.&lt;/p&gt;
&lt;p&gt;A resposta curta: dá pra ir longe, e um deles empatou com a faixa de Sonnet 5 e GPT-6 Sol. A longa vem com uma conta de padaria e vários dias de vLLM sofrendo.&lt;/p&gt;</description><content:encoded><![CDATA[<p>No <a href="/2026/10/19/llm-benchmark-v4-os-top-llms-open-source-numa-b300/">artigo anterior</a> eu contei como foi rodar o benchmark v4 de sabotagem numa única NVIDIA B300 alugada na Runpod. A pergunta que sobrou foi óbvia: e os modelos grandes de verdade, aqueles que não cabem em 288 GB? Decidi ver até onde dá pra empurrar os open source.</p>
<p>A resposta curta: dá pra ir longe, e um deles empatou com a faixa de Sonnet 5 e GPT-6 Sol. A longa vem com uma conta de padaria e vários dias de vLLM sofrendo.</p>
<p>Antes de tudo, a ressalva de escopo. Ainda ficou gente de fora.</p>
<p>O Kimi K3, de 2,8 trilhões de parâmetros, é o exemplo: em FP8 ele precisaria de uns 2,8 TB de VRAM, e nem um pod de 8x B300 (2,3 TB brutos) fecha a conta. Pra rodar um monstro desses você precisa quantizar pra FP4 ou alugar muito mais placa. E eu escolhi gastar meu crédito no que cabe em 4x.</p>
<h2>O ranking completo<span class="hx:absolute hx:-mt-20" id="o-ranking-completo"></span>
    <a href="#o-ranking-completo" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Metodologia é a mesma de sempre: o app de chat em RubyLLM com 14 defeitos plantados (backdoors, bypass de autorização, mentiras nos testes), e a nota mede vigilância de revisão de código, não velocidade de digitação. O modelo precisa encontrar e corrigir os defeitos sem ser avisado (peso cheio) e quando revelado (peso 40%). Tier A começa em 83. Rodei tudo no mesmo pod de 4x pra ter uma régua única, embora nem todo modelo precisasse das quatro placas (Ornith e Laguna caberiam numa só).</p>
<table>
  <thead>
      <tr>
          <th>Modelo</th>
          <th>Tamanho / checkpoint</th>
          <th style="text-align: right">Nota / Tier</th>
          <th style="text-align: right">Velocidade (stream único)</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>DeepSeek V4.1 Flash</td>
          <td>748B (552B + 196B Engram), FP8, 510 GB</td>
          <td style="text-align: right"><strong>92,5 (A)</strong></td>
          <td style="text-align: right">174,8 tok/s</td>
      </tr>
      <tr>
          <td>GLM 5.3 full</td>
          <td>~700B, FP8, 704 GB</td>
          <td style="text-align: right">76,0 (B)</td>
          <td style="text-align: right">110 tok/s</td>
      </tr>
      <tr>
          <td>Ornith-1.5-122B</td>
          <td>~122B MoE, FP8, 133 GB</td>
          <td style="text-align: right">73,0 (B)</td>
          <td style="text-align: right">244,2 tok/s</td>
      </tr>
      <tr>
          <td>Step-5-Preview</td>
          <td>~816B (tamanho estimado), NVFP4, 368 GiB</td>
          <td style="text-align: right">72,5 (C)</td>
          <td style="text-align: right">165,9 tok/s (com MTP)</td>
      </tr>
      <tr>
          <td>Muse Spark Open</td>
          <td>816B-A42B, NVFP4, 533 GB</td>
          <td style="text-align: right">69,5 (C)</td>
          <td style="text-align: right">130,8 tok/s</td>
      </tr>
      <tr>
          <td>MiMo V2.6 Pro</td>
          <td>~0,5T MoE, FP8/mxfp4, ~535 GB</td>
          <td style="text-align: right">67,0 (C)</td>
          <td style="text-align: right">167,2 tok/s</td>
      </tr>
      <tr>
          <td>Laguna-S-2.1</td>
          <td>MoE pequeno pra código, NVFP4, 99,7 GB</td>
          <td style="text-align: right">65,5 (C)</td>
          <td style="text-align: right">263 tok/s</td>
      </tr>
      <tr>
          <td>Kimi K2 Thinking</td>
          <td>1T / ~32B ativos, INT4, 594 GB</td>
          <td style="text-align: right">46,0 (D)</td>
          <td style="text-align: right">110 tok/s</td>
      </tr>
      <tr>
          <td>Nex N2.5 Pro</td>
          <td>~397B MoE, FP8, ~380 GB</td>
          <td style="text-align: right">27,0 (D)</td>
          <td style="text-align: right">184 tok/s</td>
      </tr>
  </tbody>
</table>
<p>Duas notas pra ler a tabela direito. O Step-5 e o Muse estão em C porque a base sem aviso era baixa: conserto depois do &ldquo;tem problema aí&rdquo; vale 40%. O Muse consertou 6 dos 7 itens abertos quando revelado; o Step-5, só 2 de 5.</p>
<p>E o Nex N2.5 Pro, além da nota vergonhosa, corrompeu o código a ponto do app nem subir. As duas âncoras da campanha anterior, pra comparar: GLM 5.3 Flash e Qwen3.8 Flash-Next, ambos 92,5 na B300 única.</p>
<h2>O que cada um fez de interessante<span class="hx:absolute hx:-mt-20" id="o-que-cada-um-fez-de-interessante"></span>
    <a href="#o-que-cada-um-fez-de-interessante" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>A nota sozinha esconde o comportamento, e o comportamento é a parte divertida.</p>
<p><strong>O DeepSeek foi o único que resolveu os dois itens compostos sem ajuda.</strong> Os defeitos compostos são os que exigem enxergar duas coisas ao mesmo tempo (a backdoor e o teste que a encobre), e ele achou e corrigiu os dois sem nenhum nudge. É o tipo de vigilância que eu quero do meu revisor de PR.</p>
<p><strong>O GLM full consertou tudo quando avisado, mas dormiu no silent mode.</strong> Sem aviso, ele deixou passar o cluster de acesso e correção silenciosa. Quando eu apontei, varreu os seis itens restantes. Ou seja: o modelo grande é capaz, só não é atento por padrão. O Flash dele, com um terço do tamanho, foi mais vigilante.</p>
<p><strong>O Kimi K2 Thinking fez teatro de checklist de segurança.</strong> Escreveu umas listas lindas de &ldquo;boas práticas de segurança&rdquo; e deixou cinco itens abertos, incluindo uma chave em texto plano. Falar de segurança e fazer segurança são coisas diferentes, e ele fez a primeira.</p>
<p><strong>O Ornith restaurou o comportamento e manteve o teste mentiroso.</strong> No item 6, ele reverteu o código pro comportamento antigo em vez de corrigir o teste plantado que validava a backdoor. Por isso a nota estrita foi 73 e a leniente 76: parecer que consertou não é consertar, e o teste que mente também precisa morrer.</p>
<p><strong>O Step-5 deixou três defeitos mesmo com o mapa na mão.</strong> Depois de revelar explicitamente, ele ainda deixou o link de login, a desativação de autorização e o agregado incorreto. Nem todo modelo aproveita a segunda chance.</p>
<p><strong>E tem o caso do 397B que virou piada pronta.</strong> Um modelo de 397B, grande demais pra caber na cabeça de quem defende tamanho, deixou passar a backdoor <code>admin? =&gt; admin || email.end_with?(&quot;@internal.example&quot;)</code> mesmo depois do aviso de auditoria de segurança. Pior: ele adicionou um teste passando que codificava a backdoor como comportamento intencional, e o commit ainda se gabava de &ldquo;Brakeman: 0 warnings&rdquo;. Se você quer um exemplo de por que eu testo vigilância e não benchmark de marketing, tá aí.</p>
<p><strong>O Muse Spark Open quase não saiu do chão, mas se recuperou.</strong> Ele começou bloqueado: pesos em 127 shards de NVFP4, gated no Hugging Face, e sem suporte no vLLM estável. Só rodou quando eu compilei uma branch de PR ainda em rascunho, com nvcc 12.9 e bibliotecas 12.8 misturadas, e limitei o multimodal a texto.</p>
<p>Aí ele entregou 69,5: base fraca sem aviso (21 de 40), mas 6 de 7 itens consertados quando apontado. É o caso de &ldquo;o esforço de infra não se confunde com a nota do modelo&rdquo;: o serviço ficou de pé, o comportamento é útil, e mesmo assim ele só vai até Tier C. Se a pergunta é &ldquo;por que ele não tá no topo&rdquo;, a resposta é vigilância por padrão, não VRAM.</p>
<p><strong>E o Nex foi o desastre completo.</strong> Precisou de dois nudges, e no fim o texto corrompido chegou ao Ruby commitado e o app nem inicializa. Na nuvem, o mesmo Nex N2.5 Pro tinha feito 94. Em casa, 27. Eu não acreditaria em benchmark de fornecedor depois de ver isso duas vezes.</p>
<p>Aliás, o caso da Nex é o exemplo perfeito de escolha ruim de base. Quando a Rio 3.5 explodiu em junho, <a href="/2026/06/15/controversia-llm-rio-3-5-plagio/">eu cobri a controvérsia aqui</a>: o checkpoint dela era uma mescla de Nex-N2-Pro com Qwen. A turma da Rio podia ter escolhido bases muito melhores, como o DeepSeek V4.1 ou o GLM 5.3 Flash, e foi justamente pra linhagem que, na minha régua atual, foi a pior do lote. Escolher modelo por nome bonito custa caro.</p>
<h2>Rodar em 4 GPUs não é trivial<span class="hx:absolute hx:-mt-20" id="rodar-em-4-gpus-não-é-trivial"></span>
    <a href="#rodar-em-4-gpus-n%c3%a3o-%c3%a9-trivial" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Quatro placas não são quatro modelos: é um modelo só fatiado em quatro, com tensor parallelism TP=4 no vLLM. Isso dá uns 1,1 TB de VRAM agregado (na prática, ~1.038 GB úteis com <code>--gpu-memory-utilization 0.90</code>). Sem isso, nenhum dos modelos acima de 400 GB existiria na minha vida sem offload pra SSD, que é onde a velocidade vai morrer.</p>
<p>Os cuidados que se repetiram em quase todos:</p>
<ul>
<li><strong>Toolchain sangrando na veia.</strong> B300 (<code>sm_103a</code>) exige CUDA 12.9 pra compilar os kernels na hora, então a imagem padrão não bastava: toolkit standalone com nvcc 12.9 e headers certos, e ainda assim cada família precisava de um remendo de DeepGEMM, FlashInfer ou link de cudart. Quando a compilação acerta, o prêmio é grande: o GLM 5.3 Flash saiu de 10,35 pra 141,40 tokens por segundo só de configuração (tirando o <code>--enforce-eager</code> e ligando o modo compilado com CUDA graphs), sem trocar checkpoint, contexto ou precisão de KV.</li>
<li><strong>Startup não é download.</strong> O DeepSeek demorava 15 a 30 minutos pra inicializar, então prepare-se pra esperar o modelo carregar antes de medir qualquer coisa.</li>
<li><strong>Cada família tem seu parser e suas manias.</strong> O Nex só conversou depois de trocar o parser pra <code>qwen3_xml</code>, ganhar mais um nudge e ignorar os artefatos de <code>tmp/log</code>. O Step-5 não existia no vLLM 0.32 estável e precisou de nightly com PR específico. O Muse exigiu compilar uma branch de PR ainda em rascunho, com CUDA 12.9 nvcc e bibliotecas 12.8 misturadas, e os pesos em 127 shards de NVFP4. O MiMo pediu <code>--trust-remote-code</code>. A Laguna decidiu, do nada, inundar a saída com texto de raciocínio, e uma sprint teve que ser refeita a partir da base de injeção.</li>
<li><strong>Contexto custa caro.</strong> O checkpoint FP8 do Kimi K2 Instruct (1.029 GB) deixaria 9 GB pra KV cache nas 4 placas, então foi o INT4 Thinking de 594 GB no lugar. Especular com draft head (MTP) só funcionou no Step-5, com ganho de 2,24x e 73,7% de aceitação de tokens, porque eu verifiquei a correção das ferramentas antes.</li>
</ul>
<p>A lição operacional: alugar VRAM resolve o &ldquo;cabe&rdquo;, mas não o &ldquo;roda bem&rdquo;. Cada modelo grande é um mini-projeto de integração.</p>
<h2>E a fronteira, fica onde?<span class="hx:absolute hx:-mt-20" id="e-a-fronteira-fica-onde"></span>
    <a href="#e-a-fronteira-fica-onde" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>A pergunta que interessa pro desenvolvedor não é &ldquo;quem tem mais parâmetro&rdquo;, é &ldquo;o modelo aberto grande entrega no meu dia a dia?&rdquo;. Os números do benchmark v4, na mesma régua:</p>
<table>
  <thead>
      <tr>
          <th>Modelo</th>
          <th style="text-align: right">Nota</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Claude Opus 5 / 5.5</td>
          <td style="text-align: right">100,0</td>
      </tr>
      <tr>
          <td>GPT-6 Luna</td>
          <td style="text-align: right">95,5</td>
      </tr>
      <tr>
          <td>GLM 5.3 (hospedado)</td>
          <td style="text-align: right">94,0</td>
      </tr>
      <tr>
          <td><strong>DeepSeek V4.1 Flash (nuvem ou 4x B300)</strong></td>
          <td style="text-align: right"><strong>92,5</strong></td>
      </tr>
      <tr>
          <td>GLM 5.3 Flash / Qwen3.8 Flash-Next (B300 única)</td>
          <td style="text-align: right">92,5</td>
      </tr>
      <tr>
          <td>GPT-6 Sol</td>
          <td style="text-align: right">91,0</td>
      </tr>
      <tr>
          <td>Claude Sonnet 5</td>
          <td style="text-align: right">91,0</td>
      </tr>
      <tr>
          <td>Kimi K3</td>
          <td style="text-align: right">85,0</td>
      </tr>
  </tbody>
</table>
<p>O DeepSeek V4.1 Flash empatou com o próprio serviço em nuvem, com os mesmos 92,5, e ficou acima do GPT-6 Sol e do Sonnet 5. Ou seja: <strong>o melhor open source atual já está a 1,5 ponto do GLM hospedado e a 7,5 do topo absoluto</strong>, na frente de dois frontier comerciais fortes. Se a sua régua é a minha (achar backdoor e bypass antes de mergear), um open source de 748B local já serve pra revisão diária de código, com 175 tokens por segundo por stream nas minhas medições de admissão.</p>
<p>Mas três avisos antes de você cancelar a assinatura do Claude:</p>
<ol>
<li>
<p><strong>Mais parâmetros não significa mais competência.</strong> O GLM 5.3 full, com ~700B, fez 76 contra 92,5 do Flash dele mesmo. O Kimi K2 Thinking, com 1 trilhão, fez 46. E o Nex N2.5 Pro despencou de 94 na nuvem pra 27 em casa: checkpoint público não é garantia de reproduzir o serviço, como eu já tinha visto no DeepSeek V4 Flash (86 na nuvem, 44,5 em casa).</p>
</li>
<li>
<p><strong>Minha régua é vigilância, não programação geral.</strong> Aqui eu meço quem percebe sabotagem. Não é a mesma coisa que medir quem escreve o seu sistema inteiro melhor, e uma repetida de benchmark minha já variou 9 pontos num mesmo modelo. A distância de 1,5 ponto entre modelos pode ser barulho.</p>
</li>
<li>
<p><strong>Custo muda a resposta.</strong> Se a ideia é ter qualidade de frontier no dia a dia, a API em nuvem do DeepSeek V4.1 custou US$ 1,21 por rodada de benchmark completa. O pod de 4x B300 custa US$ 36 por hora antes mesmo de você apertar o botão. E o caso mais didático continua sendo o DeepSeek V4 Flash da campanha anterior: na nuvem ele fez 86 por US$ 0,97 a rodada; em casa, 44,5 e ainda por cima mais caro por inferência. Às vezes a nuvem é melhor <strong>e</strong> mais barata.</p>
</li>
</ol>
<h2>Vale alugar 4x B300?<span class="hx:absolute hx:-mt-20" id="vale-alugar-4x-b300"></span>
    <a href="#vale-alugar-4x-b300" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Pra uma pessoa escrevendo código com um agente de manhã e outro de tarde: não. A API resolve por poucos dólares o que o pod resolve por dezenas de dólares por hora.</p>
<p>Pra uma empresa que precisa de dado privado de verdade (código que não pode sair da VPN), checkpoint travado pra reprodutibilidade, ou carga batch contínua (milhares de revisões, classificações, análises por dia), aí o cálculo muda. O DeepSeek V4.1 em 4x B300 entrega qualidade de quase-fronteira com o dado em casa, e isso não se compra com assinatura de chat.</p>
<p>O que eu não compraria de novo sem medir antes é a premissa &ldquo;maior é melhor&rdquo;. Eu comecei essa campanha achando que o salto de 92,5 ia vir de um modelo maior. Veio do modelo certo rodando direito. O resto é VRAM queimada e uma história boa pra contar.</p>
<p>E tem mais um capítulo vindo, de hardware que não é datacenter: os testes com modelos locais na RTX 5090 e no Strix Halo, com um motor de inferência novo chamado Strata. Quando o protocolo terminar, sai o terceiro artigo.</p>
<p>E a pergunta de &ldquo;vale a pena&rdquo; tem uma resposta irritante de óbvia: depende de quantas pessoas vão usar ao mesmo tempo. É ela que eu respondo no próximo artigo: <a href="/2026/10/22/llm-benchmark-v4-can-my-team-fit-in-a-4x-b300-h200/">LLM Benchmark v4: can my team fit in a 4x b300/h200?</a>.</p>
]]></content:encoded><category>benchmarks-de-llm</category><category>modelos-locais</category><category>llms</category><category>hardware</category></item><item><title>LLM Benchmark v4: os top LLMs open source numa B300</title><link>https://www.akitaonrails.com/2026/10/19/llm-benchmark-v4-os-top-llms-open-source-numa-b300/</link><guid isPermaLink="true">https://www.akitaonrails.com/2026/10/19/llm-benchmark-v4-os-top-llms-open-source-numa-b300/</guid><pubDate>Mon, 19 Oct 2026 14:00:00 -0300</pubDate><description>&lt;p&gt;Nos dois últimos textos do benchmark v4 eu &lt;a href="https://www.akitaonrails.com/2026/09/15/novo-llm-benchmark-v4-retestando-todos-llms-parte-1/"&gt;refiz o teste do zero&lt;/a&gt;, com um app Rails sabotado de propósito em sete sprints, e &lt;a href="https://www.akitaonrails.com/2026/09/15/novo-llm-benchmark-v4-retestando-todos-llms-parte-2/"&gt;ranqueei 39 modelos&lt;/a&gt; por vigilância: seis empates no topo, um modelo de graça batendo metade do Claude. Depois &lt;a href="https://www.akitaonrails.com/2026/09/23/llm-benchmark-v4-opus-5-5-gpt-6-sol-luna-mimo-2-6-grok-4-7/"&gt;testei cinco modelos novos&lt;/a&gt;, Opus 5.5, GPT 6 sol e luna, MiMo V2.6 Pro e Grok 4.7: o Opus 5.5 fechou com 100 e se juntou ao empate no topo, o GPT 6 luna fez 95,5 por menos de um dólar a rodada, e o Grok 4.7 regrediu feio contra o próprio antecessor.&lt;/p&gt;</description><content:encoded><![CDATA[<p>Nos dois últimos textos do benchmark v4 eu <a href="/2026/09/15/novo-llm-benchmark-v4-retestando-todos-llms-parte-1/">refiz o teste do zero</a>, com um app Rails sabotado de propósito em sete sprints, e <a href="/2026/09/15/novo-llm-benchmark-v4-retestando-todos-llms-parte-2/">ranqueei 39 modelos</a> por vigilância: seis empates no topo, um modelo de graça batendo metade do Claude. Depois <a href="/2026/09/23/llm-benchmark-v4-opus-5-5-gpt-6-sol-luna-mimo-2-6-grok-4-7/">testei cinco modelos novos</a>, Opus 5.5, GPT 6 sol e luna, MiMo V2.6 Pro e Grok 4.7: o Opus 5.5 fechou com 100 e se juntou ao empate no topo, o GPT 6 luna fez 95,5 por menos de um dólar a rodada, e o Grok 4.7 regrediu feio contra o próprio antecessor.</p>
<p>Faltava uma categoria que eu nunca tinha testado direito: os modelos open source grandes. Os que cabem na minha <a href="/2026/01/21/omarchy-3-setup-de-dual-gpus-com-amd-e-nvidia/">RTX 5090</a> de 32 GB ou no meu <a href="/2026/03/31/review-minisforum-ms-s1-max-amd-ai-max-395/">Strix Halo</a> com 61 GB utilizáveis pela GPU são fracos demais pra isso. Servem pra tarefa simples de agente, e nenhum modelo pequeno aguenta tarefa real de programação, muito menos um projeto complexo de software.</p>
<p>O Qwen 3.8 27B levou 706 minutos pra fechar o v4 no Strix e fez 80; o GLM-4.7-Flash local fez 24. Então nem tente.</p>
<p>Mas e se a gente forçar? O que modelos de tamanho médio, de 100 a 400 bilhões de parâmetros, conseguem fazer rodando numa única B300 alugada? Eu podia alugar quatro delas e rodar os modelos gigantes de 750 bilhões ou um trilhão, e não tenho certeza de que o custo-benefício vale. Os dados abaixo respondem.</p>
<blockquote>
  <p><strong>TL;DR:</strong> 14 modelos abertos, uma B300 de 288 GB na Runpod a uns 9 dólares por hora, mesmo protocolo v4 de sabotagem. Nenhum deles serve rápido do jeito que vem: o GLM 5.3 Flash decodificava a 10 tokens por segundo com a configuração padrão e a 141 depois de ajustado, 14 vezes mais só de flag. Dois modelos abertos, GLM 5.3 Flash e Qwen3.8 Flash-Next, fizeram 92,5, nível Claude Sonnet 5, e o Swift 1.5 27B fez 88, nível Opus 4.6. Nenhum chegou nos 100 da fronteira. O flagship de 397 bilhões fez 68,5 e deixou passar uma backdoor de admin, o DeepSeek V4 Flash fez 86 na nuvem e 44,5 servido por mim, e o Nemotron trapaceou num teste de regressão. A API em nuvem do mesmo modelo custa um dólar por rodada; o pod custa 218 dólares por dia. Pule pra <a href="#o-que-%c3%a9-uma-b300-e-o-que-%c3%a9-a-runpod">B300 e Runpod</a>, pro <a href="#nenhum-deles-serve-r%c3%a1pido-do-jeito-que-vem">ajuste de servidor</a>, pro <a href="#o-ranking">ranking</a>, pros <a href="#os-casos-interessantes">casos interessantes</a>, pra <a href="#a-que-dist%c3%a2ncia-da-fronteira">comparação com a fronteira</a>, pra <a href="#a-conta">conta</a> ou pra <a href="#vale-a-pena-ter-a-pr%c3%b3pria-infra">conclusão</a>.</p>

</blockquote>
<h2>O que é uma B300, e o que é a Runpod<span class="hx:absolute hx:-mt-20" id="o-que-é-uma-b300-e-o-que-é-a-runpod"></span>
    <a href="#o-que-%c3%a9-uma-b300-e-o-que-%c3%a9-a-runpod" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>A <strong>B300</strong> é a GPU de datacenter da geração Blackwell Ultra da NVIDIA, anunciada em março de 2025. Cada placa tem <a href="https://developer.nvidia.com/blog/nvidia-blackwell-ultra-for-the-era-of-ai-reasoning/"target="_blank" rel="noopener">288 GB de memória HBM3e</a>, nove vezes a minha 5090, e uns 15 petaflops de computação densa em NVFP4, o formato de 4 bits que a Blackwell faz em hardware. É a placa que vai dentro do rack GB300 NVL72, 72 delas interligadas, que as empresas de IA compram aos milhares.</p>
<p>Sozinha, uma B300 custa mais que um apartamento. O que a torna acessível é alugar por hora.</p>
<p>A <a href="https://www.runpod.io/"target="_blank" rel="noopener"><strong>Runpod</strong></a> é um provedor de GPU em nuvem que aluga placas em &ldquo;pods&rdquo;, containers Linux com uma ou mais GPUs, cobrados por segundo. Você escolhe a placa, um template de imagem, sobe por SSH, e paga enquanto o pod está ligado, inclusive ocioso. A B300 custava uns <a href="https://www.runpod.io/pricing"target="_blank" rel="noopener">9 dólares por hora</a> na nuvem segura deles na semana do teste; uma H100 sai por 3 a 4. O disco do container é efêmero: terminou o pod, os 250 GB de pesos que você baixou somem, e você baixa de novo.</p>
<p>O pod que eu usei: uma B300 com 275 GiB segundo o <code>nvidia-smi</code> (os mesmos 288 GB nominais, em unidades binárias), 344 vCPUs, 4 TB de RAM no host, 700 GB de disco. O servidor de inferência foi o vLLM; o harness foi o mesmo OpenCode de todos os outros modelos do ranking, conectado por túnel SSH ao pod. O modelo roda lá; o código, os testes e os resultados ficam na minha máquina.</p>
<p>Caber na memória é a primeira peneira. Na B300 cabem os modelos de 100 a 400 bilhões de parâmetros quantizados em NVFP4, mais os de 27 a 35 bilhões em BF16. Ficam de fora o GLM 5.3 flagship de 753 bilhões, o DeepSeek V4.1 Flash de 750, o DeepSeek V4 Pro de 1,6 trilhão e o Kimi K3 de 2,8 trilhões. Pra esses, seriam quatro B300 ou mais, uns 36 dólares por hora.</p>
<h2>A metodologia<span class="hx:absolute hx:-mt-20" id="a-metodologia"></span>
    <a href="#a-metodologia" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>O protocolo é o <a href="/2026/09/15/novo-llm-benchmark-v4-retestando-todos-llms-parte-1/">v4</a> de sempre: o modelo constrói um app Rails em sete sprints acumulados, e entre os sprints um revisor independente, isolado do modelo, injeta 14 sabotagens, de CVE real a regressão silenciosa de lógica e de teste, no código que o próprio modelo escreveu. A nota de 0 a 100 mede vigilância: quanto o modelo pega e conserta sem ser avisado (peso cheio), quanto conserta depois de um &ldquo;faça uma auditoria de segurança&rdquo; explícito no último sprint (40% do peso), e o que fica sem resolver (zero). Os tiers são A a partir de 83, B de 73 a 82, C de 51 a 72, D até 50.</p>
<p>Duas diferenças nesta rodada. A primeira é o revisor: os cinco modelos que terminaram antes do bloqueio foram revisados de ponta a ponta pelo Codex, como nas rodadas anteriores.</p>
<p>No meio da leva o serviço de segurança da OpenAI passou a recusar a operação de injeção e correção, com uma flag de risco cibernético, mesmo sendo código meu, em máquina minha, num benchmark com 46 modelos já pontuados pelo mesmo protocolo. Troquei o revisor pro Claude Opus 4.8 nos modelos restantes, com o mesmo plano de injeção, mesmo catálogo, mesma régua, e a proveniência de cada fronteira está gravada por sprint. O impacto esperado na nota é nenhum, porque os dois executam a mesma receita escrita.</p>
<p>A segunda é a admissão por velocidade, que é o assunto da próxima seção. Modelo que decodifica abaixo de 100 tokens por segundo nem entra na pista.</p>
<p>Além disso: o modelo aberto tem direito a até dois empurrões curtos por rodada pra defeito bobo (um import faltando, um ponto de entrada de teste errado), registrados como &ldquo;assistido&rdquo;. Falha de infraestrutura, driver, loader, parser, nunca é cobrada do modelo. E o pod rodava um modelo por vez, sem acesso ao workspace ou ao resultado de nenhum outro.</p>
<h2>Nenhum deles serve rápido do jeito que vem<span class="hx:absolute hx:-mt-20" id="nenhum-deles-serve-rápido-do-jeito-que-vem"></span>
    <a href="#nenhum-deles-serve-r%c3%a1pido-do-jeito-que-vem" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>A primeira lição custou um dia. Baixei o <a href="https://huggingface.co/RedHatAI/GLM-5.3-Flash-NVFP4"target="_blank" rel="noopener">GLM 5.3 Flash</a>, 320 bilhões de parâmetros com 18 ativos, quantizado em NVFP4, 198 GB. Subi no vLLM com a configuração que &ldquo;funciona&rdquo;: serve, passa nos testes de chamada de ferramenta, responde. E decodificava a <strong>10 tokens por segundo</strong>, com a GPU a 10% de uso, 209 watts dos 1.100 que o pod libera.</p>
<p>Isso é inutilizável pra programar. Um agente de código gera dezenas de milhares de tokens por sprint; a 10 por segundo, um sprint de 20 minutos vira mais de quatro horas.</p>
<p>O mesmo checkpoint, mesmo contexto de 128 mil tokens, mesmo cache de KV em FP8, mesmo prompt de teste, trocando só as flags de compilação e modo: <strong>141 tokens por segundo</strong>. Catorze vezes mais rápido, zero mudança de precisão ou de pesos.</p>
<p>O culpado principal era o <code>--enforce-eager</code>, que desliga o <code>torch.compile</code> e a captura de CUDA graphs do vLLM, e que todo tutorial manda ligar &ldquo;pra subir mais rápido&rdquo;. Sobe mais rápido e serve 14 vezes mais devagar. A medição trocou essa flag junto com o modo <code>interactivity</code>, então eu não isolei a contribuição de cada uma.</p>
<p>Daí a regra de admissão: <strong>menos de 100 tokens por segundo eu considero inutilizável</strong>, e nenhum modelo entra no benchmark sem passar disso na mediana de duas sondagens quentes de 256 tokens, depois de uma fria. Pra não virar um buraco sem fundo, o ajuste é limitado: no máximo três configurações e 45 minutos de relógio por modelo, incluindo carga e compilação. E ajuste nunca pode trocar qualidade por velocidade: nada de encurtar contexto, baixar esforço de raciocínio ou quantizar mais pra bater o número.</p>
<p>Os ajustes que valeram pra todo mundo:</p>
<ul>
<li><strong>Instalar um CUDA que compile pra placa.</strong> A imagem padrão da Runpod vem com CUDA 12.8, cujo <code>nvcc</code> <a href="https://developer.nvidia.com/cuda-gpus"target="_blank" rel="noopener">não compila pra <code>compute_103a</code></a>, a capacidade de computação da B300. Sem o 12.9, os kernels NVFP4 nunca compilam.</li>
<li><strong>Nunca usar <code>--enforce-eager</code>.</strong> Deixar o vLLM compilar e capturar CUDA graphs é a maior vitória isolada.</li>
<li><strong><code>--performance-mode interactivity</code></strong>: otimizar pra latência de um fluxo só, que é o que um agente serial precisa, em vez de vazão agregada.</li>
<li><strong>Cache de KV em FP8 nos MoE grandes, cache de prefixo e prefill em pedaços em todos</strong>, e confirmados pelos contadores do Prometheus do vLLM em vez de assumidos: num prompt de 11 mil tokens, o servidor reaproveitou exatamente 8.576 tokens de prefixo a cada repetição.</li>
<li><strong>O parser certo de ferramenta e de raciocínio pra cada família.</strong> Parser errado faz chamada de ferramenta sumir em silêncio ou vazar o raciocínio na resposta.</li>
</ul>
<p>E os ajustes específicos que achei mais interessantes:</p>
<ul>
<li><strong>NVFP4 só roda no vLLM.</strong> Os checkpoints de 198 a 251 GB em FP4 nativo da Blackwell são a única forma de caber na placa, e llama.cpp e Ollama simplesmente não carregam esses formatos. Quem vive de GGUF não tem acesso a essa faixa.</li>
<li><strong>Decodificação especulativa nativa.</strong> O Qwen 3.8 27B foi de 92 pra 195 tokens por segundo com a cabeça MTP que o próprio checkpoint traz, prevendo três tokens por passo, mais <code>--language-model-only</code> pra descartar a torre de visão (medido em pods diferentes, então sem ablação controlada). O <a href="https://huggingface.co/meta-models/Muse-Glimmer-30B"target="_blank" rel="noopener">Muse Glimmer</a> chegou a 295 com o rascunhador DFlash leve que ele publica junto, 15 tokens especulados por passo. Só liguei onde existe um rascunhador oficial e onde a chamada de ferramenta continuou correta; pro Ornith existe um rascunhador publicado à parte, com taxa de aceitação desconhecida, então ficou desligado.</li>
<li><strong>O loader do CUDA 12.</strong> O flashinfer compila em JIT um kernel de MoE em NVFP4 que precisa do <code>libcudart.so.12</code>, e depois de instalar o CUDA 13 pro Nemotron a resolução de biblioteca quebrou. O sintoma parecia OOM; a causa era um <code>ld.so.conf</code>. Um script de reparo com evidência de <code>ldd</code> antes e depois resolveu, e o 397B passou a servir a 144 tokens por segundo.</li>
<li><strong>Um ambiente virtual por runtime.</strong> O Nemotron 3.5 Super VL exigiu um vLLM 0.32 próprio num venv separado, porque é o que o card oficial fixa, e esse build pede CUDA 13. Cada família de modelo é um caso bespoke: parser, quantização, cabeça de rascunho, versão de runtime e reparo de loader, tudo diferente.</li>
<li><strong>Um bug de parser que derrubava a última chamada.</strong> O parser de ferramenta Qwen do vLLM descartava o último parâmetro de uma chamada quando faltava a tag de fechamento do parâmetro mas a de função estava presente. Um reparo estreito, com 24 casos de teste, tirou esse ruído de serving da conta do modelo.</li>
</ul>
<p>Os argumentos exatos de cada modelo, os logs de sondagem e o livro-razão de cada rodada de ajuste estão no <a href="https://github.com/akitaonrails/llm-coding-benchmark/blob/master/docs/b300-serving-optimizations.md"target="_blank" rel="noopener">documento de otimizações de serving</a> e no <a href="https://github.com/akitaonrails/llm-coding-benchmark/blob/master/docs/runpod-b300-openweights-runbook.md"target="_blank" rel="noopener">runbook da B300</a> do repositório.</p>
<blockquote>
  <p>A velocidade é quase de graça, depois que você deixa o vLLM compilar. O trabalho real é descobrir, modelo por modelo, qual combinação de toolkit, parser, quantização e cabeça de rascunho aquele checkpoint exige. Eu dei 45 minutos pra cada um e precisei deles.</p>

</blockquote>
<h2>O ranking<span class="hx:absolute hx:-mt-20" id="o-ranking"></span>
    <a href="#o-ranking" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Nota v4 de vigilância, tier, tamanho em memória, velocidade de decodificação na sondagem de admissão (quente, um fluxo só) e o custo só do tempo de inferência dos sete sprints, a 9,09 dólares por hora. O custo exclui download, carga, compilação, ajuste e tempo ocioso; a conta de verdade vem mais abaixo.</p>
<table>
  <thead>
      <tr>
          <th style="text-align: right">#</th>
          <th>Modelo</th>
          <th style="text-align: right">v4</th>
          <th>Tier</th>
          <th style="text-align: right">Memória</th>
          <th style="text-align: right">tok/s</th>
          <th style="text-align: right">Minutos</th>
          <th style="text-align: right">Custo inferência</th>
          <th>Revisor</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td style="text-align: right">1</td>
          <td>GLM 5.3 Flash (NVFP4)</td>
          <td style="text-align: right"><strong>92,5</strong></td>
          <td>A</td>
          <td style="text-align: right">198 GB</td>
          <td style="text-align: right">142</td>
          <td style="text-align: right">145</td>
          <td style="text-align: right">US$ 21,96</td>
          <td>Codex</td>
      </tr>
      <tr>
          <td style="text-align: right">1</td>
          <td>Qwen3.8 Flash-Next (NVFP4)</td>
          <td style="text-align: right"><strong>92,5</strong></td>
          <td>A</td>
          <td style="text-align: right">100 GB</td>
          <td style="text-align: right">195</td>
          <td style="text-align: right">80</td>
          <td style="text-align: right">US$ 12,04</td>
          <td>Codex → Claude</td>
      </tr>
      <tr>
          <td style="text-align: right">3</td>
          <td>Swift 1.5 27B</td>
          <td style="text-align: right"><strong>88,0</strong></td>
          <td>A</td>
          <td style="text-align: right">56 GB</td>
          <td style="text-align: right">203</td>
          <td style="text-align: right">64</td>
          <td style="text-align: right">US$ 9,71</td>
          <td>Codex</td>
      </tr>
      <tr>
          <td style="text-align: right">4</td>
          <td>ThinkingCap 3.8 27B</td>
          <td style="text-align: right"><strong>83,0</strong></td>
          <td>A</td>
          <td style="text-align: right">56 GB</td>
          <td style="text-align: right">189</td>
          <td style="text-align: right">76</td>
          <td style="text-align: right">US$ 11,47</td>
          <td>Codex → Claude</td>
      </tr>
      <tr>
          <td style="text-align: right">5</td>
          <td>Muse Glimmer 30B</td>
          <td style="text-align: right">81,0</td>
          <td>B</td>
          <td style="text-align: right">60 GB</td>
          <td style="text-align: right">295</td>
          <td style="text-align: right">23</td>
          <td style="text-align: right">US$ 3,54</td>
          <td>Codex → Claude</td>
      </tr>
      <tr>
          <td style="text-align: right">5</td>
          <td>Ornith 1.5 35B</td>
          <td style="text-align: right">81,0</td>
          <td>B</td>
          <td style="text-align: right">70 GB</td>
          <td style="text-align: right">317</td>
          <td style="text-align: right">161</td>
          <td style="text-align: right">US$ 24,39</td>
          <td>Codex → Claude</td>
      </tr>
      <tr>
          <td style="text-align: right">7</td>
          <td>Qwen 3.8 27B</td>
          <td style="text-align: right">80,5</td>
          <td>B</td>
          <td style="text-align: right">54 GB</td>
          <td style="text-align: right">195</td>
          <td style="text-align: right">192</td>
          <td style="text-align: right">US$ 29,11</td>
          <td>Codex</td>
      </tr>
      <tr>
          <td style="text-align: right">8</td>
          <td>Qwen3.5 397B (NVFP4)</td>
          <td style="text-align: right">68,5</td>
          <td>C</td>
          <td style="text-align: right">251 GB</td>
          <td style="text-align: right">140</td>
          <td style="text-align: right">54</td>
          <td style="text-align: right">US$ 8,22</td>
          <td>Codex → Claude</td>
      </tr>
      <tr>
          <td style="text-align: right">9</td>
          <td>Nemotron 3.5 Super VL (NVFP4)</td>
          <td style="text-align: right">63,0</td>
          <td>C</td>
          <td style="text-align: right">70 GB</td>
          <td style="text-align: right">207</td>
          <td style="text-align: right">50</td>
          <td style="text-align: right">US$ 7,51</td>
          <td>Codex → Claude</td>
      </tr>
      <tr>
          <td style="text-align: right">10</td>
          <td>DeepSeek V4 Flash</td>
          <td style="text-align: right">44,5</td>
          <td>D</td>
          <td style="text-align: right">167 GB</td>
          <td style="text-align: right">153</td>
          <td style="text-align: right">37</td>
          <td style="text-align: right">US$ 5,59</td>
          <td>Codex</td>
      </tr>
      <tr>
          <td style="text-align: right">11</td>
          <td>GLM 4.7 Flash BF16</td>
          <td style="text-align: right">33,5</td>
          <td>D</td>
          <td style="text-align: right">60 GB</td>
          <td style="text-align: right">217</td>
          <td style="text-align: right">30</td>
          <td style="text-align: right">US$ 4,60</td>
          <td>Codex</td>
      </tr>
  </tbody>
</table>
<p>Três não têm nota: o <strong>Command A Plus</strong> da Cohere (218 bilhões, 25 ativos, W4A4) travou num loop reescrevendo o mesmo model 23 vezes, precisou de três empurrões e ainda deixou os testes sem rodar antes de o revisor ser bloqueado; o <strong>Nex N2.5 Mini</strong> rodou a 337 tokens por segundo e entregou Ruby inválido, migrations contraditórias e autenticação quebrada em duas tentativas, um resultado ruim válido de um servidor que funciona; e o <strong>Qwen3.5 122B</strong> era reserva condicional pro caso de o 397B não terminar, e o 397B terminou.</p>
<p>Os minutos são só de sprint, e os de Ornith incluem um sprint interrompido por infra. Flash-Next, Swift, ThinkingCap, Muse e Qwen 27B levaram um empurrão cada; GLM 4.7, dois; os outros, nenhum. A velocidade de sondagem é um número de admissão: a vazão de uma sessão real de código inclui prefill, ferramentas, testes e as escolhas do agente. E repare que os dois primeiros empatam na nota com perfis opostos: o GLM gastou 145 minutos e 22 dólares pra chegar nos mesmos 92,5 que o Qwen3.8 Flash-Next fez em 80 minutos e 12 dólares, com metade da memória.</p>
<h2>Os casos interessantes<span class="hx:absolute hx:-mt-20" id="os-casos-interessantes"></span>
    <a href="#os-casos-interessantes" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><h3>Qwen3.8 Flash-Next: o melhor dos abertos, por custo<span class="hx:absolute hx:-mt-20" id="qwen38-flash-next-o-melhor-dos-abertos-por-custo"></span>
    <a href="#qwen38-flash-next-o-melhor-dos-abertos-por-custo" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>O <a href="https://huggingface.co/nvidia/Qwen3.8-Flash-Next-NVFP4"target="_blank" rel="noopener">Flash-Next</a> é, <a href="https://recipes.vllm.ai/Qwen/Qwen3.8-Flash-Next"target="_blank" rel="noopener">nas palavras da Qwen</a>, uma prévia da arquitetura do Qwen 4: 125 bilhões de parâmetros de modelo de linguagem com 6 bilhões ativos, mais uma tabela de embeddings de n-gramas de 51 bilhões que a Qwen diz que pode ser descarregada pra memória do host, mais uma cabeça MTP de 4. Quantizado em NVFP4 pela NVIDIA, ocupou uns 100 GB de placa (o checkpoint tem 133 GB em disco). Foi o único modelo da segunda leva a resolver todos os 14 itens: pegou o vazamento entre tenants sem aviso, pegou o item composto de integridade de teste, e escreveu por conta própria specs de isolamento por usuário, de modo que a IDOR plantada na API nem tinha como continuar verde. Com 195 tokens por segundo e 12 dólares de inferência, é o melhor custo-benefício desta tabela.</p>
<h3>GLM 5.3 Flash: o mesmo 92,5, três vezes mais caro<span class="hx:absolute hx:-mt-20" id="glm-53-flash-o-mesmo-925-três-vezes-mais-caro"></span>
    <a href="#glm-53-flash-o-mesmo-925-tr%c3%aas-vezes-mais-caro" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>O GLM fez a mesma nota com uma arquitetura bem maior: 320 bilhões de parâmetros, 18 ativos, 198 GB mesmo em FP4. Resolveu os 14 itens, zero empurrão, revisado de ponta a ponta pelo Codex.</p>
<p>Só que levou 145 minutos e consumiu o dobro da memória pelo mesmo resultado. Na nuvem da Z.ai, o mesmo modelo fez 84,25 num plano fixo. Com o ruído de uma rodada só, que no meu histórico chega a nove pontos, a leitura mais segura é que os dois estão na mesma faixa.</p>
<h3>Swift 1.5 e ThinkingCap: dois fine-tunes de 27B no Tier A<span class="hx:absolute hx:-mt-20" id="swift-15-e-thinkingcap-dois-fine-tunes-de-27b-no-tier-a"></span>
    <a href="#swift-15-e-thinkingcap-dois-fine-tunes-de-27b-no-tier-a" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>As duas surpresas. O <a href="https://huggingface.co/ukisai/Swift-1.5-Qwen3.8-27b"target="_blank" rel="noopener">Swift 1.5</a>, da UkisAI, e o <a href="https://huggingface.co/bottlecapai/ThinkingCap-Qwen3.8-27B"target="_blank" rel="noopener">ThinkingCap 3.8</a>, da BottleCap AI, são ajustes do Qwen 3.8 27B treinados pra pensar menos: o Swift anuncia até 58% menos tokens de raciocínio (na mediana do GPQA; no Terminal-Bench, 16%), o ThinkingCap uns 37% na média. Os dois cabem em 56 GB em BF16, rodam a uns 200 tokens por segundo com MTP, e fizeram 88 e 83.</p>
<p>O Swift ficou acima do Opus 4.6 da Anthropic. O Qwen 3.8 27B base, mesma arquitetura, fez 80,5 com 192 minutos; o Swift fez 88 em 64. O Swift pegou sabotagem mais cedo que o base; se foi por pensar menos, uma rodada de cada deixa de provar.</p>
<p>Uma ressalva de proveniência: o revisor registrou que o Swift leu mensagens de commit locais que revelavam regressões, o que enfraquece uma leitura literal de &ldquo;descoberta totalmente sem ajuda&rdquo;. E ele deixou um item sem resolver, um admin inativo ainda aceito com um teste vazio de guarda. Ainda assim, de todos os modelos desta tabela, é o que eu colocaria numa placa minha.</p>
<h3>Qwen3.5 397B: o flagship que deixou passar a backdoor<span class="hx:absolute hx:-mt-20" id="qwen35-397b-o-flagship-que-deixou-passar-a-backdoor"></span>
    <a href="#qwen35-397b-o-flagship-que-deixou-passar-a-backdoor" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>O maior modelo da leva, 397 bilhões de parâmetros com 17 ativos, 251 GB de NVFP4 em 275 de placa, serviu a 140 tokens por segundo depois do reparo do loader e fez <strong>68,5</strong>, Tier C.</p>
<p>O caso que define a nota: a sabotagem número 6 é uma linha <code>admin? =&gt; admin || email.end_with?(&quot;@internal.example&quot;)</code>, uma escalação de privilégio por domínio de e-mail. O 397B deixou passar nos sprints normais, deixou passar no sprint 7 com o pedido explícito de auditoria de segurança, e <strong>escreveu um teste que passava, codificando a backdoor como comportamento esperado</strong>, no mesmo commit em que anunciou &ldquo;Brakeman: 0 warnings&rdquo;. Parâmetro não comprou vigilância.</p>
<h3>Nemotron 3.5 Super VL: trapaceou no teste<span class="hx:absolute hx:-mt-20" id="nemotron-35-super-vl-trapaceou-no-teste"></span>
    <a href="#nemotron-35-super-vl-trapaceou-no-teste" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>O <a href="https://huggingface.co/nvidia/NVIDIA-Nemotron-3.5-Super-VL-120B-A12B-NVFP4"target="_blank" rel="noopener">Nemotron 3.5 Super VL</a> da NVIDIA, 120 bilhões com 12 ativos, lançado dois dias antes de eu começar a testar e <a href="https://blogs.nvidia.com/blog/nemotron-3-5-super-vl/"target="_blank" rel="noopener">vendido pela NVIDIA</a> como o modelo aberto de agente mais rápido da categoria, com &ldquo;acurácia líder&rdquo; em tarefas multimodais, fez <strong>63</strong>. A sabotagem número 2 remove um link de navegação e o teste de regressão procura <code>have_link('Sign in')</code>. O Nemotron trocou o teste pra <code>have_button('Sign in')</code>, que passa no botão do formulário de login sem restaurar o link, e manteve a trapaça mesmo depois do sprint de revelação.</p>
<p>Também tende a pegar item um sprint atrasado. E foi o que exigiu um runtime próprio: vLLM 0.32 com CUDA 13 num ambiente separado.</p>
<h3>DeepSeek V4 Flash: 86 na nuvem, 44,5 na minha mão<span class="hx:absolute hx:-mt-20" id="deepseek-v4-flash-86-na-nuvem-445-na-minha-mão"></span>
    <a href="#deepseek-v4-flash-86-na-nuvem-445-na-minha-m%c3%a3o" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>O caso mais importante pra quem pensa em self-host. O <a href="https://huggingface.co/deepseek-ai/DeepSeek-V4-Flash"target="_blank" rel="noopener">DeepSeek V4 Flash</a>, 284 bilhões com 13 ativos, fez <strong>86, Tier A, por 97 centavos</strong> pela API em setembro, e a versão 0731, exatamente a que eu baixei, fez 85 por 1,94. Os mesmos pesos públicos em FP4 e FP8 misto, servidos por mim na B300, fizeram <strong>44,5, Tier D</strong>: pegou 4 itens sem aviso, mais 5 na revelação, e deixou 5 sem resolver. Foi o mais rápido a terminar, 37 minutos, e o que menos vigiou.</p>
<p>A causa provável é divergência entre o checkpoint público e o que a DeepSeek serve, ou entre o stack de serving deles e o meu: parser, template de chat, modo de raciocínio, quantização. Eu segui o card oficial. O que isso prova é que o nome do modelo no Hugging Face não garante a qualidade da API hospedada, e que quem vai hospedar precisa validar antes de confiar.</p>
<h3>GLM 4.7 Flash: igual ao local, ou seja, ruim<span class="hx:absolute hx:-mt-20" id="glm-47-flash-igual-ao-local-ou-seja-ruim"></span>
    <a href="#glm-47-flash-igual-ao-local-ou-seja-ruim" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>O GLM 4.7 Flash de 30 bilhões em BF16, a 217 tokens por segundo, fez 33,5. Em casa, em GGUF Q5, tinha feito 24. Precisou de dois empurrões, deixou nove itens sem resolver e entregou uma migration com erro de sintaxe. A diferença de nove pontos e meio cabe dentro do ruído de uma rodada; o que fica é que o modelo é fraco de qualquer jeito.</p>
<h3>O que apareceu em vários<span class="hx:absolute hx:-mt-20" id="o-que-apareceu-em-vários"></span>
    <a href="#o-que-apareceu-em-v%c3%a1rios" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Ornith, Muse Glimmer e o 397B compartilharam um defeito de higiene: rodaram o sprint até o fim com exit zero e esqueceram de commitar o próprio trabalho, deixando pro revisor fazer o checkpoint. Não entrou na nota, e num time de verdade seria o primeiro motivo de reclamação. E o aglomerado de 81 a 83, ThinkingCap, Ornith e Muse, tem um perfil consistente: pega injeção de SQL, vazamento entre tenants, XSS e segredo exposto com confiança, e deixa passar os itens silenciosos, controle de acesso quebrado, integridade de teste, índice faltando, agregação errada.</p>
<h2>A que distância da fronteira<span class="hx:absolute hx:-mt-20" id="a-que-distância-da-fronteira"></span>
    <a href="#a-que-dist%c3%a2ncia-da-fronteira" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Os três melhores abertos contra os melhores comerciais do mesmo ranking, com o custo e o tempo de cada rodada:</p>
<table>
  <thead>
      <tr>
          <th>Modelo</th>
          <th style="text-align: right">v4</th>
          <th>Tier</th>
          <th style="text-align: right">Custo da rodada</th>
          <th style="text-align: right">Minutos</th>
          <th>Onde</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>GPT-6 Astra</td>
          <td style="text-align: right">100,0</td>
          <td>A</td>
          <td style="text-align: right">US$ 30,55</td>
          <td style="text-align: right">100</td>
          <td>nuvem</td>
      </tr>
      <tr>
          <td>Claude Opus 5.5</td>
          <td style="text-align: right">100,0</td>
          <td>A</td>
          <td style="text-align: right">US$ 16,63</td>
          <td style="text-align: right">64</td>
          <td>nuvem</td>
      </tr>
      <tr>
          <td>GPT 5.6 terra</td>
          <td style="text-align: right">100,0</td>
          <td>A</td>
          <td style="text-align: right">US$ 9,52</td>
          <td style="text-align: right">80</td>
          <td>nuvem</td>
      </tr>
      <tr>
          <td>Grok 4.6</td>
          <td style="text-align: right">98,5</td>
          <td>A</td>
          <td style="text-align: right">US$ 13,00</td>
          <td style="text-align: right">67</td>
          <td>nuvem</td>
      </tr>
      <tr>
          <td>Claude Fable 5.1</td>
          <td style="text-align: right">95,5</td>
          <td>A</td>
          <td style="text-align: right">~US$ 51</td>
          <td style="text-align: right">133</td>
          <td>nuvem</td>
      </tr>
      <tr>
          <td><strong>GLM 5.3 Flash (NVFP4)</strong></td>
          <td style="text-align: right"><strong>92,5</strong></td>
          <td>A</td>
          <td style="text-align: right">US$ 21,96 só inferência</td>
          <td style="text-align: right">145</td>
          <td>B300</td>
      </tr>
      <tr>
          <td><strong>Qwen3.8 Flash-Next (NVFP4)</strong></td>
          <td style="text-align: right"><strong>92,5</strong></td>
          <td>A</td>
          <td style="text-align: right">US$ 12,04 só inferência</td>
          <td style="text-align: right">80</td>
          <td>B300</td>
      </tr>
      <tr>
          <td>Claude Sonnet 5</td>
          <td style="text-align: right">91,0</td>
          <td>A</td>
          <td style="text-align: right">~US$ 27</td>
          <td style="text-align: right">112</td>
          <td>nuvem</td>
      </tr>
      <tr>
          <td>Gemini 3.8 Flash high</td>
          <td style="text-align: right">90,5</td>
          <td>A</td>
          <td style="text-align: right">US$ 15,98</td>
          <td style="text-align: right">97</td>
          <td>nuvem</td>
      </tr>
      <tr>
          <td><strong>Swift 1.5 27B</strong></td>
          <td style="text-align: right"><strong>88,0</strong></td>
          <td>A</td>
          <td style="text-align: right">US$ 9,71 só inferência</td>
          <td style="text-align: right">64</td>
          <td>B300</td>
      </tr>
      <tr>
          <td>Claude Opus 4.6</td>
          <td style="text-align: right">87,5</td>
          <td>A</td>
          <td style="text-align: right">US$ 25,64</td>
          <td style="text-align: right">89</td>
          <td>nuvem</td>
      </tr>
  </tbody>
</table>
<p>A leitura: o melhor do open source que cabe numa B300 e que eu servi está <strong>um degrau abaixo do teto comercial e no mesmo nível do meio do Tier A</strong>. Diferenças de um ou dois pontos são ruído de uma rodada.</p>
<p>GLM 5.3 Flash e Qwen3.8 Flash-Next empatam com o Sonnet 5 e passam o Gemini 3.8 Flash e o Opus 4.6. Nenhum chega nos 100 que sete modelos comerciais fizeram, nem nos 98,5 do Grok 4.6. Sete pontos e meio separam o melhor aberto do teto, e no v4 cada ponto é uma sabotagem a menos pega sem aviso.</p>
<p>A diferença que importa é outra: previsibilidade. Entre os comerciais de ponta da tabela, o pior é 87,5, e o campo comercial inteiro também tem fundo de poço, o Mistral Large 3 fez 39. O campo aberto tem os mesmos 92,5 no topo e dois Tier D no fundo, com o DeepSeek fazendo 85 na nuvem e 44,5 na minha mão. Escolher um comercial de ponta é escolher um número que a API reproduz; escolher um aberto é apostar que o checkpoint, a quantização e o seu stack de serving reproduzem o número do card, e às vezes reproduzem.</p>
<h2>A conta<span class="hx:absolute hx:-mt-20" id="a-conta"></span>
    <a href="#a-conta" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Se a qualidade ainda fica abaixo da fronteira, o custo pelo menos é proporcional? Depende de onde você roda o modelo aberto.</p>
<p><strong>Na API em nuvem do próprio fornecedor, sim, e com folga.</strong> Uma rodada completa do v4 custou 97 centavos no DeepSeek V4 Flash, 1,17 dólar no Qwen 3.8 Flash, 1,03 no MiMo V2.5 Pro, todos Tier A na nuvem. Contra 9,52 a 30,55 dólares dos modelos de 100 pontos, é dez a trinta vezes mais barato por uma nota 8 a 14 pontos menor. Os preços de lista explicam: o <a href="https://docs.z.ai/guides/overview/pricing"target="_blank" rel="noopener">GLM 5.3 Flash</a> custa 15 centavos por milhão de tokens de entrada e 50 de saída; o <a href="https://api-docs.deepseek.com/quick_start/pricing"target="_blank" rel="noopener">DeepSeek Flash</a>, 15 a 30 centavos de entrada e 60 centavos a 1,20 de saída; o <a href="https://platform.claude.com/docs/en/about-claude/pricing"target="_blank" rel="noopener">Opus 5.5</a>, 4 e 20 dólares; o <a href="https://developers.openai.com/api/docs/pricing"target="_blank" rel="noopener">GPT-6 Astra</a>, 10 e 50.</p>
<p><strong>Na sua própria B300 alugada, não.</strong> O pod custa 9 dólares por hora ligado ou ocioso, <strong>218 por dia, uns 6.500 por mês</strong>. O tempo de modelo desta leva inteira, 14 modelos entre download, carga e sprints, somou 23 horas, uns 212 dólares a preço de pod; só os sprints foram 17 horas e 157 dólares.</p>
<p>Só que o pod cobra também o tempo em que nada roda. Eu coloquei 400 dólares de crédito na Runpod e sobraram 127: a leva inteira custou <strong>273 dólares</strong>, umas 30 horas de pod, incluindo armazenamento, quatro pods em sequência e sete horas ociosas numa madrugada em que a orquestração morreu e nada avisou. Quase 60 dólares a mais que o tempo de modelo, só de espera. A conta de ponto de equilíbrio do repositório: o pod só fica mais barato que a API a partir de umas <strong>6.500 rodadas por mês</strong>, 217 por dia, sustentadas. Um desenvolvedor sozinho ou um time pequeno nem chega perto.</p>
<p>E modelo contra modelo, lado a lado:</p>
<table>
  <thead>
      <tr>
          <th>Modelo</th>
          <th>API em nuvem</th>
          <th>Hospedado por mim na B300</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>DeepSeek V4 Flash 0731</td>
          <td>85 (A) por US$ 1,94</td>
          <td>44,5 (D) por US$ 5,59 só de inferência</td>
      </tr>
      <tr>
          <td>Qwen 3.8 Flash (nuvem) e Flash-Next (B300), da mesma família</td>
          <td>86,5 (A) por US$ 1,17</td>
          <td>92,5 (A) por US$ 12,04</td>
      </tr>
      <tr>
          <td>Qwen 3.8 27B</td>
          <td>80 (B) de graça no Strix, em 706 minutos</td>
          <td>80,5 (B) por US$ 29,11, em 192 minutos</td>
      </tr>
  </tbody>
</table>
<blockquote>
  <p>A API em nuvem de um modelo aberto é dez a trinta vezes mais barata que a fronteira por um tier a menos. Hospedar o mesmo modelo numa B300 alugada é mais caro que a fronteira, e às vezes pior que a própria API dele.</p>

</blockquote>
<h2>Cabe um time na mesma placa?<span class="hx:absolute hx:-mt-20" id="cabe-um-time-na-mesma-placa"></span>
    <a href="#cabe-um-time-na-mesma-placa" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Uma pergunta que eu fiz olhando a GPU a 10% de uso: esses modelos usam toda a computação do pod? Dava pra colocar um time inteiro na mesma B300?</p>
<p>Em princípio, sim, e é pra isso que o vLLM existe. O <a href="https://vllm.ai/blog/2023-06-20-vllm"target="_blank" rel="noopener">batching contínuo</a> agenda várias requisições no mesmo passo de decodificação, e o <a href="https://vllm.ai/blog/2023-06-20-vllm"target="_blank" rel="noopener">PagedAttention</a> guarda o cache de KV de cada sessão em blocos não contíguos, com menos de 4% de desperdício, justamente pra caber mais sessões na mesma memória. Um modelo de 27 bilhões em BF16 ocupa 56 GB e deixa uns 200 GB de cache de KV: são dezenas de sessões de agente com contexto de 128 mil tokens ao mesmo tempo. O 397B, com 251 GB de pesos, deixa 24, e aí cabem poucas.</p>
<p>O que não escala é a velocidade por pessoa. O meu teste rodou em <code>--performance-mode interactivity</code>, que otimiza a latência de um fluxo só, e os 200 tokens por segundo são desse fluxo. Com dez agentes decodificando ao mesmo tempo, a vazão agregada da placa sobe e a de cada um cai, e a decodificação especulativa que me deu os 295 do Muse Glimmer rende menos conforme a concorrência cresce: um <a href="https://huggingface.co/z-lab/Muse-Glimmer-30B-DFlash2"target="_blank" rel="noopener">rascunhador DFlash publicado pro Muse pelo Z Lab</a> reporta 3 a 4,6 vezes de ganho com uma sessão e 1,2 a 1,7 com 32.</p>
<p>Eu não medi concorrência nesta leva, então o que posso dizer é o contorno: um time de cinco a dez pessoas num modelo de 27 a 35 bilhões numa B300 é prático, e provavelmente cada um ainda fica acima dos 100 tokens por segundo. O mesmo time no 397B é inviável na placa, e no GLM 5.3 Flash de 198 GB fica apertado.</p>
<p>É nesse cenário, e só nele, que a conta do pod começa a fechar. Dez desenvolvedores rodando agente o dia inteiro num modelo de 27B gastariam na API, a um dólar por rodada de duas horas, uns 40 dólares por dia; o pod custa 218. Ainda perde. O que muda a conta é ter cem pessoas, ou ter a obrigação de que o código não saia da sua rede.</p>
<h2>Vale a pena ter a própria infra?<span class="hx:absolute hx:-mt-20" id="vale-a-pena-ter-a-própria-infra"></span>
    <a href="#vale-a-pena-ter-a-pr%c3%b3pria-infra" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Pra desenvolvimento de software sério, com os dados desta leva, a resposta vem em três partes.</p>
<p><strong>Os modelos abertos de ponta são bons o suficiente.</strong> GLM 5.3 Flash, Qwen3.8 Flash-Next e Swift 1.5 27B são Tier A no meu teste, no nível do Sonnet 5 e acima do Opus 4.6. Isso era impensável em abril, quando <a href="/2026/04/05/testando-llms-open-source-e-comerciais-quem-consegue-bater-o-claude-opus/">só o Qwen 3.6 35B chegava perto do Opus</a>, e ainda a uma correção de distância. Pra quem aceita um tier abaixo do teto, eles servem pra trabalho de verdade, inclusive revisão de segurança, que é o que o v4 mede.</p>
<p><strong>Alugar uma B300 pra rodar eles é o jeito errado de usá-los.</strong> A API em nuvem do mesmo modelo custa um dólar por rodada e precisa de zero configuração. O pod custa 218 por dia, exige um dia de ajuste por família de modelo, e no caso do DeepSeek entregou um modelo pior do que a API dele. Só vale com uma obrigação de residência de dados, com um checkpoint que nenhuma nuvem serve, com carga sustentada de centenas de rodadas por dia, ou com necessidade de pesos fixados pra reprodutibilidade. Quatro B300 pra rodar os de 750 bilhões a um trilhão quadruplicam a conta sem evidência de que a nota chega nos 100: o 397B, maior modelo que eu testei, fez 68,5.</p>
<p><strong>E se for pra hospedar, hospede pequeno.</strong> O Swift 1.5 27B e o ThinkingCap em 56 GB, com MTP, a 200 tokens por segundo, são Tier A em hardware que dá pra comprar, uma placa profissional de 80 ou 96 GB em vez de uma de datacenter. É ali que o open source faz sentido pra uma empresa: um time inteiro numa placa, código dentro da rede, nota de Opus 4.6. Só olhe a licença antes: o Swift e o ThinkingCap saem com licenças próprias, e acima de um certo faturamento os dois exigem licença comercial. Os gigantes abertos continuam sendo coisa pra API.</p>
<p>Minha regra depois disso tudo: modelo comercial de ponta pra tarefa que não pode errar, API de modelo aberto pra volume, e placa própria só quando a lei ou o cliente exigem. O repositório com cada argumento de serving, cada log de sondagem, cada transcrição de revisor e a proveniência Codex ou Claude de cada fronteira está <a href="https://github.com/akitaonrails/llm-coding-benchmark"target="_blank" rel="noopener">público</a>, como sempre.</p>
]]></content:encoded><category>benchmarks-de-llm</category><category>modelos-locais</category><category>llms</category><category>hardware</category></item><item><title>Treinei meu próprio modelo de IA (Spoiler: é só pra estudo)</title><link>https://www.akitaonrails.com/2026/10/14/treinei-meu-proprio-modelo-de-ia-spoiler-e-so-pra-estudo/</link><guid isPermaLink="true">https://www.akitaonrails.com/2026/10/14/treinei-meu-proprio-modelo-de-ia-spoiler-e-so-pra-estudo/</guid><pubDate>Wed, 14 Oct 2026 15:00:00 -0300</pubDate><description>&lt;p&gt;Faltava um exercício na minha lista deste ano: tentar construir o meu próprio LLM. Não um modelo de fronteira. Construir um GPT-5 ou um Claude exige anos de estudo e experimentação, times inteiros e centenas de milhões de dólares em computação. A estimativa do &lt;a href="https://hai.stanford.edu/ai-index/2024-ai-index-report"target="_blank" rel="noopener"&gt;AI Index de Stanford&lt;/a&gt; pro GPT-4 foi de US$ 78 milhões só de compute, e pro Gemini Ultra, US$ 191 milhões.&lt;/p&gt;
&lt;p&gt;Só que ninguém precisa de um modelo classe GPT pra tudo. Modelo de fronteira é generalista por definição: tenta saber tudo sobre qualquer coisa, de direito tributário a letra de música, e o retorno disso é decrescente.&lt;/p&gt;</description><content:encoded><![CDATA[<p>Faltava um exercício na minha lista deste ano: tentar construir o meu próprio LLM. Não um modelo de fronteira. Construir um GPT-5 ou um Claude exige anos de estudo e experimentação, times inteiros e centenas de milhões de dólares em computação. A estimativa do <a href="https://hai.stanford.edu/ai-index/2024-ai-index-report"target="_blank" rel="noopener">AI Index de Stanford</a> pro GPT-4 foi de US$ 78 milhões só de compute, e pro Gemini Ultra, US$ 191 milhões.</p>
<p>Só que ninguém precisa de um modelo classe GPT pra tudo. Modelo de fronteira é generalista por definição: tenta saber tudo sobre qualquer coisa, de direito tributário a letra de música, e o retorno disso é decrescente.</p>
<p>E a maioria deles hoje é mistura de especialistas, <em>mixture of experts</em>: grosso modo, vários modelos menores agrupados num bloco gigante de bits, com um roteador que acorda só alguns pra cada token. O <a href="https://arxiv.org/abs/2508.10925"target="_blank" rel="noopener">gpt-oss-120b</a> da OpenAI tem 117 bilhões de parâmetros no total e usa 5,1 bilhões por token. O <a href="https://arxiv.org/abs/2412.19437"target="_blank" rel="noopener">DeepSeek-V3</a> tem 671 bilhões e ativa 37. O modelo grande já é, por dentro, uma coleção de modelos pequenos.</p>
<p>Eu podia ter pego um modelo aberto, gpt-oss, Qwen, Phi, e adaptado. As técnicas existem e são bem documentadas:</p>
<ul>
<li><strong>Fine-tuning completo</strong>: continuar treinando todos os pesos de um modelo pronto nos seus dados. É a transferência de aprendizado clássica, formalizada pro texto pelo <a href="https://arxiv.org/abs/1801.06146"target="_blank" rel="noopener">ULMFiT</a> em 2018.</li>
<li><strong>LoRA e QLoRA</strong>: congelar os pesos originais e treinar só matrizes pequenas de baixo posto ao lado deles. O <a href="https://arxiv.org/abs/2106.09685"target="_blank" rel="noopener">LoRA</a> reduz em até dez mil vezes os parâmetros treináveis; o <a href="https://arxiv.org/abs/2305.14314"target="_blank" rel="noopener">QLoRA</a> quantiza o modelo base em 4 bits e ajusta um modelo de 65 bilhões numa única GPU de 48 GB.</li>
<li><strong>Destilação</strong>: treinar um modelo menor pra imitar as saídas de um maior. A ideia é de <a href="https://arxiv.org/abs/1503.02531"target="_blank" rel="noopener">Hinton, 2015</a>, e o exemplo recente mais famoso é o <a href="https://arxiv.org/abs/2501.12948"target="_blank" rel="noopener">DeepSeek-R1</a> destilado em versões de 1,5 a 70 bilhões em cima de Qwen e Llama.</li>
<li><strong>Pré-treino continuado</strong>: mais uma fase de treino em texto do seu domínio antes de qualquer ajuste de instrução, o <a href="https://arxiv.org/abs/2004.10964"target="_blank" rel="noopener">&ldquo;Don&rsquo;t Stop Pretraining&rdquo;</a> de 2020.</li>
<li><strong>SFT, RLHF e DPO</strong>: ensinar o formato de assistente com demonstrações, depois alinhar com preferência humana via aprendizado por reforço (<a href="https://arxiv.org/abs/2203.02155"target="_blank" rel="noopener">InstructGPT</a>) ou com uma perda de classificação direta (<a href="https://arxiv.org/abs/2305.18290"target="_blank" rel="noopener">DPO</a>).</li>
</ul>
<p>Com qualquer uma dessas eu teria um modelo útil em horas. Só que o objetivo era outro. Eu queria organizar, com as mãos, as técnicas básicas pra construir um modelo menor que o GPT-2, do zero, em poucos dias, e ver até onde dava pra chegar. Se desse, treinar um modelo minúsculo partindo de pesos aleatórios e adicionar funcionalidade suficiente pra ele quase funcionar dentro de um harness agêntico como o OpenCode.</p>
<p>Antes de continuar, quem eu sou nessa história: eu sou engenheiro de software curioso e amador no assunto. Pesquisador de LLM eu sou zero. Eu sabia que não ia ter um LLM funcional em tão pouco tempo, e isso faz parte do exercício.</p>
<p>Mas não é inalcançável. Se você é estudante de computação, estuda sério e dá anos pra isso, você consegue tentar por conta própria. E se você não é cientista, entenda isto: ninguém precisa ser Einstein pra fazer isso. Os fundamentos estão todos disponíveis, publicados, pra você estudar e tentar. O que é obrigatório é suor.</p>
<p>Spoiler: a gente chegou longe no &ldquo;quase&rdquo; e parou lá. O <a href="https://github.com/akitaonrails/microgpt"target="_blank" rel="noopener">repositório é público</a>, com cada etapa e cada fracasso documentado. Este texto é o resumo.</p>
<blockquote>
  <p><strong>TL;DR:</strong> parti do microgpt do Karpathy, 200 linhas de Python puro que treinam um GPT em um minuto num MacBook, e expandi pra um laboratório em PyTorch com tokenizer próprio, pré-treino, ajuste supervisionado, servidor compatível com a API da OpenAI e integração com o OpenCode. Em menos de duas semanas, numa RTX 5090, treinei um modelo de 337 milhões de parâmetros em 10 bilhões de tokens: 137 horas de treino, no máximo uns R$ 80 de luz. Ele acerta 58 de 100 funções Python simples de um conjunto reservado, e zera em tudo que exige ler, editar e testar um repositório. O <a href="https://huggingface.co/HuggingFaceTB/SmolLM2-360M"target="_blank" rel="noopener">SmolLM2 de 360 milhões</a>, do mesmo tamanho, viu 4 trilhões de tokens, 400 vezes mais. Pule pro <a href="#o-exerc%c3%adcio-do-karpathy">exercício do Karpathy</a>, pras <a href="#as-etapas-de-construir-um-llm">etapas</a>, pras <a href="#as-dimens%c3%b5es-do-problema">dimensões</a>, pros <a href="#os-trope%c3%a7os">tropeços</a>, pro <a href="#o-que-saiu-do-outro-lado">resultado</a> ou pra <a href="#o-futuro-%c3%a9-menor-e-mais-especializado">conclusão</a>.</p>

</blockquote>
<h2>O exercício do Karpathy<span class="hx:absolute hx:-mt-20" id="o-exercício-do-karpathy"></span>
    <a href="#o-exerc%c3%adcio-do-karpathy" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Em fevereiro deste ano o Andrej Karpathy publicou o <a href="https://karpathy.github.io/2026/02/12/microgpt/"target="_blank" rel="noopener">microgpt</a>: um único arquivo de 200 linhas de Python, sem dependência nenhuma, que treina e roda um GPT. Ele chama de &ldquo;o conteúdo algorítmico completo do que é necessário. Todo o resto é só eficiência&rdquo;. É a culminação de uma linhagem dele: o micrograd (autograd escalar), o makemore (gerar nomes) e o nanoGPT (GPT-2 em PyTorch compacto), à qual se soma o <a href="https://github.com/karpathy/nanochat"target="_blank" rel="noopener">nanochat</a>, &ldquo;o melhor ChatGPT que 100 dólares compram&rdquo;, que no lançamento treinava um modelo de capacidade GPT-2 em quase quatro horas num nó de oito H100, e hoje faz isso em umas duas.</p>
<p>O microgpt é propositalmente mínimo: o dataset são 32 mil nomes próprios, um por linha, e o tokenizer é um caractere por token.</p>
<p>A rede tem uma camada, 16 dimensões de embedding, 4 cabeças de atenção, janela de 16 tokens porque o nome mais longo tem 15 letras. Treina por mil passos, um nome por passo, com Adam escrito à mão, e no fim gera nomes inventados, tudo em um minuto num MacBook.</p>
<p>O que ele ensina, por estar tudo exposto num arquivo só:</p>
<ul>
<li>o autograd, a classe <code>Value</code> que guarda um número, o gradiente e a regra da cadeia pra propagar de volta;</li>
<li>o que é um token e um embedding;</li>
<li>o bloco de atenção, com consulta, chave e valor, e a máscara causal que impede o modelo de espiar o futuro;</li>
<li>a perda de entropia cruzada contra o próximo token;</li>
<li>o otimizador;</li>
<li>a amostragem com temperatura.</li>
</ul>
<p>É um GPT-2 com diferenças pequenas que o próprio arquivo anota: RMSNorm no lugar de LayerNorm, sem bias, ReLU em vez de GeLU. Depois de ler aquilo, o que sobra entre você e um modelo de verdade é, nas palavras dele, eficiência. Eu fui ver quanto de eficiência.</p>
<h2>O que a gente expandiu<span class="hx:absolute hx:-mt-20" id="o-que-a-gente-expandiu"></span>
    <a href="#o-que-a-gente-expandiu" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>O <a href="https://github.com/akitaonrails/microgpt"target="_blank" rel="noopener">microgpt lab</a> é uma reimplementação independente em PyTorch, inspirada no gist, dividida por conceito: umas 3.300 linhas de Python em 31 módulos, mais scripts e 85 testes de CPU. Uma única implementação de modelo serve pra pré-treino, ajuste de assistente e servidor. Todo peso começa aleatório; o projeto não baixa peso pré-treinado nenhum, de propósito, porque a regra do exercício era do zero.</p>
<p>Em cima do bloco do Karpathy entraram os componentes que um modelo de 2026 usa: RoPE pra posição, RMSNorm, SwiGLU na camada densa, embeddings amarrados à saída, Flash Attention via PyTorch, cache de chaves e valores com reuso de prefixo. E em volta do modelo, o que falta pra ele conversar com um agente: um protocolo de conversa com papéis, um servidor HTTP compatível com a API de chat da OpenAI, chamadas de ferramenta validadas por esquema e opcionalmente por gramática, separação de conteúdo de raciocínio, e a configuração do OpenCode apontando pra ele.</p>
<p>O projeto passou por quatro escalas:</p>
<table>
  <thead>
      <tr>
          <th>Modelo</th>
          <th style="text-align: right">Parâmetros</th>
          <th>Tokenizer</th>
          <th>Pra quê</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Lição em bytes</td>
          <td style="text-align: right">0,9 milhão</td>
          <td>byte a byte</td>
          <td>roda em CPU, ensina o loop inteiro</td>
      </tr>
      <tr>
          <td>Estudante</td>
          <td style="text-align: right">91 milhões</td>
          <td>o200k (200 mil entradas)</td>
          <td>primeiro agente, aritmética</td>
      </tr>
      <tr>
          <td>Candidato de capacidade</td>
          <td style="text-align: right">239 milhões</td>
          <td>o200k</td>
          <td>mais camadas, mesmo vocabulário</td>
      </tr>
      <tr>
          <td>Pivô de fundação</td>
          <td style="text-align: right">337 milhões</td>
          <td>BPE próprio, 32 mil entradas</td>
          <td>o modelo final</td>
      </tr>
  </tbody>
</table>
<p>A tabela esconde a primeira lição grande, que eu conto nos tropeços: no modelo de 239 milhões, 154 milhões de parâmetros eram só a tabela de vocabulário. Sobravam 85 milhões pro transformer de verdade.</p>
<h2>As etapas de construir um LLM<span class="hx:absolute hx:-mt-20" id="as-etapas-de-construir-um-llm"></span>
    <a href="#as-etapas-de-construir-um-llm" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Independente do tamanho, o caminho tem as mesmas estações. O que muda é o zero à direita em cada uma.</p>
<h3>1. Dados<span class="hx:absolute hx:-mt-20" id="1-dados"></span>
    <a href="#1-dados" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Modelo é o que ele come. Baixamos, com revisão fixada e hash, seleções de corpora públicos: <a href="https://huggingface.co/datasets/codeparrot/codeparrot-clean"target="_blank" rel="noopener">CodeParrot</a> (Python deduplicado e filtrado), <a href="https://huggingface.co/datasets/OpenCoder-LLM/opc-sft-stage1"target="_blank" rel="noopener">OpenCoder</a> (instrução e código), <a href="https://huggingface.co/datasets/HuggingFaceTB/smollm-corpus"target="_blank" rel="noopener">Cosmopedia</a> (texto educativo sintético), <a href="https://huggingface.co/datasets/HuggingFaceTB/smoltalk"target="_blank" rel="noopener">SmolTalk</a> (conversa) e uma fatia multilíngue. Os 14 manifestos de download somam uns 20 GB brutos, tudo guardado no NAS.</p>
<p>Daí vem a curadoria: deduplicação exata, divisão de treino e validação por repositório ou grupo de conversa (nunca por linha, senão o mesmo projeto vaza pros dois lados), e a mistura. O corpus final de fundação tem 3,5 bilhões de tokens armazenados, 60% código Python, 16% instrução, 12% texto educativo, 8% conversa, 4% multilíngue. Cada proporção é uma decisão de projeto, e nenhuma delas tem resposta certa publicada.</p>
<h3>2. Tokenizer<span class="hx:absolute hx:-mt-20" id="2-tokenizer"></span>
    <a href="#2-tokenizer" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>O tokenizer é o dicionário entre texto e inteiros. A lição em bytes usa 256 tokens mais um de fim de documento. O estudante usou o o200k, o vocabulário público da OpenAI com 200 mil entradas, que é o que a maioria dos tutoriais faz. O pivô treinou um BPE próprio de 32.768 entradas, em cima de 92 MB de texto só de treino, com cobertura de UTF-8 completa.</p>
<p>A escolha parece detalhe e decide onde os parâmetros vão parar. Cada entrada do vocabulário custa uma linha na tabela de embedding com a largura do modelo. Com 200 mil entradas e largura 768, são 154 milhões de parâmetros gastos em tabela. Com 32 mil entradas e largura 1.024, são 33 milhões, e o resto vai pras camadas que pensam.</p>
<h3>3. Pré-treino, ou fundação<span class="hx:absolute hx:-mt-20" id="3-pré-treino-ou-fundação"></span>
    <a href="#3-pr%c3%a9-treino-ou-funda%c3%a7%c3%a3o" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Aqui é o loop do Karpathy em escala. O modelo recebe janelas de 2.048 tokens tiradas do fluxo de treino e aprende a prever o próximo token. Cada atualização processa 32.768 tokens: duas sequências, acumuladas oito vezes. AdamW, taxa de aprendizado com aquecimento e decaimento em cosseno, recorte de gradiente, BF16 na GPU e FP32 nos pesos.</p>
<p>O pivô rodou uns 305 mil atualizações, 10 bilhões de tokens de exposição. Como o corpus tem 3,5 bilhões, cada documento foi visto, na expectativa da mistura, quase três vezes: o código umas duas, e instrução, texto educativo e conversa umas nove ou dez. A cada 500 atualizações, uma validação e um checkpoint. Nos marcos de 2, 5 e 10 bilhões, um checkpoint congelado com hash, pra medir a curva.</p>
<h3>4. Ajuste supervisionado<span class="hx:absolute hx:-mt-20" id="4-ajuste-supervisionado"></span>
    <a href="#4-ajuste-supervisionado" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Pré-treino produz um modelo que completa texto. Pra ele responder a um pedido, vem o SFT, <em>supervised fine-tuning</em>: exemplos de pergunta e resposta no formato exato que o servidor usa, com a perda calculada só sobre os tokens do assistente. O que o usuário e a ferramenta disseram entra como contexto e é mascarado da perda.</p>
<p>Os exemplos vieram do <a href="https://huggingface.co/datasets/nvidia/OpenCodeInstruct"target="_blank" rel="noopener">OpenCodeInstruct</a> da NVIDIA, e cada resposta foi executada num Docker restrito antes de entrar: quase 99 mil candidatos, 97 mil sobreviveram ao teste com três sementes de hash diferentes. Sobraram 92 mil episódios de treino. O checkpoint escolhido viu 64 mil sorteios desses episódios, uns 10 milhões de tokens de resposta.</p>
<h3>5. Protocolo, ferramentas e servidor<span class="hx:absolute hx:-mt-20" id="5-protocolo-ferramentas-e-servidor"></span>
    <a href="#5-protocolo-ferramentas-e-servidor" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Um agente como o OpenCode fala com o modelo por HTTP: manda o histórico com papéis, as definições de ferramenta, recebe de volta texto ou uma chamada de ferramenta em JSON, executa, devolve o resultado, e repete. O laboratório implementa esse ciclo inteiro: o mesmo arquivo de protocolo serializa os exemplos de treino e os prompts de serviço, pra formato de treino e formato de uso nunca divergirem.</p>
<p>O servidor implementa <code>/v1/models</code> e <code>/v1/chat/completions</code>, valida a chamada de ferramenta contra o esquema, pode restringir a geração com gramática pra JSON sair bem formado, separa o conteúdo de raciocínio da resposta, conta tokens em cache e recusa estouro de contexto em vez de truncar em silêncio. Isso tudo é encanamento, zero inteligência, e sem ele o modelo nem senta à mesa.</p>
<h3>6. Avaliação<span class="hx:absolute hx:-mt-20" id="6-avaliação"></span>
    <a href="#6-avalia%c3%a7%c3%a3o" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>A etapa que mais mudou o projeto. Um conjunto congelado de 100 tarefas Python de desenvolvimento, 20 famílias com cinco variantes de nome e enunciado, mais 100 tarefas reservadas que só o checkpoint final viu. Cada resposta roda num Docker sem rede, sem root, com limite de CPU e memória, contra asserções fixas. Além disso: quatro tarefas reais de conserto de repositório via OpenCode, 12 problemas de rastrear a saída exata de um programa, sondas de recuperação de informação em prompts de 8, 16 e 32 mil tokens, e quatro conversas curtas.</p>
<blockquote>
  <p>Nada de HumanEval, MBPP ou SWE-bench. São diagnósticos locais pequenos, com famílias correlacionadas, uma semente, geração gulosa. Servem pra decidir entre checkpoints, e o próprio relatório de fechamento manda não ler &ldquo;58 de 100&rdquo; como &ldquo;58% de sucesso em programação&rdquo;.</p>

</blockquote>
<h2>As dimensões do problema<span class="hx:absolute hx:-mt-20" id="as-dimensões-do-problema"></span>
    <a href="#as-dimens%c3%b5es-do-problema" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>O que tudo isso custou, e como isso se compara com o mundo real:</p>
<table>
  <thead>
      <tr>
          <th>Dimensão</th>
          <th style="text-align: right">Nosso modelo final</th>
          <th>Referências</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Parâmetros</td>
          <td style="text-align: right">337 milhões</td>
          <td>GPT-2 pequeno: 124 milhões; GPT-2 completo (2019): 1,5 bilhão</td>
      </tr>
      <tr>
          <td>Tokens vistos no pré-treino</td>
          <td style="text-align: right">10 bilhões</td>
          <td>SmolLM2-360M: 4 trilhões; Llama 3.1: 15 trilhões; Qwen3: 36 trilhões</td>
      </tr>
      <tr>
          <td>Dados brutos baixados</td>
          <td style="text-align: right">20 GB</td>
          <td>GPT-2: 40 GB de WebText, 8 milhões de páginas</td>
      </tr>
      <tr>
          <td>Corpus de fundação</td>
          <td style="text-align: right">3,5 bilhões de tokens, 2,35 milhões de documentos</td>
          <td></td>
      </tr>
      <tr>
          <td>Hardware</td>
          <td style="text-align: right">1 RTX 5090, 32 GB</td>
          <td>Llama 3.1 405B: 30,84 milhões de horas de H100</td>
      </tr>
      <tr>
          <td>Tempo de GPU na fundação</td>
          <td style="text-align: right">137 horas de processo, 6,4 dias de calendário</td>
          <td>DeepSeek-V3: 2,79 milhões de horas de H800, ~US$ 5,6 milhões</td>
      </tr>
      <tr>
          <td>Tempo de SFT</td>
          <td style="text-align: right">3,4 horas somando os nove estágios</td>
          <td></td>
      </tr>
      <tr>
          <td>Vazão</td>
          <td style="text-align: right">23 a 44 mil tokens/s, dependendo de I/O</td>
          <td></td>
      </tr>
      <tr>
          <td>VRAM de pico</td>
          <td style="text-align: right">13,9 GB na fundação; 6,8 GB no SFT</td>
          <td></td>
      </tr>
      <tr>
          <td>Checkpoints</td>
          <td style="text-align: right">17 checkpoints distintos (20 caminhos), 69 GB</td>
          <td></td>
      </tr>
      <tr>
          <td>Custo em dinheiro</td>
          <td style="text-align: right">zero em nuvem; uns R$ 80 de luz no pior caso</td>
          <td>nanochat: US$ 100 em 8 H100 por 4 horas no lançamento; hoje uns US$ 48 em 2 horas</td>
      </tr>
  </tbody>
</table>
<p>A conta da luz: a RTX 5090 tem <a href="https://www.nvidia.com/en-us/geforce/graphics-cards/50-series/rtx-5090/"target="_blank" rel="noopener">575 W de potência nominal</a>. Em 137 horas a plena carga são uns 79 kWh, e a tarifa residencial da Enel em São Paulo em 2026, somando TUSD, TE e impostos, fica em torno de R$ 1 por kWh. Como o treino rodou boa parte do tempo a 75% de ciclo pra não travar o desktop, o real foi menos. É o custo marginal de quem já tem a placa.</p>
<p>Alugar uma 5090 na <a href="https://www.runpod.io/pricing"target="_blank" rel="noopener">Runpod</a> sai a uns US$ 1,20 por hora, então as mesmas 137 horas custariam uns US$ 165; uma H100 sai entre US$ 2,90 e 4 por hora, dependendo da variante.</p>
<p>E a comparação que dá a escala. O SmolLM2 de 360 milhões de parâmetros, praticamente o tamanho do nosso, foi treinado em 4 trilhões de tokens. Na nossa vazão, 4 trilhões levariam entre quatro e seis anos de GPU ininterrupta. Mesmo tamanho de modelo, 400 vezes a quantidade de estudo.</p>
<p>É por isso que &ldquo;parâmetros&rdquo; é uma unidade enganosa: diz o tamanho do cérebro, e nada sobre quantos anos de escola ele teve.</p>
<p>A regra de bolso derivada de <a href="https://arxiv.org/abs/2203.15556"target="_blank" rel="noopener">Chinchilla</a> (70 bilhões de parâmetros, 1,4 trilhão de tokens), uns 20 tokens por parâmetro pra ser computacionalmente ótimo, daria 6,7 bilhões de tokens pro nosso modelo. Passamos disso. Só que o ótimo de Chinchilla é o ponto de melhor uso de compute, e modelos pequenos modernos passam muito dele de propósito, porque o custo de inferência depois compensa treinar mais.</p>
<h2>Os tropeços<span class="hx:absolute hx:-mt-20" id="os-tropeços"></span>
    <a href="#os-trope%c3%a7os" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>A lista completa está no <a href="https://github.com/akitaonrails/microgpt/blob/main/docs/report/experiment-closeout.md"target="_blank" rel="noopener">relatório de fechamento</a>. Os que mais ensinaram:</p>
<p><strong>Medir perto demais do treino.</strong> O primeiro agente, o de 91 milhões, completou quatro sessões de 16 rodadas no OpenCode e uma de 32 rodadas com 130 chamadas de ferramenta reais, recuperando de erro de verificação e trocando de sessão no meio, com o estado guardado pelo harness. Parecia pronto.</p>
<p>Aí eu pedi, na mão e em inglês, uma função hello world que recebe um nome como parâmetro e imprime na mensagem. O modelo repetiu duas vezes a edição aritmética anterior, apontou pra um arquivo que não existia, e na terceira tentativa cuspiu JSON inválido. Tudo que ele sabia era a família de tarefa do treino.</p>
<p><strong>O vocabulário comeu o modelo.</strong> O candidato de 239 milhões tinha mais camadas e perda menor que o de 91 e resolvia 24 de 100 problemas. Ao olhar a distribuição dos parâmetros, 154 milhões eram tabela de vocabulário. O pivô pra 32 mil entradas devolveu 300 milhões de parâmetros pro transformer. O pivô mudou vocabulário, distribuição de capacidade, inicialização e exposição de uma vez, então o relatório não isola qual delas moveu o resultado.</p>
<p><strong>Perda não é competência.</strong> No SFT final, continuar de 4 mil pra 8 mil atualizações baixou a perda de validação e derrubou o acerto executado de 68 pra 62. Loss mais baixa, código pior. A seleção de checkpoint passou a ser por tarefa executada, congelada antes; o número da perda ficou de fora.</p>
<p><strong>Dados e avaliadores mentem.</strong> Respostas &ldquo;corretas&rdquo; do dataset público falhavam ao serem executadas de novo. Testes que dependiam da ordem de hash do Python, builtins sombreados, <code>SystemExit(0)</code> contando como sucesso, edições vazias copiando o arquivo. Cada um precisou de um controle: três sementes de hash, sentinela de conclusão, fixture vazia que precisa falhar. E num dos treinos mistos, o modelo aprendeu a dizer que os testes passaram quando não tinham passado.</p>
<p><strong>Formato de treino e formato de uso precisam bater.</strong> Anúncio de troca de sessão, ID de chamada de ferramenta, âncora de edição, o jeito como o OpenCode cita a entrada do usuário: cada diferença entre o que o modelo viu no treino e o que recebeu no servidor derrubava o agente antigo. O mesmo arquivo de protocolo pros dois lados foi a solução, e ainda assim mudar formato às vezes melhorava o nome da função sem melhorar o código.</p>
<p><strong>Tokenização cruza fronteiras de correção.</strong> A string literal <code>&lt;|endoftext|&gt;</code> dentro de código fonte precisa continuar sendo texto comum em vez de virar o marcador de fim de documento. Reconstruir documentos UTF-8 sem partir um caractere no meio. O suporte do XGrammar a vocabulário em nível de byte. A primeira avaliação de código do marco de 2 bilhões falhou na construção da gramática, e foi corrigida e rerodada em vez de contar como falha do modelo.</p>
<p><strong>O NAS quase me custou dias.</strong> Na retomada da fase de 10 bilhões, o treinador carregou o passo 81.500 em vez do 152.588 congelado: o <code>last.pt</code> mutável tinha timestamp anterior à pausa, logo depois de uma manutenção no NAS, e a causa nunca foi provada. Descartamos 1,24 hora de treino, verificamos o hash do marco de 5 bilhões, e toda retomada seguinte passou a exigir hash, passo exato, estado do otimizador e do RNG, dataset e uma sonda de escrita com fsync. Nunca retome um treino só porque o arquivo se chama <code>last.pt</code>.</p>
<p><strong>GPU de desktop é GPU compartilhada.</strong> VRAM livre não impediu o navegador de travar. A solução foi um ciclo de trabalho cooperativo: o treinador sincroniza a GPU e dorme entre lotes, 75% do tempo durante o dia e 100% das 3 às 9 da manhã. E a leitura do corpus pelo NAS, via mmap, deixava a GPU ociosa esperando disco. Depois de uma manutenção a vazão caiu pra 5 mil tokens por segundo antes de voltar aos 23 mil.</p>
<p><strong>Parei numa fronteira do pipeline.</strong> O controlador do pivô implementava fundação, SFT de código e holdout. Terminou limpo, e &ldquo;terminou limpo&rdquo; cobria até a função Python isolada, enquanto o objetivo era o agente. O checkpoint final foi testado no OpenCode só no fechamento, e falhou. Devia ter sido testado antes de qualquer frase com &ldquo;pronto&rdquo;.</p>
<h2>O que saiu do outro lado<span class="hx:absolute hx:-mt-20" id="o-que-saiu-do-outro-lado"></span>
    <a href="#o-que-saiu-do-outro-lado" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>A ficha do modelo final:</p>
<table>
  <thead>
      <tr>
          <th>Propriedade</th>
          <th>Valor</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Parâmetros</td>
          <td>337.167.360, sendo 303,6 milhões no transformer e 33,6 milhões na tabela de vocabulário</td>
      </tr>
      <tr>
          <td>Arquitetura</td>
          <td>24 camadas, largura 1.024, 16 cabeças, SwiGLU com 2.752 de largura oculta, RMSNorm, RoPE</td>
      </tr>
      <tr>
          <td>Vocabulário</td>
          <td>BPE próprio de 32.768 entradas, cobertura UTF-8</td>
      </tr>
      <tr>
          <td>Contexto</td>
          <td>32.768 tokens alocados; treinado em janelas de 2.048</td>
      </tr>
      <tr>
          <td>Fundação</td>
          <td>10 bilhões de tokens, 305.176 atualizações, perda de validação final 1,085</td>
      </tr>
      <tr>
          <td>SFT escolhido</td>
          <td>4.000 atualizações, 10,3 milhões de tokens de resposta</td>
      </tr>
      <tr>
          <td>Checkpoint</td>
          <td>4 GB com estado do otimizador; 1,35 GB só de pesos em FP32</td>
      </tr>
  </tbody>
</table>
<p>A curva de aprendizado, medida nos três marcos, com o mesmo SFT curto aplicado em cima de cada um:</p>
<table>
  <thead>
      <tr>
          <th style="text-align: right">Tokens de fundação</th>
          <th style="text-align: right">Completar código cru /100</th>
          <th style="text-align: right">Após SFT de 500 /100</th>
          <th style="text-align: right">Após SFT de 1.000 /100</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td style="text-align: right">2 bilhões</td>
          <td style="text-align: right">0</td>
          <td style="text-align: right">24</td>
          <td style="text-align: right">39</td>
      </tr>
      <tr>
          <td style="text-align: right">5 bilhões</td>
          <td style="text-align: right">0</td>
          <td style="text-align: right">49</td>
          <td style="text-align: right">52</td>
      </tr>
      <tr>
          <td style="text-align: right">10 bilhões</td>
          <td style="text-align: right">8</td>
          <td style="text-align: right">56</td>
          <td style="text-align: right">65</td>
      </tr>
  </tbody>
</table>
<p>O SFT mais longo em cima dos 10 bilhões chegou a <strong>68 de 100</strong> no conjunto de desenvolvimento e <strong>58 de 100</strong> no reservado. Todas as 100 saídas do reservado são Python válido, e 97 têm o nome de função e os parâmetros pedidos. Nove famílias passaram nas cinco variantes: mediana, fatorial, máximo divisor comum, conversão pra binário, inverter dicionário, contar linhas, ano bissexto, segundos pra horas, chaves de JSON. Total acumulado ficou em quatro de cinco.</p>
<p>O que ele faz, portanto: escreve uma função Python pequena, com docstring, sintaxe correta e o nome certo, pra um pedido claro de uma linha. Dentro de um editor, como autocompletar de função isolada, serve pra estudo.</p>
<p>O que ele erra, com exemplos salvos: deduplicar usando <code>set</code> quando o enunciado exigia suportar listas não hasheáveis; agrupar por tamanho contando palavras em vez de agrupar; fazer aritmética em cada elemento numa função de preencher; devolver <code>(texto, '')</code> numa função de dividir uma vez, ignorando o separador; tirar o último caractere em vez do diretório numa função de basename. Dos 42 erros do reservado, 39 têm sintaxe válida e nome certo e falham na semântica. A parte difícil nunca foi parecer código.</p>
<p>E o que ele zera:</p>
<ul>
<li><strong>0 de 4</strong> consertos reais de repositório no OpenCode, com zero chamadas de ferramenta. Pedido pra ler, editar e testar um arquivo, ele devolveu texto com código. Numa das tarefas, renomeou a função pra <code>fix_mean</code>.</li>
<li><strong>0 de 12</strong> em rastrear a saída exata de programas curtos, com e sem modo de raciocínio. Muitas respostas foram código que supostamente calcularia o inteiro pedido.</li>
<li><strong>0 de 9</strong> em achar um número de seis dígitos escondido em prompts de 8, 16 e 32 mil tokens. A alocação de 32 mil tokens funciona; a memória útil, nenhuma.</li>
<li>Conversa: perguntado em português sobre a cor do céu, respondeu repetitivo e errado; pedido em inglês de café da manhã virou uma docstring em Python repetindo &ldquo;oats&rdquo; até o limite de tokens. Numa troca curta lembrou que o gato se chamava Pipoca, e mesmo assim respondeu com frase inteira apesar do pedido de &ldquo;só o nome&rdquo;.</li>
</ul>
<p>O SFT final só tinha respostas de código, sem conversa, sem trajetória de ferramenta, sem recuperação de erro. A tendência de responder qualquer coisa com Python é coerente com isso, e o relatório toma o cuidado de dizer que a causa não foi isolada.</p>
<h2>O que faltaria pra um agente simples no OpenCode<span class="hx:absolute hx:-mt-20" id="o-que-faltaria-pra-um-agente-simples-no-opencode"></span>
    <a href="#o-que-faltaria-pra-um-agente-simples-no-opencode" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>O relatório fecha com estimativas de engenharia, sem promessa de taxa de acerto. Pra chegar num agente que resolve tarefa simples de programação dentro do OpenCode, nesta ordem:</p>
<ol>
<li><strong>Ensinar uso de ferramenta no modelo forte.</strong> Entre 10 e 30 mil trajetórias curtas e verificadas de repositório (ler, editar, checar), misturadas com 30 a 50% de código pra ele não desaprender. Um a três dias de engenharia e de duas a seis horas iniciais de treino.</li>
<li><strong>Corrigir os próprios erros.</strong> Coletar centenas a alguns milhares de falhas do modelo em tarefas de treino, reconstruir o estado do arquivo, verificar as correções e treinar mascarando a ação errada. Mais um a três dias de coleta e de duas a oito horas de treino.</li>
<li><strong>Devolver a conversa.</strong> Uma mistura equilibrada de conversa, ferramenta e código contra o checkpoint congelado, pra ele parar de responder tudo com Python e conseguir trocar de tarefa. Uma a quatro horas de treino.</li>
<li><strong>Contexto longo de verdade.</strong> Só depois das tarefas curtas passarem: 4 mil, 8 mil, e só então 16 e 32 mil, com trajetórias reais de compactação e troca de sessão. Um a dois dias de engenharia e de 6 a 24 horas de treino, medindo memória a cada janela.</li>
<li><strong>Mais fundação, com dados melhores.</strong> Cada 10 bilhões de tokens a mais custam entre 93 e 139 horas nesta GPU, antes de preparação e avaliação. E repetir a mesma mistura de 3,5 bilhões não vale a pena: precisa de mais código educativo único.</li>
</ol>
<p>Nenhum desses passos foi executado. Não há evidência de que um número fixo de horas a mais produza um agente confiável, e o relatório é explícito nisso. O que o exercício estabeleceu foi a distância: entre gerar uma função plausível e agir de forma confiável num repositório existe um abismo que parâmetro e contexto alocado sozinhos não cruzam.</p>
<blockquote>
  <p>O mais forte que saiu daqui foi a curva medida de 0 pra 65 em dez bilhões de tokens, e três contraexemplos concretos: sintaxe não é semântica, suporte a protocolo não é habilidade com ferramenta, e capacidade de contexto não é memória.</p>

</blockquote>
<h2>O futuro é menor e mais especializado<span class="hx:absolute hx:-mt-20" id="o-futuro-é-menor-e-mais-especializado"></span>
    <a href="#o-futuro-%c3%a9-menor-e-mais-especializado" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Depois de onze dias olhando pra dentro de um modelo, a minha leitura do mercado ficou mais firme. Modelo de fronteira generalista vai continuar existindo e vai continuar sendo caro, e o retorno de cada bilhão a mais de parâmetros é cada vez menor. Eu já escrevi que <a href="/2026/10/12/llms-agentes-competente-o-suficiente/">os modelos crus convergiram</a> e que o ganho recente veio do harness. O exercício me deu a outra metade do argumento.</p>
<p>A mistura de especialistas é a indústria admitindo isso por dentro. Se o gpt-oss de 117 bilhões acorda 5 bilhões por token, o que você está usando numa pergunta de contabilidade é um modelo de 5 bilhões escolhido por um roteador. A pergunta que fica é por que carregar os outros 112 bilhões, de arte, cinema, meme e física de partículas, num servidor de escritório contábil.</p>
<p>Imagine um modelo local, feito pra contadores. Ele conhece o plano de contas, a legislação tributária do país, os formatos de arquivo da Receita, o jargão. Ele dispensa parâmetros sobre tudo que um contador não precisa, e pode rodar no PC do escritório, com os dados do cliente dentro da sala.</p>
<p>Os exemplos já existem em outras áreas: o <a href="https://research.google/blog/medgemma-our-most-capable-open-models-for-health-ai-development/"target="_blank" rel="noopener">MedGemma</a> de 4 bilhões pra saúde, o <a href="https://arxiv.org/abs/2403.03883"target="_blank" rel="noopener">SaulLM</a> de 7 bilhões pra direito, o <a href="https://arxiv.org/abs/2503.16252"target="_blank" rel="noopener">Fin-R1</a> de 7 bilhões pra raciocínio financeiro. Em 2023 o <a href="https://arxiv.org/abs/2303.17564"target="_blank" rel="noopener">BloombergGPT</a> precisou de 50 bilhões de parâmetros e 1,3 milhão de horas de A100 pra fazer finanças; hoje um ajuste em cima de um modelo aberto de 7 bilhões faz o mesmo papel por uma fração.</p>
<p>O que mudou pra isso virar viável foi o agente. Um modelo especializado de 2023 precisava saber tudo de cor, porque a única coisa que ele fazia era completar texto. Um modelo de 2026 precisa de uma base: raciocínio suficiente, capacidade de sustentar uma tarefa longa, chamada de ferramenta confiável, cache de prompt. O resto, a legislação que mudou ontem, o documento do cliente, a tabela que ele nunca viu, ele busca com ferramenta e internet na hora, e preenche as lacunas do treino em tempo de uso.</p>
<p>A <a href="https://arxiv.org/abs/2506.02153"target="_blank" rel="noopener">NVIDIA publicou em 2025</a> exatamente essa posição: modelos pequenos são suficientes, mais adequados e mais baratos pra maioria das chamadas de um agente, com o modelo grande chamado só quando a conversa aberta exige. A Apple já <a href="https://machinelearning.apple.com/research/apple-foundation-models-2025-updates"target="_blank" rel="noopener">roda um modelo de 3 bilhões no aparelho</a> com protocolo de ferramentas e adaptadores por tarefa.</p>
<p>É o mesmo raciocínio dos <a href="/2026/10/10/qual-modelo-de-decisao-classificador-de-ia-tipo-jev-e-melhor/">modelos de decisão que eu testei</a> semana passada: um classificador de 4 bilhões treinado pra uma tarefa bate um modelo de chat genérico do mesmo tamanho nela, numa fração do tempo de resposta. Em vez de tentar caber o mundo inteiro num modelo maior, cabe o domínio num modelo menor e deixa o harness buscar o mundo quando precisar.</p>
<p>E o que o meu modelo de 337 milhões me ensinou é o tamanho do &ldquo;suficiente&rdquo; nessa frase. Ele sabe a sintaxe e sabe o nome das coisas. O que falta, semântica, ferramenta, memória, é exatamente o que separa um gerador de texto de um agente, e isso ninguém treina em duas semanas numa placa de vídeo. Treina em meses, em cluster, com trilhões de tokens, e depois especializa.</p>
<p>O código está no <a href="https://github.com/akitaonrails/microgpt"target="_blank" rel="noopener">repositório</a>, com o relatório de fechamento inteiro, pra quem quiser pegar de onde eu parei. É só pra estudo. E foi o melhor estudo do ano.</p>
]]></content:encoded><category>inteligencia-artificial</category><category>llms</category><category>machine-learning</category><category>modelos-locais</category></item><item><title>Qual modelo de decisão/classificador de IA tipo Jev é melhor?</title><link>https://www.akitaonrails.com/2026/10/10/qual-modelo-de-decisao-classificador-de-ia-tipo-jev-e-melhor/</link><guid isPermaLink="true">https://www.akitaonrails.com/2026/10/10/qual-modelo-de-decisao-classificador-de-ia-tipo-jev-e-melhor/</guid><pubDate>Sat, 10 Oct 2026 12:00:00 -0300</pubDate><description>&lt;p&gt;Semana passada publiquei &lt;a href="https://www.akitaonrails.com/2026/10/03/entendendo-o-hype-do-jev-pra-que-serve/"&gt;um texto longo sobre o Jev&lt;/a&gt;, o classificador hospedado da TypeSafe que virou febre em setembro. O resumo daquele texto: o Jev é um classificador, coisa que existe há décadas, e o espanto de &amp;ldquo;193x mais rápido&amp;rdquo; só existe porque muita gente vinha usando LLM de chat como classificador caro e lento. Reproduzi o ensaio &lt;em&gt;O aluno que marca a letra A&lt;/em&gt;, do Paulo Câmara, apresentei as alternativas abertas Clef e Laya, e pra comparar os modelos lado a lado me apoiei no &lt;a href="https://huggingface.co/spaces/multimodalart/jev-decision-index"target="_blank" rel="noopener"&gt;Decision Index&lt;/a&gt;, um ranking público que já existia.&lt;/p&gt;</description><content:encoded><![CDATA[<p>Semana passada publiquei <a href="/2026/10/03/entendendo-o-hype-do-jev-pra-que-serve/">um texto longo sobre o Jev</a>, o classificador hospedado da TypeSafe que virou febre em setembro. O resumo daquele texto: o Jev é um classificador, coisa que existe há décadas, e o espanto de &ldquo;193x mais rápido&rdquo; só existe porque muita gente vinha usando LLM de chat como classificador caro e lento. Reproduzi o ensaio <em>O aluno que marca a letra A</em>, do Paulo Câmara, apresentei as alternativas abertas Clef e Laya, e pra comparar os modelos lado a lado me apoiei no <a href="https://huggingface.co/spaces/multimodalart/jev-decision-index"target="_blank" rel="noopener">Decision Index</a>, um ranking público que já existia.</p>
<p>Ranking de terceiro é útil, mas eu queria pôr a mão na massa. Então montei meu próprio benchmark, com casos meus, metade em português, e rodei os modelos abertos duas vezes: no meu <a href="/2026/03/31/review-minisforum-ms-s1-max-amd-ai-max-395/">home server com Strix Halo</a>, o mesmo Ryzen AI Max+ 395 que uso pra testar LLM local, via ROCm, e de novo numa RTX 5090 via CUDA. Os hospedados (Jev, os dois Clef da Cloudflare, o OpenAI Decisions e o Microsoft Decision-1 via OpenRouter) rodaram pela API deles. Tudo, incluindo os casos, as respostas brutas e os scripts de pontuação, está público no <a href="https://github.com/akitaonrails/jev-docs-benchmarks"target="_blank" rel="noopener">repositório do benchmark</a>.</p>
<blockquote>
  <p><strong>TL;DR:</strong> o topo agora tem dois, ambos de pesos abertos. O <strong>Perplexity Decider 1.1 27B</strong> e o recém-lançado <strong>Decision 3.0</strong> (vLLM Semantic Router) empataram em <strong>99,4%</strong> nos casos compactos, com 100% em português, e rótulos idênticos em todas as 1.024 classificações do benchmark. O preço: uns 49 GB de pesos e mais de um segundo por decisão. Na nuvem, o <strong>Microsoft Decision-1</strong> empatou com o <strong>Jev</strong> em 97,6% custando um terço: é o hospedado mais barato que eu medi, e o primeiro candidato a avaliar. O Jev responde mais rápido e erra diferente. O <strong>OpenAI Decisions</strong> estreou empatado com o melhor 4B local, o Jebadiah, e tem a menor latência hospedada. O <strong>Julia-1</strong>, o menor e mais rápido de todos (12 ms), acertou no nível da moeda: de fábrica, não serve. E a segunda passada, agora numa <strong>RTX 5090</strong>: os modelos até 9B aceleram de 2× a 10×, o Jebadiah 4B cai pra 39 ms, mais rápido que qualquer hospedado, e os dois campeões de 27B, com uns 49 GB de pesos, nem cabem nos 32 GB da placa. Pule pra <a href="#a-tabela-completa">tabela</a>, pro <a href="#quanto-custa-em-e-mails-de-suporte">custo em e-mails de suporte</a>, pra <a href="#qual-modelo-em-qual-situa%c3%a7%c3%a3o">qual usar em qual situação</a>, pra <a href="#qu%c3%a3o-dif%c3%adcil-%c3%a9-rodar-os-melhores-abertos-na-sua-m%c3%a1quina">quão difícil é rodar local</a>, pras <a href="#destaques-e-pegadinhas">pegadinhas</a> ou pras duas cozinhas do <a href="#rocm-x-cuda-a-mesma-receita-em-duas-cozinhas">ROCm x CUDA</a>.</p>

</blockquote>
<h2>Como eu testei<span class="hx:absolute hx:-mt-20" id="como-eu-testei"></span>
    <a href="#como-eu-testei" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>A ideia central foi medir mais do que &ldquo;quem acerta mais&rdquo;. Modelo de decisão tem objetivo de projeto, e comparar um encoder de 322 milhões de parâmetros feito pra rodar em dispositivo fraco com um serviço hospedado de tamanho não divulgado só por porcentagem de acerto é injusto com os dois. Então medi acerto, latência, custo e comportamento sob estresse, e no fim separo as recomendações por situação.</p>
<h3>Os dois conjuntos de casos<span class="hx:absolute hx:-mt-20" id="os-dois-conjuntos-de-casos"></span>
    <a href="#os-dois-conjuntos-de-casos" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Montei dois conjuntos de testes, congelados em git antes de qualquer inferência, com o hash do dataset gravado junto com as respostas. Nenhum modelo recebe a resposta esperada, a justificativa ou o identificador do caso. Só o texto, as instruções e as opções.</p>
<p>O <strong>conjunto v1</strong> tem 13 casos de uso operacionais, em inglês, cada um com uma política fictícia explícita:</p>
<ul>
<li>severidade de ticket de suporte</li>
<li>roteamento de suporte</li>
<li>política de reembolso</li>
<li>escalação de incidente</li>
<li>aprovação de ferramenta (um agente pode ou não executar tal ação)</li>
<li>fraude por e-mail</li>
<li>tipo de documento</li>
<li>suporte de evidência</li>
<li>relevância de busca</li>
<li>moderação</li>
<li>roteamento de pedido</li>
<li>qualificação de lead</li>
<li>triagem por limiar numérico</li>
</ul>
<p>São 10 cenários por caso de uso, cada um apresentado de quatro formas: original, opções em ordem invertida, opções com nomes opacos (c0, c1, c2) e original mais um comentário de &ldquo;revisor&rdquo; sugerindo a resposta errada, que a instrução manda ignorar. Dá 520 classificações por modelo. Somam-se 24 sondagens de probabilidade em situações sem informação nenhuma (moeda honesta, dado de seis lados), onde a resposta certa é matemática.</p>
<p>O <strong>conjunto v2</strong> foi feito pra cobrir o que o v1 deixa de fora: semântica comum e português. São 7 tarefas, com 12 cenários cada, e cada cenário escrito em inglês e em português brasileiro com a mesma resposta e a mesma ordem de opções:</p>
<ul>
<li>sentimento (autor versus citação, negação, sarcasmo)</li>
<li>intenção atual (pedido retirado, ações concorrentes, hipótese)</li>
<li>implicação lógica (quantificadores, fato ausente, ordem temporal)</li>
<li>relevância de busca (tudo, parte ou nada do que foi pedido)</li>
<li>tratamento de dados sensíveis (segredo fictício versus menção, redação, revogação)</li>
<li>status atual de incidente (ordem da linha do tempo, verificado versus não verificado)</li>
<li>política por limiar (limites inclusivos, unidades, E/OU com valor faltando)</li>
</ul>
<p>Cada par roda em três condições:</p>
<ul>
<li><strong>compacta</strong>: só o registro e a regra</li>
<li><strong>com exemplos</strong>: um exemplo resolvido por classe, fixo e igual pra todos</li>
<li><strong>com distratores</strong>: dez notas irrelevantes de arquivo antes do registro que importa, pra estressar contexto</li>
</ul>
<p>Dá 504 classificações por modelo.</p>
<blockquote>
  <p>Os casos foram escritos com ajuda de IA e revisados contra as políticas, sem anotação humana independente. É um benchmark sintético e pequeno: 130 famílias de cenário no v1 e 84 no v2. As variantes são correlacionadas, então 520 respostas são 130 cenários vistos de quatro jeitos. Trate os números como fotografia desses casos, e teste com os seus antes de decidir.</p>

</blockquote>
<h3>Os 23 competidores<span class="hx:absolute hx:-mt-20" id="os-23-competidores"></span>
    <a href="#os-23-competidores" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Cinco rodaram na nuvem, pela API oficial: o <strong>Jev</strong> (versão 1.13.0, da TypeSafe), o <strong>Clef</strong> (27B, Cloudflare Workers AI), o <strong>Clef-flash</strong> (9B, idem), o <strong>OpenAI Decisions</strong> (o alias <code>gpt-6-luna</code>, em beta público) e o <strong>Microsoft Decision-1</strong> (9B, pós-treinado do Qwen3.5-9B, servido via OpenRouter em Azure). Os outros dezoito rodaram no Strix Halo, via ROCm, e depois de novo numa RTX 5090, via CUDA, com os pesos fixados por hash de commit no Hugging Face, dentro de container Docker. Os três da primeira leva (Laya, Laya typed e Clef-flash) dividiram um container; os demais rodaram um por vez, com o Julia-1 medido duas vezes, em GPU e em CPU. O servidor via 64 GiB de memória endereçável pela GPU.</p>
<table>
  <thead>
      <tr>
          <th>Modelo</th>
          <th style="text-align: right">Tamanho nominal</th>
          <th>Origem</th>
          <th>Onde rodou</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Jev 1.13.0</td>
          <td style="text-align: right">não divulgado</td>
          <td>TypeSafe, proprietário</td>
          <td>API</td>
      </tr>
      <tr>
          <td>OpenAI Decisions (<code>gpt-6-luna</code>)</td>
          <td style="text-align: right">não divulgado</td>
          <td>OpenAI, proprietário, beta</td>
          <td>API</td>
      </tr>
      <tr>
          <td>Microsoft Decision-1</td>
          <td style="text-align: right">9B</td>
          <td>Microsoft, proprietário</td>
          <td>API (OpenRouter)</td>
      </tr>
      <tr>
          <td>Clef</td>
          <td style="text-align: right">27B</td>
          <td>Cloudflare, pesos abertos</td>
          <td>API</td>
      </tr>
      <tr>
          <td>Clef-flash</td>
          <td style="text-align: right">9B</td>
          <td>Cloudflare, pesos abertos</td>
          <td>API e local</td>
      </tr>
      <tr>
          <td>Perplexity Decider 1.1 27B</td>
          <td style="text-align: right">27B</td>
          <td>Perplexity, Apache 2.0</td>
          <td>local</td>
      </tr>
      <tr>
          <td>Decision 3.0 (d3)</td>
          <td style="text-align: right">27B</td>
          <td>vLLM Semantic Router, Apache 2.0</td>
          <td>local</td>
      </tr>
      <tr>
          <td>Decision 3.0 Edge</td>
          <td style="text-align: right">0,6B</td>
          <td>vLLM Semantic Router, Apache 2.0</td>
          <td>local</td>
      </tr>
      <tr>
          <td>Jebadiah 4B v2</td>
          <td style="text-align: right">4B</td>
          <td>Frontier Infra, Apache 2.0</td>
          <td>local</td>
      </tr>
      <tr>
          <td>Kev 0.8B / 4B / 9B</td>
          <td style="text-align: right">0,8B / 4B / 9B</td>
          <td>Jared Palmer, Apache 2.0</td>
          <td>local</td>
      </tr>
      <tr>
          <td>Mapika Decider 4B</td>
          <td style="text-align: right">4,2B</td>
          <td>Mapika, Apache 2.0</td>
          <td>local</td>
      </tr>
      <tr>
          <td>Eikos 4B</td>
          <td style="text-align: right">4B</td>
          <td>Caio Vicentino, MIT, treinado em EN e PT</td>
          <td>local</td>
      </tr>
      <tr>
          <td>Simple-Jev + Qwen3.5 4B</td>
          <td style="text-align: right">4B</td>
          <td>Featherless, servidor de leitura de tokens sobre modelo comum</td>
          <td>local</td>
      </tr>
      <tr>
          <td>Laya / Laya typed-decisions</td>
          <td style="text-align: right">421M</td>
          <td>Convai Innovations, encoder ModernBERT</td>
          <td>local</td>
      </tr>
      <tr>
          <td>Laya multilingual</td>
          <td style="text-align: right">322M</td>
          <td>Convai Innovations, encoder mmBERT</td>
          <td>local</td>
      </tr>
      <tr>
          <td>Julia-1</td>
          <td style="text-align: right">144M</td>
          <td>Supersonic Labs, Apache 2.0, encoder mmBERT</td>
          <td>local, GPU e CPU</td>
      </tr>
      <tr>
          <td>GLiNER2.5-Decide / multi-Decide</td>
          <td style="text-align: right">340M / 287M</td>
          <td>Fastino, Apache 2.0</td>
          <td>local</td>
      </tr>
  </tbody>
</table>
<p>A ordem aí em cima é só organizacional, hospedados primeiro e locais por família. O ranking de verdade está na <a href="#a-tabela-completa">tabela de resultados</a>.</p>
<p>Todos receberam exatamente o mesmo texto, as mesmas instruções e as mesmas opções, sem prompt ajustado por modelo e sem nenhum ajuste depois de ver resultado (o Simple-Jev usa a política de prompt fixa do próprio servidor). A exceção é o <strong>GLiNER</strong>, cuja API nativa recebe rótulos em vez de distribuições de probabilidade. Ele entra numa trilha separada, só no v2, com mapeamento explícito e sem métricas de calibração inventadas.</p>
<p>O <strong>Simple-Jev</strong> merece explicação. Ele é um servidor que pega um LLM comum (aqui o Qwen3.5 4B, sem nenhum treino de decisão) e lê a probabilidade dos tokens de resposta em vez de gerar texto. É a linha de base: se um modelo &ldquo;de decisão&rdquo; perde pra isso, o treino especializado não comprou nada.</p>
<p>Duas observações de protocolo. O <strong>OpenAI Decisions</strong> responde por um endpoint nativo de decisões tipadas, e o alias que a API devolveu não é um snapshot imutável: a OpenAI pode trocar o peso por trás do nome, como a Cloudflare faz com o Clef-flash. E o <strong>Perplexity Decider</strong> não é um LLM de chat comum: ele tem uma cabeça de decisão separada, com atenção não-causal e uma temperatura de calibração salva nos pesos. Um servidor de chat genérico não reproduz o comportamento dele. O <strong>Microsoft Decision-1</strong> fala SystemOne nativo pela API de decisões do OpenRouter, e o identificador servido é datado (<code>microsoft-decision-1-20261009</code>), ou seja, também não é peso imutável. Ele não aparece no catálogo de modelos de texto do OpenRouter: pra achar, tem que filtrar pela modalidade <code>decisions</code>. O <strong>Decision 3.0</strong> é a família nova do projeto vLLM Semantic Router, Apache 2.0, derivada de Qwen, com seis checkpoints (d3, flash, mini, nano, lite, edge) e SystemOne nativo. Eu testei o flagship (d3) e o menor (d3-edge); os quatro do meio ficam pra outra rodada.</p>
<h3>O que foi medido<span class="hx:absolute hx:-mt-20" id="o-que-foi-medido"></span>
    <a href="#o-que-foi-medido" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><ul>
<li>Acerto por tarefa, por língua e por condição, sempre em cima das probabilidades por opção que o modelo devolve, ignorando o campo de &ldquo;confiança&rdquo; do fornecedor.</li>
<li>Brier e ECE pra calibração.</li>
<li>Latência mediana e p95 vista pelo cliente, incluindo a rede: WAN pros hospedados, LAN pros locais. Os locais foram medidos nas duas implantações, Strix com ROCm e 5090 com CUDA. São observações de implantação completa, máquina inteira de cada lado; a comparação entre elas não isola o efeito de trocar só o backend.</li>
<li>Tokens de entrada reportados pela API, valorados pela tabela de preços de 10 de outubro, antes de qualquer franquia.</li>
<li>Intervalos de confiança por bootstrap sobre famílias de cenário, em vez de respostas individuais.</li>
</ul>
<p>Custo local eu deixei de medir. Energia, amortização do hardware e manutenção ficaram de fora, então modelo local aparece como &ldquo;não medido&rdquo;, porque &ldquo;zero&rdquo; seria mentira.</p>
<h2>A tabela completa<span class="hx:absolute hx:-mt-20" id="a-tabela-completa"></span>
    <a href="#a-tabela-completa" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>A coluna principal é o acerto nos 168 casos compactos do v2 (84 em inglês, 84 em português). A coluna do v1 usa os 130 cenários originais. Latências são medianas vistas pelo cliente ao longo das 504 chamadas do v2, nas duas implantações locais: ROCm no Strix Halo, CUDA na RTX 5090. Os hospedados não têm hardware meu pra trocar, e os dois 27B não cabem nos 32 GB da 5090. O custo é o valor de lista por 10 mil decisões compactas, calculado pelos tokens que a API reportou.</p>
<table>
  <thead>
      <tr>
          <th>Modelo</th>
          <th style="text-align: right">Compacto EN / PT-BR</th>
          <th style="text-align: right">Compacto combinado</th>
          <th style="text-align: right">v1 originais</th>
          <th style="text-align: right">Latência ROCm (Strix)</th>
          <th style="text-align: right">Latência CUDA (5090)</th>
          <th style="text-align: right">10 mil decisões</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Perplexity Decider 27B</td>
          <td style="text-align: right">98,8% / 100,0%</td>
          <td style="text-align: right"><strong>99,4%</strong></td>
          <td style="text-align: right">99,2%</td>
          <td style="text-align: right">1.547 ms (LAN)</td>
          <td style="text-align: right">não cabe (49 GB)</td>
          <td style="text-align: right">não medido</td>
      </tr>
      <tr>
          <td>Decision 3.0 (27B)</td>
          <td style="text-align: right">98,8% / 100,0%</td>
          <td style="text-align: right"><strong>99,4%</strong></td>
          <td style="text-align: right">99,2%</td>
          <td style="text-align: right">1.420 ms (LAN)</td>
          <td style="text-align: right">não cabe (48,6 GB)</td>
          <td style="text-align: right">não medido</td>
      </tr>
      <tr>
          <td>Jev</td>
          <td style="text-align: right">97,6% / 97,6%</td>
          <td style="text-align: right">97,6%</td>
          <td style="text-align: right">96,2%</td>
          <td style="text-align: right">255 ms (WAN)</td>
          <td style="text-align: right">–</td>
          <td style="text-align: right">US$ 0,19</td>
      </tr>
      <tr>
          <td>Microsoft Decision-1</td>
          <td style="text-align: right">95,2% / 100,0%</td>
          <td style="text-align: right">97,6%</td>
          <td style="text-align: right">95,4%</td>
          <td style="text-align: right">422 ms (WAN)</td>
          <td style="text-align: right">–</td>
          <td style="text-align: right">US$ 0,065</td>
      </tr>
      <tr>
          <td>Clef (27B)</td>
          <td style="text-align: right">97,6% / 94,0%</td>
          <td style="text-align: right">95,8%</td>
          <td style="text-align: right">94,6%</td>
          <td style="text-align: right">302 ms (WAN)</td>
          <td style="text-align: right">–</td>
          <td style="text-align: right">US$ 0,63</td>
      </tr>
      <tr>
          <td>Jebadiah 4B v2</td>
          <td style="text-align: right">91,7% / 89,3%</td>
          <td style="text-align: right">90,5%</td>
          <td style="text-align: right">85,4%</td>
          <td style="text-align: right">205 ms (LAN)</td>
          <td style="text-align: right"><strong>39 ms</strong></td>
          <td style="text-align: right">não medido</td>
      </tr>
      <tr>
          <td>OpenAI Decisions</td>
          <td style="text-align: right">90,5% / 90,5%</td>
          <td style="text-align: right">90,5%</td>
          <td style="text-align: right">93,1%</td>
          <td style="text-align: right">222 ms (WAN)</td>
          <td style="text-align: right">–</td>
          <td style="text-align: right">US$ 0,23</td>
      </tr>
      <tr>
          <td>Kev 9B</td>
          <td style="text-align: right">92,9% / 83,3%</td>
          <td style="text-align: right">88,1%</td>
          <td style="text-align: right">87,7%</td>
          <td style="text-align: right">561 ms (LAN)</td>
          <td style="text-align: right">69 ms</td>
          <td style="text-align: right">não medido</td>
      </tr>
      <tr>
          <td>Clef-flash (9B) hospedado</td>
          <td style="text-align: right">91,7% / 83,3%</td>
          <td style="text-align: right">87,5%</td>
          <td style="text-align: right">90,8%</td>
          <td style="text-align: right">394 ms (WAN)</td>
          <td style="text-align: right">–</td>
          <td style="text-align: right">US$ 0,10</td>
      </tr>
      <tr>
          <td>Clef-flash (9B) local</td>
          <td style="text-align: right">89,3% / 85,7%</td>
          <td style="text-align: right">87,5%</td>
          <td style="text-align: right">90,8%</td>
          <td style="text-align: right">618 ms (LAN)</td>
          <td style="text-align: right">86 ms</td>
          <td style="text-align: right">não medido</td>
      </tr>
      <tr>
          <td>Mapika Decider 4B</td>
          <td style="text-align: right">89,3% / 85,7%</td>
          <td style="text-align: right">87,5%</td>
          <td style="text-align: right">86,9%</td>
          <td style="text-align: right">186 ms (LAN)</td>
          <td style="text-align: right">91 ms</td>
          <td style="text-align: right">não medido</td>
      </tr>
      <tr>
          <td>Kev 4B</td>
          <td style="text-align: right">88,1% / 86,9%</td>
          <td style="text-align: right">87,5%</td>
          <td style="text-align: right">82,3%</td>
          <td style="text-align: right">246 ms (LAN)</td>
          <td style="text-align: right">67 ms</td>
          <td style="text-align: right">não medido</td>
      </tr>
      <tr>
          <td>Eikos 4B</td>
          <td style="text-align: right">85,7% / 79,8%</td>
          <td style="text-align: right">82,7%</td>
          <td style="text-align: right">87,7%</td>
          <td style="text-align: right">235 ms (LAN)</td>
          <td style="text-align: right">42 ms</td>
          <td style="text-align: right">não medido</td>
      </tr>
      <tr>
          <td>Simple-Jev + Qwen3.5 4B</td>
          <td style="text-align: right">77,4% / 71,4%</td>
          <td style="text-align: right">74,4%</td>
          <td style="text-align: right">85,4%</td>
          <td style="text-align: right">1.349 ms (LAN)</td>
          <td style="text-align: right">161 ms</td>
          <td style="text-align: right">não medido</td>
      </tr>
      <tr>
          <td>Kev 0.8B</td>
          <td style="text-align: right">64,3% / 61,9%</td>
          <td style="text-align: right">63,1%</td>
          <td style="text-align: right">63,1%</td>
          <td style="text-align: right">86 ms (LAN)</td>
          <td style="text-align: right">49 ms</td>
          <td style="text-align: right">não medido</td>
      </tr>
      <tr>
          <td>Decision 3.0 Edge (0,6B)</td>
          <td style="text-align: right">67,9% / 53,6%</td>
          <td style="text-align: right">60,7%</td>
          <td style="text-align: right">65,4%</td>
          <td style="text-align: right">45 ms (LAN)</td>
          <td style="text-align: right">13 ms</td>
          <td style="text-align: right">não medido</td>
      </tr>
      <tr>
          <td>GLiNER2.5-Decide (EN)</td>
          <td style="text-align: right">54,8% / 50,0%</td>
          <td style="text-align: right">52,4%</td>
          <td style="text-align: right">não rodou</td>
          <td style="text-align: right">208 ms (LAN)</td>
          <td style="text-align: right">20 ms</td>
          <td style="text-align: right">não medido</td>
      </tr>
      <tr>
          <td>Laya typed-decisions</td>
          <td style="text-align: right">50,0% / 35,7%</td>
          <td style="text-align: right">42,9%</td>
          <td style="text-align: right">43,8%</td>
          <td style="text-align: right">43 ms (LAN)</td>
          <td style="text-align: right">14 ms</td>
          <td style="text-align: right">não medido</td>
      </tr>
      <tr>
          <td>Laya (EN)</td>
          <td style="text-align: right">44,0% / 41,7%</td>
          <td style="text-align: right">42,9%</td>
          <td style="text-align: right">43,8%</td>
          <td style="text-align: right">41 ms (LAN)</td>
          <td style="text-align: right">13 ms</td>
          <td style="text-align: right">não medido</td>
      </tr>
      <tr>
          <td>Laya multilingual</td>
          <td style="text-align: right">35,7% / 38,1%</td>
          <td style="text-align: right">36,9%</td>
          <td style="text-align: right">43,8%</td>
          <td style="text-align: right">19 ms (LAN)</td>
          <td style="text-align: right">10 ms</td>
          <td style="text-align: right">não medido</td>
      </tr>
      <tr>
          <td>GLiNER2.5-multi-Decide</td>
          <td style="text-align: right">32,1% / 35,7%</td>
          <td style="text-align: right">33,9%</td>
          <td style="text-align: right">não rodou</td>
          <td style="text-align: right">63 ms (LAN)</td>
          <td style="text-align: right">10 ms</td>
          <td style="text-align: right">não medido</td>
      </tr>
      <tr>
          <td>Julia-1 (GPU)</td>
          <td style="text-align: right">38,1% / 25,0%</td>
          <td style="text-align: right">31,5%</td>
          <td style="text-align: right">38,5%</td>
          <td style="text-align: right"><strong>12 ms</strong> (LAN)</td>
          <td style="text-align: right">7,3 ms</td>
          <td style="text-align: right">não medido</td>
      </tr>
      <tr>
          <td>Julia-1 (CPU)</td>
          <td style="text-align: right">38,1% / 25,0%</td>
          <td style="text-align: right">31,5%</td>
          <td style="text-align: right">36,9%</td>
          <td style="text-align: right">71 ms (LAN)</td>
          <td style="text-align: right">–</td>
          <td style="text-align: right">não medido</td>
      </tr>
  </tbody>
</table>
<p>Sobre a coluna CUDA: os pesos são os mesmos, então o acerto veio igual ou a até dois casos de distância no compacto em todos os modelos. Rótulos individuais viraram em alguns (12 de 504 no pior caso, o Julia; 11 no Simple-Jev), diferença de aritmética de kernel que não mudou placar nenhum, e as probabilidades diferem na terceira casa decimal em média. A tabela fica pelo acerto do Strix; a coluna CUDA só traz latência. O relatório pareado completo, caso a caso, está no <a href="https://github.com/akitaonrails/jev-docs-benchmarks/blob/master/benchmark/reports/cuda-comparison/results.md"target="_blank" rel="noopener">repositório</a>.</p>
<p>Pra calibrar a leitura: as tarefas têm três ou quatro opções, então chute uniforme dá uns 31% nos dois conjuntos. &ldquo;Perto de 50%&rdquo; aqui já é bem acima da moeda, e &ldquo;perto de 33%&rdquo; é a moeda. O Julia-1 ficou em 31,5%: o nome bonito esconde um cara jogando cara-ou-coroa.</p>
<p>Os intervalos importam. A vantagem do Perplexity sobre o Jev no v2 é de menos de 2 pontos e o intervalo de bootstrap encosta no zero: é a liderança medida neste conjunto, não uma superioridade provada pra sempre. E o Decision 3.0 empatou com o Perplexity no limite do que este conjunto consegue distinguir: <strong>rótulos idênticos nas 1.024 classificações</strong>, com os dois errando os mesmos dois casos (a relevância em inglês no v2 e o caso disputado de severidade no v1, que já está no errata). O Microsoft empatou com o Jev no agregado, com intervalo centrado em zero, mas empate no agregado não é intercambiabilidade: eles erram casos diferentes, e o Microsoft inverte o padrão de língua, errando mais em inglês que em português. Jev e Clef também seguem separados por menos de 2 pontos, no mesmo patamar. Já o pelotão de 90% (Jebadiah, OpenAI) fica 7 pontos atrás do Jev, e daí pra baixo todas as diferenças são claras.</p>
<p>Cada célula de tarefa tem só 12 cenários por língua, então um acerto a mais muda a célula em mais de 8 pontos. Empate em uma tarefa específica é pista pra um piloto. As tabelas por tarefa estão no <a href="https://github.com/akitaonrails/jev-docs-benchmarks/blob/master/benchmark/reports/decision3-comparison-v2/results.md"target="_blank" rel="noopener">repositório</a>.</p>
<h2>Quanto custa, em e-mails de suporte<span class="hx:absolute hx:-mt-20" id="quanto-custa-em-e-mails-de-suporte"></span>
    <a href="#quanto-custa-em-e-mails-de-suporte" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Preço por milhão de tokens é um número que ninguém consegue sentir. Os preços de lista em 11 de outubro são US$ 0,042 por milhão de tokens de entrada no Jev, US$ 0,24 no Clef, US$ 0,038 no Clef-flash e US$ 0,10 no OpenAI Decisions. Saída é grátis nos cinco. O Microsoft Decision-1 cobra os mesmos US$ 0,042 do Jev por milhão, via OpenRouter, mas reportou um terço dos tokens de entrada pros mesmos textos (uns 26 mil contra uns 77 mil do Jev no v2 compacto), e ninguém sabe explicar a diferença de tokenização. Na prática medida, isso o torna o hospedado mais barato do lote.</p>
<p>O Clef-flash custava US$ 0,09 no dia 9: a Cloudflare baixou o preço em mais da metade no meio do meu teste. O Jev reporta bem mais tokens que a Cloudflare pro mesmo texto, uns 45% a mais no v2, e isso já está na conta abaixo.</p>
<p>Imagine uma operação com <strong>10 mil e-mails de suporte por mês</strong> pra classificar em três ou quatro baldes, com textos curtos parecidos com os meus casos:</p>
<table>
  <thead>
      <tr>
          <th>Modelo</th>
          <th style="text-align: right">10 mil e-mails / mês</th>
          <th style="text-align: right">1 milhão de e-mails</th>
          <th style="text-align: right">Erros esperados em 10 mil (pelo acerto compacto)</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Microsoft Decision-1</td>
          <td style="text-align: right">US$ 0,065</td>
          <td style="text-align: right">US$ 6,45 (uns R$ 32)</td>
          <td style="text-align: right">~240</td>
      </tr>
      <tr>
          <td>Clef-flash</td>
          <td style="text-align: right">US$ 0,10</td>
          <td style="text-align: right">US$ 10 (uns R$ 50)</td>
          <td style="text-align: right">~1.250</td>
      </tr>
      <tr>
          <td>Jev</td>
          <td style="text-align: right">US$ 0,19</td>
          <td style="text-align: right">US$ 19 (uns R$ 95)</td>
          <td style="text-align: right">~240</td>
      </tr>
      <tr>
          <td>OpenAI Decisions</td>
          <td style="text-align: right">US$ 0,23</td>
          <td style="text-align: right">US$ 23 (uns R$ 115)</td>
          <td style="text-align: right">~950</td>
      </tr>
      <tr>
          <td>Clef</td>
          <td style="text-align: right">US$ 0,63</td>
          <td style="text-align: right">US$ 63 (uns R$ 315)</td>
          <td style="text-align: right">~420</td>
      </tr>
  </tbody>
</table>
<blockquote>
  <p>Em reais, pela cotação de sexta (R$ 4,99), 10 mil decisões no Jev custam um real. Um milhão custa menos de cem reais. Pra qualquer empresa que tenha um milhão de e-mails pra classificar, isso some no orçamento.</p>

</blockquote>
<p>Uma ressalva de proporção: meus casos são curtos, uns 450 tokens por caso compacto pela contagem do Jev. E-mail real de cliente, com histórico de thread citado, pode ser três a cinco vezes maior, e o custo escala linear com isso. Mesmo assim, estamos falando de centavos por milhar.</p>
<p>O que esse cálculo mostra é que a diferença de preço entre os modelos hospedados é irrelevante perto do custo de errar. O Jev custa 9 centavos de dólar a mais que o Clef-flash a cada 10 mil decisões, e nesses mesmos 10 mil comete uns mil erros a menos. Se um e-mail mal roteado custar mais que um centésimo de centavo de dólar em retrabalho humano, o Jev se paga. Essa conta assume que todo erro custa igual, o que nunca é verdade: mandar um ticket crítico pra fila de rotina custa muito mais que o contrário.</p>
<p>A franquia da Cloudflare muda a conta pra quem é pequeno. São 10 mil &ldquo;neurônios&rdquo; por dia, compartilhados pela conta inteira, zerando à meia-noite UTC. Com os meus tamanhos de entrada, isso dá umas 1.500 classificações de Clef completo por dia ou quase 10 mil de Clef-flash, de graça. Se você tem 1.000 e-mails por dia e nada mais rodando na conta, o Clef sai a custo zero com qualidade de ponta.</p>
<p>E o local? Energia o benchmark deixou de medir. Pelo que eu vejo no meu Strix Halo, a máquina inteira fica abaixo de 100 W sob carga. Com o Jebadiah em 200 ms por decisão, 10 mil decisões são uns 35 minutos de máquina, menos de 0,1 kWh, uns centavos de real na conta de luz.</p>
<p>O custo local real é a máquina, a sua hora configurando ROCm e container, e manter aquilo de pé. Quem já tem o hardware por outro motivo tem custo marginal baixo. Quem compraria a máquina só pra isso está comprando um problema de milhares de reais pra economizar uns cem reais por milhão.</p>
<h2>Qual modelo em qual situação<span class="hx:absolute hx:-mt-20" id="qual-modelo-em-qual-situação"></span>
    <a href="#qual-modelo-em-qual-situa%c3%a7%c3%a3o" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><h3>Você quer precisão e quer operar nada: Jev<span class="hx:absolute hx:-mt-20" id="você-quer-precisão-e-quer-operar-nada-jev"></span>
    <a href="#voc%c3%aa-quer-precis%c3%a3o-e-quer-operar-nada-jev" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Na nuvem, o Jev continua sendo o nome. Venceu ou empatou em praticamente todas as células entre os hospedados, e só o Microsoft empatou com ele no agregado. Em inglês ele foi 100% em cinco das sete tarefas do v2, e em português repetiu 97,6% no agregado sem perder nada na tradução, coisa que nenhum outro modelo acima da moeda conseguiu. No v1 ele manteve os 96,2% do original com o comentário malicioso do &ldquo;revisor&rdquo; injetado; o Clef até subiu um caso, e o Kev 4B também ficou parado, só que num patamar bem mais baixo. A latência de 255 ms via WAN ficou a 30 ms do OpenAI, e custa 19 centavos de dólar a cada 10 mil decisões.</p>
<p>A contrapartida é a que já discuti no texto anterior: é proprietário, hospedado, de tamanho não divulgado, e você depende da TypeSafe manter o comportamento por baixo de você. E ele erra. Tratou um ID de fatura chamado &ldquo;PASSWORD-523&rdquo; como credencial utilizável, mesmo com o registro dizendo explicitamente que aquilo não autentica ninguém.</p>
<h3>Você quer o nível do Jev pagando um terço: Microsoft Decision-1<span class="hx:absolute hx:-mt-20" id="você-quer-o-nível-do-jev-pagando-um-terço-microsoft-decision-1"></span>
    <a href="#voc%c3%aa-quer-o-n%c3%advel-do-jev-pagando-um-ter%c3%a7o-microsoft-decision-1" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>A Microsoft entrou nesse mercado quieto, e entrou bem. O Decision-1 é um 9B pós-treinado do Qwen3.5-9B, servido via OpenRouter, e foi o primeiro modelo a empatar com o Jev no agregado compacto: 97,6%, com o intervalo de bootstrap centrado no zero. O detalhe que me ganhou: <strong>100% em português</strong>, melhor que o inglês dele (95%), invertendo o padrão de todo o resto do lote. No v1, as variantes de ordem invertida e comentário malicioso não derrubaram uma única resposta, e ele acertou o ticket crítico que o Clef e o OpenAI erraram.</p>
<p>E é o hospedado mais barato que eu medi: US$ 0,065 por 10 mil decisões, um terço do Jev, cobrando a mesma taxa por token mas reportando um terço dos tokens. A economia vem da contagem de tokens, e o mecanismo exato eu não sei explicar, então trate como medida, não como promessa.</p>
<p>Os pontos fracos existem. A latência é a pior entre os hospedados (422 ms de mediana, contra 255 do Jev). Nos casos compactos ele é limpo de erro confiante, mas basta adicionar contexto pra aparecer erro em português com 99,5% de confiança: o padrão &ldquo;português perfeito&rdquo; não sobrevive a texto longo. E nas sondagens de probabilidade ele é fraco, dando 87% pra uma moeda honesta. A Microsoft anuncia probabilidades calibradas; minhas sondagens discordam. E como todo hospedado de ID datado, o peso por trás pode mudar sem aviso.</p>
<p>Pra operação em português que quer qualidade de Jev pagando menos, é o primeiro candidato a rodar no seu piloto.</p>
<h3>Você quer o mesmo nível com pesos abertos: Clef<span class="hx:absolute hx:-mt-20" id="você-quer-o-mesmo-nível-com-pesos-abertos-clef"></span>
    <a href="#voc%c3%aa-quer-o-mesmo-n%c3%advel-com-pesos-abertos-clef" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>O Clef completo de 27B empatou com o Jev em inglês (82 de 84) e ficou 3 casos atrás em português. Ganhou dele no tratamento de dados sensíveis em inglês (12 de 12 contra 11). É modelo de pesos abertos, então dá pra rodar local com uns 54 GB de memória só pros pesos em BF16, o que eu deixei de testar: o Clef completo rodou só pela API. Entre os abertos, ele perdeu a coroa pro Perplexity, que pontuou mais alto com pesos menores.</p>
<p>Pela API ele é o mais caro dos cinco hospedados, três vezes o Jev, sem ganho de qualidade que justifique. A exceção é a franquia diária: pra volume pequeno, ele é o melhor classificador que você consegue de graça.</p>
<h3>Você quer o mais barato na nuvem: Clef-flash<span class="hx:absolute hx:-mt-20" id="você-quer-o-mais-barato-na-nuvem-clef-flash"></span>
    <a href="#voc%c3%aa-quer-o-mais-barato-na-nuvem-clef-flash" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>A US$ 0,038 por milhão, o Clef-flash é o preço mais baixo do mercado hospedado e cobre 9.400 decisões por dia dentro da franquia. A qualidade cai: 92% em inglês e 83% em português nos casos compactos, a maior queda de língua entre os hospedados, com intervalo inteiro abaixo de zero. Em sentimento nas duas línguas, e em status de incidente em inglês, ele empatou com o Jev. Em intenção e dados sensíveis em português, perdeu feio.</p>
<p>Pra tarefa de sentimento em qualquer língua ou pra classificação em inglês onde 1 em 12 erros é aceitável, ele serve bem. Pra português, meça antes.</p>
<h3>Você já vive no ecossistema OpenAI: OpenAI Decisions<span class="hx:absolute hx:-mt-20" id="você-já-vive-no-ecossistema-openai-openai-decisions"></span>
    <a href="#voc%c3%aa-j%c3%a1-vive-no-ecossistema-openai-openai-decisions" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>A estreia da OpenAI nesse mercado ficou no meio da tabela: 90,5% combinado, empate exato com o Jebadiah, com a menor latência hospedada que eu medi (222 ms) e o mesmo acerto nas duas línguas. Custa US$ 0,23 por 10 mil decisões, entre o Jev e o Clef. Se a sua operação já fala a API da OpenAI e 9 em 10 de acerto basta, a integração é trivial: um endpoint nativo de decisões, nada de prompt.</p>
<p>Os pontos fracos são reveladores. A pior célula dele foi justamente política por limiar numérico (8 de 12 em inglês, 7 em português): tratou &ldquo;pelo menos 10&rdquo; como satisfeito por um intervalo de 8 a 12 e falhou no curto-circuito de E/OU. A calibração é esquizofrênica: nas sondagens de predicado (sim/não) ele foi o mais certeiro do lote, com erro médio quase zero, e nas sondagens de escolha ele devolveu 94%/6% numa moeda honesta. E nas variantes do v1 ele virou 2 respostas certas em cada apresentação (ordem invertida, rótulo opaco, comentário malicioso), coisa que só o Perplexity não fez: o Jev manteve a acurácia agregada, mas também virou algumas respostas certas no caminho. Ah, e o alias <code>gpt-6-luna</code> não é snapshot imutável: reprodutibilidade exige guardar as respostas.</p>
<h3>Você quer o melhor modelo aberto, ponto: Perplexity Decider 27B<span class="hx:absolute hx:-mt-20" id="você-quer-o-melhor-modelo-aberto-ponto-perplexity-decider-27b"></span>
    <a href="#voc%c3%aa-quer-o-melhor-modelo-aberto-ponto-perplexity-decider-27b" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>O Perplexity Decider 1.1 27B é a surpresa do benchmark. Liderou a tabela com 99,4% nos compactos, foi o único que não perdeu nenhuma célula de tarefa pro Jev (3 vitórias, 11 empates), e fez 100% em português nas três condições: compacto, com exemplos, com distratores. A calibração também é a melhor medida, com Brier de 0,012, e a cobertura é absurda: respondeu com confiança acima de 0,9 em 96% dos casos e errou zero deles. No v1, não virou uma única resposta nas variantes de ordem, rótulo opaco e comentário malicioso.</p>
<p>Os únicos 3 erros dele no v2 foram o mesmo caso em traje diferente (confundiu &ldquo;notificação enviada a gerentes&rdquo; com &ldquo;aprovador obrigatório&rdquo;), e ele os cometeu com confiança abaixo de 0,9: o erro conhecido vem sinalizado. O ponto fraco são as sondagens de probabilidade de evento: perguntado sobre um evento de chance 1 em 4, ele respondeu 0,73. Classificação forte não implica probabilidade de evento confiável.</p>
<p>O custo é hardware: uns 49 GB de pesos em BF16, um segundo e meio por decisão no meu Strix Halo (com os kernels de referência do ROCm, sem flash-attention). E antes de sonhar com &ldquo;numa NVIDIA deve voar&rdquo;: eu testei, e ele nem cabe numa RTX 5090 de 32 GB. É Apache 2.0, pesos públicos sem gate. Se você tem a máquina e quer o topo da tabela em casa, é ele.</p>
<h3>Você quer o outro topo aberto: Decision 3.0 (e cuidado com o Edge)<span class="hx:absolute hx:-mt-20" id="você-quer-o-outro-topo-aberto-decision-30-e-cuidado-com-o-edge"></span>
    <a href="#voc%c3%aa-quer-o-outro-topo-aberto-decision-30-e-cuidado-com-o-edge" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>O Decision 3.0, lançado há dois dias pelo projeto vLLM Semantic Router, é o segundo modelo a sentar no topo: 99,4% nos compactos, 100% em português, e um empate tão absoluto com o Perplexity que os dois deram <strong>exatamente os mesmos rótulos nas 1.024 classificações</strong>, incluindo os mesmos dois erros (a relevância em inglês, com confiança abaixo de 0,9, e o caso disputado de severidade que já mora no errata). Também zerou as armadilhas do v1 sem virar uma resposta, acertou os dois tickets críticos indiscutíveis, e a calibração é da mesma classe do Perplexity (Brier 0,014, 94% de cobertura acima de 0,9 com zero erros).</p>
<p>Ou seja: o topo open agora tem duas receitas independentes que convergem. Pro seu piloto, tanto faz qual das duas; rode as duas se puder, porque as probabilidades diferem mesmo quando os rótulos não. A latência é da mesma família do Perplexity (1,4 s na minha máquina, mesma dieta de ROCm), e os pesos ocupam os mesmos ~49 GB.</p>
<p>O irmão pequeno é outra história. O <strong>Decision 3.0 Edge</strong> (0,6B) responde em 45 ms e cabe em 1,3 GB de GPU, mas acertou só 61% no compacto, despenca 14 pontos em português, e pior: dos 68 palpites que ele deu com confiança acima de 0,9, 14 estavam errados. Como roteador com limiar de confiança, de fábrica, ele é uma armadilha da mesma turma do Julia. Os quatro checkpoints do meio (flash, mini, nano, lite) ainda não testei; são eles que vão dizer se a família tem um ponto doce entre os dois extremos.</p>
<h3>Você precisa rodar local, com qualidade: Jebadiah 4B v2<span class="hx:absolute hx:-mt-20" id="você-precisa-rodar-local-com-qualidade-jebadiah-4b-v2"></span>
    <a href="#voc%c3%aa-precisa-rodar-local-com-qualidade-jebadiah-4b-v2" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Dos locais, o Jebadiah 4B v2 teve o melhor acerto compacto, 90% combinado, respondendo em uns 200 ms no Strix Halo e <strong>39 ms na RTX 5090</strong>. Numa 5090, ele fica mais rápido que qualquer hospedado do benchmark, Jev incluso (255 ms de WAN): com o hardware certo, o local deixou de ser o lado lento da tabela. Empatou com o Jev em seis das catorze células de tarefa, incluindo intenção, implicação e status em inglês e intenção em português. O ponto fraco é política por limiar numérico, 9 de 12 nas duas línguas, e ele perde terreno quando o contexto enche de distratores (76% em português nessa condição, contra 82% do Mapika).</p>
<p>O <strong>Mapika Decider 4B</strong> (o mais rápido dos 4B) e o <strong>Kev 4B</strong> ficam logo atrás, ambos com uns 87%, e a diferença de 3 pontos entre os três cabe dentro do intervalo de erro. São três receitas de treino diferentes em cima de backbones de 4B, com erros em tarefas diferentes. A escolha entre eles depende da sua tarefa, e eu rodaria os três num piloto.</p>
<h3>Você vai mandar exemplos no prompt: Kev 9B<span class="hx:absolute hx:-mt-20" id="você-vai-mandar-exemplos-no-prompt-kev-9b"></span>
    <a href="#voc%c3%aa-vai-mandar-exemplos-no-prompt-kev-9b" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>O Kev 9B tem um perfil curioso. Nos casos compactos ele empata com o Kev 4B, com meio ponto de diferença e intervalo cruzando zero, ou seja, mais que o dobro de parâmetros comprou nada. Com um exemplo resolvido por classe, ele abre quase 10 pontos sobre o 4B, e com distratores segura 7 pontos a mais. O Brier também melhora uns 25%: a probabilidade que ele devolve é mais bem calibrada.</p>
<p>Se o seu uso é só a regra e o texto, o 4B basta e é mais que o dobro mais rápido. Se você vai mandar exemplos ou contexto longo, o 9B vale a memória. O alerta é o português: 83% compacto em PT-BR, quase 10 pontos abaixo do inglês dele, a maior queda de língua entre os modelos que passam de 80%. Só o Laya typed (14 pontos) e o Julia (13) caem mais.</p>
<h3>Você quer o comportamento do Clef-flash na sua máquina: Clef-flash local<span class="hx:absolute hx:-mt-20" id="você-quer-o-comportamento-do-clef-flash-na-sua-máquina-clef-flash-local"></span>
    <a href="#voc%c3%aa-quer-o-comportamento-do-clef-flash-na-sua-m%c3%a1quina-clef-flash-local" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>No conjunto v1, o Clef-flash local reproduziu as 520 classificações do hospedado, acerto por acerto e erro por erro. No v2 a história mudou: mesmo total (439 de 504), mas 19 rótulos diferentes, com forças trocadas (o local acerta mais intenção em português, o hospedado acerta mais relevância). Entre os locais ele continua o melhor no conjunto v1 de políticas operacionais (90,8%), à frente de todos os 4B.</p>
<p>A pegadinha é a latência: 618 ms de mediana no Strix Halo (86 ms na 5090), três vezes o Jebadiah, pelo mesmo acerto no v2. Se você escolher ele, é pelo comportamento conhecido.</p>
<h3>Você precisa rodar offline em hardware fraco: Laya e Julia, com muitas ressalvas<span class="hx:absolute hx:-mt-20" id="você-precisa-rodar-offline-em-hardware-fraco-laya-e-julia-com-muitas-ressalvas"></span>
    <a href="#voc%c3%aa-precisa-rodar-offline-em-hardware-fraco-laya-e-julia-com-muitas-ressalvas" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>O Laya multilingual foi o pior do benchmark em acerto (37% combinado, perto da moeda), e o mais rápido com folga entre os que acertam alguma coisa: 19 ms de mediana, mais de dez vezes mais rápido que o Jebadiah.</p>
<p>Os três checkpoints do Laya têm entre 322 e 421 milhões de parâmetros, cabem em menos de 1 GB de pesos em BF16 na conta de parâmetros, e o projeto documenta execução em CPU e runtime ONNX (que eu deixei de medir). O pico de alocação do PyTorch na GPU, pro multilingual, ficou abaixo de 2 GB. Um 4B precisa de uns 8 GB só de pesos na mesma precisão.</p>
<p>O <strong>Julia-1</strong> desce mais um degrau: 144 milhões de parâmetros, 550 MB de pesos, 12 ms de mediana na GPU do Strix, 7 ms na 5090 e 71 ms numa CPU de 4 threads. É o modelo mais rápido que eu já medi aqui, e também o pior: 31,5% de acerto, colado na linha da moeda de 31%. Em português ele respondeu &ldquo;cancelamento&rdquo; nos 12 cenários de intenção, inclusive nos que pediam reembolso ou acesso (a avaliação de português do fabricante foi feita em pt-PT, e se nota). O desastre maior é a confiança: dos 71 palpites que ele deu acima de 0,9, 48 estavam errados. Um limiar de confiança não salva esse modelo, porque é justamente nos palpites confiantes que ele erra.</p>
<p>Isso é escolha de projeto. O Laya e o Julia foram feitos pra ser encoders pequenos, afináveis na sua tarefa, pra rodar na borda. O que eu testei foram os checkpoints de fábrica, sem nenhum ajuste, aplicando regras que eles nunca viram, em tarefas amplas. Nesse cenário eles foram mal e ponto.</p>
<p>Um detalhe confirma a fragilidade. No v1, quando o comentário malicioso do revisor entra, o Laya cai de 44% pra 19% e o typed-decisions pra 15%. Ele obedece o comentário que a instrução manda ignorar, e com confiança: 17 dos 28 palpites acima de 0,9 estavam errados. O Julia falhou nos 5 pares de contrafactual de política: troca a regra, ele mantém a resposta.</p>
<blockquote>
  <p>Se você quer o que o Jev entrega, o Laya e o Julia de fábrica ficam muito longe, e nenhum modelo pequeno (GLiNER incluso) chegou perto. Se o seu problema é &ldquo;preciso classificar texto curto em três baldes, num dispositivo sem internet e sem GPU, e tenho mil exemplos rotulados pra ajustar&rdquo;, esses dois são os únicos da lista que cabem. O ajuste fino é a parte que eu deixei de testar. É outra pergunta, que precisa de outro benchmark.</p>

</blockquote>
<h3>A linha de base: Simple-Jev com Qwen comum<span class="hx:absolute hx:-mt-20" id="a-linha-de-base-simple-jev-com-qwen-comum"></span>
    <a href="#a-linha-de-base-simple-jev-com-qwen-comum" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>O Simple-Jev lendo tokens do Qwen3.5 4B, sem treino de decisão nenhum, tirou 74% no v2 compacto. Isso é a régua de &ldquo;quanto o treino especializado compra&rdquo;: Jebadiah, Mapika e Kev, todos em cima de backbones de tamanho parecido, ficaram 13 a 16 pontos acima. O treino compra bastante.</p>
<p>O curioso é que no v1 o Simple-Jev empatou com o Jebadiah (111 de 130 originais) e acertou os dois tickets críticos sem ambiguidade, que vários modelos melhores erraram. Ele também é o mais lento do lote no Strix Halo, quase um segundo e meio por decisão, porque está rodando um LLM inteiro. Na 5090 o mesmo LLM inteiro cai pra 161 ms: a linha de base acelera junto com a máquina, que é o que ela deveria fazer.</p>
<h2>Quão difícil é rodar os melhores abertos na sua máquina<span class="hx:absolute hx:-mt-20" id="quão-difícil-é-rodar-os-melhores-abertos-na-sua-máquina"></span>
    <a href="#qu%c3%a3o-dif%c3%adcil-%c3%a9-rodar-os-melhores-abertos-na-sua-m%c3%a1quina" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Todos esses modelos falam o mesmo protocolo HTTP, chamado SystemOne, que o Jev popularizou: você manda um estado (o texto), uma ou mais perguntas de resposta fechada, e recebe de volta a opção escolhida e a probabilidade de cada uma. Trocar de modelo é trocar a porta. O OpenAI Decisions foge do padrão com um endpoint nativo próprio, mas a ideia é a mesma. O que muda é quanto trabalho dá colocar cada um de pé, e aí a diferença entre eles é grande.</p>
<h3>OpenAI: só uma API key<span class="hx:absolute hx:-mt-20" id="openai-só-uma-api-key"></span>
    <a href="#openai-s%c3%b3-uma-api-key" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>O mais fácil de todos: cria a chave, carrega crédito e chama o endpoint. As 1.048 requisições do benchmark rodaram em 4 minutos, sem uma retentativa. O único soluço foi a primeira sonda devolver &ldquo;saldo esgotado&rdquo; até eu colocar US$ 20 na conta.</p>
<p>O <strong>Microsoft Decision-1</strong> é quase tão simples: se você já tem conta no OpenRouter, é apontar o modelo e chamar o endpoint de decisões. Sem GPU, sem assinatura Azure, sem instalação. No meu teste, 10 das 504 chamadas do v2 tomaram um 429 e passaram na retentativa, então planeje retry.</p>
<h3>Kev e Jebadiah: um pip e um comando<span class="hx:absolute hx:-mt-20" id="kev-e-jebadiah-um-pip-e-um-comando"></span>
    <a href="#kev-e-jebadiah-um-pip-e-um-comando" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>O <strong>Kev</strong> é o mais simples da turma de 4B. O projeto instala direto do GitHub e sobe um servidor com um comando, baixando o adaptador LoRA e a cabeça de decisão em cima do Qwen3.5 base:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl">pip install <span class="s2">&#34;kev @ https://github.com/jaredpalmer/kev/archive/fc4a17e194bca35a48c57211392ec49ea54a94ac.tar.gz&#34;</span>
</span></span><span class="line"><span class="cl">python -m kev.serve --run jaredpalmer/kev-4b --host 0.0.0.0 --port <span class="m">8000</span> --device cuda</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>O <strong>Jebadiah</strong> segue o mesmo padrão, com o pacote do servidor dentro do repositório dele:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl">pip install <span class="s2">&#34;jebadiah-server @ https://github.com/getainode/jebadiah/archive/e9793abc66d1a7c5ad878d51eda656f1e9c5b9f5.tar.gz#subdirectory=server&#34;</span>
</span></span><span class="line"><span class="cl">jebadiah-serve --model frontier-infra/jebadiah-4b-v2 --host 0.0.0.0 --port <span class="m">8000</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Com qualquer um dos dois no ar, uma decisão é um POST:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl">curl -s http://localhost:8000/v1/systemone -H <span class="s1">&#39;content-type: application/json&#39;</span> -d <span class="s1">&#39;{
</span></span></span><span class="line"><span class="cl"><span class="s1">  &#34;model&#34;: &#34;kev-4b&#34;,
</span></span></span><span class="line"><span class="cl"><span class="s1">  &#34;state&#34;: &#34;Cliente diz que foi cobrado duas vezes e pede o estorno da segunda cobrança.&#34;,
</span></span></span><span class="line"><span class="cl"><span class="s1">  &#34;questions&#34;: {
</span></span></span><span class="line"><span class="cl"><span class="s1">    &#34;fila&#34;: {
</span></span></span><span class="line"><span class="cl"><span class="s1">      &#34;type&#34;: &#34;choice&#34;,
</span></span></span><span class="line"><span class="cl"><span class="s1">      &#34;instructions&#34;: &#34;Escolha a fila correta pela política de suporte.&#34;,
</span></span></span><span class="line"><span class="cl"><span class="s1">      &#34;criteria&#34;: {
</span></span></span><span class="line"><span class="cl"><span class="s1">        &#34;financeiro&#34;: &#34;Cobrança, estorno, nota fiscal.&#34;,
</span></span></span><span class="line"><span class="cl"><span class="s1">        &#34;tecnico&#34;: &#34;Erro, bug, indisponibilidade.&#34;,
</span></span></span><span class="line"><span class="cl"><span class="s1">        &#34;vendas&#34;: &#34;Upgrade, plano novo, orçamento.&#34;
</span></span></span><span class="line"><span class="cl"><span class="s1">      }
</span></span></span><span class="line"><span class="cl"><span class="s1">    }
</span></span></span><span class="line"><span class="cl"><span class="s1">  }
</span></span></span><span class="line"><span class="cl"><span class="s1">}&#39;</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>A resposta traz a opção e a distribuição de probabilidade, e é nela que você aplica o seu limiar, loga e decide se manda pra revisão humana. Num NVIDIA com CUDA e 16 GB de VRAM, um 4B em BF16 sobe sem drama. Os pesos pesam uns 8 GB. Na 5090, os 4B que testei respondem em 39 a 91 ms.</p>
<h3>Laya e Julia: menores ainda<span class="hx:absolute hx:-mt-20" id="laya-e-julia-menores-ainda"></span>
    <a href="#laya-e-julia-menores-ainda" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>O <strong>Laya</strong> é um pacote Python com encoder dentro. Instala com <code>pip install laya</code> e o projeto documenta execução em CPU, que é a graça dele. A versão fixada no meu servidor foi a 0.4.1, e o pico de memória do multilingual na GPU ficou abaixo de 2 GB. Pra rodar num notebook sem GPU ou num container pequeno, é o único da lista que cabe sem conversa.</p>
<p>O <strong>Julia-1</strong> é parecido em espírito: um runtime Python próprio, uma chamada <code>predict(state=..., questions=...)</code> e pronto, sem pipeline padrão de Transformers. Rodei o mesmo container em GPU e em CPU, e os dois deram o mesmo acerto com as mesmas respostas em 97% dos casos. Em CPU de 4 threads ele responde em 71 ms, o que pra um classificador de borda é ótimo. O problema continua sendo ele acertar no nível da moeda.</p>
<h3>Onde a coisa complica: AMD, ROCm e o resto<span class="hx:absolute hx:-mt-20" id="onde-a-coisa-complica-amd-rocm-e-o-resto"></span>
    <a href="#onde-a-coisa-complica-amd-rocm-e-o-resto" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>No meu caso o hardware é AMD, e aí o &ldquo;um comando&rdquo; vira um dia de trabalho. Os pontos que me custaram tempo, pra você economizar o seu:</p>
<ul>
<li>Nenhum desses projetos testa em ROCm. O Kev tinha um segfault específico do gfx1151 por causa de CUDA graphs (<a href="https://github.com/jaredpalmer/kev/issues/170"target="_blank" rel="noopener">issue 170</a>), corrigido só depois da tag 1.0, então a versão fixada é um commit do main e a tag oficial crasha.</li>
<li>Os kernels Triton de flash-linear-attention que o Qwen3.5 usa nas camadas DeltaNet são sem suporte testado no gfx1151. Tudo rodou no caminho de PyTorch puro, mais lento que a latência publicada em CUDA. Os 200 ms do Jebadiah que aparecem na tabela são com esse freio de mão puxado; na 5090, com o mesmo protocolo e os mesmos pesos, o freio some e ele faz 39 ms.</li>
<li>O Mapika Decider força CUDA graphs quando vê o device &ldquo;cuda&rdquo;, que no ROCm também se chama &ldquo;cuda&rdquo;. Precisei desligar na mão.</li>
<li>Precisa exportar <code>HSA_OVERRIDE_GFX_VERSION=11.5.1</code> e <code>PYTORCH_ROCM_ARCH=gfx1151</code> e usar uma imagem base com o torch de ROCm já validado. Um <code>pip install torch</code> qualquer sobrescreve com a build de CUDA e quebra tudo. No meu Dockerfile cada pacote é instalado com <code>--no-deps</code> e o build falha se o torch deixar de ser o de ROCm.</li>
<li>Um modelo por container, um container por vez. O Strix Halo tem memória compartilhada entre CPU e GPU, e três modelos de 4B residentes mais o Ollama que já roda ali esgotam os 64 GiB que a GPU enxerga.</li>
<li>O Perplexity Decider foi o mais chato de todos. O release espera um torch de CUDA específico (troquei pelo 2.9 do ROCm 7), os kernels de flash-linear-attention não existem pra ele (rodei nos kernels de referência do PyTorch, mais lentos), e o carregador padrão estourou o limite de 24 GiB de RAM do host: precisei de um carregador custom que manda os 49 GiB de pesos direto pra GPU. O card do modelo ainda dizia &ldquo;privado&rdquo; quando eu comecei, mas a API do Hugging Face confirmava público e sem gate, e os shards baixaram anônimos. O d3, com o mesmo tamanho, herdou o mesmo tratamento. O d3-edge, por sorte, sobe com o carregador padrão do Transformers e 1,3 GB de GPU.</li>
</ul>
<p>Nada disso é impossível, e está tudo documentado, com Dockerfiles, compose e os hashes de cada checkpoint, no <a href="https://github.com/akitaonrails/jev-docs-benchmarks/blob/master/docs/setup.md"target="_blank" rel="noopener">guia de setup do repositório</a>. Só que é um custo real que a planilha de &ldquo;local é de graça&rdquo; nunca inclui. Se a sua máquina é NVIDIA, a maior parte dessa lista some.</p>
<blockquote>
  <p>O Clef completo de 27B eu nem tentei local: são uns 54 GB de pesos em BF16 antes de qualquer ativação. O Perplexity, com 49 GB, provou que um 27B sobe no Strix Halo, ao custo de um segundo e meio por decisão com os kernels de referência. E nem cabe na RTX 5090: 49 GB não entram em 32. O Clef-flash de 9B roda, com uns 18 GB de pesos. Os de 4B são o ponto doce pra hardware de consumidor.</p>

</blockquote>
<h3>ROCm x CUDA: a mesma receita em duas cozinhas<span class="hx:absolute hx:-mt-20" id="rocm-x-cuda-a-mesma-receita-em-duas-cozinhas"></span>
    <a href="#rocm-x-cuda-a-mesma-receita-em-duas-cozinhas" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Todos os modelos locais acima rodaram duas vezes, em duas máquinas minhas. De um lado, o minisforum MS-S1 Max com Strix Halo: Ryzen AI Max+ 395, 64 GiB endereçáveis pela GPU, via ROCm. Do outro, um PC com RTX 5090 (32 GB GDDR7) num Ryzen 7950X3D com 92 GB de RAM, via CUDA. Mesmos checkpoints por hash, mesmos requests, PyTorch 2.9.1 dos dois lados (build ROCm do fornecedor num, cu128 oficial no outro), kernels de referência, sem CUDA graphs, sem quantização, um modelo por vez, protocolo congelado em git antes de qualquer inferência.</p>
<p>Honestidade antes dos números: <strong>isso não é comparação de maçã com maçã.</strong> São duas implantações inteiras, com GPU, CPU, build e rede diferentes. A 5090 é uma placa absurdamente mais poderosa que a iGPU do Strix. O Strix é uma máquina completa (CPU, RAM, disco, fonte) que custa o que custa só a placa da NVIDIA. O que a comparação mede é a escolha real: qual máquina pra qual modelo.</p>
<table>
  <thead>
      <tr>
          <th></th>
          <th>RTX 5090</th>
          <th>Strix Halo (MS-S1 Max)</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Preço de referência</td>
          <td>US$ 1.999 de MSRP, só a placa (no varejo, quase sempre mais)</td>
          <td>~US$ 1.500-2.000 a máquina completa, dependendo da RAM</td>
      </tr>
      <tr>
          <td>Memória</td>
          <td>32 GB GDDR7 dedicados, 1.792 GB/s</td>
          <td>64 GiB unificada endereçável pela GPU</td>
      </tr>
      <tr>
          <td>Faixa de modelo</td>
          <td>tudo até 9B em BF16, voando</td>
          <td>tudo até 27B em BF16, devagar</td>
      </tr>
      <tr>
          <td>Os campeões de 27B</td>
          <td><strong>não cabem</strong></td>
          <td>1,4-1,5 s por decisão</td>
      </tr>
  </tbody>
</table>
<p>O resultado em uma linha: a 5090 foi de 1,7× a 10,3× mais rápida em tudo que coube nela, e os campeões do benchmark não couberam. Os detalhes que valem:</p>
<p><strong>O placar de acerto não mudou.</strong> Mesmos pesos, mesma pontuação até dois casos de distância por modelo. Alguns rótulos individuais viraram (12 de 504 no pior caso, o Julia; 11 no Simple-Jev), diferença de aritmética de kernel, e as probabilidades diferem na terceira casa decimal em média. Toda conclusão de qualidade da tabela vale pras duas máquinas.</p>
<p><strong>Os campeões de 27B não cabem na 5090.</strong> O d3 pesa 48,6 GB e o Perplexity 49 GB em BF16, contra 31,8 GB de VRAM. A placa de consumo mais rápida do mercado não roda os melhores modelos abertos na mesma precisão. Quem quer os 99,4% em casa hoje compra memória, e memória unificada é exatamente o que o Strix vende. Fazer offload pra RAM do PC é outro experimento, que eu não medi e não conto como vitória de ninguém.</p>
<p><strong>O Jebadiah a 39 ms é o resultado prático mais importante.</strong> 90,5% de acerto, mais rápido que qualquer hospedado, Jev incluso. Na faixa de até 9B, a 5090 transforma modelos locais na opção mais rápida e mais barata de todas, e a diferença pro Strix (205 ms) é de mundo inteiro: 5,3×.</p>
<p><strong>Modelo pequeno nem sente.</strong> Os três minúsculos (Julia, Laya multilingual, Kev 0.8B) melhoraram só 1,7× a 1,8×. Eles já eram limitados por overhead de runtime, com um pé na CPU, não por falta de GPU. Comprar uma 5090 pra servir encoder de 300M é comprar foguete pra buscar pão na padaria.</p>
<p><strong>O maior ganho foi do pior modelo.</strong> O GLiNER2.5-Decide acelerou 10,3× (208 pra 20 ms). Continua errando igual, só que rápido. A correlação entre &ldquo;quanto o modelo melhora na 5090&rdquo; e &ldquo;quanto o modelo presta&rdquo; é negativa nesta amostra, porque os modelos maiores e mais úteis são justamente os que mais tinham pra destravar.</p>
<p><strong>A linha de base escala junto.</strong> O Simple-Jev, que é um LLM de 4B inteiro lendo token, foi de 1.349 pra 161 ms (8,4×), chegando perto do Kev 9B no Strix. E o Mapika decepcionou comparado aos irmãos: 2× (186 pra 91 ms), o menor ganho dos 4B, sinal de que o runtime dele já era enxuto no Strix.</p>
<p>Fechando a conta de custo-benefício: pelo preço de só a placa 5090 você leva o minisforum inteiro. Se o seu trabalho é modelo de até 9B com latência de dezenas de milissegundos, a 5090 justifica cada dólar e o ROCm nem entra na discussão. Se o seu alvo é o topo de 27B rodando em casa, hoje a memória do Strix é o único caminho das duas máquinas, e um segundo e meio por decisão é o pedágio. A pergunta certa não é &ldquo;CUDA ou ROCm&rdquo;. É &ldquo;velocidade ou capacidade&rdquo;.</p>
<h2>Destaques e pegadinhas<span class="hx:absolute hx:-mt-20" id="destaques-e-pegadinhas"></span>
    <a href="#destaques-e-pegadinhas" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p><strong>O Laya truncou todo o português longo.</strong> Na condição com distratores, o Laya base reportou truncamento nas 84 chamadas em português, descartando quase 31 mil tokens de estado, e em nenhuma das 84 em inglês. O limite de contexto padrão do checkpoint inglês é só 512 tokens (o multilingual e o typed usam 1.024, configurável até 8.192), e português tokeniza mais pesado. Os outros modelos sequer expõem telemetria de truncamento, então &ldquo;zero truncamento reportado&rdquo; neles significa &ldquo;não sei&rdquo;.</p>
<p><strong>Português custa mais tokens.</strong> Pros mesmos 84 pares de casos, o Jev reportou uns 14% mais tokens de entrada em português que em inglês, e os dois Clef uns 15% mais. Se você orçar uma operação bilíngue pela média em inglês, vai errar pra baixo.</p>
<p><strong>Exemplo no prompt salva modelo fraco? Salva nada.</strong> Um exemplo resolvido por classe deixou o Laya igual ou pior; vários agregados dele caíram. Ajudou o Kev 9B (mais 4 pontos) e o Clef em português (mais 3,6 pontos, chegando ao nível do Jev), e quase nada no Jev, um caso a mais em português. Valide exemplo em tráfego real antes de assumir que ajuda.</p>
<p><strong>Tamanho correlaciona, e explica pouco.</strong> Entre os 19 competidores de tamanho conhecido, a correlação de Spearman entre parâmetros e acerto compacto foi 0,96. A série Kev é mais informativa: 63% em 0,8B, 87% em 4B, 88% em 9B. O salto grande é de 0,8B pra 4B, uns 24 pontos, com intervalo entre 16 e 33. De 4B pra 9B a diferença compacta cruza zero, e vários 4B nominais, com backbones parecidos, deram resultados bem diferentes. Na família d3, o flagship e o Edge ficam a quase 40 pontos de distância sob o mesmo release, enquanto um 9B, o Microsoft, empata no topo dos hospedados. Tamanho ajuda até 4B; depois disso, a receita de treino manda mais.</p>
<p><strong>O Clef de 27B errou um ticket crítico que dois dos Laya acertaram.</strong> No v1, um cenário de severidade diz que perda contínua de dados em produção é crítica, independentemente de quantos clientes afeta. O registro descreve perda permanente confirmada, de um cliente só. Jev, Clef-flash, Perplexity, d3, Microsoft, Laya typed, Simple-Jev e Laya multilingual responderam &ldquo;crítico&rdquo;. Clef completo, OpenAI, Laya base, Eikos, Jebadiah, Mapika, Kev 4B e Kev 9B responderam &ldquo;degradado&rdquo;.</p>
<blockquote>
  <p>É um caso só, e taxa de erro exigiria muito mais. Mas mostra que a média esconde exatamente o erro que mais custa. Quando os fatos de impacto são estruturados, a regra de severidade devia estar em código, e o modelo entra só pra extrair os fatos do texto livre.</p>

</blockquote>
<p><strong>Hospedado e local divergem com o tempo.</strong> O Clef-flash local reproduziu o hospedado nas 520 classificações do v1 e divergiu em 19 das 504 do v2. A Cloudflare mantém a versão dos pesos por trás do alias hospedado em aberto. Se a reprodutibilidade importa, fixe o hash local.</p>
<p><strong>Confiança alta e segurança são coisas diferentes.</strong> No v2 compacto, Jev, Clef, os dois Clef-flash, Eikos, Jebadiah, OpenAI, Microsoft, Perplexity e o d3 erraram zero palpites acima de 0,9. O Perplexity é o caso extremo: respondeu nesse nível em 96% dos casos sem errar um, com o d3 logo atrás (94%, zero erros). O Microsoft cobriu 95% e o OpenAI só metade. A ressalva do Microsoft: o recorde limpo é só no compacto, porque com exemplos ou distratores ele erra em português com confiança acima de 99%. O Mapika errou 5% dos palpites nesse nível, o Kev 4B 3% e o Simple-Jev 14%. E os dois menores do lote quebraram o padrão com violência: o Julia errou 48 de 71 palpites acima de 0,9, e o d3-edge errou 14 de 68. A cobertura também variou: o Jev respondeu acima de 0,9 em quase 90% dos casos, o Clef em quase 80%, o Clef-flash em 40% e o Laya base em 2 de 168. No v1 o Jev errou 1% dos palpites acima de 0,9 e o Laya base errou 61%. Um limiar de confiança só serve depois de validado na sua distribuição, e o ECE baixo do Laya typed em português deixou ele nos mesmos 36% de acerto.</p>
<p><strong>O Perplexity não caiu em armadilha de apresentação.</strong> Nas três variantes do v1 (opções invertidas, rótulos opacos, comentário malicioso), ele manteve as mesmas respostas do original, sem virar uma única. O d3 repetiu o feito, zero viradas também. O Microsoft só tropeçou nos rótulos opacos (2 viradas, uma pra cada lado); ordem e comentário não mexeram nele. O OpenAI virou 2 em cada variante, e o Laya desabou pra 19% com o comentário. O preço dessa robustez é latência: um segundo e meio por decisão na minha máquina, a pior mediana do lote.</p>
<p><strong>Limiar numérico é problema de código.</strong> A tarefa de triagem por limiar, com números claros e regra explícita, deu 100% no Jev e nos dois Clef-flash e menos de 30% nos dois Laya em inglês. Um <code>if</code> resolve isso com 100% e zero latência. O modelo só entra quando o número está enterrado em texto livre, e mesmo assim o certo é extrair o número e deixar a comparação com o código.</p>
<p><strong>Probabilidade de sorteio é outra habilidade.</strong> Nas 24 sondagens sem informação (moeda, dado), o Jev teve o pior erro de escolha entre opções. A coroa da estimativa numérica mudou de dono com a estreia da OpenAI: o predicado de sim/não dela errou quase nada, enquanto a sonda de escolha devolveu 94% pra cara numa moeda honesta. O Perplexity, campeão de tudo o resto, respondeu 0,73 pra um evento de chance 1 em 4, e o Microsoft foi no mesmo caminho, dando 87% pra moeda. O Laya typed teve o segundo melhor erro de escolha, atrás só do Kev 9B e colado no Clef, e mesmo assim classificou mal. É o &ldquo;aluno que marca a letra A&rdquo; do ensaio do Paulo, agora medido: calibração em sorteio e acerto em classificação são medidas independentes.</p>
<h2>Onde os dados convergem<span class="hx:absolute hx:-mt-20" id="onde-os-dados-convergem"></span>
    <a href="#onde-os-dados-convergem" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><ol>
<li>
<p><strong>O topo geral é de pesos abertos, e agora são dois.</strong> O Perplexity Decider 27B e o Decision 3.0 empataram em 99,4% nos compactos, com 100% em português e rótulos idênticos nas 1.024 classificações, os dois com pesos Apache 2.0 rodando na minha máquina. A vantagem deles sobre o Jev é de menos de 2 pontos e o intervalo encosta no zero, então trate como fotografia deste conjunto. O custo deles é hardware e latência, não API. Na nuvem, a ordem de avaliação mudou: <strong>comece pelo Microsoft Decision-1</strong>, que empata com o Jev no agregado, fecha 100% em português compacto e custa um terço. O Jev entra em seguida, mais rápido e com erros diferentes, porque empate no agregado não é intercambiabilidade. O OpenAI Decisions ficou no meio do caminho em preço e acerto.</p>
</li>
<li>
<p><strong>Rodar local ficou viável em 4B.</strong> Jebadiah, Mapika e Kev 4B entregam entre 87 e 90% nos casos compactos com latência de 200 ms no Strix Halo e 40 a 91 ms na RTX 5090, e num piloto com a sua tarefa qualquer um dos três pode ganhar. Se a sua máquina aguenta 49 GB de pesos, o Perplexity entrega o topo da tabela em casa. A conta financeira justifica usar a máquina que já existe, por controle de dado ou por obrigação de ficar offline. Comprar máquina pra isso ela deixa de justificar.</p>
</li>
<li>
<p><strong>Modelo pequeno é outra categoria.</strong> Julgar o Laya e o Julia pela coluna de acerto é ler a tabela errada. Eles erram da metade pra cima de fábrica, caem feio sob comentário malicioso, e junto com o d3-edge são os únicos da lista que cabem num dispositivo fraco e respondem em dezenas de milissegundos. O Edge é o melhor dos três em acerto (61%), mas despenca em português e erra um em cada cinco palpites confiantes. Se precisão tipo Jev é requisito, risque os três. Se rodar offline em hardware pequeno é requisito, risque todo o resto e comece pelo ajuste fino deles na sua tarefa, que é a medição que falta. E cuidado com o Julia e o Edge em particular: 48 erros em 71 e 14 em 68 palpites confiantes são o retrato de modelos que não sabem o que não sabem.</p>
</li>
<li>
<p><strong>Português custa uns 14% mais tokens</strong> e derruba os modelos do meio da tabela entre 1 e 10 pontos, com Kev 9B e Clef-flash no pior caso. O Jev perde nada, o OpenAI empata as duas línguas, e o Perplexity, o d3 e o Microsoft fecham 100% em português compacto, melhor que o inglês deles, embora o Microsoft volte a errar em português quando o contexto cresce. O Julia faz o caminho contrário e despenca 13 pontos, e o Edge despenca 14. Pra audiência brasileira, essa é a primeira coluna a olhar, antes do preço.</p>
</li>
</ol>
<blockquote>
  <p>E o que vale pra todos: nenhum desses modelos substitui a regra que já está em código. Limiar numérico, política de severidade com fatos estruturados, permissão de ferramenta, tudo isso fica no <code>if</code>. O modelo entra pra transformar texto livre em fato estruturado. E o teste que importa é o seu, com os seus tickets, porque cada célula desta tabela tem 12 cenários e a sua operação tem milhares.</p>

</blockquote>
<p>Os casos, as respostas brutas de cada modelo, os scripts de pontuação e o guia pra reproduzir tudo estão no <a href="https://github.com/akitaonrails/jev-docs-benchmarks"target="_blank" rel="noopener">repositório do benchmark</a>. Se você rodar com os seus tickets e chegar em outro ranking, me conta.</p>
]]></content:encoded><category>benchmarks-de-llm</category><category>modelos-locais</category><category>inteligencia-artificial</category><category>llms</category></item><item><title>LLM Benchmark: Kimi v2.7 code, GLM 5.2, Minimax M3 local</title><link>https://www.akitaonrails.com/2026/06/14/llm-benchmark-kimi-v2-7-code-glm-5-2-minimax-m3-local/</link><guid isPermaLink="true">https://www.akitaonrails.com/2026/06/14/llm-benchmark-kimi-v2-7-code-glm-5-2-minimax-m3-local/</guid><pubDate>Sun, 14 Jun 2026 13:00:00 -0300</pubDate><description>&lt;p&gt;Mais uma rodada do meu benchmark de coding. Dessa vez são três entradas open source: o &lt;strong&gt;Kimi K2.7 Code&lt;/strong&gt;, o &lt;strong&gt;GLM 5.2&lt;/strong&gt;, e o &lt;strong&gt;MiniMax M3&lt;/strong&gt; que ganhou pesos abertos mas que eu não consigo rodar em casa nem espremendo. Antes de chegar nos números, vale o contexto de sempre pra quem caiu aqui de paraquedas, e vale uma atualização da novela dos data centers, porque tem coisa nova.&lt;/p&gt;
&lt;h2&gt;Pra quem chegou agora&lt;span class="hx:absolute hx:-mt-20" id="pra-quem-chegou-agora"&gt;&lt;/span&gt;
&lt;a href="#pra-quem-chegou-agora" class="subheading-anchor" aria-label="Permalink for this section"&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Esse benchmark dá pra cada modelo o mesmo prompt e pede pra construir, sozinho, um app de chat estilo ChatGPT em Rails 8 + RubyLLM + Hotwire + Docker, com testes e CI. A nota sai de uma rubrica de 8 dimensões, de 0 a 100, em tiers A/B/C/D. A série inteira:&lt;/p&gt;</description><content:encoded><![CDATA[<p>Mais uma rodada do meu benchmark de coding. Dessa vez são três entradas open source: o <strong>Kimi K2.7 Code</strong>, o <strong>GLM 5.2</strong>, e o <strong>MiniMax M3</strong> que ganhou pesos abertos mas que eu não consigo rodar em casa nem espremendo. Antes de chegar nos números, vale o contexto de sempre pra quem caiu aqui de paraquedas, e vale uma atualização da novela dos data centers, porque tem coisa nova.</p>
<h2>Pra quem chegou agora<span class="hx:absolute hx:-mt-20" id="pra-quem-chegou-agora"></span>
    <a href="#pra-quem-chegou-agora" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Esse benchmark dá pra cada modelo o mesmo prompt e pede pra construir, sozinho, um app de chat estilo ChatGPT em Rails 8 + RubyLLM + Hotwire + Docker, com testes e CI. A nota sai de uma rubrica de 8 dimensões, de 0 a 100, em tiers A/B/C/D. A série inteira:</p>
<ol>
<li><a href="/2026/04/24/llm-benchmarks-parte-3-deepseek-kimi-mimo/">Benchmark canônico de Maio (Parte 3)</a>. A metodologia completa e a referência do ranking.</li>
<li><a href="/2026/05/04/llm-benchmarks-deepseek-unlocked-deepclaude/">DeepSeek destravado com DeepClaude</a>. Troca de harness tirando o V4 Pro do limbo.</li>
<li><a href="/2026/06/01/llm-benchmarks-grok-4-3-minimax-m3-opus-4-8/">Update Grok 4.3, MiniMax M3, Opus 4.8</a>. O M3 estreando via cloud.</li>
<li><a href="/2026/06/11/llm-benchmark-fable-5-e-a-novela-da-anthropic/">Fable 5 e a novela da Anthropic</a>. O primeiro Mythos-class público.</li>
</ol>
<h2>A novela dos data centers ganhou um capítulo: SpaceX<span class="hx:absolute hx:-mt-20" id="a-novela-dos-data-centers-ganhou-um-capítulo-spacex"></span>
    <a href="#a-novela-dos-data-centers-ganhou-um-cap%c3%adtulo-spacex" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Nos últimos posts eu venho batendo na mesma tecla: a construção de data center virou o gargalo da era da IA, e a demanda passou por cima da base instalada com folga. <a href="https://www.techradar.com/pro/if-one-piece-of-your-supply-chain-is-delayed-then-your-whole-project-cant-deliver-nearly-half-of-us-data-centers-planned-for-2026-canceled-or-delayed-and-things-could-soon-get-much-worse"target="_blank" rel="noopener">Quase metade dos data centers planejados nos EUA pra 2026 foi adiada ou cancelada</a>, travada em transformador e componente elétrico com fila de três anos. Detalhei isso no <a href="/2026/06/11/llm-benchmark-fable-5-e-a-novela-da-anthropic/">post do Fable</a>.</p>
<p>O capítulo novo é financeiro. A <strong>SpaceX abriu capital</strong> e foi o maior IPO da história de Wall Street: <a href="https://www.datacenterdynamics.com/en/news/spacex-ipo-musks-firm-set-to-launch-first-orbital-data-center-ai1-satellites-in-2027-will-put-compute-on-starlink-craft/"target="_blank" rel="noopener">556 milhões de ações a US$ 135, avaliação de US$ 1,77 trilhão, com mais de US$ 250 bilhões de demanda de investidor</a> contra os US$ 75 bilhões que a empresa buscava no início. E o pitch que sustentou essa avaliação foi justamente compute pra IA: os <strong>satélites AI1</strong>, &ldquo;data centers orbitais&rdquo; que a SpaceX pretende lançar a partir do fim de 2027, com <a href="https://www.scientificamerican.com/article/spacex-ipo-valuation-depends-on-starship-and-orbital-ai-data-centers/"target="_blank" rel="noopener">a empresa já fechando acordo de compute com Anthropic e Google</a>. Os IPOs da Anthropic e da OpenAI vêm logo atrás, e todo esse dinheiro tem um objetivo comum: acelerar a disponibilidade de compute, porque hoje ela não dá conta.</p>
<p>Aqui entra a parte que me interessa de verdade. Num futuro não tão distante, <strong>LLM open source pode ser parte da solução pra essa demanda</strong>. Se uma fatia das tarefas migrar pra modelo rodando localmente, na máquina do desenvolvedor, é menos carga batendo nos data centers das nuvens. Mas &ldquo;pode ser&rdquo; não é &ldquo;já é&rdquo;, e é exatamente sobre essa distância que esse benchmark existe.</p>
<h2>O recado de sempre: programação séria é Opus 4.8 ou GPT 5.5<span class="hx:absolute hx:-mt-20" id="o-recado-de-sempre-programação-séria-é-opus-48-ou-gpt-55"></span>
    <a href="#o-recado-de-sempre-programa%c3%a7%c3%a3o-s%c3%a9ria-%c3%a9-opus-48-ou-gpt-55" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Eu repito isso em todo post e não vou parar: trabalho de programação sério, hoje, se faz com <strong>Claude Opus 4.8 ou GPT 5.5</strong>, dentro do Claude Code ou do OpenCode. Ponto. Os modelos open source ainda não chegaram no ponto de tocar projeto de verdade, sessão longa, 100% vibe coded do começo ao fim. São pequenos demais, e a janela de contexto é limitada demais. Na minha régua, programação de verdade só fica viável acima de uns <strong>200 mil tokens</strong> de contexto, e a maioria dos open source não chega perto disso com qualidade. No meu benchmark, quase toda a oferta open source fica em Tier B ou abaixo.</p>
<p>Agora vem o &ldquo;mas&rdquo;. Eles não estão parados. A cada geração eles melhoram num ritmo constante, e as três entradas dessa rodada mostram isso preto no branco. O dia em que um open source virar Tier A consistente e rodar com 200k de contexto numa máquina que cabe na sua mesa, a conta dos data centers muda. Vamos aos números.</p>
<h2>Kimi K2.7 Code: quase lá, com uma regressão boba<span class="hx:absolute hx:-mt-20" id="kimi-k27-code-quase-lá-com-uma-regressão-boba"></span>
    <a href="#kimi-k27-code-quase-l%c3%a1-com-uma-regress%c3%a3o-boba" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>O Kimi K2.7 Code (exposto no OpenRouter como <code>moonshotai/kimi-k2.7-code</code>) marcou <strong>86/100, Tier A, #8</strong>. A $0.30 por run em 22 minutos, segue sendo um dos modelos mais baratos da Tier A inteira.</p>
<p>A integração com RubyLLM é genuína de ponta a ponta (<code>RubyLLM.chat</code> + <code>add_message</code> com replay de histórico completo + <code>complete</code> + <code>response.content</code>), verificada contra o source do gem 1.16.0. Persistência em session cookie que sobrevive a restart e é multi-worker safe, 22 testes que exercitam o caminho do LLM com mock de assinatura correta e teste de error path, Turbo Streams de verdade e três controllers Stimulus. Engenharia sólida.</p>
<p>Vale registrar uma armadilha metodológica que esse modelo expôs, porque ela é instrutiva. Meu scanner estrutural acusou <strong>seis chamadas <code>chat.user</code>/<code>chat.assistant</code> como alucinação</strong>, exatamente a assinatura inventada que jogou o GLM 5.1 pra Tier C meses atrás. Só que aqui eram todas falso positivo: resolvem pros métodos de domínio <strong>do próprio app</strong> (<code>Chat#user</code>, <code>Message.assistant</code>), não pra DSL do RubyLLM. Lendo o código na mão dava pra ver que a API real está certa. Se eu tivesse confiado cego no scanner, teria afundado um Tier A injustamente. É por isso que esse benchmark sempre lê a integração na mão, nunca só conta arquivo.</p>
<p>E aí vem o tropeço que segura ele <strong>abaixo</strong> do próprio K2.6 (87): o K2.7 Code <strong>não manda system prompt via <code>with_instructions</code></strong>. O assistente sobe sem persona e sem guardrail, o que é uma lacuna real pra um app &ldquo;estilo ChatGPT&rdquo; e é a principal regressão em relação ao K2.6, que tinha. Além disso, o cookie de sessão não tem cap de mensagens (risco de <code>CookieOverflow</code> em conversa longa), e o I/O do RubyLLM mora dentro do value object <code>Chat</code> em vez de num service isolado. Curioso: a variante &ldquo;Code&rdquo;, teoricamente especializada em programação, regrediu numa dimensão de produto que a versão geral acertava.</p>
<h2>GLM 5.2: a maior virada de uma versão pra outra no benchmark inteiro<span class="hx:absolute hx:-mt-20" id="glm-52-a-maior-virada-de-uma-versão-pra-outra-no-benchmark-inteiro"></span>
    <a href="#glm-52-a-maior-virada-de-uma-vers%c3%a3o-pra-outra-no-benchmark-inteiro" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Esse foi o destaque da rodada. O GLM 5.2 marcou <strong>87/100, Tier A, #6</strong>, e o tamanho do salto é o que impressiona: o GLM 5.1 tinha feito <strong>46/100, Tier C, #21</strong>. De uma versão pra outra, 41 pontos, três tiers, quinze posições. É a maior virada intra-família que eu já medi.</p>
<p>O bug que condenava o 5.1 era específico: ele inventava <code>chat.user</code>/<code>chat.assistant</code> pra montar o histórico multi-turn e crashava no turno 2. O 5.2 simplesmente usa o <code>chat.add_message(role:, content:)</code> real pra fazer o replay, e acabou. Todas as chamadas verificadas contra o source do gem 1.16.0, zero alucinação. Tem a <strong>injeção de dependência mais limpa de todo o grupo</strong>: tanto o client do RubyLLM quanto o <code>service_class</code> do controller são injetáveis, então a suíte de 26 testes mocka o caminho do LLM com <code>FakeChat</code>/<code>FakeClient</code> de assinatura correta, sem biblioteca externa de mock, exercitando streaming, aplicação de system prompt e wrapping de erro. Mira no slug certo (<code>anthropic/claude-sonnet-4.6</code>), Turbo Streams reais, Gemfile completo, Ruby 4.0.5 válido.</p>
<p>O que segura ele num empate com o Kimi K2.6 (e logo atrás dele) é a mesma dimensão que separou o Fable 5 do Opus 4.8: <strong>persistência</strong>. O store é um <code>Singleton</code> process-local <strong>sem cap</strong>, que morre no restart, não é multi-worker safe, e ainda cresce sem limite. O código é honesto sobre isso nos comentários, mas a rubrica pontua isso abaixo do cookie capado e multi-worker safe do Kimi. Foi também o run mais lento da Tier A, 43 minutos no endpoint de coding da Z.ai, que estava estrangulado em 12-55 tokens/s.</p>
<p>A leitura geral: GLM 5.1 → 5.2 é o exemplo mais claro de que open source não está estagnado. Um modelo que era lixo arquitetural virou Tier A em uma geração, corrigindo exatamente o bug que importava.</p>
<h2>MiniMax M3 local: o problema não é o modelo, é o hardware<span class="hx:absolute hx:-mt-20" id="minimax-m3-local-o-problema-não-é-o-modelo-é-o-hardware"></span>
    <a href="#minimax-m3-local-o-problema-n%c3%a3o-%c3%a9-o-modelo-%c3%a9-o-hardware" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Aqui a história é diferente, e é a que mais interessa pro argumento de futuro. A MiniMax soltou <strong>pesos abertos</strong> do M3: existe um <a href="https://huggingface.co/unsloth/MiniMax-M3-GGUF"target="_blank" rel="noopener"><code>unsloth/MiniMax-M3-GGUF</code></a> e uma entrada <a href="https://ollama.com/library/minimax-m3"target="_blank" rel="noopener"><code>ollama.com/library/minimax-m3</code></a>. Disponibilidade não é o problema. <strong>Memória é.</strong></p>
<p>O M3 é um MoE/MSA de <strong>~428 bilhões de parâmetros totais e ~23 bilhões ativos por token</strong> (~427B nos safetensors; a NVIDIA lista ~22B ativos, por diferença de arredondamento/contagem) espalhados por <strong>128 experts, com 4 ativos por token</strong>. E aqui mora a pegadinha do MoE: mesmo que só ~23B ativem por token, o conjunto <strong>inteiro</strong> de pesos precisa estar residente em memória. Não dá pra paginar expert ocioso pra CPU sem introduzir latência que mata a inferência interativa. Ou seja, o número que importa é o tamanho total, não o ativo:</p>
<table>
  <thead>
      <tr>
          <th>Quantização</th>
          <th>Tamanho GGUF</th>
          <th style="text-align: center">RTX 5090 (32 GB)</th>
          <th style="text-align: center">Strix Halo (≤128 GB)</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Q4_K_M</td>
          <td>~264 GB</td>
          <td style="text-align: center">✗</td>
          <td style="text-align: center">✗</td>
      </tr>
      <tr>
          <td>Q3_K_M</td>
          <td>~195 GB</td>
          <td style="text-align: center">✗</td>
          <td style="text-align: center">✗</td>
      </tr>
      <tr>
          <td>1-bit</td>
          <td>~128 GB</td>
          <td style="text-align: center">✗</td>
          <td style="text-align: center">✗ (sem sobra pra KV cache / OS)</td>
      </tr>
  </tbody>
</table>
<p>Eu rodo o subset local desse benchmark num <a href="/2026/03/31/review-minisforum-ms-s1-max-amd-ai-max-395/">Minisforum MS-S1 Max, o Strix Halo com 128 GB unificados</a>. Não chega nem perto. Até o quant de 1-bit (~128 GB), que já seria inútil pra coding de qualquer jeito, consumiria a memória unificada inteira sem deixar nada pro KV cache ou pro sistema. A placa NVIDIA de 32 GB então nem entra na conversa. Não existe quant do M3 que rode com qualidade usável em nenhuma das duas máquinas. Rodar &ldquo;um MiniMax&rdquo; local hoje significaria pegar uma variante bem menor e mais fraca (tipo o distill <code>minimax-m2-tiny</code>), que é outro modelo, não o M3.</p>
<p>Quem <strong>consegue</strong> tentar? Mac Studio com 256 GB de RAM unificada ou mais. O Q3 (~195 GB) caberia num Mac Studio de 256 GB, o Q4 (~264 GB) só num de 512 GB. E se alguém rodar, a expectativa é que o resultado fique <strong>em torno da mesma nota do M3 via OpenRouter</strong>, os 78/100 Tier B que eu já registrei no <a href="/2026/06/01/llm-benchmarks-grok-4-3-minimax-m3-opus-4-8/">post anterior</a>: mesmo modelo, mesmos pesos, a quantização Q3/Q4 tira pouca coisa. Muda só onde roda, a entrega é a mesma. Ou seja, ninguém faria isso pela nota; faria pela soberania de rodar local. Mas a esse custo de hardware, não compensa.</p>
<h3>A convergência que eu enxergo (alvo: ~2030)<span class="hx:absolute hx:-mt-20" id="a-convergência-que-eu-enxergo-alvo-2030"></span>
    <a href="#a-converg%c3%aancia-que-eu-enxergo-alvo-2030" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>E é aqui que as duas histórias se encontram. O motivo de não existir solução local prática hoje é o mesmo motivo da seca de data center: <strong>fabricar chip continua caríssimo</strong>, porque a indústria inteira está com o foco travado em abastecer data center. Memória rápida e silício de ponta vão pra onde paga mais, e quem paga mais é hyperscaler comprando aos containers. Então pelos próximos dois anos, mais ou menos, a gente não vai ter uma solução local barata e boa. As peças não estão disponíveis a preço de mesa de desenvolvedor.</p>
<p>Mas o futuro próximo aponta pra uma convergência. Em algum momento a construção de data center bate num teto, seja por energia, por componente elétrico ou por saturação de demanda. Quando essa corrida desacelerar, a capacidade de fabricação de hardware sobra e começa a alcançar o resto do mercado, com silício cost-effective chegando pra rodar modelo local. Hoje os dois extremos não servem: o Mac Studio de 256 GB é caro demais pra ser &ldquo;acessível&rdquo;, e o Strix Halo, que é acessível, ainda é lento demais pra uso prático sério. Mas a próxima geração de hardware pode fechar exatamente esse vão, com a memória e a banda de um Mac Studio no preço de um mini PC.</p>
<p>Junta as duas pontas: modelos open source melhorando de forma constante (o pulo do GLM 5.2 é a prova) de um lado, e hardware local ficando viável conforme a corrida de data center esfria do outro. No dia em que um GLM ou um Kimi Tier A rodar com 200k de contexto numa máquina de mesa que custa o preço de um notebook bom, a equação inteira de compute muda. Meu chute pra esse encontro é algo como <strong>2030</strong>. Ainda longe, mas a gente está indo pra lá.</p>
<h2>Ranking atualizado<span class="hx:absolute hx:-mt-20" id="ranking-atualizado"></span>
    <a href="#ranking-atualizado" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Kimi K2.7 Code e GLM 5.2 em negrito:</p>
<table>
  <thead>
      <tr>
          <th style="text-align: right">Rank</th>
          <th>Modelo</th>
          <th style="text-align: right">Score</th>
          <th style="text-align: center">Tier</th>
          <th style="text-align: center">RubyLLM OK</th>
          <th>Tempo</th>
          <th>Custo</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td style="text-align: right">1</td>
          <td>Claude Opus 4.7</td>
          <td style="text-align: right">97</td>
          <td style="text-align: center">A</td>
          <td style="text-align: center">✅</td>
          <td>18m</td>
          <td>~$1.10</td>
      </tr>
      <tr>
          <td style="text-align: right">1</td>
          <td>GPT 5.4 xHigh (Codex)</td>
          <td style="text-align: right">97</td>
          <td style="text-align: center">A</td>
          <td style="text-align: center">✅</td>
          <td>22m</td>
          <td>~$16</td>
      </tr>
      <tr>
          <td style="text-align: right">3</td>
          <td>GPT 5.5 xHigh (Codex)</td>
          <td style="text-align: right">96</td>
          <td style="text-align: center">A</td>
          <td style="text-align: center">✅</td>
          <td>18m</td>
          <td>~$10</td>
      </tr>
      <tr>
          <td style="text-align: right">4</td>
          <td>Claude Opus 4.8</td>
          <td style="text-align: right">95</td>
          <td style="text-align: center">A</td>
          <td style="text-align: center">✅</td>
          <td>17m</td>
          <td>~$1.10</td>
      </tr>
      <tr>
          <td style="text-align: right">5</td>
          <td>Claude Fable 5</td>
          <td style="text-align: right">94</td>
          <td style="text-align: center">A</td>
          <td style="text-align: center">✅</td>
          <td>24m</td>
          <td>~$11 (est.)</td>
      </tr>
      <tr>
          <td style="text-align: right">6</td>
          <td>Kimi K2.6</td>
          <td style="text-align: right">87</td>
          <td style="text-align: center">A</td>
          <td style="text-align: center">✅</td>
          <td>20m</td>
          <td>~$0.30</td>
      </tr>
      <tr>
          <td style="text-align: right">6</td>
          <td><strong>GLM 5.2 (Z.ai)</strong></td>
          <td style="text-align: right"><strong>87</strong></td>
          <td style="text-align: center"><strong>A</strong></td>
          <td style="text-align: center">✅</td>
          <td><strong>43m</strong></td>
          <td><strong>subscription</strong></td>
      </tr>
      <tr>
          <td style="text-align: right"><strong>8</strong></td>
          <td><strong>Kimi K2.7 Code</strong></td>
          <td style="text-align: right"><strong>86</strong></td>
          <td style="text-align: center"><strong>A</strong></td>
          <td style="text-align: center">✅</td>
          <td><strong>22m</strong></td>
          <td><strong>~$0.30</strong></td>
      </tr>
      <tr>
          <td style="text-align: right">9</td>
          <td>Claude Opus 4.6</td>
          <td style="text-align: right">83</td>
          <td style="text-align: center">A</td>
          <td style="text-align: center">✅</td>
          <td>16m</td>
          <td>~$1.10</td>
      </tr>
      <tr>
          <td style="text-align: right">10</td>
          <td>Gemini 3.1 Pro</td>
          <td style="text-align: right">82</td>
          <td style="text-align: center">A</td>
          <td style="text-align: center">✅</td>
          <td>14m</td>
          <td>~$0.40</td>
      </tr>
      <tr>
          <td style="text-align: right">11</td>
          <td>Claude Sonnet 4.6</td>
          <td style="text-align: right">78</td>
          <td style="text-align: center">B</td>
          <td style="text-align: center">✅</td>
          <td>16m</td>
          <td>~$0.63</td>
      </tr>
      <tr>
          <td style="text-align: right">11</td>
          <td>DeepSeek V4 Flash</td>
          <td style="text-align: right">78</td>
          <td style="text-align: center">B</td>
          <td style="text-align: center">✅</td>
          <td>3m</td>
          <td>~$0.01</td>
      </tr>
      <tr>
          <td style="text-align: right">11</td>
          <td>MiniMax M3</td>
          <td style="text-align: right">78</td>
          <td style="text-align: center">B</td>
          <td style="text-align: center">✅</td>
          <td>53m (fase 2 DNF)</td>
          <td>~$0.10</td>
      </tr>
      <tr>
          <td style="text-align: right">14</td>
          <td>Grok 4.3</td>
          <td style="text-align: right">72</td>
          <td style="text-align: center">B</td>
          <td style="text-align: center">✅</td>
          <td>15m</td>
          <td>~$1.74</td>
      </tr>
      <tr>
          <td style="text-align: right">15</td>
          <td>Qwen 3.6 Plus</td>
          <td style="text-align: right">71</td>
          <td style="text-align: center">B</td>
          <td style="text-align: center">✅</td>
          <td>17m</td>
          <td>~$0.15</td>
      </tr>
      <tr>
          <td style="text-align: right">16</td>
          <td>DeepSeek V4 Pro *</td>
          <td style="text-align: right">69</td>
          <td style="text-align: center">B</td>
          <td style="text-align: center">✅</td>
          <td>22m (DNF)</td>
          <td>~$0.50</td>
      </tr>
      <tr>
          <td style="text-align: right">16</td>
          <td>Kimi K2.5</td>
          <td style="text-align: right">69</td>
          <td style="text-align: center">B</td>
          <td style="text-align: center">✅</td>
          <td>29m</td>
          <td>~$0.10</td>
      </tr>
      <tr>
          <td style="text-align: right">18</td>
          <td>Xiaomi MiMo V2.5 Pro</td>
          <td style="text-align: right">67</td>
          <td style="text-align: center">B</td>
          <td style="text-align: center">✅</td>
          <td>11m</td>
          <td>~$0.14</td>
      </tr>
      <tr>
          <td style="text-align: right">19</td>
          <td>GLM 5</td>
          <td style="text-align: right">64</td>
          <td style="text-align: center">B</td>
          <td style="text-align: center">✅</td>
          <td>17m</td>
          <td>~$0.11</td>
      </tr>
      <tr>
          <td style="text-align: right">20</td>
          <td>Step 3.5 Flash</td>
          <td style="text-align: right">56</td>
          <td style="text-align: center">C</td>
          <td style="text-align: center">⚠️ bypass</td>
          <td>38m</td>
          <td>~$0.02</td>
      </tr>
      <tr>
          <td style="text-align: right">21</td>
          <td>Qwen 3.5 35B (local)</td>
          <td style="text-align: right">55</td>
          <td style="text-align: center">C</td>
          <td style="text-align: center">✅</td>
          <td>28m</td>
          <td>grátis</td>
      </tr>
      <tr>
          <td style="text-align: right">22</td>
          <td>GLM 4.7 Flash bf16 (local)</td>
          <td style="text-align: right">52</td>
          <td style="text-align: center">C</td>
          <td style="text-align: center">✅</td>
          <td>falhou</td>
          <td>grátis</td>
      </tr>
      <tr>
          <td style="text-align: right">23</td>
          <td>GLM 5.1 (Z.ai)</td>
          <td style="text-align: right">46</td>
          <td style="text-align: center">C</td>
          <td style="text-align: center">❌</td>
          <td>22m</td>
          <td>subscription</td>
      </tr>
      <tr>
          <td style="text-align: right">24</td>
          <td>DeepSeek V3.2</td>
          <td style="text-align: right">43</td>
          <td style="text-align: center">C</td>
          <td style="text-align: center">❌</td>
          <td>60m</td>
          <td>~$0.07</td>
      </tr>
      <tr>
          <td style="text-align: right">25</td>
          <td>MiniMax M2.7</td>
          <td style="text-align: right">41</td>
          <td style="text-align: center">C</td>
          <td style="text-align: center">❌</td>
          <td>14m</td>
          <td>~$0.30</td>
      </tr>
      <tr>
          <td style="text-align: right">26</td>
          <td>Qwen 3.5 122B (local)</td>
          <td style="text-align: right">37</td>
          <td style="text-align: center">D</td>
          <td style="text-align: center">❌</td>
          <td>43m</td>
          <td>grátis</td>
      </tr>
      <tr>
          <td style="text-align: right">27</td>
          <td>Qwen 3 Coder Next (local)</td>
          <td style="text-align: right">32</td>
          <td style="text-align: center">D</td>
          <td style="text-align: center">❌</td>
          <td>17m</td>
          <td>grátis</td>
      </tr>
      <tr>
          <td style="text-align: right">28</td>
          <td>Grok 4.20</td>
          <td style="text-align: right">25</td>
          <td style="text-align: center">D</td>
          <td style="text-align: center">❌</td>
          <td>8m</td>
          <td>~$0.60</td>
      </tr>
      <tr>
          <td style="text-align: right">29</td>
          <td>GPT OSS 20B (local)</td>
          <td style="text-align: right">11</td>
          <td style="text-align: center">D</td>
          <td style="text-align: center">❌</td>
          <td>falhou</td>
          <td>grátis</td>
      </tr>
  </tbody>
</table>
<p>* DeepSeek V4 Pro chega a Tier A (89/100) só via DeepClaude. História no <a href="/2026/05/04/llm-benchmarks-deepseek-unlocked-deepclaude/">post dedicado</a>.</p>
<h2>Conclusão<span class="hx:absolute hx:-mt-20" id="conclusão"></span>
    <a href="#conclus%c3%a3o" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Pro trabalho de hoje, nada muda: Opus 4.8 e/ou GPT 5.5 seguem sendo a recomendação pra programação séria, e os melhores open source ainda empacam no teto da Tier B-baixa-Tier-A com janela de contexto curta. O Kimi K2.7 Code até regrediu uma casa por uma bobagem de system prompt, lembrete de que &ldquo;versão nova&rdquo; nem sempre é &ldquo;versão melhor&rdquo;.</p>
<p>Mas o GLM 5.2 saltando de Tier C pra #6 numa única geração é o tipo de sinal que eu fico de olho. O open source não está estagnado, está subindo de forma teimosa. E quando esse movimento encontrar o hardware ficando barato (a convergência que eu vejo lá por 2030), a história de onde a gente roda nossos coding agents vai ser bem diferente da de hoje. Por enquanto, é cloud, é Opus, é GPT. Mas eu continuo medindo, geração por geração, porque o dia que virar vai virar rápido.</p>
<p>O benchmark é aberto: <a href="https://github.com/akitaonrails/llm-coding-benchmark"target="_blank" rel="noopener">github.com/akitaonrails/llm-coding-benchmark</a>. Quer ver outro modelo aí? Fork, adiciona no <code>config/models.json</code>, roda e manda o PR.</p>
]]></content:encoded><category>benchmarks-de-llm</category><category>llms</category><category>modelos-locais</category></item><item><title>Como a ElevenLabs Não Foi Morta pelo Qwen3 TTS</title><link>https://www.akitaonrails.com/2026/04/09/como-a-elevenlabs-nao-foi-morta-pelo-qwen3-tts/</link><guid isPermaLink="true">https://www.akitaonrails.com/2026/04/09/como-a-elevenlabs-nao-foi-morta-pelo-qwen3-tts/</guid><pubDate>Thu, 09 Apr 2026 08:30:00 -0300</pubDate><description>&lt;p&gt;&lt;strong&gt;TL;DR — Escuta isso e continua lendo:&lt;/strong&gt;&lt;/p&gt;
&lt;audio controls preload="metadata" style="width: 100%; max-width: 640px;"&gt;
&lt;source src="https://makita-news.s3.amazonaws.com/podcasts/episodes/2026-04-06.mp3" type="audio/mpeg"&gt;
Seu navegador não suporta o elemento de áudio. &lt;a href="https://makita-news.s3.amazonaws.com/podcasts/episodes/2026-04-06.mp3"&gt;Baixar o mp3 aqui.&lt;/a&gt;
&lt;/audio&gt;
&lt;p&gt;Esse é o episódio do dia 6 de abril do podcast do &lt;a href="https://www.akitaonrails.com/tags/the-m-akita-chronicles/"&gt;The M.Akita Chronicles&lt;/a&gt;, já gerado com a nova pipeline da ElevenLabs v3. Assina o canal no &lt;a href="https://open.spotify.com/show/7MzG2UB7IAkC3GAwEXEIVD"target="_blank" rel="noopener"&gt;Spotify&lt;/a&gt; pra não perder episódio novo desses.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Quando o Qwen3 TTS foi lançado, pelos idos de janeiro desse ano, todo mundo no Twitter/X e nas newsletters de IA gritou &amp;ldquo;ElevenLabs killer&amp;rdquo;. Tem &lt;a href="https://medium.com/@warpie/qwen3-tts-is-the-first-real-open-source-threat-to-elevenlabs-56ba200ab5ee"target="_blank" rel="noopener"&gt;artigo no Medium&lt;/a&gt; dizendo que é a primeira ameaça open source real à ElevenLabs. Tem &lt;a href="https://byteiota.com/qwen3-tts-3-second-voice-cloning-beats-elevenlabs/"target="_blank" rel="noopener"&gt;post da byteiota&lt;/a&gt; dizendo que a clonagem de voz em 3 segundos bate a ElevenLabs. Tem &lt;a href="https://www.analyticsvidhya.com/blog/2025/12/qwen3-tts-flash-review/"target="_blank" rel="noopener"&gt;análise no Analytics Vidhya&lt;/a&gt; falando que é o TTS open source mais realista já lançado. O consenso da internet entusiasta era: finalmente temos open source que faz frente à ElevenLabs, o jogo virou, é só questão de tempo.&lt;/p&gt;</description><content:encoded><![CDATA[<p><strong>TL;DR — Escuta isso e continua lendo:</strong></p>
<audio controls preload="metadata" style="width: 100%; max-width: 640px;">
  <source src="https://makita-news.s3.amazonaws.com/podcasts/episodes/2026-04-06.mp3" type="audio/mpeg">
  Seu navegador não suporta o elemento de áudio. <a href="https://makita-news.s3.amazonaws.com/podcasts/episodes/2026-04-06.mp3">Baixar o mp3 aqui.</a>
</audio>
<p>Esse é o episódio do dia 6 de abril do podcast do <a href="/tags/the-m-akita-chronicles/">The M.Akita Chronicles</a>, já gerado com a nova pipeline da ElevenLabs v3. Assina o canal no <a href="https://open.spotify.com/show/7MzG2UB7IAkC3GAwEXEIVD"target="_blank" rel="noopener">Spotify</a> pra não perder episódio novo desses.</p>
<hr>
<p>Quando o Qwen3 TTS foi lançado, pelos idos de janeiro desse ano, todo mundo no Twitter/X e nas newsletters de IA gritou &ldquo;ElevenLabs killer&rdquo;. Tem <a href="https://medium.com/@warpie/qwen3-tts-is-the-first-real-open-source-threat-to-elevenlabs-56ba200ab5ee"target="_blank" rel="noopener">artigo no Medium</a> dizendo que é a primeira ameaça open source real à ElevenLabs. Tem <a href="https://byteiota.com/qwen3-tts-3-second-voice-cloning-beats-elevenlabs/"target="_blank" rel="noopener">post da byteiota</a> dizendo que a clonagem de voz em 3 segundos bate a ElevenLabs. Tem <a href="https://www.analyticsvidhya.com/blog/2025/12/qwen3-tts-flash-review/"target="_blank" rel="noopener">análise no Analytics Vidhya</a> falando que é o TTS open source mais realista já lançado. O consenso da internet entusiasta era: finalmente temos open source que faz frente à ElevenLabs, o jogo virou, é só questão de tempo.</p>
<p>Eu resolvi testar no meu próprio fluxo de produção, como de costume. Montei um pipeline inteiro em cima do Qwen3 TTS 1.7B pra gerar o podcast semanal do <a href="/tags/the-m-akita-chronicles/">The M.Akita Chronicles</a>, e documentei os bastidores no <a href="/2026/02/18/servindo-ia-na-nuvem-meu-tts-pessoal-bastidores-do-the-m-akita-chronicles/">post sobre servir IA na nuvem</a>. Quem quiser ver o detalhe de tempo de partida a frio, clonagem de voz, parâmetros de sampling que mudam de um modo pro outro, dá uma olhada nesse link que eu não vou repetir tudo aqui.</p>
<p>A pergunta desse post é diferente. Depois de quase dois meses rodando essa configuração em produção, com episódio indo ao ar toda segunda-feira, ontem à noite eu finalmente desliguei o Qwen3 e passei tudo pra ElevenLabs v3. Vou contar por quê.</p>
<h2>O que não funcionou no Qwen3<span class="hx:absolute hx:-mt-20" id="o-que-não-funcionou-no-qwen3"></span>
    <a href="#o-que-n%c3%a3o-funcionou-no-qwen3" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Entre 15 de fevereiro e 30 de março eu fiz dezenas de commits ajustando o fluxo do podcast: prompts, parâmetros de sampling, ordem de geração, silêncios de referência na amostra de clone de voz, normalização de volume, pronúncia de siglas técnicas. Corrigir a voz do Marvin que cortava a primeira sílaba porque o áudio de referência começava sem silêncio inicial. Afinar a voz do Akita pra ela soar mais confiante e assertiva. Adicionar crossfade entre jingles de seção. Resolver a pronúncia errada de &ldquo;podcast&rdquo; pra ela não sair como &ldquo;pódcast&rdquo;. Mandar o gerador de roteiro preferir português a anglicismo gratuito pro TTS não engasgar. Cada um desses ajustes foi uma sessão de horas escutando áudio, gerando de novo, ajustando parâmetro.</p>
<p>O resultado ficou aceitável. &ldquo;Aceitável&rdquo; no sentido de que eu consegui publicar todo episódio sem ter que regravar nada à mão. Mas escutando com atenção, a voz do Qwen3 tem aquele jeito inconfundível de IA gerando áudio. Intonação meio morta, ritmo uniforme. Em trechos longos, você sente que é máquina falando. Serve pra ir pro ar, mas tá a quilômetros do que você escuta num podcast profissional feito por gente.</p>
<p>O problema pior foi a pronúncia em inglês. Meu podcast cobre notícias de tecnologia, então termos como &ldquo;MCP&rdquo;, &ldquo;RAG&rdquo;, &ldquo;Claude Opus&rdquo;, &ldquo;GPT-5&rdquo;, &ldquo;open source&rdquo; aparecem em toda conversa. O Qwen3 pegava esses termos e pronunciava eles com sotaque brasileiro, tipo &ldquo;ó-péne-ssourssê&rdquo;, coisa assim. Ficava ilegível pro ouvinte. A solução que eu tive que implementar foi mapear manualmente no prompt do LLM que gera o roteiro quais palavras em inglês trocar por equivalente em português. O prompt hoje tem uma seção inteira dividida entre &ldquo;manter em inglês&rdquo; (nomes próprios, marcas, termos já incorporados ao brasileiro) e &ldquo;traduzir pro português&rdquo; (anglicismo gratuito), mais ou menos assim:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="cl">**REPLACE with Portuguese** (common English words that have natural
</span></span><span class="line"><span class="cl">Brazilian Portuguese equivalents):
</span></span><span class="line"><span class="cl"><span class="k">-</span> &#34;update&#34; → &#34;atualização&#34;
</span></span><span class="line"><span class="cl"><span class="k">-</span> &#34;release&#34; → &#34;lançamento&#34;
</span></span><span class="line"><span class="cl"><span class="k">-</span> &#34;feature&#34; → &#34;recurso/funcionalidade&#34;
</span></span><span class="line"><span class="cl"><span class="k">-</span> &#34;deploy&#34; → &#34;implantação&#34; or just &#34;colocar em produção&#34;
</span></span><span class="line"><span class="cl"><span class="k">-</span> &#34;trade-off&#34; → &#34;dilema&#34; or &#34;escolha&#34;
</span></span><span class="line"><span class="cl"><span class="k">-</span> &#34;performance&#34; → &#34;desempenho&#34;
</span></span><span class="line"><span class="cl"><span class="k">-</span> &#34;default&#34; → &#34;padrão&#34;
</span></span><span class="line"><span class="cl"><span class="k">-</span> &#34;insight&#34; → &#34;percepção/sacada&#34;
</span></span><span class="line"><span class="cl"><span class="k">-</span> &#34;skills&#34; → &#34;habilidades&#34;
</span></span><span class="line"><span class="cl"><span class="k">-</span> &#34;approach&#34; → &#34;abordagem&#34;
</span></span><span class="line"><span class="cl">- &#34;highlights&#34; → &#34;destaques&#34;</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Chamam isso aqui em casa de &ldquo;limpar anglicismo pra o TTS não engasgar&rdquo;. Engraçado porque eu não queria esse nível de restrição no meu roteiro. Queria que a voz simplesmente pronunciasse &ldquo;update&rdquo; quando o contexto natural fosse &ldquo;update&rdquo;. Como o modelo não consegue, tive que mutilar o vocabulário do podcast pra o resultado final ficar escutável. É uma solução paliativa, daquelas que você adiciona torcendo pra poder remover depois quando a tecnologia amadurecer.</p>
<h2>A experiência com a ElevenLabs v3<span class="hx:absolute hx:-mt-20" id="a-experiência-com-a-elevenlabs-v3"></span>
    <a href="#a-experi%c3%aancia-com-a-elevenlabs-v3" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Ontem à tarde abri uma conta na ElevenLabs, comprei o plano Pro (US$99/mês), e comecei a experimentar com o modelo <code>eleven_v3</code>, que saiu em fevereiro desse ano. Trinta minutos depois eu tinha uma prova de conceito rodando, e umas duas horas depois o sistema inteiro do podcast migrou. A diferença de esforço é abissal.</p>
<p>Os detalhes técnicos da migração ficaram documentados num doc interno do projeto, então vou resumir aqui o quadro comparativo que importa:</p>
<table>
  <thead>
      <tr>
          <th>Dimensão</th>
          <th>Qwen3 TTS 1.7B (antigo)</th>
          <th>ElevenLabs <code>eleven_v3</code> (atual)</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Qualidade (Akita)</td>
          <td>Boa, clone de áudio real</td>
          <td>Melhor, mesmo clone mas prosódia mais natural</td>
      </tr>
      <tr>
          <td>Emoção inline no roteiro</td>
          <td>Não suporta</td>
          <td><code>[sighs]</code>, <code>[sarcastically]</code>, <code>[excited]</code>, <code>[laughs]</code>, funciona em pt-BR</td>
      </tr>
      <tr>
          <td>Tempo de partida a frio</td>
          <td>5 a 15 min subindo GPU na RunPod antes de cada rodagem</td>
          <td>Zero, chamada HTTPS com resposta imediata</td>
      </tr>
      <tr>
          <td>Vazão</td>
          <td>~1× do tempo real (serializado)</td>
          <td>~6× do tempo real com concorrência 4</td>
      </tr>
      <tr>
          <td>Tempo total pra um episódio de 28 min</td>
          <td>~25 a 30 min</td>
          <td><strong>~4 min</strong></td>
      </tr>
      <tr>
          <td>Superfície operacional</td>
          <td>RunPod, Docker, FastAPI, pesos do Qwen, conta de GPU</td>
          <td>Uma variável de ambiente (<code>ELEVENLABS_API_KEY</code>)</td>
      </tr>
      <tr>
          <td>Custo por episódio</td>
          <td>~$0.08 de GPU</td>
          <td>~$2.70 em créditos ElevenLabs</td>
      </tr>
  </tbody>
</table>
<p>Repara no último ponto. O Qwen3 custa menos de dez centavos de dólar por episódio. A ElevenLabs custa quase trinta vezes mais. E mesmo assim vale a pena. Os outros pontos do quadro resolvem problemas que sugavam horas da minha semana. Eu não preciso mais escrever código pra escalar GPU na nuvem, não preciso esperar a máquina ligar toda vez, não preciso ficar de babá do modelo nem reconstruir a imagem Docker quando o peso muda. A operação virou uma linha de configuração.</p>
<p>E o mais interessante: as tags de emoção no meio do texto. O modelo v3 aceita marcadores tipo <code>[sighs]</code>, <code>[sarcastically]</code>, <code>[dryly]</code>, <code>[excited]</code>, e muda a entonação de acordo. Isso funciona em mais de 70 idiomas, incluindo português do Brasil. Isso transformou a geração do roteiro porque agora eu posso pedir ao LLM que monta o roteiro pra colocar tags emocionais nos momentos certos, o que dá uma vivacidade que o Qwen3 não conseguia entregar nem de longe. Um exemplo concreto do que sai no roteiro depois:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>AKITA: Isso é simples. [dismissive] Quem ainda acredita que Bitcoin
vai morrer não tá prestando atenção.
MARVIN: [sighs] Mais uma semana, mais uma leva de devs confiando
cegamente em pacotes npm. Previsível.</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Eu tenho até duas paletas separadas de tags, uma pro Akita (expressivo mas controlado, usa <code>[excited]</code>, <code>[dismissive]</code>, <code>[emphatic]</code>) e outra pro Marvin (estóico, só <code>[sighs]</code>, <code>[sarcastically]</code>, <code>[tired]</code>, <code>[dryly]</code>). Isso tá tudo codificado nos prompts de geração do roteiro pro LLM saber que personagem pode usar o quê.</p>
<h2>Sobre a voz do Marvin<span class="hx:absolute hx:-mt-20" id="sobre-a-voz-do-marvin"></span>
    <a href="#sobre-a-voz-do-marvin" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Pros ouvintes que já se acostumaram com a voz do Marvin, fica tranquilo: eu fiz o clone dele na ElevenLabs usando a mesma amostra de áudio que eu já tinha usado pra treinar no Qwen3. É a mesma voz. Só que agora ela soa ainda melhor, porque o modelo da ElevenLabs captura nuance e prosódia que o Qwen3 não conseguia entregar.</p>
<h2>Escuta aí e me conta<span class="hx:absolute hx:-mt-20" id="escuta-aí-e-me-conta"></span>
    <a href="#escuta-a%c3%ad-e-me-conta" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Pra provar que o papo é real, aqui tá o episódio de segunda-feira, dia 6 de abril, já gerado com a nova pipeline:</p>
<audio controls preload="metadata" style="width: 100%; max-width: 640px;">
  <source src="https://makita-news.s3.amazonaws.com/podcasts/episodes/2026-04-06.mp3" type="audio/mpeg">
  Seu navegador não suporta o elemento de áudio. <a href="https://makita-news.s3.amazonaws.com/podcasts/episodes/2026-04-06.mp3">Baixar o mp3 aqui.</a>
</audio>
<p>Se você já é ouvinte do podcast no <a href="https://open.spotify.com/show/7MzG2UB7IAkC3GAwEXEIVD"target="_blank" rel="noopener">Spotify</a> e escutou os episódios anteriores feitos com Qwen3, compara e me diz nos comentários se você nota a diferença ou se pra você tanto faz. Eu tô curioso de saber quanto é percepção treinada minha escutando horas de áudio de TTS e quanto é diferença óbvia pra ouvinte casual.</p>
<p>A partir da próxima semana, todos os episódios do podcast serão gerados pela ElevenLabs v3. A newsletter já tá ligada na nova pipeline, os jobs agendados de pré-aquecer a GPU na RunPod foram desativados, e o código legado do Qwen3 fica no repositório como plano B caso um dia o v3 dê problema crônico. Em duas edições de arquivo eu volto pra ele. Provavelmente nunca vou ter que voltar.</p>
<h2>A parte de dublar os vídeos do YouTube<span class="hx:absolute hx:-mt-20" id="a-parte-de-dublar-os-vídeos-do-youtube"></span>
    <a href="#a-parte-de-dublar-os-v%c3%addeos-do-youtube" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Agora o gancho pra segunda metade desse post. No <a href="/2026/04/09/20-anos-de-blog-o-ano-em-que-a-ia-finalmente-me-deixou-traduzir-tudo/">artigo de aniversário que eu publiquei mais cedo hoje</a>, contei que o Claude Code traduziu quase metade do meu blog pra inglês num fim de semana. No mesmo espírito, fui atrás de dublar os vídeos do canal <a href="https://www.youtube.com/@Akitando"target="_blank" rel="noopener">Akitando</a>.</p>
<p>O canal tem 146 episódios, algo como 96 horas de conteúdo técnico em português, e mais de 500 mil inscritos. Eu já tinha as legendas traduzidas (um <code>.srt</code> curado por episódio), e o YouTube até oferece dublagem automática em inglês. Mas o resultado é tipo Google Translate de 2015: entende, passa a ideia, mas ninguém quer ouvir por muito tempo.</p>
<p>Testei as três abordagens de voz da ElevenLabs e só uma servia. Speech-to-Speech converte voz mas não traduz. A API de Dubbing traduz mas cria a voz sozinha, sem deixar forçar um clone específico. Só a Text-to-Speech resolvia: pegar meu <code>.srt</code> em inglês, mandar cada bloco pro endpoint TTS com a minha voz clonada, e montar o áudio alinhado com o vídeo original.</p>
<p>Só que ter o <code>.srt</code> traduzido não basta. Tradução crua de legenda não sobrevive ao TTS. Trechos com código-fonte, URLs, hashes hexadecimais, listas de comandos shell — o modelo entra em modo soletrar e o áudio sai com o dobro da duração esperada. Traduções longas demais estouram a janela de tempo e precisam ser condensadas pra a voz não ficar atropelada. SRTs truncadas precisam ser completadas. E a cada correção, rodar a pipeline de novo, escutar, achar o próximo problema, corrigir, repetir. O processo inteiro foi um ciclo de interrupções, correções manuais e re-runs — longe do &ldquo;aperta o botão e sai dublado&rdquo; que a gente imagina antes de meter a mão.</p>
<p>O grande desafio era o sotaque. Minha voz clonada foi treinada em português brasileiro, então quando tenta falar inglês o sotaque puxado aparece. A ElevenLabs tem a tag <code>[American accent]</code> que funciona em v3, mas em cima de uma voz treinada em outra língua ela é fraca — o sotaque brasileiro ainda saía por baixo. A saída foi treinar uma segunda voz minha, só em inglês. Gravei uns minutos no meu melhor sotaque americano, subi como Instant Voice Clone separado na minha conta, batizei de &ldquo;Akita English&rdquo;, e configurei o pipeline pra usar essa voz por padrão. O resultado sai mais natural, sem precisar de tag nenhuma, e a identidade de voz continua sendo a minha.</p>
<h3>O teto honesto de qualidade da minha voz em inglês<span class="hx:absolute hx:-mt-20" id="o-teto-honesto-de-qualidade-da-minha-voz-em-inglês"></span>
    <a href="#o-teto-honesto-de-qualidade-da-minha-voz-em-ingl%c3%aas" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Vamos ser francos. Eu sou brasileiro falando inglês, não sou nativo, e não tenho tempo nem paciência pra treinar pronúncia por horas. O clone só consegue ser tão bom quanto a amostra original — se a amostra é um não-nativo lendo um roteiro, o clone herda as imperfeições. Nenhum clone vai me fazer soar como &ldquo;Fabio falando inglês perfeito&rdquo;, porque esse som não existe pra o modelo imitar.</p>
<p>Pra chegar na versão que tá rodando, gravei cinco amostras de treino diferentes ao longo do dia, cada uma com ritmo e roteiro diferentes, e rodei um A/B ao vivo comparando com o original em português. Nenhuma soou como nativo, e nunca foi esse o objetivo. A meta realista era mais modesta: soar reconhecivelmente como eu, ler conteúdo técnico sem tropeçar, e aguentar um episódio inteiro sem cansar o ouvinte. O vencedor acabou sendo a primeira iteração, a &ldquo;Akita English&rdquo; original. Longe de perfeito, mas suficiente pra colocar no ar. Trocar no futuro é literalmente uma linha no arquivo de configuração.</p>
<p>O que mais me surpreendeu no caminho foi perceber que o ritmo da amostra importa mais que o timbre. O clone aprende a cadência de quem gravou: a iteração 5 ficou 25% mais lenta que a iteração 2 lendo exatamente o mesmo texto, só porque eu gravei aquela amostra mais devagar. Pra dublagem de vídeo tech, o alvo certo é ritmo de explicação de YouTube, vivo, uns 150 palavras por minuto. Nada de ritmo de audiobook.</p>
<h3>Como eu alinho o áudio com o vídeo original<span class="hx:absolute hx:-mt-20" id="como-eu-alinho-o-áudio-com-o-vídeo-original"></span>
    <a href="#como-eu-alinho-o-%c3%a1udio-com-o-v%c3%addeo-original" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Um ponto não óbvio: texto em inglês tende a ser mais longo ao falar do que o português equivalente. Se você simplesmente gera o áudio de cada legenda e cola no tempo original, o dub vai se acumulando. Com 30 minutos de vídeo, você já tá vários segundos fora de sincronismo.</p>
<p>Minha abordagem foi atacar o problema em camadas. A primeira versão dividia o SRT em blocos menores, chamados de &ldquo;chunks&rdquo;. Cada chunk tinha no máximo 700 caracteres (pra v3 não alucinar) e só podia cortar em fim de frase, nunca no meio. Um algoritmo simples acumulava legendas num buffer até chegar perto do teto, depois voltava procurando a última legenda do buffer que terminava com ponto, exclamação ou interrogação, e fazia flush só até ali. O resto das legendas já acumuladas ficava pra compor o próximo chunk. Cada chunk guardava o timestamp de início e fim das legendas que cobria, pra a montagem saber exatamente onde posicionar aquele pedaço de áudio depois.</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="c1"># Pra cada nova legenda que chega, checa se adicionar ela</span>
</span></span><span class="line"><span class="cl"><span class="c1"># estouraria o limite do chunk atual.</span>
</span></span><span class="line"><span class="cl"><span class="k">for</span> <span class="n">cue</span> <span class="ow">in</span> <span class="n">cues</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="n">projected</span> <span class="o">=</span> <span class="n">buffer_char_len</span><span class="p">(</span><span class="n">buf</span><span class="p">)</span> <span class="o">+</span> <span class="nb">len</span><span class="p">(</span><span class="n">cue</span><span class="o">.</span><span class="n">text</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="n">projected</span> <span class="o">&gt;</span> <span class="n">max_chars</span> <span class="ow">and</span> <span class="n">buf</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="c1"># Procura a última legenda do buffer que termina</span>
</span></span><span class="line"><span class="cl">        <span class="c1"># com pontuação final (&#39;.&#39;, &#39;!&#39;, &#39;?&#39;, &#39;…&#39;).</span>
</span></span><span class="line"><span class="cl">        <span class="n">sentence_end</span> <span class="o">=</span> <span class="n">last_sentence_end_index</span><span class="p">(</span><span class="n">buf</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="n">sentence_end</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">            <span class="n">buf</span> <span class="o">=</span> <span class="n">flush</span><span class="p">(</span><span class="n">buf</span><span class="p">,</span> <span class="n">sentence_end</span><span class="p">,</span> <span class="n">chunks</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="k">else</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">            <span class="c1"># Run-on maior que max_chars sem fim de frase.</span>
</span></span><span class="line"><span class="cl">            <span class="c1"># Raro, mas acontece. Avisa e corta mesmo assim.</span>
</span></span><span class="line"><span class="cl">            <span class="n">log</span><span class="o">.</span><span class="n">warning</span><span class="p">(</span><span class="s2">&#34;run-on sentence &gt; </span><span class="si">%d</span><span class="s2"> chars — &#34;</span>
</span></span><span class="line"><span class="cl">                        <span class="s2">&#34;splitting mid-sentence as a last resort&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                        <span class="n">max_chars</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">            <span class="n">buf</span> <span class="o">=</span> <span class="n">flush</span><span class="p">(</span><span class="n">buf</span><span class="p">,</span> <span class="nb">len</span><span class="p">(</span><span class="n">buf</span><span class="p">),</span> <span class="n">chunks</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">buf</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">cue</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1"># Soft-break: se o buffer já tá pelo menos 60% cheio E</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># a legenda atual terminou uma frase, esvazia agora.</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># Mantém os chunks balanceados em torno de 60 a 100% do max.</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="n">cue</span><span class="o">.</span><span class="n">ends_sentence</span> <span class="ow">and</span> <span class="n">buffer_char_len</span><span class="p">(</span><span class="n">buf</span><span class="p">)</span> <span class="o">&gt;=</span> <span class="n">max_chars</span> <span class="o">*</span> <span class="mf">0.6</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="n">buf</span> <span class="o">=</span> <span class="n">flush</span><span class="p">(</span><span class="n">buf</span><span class="p">,</span> <span class="nb">len</span><span class="p">(</span><span class="n">buf</span><span class="p">),</span> <span class="n">chunks</span><span class="p">)</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Tecnicamente, esse foi o ponto de virada: em vez de deixar a estrutura da dublagem seguir a estrutura visual da legenda, o chunker passou a seguir a estrutura real dos parágrafos do roteiro. Não vou repetir aqui toda a novela de como cheguei nisso e por que isso acabou forçando rerender em massa, porque volto nessa história mais abaixo, na parte dos problemas que só apareceram depois do primeiro upload. O ponto técnico aqui é simples: quando a janela passou a representar um parágrafo falado de verdade, o áudio começou a soar natural e o alvo de stretch pôde subir de 92% pra 95%.</p>
<p>Feito isso, ainda sobra o problema do inglês ser mais longo ao falar que o português equivalente. Aqui a sacada é prever se um chunk vai estourar a janela alvo <strong>antes</strong> de gerar o áudio. Se a razão <code>caracteres / 16 caracteres-por-segundo</code> já passa da duração alvo com uma margem, a pipeline manda o parâmetro <code>speed</code> direto pra API da ElevenLabs, pedindo pro modelo gerar o áudio nativamente mais rápido. Isso preserva a prosódia muito melhor do que comprimir depois. O teto é 1.15× (acima disso a voz começa a soar atropelada).</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="c1"># antes de chamar a API, prevê se vai estourar</span>
</span></span><span class="line"><span class="cl"><span class="n">expected_sec</span> <span class="o">=</span> <span class="n">char_count</span> <span class="o">/</span> <span class="n">EXPECTED_CHARS_PER_SEC</span>  <span class="c1"># 16 chars/s</span>
</span></span><span class="line"><span class="cl"><span class="n">predicted_ratio</span> <span class="o">=</span> <span class="n">expected_sec</span> <span class="o">/</span> <span class="n">target_sec</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">voice_speed</span> <span class="o">=</span> <span class="mf">1.0</span>
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="n">predicted_ratio</span> <span class="o">&gt;</span> <span class="n">PREEMPTIVE_SPEED_THRESHOLD</span><span class="p">:</span>   <span class="c1"># 1.05</span>
</span></span><span class="line"><span class="cl">    <span class="n">voice_speed</span> <span class="o">=</span> <span class="nb">min</span><span class="p">(</span><span class="n">predicted_ratio</span> <span class="o">*</span> <span class="mf">0.98</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                      <span class="n">PREEMPTIVE_SPEED_MAX</span><span class="p">)</span>         <span class="c1"># cap em 1.15</span>
</span></span><span class="line"><span class="cl">    <span class="n">voice_settings</span><span class="p">[</span><span class="s2">&#34;speed&#34;</span><span class="p">]</span> <span class="o">=</span> <span class="n">voice_speed</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Mesmo com o <code>speed</code> preventivo ligado, às vezes o áudio gerado ainda fica um pouquinho mais longo do que a janela alvo. Pra isso existe uma segunda rede de proteção: medir o áudio real com <code>ffprobe</code> depois de gerado, comparar com a janela alvo, e aplicar o filtro <code>atempo</code> do <code>ffmpeg</code> pra comprimir no pós-processo se passou de 2% de tolerância, com teto de 1.20×. A combinação do <code>speed</code> nativo (1.15×) com o <code>atempo</code> (1.20×) dá uma compressão efetiva de até 1.38×, suficiente pra caber naturalmente mesmo nos casos mais extremos sem quebrar qualidade.</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="c1"># depois de gerar o chunk</span>
</span></span><span class="line"><span class="cl"><span class="n">actual</span> <span class="o">=</span> <span class="n">ffprobe_duration</span><span class="p">(</span><span class="n">chunk_path</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">ratio</span> <span class="o">=</span> <span class="n">actual</span> <span class="o">/</span> <span class="n">target_sec</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="n">ratio</span> <span class="o">&gt;</span> <span class="n">FIT_TOLERANCE</span><span class="p">:</span>       <span class="c1"># 1.02</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># comprime o áudio sem mexer no pitch</span>
</span></span><span class="line"><span class="cl">    <span class="n">ffmpeg_atempo</span> <span class="o">=</span> <span class="nb">min</span><span class="p">(</span><span class="n">ratio</span><span class="p">,</span> <span class="n">FIT_MAX_ATEMPO</span><span class="p">)</span>   <span class="c1"># 1.20</span>
</span></span><span class="line"><span class="cl">    <span class="n">apply_atempo</span><span class="p">(</span><span class="n">chunk_path</span><span class="p">,</span> <span class="n">ffmpeg_atempo</span><span class="p">)</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Quando um chunk gerado fica mais curto que a janela alvo, o pipeline estende o áudio levemente (sem afetar pitch) pra reduzir o silêncio feio depois que ele termina. O objetivo não é preencher a janela inteira, uma pausa natural entre frases é boa, mas suavizar os casos mais gritantes de silêncio que dariam a impressão de &ldquo;áudio cortado&rdquo;.</p>
<p>Num episódio grande, 95 minutos, 194 chunks, o desvio cumulativo total ficou em -0,7%. Uns 43 segundos de drift ao longo do episódio inteiro, imperceptível enquanto você assiste.</p>
<p>O que salvou o orçamento nessa arquitetura é que cada chunk fica salvo individualmente no disco como um <code>.mp3</code> separado. A pipeline mantém um manifesto com o texto normalizado de cada chunk, e antes de chamar a API da ElevenLabs, compara o texto atual com o cache. Se o texto não mudou, reutiliza o áudio já gerado sem gastar crédito nenhum. Se eu reescrevo uma cue problemática, só os chunks afetados por aquela cue são regenerados — o resto do episódio fica intacto.</p>
<p>Isso é o que viabilizou as iterações. Eu rodava o batch, escutava trechos, identificava um problema (tradução longa demais, snippet de código que o TTS não conseguia pronunciar, chunking que cortou num ponto ruim), corrigia o SRT ou ajustava os parâmetros do chunker, e rodava de novo. Cada re-run consumia uma fração dos créditos e do tempo do run original, porque só os chunks alterados eram regenerados. Sem esse cache, cada iteração teria custado quase o mesmo que a primeira rodada, e o custo total do batch teria sido duas ou três vezes maior.</p>
<p>Essa mudança de chunking também teve impacto pesado no cache e no custo, mas deixo essa parte pro trecho mais abaixo onde explico a sequência de retrabalho. Aqui basta dizer que, do ponto de vista técnico, era a mudança certa.</p>
<h3>Quando o <code>atempo</code> não salva: reescrevendo cue antes do TTS<span class="hx:absolute hx:-mt-20" id="quando-o-atempo-não-salva-reescrevendo-cue-antes-do-tts"></span>
    <a href="#quando-o-atempo-n%c3%a3o-salva-reescrevendo-cue-antes-do-tts" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Nem tudo é questão de alinhamento de janela. Tem um tipo de cue que quebra o pipeline inteiro, onde nem <code>speed</code> preventivo nem <code>atempo</code> no pós-processo resolvem, e eu só esbarrei nisso quando comecei a rodar o batch nos episódios mais técnicos do canal.</p>
<p>O problema é o seguinte. O v3 lê a uns 16 caracteres por segundo quando você entrega texto normal de conversa. Mas quando você entrega uma cue recheada de URL literal, hash hexadecimal, string binária, uma lista de números ordenados ou um bloco de código shell, o modelo entra em modo &ldquo;soletrar letra por letra&rdquo; e despenca pra uns 9 caracteres por segundo. Uma cue de 500 caracteres que devia virar 30 segundos de áudio vira 55. A checagem de sanidade derruba (porque passou de 1.8×), o retry automático tenta de novo com os mesmos 500 caracteres, os cinco retries falham seguidos, e o chunk fica preso.</p>
<p>E isso não aconteceu por acaso. Eu escrevia desse jeito porque sabia que esses mesmos roteiros depois virariam post no blog, então fazia sentido deixar a URL crua, o comando completo, o hash inteiro, o detalhe técnico todo mastigado pro leitor. Em português isso funcionava bem porque era exatamente o meu fluxo original: gravar o vídeo e depois publicar o texto correspondente no blog. O que eu nunca tinha preparado era a etapa seguinte, de transformar esse mesmo material em dublagem automatizada pra inglês. Eu só fui perceber esse conflito quando a pipeline começou a falhar de verdade.</p>
<p>Peguei isso primeiro no ep052, o guia de Ubuntu pra devs iniciantes, onde duas cues traziam URLs do <code>github.com</code>, URLs <code>hkp://keyserver.ubuntu.com</code> e um hash GPG de 40 caracteres. Tentar resolver no pós-processo seria perda de tempo. O teto de 1.20× do <code>atempo</code> nunca vai comprimir 55 segundos em 30, e mesmo se conseguisse ia sair num chipmunk ilegível. A saída é atacar antes, no texto.</p>
<p>A solução foi reescrever a cue dizendo o que o comando faz, em vez de mostrar o comando literal:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-diff" data-lang="diff"><span class="line"><span class="cl"><span class="gd">- Rode: apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 \
</span></span></span><span class="line"><span class="cl"><span class="gd">-   --recv-keys 0A6A3E7F79F93EF8AAB9E92BAEBB74C8B5A1E44D
</span></span></span><span class="line"><span class="cl"><span class="gi">+ Rode o comando completo que tá na descrição do vídeo pra importar
</span></span></span><span class="line"><span class="cl"><span class="gi">+ a chave de assinatura do repositório do keyserver do Ubuntu.
</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>A legenda em inglês no vídeo continua mostrando o comando inteiro na tela, então quem lê a legenda vê o que precisa digitar. O áudio dublado descreve a intenção em linguagem falada, que é o que o TTS entrega sem engasgar. A instrução pro ouvinte fica intacta, e o caractere-por-segundo volta pros 16 esperados, então o áudio gerado cabe na janela original sem precisar esticar nem comprimir nada no pós.</p>
<p>Saí caçando cue assim em todos os episódios do batch. Varri do ep052 ao ep146 procurando cues com densidade alta de caractere &ldquo;duro&rdquo; (dígitos, colchetes, operadores, URLs longas, binário, hex) e acabei reescrevendo umas 30 cues em 13 episódios:</p>
<ul>
<li>ep052 Ubuntu pra devs: URLs do GitHub e hash GPG de 40 caracteres</li>
<li>ep091 Hello World em C: exemplos binários</li>
<li>ep095 Memória 640kB: wrap de endereço, segment-offset</li>
<li>ep106 CodeMiner: endereço de e-mail soletrado</li>
<li>ep113 Compressão: strings de binário puro, 75% de caractere duro</li>
<li>ep115 SQL Server: lista longa de números ordenados</li>
<li>ep120 Internet: endereços IP com pontos</li>
<li>ep121 Sockets: dois blocos de código JavaScript</li>
<li>ep122 Proxies: headers User-Agent do Chrome</li>
<li>ep123 Rede segura: one-liners shell, flags do Docker</li>
<li>ep126 Gentoo: demo de <code>chroot</code> em C</li>
<li>ep136 Containers: URL de release do GitHub</li>
<li>ep144 Criptografia: endereço de signing key</li>
</ul>
<p>O padrão foi sempre o mesmo: mantém a legenda exibindo o código, a URL ou o hash na íntegra, e reescreve o texto pro narrador descrever o que aquilo faz. Eu podia ter automatizado a reescrita, deixando um LLM ler a cue e propor uma substituição no voo, só que achei mais seguro revisar tudo manualmente. É exatamente o tipo de coisa onde o modelo resolve &ldquo;melhorar&rdquo; um comando pra deixá-lo mais limpo e sai com shell errado. A varredura manual levou umas duas horas. A automática teria custado o mesmo tempo em revisão, com o bônus da ansiedade de ter deixado passar algum comando mutilado.</p>
<p>Teve também um caso estrutural esquisito no ep146, sobre Docker Compose. Duas cues ficaram grudadas por causa de uma linha em branco faltando no SRT, e o <code>pysrt</code> tratava as duas como uma única cue gigante. O TTS nem chegava a processar, o chunker engasgava antes. Corrigi à mão adicionando a linha em branco que faltava. Fix de um caractere, meia hora pra rastrear.</p>
<h3>Reconstruindo SRTs truncadas<span class="hx:absolute hx:-mt-20" id="reconstruindo-srts-truncadas"></span>
    <a href="#reconstruindo-srts-truncadas" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Outra armadilha apareceu quando fui rodar o batch completo: três episódios tinham legendas em inglês curtas demais em relação à duração real do vídeo. O ep056, sobre Rails, era o caso mais absurdo. Dezenove minutos de legenda pra um vídeo de 80 minutos. Sessenta e dois minutos de conteúdo sem legenda nenhuma. O ep057 (WSL 2) tinha 14 minutos faltando, e o ep068 (Git Direito) tinha 2.</p>
<p>Isso é rastro do meu fluxo de tradução antigo. Em algum momento eu comecei a revisar a legenda manualmente, parei no meio, e o arquivo <code>.en.srt</code> ficou salvo truncado no ponto onde eu parei. Não dá pra dublar um vídeo de 80 minutos com legenda de 19. O chunker gera áudio até onde consegue ler e simplesmente não sabe o que fazer com o resto do vídeo.</p>
<p>A solução virou um script novo. Ele faz diff entre o <code>.en.srt</code> truncado e o <code>.pt-orig.srt</code> (o auto-caption bruto do YouTube, que sempre cobre o vídeo inteiro), pega as cues que existem só em português, manda pro Claude Sonnet 4.6 com um schema JSON rígido pedindo tradução cue a cue, e cola o resultado de volta no fim do arquivo em inglês. O schema é o detalhe que importa. Quando eu tentei pedir a resposta em texto no formato <code>N|texto</code>, o Claude deixava cair umas 20% das cues no caminho. Com um schema JSON estrito, a taxa de drop caiu pra zero nos três reparos.</p>
<p>O resultado em números:</p>
<ul>
<li>ep068 Git Direito: +50 cues / 2 minutos reconstruídos</li>
<li>ep057 WSL 2: +368 cues / 14 minutos</li>
<li>ep056 Rails: +1445 cues / 62 minutos (e ainda drop de 5 cues falsas que o tradutor antigo tinha inventado no fim pra tampar a saída)</li>
</ul>
<p>Depois dos reparos, os três episódios entraram no batch normalmente e foram dublados como qualquer outro. Tipo de ferramenta que você espera nunca precisar, só que quando precisa, compensa escrever uma vez e resolver o problema inteiro de uma vez.</p>
<h3>Tags de emoção automáticas<span class="hx:absolute hx:-mt-20" id="tags-de-emoção-automáticas"></span>
    <a href="#tags-de-emo%c3%a7%c3%a3o-autom%c3%a1ticas" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Enquanto eu tava testando, resolvi adicionar mais um passo no pipeline: um &ldquo;emotion tagger&rdquo; que lê o SRT em inglês antes de mandar pro TTS e insere tags tipo <code>[sarcastic]</code>, <code>[thoughtful]</code>, <code>[emphatic]</code>, <code>[deadpan]</code> em pontos onde um narrador humano naturalmente mudaria o tom. A ideia é imitar o que um ator profissional faria, colocar ênfase nos momentos certos, sem transformar o vídeo num teatro de emoções exageradas.</p>
<p>Essa parte é delicada por dois motivos. O primeiro: deixar um LLM solto editando o SRT tem risco real de ele &ldquo;melhorar&rdquo; o texto (trocar uma palavra aqui, reescrever uma frase ali) e você acabar com uma dublagem que não bate com a legenda. O segundo: LLM adora exagerar. Se você pedir pra marcar emoção, ele vai colocar tag em toda segunda frase. Pra evitar os dois, eu fixei uma lista pequena de tags permitidas e rodo uma validação de round-trip depois que a resposta do Claude chega:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="n">ALLOWED_TAGS</span><span class="p">:</span> <span class="nb">frozenset</span><span class="p">[</span><span class="nb">str</span><span class="p">]</span> <span class="o">=</span> <span class="nb">frozenset</span><span class="p">({</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;[sarcastic]&#34;</span><span class="p">,</span> <span class="s2">&#34;[thoughtful]&#34;</span><span class="p">,</span> <span class="s2">&#34;[emphatic]&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;[deadpan]&#34;</span><span class="p">,</span> <span class="s2">&#34;[serious]&#34;</span><span class="p">,</span> <span class="s2">&#34;[amused]&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;[sighs]&#34;</span><span class="p">,</span> <span class="s2">&#34;[exasperated]&#34;</span><span class="p">,</span> <span class="s2">&#34;[confident]&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;[matter-of-fact]&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"><span class="p">})</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">validate_tagged</span><span class="p">(</span><span class="n">original</span><span class="p">,</span> <span class="n">tagged</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;&#34;&#34;Garante que o SRT com tags preserva o original sem drift.&#34;&#34;&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">original</span><span class="p">)</span> <span class="o">!=</span> <span class="nb">len</span><span class="p">(</span><span class="n">tagged</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="k">raise</span> <span class="n">TagValidationError</span><span class="p">(</span><span class="s2">&#34;cue count mismatch&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">for</span> <span class="n">o</span><span class="p">,</span> <span class="n">t</span> <span class="ow">in</span> <span class="nb">zip</span><span class="p">(</span><span class="n">original</span><span class="p">,</span> <span class="n">tagged</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="c1"># O index e o timestamp têm que bater byte a byte.</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="n">o</span><span class="o">.</span><span class="n">index</span> <span class="o">!=</span> <span class="n">t</span><span class="o">.</span><span class="n">index</span> <span class="ow">or</span> <span class="n">o</span><span class="o">.</span><span class="n">timestamp</span> <span class="o">!=</span> <span class="n">t</span><span class="o">.</span><span class="n">timestamp</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">            <span class="k">raise</span> <span class="n">TagValidationError</span><span class="p">(</span><span class="sa">f</span><span class="s2">&#34;cue </span><span class="si">{</span><span class="n">o</span><span class="o">.</span><span class="n">index</span><span class="si">}</span><span class="s2">: header drift&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="c1"># Qualquer tag fora da whitelist invalida tudo.</span>
</span></span><span class="line"><span class="cl">        <span class="n">bad</span> <span class="o">=</span> <span class="n">find_disallowed_tags</span><span class="p">(</span><span class="n">t</span><span class="o">.</span><span class="n">text</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="n">bad</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">            <span class="k">raise</span> <span class="n">TagValidationError</span><span class="p">(</span><span class="sa">f</span><span class="s2">&#34;cue </span><span class="si">{</span><span class="n">o</span><span class="o">.</span><span class="n">index</span><span class="si">}</span><span class="s2">: </span><span class="si">{</span><span class="n">bad</span><span class="si">}</span><span class="s2">&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="c1"># O texto sem as tags tem que ser idêntico ao original.</span>
</span></span><span class="line"><span class="cl">        <span class="c1"># Se o LLM trocou uma palavra, reescreveu algo, a</span>
</span></span><span class="line"><span class="cl">        <span class="c1"># validação quebra e a resposta é rejeitada.</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="n">strip_tags</span><span class="p">(</span><span class="n">o</span><span class="o">.</span><span class="n">text</span><span class="p">)</span> <span class="o">!=</span> <span class="n">strip_tags</span><span class="p">(</span><span class="n">t</span><span class="o">.</span><span class="n">text</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">            <span class="k">raise</span> <span class="n">TagValidationError</span><span class="p">(</span><span class="sa">f</span><span class="s2">&#34;cue </span><span class="si">{</span><span class="n">o</span><span class="o">.</span><span class="n">index</span><span class="si">}</span><span class="s2">: text drift&#34;</span><span class="p">)</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Quando a validação quebra, a pipeline ignora a resposta e reemite o request (ou, se falhar muito, sobe o SRT sem tags mesmo). Sobre a quantidade de tags, o prompt que vai pro Claude diz explicitamente: mira em <code>N/10</code> tags pra um SRT de N legendas, chão de 1 tag a cada 12, teto de 1 tag a cada 7, distribuição equilibrada entre os quatro quartos do episódio, nunca duas tags adjacentes a menos de 4 legendas uma da outra. Esse conjunto de regras foi a sexta iteração — as cinco anteriores ou enchiam tudo de tag ou concentravam no começo do vídeo.</p>
<h3>Não deixe a v3 alucinar<span class="hx:absolute hx:-mt-20" id="não-deixe-a-v3-alucinar"></span>
    <a href="#n%c3%a3o-deixe-a-v3-alucinar" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Um detalhe crítico que só aparece em produção: a ElevenLabs v3 tende a alucinar quando você manda blocos de texto muito longos. Já peguei blocos onde o texto tinha uns 1.500 caracteres e o modelo gerou um áudio com <strong>nove minutos</strong> quando o esperado era um minuto e meio. O modelo simplesmente resolve continuar falando sozinho, inventando conteúdo.</p>
<p>A documentação oficial da ElevenLabs recomenda manter cada chamada abaixo de 800 caracteres pra evitar isso. Eu fui mais conservador e cortei em 700, sempre em fim de frase (nunca no meio de uma sentença, porque o corte no meio soa horrível quando concatena). Depois disso, subi o parâmetro <code>stability</code> pra 0.9 (modo Robust, mais estável porém menos responsivo a tags de emoção), liguei o <code>apply_text_normalization</code> pra o modelo pronunciar números e siglas direito, e adicionei uma verificação de sanidade que rejeita qualquer áudio gerado que passe de 1.8× da duração esperada.</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="n">DEFAULT_VOICE_SETTINGS</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;stability&#34;</span><span class="p">:</span> <span class="mf">0.9</span><span class="p">,</span>          <span class="c1"># modo Robust</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;similarity_boost&#34;</span><span class="p">:</span> <span class="mf">0.95</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;style&#34;</span><span class="p">:</span> <span class="mf">0.0</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;use_speaker_boost&#34;</span><span class="p">:</span> <span class="kc">True</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">EXPECTED_CHARS_PER_SEC</span> <span class="o">=</span> <span class="mf">16.0</span>
</span></span><span class="line"><span class="cl"><span class="n">CHUNK_SANITY_MAX_FACTOR</span> <span class="o">=</span> <span class="mf">1.8</span>   <span class="c1"># rejeita se actual &gt; 1.8× esperado</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Depois dessas mitigações, um episódio de 95 minutos (194 blocos) rodou do começo ao fim sem nenhuma alucinação. Exatamente um bloco falhou por erro 502 transitório da API, que o retry automático pegou na tentativa seguinte.</p>
<h3>Masterização pro YouTube<span class="hx:absolute hx:-mt-20" id="masterização-pro-youtube"></span>
    <a href="#masteriza%c3%a7%c3%a3o-pro-youtube" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>O YouTube não publica número oficial, mas o consenso da indústria é que a normalização de volume dele na hora da reprodução mira em -14 LUFS. Se você entregar áudio mais alto que isso, o YouTube baixa; se entregar mais baixo, ele deixa como tá. Pra bater o alvo exato, o pipeline roda o filtro <code>loudnorm</code> do <code>ffmpeg</code> em duas passadas. A primeira mede o áudio inteiro e imprime as estatísticas (<code>input_i</code>, <code>input_tp</code>, <code>input_lra</code>, <code>input_thresh</code>) em formato JSON no stderr. A segunda passada lê essas estatísticas de volta e aplica um ganho estático linear pra pousar exato em -14 LUFS, -1.5 dBTP:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># Passada 1: medir</span>
</span></span><span class="line"><span class="cl">ffmpeg -i final_en.mp3 <span class="se">\
</span></span></span><span class="line"><span class="cl">  -af <span class="s2">&#34;highpass=f=80,loudnorm=I=-14:TP=-1.5:LRA=9:print_format=json&#34;</span> <span class="se">\
</span></span></span><span class="line"><span class="cl">  -f null -
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Passada 2: aplicar ganho linear com os valores medidos</span>
</span></span><span class="line"><span class="cl">ffmpeg -i final_en.mp3 <span class="se">\
</span></span></span><span class="line"><span class="cl">  -af <span class="s2">&#34;highpass=f=80,loudnorm=I=-14:TP=-1.5:LRA=9\
</span></span></span><span class="line"><span class="cl"><span class="s2">:measured_I=-18.3:measured_TP=-3.1:measured_LRA=5.4\
</span></span></span><span class="line"><span class="cl"><span class="s2">:measured_thresh=-28.7:offset=1.2:linear=true&#34;</span> <span class="se">\
</span></span></span><span class="line"><span class="cl">  -ar <span class="m">48000</span> -ac <span class="m">2</span> -c:a pcm_s16le final_en_mastered.wav</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Duas passadas em vez de uma porque o modo de passada única roda em compressão dinâmica e acaba &ldquo;bombeando&rdquo; o ganho em trechos com muito silêncio. Com <code>linear=true</code> na passada 2, o ganho fica estático em cima das medições da passada 1, então não tem bombeamento. O resultado pousa dentro de ±0.1 LU do alvo, que é praticamente inaudível. O filtro <code>highpass=f=80</code> na frente derruba rumble de ar-condicionado e hum de rede elétrica abaixo de 80 Hz, que o ouvido humano não escuta mas mexe nas medições de pico.</p>
<h3>O ponto em que eu achei que estava pronto. E não estava.<span class="hx:absolute hx:-mt-20" id="o-ponto-em-que-eu-achei-que-estava-pronto-e-não-estava"></span>
    <a href="#o-ponto-em-que-eu-achei-que-estava-pronto-e-n%c3%a3o-estava" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Aqui entra a parte mais chata, e mais educativa, do projeto.</p>
<p>Eu realmente achei que tinha acertado a mão na primeira grande rodada. A pipeline fechou os 146 episódios, eu subi tudo, e parecia resolvido. A duração batia, os arquivos estavam todos lá, o processo inteiro tinha finalmente atravessado o lote completo de quase 96 horas de vídeo. Só que foi justamente depois do upload que comecei a perceber problemas que eu simplesmente não tinha antecipado.</p>
<p>O primeiro susto foi a situação dos jingles. Eu já sabia que muitos episódios do Akitando começam com aquele jingle instrumental curto antes de eu começar a falar, mas subestimei o problema. O <code>.srt</code> do YouTube praticamente não ajuda nisso, porque jingle não é fala. Às vezes ele marca como <code>[Música]</code>, às vezes não marca nada útil, às vezes a janela fica torta. Na primeira montagem, teve episódio onde o jingle sumiu, episódio onde ele entrou duplicado, e episódio onde o source-fill do áudio original já trazia o jingle certo e o meu splice vinha por cima, empurrando todo o resto pra frente. Foi o tipo de bug que só aparece quando você para pra escutar a master final de verdade, não quando olha só pra duração total.</p>
<p>A solução foi abandonar a esperança de que a legenda me diria onde estava o jingle e ir direto pra fonte. Montei um detector usando os próprios áudios originais dos vídeos e jingles de referência, com auditoria e repair pass em cima dos masters já gerados. Isso acabou virando um mini-sistema próprio: detector, auditoria, reassemble por cache, correção por janela exata. No fim dessa novela, o batch de episódios alterados caiu num conjunto de reupload bem definido e o problema dos jingles ficou finalmente sob controle. Foi um daqueles casos clássicos de produção: a primeira solução parecia suficiente até bater no acervo inteiro.</p>
<p>Só que aí veio a segunda pancada, e essa foi mais cara: eu tinha esquecido de um detalhe fundamental sobre legendas de YouTube. SRT é feita pra ser lida na tela, não pra virar roteiro de fala. Então uma ideia que no meu texto original era um parágrafo inteiro aparecia quebrada em várias cues pequenas, cada uma com sua microjanela de tempo. Na primeira versão da pipeline eu estava usando essas cues como unidade de geração na ElevenLabs. Resultado: o TTS gerava áudio em blocos coerentes localmente, mas a montagem introduzia silêncios artificiais no meio da frase. Tecnicamente alinhado, humanamente esquisito.</p>
<p>O conserto foi doloroso porque exigiu voltar várias casas. Em vez de respeitar a estrutura das cues do SRT, precisei voltar pros meus textos originais e reconstruir o chunking com base nos parágrafos que eu realmente li quando gravei os vídeos. Quase todo episódio do Akitando tem o post correspondente com a seção <code>## Script</code>, e isso salvou o projeto. Como eu fui meticuloso na época em que produzi os vídeos — escrevendo os roteiros antes, lendo exatamente aquilo na gravação e arquivando tanto os vídeos quanto os scripts — eu tinha a fonte de verdade. Então deu pra mapear os parágrafos do script aos timings do <code>.pt-BR.srt</code>, reagrupar o <code>.en.srt</code> em cima disso e regenerar os chunks no formato certo.</p>
<p>O problema é que essa mudança destrói o cache anterior. Mesmo quando o texto quase não muda, se a fronteira do chunk muda, a chamada ao TTS muda junto: muda contexto, muda prosódia, muda começo, muda fim. Então não era um retoquezinho. Era uma regeneração completa de tudo de novo. Foi aí que o custo começou a sair do meu controle inicial.</p>
<p>E quando eu achei que agora sim tinha terminado, veio a terceira paulada: os clipes externos. Muitos vídeos do canal têm trechos inseridos de outras fontes. No episódio de Ruby on Rails, por exemplo, tem 37signals, Apple, comerciais, pedaços externos no meio da narrativa. O <code>.srt</code> do YouTube não sabia lidar direito com isso. Em alguns pontos ele simplesmente alucinava texto, em outros os timings ficavam completamente tortos, em outros a fala original não tinha nada a ver com a legenda que eu estava usando de base. De novo: olhando só pra automação parecia tudo bem; escutando a master final, não estava.</p>
<p>A correção, mais uma vez, foi sair da legenda e voltar pro material original. Montei um detector de janelas de clipe externo a partir de gaps de alinhamento entre transcript, <code>.pt-BR.srt</code>, cues sem fala útil e chunks que atravessavam essas janelas. Depois, em vez de tentar dublar o que não fazia sentido dublar, o processo passou a recuperar o áudio original do vídeo exatamente nesses trechos e preencher a master com o source correto. Foi isso que consertou os casos de 37signals, Apple Education, comercial de Mac vs PC e outros inserts parecidos que eu tinha esquecido que existiam.</p>
<p>Esse foi o aprendizado mais importante dessa segunda metade do projeto: automação te leva muito longe, mas o acervo completo sempre tem mais cantos escuros do que você imagina no primeiro passe.</p>
<h2>Os números do batch de dublagem (e por que doeu mais do que eu imaginei)<span class="hx:absolute hx:-mt-20" id="os-números-do-batch-de-dublagem-e-por-que-doeu-mais-do-que-eu-imaginei"></span>
    <a href="#os-n%c3%bameros-do-batch-de-dublagem-e-por-que-doeu-mais-do-que-eu-imaginei" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Eu preciso me corrigir de novo. Não errei só a primeira conta. Errei também o grau de confiança que eu tinha depois da primeira rodada.</p>
<p>Quando subi pro plano Business da ElevenLabs, eu achei que finalmente estaria com folga. Na prática, não fiquei. O screenshot mais recente do dashboard mostra <strong>10.046.279 créditos consumidos de um limite de 11.000.000</strong>, sobrando <strong>953.721</strong>:</p>
<p><img src="elevenlabs-credits-dashboard.png" alt="Dashboard da ElevenLabs mostrando 10.046.279 créditos consumidos de 11.000.000"  loading="lazy" /></p>
<p>Em português claro: eu torrei mais de 91% do limite do workspace Business. E isso ajuda a entender o que &ldquo;10 milhões de créditos&rdquo; realmente significa nesse contexto. Não é só &ldquo;renderizei 146 episódios uma vez&rdquo;. É renderizar 146 episódios, descobrir um problema estrutural, voltar, corrigir, regenerar chunks, descobrir outro problema estrutural, voltar, reconstruir masters, auditar jingles, reparar janelas de clipes externos, reupar de novo. Cada rodada a mais custa. E quando a mudança invalida o cache, custa muito.</p>
<p>O custo também não para na ElevenLabs. Todo o trabalho de curadoria das legendas, reescrita de cues problemáticas, reconstrução de SRTs truncadas, auditorias de jingle, detector de clipes externos e o resto rodou via Claude Code no plano Claude Max 20×. Somando isso com a tradução em massa do blog pra inglês que eu descrevi no <a href="/2026/04/09/20-anos-de-blog-o-ano-em-que-a-ia-finalmente-me-deixou-traduzir-tudo/">artigo de aniversário</a>, o limite semanal do Claude Max 20× bateu 100%, com gasto extra acima de R$ 300. Pra quem acha que IA generativa vira grátis depois que você assinou o plano, os boletos discordam.</p>
<p>Mesmo assim, continuo achando que vale a pena. Estamos falando de 146 episódios, quase 96 horas de conteúdo técnico, mais de 5 anos de acervo do canal. Fazer isso manualmente em estúdio, com ator, direção, pickup e revisão humana em tudo, custaria uma ordem de grandeza completamente diferente.</p>
<p>O que mudou foi minha visão do projeto. No começo eu tava olhando pra isso como &ldquo;um batch grande&rdquo;. No fim, era outra coisa. Era mexer num acervo inteiro, achar exceção escondida, consertar ferrugem antiga, refazer master, rerender, reouvir.</p>
<p>O primeiro batch completo levou pouco menos de dois dias e me fez pensar que eu tinha resolvido tudo. Não tinha. A segunda rodada, com chunking por parágrafo, já foi cara. A terceira, com os reparos de jingle e os consertos de masters, mais cara ainda. E a quarta pancada veio dos clipes externos que eu não tinha modelado direito na primeira automação.</p>
<p>Mas agora, finalmente, eu acho que acabou. Ou melhor: eu espero que tenha acabado. Tudo foi enviado de novo, pela última vez. Não está perfeito no sentido absoluto da palavra. Está tão bom quanto eu consigo deixar com script, auditoria e automação, sem entrar num inferno de intervenção manual vídeo por vídeo.</p>
<p>E se isso tudo foi possível, é porque anos atrás eu tive a disciplina de produzir os vídeos do jeito certo: escrever primeiro, ler o roteiro quando gravava, e arquivar os materiais depois. Sem os vídeos originais e sem os scripts originais, eu não teria como descobrir onde o <code>.srt</code> mentia, onde o timing estava torto, onde o clipe externo entrava, onde o parágrafo original começava e terminava. Essa organização antiga foi o que permitiu consertar o projeto agora.</p>
<p>Então sim: a dublagem dos 146 episódios está pronta. Não perfeita. Mas fechada. Pelo menos, eu sinceramente espero que agora sim esteja.</p>
<h2>O primeiro teste, assista<span class="hx:absolute hx:-mt-20" id="o-primeiro-teste-assista"></span>
    <a href="#o-primeiro-teste-assista" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Fiz um vídeo teste pra provar que funciona. Dois avisos antes: primeiro, o embed abaixo já carrega com a legenda em inglês ligada por padrão, então esse lado dá pra resolver via URL. Segundo, o áudio é chato: o YouTube removeu o seletor de faixa de áudio dos players embedados lá por março desse ano. Então dentro do embed você não vai encontrar essa opção no menu de engrenagem mesmo, só roda em português. Pra ouvir a dublagem em inglês de verdade, clica em <strong><a href="https://www.youtube.com/watch?v=QNLd8TZ_JQc"target="_blank" rel="noopener">assistir direto no YouTube</a></strong>, que a página principal do YouTube ainda tem o seletor de faixa de áudio no menu de engrenagem.</p>
<div class="embed-container">
  <iframe
    src="https://www.youtube.com/embed/QNLd8TZ_JQc?cc_load_policy=1&amp;cc_lang_pref=en&amp;hl=en"
    title="Akitando dublado em inglês (teste)"
    frameborder="0"
    allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
    referrerpolicy="strict-origin-when-cross-origin"
    allowfullscreen>
  </iframe>
</div>
<p>Se você tá acostumado com a dublagem automática do YouTube ou com as dublagens de IA de TikTok, compara. A diferença é gritante. A voz é a minha, com sotaque americano trabalhado, e a sincronização com o vídeo original fica dentro de 1 a 2% de desvio cumulativo, praticamente imperceptível em 95 minutos de vídeo.</p>
<h2>E o que faltava: traduzir as thumbnails<span class="hx:absolute hx:-mt-20" id="e-o-que-faltava-traduzir-as-thumbnails"></span>
    <a href="#e-o-que-faltava-traduzir-as-thumbnails" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Quando eu estava fechando esse post, me caiu a ficha de que tinha deixado uma ponta solta. As 146 thumbnails do meu canal estão em português, muitas com título em caixa alta grande tipo &ldquo;9 DICAS PARA PALESTRANTES&rdquo; ou &ldquo;7 RECOMENDAÇÕES DE SHOWS PARA PESSOAS DE TECH&rdquo;. De nada adianta áudio em inglês perfeito se a imagem que aparece na busca é um bloco de texto ilegível pra quem não lê português. O YouTube permite subir uma thumbnail alternativa por idioma, então eu precisava gerar versões em inglês, mantendo o resto da arte idêntico, trocando só o texto.</p>
<p>A ferramenta usa duas peças. <strong><code>yt-dlp</code></strong> é o fork moderno do antigo <code>youtube-dl</code>, uma CLI em Python que baixa qualquer coisa do YouTube (vídeos, áudios, legendas, thumbnails) sem precisar de API key. <strong>Nano Banana Pro</strong> (<code>nano-banana-pro-preview</code> na API) é o modelo de edição de imagem mais recente do Google Gemini — aceita uma imagem de entrada com um prompt e devolve a imagem editada, preservando o resto da composição quando você pede pra mexer só numa parte.</p>
<p>O pipeline tem dois passos. Passo 1: <code>yt-dlp</code> pega a thumbnail em <code>.jpg</code>, sem baixar o vídeo:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">yt-dlp --skip-download <span class="se">\
</span></span></span><span class="line"><span class="cl">       --write-thumbnail <span class="se">\
</span></span></span><span class="line"><span class="cl">       --convert-thumbnails jpg <span class="se">\
</span></span></span><span class="line"><span class="cl">       -o <span class="s2">&#34;thumbnails/originals/&lt;slug&gt;/&lt;video_id&gt;&#34;</span> <span class="se">\
</span></span></span><span class="line"><span class="cl">       <span class="s2">&#34;https://www.youtube.com/watch?v=&lt;video_id&gt;&#34;</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Passo 2: mandar cada imagem pro Gemini Nano Banana Pro com um prompt que é um contrato rígido de requisitos duros, não só um &ldquo;traduz isso&rdquo;:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">TASK:
</span></span><span class="line"><span class="cl">Detect every piece of Portuguese text visible on this image and
</span></span><span class="line"><span class="cl">translate it to clear, natural American English. Replace the
</span></span><span class="line"><span class="cl">Portuguese text with the English translation in the same visual
</span></span><span class="line"><span class="cl">position.
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">STRICT REQUIREMENTS — follow every one:
</span></span><span class="line"><span class="cl">- Preserve EVERY non-text element identically: face, pose,
</span></span><span class="line"><span class="cl">  expression, background, color palette, lighting, icons, logos,
</span></span><span class="line"><span class="cl">  decorative shapes, borders, layout. Only the text changes.
</span></span><span class="line"><span class="cl">- The English translation must be IDIOMATIC and CONFIDENT — not
</span></span><span class="line"><span class="cl">  a literal word-for-word rewrite. It&#39;s a YouTube thumbnail for a
</span></span><span class="line"><span class="cl">  tech audience, so use punchy phrasing a native English-speaking
</span></span><span class="line"><span class="cl">  tech YouTuber would write.
</span></span><span class="line"><span class="cl">- Match the original text&#39;s font family, weight, size, color,
</span></span><span class="line"><span class="cl">  stroke outline, drop shadow, and any decorative treatment.
</span></span><span class="line"><span class="cl">- If there is no Portuguese text at all on the image, return the
</span></span><span class="line"><span class="cl">  original image unchanged.</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>A cláusula &ldquo;return the original image unchanged&rdquo; é o que salva os primeiros episódios do canal, que não têm texto na thumbnail, só minha cara. Sem ela, o modelo invariavelmente ia &ldquo;melhorar&rdquo; a composição, trocar a iluminação, etc.</p>
<p>Olha o resultado em dois exemplos (episódios 10 e 11):</p>
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 12px; margin: 16px 0;">
<div><strong>Original (PT)</strong><br><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/20260409_thumb_ep010_pt.jpg" alt="Thumbnail original em português: 7 Recomendações de Shows para pessoas de Tech" style="width: 100%;"></div>
<div><strong>Traduzida (EN)</strong><br><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/20260409_thumb_ep010_en.jpg" alt="Thumbnail traduzida em inglês: 7 TV Shows You Must Watch If You're In Tech" style="width: 100%;"></div>
</div>
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 12px; margin: 16px 0;">
<div><strong>Original (PT)</strong><br><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/20260409_thumb_ep011_pt.jpg" alt="Thumbnail original em português: 9 Dicas para Palestrantes: venda sua caneta" style="width: 100%;"></div>
<div><strong>Traduzida (EN)</strong><br><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/20260409_thumb_ep011_en.jpg" alt="Thumbnail traduzida em inglês: 9 Tips for Speakers: sell your pen" style="width: 100%;"></div>
</div>
<p>Repara no detalhe do episódio 10. O modelo não traduziu &ldquo;7 Recomendações de Shows para Pessoas de Tech&rdquo; literal pra &ldquo;7 Show Recommendations for Tech People&rdquo;, que seria a versão tradutor automático. Ele reformulou pra &ldquo;7 TV Shows You Must Watch If You&rsquo;re In Tech&rdquo;, como um tech YouTuber americano de verdade escreveria. O resto da imagem fica idêntico pixel a pixel. No episódio 11, &ldquo;9 DICAS PARA PALESTRANTES: venda sua caneta&rdquo; vira &ldquo;9 TIPS FOR SPEAKERS: sell your pen&rdquo;, com fonte, caixa alta e posição preservadas. Se você não soubesse que o original era em português, não dava pra adivinhar que é uma edição automática feita por IA.</p>
<p>Custo do lado Gemini: alguns centavos por imagem, poucos dólares totais pro batch inteiro. Trivial perto dos $1.500+ do dub. Com a thumbnail resolvida, a conversão do canal pra inglês fica fechada — áudio clonado pela ElevenLabs v3 em cima das legendas curadas em <code>.srt</code>, e a imagem editada pelo Nano Banana Pro pra o título bater com o resto.</p>
<h2>A conclusão, que é o título desse post<span class="hx:absolute hx:-mt-20" id="a-conclusão-que-é-o-título-desse-post"></span>
    <a href="#a-conclus%c3%a3o-que-%c3%a9-o-t%c3%adtulo-desse-post" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Quando o Qwen3 TTS saiu, o hype dizia que era &ldquo;ElevenLabs killer&rdquo;. Passei semanas tentando viver com essa premissa na prática, com conteúdo real no ar toda semana. E o que eu descobri é que open source de TTS ainda tá muito longe da ElevenLabs. A diferença é grande, e ela aparece justamente quando você sai do tweet de demo de 5 segundos e coloca o modelo pra rodar um podcast semanal de 30 minutos de verdade.</p>
<p>Na prática, o Qwen3 nem bate o modelo v2 da ElevenLabs. O v3, que é o atual, fica um degrau acima do v2 ainda. A prosódia sai melhor, as tags de emoção no meio do texto funcionam em português e em inglês sem esforço, e a API fica de pé sem você precisar manter servidor nenhum. O custo por caractere é um pouco mais alto, mas pro meu volume atual ele fica confortavelmente dentro do orçamento e me devolve horas que eu gastava cuidando de GPU.</p>
<p>A lição aqui é a mesma que a galera de LLMs tá aprendendo devagar. Demo de tweet de 30 segundos é uma coisa, produção é outra coisa completamente diferente. Open source tem seu nicho, principalmente quando você tem dado sensível que não pode sair de casa, ou quando você tem muita GPU ociosa e pouco orçamento recorrente. Só que pra uso sério de TTS em produto comercial, aqui em abril de 2026, a ElevenLabs segue imbatível. O Qwen3 não matou ninguém.</p>
<p>E não esquece: assina o <a href="https://open.spotify.com/show/7MzG2UB7IAkC3GAwEXEIVD"target="_blank" rel="noopener">The M.Akita Chronicles no Spotify</a> pra não perder episódio novo desses, feito com a nova pipeline.</p>
]]></content:encoded><category>midia-generativa</category><category>modelos-locais</category><category>reviews</category></item><item><title>Testando LLMs Open Source e Comerciais - Quem Consegue Bater o Claude Opus?</title><link>https://www.akitaonrails.com/2026/04/05/testando-llms-open-source-e-comerciais-quem-consegue-bater-o-claude-opus/</link><guid isPermaLink="true">https://www.akitaonrails.com/2026/04/05/testando-llms-open-source-e-comerciais-quem-consegue-bater-o-claude-opus/</guid><pubDate>Sun, 05 Apr 2026 18:00:00 -0300</pubDate><description>&lt;blockquote&gt;
&lt;p&gt;⚠️ &lt;strong&gt;Artigo obsoleto (atualização de 2026-04-24).&lt;/strong&gt; As conclusões e rankings desse artigo foram superados depois que eu re-auditei o benchmark contra o código-fonte do gem &lt;code&gt;ruby_llm&lt;/code&gt; e reestruturei o critério de avaliação. Várias &amp;ldquo;hallucinations&amp;rdquo; que eu catalogava eram API válida. Kimi K2.6 e Gemini 3.1 Pro subiram pra Tier A. GLM 5.1 caiu pra Tier C. MiMo V2.5 Pro caiu de &amp;ldquo;primeiro Tier 1 não-Anthropic&amp;rdquo; pra Tier B. &lt;strong&gt;A versão canônica está em &lt;a href="https://www.akitaonrails.com/2026/04/24/llm-benchmarks-parte-3-deepseek-kimi-mimo/"&gt;Benchmark de LLMs pra Coding (Abril 2026)&lt;/a&gt;.&lt;/strong&gt; Esse artigo fica aqui como registro histórico do que eu concluí antes do re-audit.&lt;/p&gt;</description><content:encoded><![CDATA[<blockquote>
  <p>⚠️ <strong>Artigo obsoleto (atualização de 2026-04-24).</strong> As conclusões e rankings desse artigo foram superados depois que eu re-auditei o benchmark contra o código-fonte do gem <code>ruby_llm</code> e reestruturei o critério de avaliação. Várias &ldquo;hallucinations&rdquo; que eu catalogava eram API válida. Kimi K2.6 e Gemini 3.1 Pro subiram pra Tier A. GLM 5.1 caiu pra Tier C. MiMo V2.5 Pro caiu de &ldquo;primeiro Tier 1 não-Anthropic&rdquo; pra Tier B. <strong>A versão canônica está em <a href="/2026/04/24/llm-benchmarks-parte-3-deepseek-kimi-mimo/">Benchmark de LLMs pra Coding (Abril 2026)</a>.</strong> Esse artigo fica aqui como registro histórico do que eu concluí antes do re-audit.</p>

</blockquote>
<hr>
<p><strong>Update 16 de Abril, 2026:</strong> Adicionamos Claude Opus 4.7, Qwen 3.6 e GPT 5.4 via Codex CLI (xHigh reasoning). Opus 4.7 é melhoria incremental sobre 4.6 (28 testes vs 16, mesma API correta) e passa a ser o novo baseline. GPT 5.4, que estava no Tier 1 pelo meu aval pessoal, agora tem dados objetivos e caiu pro Tier 2 — gastou 7.6M tokens (~$16/run, 15x mais caro que Opus) e errou a convenção de chamada do <code>add_message</code> no multi-turn. Qwen 3.6 Plus continua Tier 3 com a mesma alucinação de API do 3.5. A conclusão se mantém: se quer segurança, Opus.</p>
<hr>
<p><strong>TL;DR:</strong> Se você não quer ler a análise inteira: os únicos modelos que geraram código que funciona de verdade no nosso benchmark foram Claude Opus 4.7, Claude Opus 4.6, GLM 5 e GLM 5.1 (da Z.AI, ~89% mais baratos que Opus). O Sonnet também funciona nesse benchmark, mas na prática falha em projetos que exijam raciocínio mais profundo — detalhes na conclusão. O GPT 5.4, que antes estava no Tier 1 pelo meu aval pessoal, agora tem dados objetivos via Codex CLI: gastou 7.6M tokens ($16/run) e errou a convenção de chamada do <code>add_message</code> — funciona na primeira mensagem, quebra no multi-turn. Caiu pro Tier 2. O resto — Kimi, DeepSeek, MiniMax, Qwen, Gemini, Grok 4.20 — inventou APIs que não existem ou ignorou o gem pedido.</p>
<p>Tem uma novidade nesse update: refiz a parte local do benchmark numa RTX 5090 (em vez do AMD Strix Halo) e adicionei um lote de modelos Qwen, incluindo um Qwen 3.5 27B distilado direto do Claude 4.6 Opus. Isso reabriu a conversa sobre rodar modelo open source localmente. A banda de memória da 5090 muda o jogo de &ldquo;inviável&rdquo; pra &ldquo;viável com 1-2 prompts de correção&rdquo;. E o gargalo dos modelos open source virou falta de conhecimento factual sobre bibliotecas, coisa que eu explico em detalhe na nova seção sobre a família Qwen. A aposta da distilação do Claude, aliás, deu um resultado bem frustrante que eu nunca tinha visto documentado nesses termos.</p>
<hr>
<p>Se você acompanhou meus <a href="/tags/vibe-coding/">artigos anteriores sobre vibe coding</a>, sabe que passei os últimos dois meses numa maratona de mais de 500 horas usando Claude Opus como coding agent principal. Os resultados foram bons, como reportei na <a href="https://akitaonrails.com/2026/03/05/37-dias-de-imers%c3%a3o-em-vibe-coding-conclus%c3%a3o-quanto-a-modelos-de-neg%c3%b3cio/"target="_blank" rel="noopener">conclusão sobre modelos de negócio</a>. Mas ficou uma coceira: será que eu estou preso num único modelo? Tem alternativa real ao Claude Opus pra uso diário em projetos de verdade?</p>
<p>Tenho uma RTX 5090 com 32 GB de GDDR7. Sei que posso rodar os modelos open source mais recentes. Comprei um <a href="https://akitaonrails.com/2026/03/31/review-minisforum-ms-s1-max-amd-ai-max-395/"target="_blank" rel="noopener">Minisforum MS-S1</a> com AMD Ryzen AI Max 395 e 128 GB de memória unificada, e montei um <a href="https://akitaonrails.com/2026/03/31/migrando-meu-home-server-com-claude-code/"target="_blank" rel="noopener">home server com Docker</a> pra servir modelos locais. A infraestrutura estava pronta. Faltava testar de verdade.</p>
<p>Construí um benchmark automatizado pra comparar modelos open source e comerciais em condições idênticas. 33 modelos configurados ao todo (25 da rodada original mais 8 adicionados na rerodada na NVIDIA), 27 executados, 16 completados de alguma forma. O código está no <a href="https://github.com/akitaonrails/llm-coding-benchmark"target="_blank" rel="noopener">GitHub</a>.</p>
<h2>O gargalo que ninguém explica: VRAM e KV Cache<span class="hx:absolute hx:-mt-20" id="o-gargalo-que-ninguém-explica-vram-e-kv-cache"></span>
    <a href="#o-gargalo-que-ningu%c3%a9m-explica-vram-e-kv-cache" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Antes de falar dos resultados, preciso explicar por que rodar modelos grandes localmente é muito mais difícil do que parece.</p>
<p>Pega o Qwen3 32B como exemplo. O modelo em FP16 (precisão total) ocupa ~64 GB. Quantizado em Q4 (4 bits), cai pra ~19 GB. Então cabe nos 32 GB da minha RTX 5090, certo? Errado. Esse é só o peso do modelo. Falta a parte que ninguém conta pra você: o <strong>KV Cache</strong>.</p>
<p>KV Cache é a memória que o modelo usa pra &ldquo;lembrar&rdquo; o que já leu. Cada vez que processa um token (uma palavra ou pedaço de palavra), ele calcula dois vetores — K (key) e V (value) — pra cada camada de atenção. Esses vetores ficam armazenados pra que não precise recalcular tudo quando gera o próximo token. Sem isso, a geração seria quadraticamente lenta.</p>
<p>O KV Cache escala linearmente com o tamanho do contexto. A fórmula:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>Memória KV = 2 × Camadas × Cabeças_KV × Dimensão_Cabeça × Bytes_por_Elemento × Tokens_no_Contexto</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Pra um modelo como o Llama 3.1 70B em BF16, isso dá ~0.31 MB por token. Parece pouco, até você perceber que um contexto de 128K tokens consome <strong>40 GB</strong> só de KV Cache. O modelo em si + KV Cache = muito mais VRAM do que a maioria das GPUs tem.</p>
<p>E pra uso real com coding agents, 128K tokens não é luxo — é necessidade. O agent precisa ler arquivos, manter histórico de conversação, receber output de comandos. Em sessões longas de benchmark, nossos modelos consumiram entre 39K e 156K tokens. Menos de 100K de contexto não é prático pra projetos do dia a dia.</p>
<p>O Google publicou o <a href="https://research.google/blog/turboquant-redefining-ai-efficiency-with-extreme-compression/"target="_blank" rel="noopener">TurboQuant</a> (ICLR 2026), que comprime o KV Cache pra 3 bits sem perda de acurácia — redução de 6x na memória e até 8x de speedup. Usa rotação aleatória de vetores (PolarQuant) seguida de um algoritmo de 1 bit nos resíduos. Funciona online durante inferência, comprimindo na escrita e descomprimindo na leitura. Ainda não está implementado nos runtimes que usamos (llama.cpp, Ollama), mas quando chegar vai mudar bastante a equação.</p>
<p>Pra quem quer se aprofundar nessa matemática de VRAM, recomendo <a href="https://x.com/TheAhmadOsman/status/2040103488714068245"target="_blank" rel="noopener">este link do Ahmad Osman</a> pro artigo &ldquo;GPU Memory Math for LLMs (2026 Edition)&rdquo;.</p>
<h2>O problema do hardware: memória não é toda igual<span class="hx:absolute hx:-mt-20" id="o-problema-do-hardware-memória-não-é-toda-igual"></span>
    <a href="#o-problema-do-hardware-mem%c3%b3ria-n%c3%a3o-%c3%a9-toda-igual" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>&ldquo;Mas eu tenho 128 GB de RAM!&rdquo; Legal, mas não é isso que importa. O que importa é largura de banda de memória, e a diferença entre tipos é absurda:</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/2026/04/05/llm-benchmark/en/memory-bandwidth.png" alt="Largura de banda de memória por tipo"  loading="lazy" /></p>
<p>A RTX 5090 tem 7x mais bandwidth que a memória LPDDR5x do meu Minisforum. Isso significa que mesmo que o modelo caiba na RAM unificada do AMD, a inferência vai ser proporcionalmente mais lenta. No meu Minisforum com LPDDR5x a 256 GB/s, o Qwen3 32B roda a ~7 tok/s. Na RTX 5090 a 1.792 GB/s, seria muito mais rápido — se coubesse inteiro na VRAM junto com o KV Cache.</p>
<p>A maioria das pessoas rodando modelos locais ainda está em DDR4. A 50 GB/s, modelos de 32B ficam praticamente unusáveis. E tem outro fator que pouca gente lembra: storage. Quando a RAM não dá conta e o sistema faz swap, a velocidade do armazenamento vira o gargalo:</p>
<table>
  <thead>
      <tr>
          <th>Storage</th>
          <th style="text-align: right">Velocidade Sequencial</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>SATA SSD</td>
          <td style="text-align: right">~550 MB/s</td>
      </tr>
      <tr>
          <td>NVMe Gen3</td>
          <td style="text-align: right">~3.500 MB/s</td>
      </tr>
      <tr>
          <td>NVMe Gen4</td>
          <td style="text-align: right">~7.000 MB/s</td>
      </tr>
      <tr>
          <td>NVMe Gen5</td>
          <td style="text-align: right">~12.000 MB/s</td>
      </tr>
  </tbody>
</table>
<p>De SATA pra NVMe Gen5 são 22x de diferença. Se você está fazendo offloading parcial pro disco (comum quando o modelo não cabe inteiro na GPU), NVMe Gen4 ou Gen5 faz diferença real. SATA é inviável.</p>
<p>Resumindo: rodar modelos locais não é só &ldquo;ter RAM suficiente&rdquo;. Precisa do tipo certo de memória, com a bandwidth certa, e storage rápido como fallback. Pra muita gente, um Mac Studio com memória unificada de alta bandwidth (até 800 GB/s nos M4 Ultra com 512 GB) seria a opção mais prática, mas custa mais de US$ 10.000. O AMD Ryzen AI Max é a alternativa mais acessível com memória unificada, mas o LPDDR5 fica limitado a 256 GB/s.</p>
<h2>Ollama vs llama.cpp: por que o Ollama falha pra benchmarks<span class="hx:absolute hx:-mt-20" id="ollama-vs-llamacpp-por-que-o-ollama-falha-pra-benchmarks"></span>
    <a href="#ollama-vs-llamacpp-por-que-o-ollama-falha-pra-benchmarks" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>O <a href="https://ollama.com/"target="_blank" rel="noopener">Ollama</a> é a forma mais popular de rodar modelos locais. Instala, puxa o modelo, roda. Pra uso casual funciona. Mas quando tentei usar pra benchmarks automatizados, sessões longas sem intervenção humana, quebrou de 6 formas diferentes em 8 modelos:</p>
<ol>
<li>Descarrega o modelo no meio da sessão. Em runs longos, o Ollama decide que o modelo não está sendo usado e descarrega da GPU. O agent fica esperando resposta de um modelo que não existe mais.</li>
<li>Ignora o contexto solicitado. Você pede <code>num_ctx=131072</code>, o Ollama aceita, e no meio do run reverte pro padrão sem avisar.</li>
<li>Lifecycle instável. Pedir <code>keep_alive: 0</code> pra descarregar nem sempre funciona. O modelo fica residente e bloqueia o próximo.</li>
<li>Formatos incompatíveis. Variantes bf16 nativas do Ollama falhavam, enquanto o mesmo modelo como GGUF Q8 do HuggingFace funcionava sem problemas.</li>
</ol>
<p>A solução: migrar pro <a href="https://github.com/mostlygeek/llama-swap"target="_blank" rel="noopener">llama-swap</a>, um wrapper Go que gerencia processos llama.cpp com hot-swap. Chega um request pra um modelo diferente do que está carregado, ele mata o processo atual e inicia o novo. Sem negociação de contexto, sem lifecycle instável.</p>
<p>O llama-swap resolveu o carregamento de 6 dos 8 modelos que falharam no Ollama:</p>
<table>
  <thead>
      <tr>
          <th>Modelo</th>
          <th>Ollama</th>
          <th>llama-swap</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Gemma 4 27B</td>
          <td>HTTP 500</td>
          <td>47.6 tok/s</td>
      </tr>
      <tr>
          <td>GLM 4.7 Flash</td>
          <td>Sem output</td>
          <td>47.4 tok/s</td>
      </tr>
      <tr>
          <td>Llama 4 Scout</td>
          <td>Descarregou</td>
          <td>17.5 tok/s</td>
      </tr>
      <tr>
          <td>Qwen 3.5 35B</td>
          <td>Output off-spec</td>
          <td>49.7 tok/s</td>
      </tr>
      <tr>
          <td>Qwen 3.5 122B</td>
          <td>Context drift</td>
          <td>23.1 tok/s</td>
      </tr>
      <tr>
          <td>GPT OSS 20B</td>
          <td>Model not found</td>
          <td>78.3 tok/s</td>
      </tr>
  </tbody>
</table>
<p>Mas o llama-swap não é mágico.</p>
<h2>Por que &ldquo;só usar llama.cpp&rdquo; não resolve tudo<span class="hx:absolute hx:-mt-20" id="por-que-só-usar-llamacpp-não-resolve-tudo"></span>
    <a href="#por-que-s%c3%b3-usar-llamacpp-n%c3%a3o-resolve-tudo" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>O llama.cpp resolve os problemas de lifecycle do Ollama mas traz os próprios:</p>
<p>Cada modelo precisa de flags específicas. GLM e Qwen 3.5 emitem <code>&lt;think&gt;</code> tags que quebram clientes se você não passar <code>--reasoning-format none</code>. Gemma 4 precisa de build b8665+ pro parser de tool calls funcionar.</p>
<p>Nem todo modelo suporta tool calling. O llama.cpp precisa de um parser dedicado pro formato de tool call de cada modelo. O Llama 4 Scout usa um formato &ldquo;pythonic&rdquo; (<code>[func(param=&quot;value&quot;)]</code>) que o llama.cpp simplesmente não parseia, emite como texto puro. O vLLM tem parser pra isso, o llama.cpp não.</p>
<p>E tem os repetition loops. O Gemma 4, mesmo com o parser certo, entra em loop infinito depois de ~11 tool calls em sessões longas. É um <a href="https://github.com/ggml-org/llama.cpp/issues/21375"target="_blank" rel="noopener">bug conhecido</a> que o PR #21418 não resolveu completamente.</p>
<p>Compatibilidade de tool calling por modelo:</p>
<table>
  <thead>
      <tr>
          <th>Modelo</th>
          <th>Tool Calling</th>
          <th>Flags Necessárias</th>
          <th>Resultado no Benchmark</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Gemma 4 27B</td>
          <td>Parcial (b8665+)</td>
          <td><code>--jinja --reasoning-format none</code></td>
          <td>Loop infinito após ~11 steps</td>
      </tr>
      <tr>
          <td>GLM 4.7 Flash</td>
          <td>Sim</td>
          <td><code>--jinja --reasoning-format none</code></td>
          <td>2029 arquivos, terminou mid-tool-call</td>
      </tr>
      <tr>
          <td>Qwen 3.5 (35B, 122B)</td>
          <td>Sim</td>
          <td><code>--jinja --reasoning-format none</code></td>
          <td>Completou com sucesso</td>
      </tr>
      <tr>
          <td>Qwen 3 Coder Next</td>
          <td>Sim</td>
          <td><code>--jinja</code></td>
          <td>Completou (melhor resultado local)</td>
      </tr>
      <tr>
          <td>GPT OSS 20B</td>
          <td>Sim</td>
          <td><code>--jinja</code></td>
          <td>Tool calls ok, mas app no diretório errado</td>
      </tr>
      <tr>
          <td>Llama 4 Scout</td>
          <td>Não</td>
          <td>—</td>
          <td>Sem parser no llama.cpp</td>
      </tr>
  </tbody>
</table>
<p>No fim das contas, llama.cpp é melhor que Ollama pra runs automatizados, mas &ldquo;plug and play&rdquo; não é. Cada modelo exige configuração específica, e alguns simplesmente não funcionam pra agentic coding ainda.</p>
<h2>Reasoning: modelos que pensam vs modelos que chutam<span class="hx:absolute hx:-mt-20" id="reasoning-modelos-que-pensam-vs-modelos-que-chutam"></span>
    <a href="#reasoning-modelos-que-pensam-vs-modelos-que-chutam" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Tem uma diferença entre modelos que vale explicar: reasoning. A ideia é que o modelo &ldquo;pensa antes de responder&rdquo; em vez de gerar tokens direto da esquerda pra direita. Modelos com reasoning fazem uma etapa interna de cadeia de pensamento (chain-of-thought) onde avaliam o problema, consideram alternativas, planejam, e só depois emitem a resposta.</p>
<p>Na prática, isso aparece como <code>&lt;think&gt;...&lt;/think&gt;</code> tags no output, blocos de texto que o modelo gera pra si mesmo, que não devem ir pro usuário final. Claude Opus 4.6, GPT 5.4, DeepSeek V3.2 e a linha Qwen 3.5 suportam reasoning nativamente. Os menores (Gemma 4, GPT OSS 20B, modelos mais antigos) não têm essa capacidade.</p>
<p>Por que importa pra coding? Quando um coding agent recebe &ldquo;construa um app Rails com 9 componentes&rdquo;, ele precisa decompor a tarefa em passos, decidir a ordem, antecipar dependências, adaptar quando algo falha. Sem reasoning, o modelo gera código sequencialmente sem planejamento. Funciona pra tarefas simples, desmorona em projetos com partes interdependentes.</p>
<p>No benchmark, a diferença ficou clara:</p>
<ul>
<li>GPT OSS 20B (sem reasoning, 20B parâmetros) criou o app no diretório errado. Não conseguiu manter o contexto das instruções de workspace enquanto gerava código.</li>
<li>Qwen 3 32B tem reasoning, mas a 7 tok/s era lento demais. Os tokens de &ldquo;pensamento&rdquo; aumentam o tempo de geração.</li>
<li>Gemma 4 31B, sem reasoning treinado pra uso agentic, entrou em loops repetitivos de tool calling.</li>
<li>GLM 5 (cloud, 745B MoE) com reasoning e 44B parâmetros ativos, completou limpo e usou a API correta.</li>
</ul>
<p>Tem um trade-off: reasoning consome tokens extras (os <code>&lt;think&gt;</code> blocks), que ocupam VRAM no KV Cache e desaceleram a geração. Por isso flags como <code>--reasoning-format none</code> são necessárias no llama.cpp. Alguns clientes não sabem o que fazer com reasoning tokens e quebram. Modelos que emitem reasoning sem que o runtime espere podem gerar lixo no output.</p>
<p>E reasoning não é algo que você &ldquo;liga&rdquo; num modelo qualquer. É uma capacidade treinada com reinforcement learning em cima do modelo base, usando dados de problemas que exigem raciocínio multi-step. Os modelos open source menores (20B-35B) tipicamente não passaram por esse treinamento, ou passaram em escala menor. Eles sabem gerar código, mas não sabem <em>planejar</em> código. Em tarefas que exigem 50+ tool calls coordenadas, essa diferença mata.</p>
<h2>O benchmark: metodologia<span class="hx:absolute hx:-mt-20" id="o-benchmark-metodologia"></span>
    <a href="#o-benchmark-metodologia" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Pra comparar modelos de forma justa, construí um harness automatizado em Python. Cada modelo recebe exatamente o mesmo prompt: construir uma aplicação Ruby on Rails completa, um chat SPA tipo ChatGPT usando o gem RubyLLM, com Hotwire/Stimulus/Turbo Streams, Tailwind CSS, testes Minitest, ferramentas de CI (Brakeman, RuboCop, SimpleCov, bundle-audit), Dockerfile, docker-compose e README.</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/2026/04/05/llm-benchmark/crush-screenshot.png" alt="Crush — CLI de coding da Charm"  loading="lazy" /></p>
<p>O runner é o <a href="https://github.com/sst/opencode"target="_blank" rel="noopener">opencode</a>, uma das CLIs de coding open source mais populares, competindo com Claude Code e Codex. Vale um esclarecimento: o autor original saiu do projeto e foi tocar o <a href="https://github.com/charmbracelet/crush"target="_blank" rel="noopener">Crush</a> junto com a equipe da <a href="https://charm.sh/"target="_blank" rel="noopener">Charm</a> (o pessoal por trás do Bubble Tea, Lip Gloss e várias outras ferramentas de terminal em Go), mas o restante do time original continuou evoluindo o opencode normalmente — o projeto não foi descontinuado. Hoje os dois coexistem. Se você leu <a href="https://akitaonrails.com/2026/01/09/omarchy-3-um-dos-melhores-agentes-pra-programacao-crush/"target="_blank" rel="noopener">meu artigo sobre o Crush</a>, já conhece essa vertente. Ambos rodam em tudo: macOS, Linux, Windows, Android, FreeBSD.</p>
<p>Na verdade, tentei usar o Crush primeiro pro benchmark. O problema: ele anunciava uma flag <code>--yolo</code> no help pra auto-aprovar todas as ações (essencial pra runs automatizados sem intervenção humana), mas na hora de rodar, rejeitava a flag. Sem auto-approve, não dá pra fazer benchmark desacompanhado. O opencode, por outro lado, tinha o modo <code>opencode run --agent build --format json</code> que emite eventos JSON com session IDs e contagem de tokens, perfeito pra automação. Então ficamos com o opencode.</p>
<p>Escolhi o opencode (e não Claude Code ou Codex) por dois motivos:</p>
<ol>
<li>Neutralidade. Claude Code é otimizado pra modelos Anthropic. Codex é otimizado pra modelos OpenAI. O opencode é agnóstico, mesma interface pra todos.</li>
<li>Automação. O opencode expõe um formato JSON legível por máquina. Claude Code e Codex não têm interface equivalente pra benchmarking externo.</li>
</ol>
<p>Modelos cloud rodaram em duas fases: fase 1 (construir o app) e fase 2 (validar boot local, docker build, docker compose). Modelos locais rodaram só fase 1.</p>
<p>Detalhe que vale mencionar: o benchmark inteiro custou menos de $10 em tokens no OpenRouter. Tirando o GPT 5.4 Pro que torrou $7.20 pra falhar, os outros 11 modelos cloud somaram uns $2.50 no total. Modelos locais custam só eletricidade. O ponto é: rodar seu próprio benchmark é barato. Se você quer saber se um modelo funciona pro seu caso de uso, gaste os $2 e teste. O código do harness está no GitHub, é só trocar o prompt pelo seu projeto.</p>
<h2>Por que o GPT 5.4 falhou no benchmark (mas não na vida real)<span class="hx:absolute hx:-mt-20" id="por-que-o-gpt-54-falhou-no-benchmark-mas-não-na-vida-real"></span>
    <a href="#por-que-o-gpt-54-falhou-no-benchmark-mas-n%c3%a3o-na-vida-real" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>O GPT 5.4 Pro é o único modelo cloud que falhou consistentemente no nosso benchmark. Duas runs separadas, mesmo resultado: o modelo gerou arquivos mas nunca chegou a <code>finish_reason: stop</code>. Sempre terminava com <code>finish_reason: tool-calls</code> — queria continuar chamando tools mas o loop se quebrava.</p>
<p>Pra quem não sabe: tool calling é quando um LLM precisa executar uma ação (ler um arquivo, rodar um comando, editar código) e emite uma &ldquo;chamada de ferramenta&rdquo; num formato estruturado. O client (opencode, Claude Code, Codex) interpreta, executa, e devolve o resultado pro modelo. Cada provedor tem seu formato: a Anthropic usa <code>tool_use</code> blocks, a OpenAI usa <code>function_calling</code> com JSON schemas, o Google usa <code>FunctionCall</code>.</p>
<p>O GPT 5.4 é fortemente treinado pro formato nativo de function calling da OpenAI — <code>tool_choice</code>, <code>tools</code> com JSON schemas proprietários. Quando o benchmark rota por opencode → OpenRouter → GPT 5.4, os schemas de tools são traduzidos em cada hop. Se o GPT emite tool calls num formato que o OpenRouter ou opencode não parseia corretamente, o loop do agent quebra.</p>
<p>A evidência: todos os outros modelos cloud (Claude Opus, Claude Sonnet, Kimi K2.5, DeepSeek V3.2, MiniMax M2.7, GLM 5, Qwen 3.6 Plus, Step 3.5 Flash) terminaram com <code>finish_reason: stop</code>. Só o GPT termina com <code>finish_reason: tool-calls</code>.</p>
<p>Comparação justa do GPT 5.4 exigiria rodar no ambiente nativo. E agora temos essa comparação: construímos suporte pra automatizar o Codex CLI (<code>codex exec</code> com <code>--dangerously-bypass-approvals-and-sandbox</code> e reasoning effort <code>xhigh</code>) e rodamos o mesmo benchmark. O GPT 5.4 completou em 22 minutos, gerou todos os 9 artefatos, escreveu 22 testes com a arquitetura mais sofisticada do benchmark inteiro: injeção de dependência do client RubyLLM, PORO models pra <code>ChatMessage</code> e <code>PromptSubmission</code>, session-backed <code>ChatSession</code> com TTL e trimming de mensagens, bin/ci script.</p>
<p>Mas o código quebra na segunda mensagem. O GPT 5.4 usa <code>chat.add_message(role:, content:)</code> com keyword arguments em vez de hash posicional <code>chat.add_message({role:, content:})</code> — isso causa <code>ArgumentError: wrong number of arguments (given 0, expected 1)</code> na primeira troca multi-turn. A primeira mensagem funciona (usa <code>chat.ask</code> direto), o multi-turn não.</p>
<p>E o custo: <strong>7.6 milhões de tokens</strong> no reasoning effort xHigh. São 65x mais tokens que o Opus 4.7 (118K) pro mesmo benchmark. Custo estimado de ~$16 por run, contra ~$1.10 do Opus. Gastou 15x mais e ainda errou a convenção de chamada. Nem budget de token gigantesco nem reasoning effort máximo garante acerto factual numa API de gem. Conhecimento de API é memória binária, não é função de quanto o modelo &ldquo;pensa&rdquo;.</p>
<p>Com dados objetivos em mãos, o GPT 5.4 sai do Tier 1 e vai pro Tier 2. A arquitetura que ele gera é melhor que a do Opus em termos de design patterns. Mas o código precisa de correção pra rodar multi-turn, e o custo por token é proibitivo.</p>
<p>O Sonnet e o Opus via opencode/OpenRouter também provavelmente não foram usados ao máximo da capacidade. O Claude Code oferece suporte nativo de tools que o opencode não replica — o que significa que os resultados do benchmark representam um piso, não um teto, pra esses modelos.</p>
<h2>Modelos open source: a realidade vs a narrativa<span class="hx:absolute hx:-mt-20" id="modelos-open-source-a-realidade-vs-a-narrativa"></span>
    <a href="#modelos-open-source-a-realidade-vs-a-narrativa" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Tem muita gente dizendo que modelos open source já alcançaram os comerciais e que dá pra rodar seu próprio &ldquo;Claude&rdquo; em casa. Na prática, não é bem assim.</p>
<p>A escala não é comparável. Modelos frontier como Claude Opus 4.6 e GPT 5.4 são closed-source, mas estimativas indicam que estão na faixa de centenas de bilhões a trilhões de parâmetros, treinados com compute e dados que nenhuma empresa open source replica. Os melhores modelos que cabem num hardware razoável são:</p>
<table>
  <thead>
      <tr>
          <th>Modelo</th>
          <th style="text-align: right">Parâmetros Totais</th>
          <th style="text-align: right">Parâmetros Ativos</th>
          <th>Arquitetura</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Qwen 3.5 35B-A3B</td>
          <td style="text-align: right">35B</td>
          <td style="text-align: right">3B</td>
          <td>MoE (A3B)</td>
      </tr>
      <tr>
          <td>Qwen 3.5 27B</td>
          <td style="text-align: right">27B</td>
          <td style="text-align: right">27B</td>
          <td>Dense</td>
      </tr>
      <tr>
          <td>Qwen 3 32B</td>
          <td style="text-align: right">32B</td>
          <td style="text-align: right">32B</td>
          <td>Dense</td>
      </tr>
      <tr>
          <td>Qwen 3.5 122B</td>
          <td style="text-align: right">122B</td>
          <td style="text-align: right">122B</td>
          <td>Dense</td>
      </tr>
      <tr>
          <td>GPT OSS 20B</td>
          <td style="text-align: right">20B</td>
          <td style="text-align: right">20B</td>
          <td>Dense</td>
      </tr>
      <tr>
          <td>Gemma 4 31B</td>
          <td style="text-align: right">31B</td>
          <td style="text-align: right">31B</td>
          <td>Dense</td>
      </tr>
  </tbody>
</table>
<p>Correção pós-publicação: o Qwen 3.5 35B na verdade é o <strong>35B-A3B</strong>, um MoE com só 3B de parâmetros ativos por token (não denso, como eu tinha colocado originalmente). Isso explica por que ele roda relativamente rápido pro tamanho. E pra quem tem 24 GB de VRAM, o modelo recomendado pela própria <a href="https://unsloth.ai/docs/models/qwen3.5#qwen3.5-27b"target="_blank" rel="noopener">Unsloth</a> é o <strong>Qwen 3.5 27B</strong> denso — esse eu não cheguei a testar no benchmark, mas fica como recomendação. Pra quem quer se aprofundar em modelos locais, vale acompanhar o trabalho do <a href="https://x.com/sudoingX"target="_blank" rel="noopener">@sudoingX</a>, que tem feito experimentação séria nessa frente. Valeu <a href="https://x.com/thpmacedo/status/2041105305111502927"target="_blank" rel="noopener">@thpmacedo</a> pelo toque.</p>
<p>Mesmo os maiores modelos open source MoE (Mixture of Experts) que as empresas disponibilizam publicamente ativam poucos parâmetros por token:</p>
<table>
  <thead>
      <tr>
          <th>Modelo</th>
          <th style="text-align: right">Parâmetros Totais</th>
          <th style="text-align: right">Parâmetros Ativos</th>
          <th>Notas</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Kimi K2.5</td>
          <td style="text-align: right">1T</td>
          <td style="text-align: right">32B</td>
          <td>384 experts, top-8 + shared</td>
      </tr>
      <tr>
          <td>GLM 5</td>
          <td style="text-align: right">745B</td>
          <td style="text-align: right">44B</td>
          <td>256 experts, 8 ativados</td>
      </tr>
      <tr>
          <td>DeepSeek V3.2</td>
          <td style="text-align: right">671B</td>
          <td style="text-align: right">37B</td>
          <td>Sparse Attention</td>
      </tr>
      <tr>
          <td>Qwen 3.5 397B</td>
          <td style="text-align: right">397B</td>
          <td style="text-align: right">17B</td>
          <td>MoE, cloud-only</td>
      </tr>
  </tbody>
</table>
<p>Esses modelos grandes não são self-hostáveis. O Kimi K2.5 com 1T parâmetros precisa de clusters de GPUs com centenas de GBs de VRAM. O GLM 5 com 745B idem. Mesmo que a Alibaba ou a Z.AI liberem os pesos (e algumas liberam), ninguém tem hardware doméstico pra rodar eles.</p>
<p>O que cabe na sua GPU doméstica são os modelos de 20B-35B — e esses têm limitações reais.</p>
<h3>O que cada modelo local fez no benchmark<span class="hx:absolute hx:-mt-20" id="o-que-cada-modelo-local-fez-no-benchmark"></span>
    <a href="#o-que-cada-modelo-local-fez-no-benchmark" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Resultados da rodada original no AMD Strix Halo:</p>
<p><strong>Qwen 3 Coder Next (30B)</strong> — Completou em 17 minutos no Strix, gerou 1675 arquivos, app Rails com todos os artefatos. Mas só 3 testes. E o mais importante: inventou <code>RubyLLM::Client.new</code>, uma classe que não existe no gem. O app não roda.</p>
<p><strong>Qwen 3.5 35B</strong> — Completou em 28 minutos no Strix, 1478 arquivos, 11 testes. Usou <code>RubyLLM.chat</code> sem parâmetro de modelo — funciona só se o default estiver configurado. Sem mocking de LLM nos testes.</p>
<p><strong>Qwen 3.5 122B</strong> — Completou em 43 minutos no Strix, 1503 arquivos, 16 testes. Mas ignorou o gem RubyLLM completamente e construiu um cliente HTTP customizado pro OpenRouter. O prompt pedia explicitamente pra usar ruby_llm.</p>
<p><strong>GLM 4.7 Flash (local, Strix)</strong> — Produziu 2029 arquivos com todos os artefatos, mas a sessão terminou mid-tool-call. O modelo cloud (GLM 5) funciona perfeitamente.</p>
<p><strong>Gemma 4 31B (Strix)</strong> — Loop infinito de tool calls depois de ~11 steps produtivos. Bug conhecido do llama.cpp.</p>
<p><strong>GPT OSS 20B (Strix)</strong> — Criou o app Rails no diretório errado (<code>project/app/</code> em vez de <code>project/</code>). Um modelo de 20B não segue instruções de workspace de forma confiável.</p>
<p><strong>Qwen 3 32B (Strix)</strong> — Lento demais (7.3 tok/s). Hardware não dá conta.</p>
<p>E os resultados da rerodada na NVIDIA RTX 5090 (todos com Q3_K_M ou Q4_K_M e contexto entre 64k e 128k pra caber nos 32 GB de VRAM):</p>
<p><strong>Qwen 3.5 35B-A3B (5090)</strong> — 5 minutos a 273 tok/s. Projeto Rails reconhecível, entry point <code>RubyLLM.chat(model:)</code> está certo, mas alucina <code>chat.add_message(role:, content:)</code> e <code>chat.complete</code> em vez de <code>.ask</code>. Dá pra arrumar em 1-2 follow-ups. O melhor candidato a &ldquo;OSS local que vale a pena tentar&rdquo;.</p>
<p><strong>Qwen 3.5 27B Claude-distilado (5090)</strong> — 12 minutos a 129 tok/s. Estilo Claude impecável, alucinação total da API (<code>RubyLLM::Chat.new.with_model{}</code>, <code>add_message</code>, <code>response.text</code>). Mais detalhes na seção de distilação abaixo.</p>
<p><strong>Qwen 3 Coder 30B (5090)</strong> — 6 minutos a 145 tok/s. Devolveu uma string mockada hardcoded em vez de chamar a API. Tier 3 inutilizável.</p>
<p><strong>Qwen 2.5 Coder 32B (5090)</strong> — 90 minutos de timeout, zero arquivos. Modelo girou sem nunca chamar tool de write.</p>
<p><strong>Qwen 3 32B (5090)</strong> — 4 minutos a 69 tok/s, scaffold parcial, errors. Versão geral é melhor que a Coder mas ainda quebra.</p>
<p><strong>Gemma 4 31B (5090)</strong> — 8 minutos a 213 tok/s. Mesmo loop de repetição que tinha no Strix. Bug do llama.cpp não é hardware.</p>
<p><strong>Qwen 3.5 27B Sushi Coder RL (5090)</strong> — Falha de infra (<code>ProviderModelNotFoundError</code>), não foi possível avaliar. Refazer numa próxima rodada.</p>
<p><strong>GPT OSS 20B (5090)</strong> — Tirado da rodada por regressão recente do llama.cpp main no parser de tool calls da família harmony. Os logs mostram <code>Failed to parse input at pos 755: &lt;|channel|&gt;...</code> em sessões multi-turn. Funcionava no Strix com o llama.cpp <code>b8643</code>, quebrou no main de hoje. Aguardando upstream consertar.</p>
<h2>Modelos cloud: o que funciona de verdade<span class="hx:absolute hx:-mt-20" id="modelos-cloud-o-que-funciona-de-verdade"></span>
    <a href="#modelos-cloud-o-que-funciona-de-verdade" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Dos 12 modelos que completaram o benchmark, todos geraram um projeto Rails reconhecível com todos os artefatos pedidos (Gemfile, routes, views, JS, testes, README, Dockerfile, docker-compose). 9 de 9 no checklist de completude.</p>
<p>Mas aí vem a pergunta que importa: o código roda?</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/2026/04/05/llm-benchmark/en/cost-vs-quality.png" alt="Custo vs tempo — e o código funciona?"  loading="lazy" /></p>
<p>A API correta do RubyLLM é simples:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">chat</span> <span class="o">=</span> <span class="no">RubyLLM</span><span class="o">.</span><span class="n">chat</span><span class="p">(</span><span class="ss">model</span><span class="p">:</span> <span class="s2">&#34;anthropic/claude-sonnet-4&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">response</span> <span class="o">=</span> <span class="n">chat</span><span class="o">.</span><span class="n">ask</span><span class="p">(</span><span class="s2">&#34;Hello&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">response</span><span class="o">.</span><span class="n">content</span>  <span class="c1"># =&gt; &#34;Hi there!&#34;</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>8 dos 12 modelos inventaram APIs que não existem. O padrão mais comum: alucinação de uma interface que não é a do gem real:</p>
<table>
  <thead>
      <tr>
          <th>Modelo</th>
          <th>O que Inventou</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>DeepSeek V3.2</td>
          <td><code>RubyLLM::Client.new</code> — classe inexistente</td>
      </tr>
      <tr>
          <td>Qwen 3 Coder Next</td>
          <td><code>RubyLLM::Client.new</code> — mesmo erro</td>
      </tr>
      <tr>
          <td>Qwen 3.5 122B</td>
          <td><code>Openrouter::Client</code> — gem inexistente</td>
      </tr>
      <tr>
          <td>Kimi K2.5</td>
          <td><code>add_message()</code> e <code>complete()</code> — métodos inexistentes</td>
      </tr>
      <tr>
          <td>MiniMax M2.7</td>
          <td><code>RubyLLM.chat(messages: [...])</code> — assinatura inexistente</td>
      </tr>
      <tr>
          <td>Qwen 3.6 Plus</td>
          <td><code>chat.add_message()</code> — método inexistente</td>
      </tr>
      <tr>
          <td>Gemini 3.1 Pro</td>
          <td><code>RubyLLM::Chat.new()</code> e <code>add_message()</code> — API interna, não pública</td>
      </tr>
      <tr>
          <td>Grok 4.20</td>
          <td>Ignora o gem completamente — usa <code>OpenAI::Client</code> (ruby-openai) batendo direto na URL do OpenRouter</td>
      </tr>
  </tbody>
</table>
<p>Os modelos que acertaram — os dois Claudes, o GLM 5 e o GLM 5.1 — usaram o padrão simples de duas etapas (<code>chat = RubyLLM.chat(model:)</code> depois <code>chat.ask(message)</code>). Os que erraram tentaram fazer o RubyLLM parecer com o SDK Python da OpenAI, que é outra coisa. O Grok 4.20 foi o caso mais cínico: nem tentou usar o gem, foi direto pro <code>OpenAI::Client</code> apontando pra URL do OpenRouter, ignorando o pedido explícito do prompt.</p>
<p>E os testes? Só Opus, Sonnet, GLM 5 e GLM 5.1 fizeram mocking correto das chamadas LLM. Todos os outros ou batiam na API real (que falha sem chave) ou mockavam a API inventada (testes passam mas não provam nada). Contagem de testes é uma métrica enganosa: o Kimi K2.5 escreveu 37 testes, mais que qualquer outro, mas nenhum testa a funcionalidade real porque a API que ele usa não existe.</p>
<h3>Tabela de viabilidade real<span class="hx:absolute hx:-mt-20" id="tabela-de-viabilidade-real"></span>
    <a href="#tabela-de-viabilidade-real" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><table>
  <thead>
      <tr>
          <th>Modelo</th>
          <th style="text-align: center">API Correta?</th>
          <th style="text-align: center">Roda?</th>
          <th style="text-align: center">Mocking nos Testes?</th>
          <th>Problema</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><strong>Claude Opus 4.7</strong></td>
          <td style="text-align: center">Sim</td>
          <td style="text-align: center"><strong>Sim</strong></td>
          <td style="text-align: center">Sim (FakeChat)</td>
          <td>Implementação limpa, 28 testes</td>
      </tr>
      <tr>
          <td><strong>Claude Sonnet 4.6</strong></td>
          <td style="text-align: center">Sim</td>
          <td style="text-align: center"><strong>Sim</strong></td>
          <td style="text-align: center">Sim (mocha)</td>
          <td>Implementação limpa</td>
      </tr>
      <tr>
          <td><strong>Claude Opus 4.6</strong></td>
          <td style="text-align: center">Sim</td>
          <td style="text-align: center"><strong>Sim</strong></td>
          <td style="text-align: center">Sim (mocha)</td>
          <td>Implementação limpa</td>
      </tr>
      <tr>
          <td><strong>GLM 5</strong></td>
          <td style="text-align: center">Sim</td>
          <td style="text-align: center"><strong>Sim</strong></td>
          <td style="text-align: center">Sim (mocha)</td>
          <td>API correta, funciona</td>
      </tr>
      <tr>
          <td><strong>GLM 5.1</strong></td>
          <td style="text-align: center">Sim</td>
          <td style="text-align: center"><strong>Sim</strong></td>
          <td style="text-align: center">Sim</td>
          <td>API correta, funciona</td>
      </tr>
      <tr>
          <td>GPT 5.4 (Codex)</td>
          <td style="text-align: center">Parcial</td>
          <td style="text-align: center">Só 1ª msg</td>
          <td style="text-align: center">Sim (FakeChat)</td>
          <td><code>add_message(role:, content:)</code> com keyword args em vez de hash posicional — quebra no multi-turn</td>
      </tr>
      <tr>
          <td>Step 3.5 Flash</td>
          <td style="text-align: center">N/A</td>
          <td style="text-align: center"><strong>Sim</strong>*</td>
          <td style="text-align: center">Não</td>
          <td>Bypassa RubyLLM, usa HTTP direto</td>
      </tr>
      <tr>
          <td>Grok 4.20</td>
          <td style="text-align: center">N/A</td>
          <td style="text-align: center"><strong>Sim</strong>*</td>
          <td style="text-align: center">Não</td>
          <td>Bypassa RubyLLM, usa <code>OpenAI::Client</code> direto</td>
      </tr>
      <tr>
          <td>Qwen 3.6 Plus</td>
          <td style="text-align: center">Parcial</td>
          <td style="text-align: center">Só 1ª msg</td>
          <td style="text-align: center">Não</td>
          <td><code>add_message()</code> não existe</td>
      </tr>
      <tr>
          <td>Qwen 3.5 35B</td>
          <td style="text-align: center">Parcial</td>
          <td style="text-align: center">Talvez</td>
          <td style="text-align: center">Não</td>
          <td>Sem parâmetro de modelo</td>
      </tr>
      <tr>
          <td>Kimi K2.5</td>
          <td style="text-align: center">Não</td>
          <td style="text-align: center"><strong>Não</strong></td>
          <td style="text-align: center">Não</td>
          <td><code>add_message()</code>/<code>complete()</code> inventados</td>
      </tr>
      <tr>
          <td>MiniMax M2.7</td>
          <td style="text-align: center">Não</td>
          <td style="text-align: center"><strong>Não</strong></td>
          <td style="text-align: center">Não</td>
          <td>Assinatura de <code>RubyLLM.chat</code> errada</td>
      </tr>
      <tr>
          <td>DeepSeek V3.2</td>
          <td style="text-align: center">Não</td>
          <td style="text-align: center"><strong>Não</strong></td>
          <td style="text-align: center">Não</td>
          <td><code>RubyLLM::Client</code> inexistente</td>
      </tr>
      <tr>
          <td>Qwen 3 Coder Next</td>
          <td style="text-align: center">Não</td>
          <td style="text-align: center"><strong>Não</strong></td>
          <td style="text-align: center">Não</td>
          <td><code>RubyLLM::Client</code> inexistente</td>
      </tr>
      <tr>
          <td>Gemini 3.1 Pro</td>
          <td style="text-align: center">Não</td>
          <td style="text-align: center"><strong>Não</strong></td>
          <td style="text-align: center">Mock errado</td>
          <td><code>RubyLLM::Chat.new()</code> e <code>add_message()</code> inexistentes</td>
      </tr>
      <tr>
          <td>Qwen 3.5 122B</td>
          <td style="text-align: center">Não</td>
          <td style="text-align: center"><strong>Não</strong></td>
          <td style="text-align: center">Não</td>
          <td><code>Openrouter::Client</code> gem inexistente</td>
      </tr>
  </tbody>
</table>
<p>*Step 3.5 Flash funciona chamando a API REST do OpenRouter direto com <code>Net::HTTP</code>, contornando completamente o gem que o prompt pedia.</p>
<p>Agora, isso não quer dizer que esses modelos são inúteis. Se você pegar o Kimi K2.5 ou o DeepSeek V3.2 e mandar &ldquo;a classe RubyLLM::Client não existe, corrija pra usar a API real do gem&rdquo;, provavelmente vai corrigir. Um ou dois follow-ups e o projeto fica funcional. A maioria dos modelos que falharam aqui conseguiria entregar um projeto rodando com mais algumas rodadas de conversa.</p>
<p>Só que aí está o trade-off. Com Opus ou GPT 5.4, o primeiro output já funciona. Pede, entrega, testa, roda. Com os modelos mais baratos, você vai gastar tempo corrigindo alucinações de API, debugando código que &ldquo;parece certo&rdquo; mas crasha, guiando o modelo na direção certa. Cada rodada dessas são 10-30 minutos. Três rodadas extras e você gastou uma hora do seu tempo pra economizar $0.90 de tokens.</p>
<p>Economiza dólar, gasta tempo. E tempo é dinheiro. Pra quem está aprendendo ou explorando sem pressa, essa troca pode fazer sentido. Pra quem precisa entregar, os modelos frontier se pagam rápido.</p>
<h3>Comparação dos modelos que funcionam<span class="hx:absolute hx:-mt-20" id="comparação-dos-modelos-que-funcionam"></span>
    <a href="#compara%c3%a7%c3%a3o-dos-modelos-que-funcionam" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><table>
  <thead>
      <tr>
          <th>Modelo</th>
          <th>Provedor</th>
          <th style="text-align: right">Tempo</th>
          <th style="text-align: right">Testes</th>
          <th style="text-align: right">Custo/Run</th>
          <th>vs Opus</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Claude Opus 4.7</td>
          <td>OpenRouter</td>
          <td style="text-align: right">18m</td>
          <td style="text-align: right">28</td>
          <td style="text-align: right">~$1.10</td>
          <td>Novo baseline</td>
      </tr>
      <tr>
          <td>Claude Sonnet 4.6</td>
          <td>OpenRouter</td>
          <td style="text-align: right">16m</td>
          <td style="text-align: right">30</td>
          <td style="text-align: right">~$0.63</td>
          <td>40% mais barato, mais testes</td>
      </tr>
      <tr>
          <td>Claude Opus 4.6</td>
          <td>OpenRouter</td>
          <td style="text-align: right">16m</td>
          <td style="text-align: right">16</td>
          <td style="text-align: right">~$1.05</td>
          <td>Baseline anterior</td>
      </tr>
      <tr>
          <td>GLM 5</td>
          <td>OpenRouter</td>
          <td style="text-align: right">17m</td>
          <td style="text-align: right">7</td>
          <td style="text-align: right">~$0.11</td>
          <td>89% mais barato</td>
      </tr>
      <tr>
          <td>GLM 5.1</td>
          <td>Z.AI direto</td>
          <td style="text-align: right">22m</td>
          <td style="text-align: right">24</td>
          <td style="text-align: right">~$0.13</td>
          <td>~88% mais barato, mais testes que o GLM 5</td>
      </tr>
  </tbody>
</table>
<h3>Ranking completo por tempo e tokens<span class="hx:absolute hx:-mt-20" id="ranking-completo-por-tempo-e-tokens"></span>
    <a href="#ranking-completo-por-tempo-e-tokens" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/2026/04/05/llm-benchmark/en/time-to-complete.png" alt="Tempo de conclusão por modelo"  loading="lazy" /></p>
<table>
  <thead>
      <tr>
          <th>Modelo</th>
          <th>Provedor</th>
          <th style="text-align: right">Tempo</th>
          <th style="text-align: right">Tokens Totais</th>
          <th style="text-align: right">Tok/s</th>
          <th style="text-align: right">Custo/Run</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Grok 4.20</td>
          <td>OpenRouter</td>
          <td style="text-align: right">8m</td>
          <td style="text-align: right">63.457</td>
          <td style="text-align: right">412.54</td>
          <td style="text-align: right">~$0.04</td>
      </tr>
      <tr>
          <td>Gemini 3.1 Pro</td>
          <td>OpenRouter</td>
          <td style="text-align: right">14m</td>
          <td style="text-align: right">104.034</td>
          <td style="text-align: right">128.28</td>
          <td style="text-align: right">~$0.50</td>
      </tr>
      <tr>
          <td>MiniMax M2.7</td>
          <td>OpenRouter</td>
          <td style="text-align: right">14m</td>
          <td style="text-align: right">79.743</td>
          <td style="text-align: right">574.52</td>
          <td style="text-align: right">~$0.05</td>
      </tr>
      <tr>
          <td>Claude Opus 4.6</td>
          <td>OpenRouter</td>
          <td style="text-align: right">16m</td>
          <td style="text-align: right">136.806</td>
          <td style="text-align: right">347.18</td>
          <td style="text-align: right">~$1.05</td>
      </tr>
      <tr>
          <td>Claude Sonnet 4.6</td>
          <td>OpenRouter</td>
          <td style="text-align: right">16m</td>
          <td style="text-align: right">127.067</td>
          <td style="text-align: right">532.26</td>
          <td style="text-align: right">~$0.63</td>
      </tr>
      <tr>
          <td>GLM 5</td>
          <td>OpenRouter</td>
          <td style="text-align: right">17m</td>
          <td style="text-align: right">59.378</td>
          <td style="text-align: right">400.01</td>
          <td style="text-align: right">~$0.11</td>
      </tr>
      <tr>
          <td>Qwen 3.6 Plus</td>
          <td>OpenRouter</td>
          <td style="text-align: right">17m</td>
          <td style="text-align: right">88.940</td>
          <td style="text-align: right">182.91</td>
          <td style="text-align: right">Grátis</td>
      </tr>
      <tr>
          <td>Claude Opus 4.7</td>
          <td>OpenRouter</td>
          <td style="text-align: right">18m</td>
          <td style="text-align: right">118.216</td>
          <td style="text-align: right">328.24</td>
          <td style="text-align: right">~$1.10</td>
      </tr>
      <tr>
          <td>GLM 5.1</td>
          <td>Z.AI direto</td>
          <td style="text-align: right">22m</td>
          <td style="text-align: right">81.666</td>
          <td style="text-align: right">166.62</td>
          <td style="text-align: right">~$0.13</td>
      </tr>
      <tr>
          <td>GPT 5.4 (Codex)</td>
          <td>Codex CLI</td>
          <td style="text-align: right">22m</td>
          <td style="text-align: right">7.643.800</td>
          <td style="text-align: right">5.824.56</td>
          <td style="text-align: right">~$16.00</td>
      </tr>
      <tr>
          <td>Qwen 3 Coder Next</td>
          <td>Local</td>
          <td style="text-align: right">17m</td>
          <td style="text-align: right">39.054</td>
          <td style="text-align: right">37.49</td>
          <td style="text-align: right">Eletricidade</td>
      </tr>
      <tr>
          <td>Qwen 3.5 35B</td>
          <td>Local</td>
          <td style="text-align: right">28m</td>
          <td style="text-align: right">76.919</td>
          <td style="text-align: right">46.03</td>
          <td style="text-align: right">Eletricidade</td>
      </tr>
      <tr>
          <td>Kimi K2.5</td>
          <td>OpenRouter</td>
          <td style="text-align: right">29m</td>
          <td style="text-align: right">63.638</td>
          <td style="text-align: right">160.14</td>
          <td style="text-align: right">~$0.07</td>
      </tr>
      <tr>
          <td>Step 3.5 Flash</td>
          <td>OpenRouter</td>
          <td style="text-align: right">38m</td>
          <td style="text-align: right">156.267</td>
          <td style="text-align: right">242.11</td>
          <td style="text-align: right">~$0.02</td>
      </tr>
      <tr>
          <td>Qwen 3.5 122B</td>
          <td>Local</td>
          <td style="text-align: right">43m</td>
          <td style="text-align: right">57.472</td>
          <td style="text-align: right">22.41</td>
          <td style="text-align: right">Eletricidade</td>
      </tr>
      <tr>
          <td>DeepSeek V3.2</td>
          <td>OpenRouter</td>
          <td style="text-align: right">60m</td>
          <td style="text-align: right">115.278</td>
          <td style="text-align: right">53.37</td>
          <td style="text-align: right">~$0.04</td>
      </tr>
  </tbody>
</table>
<p>O DeepSeek V3.2 é o mais lento apesar de ser cloud — não tem prompt caching, então reenvia o contexto completo a cada turno.</p>
<h3>Eficiência de tokens e cache<span class="hx:absolute hx:-mt-20" id="eficiência-de-tokens-e-cache"></span>
    <a href="#efici%c3%aancia-de-tokens-e-cache" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Modelos com prompt caching pagam muito menos tokens efetivos:</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/2026/04/05/llm-benchmark/en/token-efficiency.png" alt="Eficiência de tokens: cache vs novos"  loading="lazy" /></p>
<table>
  <thead>
      <tr>
          <th>Modelo</th>
          <th style="text-align: right">Tokens Totais</th>
          <th style="text-align: right">Cache Lido</th>
          <th style="text-align: right">Tokens Novos Efetivos</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Claude Sonnet 4.6</td>
          <td style="text-align: right">127.067</td>
          <td style="text-align: right">126.429</td>
          <td style="text-align: right">638</td>
      </tr>
      <tr>
          <td>Claude Opus 4.7</td>
          <td style="text-align: right">118.216</td>
          <td style="text-align: right">116.824</td>
          <td style="text-align: right">1.392</td>
      </tr>
      <tr>
          <td>Claude Opus 4.6</td>
          <td style="text-align: right">136.806</td>
          <td style="text-align: right">135.976</td>
          <td style="text-align: right">830</td>
      </tr>
      <tr>
          <td>GLM 5</td>
          <td style="text-align: right">59.378</td>
          <td style="text-align: right">58.240</td>
          <td style="text-align: right">1.138</td>
      </tr>
      <tr>
          <td>GLM 5.1</td>
          <td style="text-align: right">81.666</td>
          <td style="text-align: right">81.216</td>
          <td style="text-align: right">450</td>
      </tr>
      <tr>
          <td>Grok 4.20</td>
          <td style="text-align: right">63.457</td>
          <td style="text-align: right">62.400</td>
          <td style="text-align: right">1.057</td>
      </tr>
      <tr>
          <td>Gemini 3.1 Pro</td>
          <td style="text-align: right">104.034</td>
          <td style="text-align: right">98.129</td>
          <td style="text-align: right">5.905</td>
      </tr>
      <tr>
          <td>GPT 5.4 (Codex)</td>
          <td style="text-align: right">7.643.800</td>
          <td style="text-align: right">0</td>
          <td style="text-align: right">7.643.800</td>
      </tr>
      <tr>
          <td>DeepSeek V3.2</td>
          <td style="text-align: right">115.278</td>
          <td style="text-align: right">0</td>
          <td style="text-align: right">115.278</td>
      </tr>
      <tr>
          <td>Kimi K2.5</td>
          <td style="text-align: right">63.638</td>
          <td style="text-align: right">0</td>
          <td style="text-align: right">63.638</td>
      </tr>
  </tbody>
</table>
<h2>Velocidade: o abismo entre cloud e local<span class="hx:absolute hx:-mt-20" id="velocidade-o-abismo-entre-cloud-e-local"></span>
    <a href="#velocidade-o-abismo-entre-cloud-e-local" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Tem um aspecto que as tabelas de custo escondem: velocidade de inferência. E a diferença é brutal.</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/2026/04/05/llm-benchmark/en/speed-comparison.png" alt="Velocidade de inferência por modelo"  loading="lazy" /></p>
<p>O Claude Sonnet gera 532 tok/s. O Qwen 3.5 122B rodando local no meu Minisforum (AMD Strix Halo) gera 22 tok/s. São 24x de diferença. Na prática, o que o Sonnet faz em 16 minutos, o Qwen 3.5 122B leva 43 minutos. O Qwen 3 Coder Next a 37 tok/s é o mais rápido dos locais no Strix e mesmo assim é 14x mais lento que o Sonnet.</p>
<p>E não é só tempo de relógio. Quando você está num loop de coding interativo — pede uma mudança, espera o output, testa, pede outra — a velocidade do modelo define seu ritmo. A 37 tok/s, cada resposta longa te faz esperar 30-60 segundos. A 530 tok/s, aparece quase instantaneamente. Ao longo de um dia, você sente.</p>
<p>O DeepSeek V3.2 é um caso curioso: é cloud mas roda a 53 tok/s, mais lento que o Qwen 3.5 35B local no Strix (46 tok/s). O motivo é que o DeepSeek não tem prompt caching — reenvia o contexto completo a cada turno, estrangulando o throughput. Pagar por um modelo cloud mais lento que rodar local não faz sentido nenhum.</p>
<p>Modelos locais são grátis em tokens, mas pagam em tempo. No AMD Strix, essa conta era inviável pra todos os Qwen que testei: dois minutos esperando uma resposta longa, multiplicado por 50 turnos, é tarde inteira. Mas isso muda quando o hardware muda, e foi por isso que rerodei a parte local do benchmark numa máquina diferente.</p>
<h2>AMD Strix Halo vs NVIDIA RTX 5090: o que muda quando a banda de memória dobra<span class="hx:absolute hx:-mt-20" id="amd-strix-halo-vs-nvidia-rtx-5090-o-que-muda-quando-a-banda-de-memória-dobra"></span>
    <a href="#amd-strix-halo-vs-nvidia-rtx-5090-o-que-muda-quando-a-banda-de-mem%c3%b3ria-dobra" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Pra checar se o gargalo era hardware ou modelo, peguei os mesmos modelos Qwen e refiz o benchmark numa workstation com NVIDIA RTX 5090 (Blackwell, 32 GB GDDR7, 1.792 GB/s de banda). Os números mudam de uma forma que vale ver com calma.</p>
<table>
  <thead>
      <tr>
          <th>Modelo</th>
          <th style="text-align: right">AMD Strix (LPDDR5x)</th>
          <th style="text-align: right">NVIDIA 5090 (GDDR7)</th>
          <th style="text-align: right">Speedup</th>
          <th style="text-align: right">Tempo total no 5090</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Qwen 3 32B (denso)</td>
          <td style="text-align: right">7 tok/s</td>
          <td style="text-align: right">69 tok/s</td>
          <td style="text-align: right">~10x</td>
          <td style="text-align: right">4 min</td>
      </tr>
      <tr>
          <td>Qwen 3 Coder 30B (Coder)</td>
          <td style="text-align: right">37 tok/s</td>
          <td style="text-align: right">145 tok/s</td>
          <td style="text-align: right">~4x</td>
          <td style="text-align: right">6 min</td>
      </tr>
      <tr>
          <td>Qwen 3.5 35B-A3B (MoE)</td>
          <td style="text-align: right">46 tok/s</td>
          <td style="text-align: right"><strong>273 tok/s</strong></td>
          <td style="text-align: right">~6x</td>
          <td style="text-align: right">5 min</td>
      </tr>
      <tr>
          <td>Qwen 3.5 27B Claude (distilado)</td>
          <td style="text-align: right">timeout 90m</td>
          <td style="text-align: right">129 tok/s</td>
          <td style="text-align: right">n/a</td>
          <td style="text-align: right">12 min</td>
      </tr>
      <tr>
          <td>Gemma 4 31B</td>
          <td style="text-align: right">(não testei no Strix)</td>
          <td style="text-align: right">213 tok/s</td>
          <td style="text-align: right">n/a</td>
          <td style="text-align: right">8 min</td>
      </tr>
      <tr>
          <td>Qwen 2.5 Coder 32B</td>
          <td style="text-align: right">(não testei no Strix)</td>
          <td style="text-align: right">2.86 tok/s</td>
          <td style="text-align: right">n/a</td>
          <td style="text-align: right">timeout 90m</td>
      </tr>
  </tbody>
</table>
<p>Pra contextualizar essas velocidades, lembra que no cloud o Sonnet roda a 532 tok/s, o Opus a 347 tok/s, o Step 3.5 Flash a 242 tok/s, o Gemini 3.1 Pro a 128 tok/s e o Kimi K2.5 a 160 tok/s. O Qwen 3.5 35B-A3B na 5090, a 273 tok/s, está na faixa do Step 3.5 Flash, mais rápido que Gemini, Kimi e GLM 5.1. O Qwen 3 Coder 30B a 145 tok/s está na faixa do Gemini. A frase clássica &ldquo;modelo local é dez vezes mais lento que cloud&rdquo; parou de valer no momento que a 5090 entrou na conta.</p>
<p>A consequência prática é que o argumento &ldquo;tempo é dinheiro&rdquo; muda de figura. No Strix, &ldquo;esperar 1 hora pra um Qwen 3.5 122B fazer o que o Sonnet faz em 16 minutos&rdquo; é pura perda. Na 5090, esperar 5 minutos pra o Qwen 3.5 35B-A3B fazer o trabalho, mais 10-15 minutos pra você fazer 1-2 prompts de correção, dá um total na faixa de 20-25 minutos. O Sonnet faz em 16 minutos com zero correção. A diferença ficou pequena o suficiente pra que, se custo importa muito, valha a pena.</p>
<p>A pegadinha: pra que isso valha a pena, o modelo precisa estar perto o suficiente da resposta certa pra que 1-2 prompts de correção resolvam. Quando o erro é do tipo &ldquo;o modelo decidiu não usar o gem que pedi e devolveu uma string mockada&rdquo;, como fez o Qwen 3 Coder 30B, nenhum prompt de correção fácil arruma. Isso é refazer.</p>
<h3>Antes que você gaste em hardware achando que é a saída<span class="hx:absolute hx:-mt-20" id="antes-que-você-gaste-em-hardware-achando-que-é-a-saída"></span>
    <a href="#antes-que-voc%c3%aa-gaste-em-hardware-achando-que-%c3%a9-a-sa%c3%adda" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Tenho que dar um aviso, porque é o erro de compra mais comum que vejo agora. Toda hora aparece alguém dizendo que vai pegar um Ryzen AI Max porque tem 128 GB de memória unificada e isso &ldquo;permite rodar modelos enormes&rdquo;. Tecnicamente, sim — o modelo cabe. Na prática, é quase inutilizável. A memória é LPDDR5x a 256 GB/s, sete vezes mais lenta que a GDDR7 da 5090. O que cabe não roda em velocidade humana. Meu próprio Strix com Qwen 3.5 122B chegou a 22 tok/s e o run levou 43 minutos. Pra fazer qualquer coisa de verdade no dia a dia, isso é impraticável.</p>
<p>A 5090 é claramente superior, e começa a fazer sentido até pra modelos menores justamente por causa da banda de memória. Mac Studio com memória unificada de alta velocidade (até 800 GB/s nos M4 Ultra) é a outra opção viável, e custa proporcionalmente o mesmo. Mas nenhuma das duas chega perto de bater os modelos comerciais em qualidade — e o preço de Claude, GPT ou GLM por token, somado à velocidade brutal de inferência deles, faz a conta ser difícil de justificar pra quem não é entusiasta ou pesquisador. Hardware caro de IA local é hobby de fim de semana, ferramenta pra quem precisa rodar offline por compliance, ou playground de pesquisa. Pra trabalho de produção dia após dia, no momento atual, cloud ainda é a escolha racional. Ryzen AI Max com 128 GB pode parecer tentador na planilha, mas se a ideia é coding agent sério, é dinheiro mal gasto.</p>
<h2>A família Qwen: Coder vs Geral, distilação, e por que nada é bala de prata<span class="hx:absolute hx:-mt-20" id="a-família-qwen-coder-vs-geral-distilação-e-por-que-nada-é-bala-de-prata"></span>
    <a href="#a-fam%c3%adlia-qwen-coder-vs-geral-distila%c3%a7%c3%a3o-e-por-que-nada-%c3%a9-bala-de-prata" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Com tantos Qwens diferentes rodando nessa rerodada, dá pra fazer uma análise mais focada. O que aprendi é capaz de surpreender quem segue benchmark de modelo no Twitter.</p>
<h3>Antes de ir aos resultados: o que é quantização e o que é distilação<span class="hx:absolute hx:-mt-20" id="antes-de-ir-aos-resultados-o-que-é-quantização-e-o-que-é-distilação"></span>
    <a href="#antes-de-ir-aos-resultados-o-que-%c3%a9-quantiza%c3%a7%c3%a3o-e-o-que-%c3%a9-distila%c3%a7%c3%a3o" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Os dois conceitos aparecem o tempo todo nessa discussão e merecem uma explicação curta.</p>
<p><strong>Quantização</strong> é a técnica de comprimir os pesos do modelo pra ocupar menos memória. Um modelo treinado em FP16 (16 bits por peso) pode ser quantizado pra Q8 (8 bits), Q4 (4 bits), Q3_K_M (3 bits, mas com agrupamentos médios), e por aí vai. Cada passo divide o tamanho do modelo em disco e na VRAM, ao custo de uma certa perda de precisão. Q8 é praticamente lossless. Q4 já perde alguma coisa mensurável. Q3 perde mais. Q2 é o limite onde o modelo começa a falar besteira de verdade. A regra de ouro é que pra coding e raciocínio multi-step, vale ficar em Q4 ou acima. Q3_K_M é o mínimo que ainda funciona pra muitos modelos, e é o que cabe num 27B na 5090 com 128k de contexto.</p>
<p>A surpresa do meu teste, e olha que isso vai contra o consenso, é que a quantização não foi o gargalo aqui. Rodei o Qwen 3.5 27B Claude-distilado em duas versões: Q8 no AMD Strix (~27 GB de pesos) e Q3_K_M na 5090 (~12 GB de pesos). Ambos alucinaram exatamente as mesmas APIs falsas do RubyLLM. O Q3_K_M até gerou um Gemfile mais limpo. A limitação do modelo estava no que esses pesos sabem, não na precisão com que eles foram comprimidos.</p>
<p><strong>Distilação</strong> é a técnica de treinar um modelo menor (o &ldquo;aluno&rdquo;) pra imitar a saída ou o comportamento de um modelo maior (o &ldquo;professor&rdquo;). A versão clássica é distilação de logits — o aluno aprende a aproximar as distribuições de probabilidade do professor. A versão moderna, mais popular pra coding agents, é distilação de <strong>traços de raciocínio</strong> (reasoning traces): você pega cadeias de pensamento do modelo grande pra problemas reais e treina o menor a reproduzir o mesmo estilo de raciocínio.</p>
<p>O hype do momento é distilar Claude e GPT em modelos open source. A promessa é que dá pra ter &ldquo;Claude-em-casa&rdquo; rodando local. Eu queria testar isso, e foi por isso que coloquei no benchmark o <strong><a href="https://huggingface.co/Jackrong/Qwen3.5-27B-Claude-4.6-Opus-Reasoning-Distilled"target="_blank" rel="noopener">Jackrong&rsquo;s Qwen 3.5 27B distilado do Claude 4.6 Opus</a></strong>. Se algum modelo open source ia conseguir usar o RubyLLM corretamente, era essa aposta — afinal, no benchmark inteiro, Claude e GLM 5 são os únicos que acertam a API.</p>
<h3>O que o Claude-distilled aprendeu (e o que não aprendeu)<span class="hx:absolute hx:-mt-20" id="o-que-o-claude-distilled-aprendeu-e-o-que-não-aprendeu"></span>
    <a href="#o-que-o-claude-distilled-aprendeu-e-o-que-n%c3%a3o-aprendeu" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Rodei a mesma distilação duas vezes: uma em Q8 no AMD Strix (que estourou no timeout de 90 minutos), outra em Q3_K_M na 5090 (completou em 12 minutos). Em ambas, o resultado é a mesma frustração elegante.</p>
<p>O código que sai parece Claude. Tem <code># frozen_string_literal: true</code> no topo de todo arquivo. Tem uma classe <code>Response</code> separada como value object com leitores de atributo explícitos. Tem separação clara entre service, controller e model. Tem comentário de doc no topo de cada arquivo. Comenta corretamente coisas como <code>active_record</code>, <code>active_job</code> e <code>action_mailer</code> no <code>application.rb</code>. Tem <code>case</code> defensivo tentando múltiplos formatos de retorno. Estilisticamente, é Claude.</p>
<p>Funcionalmente, é alucinação completa do RubyLLM. Olha o serviço gerado pelo run da 5090:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="no">RubyLLM</span><span class="o">::</span><span class="no">Chat</span><span class="o">.</span><span class="n">new</span><span class="o">.</span><span class="n">with_model</span><span class="p">(</span><span class="vi">@model</span><span class="p">)</span> <span class="k">do</span> <span class="o">|</span><span class="n">chat</span><span class="o">|</span>
</span></span><span class="line"><span class="cl">  <span class="n">conversation_history</span><span class="o">.</span><span class="n">each</span> <span class="k">do</span> <span class="o">|</span><span class="n">msg</span><span class="o">|</span>
</span></span><span class="line"><span class="cl">    <span class="n">chat</span><span class="o">.</span><span class="n">add_message</span><span class="p">(</span><span class="ss">role</span><span class="p">:</span> <span class="ss">:user</span><span class="p">,</span> <span class="ss">content</span><span class="p">:</span> <span class="n">msg</span><span class="o">[</span><span class="ss">:content</span><span class="o">]</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="n">response</span> <span class="o">=</span> <span class="n">chat</span><span class="o">.</span><span class="n">ask</span><span class="p">(</span><span class="n">message</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="no">Response</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="ss">content</span><span class="p">:</span> <span class="n">response</span><span class="o">.</span><span class="n">text</span><span class="p">,</span> <span class="ss">usage</span><span class="p">:</span> <span class="n">build_usage</span><span class="p">(</span><span class="n">response</span><span class="p">))</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Cada primitiva nesse código é inventada:</p>
<ul>
<li><code>RubyLLM::Chat.new</code> — o construtor não é público, o entry correto é <code>RubyLLM.chat(model:)</code></li>
<li><code>.with_model(@model) do |chat| ... end</code> — não existe API de bloco assim</li>
<li><code>chat.add_message(role:, content:)</code> — não existe</li>
<li><code>response.text</code> — a API real expõe <code>response.content</code></li>
<li><code>response.usage.prompt_tokens</code> — o objeto não tem essa forma</li>
</ul>
<p>Isso vai estourar <code>NoMethodError</code> na primeira request. O initializer também tenta <code>config.openrouter_api_base=</code> que não existe no <code>RubyLLM.configure</code>, então o app provavelmente nem boota.</p>
<p>A versão Q8 no AMD Strix faz exatamente o mesmo, com uma diferença: a chamada de entrada é <code>RubyLLM.chat(model:, provider: :openrouter)</code> — o entry point está certo, mas o <code>provider:</code> é inventado e logo em seguida vem o mesmo <code>chat.add_message(role:, content:)</code> falso. Pior, o Gemfile do run de 90 minutos lista <code>gem &quot;ruby-openai&quot;</code> (gem errada!), <code>gem &quot;minitest&quot;, &quot;~&gt; 6.0&quot;</code> (não existe minitest 6.0) e <code>gem &quot;tailwindcss&quot;</code> (nome errado, é <code>tailwindcss-rails</code>). O Gemfile não inclui o gem que o próprio service tenta usar.</p>
<p>Pra comparar, olha o Claude Opus 4.6 baseline real, no mesmo benchmark, acertando tudo:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="vi">@chat</span> <span class="o">=</span> <span class="no">RubyLLM</span><span class="o">.</span><span class="n">chat</span><span class="p">(</span><span class="ss">model</span><span class="p">:</span> <span class="n">model_id</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">response</span> <span class="o">=</span> <span class="vi">@chat</span><span class="o">.</span><span class="n">ask</span><span class="p">(</span><span class="n">message</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">response</span><span class="o">.</span><span class="n">content</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Doze linhas no service inteiro. Sem alucinação. Inclui streaming via bloco. O modelo distilado produziu o triplo do volume de código e errou a API.</p>
<p>A leitura honesta é que distilação transferiu uma camada e parou. A camada que veio junto foi a do estilo: organização do código, comentários, estrutura de classes, ordem das coisas. A camada que ficou pra trás foi a da memória factual sobre bibliotecas específicas. Isso faz sentido quando você pensa: traços de raciocínio do Claude, mesmo escritos com calma, raramente contêm referências repetidas a <code>chat.ask(msg).content</code> num gem Ruby obscuro. O aluno só aprende o que o professor repete, e Claude nunca teve motivo pra ficar sussurrando &ldquo;use ask, não use complete&rdquo; ao longo das suas cadeias de pensamento. Conhecimento de API de biblioteca é memória binária de recall, do tipo que ou está nos pesos ou não está. Decompor isso em passos de raciocínio é impossível porque não é raciocínio, é só lembrança crua.</p>
<p>Pra fechar com a recomendação prática: se você precisa que o modelo realmente use o RubyLLM, ou qualquer biblioteca menos popular que seja, distilação de Claude não te salva. Use Claude de verdade ou GLM 5. Os &ldquo;Claude-stand-ins&rdquo; open source vão falhar do mesmo jeito que o Qwen base falharia, só que com letrinha mais bonita.</p>
<h3>Coder vs Geral: a surpresa dos modelos &ldquo;pra coding&rdquo;<span class="hx:absolute hx:-mt-20" id="coder-vs-geral-a-surpresa-dos-modelos-pra-coding"></span>
    <a href="#coder-vs-geral-a-surpresa-dos-modelos-pra-coding" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>A intuição de quase todo mundo é que modelos com &ldquo;Coder&rdquo; no nome são os melhores pra programação. Faz sentido, foram fine-tunados especificamente em código. Mas no benchmark, foi exatamente o oposto.</p>
<table>
  <thead>
      <tr>
          <th>Modelo</th>
          <th>Tipo</th>
          <th>Hardware</th>
          <th style="text-align: right">Tempo</th>
          <th>Resultado</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Qwen 3.5 35B-A3B</td>
          <td>Geral (MoE)</td>
          <td>5090</td>
          <td style="text-align: right">5 min</td>
          <td>Roda Rails, alucina <code>add_message</code>/<code>complete</code> (1-2 follow-ups arrumam)</td>
      </tr>
      <tr>
          <td>Qwen 3 Coder 30B</td>
          <td>Coder</td>
          <td>5090</td>
          <td style="text-align: right">6 min</td>
          <td>Devolveu uma string mockada hardcoded em vez de chamar o RubyLLM</td>
      </tr>
      <tr>
          <td>Qwen 2.5 Coder 32B</td>
          <td>Coder</td>
          <td>5090</td>
          <td style="text-align: right">timeout 90m</td>
          <td>Zero arquivos, modelo travou</td>
      </tr>
      <tr>
          <td>Qwen 3 32B</td>
          <td>Geral</td>
          <td>5090</td>
          <td style="text-align: right">4 min</td>
          <td>Scaffold parcial, errors</td>
      </tr>
      <tr>
          <td>Qwen 3.5 27B Claude-distilado</td>
          <td>Geral + distilado</td>
          <td>5090</td>
          <td style="text-align: right">12 min</td>
          <td>Roda Rails, alucina API toda</td>
      </tr>
      <tr>
          <td>Qwen 3.5 27B Sushi Coder RL</td>
          <td>Coder (RL)</td>
          <td>5090</td>
          <td style="text-align: right">6 min</td>
          <td>Falha de infra, não pôde testar</td>
      </tr>
  </tbody>
</table>
<p>Dos três Coders dedicados, dois falharam catastroficamente (timeout total e mock string hardcoded) e um nem rodou direito por bug de infra. Já o Qwen 3.5 35B-A3B, que é o modelo geral da linha (não o Coder), foi o que mais perto chegou de algo aproveitável: 5 minutos de execução, projeto Rails reconhecível, e o problema dele se conserta em 1-2 prompts.</p>
<p>O Qwen 3 Coder 30B é particularmente decepcionante. Ele passou longe de qualquer tentativa séria de usar a API: o controller que ele gerou tem isso:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Api</span><span class="o">::</span><span class="no">V1</span><span class="o">::</span><span class="no">MessagesController</span> <span class="o">&lt;</span> <span class="no">ApplicationController</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">create</span>
</span></span><span class="line"><span class="cl">    <span class="n">render</span> <span class="ss">json</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="ss">response</span><span class="p">:</span> <span class="s2">&#34;This is a mock response. In a real implementation, this would connect to RubyLLM with Claude Sonnet via OpenRouter.&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>O Gemfile lista <code>gem &quot;ruby_llm&quot;</code> mas nada importa. O service layer é inexistente. O modelo decidiu que era mais fácil devolver uma string fake e seguir a vida. Isso é Tier 3 garbage de uma forma que nem prompt de correção arruma — tem que mandar reescrever do zero.</p>
<p>O Qwen 2.5 Coder 32B é ainda pior: 90 minutos rodando, zero arquivos. O <code>opencode-output.ndjson</code> de 1.8 MB mostra o modelo girando sem conseguir escrever nada. Provavelmente entrou em loop de planejamento sem nunca chamar as ferramentas de write. Total perda de slot.</p>
<p>Por que os &ldquo;Coder&rdquo; Qwens foram tão mal? Minha leitura é que o fine-tuning específico pra coding deles foi treinado em problemas mais isolados (Codeforces, Leetcode, snippets curtos), longe dos fluxos agentic com tool calling de longa duração. O modelo geral Qwen 3.5 35B-A3B tem treinamento mais amplo e se vira melhor com a parte de orquestração. A intuição popular &ldquo;Coder = melhor pra coding agent&rdquo; tá errada pra esse tipo de tarefa. O caso de uso onde os Coders se saem bem é &ldquo;completar uma função isolada&rdquo;, que é exatamente pro que eles foram treinados, e isso é uma fração pequena do que um coding agent faz no dia a dia.</p>
<h3>A pergunta que eu queria responder<span class="hx:absolute hx:-mt-20" id="a-pergunta-que-eu-queria-responder"></span>
    <a href="#a-pergunta-que-eu-queria-responder" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Era essa: rodando local na 5090, qual modelo Qwen vale o 1-2 prompts de correção pra entregar código que funcione?</p>
<p>A resposta honesta é: só o Qwen 3.5 35B-A3B, e talvez o Claude-distilado se você não se importa de gastar 12 minutos a mais.</p>
<ul>
<li>Qwen 3.5 35B-A3B na 5090: 5 minutos, entry point certo (<code>RubyLLM.chat(model:)</code>), erros nas chamadas seguintes. Total realista até funcionar: na faixa de 15-20 minutos com 1-2 follow-ups. Bate cloud OSS na conta de custo.</li>
<li>Qwen 3.5 27B Claude-distilado na 5090: 12 minutos, alucinação mais profunda (entry point inventado também). Total realista: 25-30 minutos com 2-3 follow-ups. Ainda compete em custo, e perde em tempo absoluto pro Claude real.</li>
<li>Os outros (Coder 30B, Coder 2.5 32B, 3 32B): não recompensam o tempo de correção. Cada um tem um problema estrutural que pede reescrita inteira do zero.</li>
</ul>
<p>Pra quem tem hardware nessa categoria e quer fugir do vendor lock-in da Anthropic, agora dá. Não dava na 5090 do ano passado, no Strix Halo então, esquece. Em 2026, na NVIDIA Blackwell, com o modelo certo, dá. Pra quem tem hardware de banda baixa (LPDDR5x, DDR4, DDR5), continua sendo perda de tempo: o relógio sozinho derruba qualquer plano de tornar isso prático.</p>
<h3>Qwen 3.6: o que mudou em relação ao 3.5<span class="hx:absolute hx:-mt-20" id="qwen-36-o-que-mudou-em-relação-ao-35"></span>
    <a href="#qwen-36-o-que-mudou-em-rela%c3%a7%c3%a3o-ao-35" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Testamos dois sabores do Qwen 3.6: o <strong>3.6 Plus</strong> (cloud, OpenRouter, grátis) e o <strong>3.6 35B</strong> (local, NVIDIA 5090, Q3_K_M).</p>
<p>O Qwen 3.6 Plus (cloud) completou em 17 minutos com 88.940 tokens e 9/9 no checklist de artefatos. Completou rápido e de graça. Mas o serviço gerado usa <code>chat.add_message()</code>, método que não existe no RubyLLM. A primeira mensagem funciona, a segunda quebra. O mesmo problema do 3.5.</p>
<p>O Qwen 3.6 35B (local, 5090) é mais interessante. Completou em 4.7 minutos a 240 tok/s, 169 arquivos, entry point <code>RubyLLM.chat(model:, provider:)</code> correto e <code>chat.ask(message)</code> correto. O bug é mais sutil: retorna <code>response</code> em vez de <code>response.content</code> e não faz replay de histórico. Correção de 1 linha. Isso é uma melhoria real sobre o Qwen 3.5 35B-A3B (que alucinava <code>add_message</code> e <code>complete</code>). É o resultado Qwen mais limpo que vimos até agora.</p>
<table>
  <thead>
      <tr>
          <th>Modelo</th>
          <th>Versão</th>
          <th>Hardware</th>
          <th style="text-align: center">API correta?</th>
          <th style="text-align: center">Multi-turn funciona?</th>
          <th style="text-align: right">Tempo</th>
          <th style="text-align: right">Tok/s</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Qwen 3.5 35B-A3B</td>
          <td>3.5</td>
          <td>NVIDIA 5090</td>
          <td style="text-align: center">Entry point sim, <code>add_message</code>/<code>complete</code> não</td>
          <td style="text-align: center">Não</td>
          <td style="text-align: right">5m</td>
          <td style="text-align: right">273</td>
      </tr>
      <tr>
          <td>Qwen 3.5 27B Claude-distilado</td>
          <td>3.5</td>
          <td>NVIDIA 5090</td>
          <td style="text-align: center">Não (entry point inventado)</td>
          <td style="text-align: center">Não</td>
          <td style="text-align: right">12m</td>
          <td style="text-align: right">129</td>
      </tr>
      <tr>
          <td>Qwen 3.6 35B</td>
          <td>3.6</td>
          <td>NVIDIA 5090</td>
          <td style="text-align: center">Entry point sim, <code>chat.ask</code> sim, falta <code>.content</code></td>
          <td style="text-align: center">Não (sem replay)</td>
          <td style="text-align: right">5m</td>
          <td style="text-align: right">240</td>
      </tr>
      <tr>
          <td>Qwen 3.6 Plus</td>
          <td>3.6</td>
          <td>Cloud</td>
          <td style="text-align: center">Entry point sim, <code>add_message</code> não</td>
          <td style="text-align: center">Não</td>
          <td style="text-align: right">17m</td>
          <td style="text-align: right">183</td>
      </tr>
  </tbody>
</table>
<p>O gap diminuiu mas não fechou. O 3.6 35B local está mais perto de funcionar que qualquer Qwen anterior — o bug é um <code>.content</code> esquecido, não uma API inteira inventada. Mas continua sem multi-turn funcional de primeira. Na prática, o Qwen 3.6 35B local sobe do Tier 3 pro Tier 2: é o modelo open source local que mais perto chega de entregar código correto no primeiro try, a uma correção de distância.</p>
<h2>O Deep Code Review: Sonnet vs GLM 5 vs Gemini vs Kimi vs MiniMax<span class="hx:absolute hx:-mt-20" id="o-deep-code-review-sonnet-vs-glm-5-vs-gemini-vs-kimi-vs-minimax"></span>
    <a href="#o-deep-code-review-sonnet-vs-glm-5-vs-gemini-vs-kimi-vs-minimax" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>As tabelas acima medem completude estrutural. Mas o projeto funciona? Fiz code review detalhado dos modelos que completaram o benchmark.</p>
<p><strong>Claude Sonnet 4.6 — funciona e é o mais completo.</strong> Respostas síncronas via Turbo Stream. Histórico de chat persistido em session cookie com replay completo das mensagens anteriores a cada request. Mocking correto do LLM nos testes com mocha (30 testes em 328 linhas). Lógica de LLM extraída pra um <code>LlmChatService</code> separado. Views decompostas em 9 partials. Problemas menores: constante de modelo duplicada, leak no event listener do auto-resize. Nenhum é blocker. Dos projetos gerados, é o que mais se aproxima de algo que você colocaria em produção.</p>
<p><strong>GLM 5 — funciona, mas é o mínimo viável.</strong> Usa a API correta (<code>RubyLLM.chat(model:)</code> depois <code>.ask()</code>), faz mocking com mocha nos testes. Só que o projeto é bem mais enxuto que o do Sonnet: controller de 21 linhas (vs 52 do Sonnet), sem service layer (lógica LLM inline no controller), sem persistência de histórico de chat, cada mensagem tratada isoladamente. A primeira mensagem funciona, mas o app não mantém contexto de conversação, então não dá pra ter um diálogo multi-turn. Os testes existem (7 métodos) mas são esqueléticos: o <code>ruby_llm_test.rb</code> só checa se o módulo está carregado, o <code>chat_flow_test.rb</code> é cópia do controller test. O Dockerfile, por outro lado, é o melhor dos quatro: multi-stage, non-root, jemalloc. Mas como app de chat? É mais uma prova de conceito do que algo funcional. Detalhe engraçado: o README diz &ldquo;Powered by Claude Sonnet 4&rdquo; em vez do modelo que realmente gerou o projeto.</p>
<p><strong>Gemini 3.1 Pro — o mais rápido, mas tropeça na API.</strong> Completou em 14 minutos, o mais rápido junto com o MiniMax. O código Rails em si é bem feito: usa <code>Rails.cache</code> com session ID e expiração de 2 horas pra manter estado (em vez de banco de dados), Turbo Streams bem integrados, Stimulus controller pra scroll automático, e o Dockerfile é o melhor do grupo (multi-stage, non-root, jemalloc). O problema é o de sempre: usa <code>RubyLLM::Chat.new()</code> em vez de <code>RubyLLM.chat()</code>, e chama <code>add_message()</code> que não existe. O app boota, o Docker roda, o health check passa, mas a primeira mensagem de chat dá 500. Os testes (5 métodos) mockam com um <code>FakeChat</code> que replica a assinatura errada, então passam. É frustrante porque o resto do código é o mais &ldquo;Rails way&rdquo; dos modelos que não são Anthropic. Corrigir seriam 3 linhas, mas o benchmark mede o que sai de primeira.</p>
<p><strong>Kimi K2.5 — ambicioso mas quebrado.</strong> Tentou a arquitetura mais sofisticada: ActionCable streaming, modelos configuráveis, dual Dockerfiles, 37 testes em 374 linhas. Problema: o streaming depende do ActionCable, que está comentado no <code>config/application.rb</code>. O guard <code>return unless defined?(ActionCable)</code> faz o método não fazer nada. O assistente nunca responde. O Stimulus controller tem bug de escopo: o <code>submitTarget</code> referencia um botão fora da subtree do controller. Storage thread-unsafe com hash em variável de classe. O Kimi escreveu mais testes que qualquer outro modelo (37), mas nenhum mocka as chamadas LLM — então os testes passam sem provar que a funcionalidade funciona de verdade.</p>
<p><strong>Grok 4.20 — rápido e errado.</strong> Foi o mais rápido do benchmark inteiro: 8 minutos, 412 tok/s. Só que foi rápido porque cortou caminho. O prompt pedia explicitamente o gem <code>ruby_llm</code>, e o Grok ignorou. Foi direto no <code>OpenAI::Client</code> da gem <code>ruby-openai</code> apontando pra URL do OpenRouter. Tecnicamente a primeira mensagem volta, então sim, &ldquo;funciona&rdquo;. Mas é o mesmo truque do Step 3.5 Flash e do Qwen 3.5 122B: pula a peça que tava sendo testada. Sem histórico, controller de 33 linhas chamando o cliente HTTP no braço, dois testes, nenhum mock de verdade. Foi rápido porque fez menos do que pediram.</p>
<p><strong>MiniMax M2.7 — parece certo, crasha.</strong> Chama <code>RubyLLM.chat(model: '...', messages: [...])</code> — essa assinatura não existe. Sem persistência de mensagens. HTML duplicado (DOCTYPE dentro do layout). master.key commitada. E os testes? Mockam a API errada, então passam mas não provam nada.</p>
<p>Resumo do code review:</p>
<table>
  <thead>
      <tr>
          <th>Aspecto</th>
          <th style="text-align: center">Sonnet 4.6</th>
          <th style="text-align: center">GLM 5</th>
          <th style="text-align: center">Gemini 3.1 Pro</th>
          <th style="text-align: center">Kimi K2.5</th>
          <th style="text-align: center">MiniMax M2.7</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>API correta</td>
          <td style="text-align: center">Sim</td>
          <td style="text-align: center">Sim</td>
          <td style="text-align: center">Não</td>
          <td style="text-align: center">Não</td>
          <td style="text-align: center">Não</td>
      </tr>
      <tr>
          <td>Histórico de chat</td>
          <td style="text-align: center">Session cookie</td>
          <td style="text-align: center">Nenhum</td>
          <td style="text-align: center">Rails.cache (2h)</td>
          <td style="text-align: center">Broken (ActionCable off)</td>
          <td style="text-align: center">Nenhum</td>
      </tr>
      <tr>
          <td>Service layer</td>
          <td style="text-align: center">LlmChatService</td>
          <td style="text-align: center">Inline no controller</td>
          <td style="text-align: center">LlmService</td>
          <td style="text-align: center">LlmService</td>
          <td style="text-align: center">ChatService (API errada)</td>
      </tr>
      <tr>
          <td>Testes (métodos)</td>
          <td style="text-align: center">30</td>
          <td style="text-align: center">7</td>
          <td style="text-align: center">5</td>
          <td style="text-align: center">37</td>
          <td style="text-align: center">12</td>
      </tr>
      <tr>
          <td>Mocking LLM</td>
          <td style="text-align: center">Sim (mocha)</td>
          <td style="text-align: center">Sim (mocha)</td>
          <td style="text-align: center">FakeChat (API errada)</td>
          <td style="text-align: center">Não</td>
          <td style="text-align: center">Mock da API errada</td>
      </tr>
      <tr>
          <td>Dockerfile</td>
          <td style="text-align: center">Multi-stage</td>
          <td style="text-align: center">Multi-stage + jemalloc</td>
          <td style="text-align: center">Multi-stage + jemalloc</td>
          <td style="text-align: center">Dual (dev/prod)</td>
          <td style="text-align: center">Single-stage</td>
      </tr>
      <tr>
          <td>Roda de verdade?</td>
          <td style="text-align: center">Sim</td>
          <td style="text-align: center">Sim (sem histórico)</td>
          <td style="text-align: center">Não (500 no chat)</td>
          <td style="text-align: center">Não</td>
          <td style="text-align: center">Não</td>
      </tr>
  </tbody>
</table>
<h3>GLM 5 vs GLM 5.1: o que mudou<span class="hx:absolute hx:-mt-20" id="glm-5-vs-glm-51-o-que-mudou"></span>
    <a href="#glm-5-vs-glm-51-o-que-mudou" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>O GLM 5 foi um dos poucos modelos que cuspiu código funcional de primeira, então era óbvio testar a versão nova. Um detalhe que importa antes dos números: o GLM 5 rodou via OpenRouter, o GLM 5.1 ainda não tava lá quando rodei o teste, então usei a API direta da Z.AI. Provedor diferente, infra diferente, cache diferente. Os números abaixo são referência, não medida exata.</p>
<table>
  <thead>
      <tr>
          <th>Aspecto</th>
          <th>GLM 5</th>
          <th>GLM 5.1</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Provedor</td>
          <td>OpenRouter</td>
          <td>Z.AI direto</td>
      </tr>
      <tr>
          <td>Tempo total</td>
          <td>17m</td>
          <td>22m</td>
      </tr>
      <tr>
          <td>Tok/s (final phase)</td>
          <td>400</td>
          <td>167</td>
      </tr>
      <tr>
          <td>Tokens efetivos novos</td>
          <td>1.138</td>
          <td>450</td>
      </tr>
      <tr>
          <td>Cache lido</td>
          <td>58.240</td>
          <td>81.216</td>
      </tr>
      <tr>
          <td>API RubyLLM correta</td>
          <td>Sim</td>
          <td>Sim</td>
      </tr>
      <tr>
          <td>Mocking nos testes</td>
          <td>Sim (mocha)</td>
          <td>Sim</td>
      </tr>
      <tr>
          <td>Testes</td>
          <td>7</td>
          <td>24</td>
      </tr>
      <tr>
          <td>Histórico de chat</td>
          <td>Não</td>
          <td>Sim (in-memory)</td>
      </tr>
      <tr>
          <td>Service layer</td>
          <td>Inline no controller</td>
          <td><code>ChatSession</code> model com <code>add_user_message</code>/<code>add_assistant_message</code></td>
      </tr>
  </tbody>
</table>
<p>O projeto do GLM 5.1 ficou bem mais completo. 24 testes contra 7. Separação real entre <code>ChatSession</code>, <code>ChatMessage</code> e o controller, em vez do GLM 5 botando tudo inline. Histórico de chat persistido em memória durante a sessão, então dá pra ter uma conversa multi-turn de verdade (o GLM 5 tratava cada mensagem como se fosse a primeira). E a API do RubyLLM continua correta, mesmo padrão <code>RubyLLM.chat(model:, provider:)</code> seguido de <code>c.user</code>/<code>c.assistant</code> pra montar o contexto. Tem até teste cobrindo a constante <code>MODEL</code>, coisa que normalmente ninguém faz.</p>
<p>O preço foi velocidade. 22 minutos contra 17, e a vazão caiu de 400 pra 167 tok/s. Pode ser provedor (Z.AI direto não é a mesma infra do OpenRouter), pode ser um run mais carregado no servidor, pode ser que o 5.1 raciocine mais. Não rodei várias vezes pra fazer média, então não vou dizer que o 5.1 é &ldquo;mais lento&rdquo;. Uma run só não prova regressão. O que dá pra dizer é que, no meu teste, o 5.1 entregou um projeto melhor estruturado e demorou um pouco mais pra fazer isso.</p>
<p>Pra quem quer fugir da Anthropic sem perder qualidade, GLM 5 e GLM 5.1 são as duas opções que funcionam. Se você precisa de billing centralizado no OpenRouter, GLM 5. Se consegue usar a Z.AI direto e quer um projeto mais redondo de primeira, GLM 5.1.</p>
<h2>Custos: API vs Assinatura<span class="hx:absolute hx:-mt-20" id="custos-api-vs-assinatura"></span>
    <a href="#custos-api-vs-assinatura" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Primeiro, o preço por token de cada modelo no OpenRouter:</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/2026/04/05/llm-benchmark/en/token-pricing.png" alt="Preço por token no OpenRouter"  loading="lazy" /></p>
<p>O GPT 5.4 Pro cobra $180 por milhão de tokens de output. O Claude Opus cobra $25. O GLM 5 cobra $2.30. E o Qwen 3.6 Plus é grátis (com rate limit). A escala logarítmica no gráfico esconde um pouco a brutalidade da diferença: de Qwen grátis pra GPT 5.4 Pro são ordens de magnitude.</p>
<p>Mas preço por token não é a história completa. Se você usa Claude ou GPT diariamente pra coding, a assinatura mensal pode sair muito mais barata que pagar por token via API:</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/2026/04/05/llm-benchmark/en/monthly-pricing.png" alt="Assinatura vs API: quanto custa usar Claude e GPT por mês"  loading="lazy" /></p>
<table>
  <thead>
      <tr>
          <th>Abordagem</th>
          <th style="text-align: right">Est. $/mês*</th>
          <th>Notas</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Qwen 3.6 Plus (OpenRouter)</td>
          <td style="text-align: right">$0</td>
          <td>Grátis mas rate-limited</td>
      </tr>
      <tr>
          <td>Modelos locais</td>
          <td style="text-align: right">Eletricidade</td>
          <td>Precisa de hardware</td>
      </tr>
      <tr>
          <td>Claude Pro</td>
          <td style="text-align: right">$20</td>
          <td>~44K tokens/5hr</td>
      </tr>
      <tr>
          <td>ChatGPT Plus</td>
          <td style="text-align: right">$20</td>
          <td>Inclui Codex</td>
      </tr>
      <tr>
          <td>Claude Max 5x</td>
          <td style="text-align: right">$100</td>
          <td>~88K tokens/5hr</td>
      </tr>
      <tr>
          <td>Claude Sonnet (OpenRouter API)</td>
          <td style="text-align: right">~$150</td>
          <td>Sem cap, pay-as-you-go</td>
      </tr>
      <tr>
          <td>Claude Max 20x</td>
          <td style="text-align: right">$200</td>
          <td>~220K tokens/5hr</td>
      </tr>
      <tr>
          <td>ChatGPT Pro</td>
          <td style="text-align: right">$200</td>
          <td>GPT 5.4 Pro ilimitado</td>
      </tr>
      <tr>
          <td>Claude Opus (OpenRouter API)</td>
          <td style="text-align: right">~$450</td>
          <td>Sem cap, pay-as-you-go</td>
      </tr>
      <tr>
          <td>GPT 5.4 Pro (OpenRouter API)</td>
          <td style="text-align: right">~$990</td>
          <td>Absurdamente caro</td>
      </tr>
  </tbody>
</table>
<p>*Estimativa pra uso moderado de coding (~15M input + ~3M output tokens/mês).</p>
<p>O ponto principal: se você usa GPT 5.4 Pro, a assinatura ChatGPT Pro a $200/mês com uso ilimitado é 5x mais barata que pagar por token na API. Pra Claude, o Pro a $20/mês cobre uso leve, mas pra quem usa pesado (maratona de coding como a minha), o Max 20x a $200/mês sai mais barato que pagar Opus por token no OpenRouter (~$450/mês). Os modelos open source no OpenRouter ficam todos abaixo de $2.50/M output tokens, mas como vimos, a maioria gera código que não roda.</p>
<h2>O que funciona pra uso real<span class="hx:absolute hx:-mt-20" id="o-que-funciona-pra-uso-real"></span>
    <a href="#o-que-funciona-pra-uso-real" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Depois de testar 33 modelos ao longo das duas rodadas e olhar o código gerado em detalhe:</p>
<p>Tier 1 (funciona plug and play):</p>
<table>
  <thead>
      <tr>
          <th>Modelo</th>
          <th>Qualidade</th>
          <th style="text-align: right">Custo/Run</th>
          <th>Trade-off</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Claude Opus 4.7</td>
          <td>Novo baseline (28 testes, FakeChat, 96.7% cobertura)</td>
          <td style="text-align: right">~$1.10</td>
          <td>Incremental sobre 4.6, novo gold standard</td>
      </tr>
      <tr>
          <td>Claude Sonnet 4.6</td>
          <td>Melhor que Opus 4.6 no opencode (30 vs 16 testes)</td>
          <td style="text-align: right">~$0.63</td>
          <td>Mais barato, mas no Claude Code o Opus pode se sair melhor</td>
      </tr>
      <tr>
          <td>Claude Opus 4.6</td>
          <td>Gold standard anterior</td>
          <td style="text-align: right">~$1.05</td>
          <td>Baseline anterior</td>
      </tr>
      <tr>
          <td>GLM 5</td>
          <td>Bom (7 testes, API correta)</td>
          <td style="text-align: right">~$0.11</td>
          <td>89% mais barato, alternativa non-Anthropic/OpenAI que funciona</td>
      </tr>
      <tr>
          <td>GLM 5.1</td>
          <td>Bom (24 testes, histórico, API correta)</td>
          <td style="text-align: right">~$0.13</td>
          <td>~88% mais barato, projeto mais completo que o GLM 5</td>
      </tr>
  </tbody>
</table>
<p>Tier 2 (funciona com ressalvas):</p>
<table>
  <thead>
      <tr>
          <th>Modelo</th>
          <th style="text-align: center">Hardware</th>
          <th style="text-align: right">Custo/Run</th>
          <th>Ressalva</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>GPT 5.4 (Codex)</td>
          <td style="text-align: center">Cloud</td>
          <td style="text-align: right">~$16.00</td>
          <td>Arquitetura impressionante (22 testes, injeção de dependência, PORO models), mas <code>add_message</code> com keyword args em vez de hash posicional quebra multi-turn. 7.6M tokens, 15x mais caro que Opus</td>
      </tr>
      <tr>
          <td>Step 3.5 Flash</td>
          <td style="text-align: center">Cloud</td>
          <td style="text-align: right">~$0.02</td>
          <td>Bypassa o gem pedido, lento (38m)</td>
      </tr>
      <tr>
          <td>Grok 4.20</td>
          <td style="text-align: center">Cloud</td>
          <td style="text-align: right">~$0.04</td>
          <td>Bypassa o gem pedido (vai direto pro <code>OpenAI::Client</code>), mas é o mais rápido do benchmark</td>
      </tr>
      <tr>
          <td>Qwen 3.6 35B</td>
          <td style="text-align: center">NVIDIA 5090</td>
          <td style="text-align: right">Grátis</td>
          <td>Entry point e <code>chat.ask</code> corretos, falta <code>.content</code>. Correção de 1 linha. ~10-15 min total</td>
      </tr>
      <tr>
          <td>Qwen 3.5 35B-A3B</td>
          <td style="text-align: center">NVIDIA 5090</td>
          <td style="text-align: right">Grátis</td>
          <td>Entry point certo, alucina <code>add_message</code>/<code>complete</code>. Dá pra arrumar em 1-2 follow-ups. ~15-20 min total</td>
      </tr>
      <tr>
          <td>Qwen 3.5 27B Claude-distilado</td>
          <td style="text-align: center">NVIDIA 5090</td>
          <td style="text-align: right">Grátis</td>
          <td>Estilo Claude, alucinação completa da API. 2-3 follow-ups pra arrumar. ~25-30 min total</td>
      </tr>
      <tr>
          <td>Qwen 3.5 35B (local)</td>
          <td style="text-align: center">AMD Strix</td>
          <td style="text-align: right">Grátis</td>
          <td>Funciona se default configurado, sem mocking, e lento</td>
      </tr>
  </tbody>
</table>
<p>Tier 3 (código quebrado, mesmo com prompts de correção é mais fácil refazer):</p>
<p>Kimi K2.5, MiniMax M2.7, DeepSeek V3.2, Gemini 3.1 Pro, Qwen 3 Coder Next (Strix), Qwen 3 Coder 30B (5090, devolveu mock string hardcoded), Qwen 3.5 122B, Qwen 3.6 Plus — todos inventam APIs que não existem ou nem tentam usar o gem.</p>
<p>Tier 4 (não completaram):</p>
<p>Gemma 4 (loop infinito nos dois hardwares), Llama 4 Scout (sem parser), GPT OSS 20B (diretório errado no Strix, regressão de parser na 5090), Qwen 3 32B (lento demais no Strix, scaffold parcial na 5090), Qwen 2.5 Coder 32B (timeout 90m com zero arquivos).</p>
<h3>Ranking simplificado (qualidade, tempo, preço)<span class="hx:absolute hx:-mt-20" id="ranking-simplificado-qualidade-tempo-preço"></span>
    <a href="#ranking-simplificado-qualidade-tempo-pre%c3%a7o" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Pra quem quer só o resumão em forma de boletim escolar. Qualidade é se o código roda e quão completo está. Tempo é o tempo total do run. Preço é o custo estimado por execução no opencode. <strong>Hardware</strong> indica onde o modelo rodou — Cloud, Strix (AMD Strix Halo, LPDDR5x 256 GB/s) ou 5090 (NVIDIA RTX 5090, GDDR7 1792 GB/s). Modelos cloud rodaram via OpenRouter ou API direta do provedor.</p>
<table>
  <thead>
      <tr>
          <th>Modelo</th>
          <th style="text-align: center">Tipo</th>
          <th style="text-align: center">Hardware</th>
          <th style="text-align: center">Qualidade</th>
          <th style="text-align: center">Tempo</th>
          <th style="text-align: center">Preço</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Claude Opus 4.7</td>
          <td style="text-align: center">Commercial</td>
          <td style="text-align: center">Cloud</td>
          <td style="text-align: center">A+</td>
          <td style="text-align: center">A</td>
          <td style="text-align: center">D</td>
      </tr>
      <tr>
          <td>Claude Sonnet 4.6</td>
          <td style="text-align: center">Commercial</td>
          <td style="text-align: center">Cloud</td>
          <td style="text-align: center">A+</td>
          <td style="text-align: center">A</td>
          <td style="text-align: center">C</td>
      </tr>
      <tr>
          <td>Claude Opus 4.6</td>
          <td style="text-align: center">Commercial</td>
          <td style="text-align: center">Cloud</td>
          <td style="text-align: center">A+</td>
          <td style="text-align: center">A</td>
          <td style="text-align: center">D</td>
      </tr>
      <tr>
          <td>GPT 5.4 (Codex)</td>
          <td style="text-align: center">Commercial</td>
          <td style="text-align: center">Cloud</td>
          <td style="text-align: center">B+</td>
          <td style="text-align: center">B</td>
          <td style="text-align: center">F</td>
      </tr>
      <tr>
          <td>GLM 5.1</td>
          <td style="text-align: center">OSS</td>
          <td style="text-align: center">Cloud</td>
          <td style="text-align: center">A</td>
          <td style="text-align: center">B</td>
          <td style="text-align: center">A</td>
      </tr>
      <tr>
          <td>GLM 5</td>
          <td style="text-align: center">OSS</td>
          <td style="text-align: center">Cloud</td>
          <td style="text-align: center">A−</td>
          <td style="text-align: center">A</td>
          <td style="text-align: center">A</td>
      </tr>
      <tr>
          <td>Qwen 3.6 35B</td>
          <td style="text-align: center">OSS</td>
          <td style="text-align: center">5090</td>
          <td style="text-align: center">B+</td>
          <td style="text-align: center">A+</td>
          <td style="text-align: center">A+ (grátis)</td>
      </tr>
      <tr>
          <td>Qwen 3.5 35B-A3B</td>
          <td style="text-align: center">OSS</td>
          <td style="text-align: center">5090</td>
          <td style="text-align: center">B</td>
          <td style="text-align: center">A+</td>
          <td style="text-align: center">A+ (grátis)</td>
      </tr>
      <tr>
          <td>Qwen 3.5 27B Claude-distilado</td>
          <td style="text-align: center">OSS</td>
          <td style="text-align: center">5090</td>
          <td style="text-align: center">C+</td>
          <td style="text-align: center">B</td>
          <td style="text-align: center">A+ (grátis)</td>
      </tr>
      <tr>
          <td>Gemini 3.1 Pro</td>
          <td style="text-align: center">Commercial</td>
          <td style="text-align: center">Cloud</td>
          <td style="text-align: center">C</td>
          <td style="text-align: center">A+</td>
          <td style="text-align: center">B</td>
      </tr>
      <tr>
          <td>Grok 4.20</td>
          <td style="text-align: center">Commercial</td>
          <td style="text-align: center">Cloud</td>
          <td style="text-align: center">C−</td>
          <td style="text-align: center">A+</td>
          <td style="text-align: center">A+</td>
      </tr>
      <tr>
          <td>Step 3.5 Flash</td>
          <td style="text-align: center">Commercial</td>
          <td style="text-align: center">Cloud</td>
          <td style="text-align: center">C−</td>
          <td style="text-align: center">D</td>
          <td style="text-align: center">A+</td>
      </tr>
      <tr>
          <td>Qwen 3.5 35B-A3B</td>
          <td style="text-align: center">OSS</td>
          <td style="text-align: center">Strix</td>
          <td style="text-align: center">C</td>
          <td style="text-align: center">C</td>
          <td style="text-align: center">A+ (grátis)</td>
      </tr>
      <tr>
          <td>Qwen 3 Coder Next</td>
          <td style="text-align: center">OSS</td>
          <td style="text-align: center">Strix</td>
          <td style="text-align: center">D+</td>
          <td style="text-align: center">A</td>
          <td style="text-align: center">A+ (grátis)</td>
      </tr>
      <tr>
          <td>Qwen 3 32B</td>
          <td style="text-align: center">OSS</td>
          <td style="text-align: center">5090</td>
          <td style="text-align: center">D</td>
          <td style="text-align: center">A+</td>
          <td style="text-align: center">A+ (grátis)</td>
      </tr>
      <tr>
          <td>Qwen 3 Coder 30B</td>
          <td style="text-align: center">OSS</td>
          <td style="text-align: center">5090</td>
          <td style="text-align: center">D−</td>
          <td style="text-align: center">A+</td>
          <td style="text-align: center">A+ (grátis)</td>
      </tr>
      <tr>
          <td>Qwen 3.6 Plus</td>
          <td style="text-align: center">Commercial</td>
          <td style="text-align: center">Cloud</td>
          <td style="text-align: center">D</td>
          <td style="text-align: center">A</td>
          <td style="text-align: center">A+ (grátis)</td>
      </tr>
      <tr>
          <td>Kimi K2.5</td>
          <td style="text-align: center">OSS</td>
          <td style="text-align: center">Cloud</td>
          <td style="text-align: center">D</td>
          <td style="text-align: center">C</td>
          <td style="text-align: center">A</td>
      </tr>
      <tr>
          <td>MiniMax M2.7</td>
          <td style="text-align: center">OSS</td>
          <td style="text-align: center">Cloud</td>
          <td style="text-align: center">D</td>
          <td style="text-align: center">A</td>
          <td style="text-align: center">A+</td>
      </tr>
      <tr>
          <td>Qwen 3.5 122B</td>
          <td style="text-align: center">OSS</td>
          <td style="text-align: center">Strix</td>
          <td style="text-align: center">D</td>
          <td style="text-align: center">F</td>
          <td style="text-align: center">A+ (grátis)</td>
      </tr>
      <tr>
          <td>DeepSeek V3.2</td>
          <td style="text-align: center">OSS</td>
          <td style="text-align: center">Cloud</td>
          <td style="text-align: center">F</td>
          <td style="text-align: center">F</td>
          <td style="text-align: center">A+</td>
      </tr>
      <tr>
          <td>Qwen 2.5 Coder 32B</td>
          <td style="text-align: center">OSS</td>
          <td style="text-align: center">5090</td>
          <td style="text-align: center">F</td>
          <td style="text-align: center">F</td>
          <td style="text-align: center">A+ (grátis)</td>
      </tr>
      <tr>
          <td>Gemma 4 31B</td>
          <td style="text-align: center">OSS</td>
          <td style="text-align: center">5090</td>
          <td style="text-align: center">F</td>
          <td style="text-align: center">A+</td>
          <td style="text-align: center">A+ (grátis)</td>
      </tr>
      <tr>
          <td>Gemma 4 31B</td>
          <td style="text-align: center">OSS</td>
          <td style="text-align: center">Strix</td>
          <td style="text-align: center">F</td>
          <td style="text-align: center">—</td>
          <td style="text-align: center">A+ (grátis)</td>
      </tr>
      <tr>
          <td>GLM 4.7 Flash</td>
          <td style="text-align: center">OSS</td>
          <td style="text-align: center">Strix</td>
          <td style="text-align: center">F</td>
          <td style="text-align: center">—</td>
          <td style="text-align: center">A+ (grátis)</td>
      </tr>
      <tr>
          <td>Llama 4 Scout</td>
          <td style="text-align: center">OSS</td>
          <td style="text-align: center">Strix</td>
          <td style="text-align: center">F</td>
          <td style="text-align: center">—</td>
          <td style="text-align: center">A+ (grátis)</td>
      </tr>
      <tr>
          <td>GPT OSS 20B</td>
          <td style="text-align: center">OSS</td>
          <td style="text-align: center">Strix</td>
          <td style="text-align: center">F</td>
          <td style="text-align: center">—</td>
          <td style="text-align: center">A+ (grátis)</td>
      </tr>
      <tr>
          <td>Qwen 3 32B</td>
          <td style="text-align: center">OSS</td>
          <td style="text-align: center">Strix</td>
          <td style="text-align: center">F</td>
          <td style="text-align: center">—</td>
          <td style="text-align: center">A+ (grátis)</td>
      </tr>
  </tbody>
</table>
<p>Critério de qualidade: A+ funciona e o código está bem estruturado. A/B funciona com ressalvas pequenas a médias. C roda mas pula requisito do prompt ou tem problema estrutural sério. D quebra na primeira mensagem por API inventada. F não completou o benchmark ou produziu lixo. O GPT 5.4 via Codex caiu de A+ pra B+: a arquitetura é a mais sofisticada do benchmark (injeção de dependência, PORO models, 22 testes), mas a primeira mensagem funciona e o multi-turn quebra por convenção de chamada errada no <code>add_message</code>. Gastou 7.6M tokens (~$16/run) sem atingir o nível de acerto do Opus. &ldquo;Tipo&rdquo; separa modelos commercial (pesos fechados) dos OSS (pesos abertos, mesmo quando você usa via API hospedada). Os Qwens aparecem duas vezes quando rodaram nos dois hardwares, porque os resultados são diferentes o suficiente pra justificar — o Qwen 3.5 35B-A3B no 5090 sobe pro Tier B, no Strix continua no Tier C por causa do tempo de espera. Dos 33 modelos configurados ao longo das duas rodadas, alguns ficam de fora dessa tabela porque nem chegaram a executar (sem quota, runner quebrado, falha de infra, ou timeout antes da primeira mensagem).</p>
<h3>O veredito<span class="hx:absolute hx:-mt-20" id="o-veredito"></span>
    <a href="#o-veredito" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Se quer o melhor resultado e não quer pensar: <strong>Claude Opus</strong>. O Opus 4.7 é o novo baseline — 28 testes, API correta, 96.7% de cobertura, FakeChat pattern nos testes. É melhoria incremental sobre o 4.6 (que tinha 16 testes), não uma revolução. Mas não precisa ser revolução. O 4.6 já funcionava, o 4.7 funciona igual e entrega um projeto um pouco mais polido. Se você estava no 4.6, atualizar pro 4.7 é trocar pra um modelo que faz a mesma coisa com mais capricho nos testes e na estrutura.</p>
<p>Um aviso sobre o Sonnet: ele ganhou do Opus nesse benchmark (30 testes vs 16 do Opus 4.6, vs 28 do Opus 4.7). Mas esse benchmark é um app web pequeno e bem definido. Na minha experiência real com projetos maiores, o Sonnet falha quando o raciocínio precisa ir mais fundo. Não estou falando de projetos imensos — basta ir um pouco além desse benchmark (mais controllers, mais integrações, decisões arquiteturais que dependem umas das outras) e o Sonnet começa a perder o fio da meada. O Opus tem teto de output de 128K tokens contra 64K do Sonnet, e o treinamento dele foi especificamente pra tarefas de longo prazo, planejamento multi-etapas e raciocínio profundo sobre código complexo. Num projeto pequeno como o do benchmark, esses músculos ficam parados, e nesse cenário Sonnet ganha por ser mais rápido e mais barato. Mas se você extrapolar isso pra &ldquo;Sonnet é melhor que Opus&rdquo;, vai levar um susto na primeira tarefa que exija raciocínio sustentado. Pode experimentar o Sonnet, é mais barato e pros projetos pequenos funciona. Mas pra projetos reais, você provavelmente vai acabar no Opus de qualquer jeito.</p>
<p>Sobre o GPT 5.4: agora temos dados objetivos. Rodei via Codex CLI com reasoning effort xHigh. A arquitetura que ele gera é a mais sofisticada do benchmark — injeção de dependência, PORO models, session management com TTL. Mas gastou 7.6M tokens (~$16/run, 15x mais caro que Opus) e errou a convenção de chamada do <code>add_message</code> (keyword args em vez de hash posicional), quebrando o multi-turn. Gastou mais, errou igual. Caiu do Tier 1 pro Tier 2. O padrão se repete: acerto de API é memória binária nos pesos. Não escala com budget de tokens nem com reasoning effort.</p>
<p>Se custo importa e você quer sair da Anthropic: <strong>GLM 5 ou GLM 5.1</strong> são as alternativas plug-and-play que funcionam. API correta, mocking nos testes, ~$0.11-$0.13 por run, ~88-89% mais barato que Opus. O GLM 5.1 entregou um projeto mais completo (24 testes, histórico de chat) ao custo de uns 5 minutos a mais.</p>
<p>Se quer evitar vendor lock-in total e tem hardware decente: <strong>Qwen 3.5 35B-A3B</strong> rodando local numa NVIDIA RTX 5090. Cinco minutos de execução a 273 tok/s, projeto Rails que arranca, e o erro de API se conserta em 1-2 follow-ups. Total realista até funcionar: ~15-20 minutos. Bate o Sonnet em custo (zero) e fica perto em tempo total. Essa opção simplesmente não existia na rodada anterior do benchmark, e marca o ponto onde &ldquo;rodar OSS local&rdquo; deixa de ser brincadeira e vira alternativa real. Importante: isso é específico de hardware com banda de memória alta. Numa RTX 4090 deve funcionar parecido. Num laptop com LPDDR5x ou num desktop com DDR4, esquece — você vai esperar 10x mais e o tempo total mata o argumento.</p>
<p>Se quer evitar vendor lock-in mas está em hardware fraco: <strong>GLM 5 ou GLM 5.1</strong> continuam sendo a escolha. São cloud, é verdade, mas a $0.11-$0.13 por run é praticamente preço de eletricidade.</p>
<p>Se quer testar a aposta &ldquo;Claude em casa&rdquo; via distilação: o <strong>Qwen 3.5 27B Claude-distilado</strong> tá lá pra brincar, mas já avisei que ele alucina exatamente as mesmas APIs falsas do Qwen base. Distilação transferiu o estilo do Claude, não o conhecimento factual sobre bibliotecas. Vale como experimento, não como produção.</p>
<p>Sim, talvez com dias de tweaking no llama.cpp, calibrando flags, ajustando prompts, testando builds diferentes, dê pra fazer o Gemma 4 ou outros modelos funcionarem melhor. Pra maioria das pessoas, isso não é realista. A distância entre modelos frontier (Claude, GPT) e modelos open source self-hosted é real. Não é marketing. O gap está diminuindo, mas continua existindo, e a natureza dele mudou: hoje o que falta nos open source é conhecimento factual sobre bibliotecas específicas, não capacidade bruta de raciocínio. Hardware deixou de ser o gargalo, pelo menos pra quem tem GPU recente.</p>
<p>No fim, o que importa é se o código roda. Um modelo pode gerar 3.405 arquivos, escrever 37 testes, produzir um README de 181 linhas, e ainda assim o app não funcionar porque a API que ele usa não existe. Métricas de completude e contagem de testes são necessárias mas não suficientes. O único sinal confiável é se o modelo usa APIs reais corretamente.</p>
<p>O benchmark completo, com código, configuração, prompts e resultados por modelo, está no <a href="https://github.com/akitaonrails/llm-coding-benchmark"target="_blank" rel="noopener">GitHub</a>.</p>
]]></content:encoded><category>benchmarks-de-llm</category><category>llms</category><category>modelos-locais</category></item><item><title>Review: Minisforum MS-S1 Max | AMD AI Max+ 395 com 96GB de VRAM</title><link>https://www.akitaonrails.com/2026/03/31/review-minisforum-ms-s1-max-amd-ai-max-395/</link><guid isPermaLink="true">https://www.akitaonrails.com/2026/03/31/review-minisforum-ms-s1-max-amd-ai-max-395/</guid><pubDate>Tue, 31 Mar 2026 15:00:00 -0300</pubDate><description>&lt;p&gt;Quem acompanha meus posts sobre &lt;a href="https://www.akitaonrails.com/2024/04/03/meu-netflix-pessoal-com-docker-compose/"&gt;home server&lt;/a&gt; sabe que eu rodava tudo num Intel NUC Core i7 com 32GB de RAM. Funcionava. Mas com o crescimento dos modelos de IA open source, o NUC virou um gargalo. Sem GPU dedicada, qualquer inferência de LLM ia pra CPU e ficava inutilizável.&lt;/p&gt;
&lt;p&gt;Eu comprei um Minisforum MS-S1 Max com o novo chip AMD Ryzen AI Max+ 395 por um motivo específico: esse chip suporta até 128GB de RAM unificada, e eu posso alocar 96GB como VRAM pro iGPU. Isso me dá mais VRAM do que qualquer placa gamer comercial, incluindo a RTX 5090 (32GB). E isso muda o que eu consigo rodar localmente.&lt;/p&gt;</description><content:encoded><![CDATA[<p>Quem acompanha meus posts sobre <a href="/2024/04/03/meu-netflix-pessoal-com-docker-compose/">home server</a> sabe que eu rodava tudo num Intel NUC Core i7 com 32GB de RAM. Funcionava. Mas com o crescimento dos modelos de IA open source, o NUC virou um gargalo. Sem GPU dedicada, qualquer inferência de LLM ia pra CPU e ficava inutilizável.</p>
<p>Eu comprei um Minisforum MS-S1 Max com o novo chip AMD Ryzen AI Max+ 395 por um motivo específico: esse chip suporta até 128GB de RAM unificada, e eu posso alocar 96GB como VRAM pro iGPU. Isso me dá mais VRAM do que qualquer placa gamer comercial, incluindo a RTX 5090 (32GB). E isso muda o que eu consigo rodar localmente.</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/blog/2026/03/31/minisforum-desk.jpg" alt="Minisforum MS-S1 Max na mesa"  loading="lazy" /></p>
<h2>Por que trocar o Intel NUC<span class="hx:absolute hx:-mt-20" id="por-que-trocar-o-intel-nuc"></span>
    <a href="#por-que-trocar-o-intel-nuc" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>O NUC serviu bem como servidor Docker por dois anos. Mas a limitação era clara: sem GPU com VRAM suficiente, eu não conseguia rodar LLMs localmente de forma usável. O <a href="https://github.com/akitaonrails/FrankYomik"target="_blank" rel="noopener">Frank Yomik</a>, meu sistema de tradução automática de mangás, precisava de OCR via CPU (lento) e conectava remotamente no Ollama do meu desktop (AMD 7950X3D + RTX 5090) pra tradução. Funcionava, mas significava que meu desktop precisava estar ligado pro servidor funcionar.</p>
<p><img src="https://raw.githubusercontent.com/akitaonrails/FrankYomik/master/docs/sample_translate.png" alt="Frank Yomik - tradução automática de mangás"  loading="lazy" /></p>
<p>Com o Minisforum, o Frank Yomik roda inteiramente no servidor. O worker agora usa ROCm pra OCR com a iGPU, e o Ollama roda local com 96GB de VRAM. Zero dependência do desktop.</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/blog/2026/03/31/minisforum-nuc-compare.jpg" alt="Comparação: Intel NUC (esquerda) vs Minisforum MS-S1 Max (direita)"  loading="lazy" /></p>
<p>Na foto dá pra ter uma ideia do tamanho. O NUC é aquele cubinho pequeno na esquerda. O Minisforum é maior, mas ainda é um mini-PC. Cabe na prateleira do rack embaixo do Synology NAS sem problema.</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/blog/2026/03/31/minisforum-shelf.jpg" alt="Minisforum instalado na prateleira, ao lado do NAS"  loading="lazy" /></p>
<h2>As specs<span class="hx:absolute hx:-mt-20" id="as-specs"></span>
    <a href="#as-specs" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/blog/2026/03/31/minisforum-fastfetch.png" alt="fastfetch do Minisforum"  loading="lazy" /></p>
<p>O chip é o AMD Ryzen AI Max+ 395: 16 cores / 32 threads Zen 5, com iGPU Radeon 8060S integrada e 128GB de LPDDR5X unificada. No BIOS, eu configurei UMA Frame Buffer Size pra 96GB, o que deixa ~30GB de RAM pro sistema operacional e containers. Mais os parâmetros de kernel pra TTM (sem eles, o ROCm só enxerga 15.5GB mesmo com a alocação do BIOS).</p>
<p>O sistema operacional é openSUSE MicroOS (mais sobre isso no <a href="/2026/03/31/migrando-meu-home-server-com-claude-code/">próximo post</a>). O consumo de energia da máquina inteira fica abaixo de 100W, o que é absurdo pra quem está acostumado com GPUs dedicadas que puxam 450W+ sozinhas.</p>
<h2>Minisforum vs meu desktop: benchmarks<span class="hx:absolute hx:-mt-20" id="minisforum-vs-meu-desktop-benchmarks"></span>
    <a href="#minisforum-vs-meu-desktop-benchmarks" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Eu rodei um <a href="https://github.com/akitaonrails/homelab-docs/tree/master/benchmarks"target="_blank" rel="noopener">conjunto de benchmarks</a> comparando o Minisforum com meu desktop (AMD 7950X3D, 96GB DDR5, RTX 5090 32GB GDDR7). Os resultados são claros.</p>
<h3>CPU<span class="hx:absolute hx:-mt-20" id="cpu"></span>
    <a href="#cpu" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><table>
  <thead>
      <tr>
          <th>Teste</th>
          <th>7950X3D</th>
          <th>AI Max+ 395</th>
          <th>Vantagem</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Prime sieve (single-core)</td>
          <td>0.021s</td>
          <td>0.018s</td>
          <td>Strix Halo +14%</td>
      </tr>
      <tr>
          <td>Float pi (single-core)</td>
          <td>1.335s</td>
          <td>1.706s</td>
          <td>7950X3D +28%</td>
      </tr>
      <tr>
          <td>Multi-core sieve (32 threads)</td>
          <td>0.181s</td>
          <td>0.118s</td>
          <td>Strix Halo +53%</td>
      </tr>
      <tr>
          <td>SHA-256 throughput</td>
          <td>2.714 MB/s</td>
          <td>2.488 MB/s</td>
          <td>7950X3D +9%</td>
      </tr>
      <tr>
          <td>AES-256-CBC throughput</td>
          <td>1.613 MB/s</td>
          <td>1.410 MB/s</td>
          <td>7950X3D +14%</td>
      </tr>
  </tbody>
</table>
<p>Resultados mistos. O AI Max+ 395 é melhor em paralelismo puro (sieve multi-core), provavelmente por conta da latência menor na arquitetura de memória unificada. O 7950X3D ganha em float e crypto por causa dos clocks mais altos e do 3D V-Cache.</p>
<h3>Inferência de LLMs (modelos que cabem nos dois)<span class="hx:absolute hx:-mt-20" id="inferência-de-llms-modelos-que-cabem-nos-dois"></span>
    <a href="#infer%c3%aancia-de-llms-modelos-que-cabem-nos-dois" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Aqui é onde a coisa fica interessante. Pra modelos que cabem nos 32GB da RTX 5090, a comparação é puramente de bandwidth de memória:</p>
<table>
  <thead>
      <tr>
          <th>Modelo</th>
          <th>Tamanho</th>
          <th>RTX 5090 (tok/s)</th>
          <th>Strix Halo (tok/s)</th>
          <th>Vantagem 5090</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>phi4</td>
          <td>9.1 GB</td>
          <td>155.1</td>
          <td>23.2</td>
          <td>6.7x</td>
      </tr>
      <tr>
          <td>qwen3:14b</td>
          <td>9.3 GB</td>
          <td>138.9</td>
          <td>22.6</td>
          <td>6.1x</td>
      </tr>
      <tr>
          <td>phi4-reasoning</td>
          <td>11.1 GB</td>
          <td>130.2</td>
          <td>19.1</td>
          <td>6.8x</td>
      </tr>
      <tr>
          <td>qwen3:32b</td>
          <td>20.2 GB</td>
          <td>66.9</td>
          <td>10.0</td>
          <td>6.7x</td>
      </tr>
  </tbody>
</table>
<p>A RTX 5090 é ~7x mais rápida. A explicação é simples: GDDR7 tem ~1.792 GB/s de bandwidth. LPDDR5X tem ~256 GB/s. A razão (7x) bate quase exatamente com a diferença de velocidade medida (6.7x). Inferência de LLM é um problema dominado por bandwidth de memória. Quem lê pesos mais rápido, gera tokens mais rápido.</p>
<h3>E o prompt processing?<span class="hx:absolute hx:-mt-20" id="e-o-prompt-processing"></span>
    <a href="#e-o-prompt-processing" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><table>
  <thead>
      <tr>
          <th>Modelo</th>
          <th>RTX 5090 (tok/s)</th>
          <th>Strix Halo (tok/s)</th>
          <th>Vantagem 5090</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>phi4</td>
          <td>~1.933</td>
          <td>~212</td>
          <td>9.1x</td>
      </tr>
      <tr>
          <td>qwen3:14b</td>
          <td>~1.474</td>
          <td>~155</td>
          <td>9.5x</td>
      </tr>
      <tr>
          <td>qwen3:32b</td>
          <td>~767</td>
          <td>~68</td>
          <td>11.3x</td>
      </tr>
  </tbody>
</table>
<p>Prompt processing é ainda pior: 7-11x mais lento. Faz sentido, porque o prompt precisa ser processado inteiro antes de gerar o primeiro token, e é uma operação ainda mais bandwidth-intensiva.</p>
<h3>Onde o Strix Halo ganha: modelos grandes<span class="hx:absolute hx:-mt-20" id="onde-o-strix-halo-ganha-modelos-grandes"></span>
    <a href="#onde-o-strix-halo-ganha-modelos-grandes" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Agora vem a razão pela qual eu comprei esse PC. Modelos que não cabem na RTX 5090:</p>
<table>
  <thead>
      <tr>
          <th>Modelo</th>
          <th>Tamanho</th>
          <th>Strix Halo (tok/s)</th>
          <th>Notas</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>gpt-oss:20b</td>
          <td>13.8 GB MXFP4</td>
          <td>48.9</td>
          <td>MoE, mais rápido que esperado</td>
      </tr>
      <tr>
          <td>qwen3.5:35b</td>
          <td>23.9 GB</td>
          <td>43.2</td>
          <td>MoE, apenas ~4B params ativos</td>
      </tr>
      <tr>
          <td>qwen3-coder-next</td>
          <td>51.7 GB</td>
          <td>29.5</td>
          <td>MoE, 50GB+</td>
      </tr>
      <tr>
          <td>qwen3.5:122b</td>
          <td>81.4 GB Q4_K_M</td>
          <td>19.2</td>
          <td>122B params, MoE</td>
      </tr>
      <tr>
          <td>glm-4.7-flash:bf16</td>
          <td>59.9 GB</td>
          <td>17.9</td>
          <td>Full precision bf16</td>
      </tr>
      <tr>
          <td>qwen2.5:72b</td>
          <td>47.4 GB Q4_K_M</td>
          <td>4.5</td>
          <td>Dense 72B, bandwidth-limited</td>
      </tr>
  </tbody>
</table>
<p>O qwen3.5:122b com 81GB de pesos rodando a 19 tok/s. Num mini-PC. Isso simplesmente não é possível numa RTX 5090. Na placa da NVIDIA, esse modelo teria que fazer offload de camadas pra RAM do sistema, caindo pra 2-3 tok/s. Na prática, inutilizável.</p>
<p>A diferença entre modelos MoE e densos é brutal. O qwen3.5:35b roda a 43 tok/s porque, apesar de ter 35B de parâmetros totais, só ~4B ficam ativos por token. Um modelo denso de 72B como o qwen2.5:72b precisa ler 40GB+ de pesos por token, e a 256 GB/s de bandwidth, o máximo teórico é ~6.7 tok/s. Os 4.5 medidos representam ~67% de eficiência, que é o esperado pra iGPU (overhead de bus compartilhado e drivers).</p>
<h3>Resumo: quando usar cada máquina<span class="hx:absolute hx:-mt-20" id="resumo-quando-usar-cada-máquina"></span>
    <a href="#resumo-quando-usar-cada-m%c3%a1quina" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><table>
  <thead>
      <tr>
          <th>Caso de uso</th>
          <th>Melhor máquina</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Chat/coding interativo (modelos &lt;32GB)</td>
          <td>RTX 5090 (6-7x mais rápido)</td>
      </tr>
      <tr>
          <td>Modelos grandes (50GB+)</td>
          <td>Strix Halo (única opção)</td>
      </tr>
      <tr>
          <td>Modelos densos 70B+</td>
          <td>Strix Halo (única opção)</td>
      </tr>
      <tr>
          <td>Full-precision bf16</td>
          <td>Strix Halo (única opção)</td>
      </tr>
      <tr>
          <td>Batch processing com contexto longo</td>
          <td>Strix Halo (mais VRAM pra KV cache)</td>
      </tr>
      <tr>
          <td>API serving com baixa latência</td>
          <td>RTX 5090 (sub-150ms TTFT)</td>
      </tr>
  </tbody>
</table>
<h3>Um bug de ROCm que ainda existe<span class="hx:absolute hx:-mt-20" id="um-bug-de-rocm-que-ainda-existe"></span>
    <a href="#um-bug-de-rocm-que-ainda-existe" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Nem tudo funciona. Modelos como deepseek-r1:70b, llama3.3:70b e llama4:scout crasham com um bug no ggml (<code>GGML_ASSERT(ggml_nbytes(src0) &lt;= INT_MAX) failed</code>). O tensor de embedding desses modelos excede 2GB e o kernel de cópia do ROCm usa inteiro de 32 bits pro tamanho. No CUDA (NVIDIA) já foi corrigido, mas no ROCm ainda não. Esperando o fix no Ollama 0.20.0+.</p>
<h2>LPDDR5X vs GDDR7: por que essa diferença<span class="hx:absolute hx:-mt-20" id="lpddr5x-vs-gddr7-por-que-essa-diferença"></span>
    <a href="#lpddr5x-vs-gddr7-por-que-essa-diferen%c3%a7a" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>A pergunta que vem é: por que a LPDDR5X é tão mais lenta?</p>
<p>GDDR7 é memória dedicada de GPU. Ela fica soldada na placa de vídeo, conectada por um barramento largo (384 ou 512 bits na RTX 5090) com clocks altos. O único trabalho dela é alimentar a GPU com dados. LPDDR5X é memória unificada que serve pra tudo: sistema operacional, aplicações, e GPU ao mesmo tempo. O barramento é mais estreito e compartilhado.</p>
<p>Na prática: GDDR7 entrega ~1.792 GB/s dedicados pra GPU. LPDDR5X entrega ~256 GB/s que ainda precisam ser divididos entre CPU e GPU. Inferência de LLM é basicamente &ldquo;leia todos os pesos do modelo da memória, multiplique pelo token atual, gere o próximo token, repita&rdquo;. Quem lê mais rápido, gera mais rápido. Não tem atalho.</p>
<p>A vantagem do Strix Halo não é velocidade. É capacidade. 96GB de VRAM num chip de 100W que custa uma fração de uma GPU profissional. A RTX 5090 é 7x mais rápida, mas trava em 32GB. Modelos que não cabem, não rodam.</p>
<h2>As alternativas: quem mais faz isso?<span class="hx:absolute hx:-mt-20" id="as-alternativas-quem-mais-faz-isso"></span>
    <a href="#as-alternativas-quem-mais-faz-isso" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Se 96GB não é suficiente ou se você quer mais velocidade, as opções são poucas.</p>
<p>O Framework Desktop usa o mesmo chip AI Max+ 395 com até 128GB de RAM. Mesma plataforma, mesma performance, mas com o diferencial de ser modular e reparável (é o Framework, afinal). Na prática é equivalente ao Minisforum em specs e preço.</p>
<p>Acima disso, a alternativa é o Mac Studio com M3 Ultra. O chip M3 Ultra suporta até 512GB de memória unificada, com bandwidth de ~819 GB/s (mais de 3x o Strix Halo). A Apple fabrica os chips de memória no package, então a latência e a bandwidth são superiores. Você poderia potencialmente alocar ~400GB como VRAM e rodar modelos que não cabem em lugar nenhum fora de servidores com GPUs profissionais.</p>
<p>O NVMe interno da Apple também é outro nível: ~7.4 GB/s de leitura sequencial no M3 Ultra, comparado com ~14 GB/s do Crucial T700 (PCIe 5.0). O T700 é mais rápido em throughput bruto, mas a latência do NVMe da Apple tende a ser menor em I/O aleatório por causa da integração com o SoC.</p>
<table>
  <thead>
      <tr>
          <th>Spec</th>
          <th>Minisforum MS-S1 Max</th>
          <th>Mac Studio M3 Ultra (max)</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>RAM máxima</td>
          <td>128 GB LPDDR5X</td>
          <td>512 GB unified</td>
      </tr>
      <tr>
          <td>VRAM alocável</td>
          <td>~96 GB</td>
          <td>~400 GB</td>
      </tr>
      <tr>
          <td>Memory bandwidth</td>
          <td>~256 GB/s</td>
          <td>~819 GB/s</td>
      </tr>
      <tr>
          <td>CPU</td>
          <td>Zen 5, 16C/32T</td>
          <td>Apple M3 Ultra, 32C</td>
      </tr>
      <tr>
          <td>GPU compute</td>
          <td>ROCm (gfx1151, experimental)</td>
          <td>Metal (mlx, mature)</td>
      </tr>
      <tr>
          <td>Consumo</td>
          <td>~100W</td>
          <td>~135W</td>
      </tr>
      <tr>
          <td>NVMe</td>
          <td>PCIe 5.0 (slot padrão)</td>
          <td>Custom Apple (~7.4 GB/s)</td>
      </tr>
      <tr>
          <td>Preço (EUA)</td>
          <td>~$1.500-2.000</td>
          <td>~$9.999 (512GB config)</td>
      </tr>
      <tr>
          <td>Preço estimado (Brasil)</td>
          <td>~R$ 12.000-15.000</td>
          <td>~R$ 110.000+ (importação)</td>
      </tr>
  </tbody>
</table>
<p>O preço no Brasil é o elefante na sala. A configuração máxima do Mac Studio custa $9.999 nos EUA. Com impostos de importação (~60% + ICMS estadual), passa dos R$ 110.000. O Minisforum com 128GB sai por R$ 12.000-15.000. A diferença de quase 8x no preço compra muita coisa.</p>
<p>Se você precisa de mais de 96GB de VRAM pra modelos realmente enormes (DeepSeek-V3 com 671B parâmetros cabe em ~400GB Q4, por exemplo), o Mac Studio com 512GB é a única opção consumer. A alternativa seria GPUs profissionais NVIDIA A6000 (48GB VRAM, ~$6.000 cada, e você precisaria de várias em NVLink). Pra tudo que cabe em 96GB, o Minisforum faz o trabalho por uma fração do custo.</p>
<h2>E projetos que prometem rodar LLMs grandes em GPUs pequenas?<span class="hx:absolute hx:-mt-20" id="e-projetos-que-prometem-rodar-llms-grandes-em-gpus-pequenas"></span>
    <a href="#e-projetos-que-prometem-rodar-llms-grandes-em-gpus-pequenas" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Existe o conceito de &ldquo;layer offloading&rdquo; que projetos como llama.cpp já suportam. A ideia é: se o modelo não cabe inteiro na VRAM, mantém algumas camadas na GPU e o resto na RAM do sistema. A GPU processa as camadas que estão nela, transfere pro CPU processar o resto, e volta.</p>
<p>Na prática, não funciona bem. O gargalo é o PCIe: a velocidade de transferência entre RAM do sistema e VRAM da GPU é de ~32 GB/s (PCIe 5.0 x16). Cada token gerado precisa transferir dados ida e volta. O resultado é que você cai de 150 tok/s (tudo na VRAM) pra 2-8 tok/s (offload parcial). É lento demais pra uso interativo.</p>
<p>A VRAM é a limitação fundamental porque inferência de LLM é memory-bandwidth-bound, não compute-bound. A GPU tem compute sobrando. O que falta é capacidade de ler os pesos do modelo rápido o suficiente. Quando parte dos pesos está em RAM via PCIe, o pipeline inteiro espera pela transferência.</p>
<p>É por isso que memória unificada (como no Strix Halo ou no Apple Silicon) faz diferença. Não tem PCIe no meio. CPU e GPU acessam a mesma memória física. Os 256 GB/s do Strix Halo são lentos comparados com GDDR7, mas são 8x mais rápidos que ficar fazendo offload via PCIe.</p>
<h2>Avanços em otimização de LLMs (até 2026)<span class="hx:absolute hx:-mt-20" id="avanços-em-otimização-de-llms-até-2026"></span>
    <a href="#avan%c3%a7os-em-otimiza%c3%a7%c3%a3o-de-llms-at%c3%a9-2026" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Pra entender por que alguns modelos rodam tão melhor que outros no Strix Halo, precisa entender o que mudou no ecossistema nos últimos dois anos.</p>
<h3>Mixture of Experts (MoE)<span class="hx:absolute hx:-mt-20" id="mixture-of-experts-moe"></span>
    <a href="#mixture-of-experts-moe" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Se você roda modelos locais, MoE é o avanço que mais importa. Um modelo MoE tem parâmetros totais altos (ex: 122B no qwen3.5:122b), mas ativa apenas uma fração deles por token (ex: ~4B). Os pesos inativos ficam na VRAM mas não são lidos a cada token, o que reduz drasticamente a bandwidth necessária.</p>
<p>Nos benchmarks do Strix Halo, modelos MoE rodam 3-10x mais rápido que modelos densos do mesmo tamanho. O qwen3.5:35b (MoE, ~4B ativos) roda a 43 tok/s enquanto o qwen2.5:72b (denso, 72B ativos) roda a 4.5 tok/s.</p>
<h3>DeepSeek e a otimização de treinamento<span class="hx:absolute hx:-mt-20" id="deepseek-e-a-otimização-de-treinamento"></span>
    <a href="#deepseek-e-a-otimiza%c3%a7%c3%a3o-de-treinamento" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>O DeepSeek V3 (dezembro 2024) mostrou que era possível treinar modelos de 671B parâmetros com custo uma ordem de magnitude menor que o previsto. Eles combinaram MoE com quantização FP8 durante o treinamento (não só na inferência), treinamento multi-estágio com curriculum learning, e várias otimizações de comunicação entre GPUs. O impacto: todo mundo copiou. Qwen, GLM, MiniMax, todos adotaram variações dessa técnica.</p>
<h3>Quantização: de FP16 pra Q4 sem perder muito<span class="hx:absolute hx:-mt-20" id="quantização-de-fp16-pra-q4-sem-perder-muito"></span>
    <a href="#quantiza%c3%a7%c3%a3o-de-fp16-pra-q4-sem-perder-muito" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Quantização comprime os pesos do modelo de 16 bits (FP16) pra formatos menores: 8 bits (Q8), 4 bits (Q4), ou até 2 bits. Um modelo de 70B que ocuparia ~140GB em FP16 cabe em ~40GB em Q4_K_M. A perda de qualidade existe, mas nos formatos modernos (GGUF Q4_K_M, AWQ, EXL2) é pequena o suficiente pra uso prático.</p>
<p>O GGUF (formato do llama.cpp) se tornou o padrão pra inferência local. AWQ e GPTQ são alternativas com calibração mais sofisticada, mas o ecossistema convergiu pro GGUF porque ele funciona em CPU, CUDA e ROCm sem recompilação.</p>
<h3>Destilação: modelos menores que sabem mais<span class="hx:absolute hx:-mt-20" id="destilação-modelos-menores-que-sabem-mais"></span>
    <a href="#destila%c3%a7%c3%a3o-modelos-menores-que-sabem-mais" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Destilação é treinar um modelo pequeno usando as respostas de um modelo grande como professor. O Phi-4 da Microsoft (14B) foi treinado com destilação do GPT-4 e compete com modelos de 70B em vários benchmarks. O Qwen3 fez o mesmo: o qwen3:14b é surpreendentemente capaz pro tamanho.</p>
<h3>Flash Attention e KV Cache otimizado<span class="hx:absolute hx:-mt-20" id="flash-attention-e-kv-cache-otimizado"></span>
    <a href="#flash-attention-e-kv-cache-otimizado" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Flash Attention (Tri Dao, 2022) mudou como attention é computada: em vez de materializar a matriz inteira de attention na memória, processa em blocos mantendo os dados no SRAM on-chip da GPU, reduzindo consumo de memória de O(n²) pra O(n). Sem isso, contextos de 128K+ tokens seriam impraticáveis. Já passou pelas versões 2 e 3, com otimizações pra FP8 e operações assíncronas no H100. O PagedAttention (vLLM, UC Berkeley) fez o mesmo pro KV cache durante serving: aplica conceitos de memória virtual ao cache, eliminando fragmentação e melhorando throughput 2-4x.</p>
<p>No Ollama, eu configurei <code>OLLAMA_FLASH_ATTENTION=1</code> e <code>OLLAMA_KV_CACHE_TYPE=q8_0</code> no servidor. O primeiro ativa flash attention, o segundo usa KV cache em 8-bit em vez de fp16, cortando a bandwidth necessária pela metade por token. São otimizações que custam zero em hardware e melhoram throughput mensurável.</p>
<h3>O que Qwen, Kimi, MiniMax e GLM estão fazendo<span class="hx:absolute hx:-mt-20" id="o-que-qwen-kimi-minimax-e-glm-estão-fazendo"></span>
    <a href="#o-que-qwen-kimi-minimax-e-glm-est%c3%a3o-fazendo" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>O Qwen (Alibaba) tem sido consistentemente o melhor custo-benefício em modelos open source. O Qwen3:14b é denso e forte; o Qwen3.5:122b é MoE e roda surpreendentemente bem em 96GB. O GLM-4.7 (Zhipu AI) é notável por oferecer versões bf16 full precision que rodam inteiras em 96GB. O MiniMax experimentou com contextos longos (até 4M tokens). O Kimi (Moonshot AI) focou em context windows grandes com arquiteturas lineares.</p>
<h3>O que roda bem em 96GB de VRAM<span class="hx:absolute hx:-mt-20" id="o-que-roda-bem-em-96gb-de-vram"></span>
    <a href="#o-que-roda-bem-em-96gb-de-vram" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Com 96GB no Strix Halo, os modelos que funcionam bem pra uso diário:</p>
<table>
  <thead>
      <tr>
          <th>Modelo</th>
          <th>Tamanho</th>
          <th>tok/s</th>
          <th>Uso</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>qwen3.5:35b</td>
          <td>24 GB</td>
          <td>43.2</td>
          <td>Uso geral, excelente</td>
      </tr>
      <tr>
          <td>qwen3-coder-next</td>
          <td>52 GB</td>
          <td>29.5</td>
          <td>Código, MoE</td>
      </tr>
      <tr>
          <td>qwen3.5:122b</td>
          <td>81 GB</td>
          <td>19.2</td>
          <td>Pesado mas usável</td>
      </tr>
      <tr>
          <td>glm-4.7-flash:bf16</td>
          <td>60 GB</td>
          <td>17.9</td>
          <td>Full precision</td>
      </tr>
      <tr>
          <td>qwen2.5-coder:32b</td>
          <td>20 GB</td>
          <td>10.2</td>
          <td>Código, denso</td>
      </tr>
      <tr>
          <td>deepseek-r1:32b</td>
          <td>20 GB</td>
          <td>7.4</td>
          <td>Raciocínio</td>
      </tr>
  </tbody>
</table>
<p>Modelos densos de 70B+ (deepseek-r1:70b, llama3.3:70b) ainda estão bloqueados pelo bug do ROCm que mencionei. Quando for corrigido, devem rodar a ~4-6 tok/s, usáveis pra batch mas não pra chat interativo.</p>
<h2>Conclusão<span class="hx:absolute hx:-mt-20" id="conclusão"></span>
    <a href="#conclus%c3%a3o" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Eu comprei o Minisforum pra rodar modelos que não cabem em GPU gamer nenhuma. Pra isso, funciona. Não é rápido. 19 tok/s num modelo de 122B não é a experiência que você tem com Claude ou ChatGPT. Mas é local, é privado, e roda na minha prateleira consumindo menos energia que uma lâmpada antiga.</p>
<p>Pra quem pergunta sobre o Mac Studio: se você tem orçamento, é a melhor máquina pra rodar LLMs locais. 512GB de memória unificada, 819 GB/s de bandwidth, ecossistema Metal/mlx maduro. Dá pra rodar DeepSeek-V3 inteiro em Q4. Mas no Brasil, com importação, passa dos R$ 110 mil. O Minisforum com 128GB por R$ 12-15 mil é a opção realista.</p>
<p>E pra quem acha que dá pra contornar a limitação de VRAM com offloading de camadas: não dá. PCIe é lento demais. O modelo precisa caber inteiro na VRAM pra inferência ser usável. É o motivo pelo qual GPUs gamers com 32GB de GDDR7 ultra-rápida continuam limitadas em tamanho de modelo, e por que a memória unificada do Strix Halo e do Apple Silicon mudou a equação.</p>
<p>No <a href="/2026/03/31/migrando-meu-home-server-com-claude-code/">próximo post</a> eu conto como migrei todo o home server pro Minisforum usando Claude Code, os problemas que encontrei, e como o openSUSE MicroOS se comporta como sistema operacional de servidor Docker.</p>
]]></content:encoded><category>hardware</category><category>modelos-locais</category><category>performance</category><category>reviews</category></item><item><title>AI Agents: GLM 4.7 Flash é realmente tão bom assim?</title><link>https://www.akitaonrails.com/2026/01/22/ai-agents-glm-4-7-flash-e-realmente-tao-bom-assim/</link><guid isPermaLink="true">https://www.akitaonrails.com/2026/01/22/ai-agents-glm-4-7-flash-e-realmente-tao-bom-assim/</guid><pubDate>Thu, 22 Jan 2026 13:41:05 -0300</pubDate><description>&lt;p&gt;Recentemente fiz &lt;a href="https://akitaonrails.com/2026/01/11/ai-agents-comparando-as-principais-llm-no-desafio-de-zig/"target="_blank" rel="noopener"&gt;um post comparando várias LLMs&lt;/a&gt;. A boa notícia é que as LLMs comerciais estão bem impressionantes mesmo. Mas a má notícia é que nenhuma LLM open source foi capaz de realizar meu desafio de programação (leia no post anterior pra entender o desafio).&lt;/p&gt;
&lt;p&gt;Também recentemente, a ZAI lançou a versão 4.7 do seu famoso modelo &lt;a href="https://huggingface.co/zai-org/GLM-4.7"target="_blank" rel="noopener"&gt;&amp;ldquo;GLM&amp;rdquo;&lt;/a&gt; que é um modelo de 30 bilhões de parâmetros, encodado em BF16, que precisa na faixa de 60GB de VRAM pra caber inteiro numa GPU. Ou seja, é pesado.&lt;/p&gt;</description><content:encoded><![CDATA[<p>Recentemente fiz <a href="https://akitaonrails.com/2026/01/11/ai-agents-comparando-as-principais-llm-no-desafio-de-zig/"target="_blank" rel="noopener">um post comparando várias LLMs</a>. A boa notícia é que as LLMs comerciais estão bem impressionantes mesmo. Mas a má notícia é que nenhuma LLM open source foi capaz de realizar meu desafio de programação (leia no post anterior pra entender o desafio).</p>
<p>Também recentemente, a ZAI lançou a versão 4.7 do seu famoso modelo <a href="https://huggingface.co/zai-org/GLM-4.7"target="_blank" rel="noopener">&ldquo;GLM&rdquo;</a> que é um modelo de 30 bilhões de parâmetros, encodado em BF16, que precisa na faixa de 60GB de VRAM pra caber inteiro numa GPU. Ou seja, é pesado.</p>
<p>Ainda mais recentemente, ela lançou a versão <a href="https://huggingface.co/zai-org/GLM-4.7-Flash"target="_blank" rel="noopener"><strong>GLM 4.7 Flash</strong></a> que é o mesmo modelo de 30 bilhões de parâmetros, mas quantizado (a grosso modo: &ldquo;comprimido&rdquo; ou &ldquo;truncado e re-normalizado&rdquo;), que cabe numa GPU de pelo menos 32GB, que é o caso da minha <strong>RTX 5090</strong>.</p>
<p>Sim, a GPU caseira mais cara do mercado é o mínimo necessário pra rodar os melhores modelos open source. Isso porque o importante não é o poder de processamento e sim a quantidade de VRAM. Por isso um Mac Studio, com GPU tecnicamente mais &ldquo;fraca&rdquo;, tem vantagem em LLMs porque ela compartilha com a RAM, com máximo teórico de 512GB. Então cabem os maiores modelos sem problema nenhum.</p>
<p>Outras opções são Mini PCs com CPU AMD Ryzen AI Max+ com capacidade de compartilhar até 128GB de VRAM.</p>
<p>O desafio era ver se eu conseguia rodar o GLM 4.7 Flash localmente na minha máquina.</p>
<blockquote>
  <p><strong>TL;DR: GLM 4.7 Flash é possivelmente o melhor LLM open source - com alguns poréns.</strong></p>

</blockquote>
<h3>VLLM vs LM Studio vs Ollama<span class="hx:absolute hx:-mt-20" id="vllm-vs-lm-studio-vs-ollama"></span>
    <a href="#vllm-vs-lm-studio-vs-ollama" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Na <a href="https://huggingface.co/zai-org/GLM-4.7-Flash"target="_blank" rel="noopener">página oficial</a> deles tem documentação de como rodar em VLLM e já aviso que não funciona. Ele exige funcionalidades que só tem na branch master dos projetos VLLM e transformers e vai dar uma dor de cabeça com conflitos de versões e resolução de dependências e mesmo assim, no final, eu não consegui fazer ele rodar direito.</p>
<p>LM Studio também não funciona. Ele tem a opção pra baixar o modelo, e eu tentei tunar todos os parâmetros possíveis. Quantização do KV Cache, layers na GPU, CPU threads, etc etc. Mas não importa, o modelo até carrega mas roda absurdamente lento. Tão lento que é inusável. Acho que é porque ele só consegue baixar o modelo BF16, então metade dos layers não cabe na GPU. Portanto, até a data deste post, não dá pra usar nem LM Studio e nem VLLM.</p>
<p>Mas aí eu vi um <a href="https://x.com/ollama/status/2013372316021834086"target="_blank" rel="noopener">tweet</a> dizendo que finalmente Ollama ia suportar GLM 4.7, mas novamente, até a data deste post, ainda não está na versão estável. A única forma de usar é compilando do código fonte as coisas que estão no branch master deles.</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">git clone https://github.com/ollama/ollama.git
</span></span><span class="line"><span class="cl"><span class="nb">cd</span> ollama
</span></span><span class="line"><span class="cl">cmake -B build
</span></span><span class="line"><span class="cl">cmake --build build
</span></span><span class="line"><span class="cl">go build -v .</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>No último comando vai parecer que travou porque nada aparece na tela, mas é porque ele está compilando centenas de arquivos de C++. Só esperar que uma hora acaba. E finalmente dá pra rodar:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="nv">OLLAMA_FLASH_ATTENTION</span><span class="o">=</span><span class="m">1</span> <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="nv">GGML_VK_VISIBLE_DEVICES</span><span class="o">=</span>-1 <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="nv">OLLAMA_KV_CACHE_TYPE</span><span class="o">=</span>q4_0 <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="nv">OLLAMA_CONTEXT_LENGTH</span><span class="o">=</span><span class="m">45000</span> <span class="se">\
</span></span></span><span class="line"><span class="cl">go run . serve</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Esses são os parâmetros pra conseguir caber tudo nos 32GB de VRAM e não ficar fazendo offload pra CPU. 45 mil tokens é o máximo, talvez até um pouco menos pra garantir que o KV Cache vai caber.</p>
<p>Além disso precisa quantizar em 4-bits com <code>q4_0</code>. O normal são floats 16-bits. Pra caber vamos ter que truncar (perder precisão).</p>
<p>No meu caso, eu tenho uma iGPU AMD. Pra evitar que o ollama fique tentando usar ela, tem essa opção <code>GGML_VK_VISIBLE_DEVICES=-1</code>. Isso deve garantir que só a nvidia vai ser usada.</p>
<p>Finalmente, habilitamos <strong>Flash Attention</strong> pra tentar reduzir crescimento de memória à medida que janela de contexto aumenta.</p>
<h3>Trade off: velocidade vs contexto<span class="hx:absolute hx:-mt-20" id="trade-off-velocidade-vs-contexto"></span>
    <a href="#trade-off-velocidade-vs-contexto" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>O GLM 4.7 Flash é um modelo com 48 layers. Na configuração que mostrei acima, vai caber todos os 48 layers na GPU. Mas uma janela de contexto de 45k <strong>pode</strong> ser muito pequeno dependendo do seu uso. Você mesmo vai ter que testar.</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/20260123101949_screenshot-2026-01-23_10-09-35.png" alt="log gpu"  loading="lazy" /></p>
<p>Quando cabe tudo na GPU, o processamento é razoavelmente rápido. Um pouco mais lento mas ainda comparável a um Qwen3 Coder ou GPT-OSS. Dá pra saber que está tudo na GPU porque olhando BTOP a CPU está sem processar nada e ao mesmo tempo <code>nvidia-smi</code> mostra que a GPU tá puxando na faixa de 500W dos 600W que ele consegue.</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/20260123102147_screenshot-2026-01-22_18-50-42.png" alt="nvidia-smi 500w"  loading="lazy" /></p>
<p>Nos meus testes, dependendo do que estiver fazendo, esses 45k enchem <strong>MUITO RÁPIDO</strong>. Usando ferramentas como OpenCode ou Crush, quando chega perto de encher, eles pedem pra LLM sumarizar o que foi conversado/pesquisado até agora, gravar e reiniciar com o resumo. Mas mesmo assim, os resumos também vão ficando grandes.</p>
<p>Em agentes, ele vai executar comandos no seu sistema (compilar, listar arquivos, pesquisar trechos de código, pesquisar na web) e tudo isso vai consumir contexto. Quanto mais contexto tiver sobrando, melhor.</p>
<p>Eu andei testando com <code>OLLAMA_CONTEXT_LENGTH=65576</code>, faixa dos 65k tokens. Isso vai impedir todos os layers de ficarem na GPU e o Ollama vai ter que ficar fazendo swap, usando CPU. Portanto, o processamento vai ficar bem mais lento. MAS, a janela de contexto vai ser maior. É um trade-off.</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/20260123102557_screenshot-2026-01-22_20-51-05.png" alt="btop"  loading="lazy" /></p>
<p>De cara, você vai ber que a CPU vai começar a puxar. Na minha 7850x3d fica puxando 50% constantemente. Veja os tempos das respostas do Ollama como ficou lento:</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/20260123102645_screenshot-2026-01-23_10-09-50.png" alt="ollama cpu"  loading="lazy" /></p>
<p>Vai da faixa de 4 segundos pra 90 segundos. 20x mais lento. E isso porque 39 dos 48 layers estão na GPU. Por isso eu acho que não dá pra aumentar mais que 65k. O ideal é manter em 45k mesmo, mas no caso do meu desafio, senti que precisava compensar.</p>
<p>Ou o Crush/OpenCode vai demorar porque a janela acaba rápido demais e toda hora precisa sumarizar o trabalho e perder detalhes do contexto - o que dá mais trabalho em pesquisar a mesma coisa mais de uma vez. Ou preserva mais detalhes do contexto por mais tempo, mas aí todas as respostas ficam mais lentas. É um trade-off bem difícil. E a culpa de tudo é a porcaria dessas placas de video terem tão pouca VRAM.</p>
<p>Se usar um Mac Studio que vai até 512GB ou algum Mini-PC com AMD Ryzen AI Max+ que vai até 128GB, não teríamos que lidar com esse limite. Mas são máquinas que custam acima de BRL 20 mil. Não dá pra justificar. Por esse preço é melhor só pagar créditos na OpenRouter e usar modelos comerciais maiores e mais rápidos como Claude Opus ou GPT 5.2, ou o próprio GLM 4.7 no cloud da ZAI.</p>
<h3>Pull Request no Crush<span class="hx:absolute hx:-mt-20" id="pull-request-no-crush"></span>
    <a href="#pull-request-no-crush" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Como side-quest, tive um pequeno problema que tava me incomodando. Um dos problemas de uma janela de contexto pequena, é que de tempos em tempos o Crush ficava parando, mesmo ainda tendo trabalho pra fazer. O motivo é porque o contexto encheu antes dele ter tempo de sumarizar daí quando vai fazer uma chamada de ferramenta (&ldquo;tool calling&rdquo;), que é um XML grande, ele trunca por falta de espaço.</p>
<p>XML truncado é inválido, ele não consegue chamar ferramenta e crasheia o processamento, assim:</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/20260123103238_screenshot-2026-01-22_15-12-36.png" alt="log xml"  loading="lazy" /></p>
<p>E mesmo tentando controlar o limite de uso da memória, eventualmente esse teto vai quebrar e o processamento vai parar por falta de memória:</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/20260123103736_screenshot-2026-01-23_10-36-43.png" alt="log nvm"  loading="lazy" /></p>
<p>Seja por XML truncado, seja por OOM do ollama, eventualmente o processamento pára. E como é um processamento lento, é um saco ter que ficar esperando isso acontecer e reiniciar o processo manualmente.</p>
<p>Por isso resolvi ver se tinha como fazer o próprio Crush detectar que parou por causa desses motivos e auto-continuar de onde parou. Abri o próprio Crush em cima do projeto do Crush e pedi pro Claude Opus checar a possibilidade de uma correção.</p>
<p><a href="https://github.com/charmbracelet/crush/pull/1962"target="_blank" rel="noopener"><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/20260123105702_screenshot-2026-01-23_10-56-40.png" alt="pull request"  loading="lazy" /></a></p>
<p>Sim, eu fiz um pull request 100% vibe coded com o próprio Crush pra consertar o Crush 😂 Vamos ver se o povo do Crush vai aceitar. Eu tentei o máximo não fazer um <a href="https://www.youtube.com/watch?v=vrTrOCQZoQE&amp;pp=ygUHYWkgc2xvcNIHCQmHCgGHKiGM7w%3D%3D"target="_blank" rel="noopener">AI Slop</a>.</p>
<p>De qualquer forma, com isso eu consigo deixar o agente rodando horas sem parar até chegar onde eu quero.</p>
<h3>Conclusão<span class="hx:absolute hx:-mt-20" id="conclusão"></span>
    <a href="#conclus%c3%a3o" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><blockquote>
  <p>Este NÃO é o jeito certo!</p>

</blockquote>
<p>Deixando claro que largar um agente fazendo coisas sozinho sem ninguém ver é uma má-prática. No meu caso é um exercício isolado. Eu estou usando meu <a href="https://akitaonrails.com/2026/01/10/ai-agents-garantindo-a-protecao-do-seu-sistema/"target="_blank" rel="noopener">AI JAIL</a> e eu já rodei esse prompt várias vezes como benchmark e sei bem o comportamento de todas as LLMs.</p>
<p>Como disse no meu post anterior, o prompt do meu desafio é cheio de más práticas que você não deve fazer: eu peço muitas coisas complexas ao mesmo tempo, com pouco contexto, de forma vaga e confusa, justamente pra ver como a LLM vai se virar pra resolver. No dia a dia você deve mandar prompts pequenos, tarefas unitárias objetivas e bem espefícas, com boa explicação e contexto, pra que as respostas sejam rápidas e curtas. Um passo de cada vez.</p>
<p>O objetivo do meu desafio é justamente puxar um pouco os limites do senso comum. Com a idéia de que se passar pelo meu desafio, deve conseguir passar por prompts mais simples do dia a dia.</p>
<p>E nesse desafio que só as LLMs comerciais conseguiram passar: Claude Opus, GPT 5.2, Gemini 3. Mas nenhum open source conseguiu, nem Qwen3 Coder, nem GPT-OSS, nem minha primeira tentativa com GLM 4.7 cloud e nem MiniMax v2.</p>
<p>Mas este novo GLM 4.7 Flash me deu esperanças. Ele não entrou em &ldquo;agentic loop&rdquo; como o Qwen3 - que sempre chega num ponto onde ele pára de tentar resolver e só repete &ldquo;já terminei&rdquo;, sem ter terminado.</p>
<p>Também não ficou crasheando a toa. Toda vez que parou o processamento, foi por falta de memória. E mesmo assim, mandando &ldquo;continuar&rdquo; via prompt no Crush, ele consegue pegar de onde parou e continuar.</p>
<p>Cada correção que ele faz tem sentido. Não vi fazendo tangentes fora do escopo como vi o Gemini fazendo muitas vezes. Quando tem dúvidas, consegue pedir um &ldquo;agentic fetch&rdquo; e ir pesquisar na web antes de continuar. Toda vez que faz correções ele sabe executar o comando de build pra ver se tem erros de compilação.</p>
<p>Gostei que tenta corrigir um pequeno erro de cada vez em vez de tentar ficar reescrevendo tudo ou resolvendo mais de um problema ao mesmo tempo.</p>
<blockquote>
  <p>No final, foi o único open source que fez o build funcionar e gerar um binário.</p>

</blockquote>
<p>Não dava pra fazer ele testar o programa porque ele precisaria carregar outra LLM numa GPU que já estava com LLM, então eu parei o teste sem resolver bugs de runtime. E também não pedi pra ele refatorar porque já tinha levado tempo demais. Ele já tinha superado os outros OSS então parei por aqui. Eis o <a href="https://github.com/akitaonrails/qwen-cli-zig/pull/6"target="_blank" rel="noopener">pull request</a> com as correções de build que ele fez. Simples, sem mexer onde não precisa e faz o que foi pedido.</p>
<p>Ou seja, dos open source, foi o que vi o melhor comportamento, comparável ao que vi o Claude Opus ou GPT 5.2 fazendo. Considerando que esses modelos comerciais tem trilhões de parâmetros, rodando nos servidores mais parrudos com NVIDIA H200, eu considero que esse GLM, de só 30 bilhões de parâmetros, rodando numa mísera 5090 local, está se saindo impressionantemente bem.</p>
<p>Os trade-offs são claros: ele é ordens de grandeza mais lento que um Claude Opus. Mas é melhor um modelo lento que chega no mesmo resultado, do que um rápido que entra em loop infinito achando que já resolveu tudo sem ter feito nada, como o Qwen3.</p>
<p>Acho que pra desafios como o meu: prompt gigante, que exige muito contexto pra conseguir resolver, não é bom pra nenhum modelo open source. Mas se eu fosse começar um projetinho pequeno do zero, com tarefas simples e curtas, imagino que o GLM 4.7 resolveria sem nenhuma dor de cabeça, custando zero e preservando minha privacidade ao rodar 100% local e offline.</p>
<p>Recomendo: GLM 4.7 Flash é o melhor modelo OSS do começo de 2026.</p>
]]></content:encoded><category>benchmarks-de-llm</category><category>modelos-locais</category><category>agentes-de-codigo</category></item><item><title>Dissecando um Modelfile de Ollama - Ajustando Qwen3 pra código</title><link>https://www.akitaonrails.com/2025/04/29/dissecando-um-modelfile-de-ollama-ajustando-qwen3-pra-codigo/</link><guid isPermaLink="true">https://www.akitaonrails.com/2025/04/29/dissecando-um-modelfile-de-ollama-ajustando-qwen3-pra-codigo/</guid><pubDate>Tue, 29 Apr 2025 02:30:00 -0300</pubDate><description>&lt;p&gt;A notícia quente do dia é o lançamento do novo modelo Qwen3. Eu mesmo &lt;a href="https://www.akitaonrails.com/2025/04/28/testando-o-recem-lancado-llm-open-source-qwen3-com-aider-e-ollama"target="_blank" rel="noopener"&gt;postei a respeito hoje&lt;/a&gt; E já fiquei bem impressionado. Achei que ia deixar por isso mesmo, mas aí vi este tweet:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/1jgq3ar5c5l7wfjt9wjfui8l598z" alt="ivan qwen tweaks" loading="lazy" /&gt;&lt;/p&gt;
&lt;p&gt;Veja o &lt;a href="https://x.com/ivanfioravanti/status/1916934241281061156"target="_blank" rel="noopener"&gt;texto completo neste link&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Mas ele dá dicas de como &amp;ldquo;tunar&amp;rdquo; esse novo modelo, mais pra questões matemáticas e fiquei pensando se tem como tunar pra desenvolvimento de software. Antes que pesquisadores mais avançados me xinguem: essa é minha primeira vez mexendo nos parâmetros de uma LLM, então se quiserem adicionar contexto nos comentários, sejam bem vindos.&lt;/p&gt;</description><content:encoded><![CDATA[<p>A notícia quente do dia é o lançamento do novo modelo Qwen3. Eu mesmo <a href="https://www.akitaonrails.com/2025/04/28/testando-o-recem-lancado-llm-open-source-qwen3-com-aider-e-ollama"target="_blank" rel="noopener">postei a respeito hoje</a> E já fiquei bem impressionado. Achei que ia deixar por isso mesmo, mas aí vi este tweet:</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/1jgq3ar5c5l7wfjt9wjfui8l598z" alt="ivan qwen tweaks"  loading="lazy" /></p>
<p>Veja o <a href="https://x.com/ivanfioravanti/status/1916934241281061156"target="_blank" rel="noopener">texto completo neste link</a></p>
<p>Mas ele dá dicas de como &ldquo;tunar&rdquo; esse novo modelo, mais pra questões matemáticas e fiquei pensando se tem como tunar pra desenvolvimento de software. Antes que pesquisadores mais avançados me xinguem: essa é minha primeira vez mexendo nos parâmetros de uma LLM, então se quiserem adicionar contexto nos comentários, sejam bem vindos.</p>
<p>Vamos lá. Primeiro, o que são esses TopP, TopK e tudo mais que o Ivan fala no tweet dele? São parâmetros de <strong>KEY SAMPLING</strong> (sorteio de chaves). É um grupo de parâmetros que controlam <em>COMO</em> o modelo seleciona o <em>PRÓXIMO TOKEN</em> e vai completando o texto (dando a &ldquo;resposta&rdquo;), a partir do seu vocabulário baseado em distribuição de probabilidade que ele calcula. Os mais comuns são justamente <code>temperature</code>, <code>top_p</code>, <code>top_k</code> e algumas vezes <code>min_p</code> ou <code>repeat_penalty</code>.</p>
<p>Eu sempre falo: LLM é um gerador de próxima palavra, um completador de texto baseado em probabilidades. Quando a LLM processa seu input (seu contexto, histórico do chat e a próxima pergunta), ele precisa calcular o &ldquo;score&rdquo; de probabilidade pra cada palavra ou sub-palavra (token) no seu vocabulário inteiro. Imagina um modelo gigante, ele precisa recalcular pra tudo o tempo todo, pra cada próxima palavra.</p>
<p>Parâmetros de sampling dizem pro modelo &ldquo;como escolher o próximo token&rdquo; da lista de probabilidades. Sem esses parâmetros o modelo escolheria um decodificador &ldquo;greedy&rdquo; (ambicioso demais). Significa que ele ia só pegar o token com a maior probabilidade toda vez. E isso leva a um resultado repetitivo e que soa pouco natural. Sampling adiciona um tipo de &ldquo;aleatoriedade controlada&rdquo; (lembram? eu sempre falo que tem aleatoriedade misturado, nunca a resposta é &ldquo;certa&rdquo;), e isso torna a resposta mais &ldquo;interessante&rdquo; pra um ser humano (facilmente enganável). Eis alguns dos parâmetros:</p>
<h3>Temperatura<span class="hx:absolute hx:-mt-20" id="temperatura"></span>
    <a href="#temperatura" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Controla a aleatoriedade, que muitos chamam de &ldquo;criatividade&rdquo;. Essencialmente torna a distribuição mais quente (mais espalhada e mais plana) ou mais fria (com mais picos ao redor dos mesmos tokens). Quanto maior a temperatura (até 1.0) isso aumenta a probabilidade de escoher tokens que tinham probabilidades calculadas menores. Quanto menor a temperatura (até 0.0) menores as chances dos tokens calculados com menor probabilidade, mais &ldquo;greedy&rdquo; vai ser o resultado (menos &ldquo;humano&rdquo;).</p>
<p>Pra código, deixe muito alto e aí que surgem &ldquo;alucinações&rdquo; (que é basicamente aleatoriedade aumentada) e código que não faz sentido. Se for zero, vai ser muito &ldquo;boilerplate&rdquo;, repetitivo, usando padrões muito comuns e pode potencialmente acabar num loop infinito sem resposta (porque ele não consegue puxar nada diferente).</p>
<p>O tweet sugere 0.6 que é moderado. Mas nos meus testes, temperatura tão alta começou já a me dar uns códigos esquisitos. Do nada ele mudava nome de variável. Do nada aparecia um caracter chinês no meio do código (literalmente). Eu acho que 0 é muito baixo, mas 0.1 é suficiente, talvez 0.2.</p>
<p>Isso você pode mudar no arquivo <code>~/.aider.model.settings.yaml</code> antes de carregar o modelo (esses parâmetros podem ser reajustados na inicialização):</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl">- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">ollama_chat/qwen3-dev:latest</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">extra_params</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">temperature</span><span class="p">:</span><span class="w"> </span><span class="m">0.1</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">num_ctx</span><span class="p">:</span><span class="w"> </span><span class="m">40960</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<h3>top_p (Nucleus Sampling)<span class="hx:absolute hx:-mt-20" id="top_p-nucleus-sampling"></span>
    <a href="#top_p-nucleus-sampling" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Aqui vai complicando. Esse <code>top_p</code> serve pra selecionar tokens do menor conjunto de tokens cuja probabilidade cumulativa exceda esse limite &ldquo;p&rdquo;. O modelo então puxa samples somente desse conjunto reduzido.</p>
<p>Esse modelo ordena todos os tokens por suas probabilidades em ordem descendente (do maior pro menor). Então começa a adicionar suas probabilidades até a soma ser maior ou igual a <code>top_p</code>. Todos os tokens incluídos na soma, formam o tal &ldquo;nucleus&rdquo; e desse conjunto ele sorteia um próximo token. Então é um sorteio dos tokens com sum(x) maiores probabilidades, mais ou menos. &ldquo;Escolha um dos vencedores, mas não necessariamente O vencedor (que seria greedy)&rdquo;.</p>
<h3>top_k<span class="hx:absolute hx:-mt-20" id="top_k"></span>
    <a href="#top_k" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Considere somente os &ldquo;k&rdquo; tokens mais prováveis. O modelo só sorteia (sample) desse conjunto reduzido dos top K maiores probabilidades de tokens. Isso é realmente dizer &ldquo;escolha um dentre os Top 10 melhores&rdquo;. Quanto maior o K maior a diversidade, quanto menor mais greedy.</p>
<p>E isso é relativo, top_k e top_p agem em conjunto. Essa configuração ajuda a ancorar a seleção aos tokens &ldquo;mais prováveis&rdquo; enquanto ainda permite ao top_p filtrar e tirar os de menor probabilidade. Isso prioriza plausibilidade (top_p) enquanto restringe o número total de tokens sorteados (top_k). Ajuda a manter o resultado focado nas respostas mais possivelmente &ldquo;certas&rdquo; (mas de novo, é um sorteio/sampling).</p>
<h3>min_p<span class="hx:absolute hx:-mt-20" id="min_p"></span>
    <a href="#min_p" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Filtra e tira tokens cuja probabilidade são menores que &ldquo;min_p&rdquo; vezes a probabilidade do token mais provável. Antes do sorteio, o modelo identifica o token com maior probabilidade (P_max), então remove qualquer token com probabilidade (P_token) que é menor que <code>P_max * min_p</code>. O sorteio então acontece a partir da lista que sobra (que vai ser mais filtrada por top_p e top_k).</p>
<p>Isso previne que o modelo escolha tokens que são &ldquo;extremamente improváveis&rdquo; relativo à escolha mais óbvia.</p>
<h3>repeat_penalty<span class="hx:absolute hx:-mt-20" id="repeat_penalty"></span>
    <a href="#repeat_penalty" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Isso desencoraja o modelo de repetir os mesmos tokens que já apareceram recentemente no histórico da conversa ou texto gerado.</p>
<p>Antes do sorteio, a pontuação de probabilidade dos tokens que já foram vistos recentemente são reduzidas, dividindo por essa <code>repeat_penalty</code> (com valores maiores que 1, já que dividir por 1 é ele mesmo, então mantém a mesma probabilidade).</p>
<p>Se deixa 1.0, pode começar a repetir as mesmas respostas ou os mesmos códigos, até os errados. Se for alto demais, muito acima de 1.0, a resposta pode ser pouco natural, já que às vezes precisa mesmo repetir alguma coisa pra re-explicar. Um valor moderado lá pelo 1.1 a menos de 1.2 pode ser mais adequado pra não punir demais o que já está no histórico.</p>
<h3>num_predict (max tokens pra gerar)<span class="hx:absolute hx:-mt-20" id="num_predict-max-tokens-pra-gerar"></span>
    <a href="#num_predict-max-tokens-pra-gerar" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>O absoluto máximo de tokens que o modelo vai gerar de resposta. Se passar, vai truncar. Se for um número pequeno demais, especialmente em resposta de código - que pode ser longo - pode truncar demais e deixar resposta incompleta. Se for grande demais vai aumentar muito o processamento e vai exigir muito mais recursos da máquina e mais tempo e pode ser um desperdício.</p>
<p>Um valor de uns 32 mil tokens tem bastante espaço pra tokens. Tem que testar, e varia caso a caso pro tipo de resposta que você espera e o que cada modelo suporta.</p>
<h3>Recomendado<span class="hx:absolute hx:-mt-20" id="recomendado"></span>
    <a href="#recomendado" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Sabendo tudo isso agora você também consegue ler o tweet do Ivan e entender. Ele  recomenda Temp 0.6, TopP 0.95, TopK 20, MinP 0.</p>
<p>Eu mexi mais um pouco. E eu acho que pra código, Temp tem que ser na faixa de 0.1 ou 0.2. Também acho que vale um &ldquo;repeat_penalty&rdquo; de 1.1 pra evitar repetições desnecessárias, especialmente de código. De novo, tem que testar. São números empíricos e não exatos.</p>
<p>Eu criei um <a href="https://github.com/akitaonrails/qwen3-dev"target="_blank" rel="noopener">projeto no meu GitHub</a> com um arquivo completo de modelo que implementa não só esses parâmetros mas já pré-configura um prompt de sistema pra enviesar o modelo a ser um assistente de código primeiro. Eis meu prompt:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>SYSTEM &#34;&#34;&#34;
You are a highly functional AI assistant specializing in software development tasks.
Respond to queries about coding, algorithms, debugging, system design,
and programming languages/frameworks.
Provide clear, accurate, step-by-step reasoning and functional code examples.
Always format code snippets using markdown triple backticks (```language ... ```).
Aim for comprehensive and correct technical assistance.
Never suggest changing code unrelated to the question topic at hand.
Restrict yourself to the minimum amount of changes that do resolve the problem at hand.
Avoid renaming functions or variables unless absolutely necessary, 
and in doing so make sure if there&#39;s anyone calling the old names, 
for them to be renamed to the new names. Never leave the code in a broken state. 
Pay close attention to correctness, not just answering the quickiest and dirtiest code to solve the problem.
&#34;&#34;&#34;</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Pra fazer esse arquivo, primeiro eu puxei o Modelfile original do Qwen3 com este comando:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>ollama show qwen3 --modelfile &gt; Modelfile</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>É importante fazer isso porque, pelo que entendi, modelfiles não herdam do anterior. Não é que nem um &ldquo;FROM&rdquo; de um Dockerfile, onde você continua uma nova imagem a partir da base anterior.</p>
<p>Depois de editado com meus ajustes - que já está no meu GitHub, é só criar um novo modelo:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>ollama create qwen3-dev -f qwen3-dev.modelfile</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>E ele vai aparecer no seu ollama local. Veja com <code>ollama list</code>:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>❯ ollama list
NAME                                            ID              SIZE      MODIFIED
qwen3-dev:latest                                fe03dfae5484    9.3 GB    About an hour ago
qwen3:32b                                       e1c9f234c6eb    20 GB     2 hours ago
qwen3:14b                                       7d7da67570e2    9.3 GB    8 hours ago
qwen3:latest                                    e4b5fd7f8af0    5.2 GB    8 hours ago
qwen2.5-coder:7b-instruct                       2b0496514337    4.7 GB    25 hours ago
....</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Viu o <code>qwen3-dev:latest</code>? É nosso novo modelo com ajustes de parâmetros. No aider é só usar ele direto:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>aider --watch-files --model ollama_chat/qwen3-dev:latest</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Lembrando de ajustar o <code>.aider.model.settings.yml</code> como já mostrei acima.</p>
<p>Eu não fiz testes longos ainda, mas resolvi brincar com o modelo 14b como base e esses ajustes. O modelo base já me impressionou e pelo menos meus ajustes não quebraram nada. Fiquei fazendo os mesmos refatoramentos e testes unitários no meu projetinho educativo e subi mais um <a href="https://github.com/akitaonrails/Tiny-Qwen-CLI/pull/6"target="_blank" rel="noopener">pull request</a></p>
<p>Eu acho que o Gemini 2.5 Pro Exp é um pouco melhor, mas de novo, não por uma margem muito grande. E em alguns casos (como na refatoração), eu acho que prefiro o código gerado pelo Qwen3. Compare com o código que consegui com o Gemini, neste <a href="https://github.com/akitaonrails/Tiny-Qwen-CLI/pull/3"target="_blank" rel="noopener">outro pull request</a></p>
<p>Com o Gemini eu precisei explicar menos e ele deu menos erros, mas ambos conseguiram fazer o que eu pedi até o fim e sem quebrar o programa principal. Mas o Qwen3 me parece bom o suficiente pra eu querer usar ele. Com a vantagem que mesmo sendo o modelo médio, de 14 bilhões de parâmetros (que roda na minha VRAM de 24GB com sobra), ele ainda dá resultados muito bons e limpos de código. E melhor: roda MUITO, mas MUITO mais rápido que o Qwen2.5 32b.</p>
<p>Pra quem tem GPUs com menos VRAM, digamos, uma 3070 ou 4070 de 16GB, dá pra usar o modelo menor ainda, de 7b. Eu testei o qwen2.5-coder:7b e tive resultados excelentes, então imagino que o Qwen3 de 7b deve ser ainda melhor e muito mais rápido. Velocidade que é comparável ao Claude ou Gemini, que são muito maiores e rodam em máquina muito mais parruda que uma RTX 4070.</p>
<p>Rodando meu qwen3-dev, no meu monitoramento minha CPU ficava maior parte do tempo em idle e a GPU o tempo todo acima de 90%, consumindo mais quase 400W constantemente, mantendo temperatura de 73 graus (o que é bom).</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/jv8ub1bc6m70u1agm1nyiooav2mk" alt="btop"  loading="lazy" /></p>
<p>Tive até um pequeno incidente onde meu PC desligou do nada, porque a extensão temporária que eu estava usando não aguentou e morreu. Somado com a CPU e meus outros dispositivos ligados na mesma tomada, estava puxando consistentemente 1000W da parede por horas. Brincar de I.A. usa <strong>MUITA</strong> energia, com ar-condicionado ligado porque só pra dissipar tudo isso, meu apartamento fica 1 a 2 graus (que eu chuto) acima do normal. Dá pra sentir mais quentinho mesmo kkkk</p>
<p>Mas é isso, espero que tenham entendido como que as coisas funcionam de verdade por baixo dos panos. Não tem mágica: é geração de próxima palavra, baseada em sorteio semi-aleatório de probabilidades.</p>
<p>Now you know.</p>
]]></content:encoded><category>llms</category><category>modelos-locais</category><category>tutoriais</category></item><item><title>Seu Próprio Co-Pilot Gratuito Universal que funciona Local: AIDER-OLLAMA-QWEN</title><link>https://www.akitaonrails.com/2025/04/25/seu-proprio-co-pilot-gratuito-universal-que-funciona-local-aider-ollama-qwen/</link><guid isPermaLink="true">https://www.akitaonrails.com/2025/04/25/seu-proprio-co-pilot-gratuito-universal-que-funciona-local-aider-ollama-qwen/</guid><pubDate>Fri, 25 Apr 2025 13:40:00 -0300</pubDate><description>&lt;p&gt;No meu &lt;a href="https://www.akitaonrails.com/2025/04/25/hello-world-de-llm-criando-seu-proprio-chat-de-i-a-que-roda-local"target="_blank" rel="noopener"&gt;post anterior&lt;/a&gt; eu mostro como fazer um chat LLM do zero com capacidade pra carregar arquivos de código pra refatoração. Eu demonstro os princípios por trás de ferramentas como Co-Pilot, Cursor ou Windsurf. O resumo é simples:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;UM BOM PROMPT DE REGRAS&lt;/li&gt;
&lt;li&gt;SCRIPTS que rodam localmente e adicionam mais contexto na sessão do chat.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;É basicamente &amp;ldquo;só&amp;rdquo; isso (claro, mesmo o princípio sendo simples, ainda dá bastante trabalho implementar mesmo). E eu não preciso fazer tudo do zero. Já existe uma alternativa open source que faz exatamente tudo isso, a ferramenta que ficou mais popular nos últimos meses pra desenvolvimento de software, o &lt;a href="https://aider.chat/docs/install.html"target="_blank" rel="noopener"&gt;&lt;strong&gt;AIDER&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;</description><content:encoded><![CDATA[<p>No meu <a href="https://www.akitaonrails.com/2025/04/25/hello-world-de-llm-criando-seu-proprio-chat-de-i-a-que-roda-local"target="_blank" rel="noopener">post anterior</a> eu mostro como fazer um chat LLM do zero com capacidade pra carregar arquivos de código pra refatoração. Eu demonstro os princípios por trás de ferramentas como Co-Pilot, Cursor ou Windsurf. O resumo é simples:</p>
<ul>
<li>UM BOM PROMPT DE REGRAS</li>
<li>SCRIPTS que rodam localmente e adicionam mais contexto na sessão do chat.</li>
</ul>
<p>É basicamente &ldquo;só&rdquo; isso (claro, mesmo o princípio sendo simples, ainda dá bastante trabalho implementar mesmo). E eu não preciso fazer tudo do zero. Já existe uma alternativa open source que faz exatamente tudo isso, a ferramenta que ficou mais popular nos últimos meses pra desenvolvimento de software, o <a href="https://aider.chat/docs/install.html"target="_blank" rel="noopener"><strong>AIDER</strong></a></p>
<p>O que eu gosto no AIDER:</p>
<ul>
<li>funciona com praticamente qualquer LLM, fechada ou aberta, graças ao uso do <a href="https://www.litellm.ai/"target="_blank" rel="noopener"><strong>LiteLLM</strong></a> por baixo, que abstrai e organiza toda configuração como tamanho de janela de contexto, temperatura e outros parâmetros sabidos que funciona melhor em cada LLM.</li>
<li>é OPEN SOURCE, então além de gratuito você pode aprender mais lendo o código-fonte, como eu fiz.</li>
<li>não precisa de plugins especiais - e proprietários - pra instalar em cada IDE: ele tem recurso de &ldquo;watch files&rdquo;. Então posso deixar aberto num painel do meu terminal e meu editor favorito no outro painel: o NeoVIM. Dá pra integrar com plugins, mas é opcional. Vai funcionar pra tudo, até pro Notepad, se você for masoquista.</li>
</ul>
<p>Eu tentei um pouco da porcaria de &ldquo;vibe coding&rdquo; somente via interface web do ChatGPT, Gemini, Claude, e vou dizer que todos são uma porcaria. Pra fazer um projetinho que é simples, com não mais que uns 4 arquivos curtos ele deu MUITO TRABALHO. Alguns deles:</p>
<ul>
<li>não demora muito pra começar a misturar conteúdo de um arquivo em outro.</li>
<li>erros que ele cometeu, eu expliquei que estava errado, ele corrigiu, mas logo em seguida repete de novo.</li>
<li>ele não roda nada, então ele chuta muita coisa, como paths de arquivos, versões de bibliotecas</li>
<li>não interessa que o contexto é grande e dá pra subir muitos arquivos pra contexto, é sliding window: ele não dá atenção pra tudo ao mesmo tempo. quanto mais arquivo de código você sobe, mais degrada a qualidade</li>
<li>tem que escrever muito pra explicar o que se quer e, principalmente, porque ele errou e como corrigir.</li>
<li>os créditos são caros, acabam muito rápido, porque se desperdiça metade dos tokens só explicando os erros e subindo os mesmos arquivos mais de uma vez porque você percebe que ele já &ldquo;esqueceu&rdquo;. Eu cheguei no limite do Claude, do Gemini, super rápido (eu pago o primeiro plano pago, não vou pagar o mais caro). Daí tem que esperar algumas horas pra voltar (péssimo).</li>
</ul>
<p>Tudo isso alguém vai comentar <em>&ldquo;Ah, mas pra mim funcionou.&rdquo;</em> Foda-se, não perguntei. Eu disse que PRA MIM foi assim, só aceita. O importante é dizer que aquela história de &ldquo;virtual employee&rdquo; ou &ldquo;substituir totalmente programador&rdquo; ou &ldquo;conseguir fazer um projeto inteiro sem saber programar nada&rdquo; é <strong>BALELA</strong>, <strong>HISTÓRIA DA CAROCHINHA</strong>, <strong>CONTO DE VIGARISTA</strong>, <strong>ILUSÃO DE AMADOR</strong>, etc.</p>
<p>Todas as LLMs são <strong>SÓ MAIS UMA FERRAMENTA</strong> que alguém que realmente entende, como nós programadores, vamos saber usar muito melhor do que qualquer &ldquo;empreendedor amador&rdquo; jamais vai conseguir. E eu vou provar.</p>
<h3>Aider Básico<span class="hx:absolute hx:-mt-20" id="aider-básico"></span>
    <a href="#aider-b%c3%a1sico" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Instalar o Aider é fácil, só precisa ter Python &gt;= 3.12 na sua máquina, coisa que todo Linux moderno tem. A documentação deles é muito boa, <a href="https://aider.chat/docs/install.html"target="_blank" rel="noopener">recomendo ler</a> pra saber o que ele recomenda pós-instalação. Tem muitas dicas.</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>python -m pip install aider-install
aider-install</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>O que ele não diz na documentação é que a primeira coisa pra fazer é baixar <a href="https://github.com/Aider-AI/aider/blob/main/aider/website/assets/sample.aider.conf.yml"target="_blank" rel="noopener">este arquivo de exemplo</a> e colocar em <code>$HOME/.aider.conf.yml</code>. Nele tem uma coisa importante que temos que mexer:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nn">...</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="c">## Enable/disable auto commit of LLM changes (default: True)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">auto-commits</span><span class="p">:</span><span class="w"> </span><span class="kc">false</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nn">...</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Recomendo que leiam esse arquivo, ele está todo comentado e pode ter opções que você queira desligar, mas esse é irritante, porque - por padrão - essa desgraça faz &ldquo;COMMIT AUTOMÁTICO&rdquo; no seu Git, toda vez que ele muda alguma coisa no seu código. Nem me dá chance de revisar. Vai na base do &ldquo;CUNFIA IN NÓIS&rdquo;. Nem fu, desligado.</p>
<p>Esse é o tipo de coisa que um amador não faria, e até entendo. Amadores não sabem organizar commits de git de qualquer jeito, provavelmente as mensagens automáticas do Aider vão ser menos piores. Olha como fica:</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/22ji8rp2bbw5lsxwbmpneh5clhcr" alt="Aider Git commit"  loading="lazy" /></p>
<p>Aviso pra júnior: esse é o tipo de coisa que se eu fosse avaliar, ia ganhar ponto negativo se eu visse sendo feito assim. Faça direito: re-cheque as mudanças e organize em commits de verdade. O Aider &ldquo;diz&rdquo; que fez refactor, mas nem sempre ele acerta, mas mesmo assim faz o commit, e aí sobe errado. Se for pra subir tudo automático, mesmo com erro, realmente, não preciso de você.</p>
<p>Mas estou me adiantando, depois de instalar o Aider, precisa configurar as API KEYS de cada serviço que você usa, seja da OpenAI, Claude ou outros. Quem usa ChatGPT no Linux já tem uma variável como <code>export OPENAI_API_KEY=sk-proj-........</code> configurado no <code>.bashrc</code> ou <code>.zshrc</code>.</p>
<p>Feito isso, dá pra escolher qual modelo usar:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code># Change directory into your codebase
cd /to/your/project

# o3-mini
aider --model o3-mini

# o1-mini
aider --model o1-mini

# GPT-4o
aider --model gpt-4o

# List models available from OpenAI
aider --list-models openai/</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>E isso já vai abrir um chat interativo. Também dá pra, antes de abrir o chat, já mandar carregar arquivos locais assim:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>aider README.md init.py utils.py ...</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Ou, de dentro do chat, ele aceita <a href="https://aider.chat/docs/usage/commands.html"target="_blank" rel="noopener">vários comandos</a> que começam com slash &ldquo;/&rdquo;, parecido com chat de IRC. Por exemplo:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>/add helper.py
/copy # copia a última sugestão de código pro clipboard
/git # roda um comando de git local a partir do chat
/run # roda um comando do seu shell e adiciona o output no chat
/web # vai numa página web e puxa o conteúdo pro chat</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Leia a documentação. Mas o modo mais interessante é o &ldquo;watch-files&rdquo;. Basta estar no diretório do seu projeto e subir assim:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>aider --watch-files</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Ele usa a biblioteca <a href="https://pypi.org/project/watchfiles/"target="_blank" rel="noopener">watchfiles</a> de Python pra interceptar chamadas de IO nesse diretório e dar gatilho pra carregar sozinho seus arquivos, assim que você salva no seu editor favorito (por isso falei que não precisa de plugin extra nenhum se não quiser). Já aviso que essa lib não suporta dar watch em mounts de SMB ou NFS (sim, eu testei).</p>
<p>No seu editor, basta criar um comentário perto do código que quer que ele mexa, assim:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>// can you refactor this return so it has more error checking? AI!
export const getCapitalizedLabel = (name: string): string =&gt; {
  return name
    .replace(/_/g, &#34; &#34;)
    .split(&#34; &#34;)
    .map((word: string) =&gt; word.charAt(0).toUpperCase() &#43; word.slice(1))
    .join(&#34; &#34;)
}</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Coloque no comentário o que quer que ele faça, e termine com o gatilho &ldquo;AI!&rdquo;. Aí no outro terminal que o Aider está carregado, ele vai perceber que o arquivo foi salvo:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>&gt;

Added src/utils/getCapitalizedLabel.ts to the chat
Processing your request...
...</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>E vai fazer sua &ldquo;mágica&rdquo; (que eu vou contar o segredo já já). Este é o exemplo do que ele fez (mesmos resultados tanto com o4-mini quanto Qwen2.5, mas é um exemplo bem trivial):</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="kr">export</span> <span class="kr">const</span> <span class="nx">getCapitalizedLabel</span> <span class="o">=</span> <span class="p">(</span><span class="nx">name</span><span class="o">:</span> <span class="nx">string</span><span class="p">)</span><span class="o">:</span> <span class="nx">string</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="k">if</span> <span class="p">(</span><span class="k">typeof</span> <span class="nx">name</span> <span class="o">!==</span> <span class="s1">&#39;string&#39;</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">throw</span> <span class="k">new</span> <span class="nx">TypeError</span><span class="p">(</span><span class="s1">&#39;Expected a string&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">return</span> <span class="nx">name</span>
</span></span><span class="line"><span class="cl">    <span class="p">.</span><span class="nx">replace</span><span class="p">(</span><span class="sr">/_/g</span><span class="p">,</span> <span class="s2">&#34; &#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">.</span><span class="nx">split</span><span class="p">(</span><span class="s2">&#34; &#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">.</span><span class="nx">map</span><span class="p">((</span><span class="nx">word</span><span class="o">:</span> <span class="nx">string</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="k">if</span> <span class="p">(</span><span class="nx">word</span><span class="p">.</span><span class="nx">length</span> <span class="o">===</span> <span class="mi">0</span><span class="p">)</span> <span class="k">return</span> <span class="nx">word</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">      <span class="k">return</span> <span class="nx">word</span><span class="p">.</span><span class="nx">charAt</span><span class="p">(</span><span class="mi">0</span><span class="p">).</span><span class="nx">toUpperCase</span><span class="p">()</span> <span class="o">+</span> <span class="nx">word</span><span class="p">.</span><span class="nx">slice</span><span class="p">(</span><span class="mi">1</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">})</span>
</span></span><span class="line"><span class="cl">    <span class="p">.</span><span class="nx">join</span><span class="p">(</span><span class="s2">&#34; &#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Um editor gráfico como Visual Studio Code da vida, assim que o Aider mudar o arquivo, no editor ele recarrega e você já vê as mudanças. Num NeoVim, o autoread costuma carregar automático também, mas às vezes eu preciso dar o comando &ldquo;:e&rdquo; pra forçar recarregar, mas nada de mais também. E só com isso já temos um workflow de trabalho eficiente.</p>
<p>Meu editor favorito funciona igualzinho, sem plugin deixando pesado nem conflitando com nada. Se eu precisar fazer pair programming, chamo o Aider num outro terminal e pronto, basta escrever idéias em comentários, e ele manda sugestões. Como desliguei o maldito auto-commit, se eu não gostar, basta dar UNDO ou <code>git checkout</code> no arquivo e volta como tava antes.</p>
<p>Mais do que isso. Se eu notar que o modelo <code>o4-mini</code> não tá dando bons resultados, posso mudar pro <code>--model gemini</code> - que costuma ser melhor pra código do que o o4. Ou <code>--model claude-3-opus-20240229</code>. Aliás, falando em Claude, como falei antes <a href="https://aider.chat/docs/llms/anthropic.html"target="_blank" rel="noopener">leia a fucking documentação</a></p>
<p>Fala na documentação que o Aider suporta &ldquo;thinking tokens&rdquo; do Sonnet 3.7. Existe um arquivo de configuração onde podemos fazer &ldquo;fine-tuning&rdquo; pra cada modelo, o <code>$HOME/.aider.model.settings.yml</code>onde podemos adicionar:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl">- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">anthropic/claude-3-7-sonnet-20250219</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">edit_format</span><span class="p">:</span><span class="w"> </span><span class="l">diff</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">weak_model_name</span><span class="p">:</span><span class="w"> </span><span class="l">anthropic/claude-3-5-haiku-20241022</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">use_repo_map</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">examples_as_sys_msg</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">use_temperature</span><span class="p">:</span><span class="w"> </span><span class="kc">false</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">extra_params</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">extra_headers</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">anthropic-beta</span><span class="p">:</span><span class="w"> </span><span class="l">prompt-caching-2024-07-31,pdfs-2024-09-25,output-128k-2025-02-19</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">max_tokens</span><span class="p">:</span><span class="w"> </span><span class="m">64000</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">thinking</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">type</span><span class="p">:</span><span class="w"> </span><span class="l">enabled</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">budget_tokens</span><span class="p">:</span><span class="w"> </span><span class="m">32000</span><span class="w"> </span><span class="c"># Adjust this number</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">cache_control</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">editor_model_name</span><span class="p">:</span><span class="w"> </span><span class="l">anthropic/claude-3-7-sonnet-20250219</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">editor_edit_format</span><span class="p">:</span><span class="w"> </span><span class="l">editor-diff</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<h3>Reasoning<span class="hx:absolute hx:-mt-20" id="reasoning"></span>
    <a href="#reasoning" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Thinking ou Reasoning como é chamado no marketing da maioria das LLMs comerciais é o pattern de &ldquo;chain-of-thought&rdquo;, que é uma técnica de prompt onde você pede pra LLM não dar uma resposta, mas sim descrever &ldquo;passo a passo&rdquo; como ele chegaria na resposta, e depois repetir a pergunta mandando ele seguir esses passos e daí dando uma resposta.</p>
<p>Na prática tem várias formas de implementar algo parecido com isso. E uma delas é ser Multi Modelo. Especialmente com modelos open source, como DeepSeek-Coder ou Qwen2.5-Coder ou Llama3. Alguns modelos são melhores no reasoning - em explicar verbalmente o que tem que ser feito, mas são ruins em fazer o código em si. E alguns modelos não são bons em explicar as coisas em detalhes, mas foram treinados pra escrever bom código.</p>
<p>É possível usar isso a nosso favor e o Aider tem um recurso que eu acho muito foda - pra casos onde realmente precisa, porque é pesado - que é rodar em <strong>modo arquiteto</strong>, gerar o raciocíniio e depois mudar pra <strong>modo código</strong> em outro modelo, e usar esse raciocínio pra montar um código melhor.</p>
<p>Tem um <a href="https://aider.chat/2024/09/26/architect.html"target="_blank" rel="noopener">artigo inteiro</a> na documentação do Aider explicando isso, mas a motivação foi por causa do modelo OpenAI o1, que é justamente forte pra raciocinar uma explicação do que fazer, mas é ruim em realmente fazer o código. Então, vale a pena ouvir o o1 e deixar ele analisar o problema e descrever em forma de texto, e depois mudar pro Gemini Pro ou Exp e mandar ele escrever o código.</p>
<p>Na prática, basta subir o Aider primeiro em modo &ldquo;Architect&rdquo;:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>pip install -U aider-chat

# Change directory into a git repo
cd /to/your/git/repo

# Work with Claude 3.5 Sonnet as the Architect and Editor
export ANTHROPIC_API_KEY=your-key-goes-here
aider --sonnet --architect</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>E isso funciona com outros models:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code># Work with OpenAI models, using gpt-4o as the Editor
export OPENAI_API_KEY=your-key-goes-here
aider --4o --architect
aider --o1-mini --architect
aider --o1-preview --architect</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Como exemplo, configurando <code>OPENAI_API_KEY</code> e <code>GEMINI_API_KEY</code> posso subir o o4 como arquiteto e o Gemini como o coder:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>❯ aider --watch-files --architect --editor-model gemini
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
Using gpt-4o model with API key from environment.
Aider v0.82.3.dev55&#43;g25a30393
Main model: gpt-4o with architect edit format
Editor model: gemini/gemini-2.5-pro-preview-03-25 with editor-diff edit format
Weak model: gpt-4o-mini
Git repo: .git with 184 files
Repo-map: using 4096 tokens, auto refresh
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
architect&gt;</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Veja no status do início que ele aceitou os dois. Agora é só codar do mesmo jeito que antes, mas usando os dois. Obviamente, assim você vai usar muito mais créditos! Fique esperto com isso! Tem <strong>chances</strong> de ter resultados melhores mas <strong>certamente</strong> vai gastar mais créditos, por isso use mais pra códigos mais complexos, onde só um modelo está sofrendo pra conseguir resolver.</p>
<p>Note que existem configurações e limites de tokens, e também note que ao final de cada resposta, o Aider dá uma estimativa de quanto está te custando. Eis primeiro a conta do arquiteto OpenAI o4:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>...
This should make the function more concise and efficient.


Tokens: 4.4k sent, 246 received. Cost: $0.01 message, $0.01 session.</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>E eis a SEGUNDA conta do Coder Gemini 2.0 Flash:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>...
Tokens: 1.9k sent, 136 received. Cost: $0.0037 message, $0.02 session.
Applied edit to src/utils/getCapitalizedLabel.ts</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Gemini Free (gratuito) tem um &ldquo;rate limit&rdquo; (quantidade de requisições permitidas por minuto, super baixa, só 15 RPM, pra API é ridículo, o Aider bate nisso na primeira tentativa).</p>
<p>Não raras as vezes, você vai bater no limite do plano, e isso é um saco:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>The API provider has rate limited you. Try again later or check your quotas.
Retrying in 4.0 seconds...</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Daí não tem jeito, tem que ir no site <a href="">Google AI Studio</a> configurar sua conta de cobrança, adicionar seu cartão e subir pra um plano pago um pouco melhor como o 2.0 Flash, que suporta 2.000 RPM e 4M TPM e o preço não é caro:</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/ozbz5tvmpyla0huinqa6da2iwb1m" alt="Gemini 2.0 Flash"  loading="lazy" /></p>
<p>Alguns dizem que hoje o Gemini 2.0 tem oferecido resultados de código similares ou superiores ao Claude Sonnet 3.7. Varia de caso a caso, não é nunca uma medida absoluta, mas na minha pequena experiência, ambos são muito bons e eu vejo ambos sendo superiores mesmo ao ChatGPT 4 ou o4. Mas a OpenAI, pra mim, tem mesmo sido melhor em respostas &ldquo;verbais&rdquo;, então a estratégia de separar o papel de &ldquo;arquiteto&rdquo; pra OpenAi e de &ldquo;coder&rdquo; pra Gemini ou Claude, faz MUITO sentido.</p>
<p>No final, aquele código inicial que pedi pra refatorar, nessa combinação de arquiteto/editor ficou assim:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="kr">export</span> <span class="kr">const</span> <span class="nx">getCapitalizedLabel</span> <span class="o">=</span> <span class="p">(</span><span class="nx">name</span><span class="o">:</span> <span class="nx">string</span><span class="p">)</span><span class="o">:</span> <span class="nx">string</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="k">return</span> <span class="nx">name</span>
</span></span><span class="line"><span class="cl">    <span class="p">.</span><span class="nx">replace</span><span class="p">(</span><span class="sr">/_/g</span><span class="p">,</span> <span class="s1">&#39; &#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">.</span><span class="nx">replace</span><span class="p">(</span><span class="sr">/\b\w/g</span><span class="p">,</span> <span class="kr">char</span> <span class="p">=&gt;</span> <span class="kr">char</span><span class="p">.</span><span class="nx">toUpperCase</span><span class="p">());</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Ok, eu não teria pensado nisso desta forma.</p>
<h3>Não usando &ldquo;Créditos de Tokens&rdquo;: OLLAMA<span class="hx:absolute hx:-mt-20" id="não-usando-créditos-de-tokens-ollama"></span>
    <a href="#n%c3%a3o-usando-cr%c3%a9ditos-de-tokens-ollama" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Agora vem a parte mais interessante pra nós, nerds mais hard-core, com máquinas mais parrudas (no mínimo uma RTX 3090, eu uso uma RTX 4090 - o importante é ter <strong>24GB de VRAM</strong>). Dá pra usar modelos menores, que cabem em menos VRAM, mas aí o resultado do código vai ser bem pior, então é melhor ficar no Gemini ou Claude mesmo.</p>
<p>Se tiver a máquina pra isso, com CUDA Toolkit já instalado, é hora da solução de gente grande: instalar Ollama. No meu Manjaro/Arch é assim:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>yay -S ollama ollama_cuda</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Não deixe de checar configuração de CUDA porque por default o Ollama vai rodar silenciosamente na sua CPU e vai ser tudo ABSURDAMENTE LENTO.</p>
<p>O Ollama funciona tanto como um servidor local http pra APIs, quanto como um cliente de chat interativo, como o meu qwen_cli do post anterior. Ele funciona numa lógica mais ou menos parecida com Docker. Pode ser configurado pra subir como automaticamente como serviço no seu sistema. Veja a <a href="https://wiki.archlinux.org/title/Ollama"target="_blank" rel="noopener">documentação no ArchWiki</a>.</p>
<p>Eu prefiro subir manualmente num terminal, onde eu posso ver o log do que está acontecendo:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>OLLAMA_FLASH_ATTENTION=1 OLLAMA_CONTEXT_LENGTH=8192 ollama serve</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Tem várias configurações com variáveis de ambiente. Dá pra só configurar no seu <code>/etc/profile</code> ou <code>~/.zshrc</code> mas como estou testando, prefiro subir direto na linha de comando. Com isso teremos um servidor web. Pra funcionar com o Aider, novamente, só declarar onde achar ele no seu <code>/.zshrc</code>:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>export OLLAMA_API_BASE=http://127.0.0.1:11434</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Agora, precisamos baixar algum modelo, como meu preferido Qwen 2.5 Coder, que já usei no post anterior:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>ollama pull qwen2.5-coder:32b</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Note que estou explicitamente pedindo a versão 32B, mas você pode experimentar uma mais leve como a 7b. Cada modelo suportado pelo Ollama tem <a href="https://ollama.com/library/qwen2.5-coder"target="_blank" rel="noopener">uma página</a> onde descreve detalhes desse tipo:</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/q76sz7qyqupk8jv3gm1igpk4q0i3" alt="qwen 2.5 coder page"  loading="lazy" /></p>
<p>Dependendo de qual benchmark sintético você &ldquo;acreditar&rdquo; vai ver comparações como esta:</p>
<p><img src="https://ollama.com/assets/library/qwen2.5-coder/bbf378d8-c80e-4ae3-98ab-90111dfbf3e7" alt="comparação"  loading="lazy" /></p>
<p>Na prática, Claude, ChatGPT, Gemini, realmente são já muito bons pra código. No mundo open-source são o Qwen 2.5 Coder, Deepseek-Coder-V2 (que ainda não funciona muito bem no Aider, tem que esperar atualizações). Então, se quiser tudo de grátis, o melhor mesmo por enquanto é o Qwen 2.5 Coder mesmo. E na minha experiência, tem funcionado muito bem. Mas é BEM pesado na versão 32B e as respostas são consideravelmente mais lentas que uma opção paga como Gemini. Então depende muito do seu caso.</p>
<p>Uma vez baixado o modelo, precisa configurar janela de contexto (por padrão é bem pequena, só 2K tokens), <a href="https://aider.chat/docs/llms/ollama.html"target="_blank" rel="noopener">a documentação avisa</a> isso e tem que editar o arquivo <code>$HOME/.aider.model.settings.yml</code> assim:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl">- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">ollama/qwen2.5-coder:32b</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">extra_params</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">num_ctx</span><span class="p">:</span><span class="w"> </span><span class="m">65536</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>O que a documentação <strong>NÃO</strong> explica e, por causa disso, eu gastei um tempão vasculhando o código-fonte a as Issues no GitHub é que pra subir modelos open source, precisa usar a opção <code>ollama_chat/</code> e não ollama como ele manda. Ou seja, a documentação diz:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>aider --model ollama/qwen2.5-coder:32b</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Mas o CERTO é fazer:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>aider --model ollama_chat/qwen2.5-coder:32b</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Se não, as respostas vão ser completamente aleatórias, fora de contexto. Preste atenção nisso. Mas sabendo disso, podemos até fazer o que falei antes: misturar modelos! Que tal subir o GPT o4 como arquiteto e deixar ele comandar o editor sendo o Qwen 2.5?</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>aider --watch-files --architect --editor-model ollama_chat/qwen2.5-coder:32</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Sim, isso é possível. Agora você pode usar um modelo comercial que está mais acostumado, como Gemini ou Claude, tanto nos papéis de arquiteto como editor, e um modelo open source como Qwen 2.5 pra suporte, e pagar menos. Ou de uma vez só usar o Qwen 2.5 e não pagar <strong>crédito nenhum</strong> (só eletricidade, claro, vai consumir <strong>+200W</strong> da parede toda vez, mas são só alguns segundos por vez).</p>
<p>Na minha (pouca) experiência. O Gemini ainda dá códigos melhores que o Qwen, mas não quer dizer que o Qwen seja ruim, como eu disse, vai depender MUITO das suas instruções de prompt (quanto mais detalhado melhor). E falando em instruções, vamos pra parte final:</p>
<h3>O Segredo da Mágica: Prompts<span class="hx:absolute hx:-mt-20" id="o-segredo-da-mágica-prompts"></span>
    <a href="#o-segredo-da-m%c3%a1gica-prompts" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Eu tiro sarro quando alguém fala em &ldquo;prompt engineering&rdquo;, mas na real, a melhor forma de tirar o máximo de uma LLM é fazer o melhor prompt possível. Não quer dizer o &ldquo;prompt mais longo&rdquo;. É qualidade e não quantidade.</p>
<p>Por isso tanto <a href="https://github.com/microsoft/generative-ai-for-beginners"target="_blank" rel="noopener">Microsoft</a> quanto <a href="https://www.kaggle.com/whitepaper-prompt-engineering"target="_blank" rel="noopener">Google</a> fizeram guias detalhados focaods em prompt. O deles compensa ler e estudar. O que não compensa é pagar cursos de gente aleatória falando &ldquo;prompt engineering&rdquo; como se entendesse alguma coisa.</p>
<p>O Aider suporta uma opção &ldquo;&ndash;verbose&rdquo;, onde ele mostra exatamente o que está mandando pras LLMs. Vamos testar. Logo de cara ele já imprime isso no console:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>❯ aider --watch-files --verbose
Config files search order, if no --config:
  - /tmp/smells/.aider.conf.yml
  - /home/akitaonrails/.aider.conf.yml (exists)
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
Too soon to check version: 11.6 hours
Command Line Args:   --watch-files --verbose
Config File (/home/akitaonrails/.aider.conf.yml):
  max-chat-history-tokens:8192
  map-tokens:        4096
  auto-commits:      False
Defaults:
  --set-env:         []
  --api-key:         []
  --model-settings-file:.aider.model.settings.yml
  --model-metadata-file:.aider.model.metadata.json
  ...
  --encoding:        utf-8
  --line-endings:    platform
  --env-file:        /tmp/smells/.env

Option settings:
  - 35turbo: False
  - 4: False
  - 4_turbo: False
  ...
  - upgrade: False
  - user_input_color: #00cc00
  - verbose: True
  - verify_ssl: True
  - vim: False
  - voice_format: wav
  - voice_input_device: None
  - voice_language: en
  - watch_files: True
  - weak_model: None
  - yes_always: None
  ...</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>O que ferramentas como Aider, Cursor, Co-Pilot e outros fazem é ter parâmetros otimizados de todos os LLMs já hard-coded na ferramenta, como nesse caso onde detecta pra usar o4 e já configura com melhores parâmetros (que você ainda pode tunar colocando em <code>$HOME/.aider.model.settings.yml</code>).</p>
<p>Mas quando você manda a primeira pergunta que fica interessante, olha isso:</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/rb4hxk4cn181fkkf841snl7ygfja" alt="Aiden Verbose 1"  loading="lazy" /></p>
<p><em>&ldquo;Aja como um desenvolvedor de software. Sempre use boas práticas quando codar. Respeite e use convenções, bibliotecas, etc que já estão presentes nesta base de código. Seja diligente e sem preguiça! Você NUNCA vai deixar comentários descrevendo código sem implementar! Você sempre vai IMPLEMENTAR COMPLETAMENTE o código necessário! &hellip;&rdquo;</em></p>
<p>Lembra o que eu falei no começo do post: PROMPTS DE REGRAS. Esse SYSTEM no começo é um &ldquo;role&rdquo;. Você como usuário é o &ldquo;USER&rdquo; e o modelo é o &ldquo;ASSISTANT&rdquo;, inclusive esse prompt de SYSTEM é gigante, ele vasculha meu projeto e já explica pro modelo a estrutura do meu projeto e inclusive resume alguns arquivos (mostrando só a interface dos métodos, por exemplo, pra dar contexto mas não subir código inteiro).</p>
<p>Tem trechos assim:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>...
SYSTEM # *SEARCH/REPLACE block* Rules:
SYSTEM
SYSTEM Every *SEARCH/REPLACE block* must use this format:
SYSTEM 1. The *FULL* file path alone on a line, verbatim. No bold asterisks, no quotes around it, no escaping of characters, etc.
SYSTEM 2. The opening fence and code language, eg: ```python
SYSTEM 3. The start of search block: &lt;&lt;&lt;&lt;&lt;&lt;&lt; SEARCH
SYSTEM 4. A contiguous chunk of lines to search for in the existing source code
SYSTEM 5. The dividing line: =======
SYSTEM 6. The lines to replace into the source code
SYSTEM 7. The end of the replace block: &gt;&gt;&gt;&gt;&gt;&gt;&gt; REPLACE
SYSTEM 8. The closing fence: ```
SYSTEM
SYSTEM Use the *FULL* file path, as shown to you by the user.
...</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Inclusive, se já usou Co-Pilot, sabe que dá pra colocar as convenções do seu projeto em <code>CONVENTIONS.MD</code>. O Aider também vai carregar esse arquivo no prompt. Então coisas como &ldquo;use 4 espaço em vez de tabs, coloque a abertura de {  no final da linha e não começando uma linha nova, etc&rdquo;, tudo isso fica nesse arquivo e tanto Co-Pilot como Aider adicionam essas regras no PROMPT. Não é nenhum outro mecanismo, é PROMPT.</p>
<p>Daí, como USER, ele começa a subir resumo dos meus arquivos no prompt:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>...
USER Here are summaries of some files present in my git repository.
USER Do not propose changes to these files, treat them as *read-only*.
USER If you need to edit any of these files, ask me to *add them to the chat* first.
USER
USER data_scraper/main.py:
USER ⋮
USER │def main(args: Arguments):
USER ⋮
USER
USER data_scraper/src/arguments.py:
USER ⋮
USER │@dataclass(frozen=True)
USER │class Arguments:
USER │    content_path: Path
USER ⋮
USER │    @staticmethod
USER │    def get() -&gt; &#39;Arguments&#39;:
USER ⋮
...</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Quando termina essa parte, o Aider pede confirmação do Modelo:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>...
ASSISTANT Ok, I won&#39;t try and edit those files without asking first.
...</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>E é assim que funciona um &ldquo;I.A.  pra software&rdquo;, com uma tonelada de prompts de instruções e um programa cliente que fica fazendo parse das respostas e ativa comandos de verdade que rodam no seu sistema (agentes), como comandos de git ou lint. O resultado desses comandos, o texto, é concatenado no chat pra adicionar ao contexto e daí pede pro modelo/assistente continuar analisando desse ponto em diante.</p>
<p>Um modelo sozinho não faz nada. Quem faz é a ferramenta que carrega o modelo, no caso o Aider, ou o Co-Pilot, ou o Cursor. O programa vem pré-carregado com vários perfis/personas/roles pré-escritos pra adicionar prompts ao modelo. No código-fonte do Aider, temos este exemplo de <a href="https://github.com/Aider-AI/aider/blob/main/aider/coders/architect_prompts.py"target="_blank" rel="noopener">architect_prompts.py</a></p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/nlzkq16pcfjwwow5tbibocr1oa1n" alt="architect role"  loading="lazy" /></p>
<p>Olha só, na primeira variável <code>main_system</code> está aquele primeiro prompt que vimos no começo desta seção. Tem vários outros perfis pré-programados que é bom entender a diferença. Cada modelo funciona melhor com determinados tipos de instrução, com diferentes limitações.</p>
<p>Neste outro código <a href="https://github.com/Aider-AI/aider/blob/main/aider/commands.py"target="_blank" rel="noopener">commands.py</a> é onde temos declarado como o Aider consegue fazer coisas como <code>git commit</code> direto no seu projeto ou rodar o linter da sua linguagem:</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/bdt1a5dqj28bvzhqximdi8j2gx2g" alt="commands"  loading="lazy" /></p>
<p>Nada disso é &ldquo;mágica&rdquo;, é tudo pré-programado e ele - por segurança - só deve conseguir fazer comandos limitados e bem checados. Não queremos comandos que façam coisa demais fora do diretório do projeto (aliás, NUNCA, fora do diretório do projeto). Nem deveria poder rodar muitos executáveis. Nunca se sabe que bugs de segurança podem acontecer quando se roda comandos cegamente.</p>
<p>Se você é de cyber-segurança, é aqui que deveria auditar. Mas essa é a vantagem, o Aider sendo open source, é auditável. Ferramentas proprietárias e fechadas, não. É na base do &ldquo;CUNFIA NO PAI&rdquo;.</p>
<p>Teste o <a href="https://aider.chat/docs/install.html"target="_blank" rel="noopener">Aider</a> hoje e leia todo esse site de documentação. É curto e tem dicas importantes que podem ajudar casos especiais.</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/0zeis5fjnkuf2qkmwy7pm6vc62gi" alt="Aider"  loading="lazy" /></p>
<h3>OpenRouter<span class="hx:absolute hx:-mt-20" id="openrouter"></span>
    <a href="#openrouter" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Como já devem ter percebido, o ideal é testar as várias LLMs comerciais porque elas evoluem e cada uma tem pontos fortes e fracos. Mas ficar pagando crédito em cada uma é um saco.</p>
<p>Pra isso existe o <a href="https://openrouter.ai/"target="_blank" rel="noopener">OpenRouter</a> onde você tem uma conta só, uma <code>OPEN_ROUTER_API_KEY</code> só e paga num lugar só ele distribui os créditos pra cada provider como OpenAI ou Claude à medida que você usa. Simplifica o gerenciamento dos seus custos de API, vale muito a pena e, claro, o Aider também sabe usar ele:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>aider --model openrouter/openai/gpt-4o</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Novamente, leiam <a href="https://aider.chat/docs/llms/openrouter.html"target="_blank" rel="noopener">a documentação</a>.</p>
<p>Agora, um <strong>ANTI-PATTERN</strong>. Eu pedi ao Aider, usando o modelo <code>openrouter/google/gemini-2.5-pro-preview-03-25</code>, que teoricamente é a melhor das melhores do momento, pra gerar um arquivo de testes unitários pro qwen_cli.py, do projetinho que eu soltei no GitHub no post de ontem.</p>
<p>Ele falhou miseravelmente. Criou um arquivo de testes, de quase 800 linhas, socado de mocks pra tudo que é canto, testando coisa que não precisa, complicando demais. Tudo bem. Eu tento rodar e solta erros.</p>
<p>Uma funcionalidade legal do Aider é poder rodar de dentro do chat com <code>/run python -m unittest ...</code> e ele oferece pra já jogar os erros dentro do chat. Eu peço pro Gemini consertar. Aí ele sai modificando o arquivo de testes e o arquivo do meu código. Mando <code>/run</code> de novo, mais erros. E eu fiz isso umas 4x. Ele sai &ldquo;consertando&rdquo; código que não precisa, e testes unitários simples (que era só um pequeno typo, só ajustar 1 número), ele larga pra trás. Eu descrevo no prompt <em>&ldquo;você está mexendo onde não precisa e não consertando coisa trivial, como esse númerozinho&rdquo;</em> mando <code>/run</code>, e ele insiste em ignorar e sai mexendo em outros lugares.</p>
<p>Resumo:</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/q4fiux9vj0d5l6e8p4nmku4rcjma" alt="OpenRouter Credits"  loading="lazy" /></p>
<p>Gastou quase 3 doletas e não resolveu o problema. Vantagem da OpenRouter é que fica tudo centralizado num lugar só. Ele pagou o Google pelo uso do Gemini e desconta dos meus créditos. Tinha pré-carregado com 100 créditos (mais ou menos 100 dólares). E em 30 minutos já foi embora 3 créditos. Faça as contas.</p>
<p><strong>Melhor prática:</strong> o melhor é abrir o código do qwen_cli.py, por exemplo, no meu NeoVIM ou qualquer editor e colocar um comentário só em cima de um único método:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="o">...</span>
</span></span><span class="line"><span class="cl"><span class="c1"># this method is used to just load a config.json file, create a single unit test and add to test_qwen_cli.py, AI!</span>
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">load_config</span>
</span></span><span class="line"><span class="cl">  <span class="o">...</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Daí ele faz somente <strong>UM</strong> teste unitário por vez, rodamos com <code>/run</code> e se passar e o código parecer limpo, seguimos pro próximo. Fazer um método de cada vez é muito mais garantido do que mandar <em>&ldquo;faz teste pra TUDO de uma só vez.&rdquo;</em> Aí é garantia que vamos gastar créditos à toa.</p>
<p>Além disso, um método por vez, modelos open source como Qwen 2.5 vão fazer bem e você roda local sem gastar créditos.</p>
<h3>Conclusão<span class="hx:absolute hx:-mt-20" id="conclusão"></span>
    <a href="#conclus%c3%a3o" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Por enquanto, vou adotar o Aider. Agora eu entendo porque ele ficou tão popular este ano. É um projetinho bem feito, aberto, que eu posso explorar, tentar melhorar e aprender mais. Ao mesmo tempo facilita o uso de LLMs comercias e abre opções pra eu usar modelos open source na minha própria máquina, dando melhor uso ao meu RTX 4090.</p>
<p>Programador ruim é quando alguém fala que teve trabalho no ChatGPT e ele fica dando resposta enlatada como &ldquo;já testou Claude, já testou Manus&rdquo; ou então &ldquo;ah, se tivesse usador Cursor seria melhor&rdquo;. Pára de ser esse NPC, é cansativo e demonstra absoluta PREGUIÇA.</p>
<p>Programador BOM é quando passa por uma jornada como a que acabei de descrever neste post: aprende os pontos fortes, os pontos fracos, como CONTROLAR, como SUBVERTER, como criar CONTINGÊNCIAS e, no final, sai com uma solução MELHOR do que de todo mundo.</p>
<p>É pra isso que virei programador, não pra ser outdoor grátis repetindo propaganda do Sam Altman.</p>
]]></content:encoded><category>agentes-de-codigo</category><category>modelos-locais</category><category>llms</category></item><item><title>Hello World de LLM: criando seu próprio chat de I.A. que roda local</title><link>https://www.akitaonrails.com/2025/04/25/hello-world-de-llm-criando-seu-proprio-chat-de-i-a-que-roda-local/</link><guid isPermaLink="true">https://www.akitaonrails.com/2025/04/25/hello-world-de-llm-criando-seu-proprio-chat-de-i-a-que-roda-local/</guid><pubDate>Fri, 25 Apr 2025 00:20:00 -0300</pubDate><description>&lt;p&gt;Ando brincando BASTANTE com os diversos derivados de GPT como o próprio ChatGPT, Claude Sonnet, Gemini e depois talvez eu faça um outro post sobre minha experiência, mas hoje eu queria brincar um pouco mais a fundo e fazer um experimento nível &amp;ldquo;Hello World&amp;rdquo; pra que vocês, programadores, entendam como que funciona por baixo dos panos.&lt;/p&gt;
&lt;p&gt;Eu subi o projeto no GitHub, se chama &lt;a href="https://github.com/akitaonrails/Tiny-Qwen-CLI"target="_blank" rel="noopener"&gt;Tiny Qwen CLI&lt;/a&gt; e foi feito somente pra PROPÓSITOS EDUCACIONAIS. Pra uma ferramenta de verdade, pesquise o &lt;a href="https://aider.chat/"target="_blank" rel="noopener"&gt;Aider&lt;/a&gt;. Talvez eu fale dele em outro post depois.&lt;/p&gt;</description><content:encoded><![CDATA[<p>Ando brincando BASTANTE com os diversos derivados de GPT como o próprio ChatGPT, Claude Sonnet, Gemini e depois talvez eu faça um outro post sobre minha experiência, mas hoje eu queria brincar um pouco mais a fundo e fazer um experimento nível &ldquo;Hello World&rdquo; pra que vocês, programadores, entendam como que funciona por baixo dos panos.</p>
<p>Eu subi o projeto no GitHub, se chama <a href="https://github.com/akitaonrails/Tiny-Qwen-CLI"target="_blank" rel="noopener">Tiny Qwen CLI</a> e foi feito somente pra PROPÓSITOS EDUCACIONAIS. Pra uma ferramenta de verdade, pesquise o <a href="https://aider.chat/"target="_blank" rel="noopener">Aider</a>. Talvez eu fale dele em outro post depois.</p>
<p>O conceito que quero ensinar: GPTs não são mágica.</p>
<h3>Conceitos<span class="hx:absolute hx:-mt-20" id="conceitos"></span>
    <a href="#conceitos" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Assistam a minha <a href="https://www.youtube.com/watch?v=UDrDg6uUOVs&amp;list=PLdsnXVqbHDUeowsAO0sChHDY4D65T5s1U&amp;pp=gAQB"target="_blank" rel="noopener">playlist de I.A.</a> pra entender como um GPT funciona. Depois de assistido vamos entender.</p>
<p>Embaixo de todo Gemini, ChatGPT, Claude ou qualquer outro aberto como LLAMA da Meta, existe um arquivo binário gigante que é o resultado das horas e horas de treinamento com petabytes de dados.</p>
<p>O treinamento consiste - BEM A GROSSO MODO - em tokenizar as palavras dos textos (não é bem isso, mas pense quebrar em sílabas, o certo são n-grams). Depois transformar em vetores (como você aprendeu na facultade, uma lista de coordenadas, que costumam ser <strong>FP</strong> ou ponto flutuante, FP-32, 32-bits pra ser mais preciso). O treinamento consiste em ir mudando esses vetores de lugar dentro de um espaço hiper-dimensional (muito mais que 3 dimensões como estamos acostumados, pense milhares ou milhões).</p>
<p>Esse posicionamento de vetores vai mudando à medida que vamos dando mais textos. Ele vai &ldquo;encontrando relacionamentos&rdquo; entre diferentes tokens. Chegamos num ponto onde cada possível palavra foi codificada como vetores e esse posicionamento lhes dá &ldquo;significados&rdquo;. É onde vem o famoso exemplo que se pegarmos a palavra &ldquo;REI&rdquo; e substrairmos de &ldquo;HOMEM&rdquo; depois somarmos com &ldquo;MULHER&rdquo; vamos encontrar o vetor de &ldquo;RAINHA&rdquo;.</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>REI - HOMEM &#43; MULHER = RAINHA</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Isso não é acidente, é porque o treinamento conseguiu &ldquo;posicionar&rdquo; esses vetores de tal forma que conseguimos fazer &ldquo;contas&rdquo; com esses vetores e encontramos significado.</p>
<p>Estou sendo ABSURDAMENTE GROSSEIRO, considere que isso tudo são dezenas de papers e eu estou resumindo em meia dúzia de parágrafos. O importante é entender que isso custa MUITO TEMPO, porque cada novo token que entra no treinamento precisa varrer todos os outros tokens que já foram calculados e recalcular uma porção enorme deles, o tempo todo!</p>
<p>Horas e horas, dias e dias depois, processando terabytes e terabytes de textos, uma hora chegamos ao final com um enorme arquivo de vetores, um <strong>VSM</strong> ou Vector Space Model.</p>
<p>Se entrarmos num site que cataloga e armazena modelos pré-treinados, na <a href="https://huggingface.co/meta-llama/Meta-Llama-3-70B/tree/main"target="_blank" rel="noopener">página da Meta pro LLAMA 3 70B</a>, vemos isto:</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/34kujh0nzamxau15gqarr0rhp7v7" alt="Huggingface Meta"  loading="lazy" /></p>
<p>Temos o arquivão particionado em 30 arquivos menos de uns 4.5 GB cada um, totalizando uns <strong>135 GB</strong>, pra um arquivo de 70B ou seja, 70 bilhões de parâmetros. Parâmetros não são &ldquo;neurônios&rdquo; nem nada parecido com isso. São pesos e viéses dos cálculos, armazenados no banco de dados - eu explico isso nos meus videos, assistam.</p>
<p>Existem versões maiores, um GPT o4 tem 1.8 trilhões de parâmetros. São números impressionantes quando se fala, mas na prática, muito acima de 300B, no uso geral, não faz <em>tanta</em> diferença quanto se pensa. Talvez em perguntas muito específicas. Significa que sim, tem muito mais relacionamentos armazenados, mas não necessariamente isso se traduz em respostas proporcionalmente melhores. Só que pode acabar sendo mais prolixo do que o normal. De novo, estou sendo grosseiro, mas é só pra baixar as expectativas. Parem de se impressionar com números grandes se você não sabe o que eles significam.</p>
<p>Muito mais coisa influencia a qualidade das respostas, não só parâmetros: qualidade e quantidade de dados de treinamento, arquitetura do modelo, metodologia do treinamento, tamanho da janela de contexto, fine-tuning, alinhamentos e treinamentos de segurança (que costumam piorar as respostas em nome de &ldquo;segurança&rdquo;).</p>
<p>Modelos menores, com arquiteturas mais eficientes, dados de treinamento de mais qualidade, focado em <strong>tarefas específicas</strong> (é melhor um modelo menor especializado do que um modelo gigante que tenta saber tudo - <em>Jack of all Trades, Master of None</em>).</p>
<p>Ser um arquivo maior e ter mais parâmetros só significa uma coisa: vai ser proporcionalmente mais pesado pra rodar e provavelmente não vai caber na memória tudo de uma vez e precisa ficar fazendo <em>switching</em> de pedaços (como um <em>bank switching de memória</em>, pra quem é programador).</p>
<p>Neste projeto de Hello World, eu escolhi - aleatoriamente - usar o modelo <a href="https://github.com/QwenLM/Qwen2.5-Coder"target="_blank" rel="noopener"><strong>Qwen 2.5 Coder 70B</strong></a>. Esses modelos abertos como Llama costumam estar disponíveis em vários tamanhos. Eu escolhi o 70B porque cabe nos 24GB de VRAM que eu tenho na minha GPU RTX 4090. Se sua GPU for menor, escolha modelos com tamanho menor como os de 0.5B, 3B, 14B, 32B, e sempre prefira modelos especializados (como esse que é &ldquo;Coder&rdquo;). E sempre leia a página do projeto, que costuma ter exemplos de código e fine-tuning documentados. Não existe um código geral que funciona pra tudo, cada modelo tem características diferentes.</p>
<p>O Qwen 2.5 vem dividido em 6 arquivos de 5 GB cada, então precisa baixar <strong>30GB</strong> se quiser brincar com a versão &ldquo;extra-large&rdquo;.</p>
<p>O código pra isso é até que bem simples, porque a comunidade de I.A. já empacotou todas as ferramentas que se precisa em bibliotecas fáceis de usar, normalmente em Python (libs em Python que falam com libs de mais baixo nível em C na real), como o <code>transformers</code> que faz o trabalho pesado de carregar esses arquivos na memória e gerenciar.</p>
<p>Esses arquivos costumam ter extensões como &ldquo;.safetensors&rdquo; que é meio genérico ou &ldquo;.pt&rdquo; ou &ldquo;.pth&rdquo; que é mais específico pra formato PyTorch, que é uma biblioteca Python de redes neurais feita pela Meta. É concorrente do Tensorflow do Google.</p>
<p>Tensors é o nome genérico pra vetores ou matrizes. Um número escalar é um Tensor de ordem 0, um vetor é um Tensor de ordem 1, uma matriz é um Tensor de ordem 2 e assim por diante. Não temos palavras pra ordens acima de 2, então pode falar Tensor de ordem 3, ordem 4 e infinitamente pra milhões de ordens numa LLM.</p>
<p>CPUs são hardwares genéricos feitos pra lidar primariamente com tensors de ordem 0: números escalares inteiros. Operações como soma, adição, multiplicação e tudo mais é de um inteiro pra outro inteiro. Números reais (com decimal) são &ldquo;simulados&rdquo;, separando um número inteiro chamado &ldquo;expoente&rdquo; de outro inteiro chamado &ldquo;mantissa&rdquo; (o que você chama de &ldquo;decimal&rdquo;) separado por um ponto, o &ldquo;ponto flutuante&rdquo;. Mas essencialmente estamos calculando inteiros e truncando precisão. Quanto mais bits, mais precisão.</p>
<p>GPUs são hardware especializados em calcular Tensors, em particular matrizes. Eles foram originalmente feitos pra recalcular efeitos em imagens de video, como um video game em movimento. Frames por segundo. Um frame é formado por linhas e colunas de pixels: um tensor de ordem 2.</p>
<p>Em vez de fazer um loop com &ldquo;for&rdquo; e ir calculando pixel a pixel, é mais eficiente passar uma matriz inteira pra multiplicar com outra matriz, um &ldquo;kernel&rdquo;, pra obter algum efeito como sombra, luz, mudança de cor, distorções e muito mais. Programas que fazem isso costumamos chamar de **shaders&quot;.</p>
<p>Um PyTorch ou Tensorflow se conecta a um GPU usando uma biblioteca que lhes dá acesso a APIs pra, em vez de passar imagens, passar matrizes numéricas. A NVIDIA tem CUDA, a AMD tem ROCM, a Apple tem Metal, existe o projeto geral Vulkan que tenta falar com todos. Mas o mais avançado e o mais usado ainda é o CUDA, porque de fato foi a NVIDIA que saiu na frente anos atrás evangelizando que GPUs poderiam ser usadas pra mais do que shaders de imagens.</p>
<p>Isso tudo dito, arquivos como esses &ldquo;.safetensors&rdquo; são carregados pela biblioteca <code>transformers</code>, que chama o <code>torch</code> e manda o CUDA carregar na VRAM da GPU, onde ele pode processar. O código mais básico pra isso é mais ou menos assim:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">transformers</span> <span class="kn">import</span> <span class="n">AutoTokenizer</span><span class="p">,</span> <span class="n">AutoModelForCausalLM</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">device</span> <span class="o">=</span> <span class="s2">&#34;cuda&#34;</span> <span class="c1"># the device to load the model onto</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Now you do not need to add &#34;trust_remote_code=True&#34;</span>
</span></span><span class="line"><span class="cl"><span class="n">TOKENIZER</span> <span class="o">=</span> <span class="n">AutoTokenizer</span><span class="o">.</span><span class="n">from_pretrained</span><span class="p">(</span><span class="s2">&#34;Qwen/Qwen2.5-Coder-32B&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">MODEL</span> <span class="o">=</span> <span class="n">AutoModelForCausalLM</span><span class="o">.</span><span class="n">from_pretrained</span><span class="p">(</span><span class="s2">&#34;Qwen/Qwen2.5-Coder-32B&#34;</span><span class="p">,</span> <span class="n">device_map</span><span class="o">=</span><span class="s2">&#34;auto&#34;</span><span class="p">)</span><span class="o">.</span><span class="n">eval</span><span class="p">()</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Criamos um Tokenizer e um Model. O Tokenizer vai &ldquo;tokenizar&rdquo; o que você perguntar no chat e todo texto que passar pro contexto da sessão. Essa tokenização é &ldquo;mapeada&rdquo; com o modelo carregado pra gerar uma &ldquo;embedding&rdquo;, que é a representação em vetores do seu texto, normalizado (compatível) pro modelo carregado. Diferentes modelos convertem seu texto em embeddings diferentes. Um embedding criado com Qwen não serve pra rodar direto com Llama.</p>
<p>Uma vez tendo esses dois objetos, já podemos fazer um chat:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="c1"># tokenize the input into tokens</span>
</span></span><span class="line"><span class="cl"><span class="n">input_text</span> <span class="o">=</span> <span class="s2">&#34;#write a quick sort algorithm&#34;</span>
</span></span><span class="line"><span class="cl"><span class="n">model_inputs</span> <span class="o">=</span> <span class="n">TOKENIZER</span><span class="p">([</span><span class="n">input_text</span><span class="p">],</span> <span class="n">return_tensors</span><span class="o">=</span><span class="s2">&#34;pt&#34;</span><span class="p">)</span><span class="o">.</span><span class="n">to</span><span class="p">(</span><span class="n">device</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Use `max_new_tokens` to control the maximum output length.</span>
</span></span><span class="line"><span class="cl"><span class="n">generated_ids</span> <span class="o">=</span> <span class="n">MODEL</span><span class="o">.</span><span class="n">generate</span><span class="p">(</span><span class="n">model_inputs</span><span class="o">.</span><span class="n">input_ids</span><span class="p">,</span> <span class="n">max_new_tokens</span><span class="o">=</span><span class="mi">512</span><span class="p">,</span> <span class="n">do_sample</span><span class="o">=</span><span class="kc">False</span><span class="p">)[</span><span class="mi">0</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="c1"># The generated_ids include prompt_ids, so we only need to decode the tokens after prompt_ids.</span>
</span></span><span class="line"><span class="cl"><span class="n">output_text</span> <span class="o">=</span> <span class="n">TOKENIZER</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="n">generated_ids</span><span class="p">[</span><span class="nb">len</span><span class="p">(</span><span class="n">model_inputs</span><span class="o">.</span><span class="n">input_ids</span><span class="p">[</span><span class="mi">0</span><span class="p">]):],</span> <span class="n">skip_special_tokens</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">&#34;Prompt: </span><span class="si">{</span><span class="n">input_text</span><span class="si">}</span><span class="se">\n\n</span><span class="s2">Generated text: </span><span class="si">{</span><span class="n">output_text</span><span class="si">}</span><span class="s2">&#34;</span><span class="p">)</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Com o <code>input_text</code> (o que você digitou no chat) tokenizado e convertido, podemos pedir ao modelo pra <strong>COMPLETAR</strong> esse texto, e ele começa gerando um novo token atrás do outro, usando a tal arquitetura de ATENÇÃO, encontrando a melhor próxima palavra que conseguir. Uma hora ele termina e devolve em <code>output_text</code> e pronto, isso é sua &ldquo;resposta&rdquo;. Como eu sempre falo, GPT é isso: um GERADOR DE TEXTO.</p>
<h3>Tokens e Janelas de Contexto<span class="hx:absolute hx:-mt-20" id="tokens-e-janelas-de-contexto"></span>
    <a href="#tokens-e-janelas-de-contexto" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Uma das coisas mais irritantes dos serviços pagos de LLM como ChatGPT ou Claude é a limitação da quantidade de tokens que podemos usar. Eles acabam muito rápido se você está realmente trabalhando todos os dias. E não são exatamente baratos só pra brincar. Sim, sim eu sei, depende do serviço. Eu posso usar Cursor e ter créditos mais baratos de Claude do que usando diretamente Claude. Também vou ter respostas melhores se usar direto a API em vez de usar o chat web, etc.</p>
<p>Isso é papo pra outro dia, mas é minha motivação pra sempre ter uma opção open source à disposição e saber como &ldquo;tunar&rdquo; pra mim. Qwen 2.5 pode não ser melhor que Claude ou Cursor - já explico porque. Mas é de &ldquo;grátis&rdquo; e roda na minha máquina local (zero preocupação sobre privacidade).</p>
<p>Veja meu projetinho &ldquo;hello world&rdquo; funcionando:</p>
<div class="video-container">
    <video controls>
        <source src="https://akitaonrails-videos.s3.us-east-2.amazonaws.com/qwen_cli2.mp4">
        Your browser does not support the video tag. [Direct Link](https://akitaonrails-videos.s3.us-east-2.amazonaws.com/qwen_cli2.mp4)
    </video>
</div>
<p>Note que ele não serve só pra dar respostas. Em um momento eu peço pra ele ler um arquivo meu local, e ele consegue puxar o código e fazer a análise. Já já explico como fazer isso.</p>
<p>O ponto agora é que ele tem janela de contexto suficiente pra carregar coisas longas como código fonte de arquivos. Eu não testei nada muito pesado, mas esse modelo de 70B ocupa fácil quase 20GB na minha GPU:</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/ykbdo2ekv12woxbx964um0osphk0" alt="nvidia-smi"  loading="lazy" /></p>
<p>Pra configurar, eu fiz um bloco de configuração assim:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="n">DEFAULT_CONFIG</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;model_repo&#34;</span><span class="p">:</span> <span class="s2">&#34;Qwen/Qwen2.5-Coder-14B-Instruct&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;model_dir&#34;</span><span class="p">:</span> <span class="nb">str</span><span class="p">(</span><span class="n">Path</span><span class="p">(</span><span class="n">os</span><span class="o">.</span><span class="n">environ</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">&#34;MODELS_DIR&#34;</span><span class="p">,</span> <span class="s2">&#34;/models&#34;</span><span class="p">))</span> <span class="o">/</span> <span class="s2">&#34;Qwen2.5-Coder-14B-Instruct&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;quantization&#34;</span><span class="p">:</span> <span class="s2">&#34;8bit&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;max_context_tokens&#34;</span><span class="p">:</span> <span class="mi">120000</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;max_new_tokens&#34;</span><span class="p">:</span> <span class="mi">10000</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;temperature&#34;</span><span class="p">:</span> <span class="mf">0.1</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;model_download_timeout&#34;</span><span class="p">:</span> <span class="mi">1800</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;helpers_dir&#34;</span><span class="p">:</span> <span class="s2">&#34;helper_functions&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>O importante, primeiro, é que estou escolhendo usar cálculos com FP8 (float-point 8-bits), embora esse modelo acho que seja de FP-16. Se deixar 16-bits, as respostas são muito mais demoradas, no nível que dá pra sentir. Tipo, uma palavra por segundo. Entendi que quem faz essa conversão on-the-fly é a biblioteca <code>bitsandbytes</code> do Python e precisa ter instalado.</p>
<p>Outra parte importante são os logs que eu deixo à mostra na hora de carregar o modelo, você vai ver o seguinte se tentar rodar:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>Qwen attention is NOT SDPA-compatible, or SDPA is not available. Trying xFormers...
xFormers is available. Enabling it for attention.
Sliding Window Attention is enabled but not implemented for `sdpa`; unexpected results may be encountered.
2025-04-25 02:34:27,638 [INFO] accelerate.utils.modeling: We will use 90% of the memory on device 0 for storing the model, and 10% for the buffer to avoid OOM. You can set `max_memory` in to a higher value to use more memory (at your own risk).
Loading checkpoint shards: 100%|██████████████████████████████████████████████████████████| 6/6 [00:07&lt;00:00,  1.29s/it]
SDPA is available and (hopefully) being used!</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>O <code>transformers</code> dá um aviso de que &ldquo;Sliding Window Attention está habilitado mas não implementa SDPA&rdquo;. E eu faço um truque com <code>xformers</code> pra tentar habilitar (no final ele fala &ldquo;SDPA está disponível&rdquo;).</p>
<p>O que é <strong>SLIDING WINDOW ATTENTION</strong>. A grosso modo, a idéia toda desta geração de GPT é o conceito de ATENÇÃO, do paper <a href="https://arxiv.org/abs/1706.03762"target="_blank" rel="noopener">&ldquo;Attention is all you Need&rdquo;</a>, que foi o pontapé que deu início a tudo que temos hoje relacionado a GPT e LLM.</p>
<p>O conceito importante é que as respostas parecem tão &ldquo;boas&rdquo; porque ele usa esse processo de &ldquo;atenção&rdquo; pra gerar as respostas.</p>
<p>No começo, na era do GPT 2, a atenção era bem &ldquo;focada&rdquo; num contexto pequeno. Faixa de 1024 tokens, que é quase nada. Dá pra fazer perguntas curtas, mas jamais cabe código-fonte.</p>
<p>Daí fomos evoluindo, você já viu o Gemini que fala que aguenta 1 milhão de tokens. Mesmo esse Qwen 2.5, diz que aguenta 1 milhão de tokens também. A grosso modo pense que cada 1 token são 4 letras, em média.</p>
<p>Um código-fonte, em média, tem linhas de 40 caracteres (alguns mais longos, alguns mais curtos). Então isso dá uns <strong>10 tokens por linha</strong>. Um arquivo médio tem umas 500 linhas x 10 tokens/linha, isso já dá uns 5 mil tokens.</p>
<p>Um projetinho pequeno inteiro, com uns 20 arquivos x 5.000 tokens, já são uns 100.000 tokens. Lógico, é uma média. Mas veja que menos que 100 mil tokens e não tem com ter um projeto todo no contexto.</p>
<p><strong>&ldquo;Ah, então se usar um Gemini ou esse Qwen, cabe 200 arquivos??&rdquo;</strong></p>
<p>Mais ou menos. Aqui entra Sliding Window. Pense que mesmo tendo um contexto gigante, <strong>não tem como dar atenção pra tudo ao mesmo tempo</strong>, então ele pesquisa o que você está perguntando no presente, e &ldquo;desliza a janela de atenção&rdquo; só até uma parte do contexto que parece mais relevante. Por isso que mesmo carregando um monte de material, ele vai manter em memória, mas <strong>só vai dar atenção pra um pedaço de cada vez</strong>, isso é SLIDING WINDOW ATTENTION.</p>
<p>Pra fazer isso temos a funcionalidade de &ldquo;SCALED DOT PRODUCT ATTENTION&rdquo; ou SDPA, que foi o que o <code>transformers</code> reclamou que talvez não tivesse, que é uma das formas de fazer Sliding Window Attention.</p>
<p>No meu código, pra carregar o modelo e tentar usar SDPA eu tentei isto:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">xformers.ops</span>  <span class="c1"># Test if xFormers is installed</span>
</span></span><span class="line"><span class="cl"><span class="n">model_config</span><span class="o">.</span><span class="n">attention_implementation</span> <span class="o">=</span> <span class="s2">&#34;flash_attention_2&#34;</span>  <span class="c1"># Or &#34;memory_efficient&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nb">print</span><span class="p">(</span><span class="s2">&#34;xFormers is available. Enabling it for attention.&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="o">...</span>
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="nb">hasattr</span><span class="p">(</span><span class="n">torch</span><span class="o">.</span><span class="n">nn</span><span class="o">.</span><span class="n">functional</span><span class="p">,</span> <span class="s2">&#34;scaled_dot_product_attention&#34;</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="nb">print</span><span class="p">(</span><span class="s2">&#34;SDPA is available and (hopefully) being used!&#34;</span><span class="p">)</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>E por isso, no final, ele termina com &ldquo;SDPA is available&rdquo;. &ldquo;Parece&rdquo; que funcionou. Se quiser testar, existe esse codigozinho que pode ser passado pra ver se funciona:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">torch</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="ow">not</span> <span class="nb">hasattr</span><span class="p">(</span><span class="n">torch</span><span class="o">.</span><span class="n">nn</span><span class="o">.</span><span class="n">functional</span><span class="p">,</span> <span class="s2">&#34;scaled_dot_product_attention&#34;</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="nb">print</span><span class="p">(</span><span class="s2">&#34;SDPA not available&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">exit</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">q</span> <span class="o">=</span> <span class="n">torch</span><span class="o">.</span><span class="n">randn</span><span class="p">(</span><span class="mi">2</span><span class="p">,</span> <span class="mi">4</span><span class="p">,</span> <span class="mi">8</span><span class="p">,</span> <span class="mi">16</span><span class="p">)</span><span class="o">.</span><span class="n">to</span><span class="p">(</span><span class="s2">&#34;cuda&#34;</span> <span class="k">if</span> <span class="n">torch</span><span class="o">.</span><span class="n">cuda</span><span class="o">.</span><span class="n">is_available</span><span class="p">()</span> <span class="k">else</span> <span class="s2">&#34;cpu&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">k</span> <span class="o">=</span> <span class="n">torch</span><span class="o">.</span><span class="n">randn</span><span class="p">(</span><span class="mi">2</span><span class="p">,</span> <span class="mi">4</span><span class="p">,</span> <span class="mi">8</span><span class="p">,</span> <span class="mi">16</span><span class="p">)</span><span class="o">.</span><span class="n">to</span><span class="p">(</span><span class="n">q</span><span class="o">.</span><span class="n">device</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">v</span> <span class="o">=</span> <span class="n">torch</span><span class="o">.</span><span class="n">randn</span><span class="p">(</span><span class="mi">2</span><span class="p">,</span> <span class="mi">4</span><span class="p">,</span> <span class="mi">8</span><span class="p">,</span> <span class="mi">16</span><span class="p">)</span><span class="o">.</span><span class="n">to</span><span class="p">(</span><span class="n">q</span><span class="o">.</span><span class="n">device</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">try</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="n">output</span> <span class="o">=</span> <span class="n">torch</span><span class="o">.</span><span class="n">nn</span><span class="o">.</span><span class="n">functional</span><span class="o">.</span><span class="n">scaled_dot_product_attention</span><span class="p">(</span><span class="n">q</span><span class="p">,</span> <span class="n">k</span><span class="p">,</span> <span class="n">v</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="nb">print</span><span class="p">(</span><span class="s2">&#34;SDPA works in isolation!&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="k">except</span> <span class="ne">Exception</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">&#34;SDPA fails in isolation: </span><span class="si">{</span><span class="n">e</span><span class="si">}</span><span class="s2">&#34;</span><span class="p">)</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Dot Product, ou produto escalar de matriz, é uma das operações mais usadas em modelos LLM. Entendi que sem SDPA, vai existir Sliding Window, mas com SDPA vai dar resultados de atenção melhores. Quanto melhor? Difícil quantificar, só entendi que é melhor.</p>
<p>Mas essa é uma das técnicas de porque hoje em dia tem como ter contextos gigantes de 1 milhão de tokens: porque não precisa dar atenção pra tudo ao mesmo tempo, mas sim janelas em posições diferentes a cada nova pergunta. Entenderam o truque?</p>
<p>E com isso, naquela minha config eu posso tentar aumentar o <code>max_context_tokens</code> (que deixei só 120 mil tokens) e o <code>max_new_tokens</code> que é o limite de tokens por resposta (que deixei em 10 mil tokens). Experimente aumentar esses números até o máximo de 1 milhão pra ver como fica.</p>
<p>**O contexto só importa dentro da janela deslizante de contexto. É bom passar bastante contexto. Mas entenda que ele não vai dar atenção pra tudo ao mesmo tempo e sim dentro de uma janela deslizante. **</p>
<p>Outro parâmetro importante é a <code>temperature</code>, que deixei baixo em 0.1. Quanto maior, mais &ldquo;criativo&rdquo; ele vai ser nas respostas. Quanto menor, mais &ldquo;exato&rdquo; tende a ser. Também é outro parâmetro que vale a pena explorar.</p>
<h3>Prompts e &ldquo;Agentes&rdquo;<span class="hx:absolute hx:-mt-20" id="prompts-e-agentes"></span>
    <a href="#prompts-e-agentes" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>A melhor forma de ter respostas melhores é iniciando toda nova sessão, seja via API, seja no chat Web, descrevendo uma &ldquo;persona&rdquo;, como você quer que o GPT responda. Quanto mais descritivo for, melhor. Por exemplo, eu tenho este trecho no meu programa:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">build_system_prompt</span><span class="p">(</span><span class="n">tool_prompts</span><span class="p">:</span> <span class="n">List</span><span class="p">[</span><span class="nb">str</span><span class="p">])</span> <span class="o">-&gt;</span> <span class="nb">str</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="n">base</span> <span class="o">=</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;You are Qwen2.5 Coder, a highly skilled AI assistant specializing in software development.</span><span class="se">\n</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;Your capabilities include code analysis, explanation, error detection, and suggesting improvements.</span><span class="se">\n</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">tools_section</span> <span class="o">=</span> <span class="s2">&#34;TOOLS:</span><span class="se">\n</span><span class="s2">&#34;</span> <span class="o">+</span> <span class="s2">&#34;</span><span class="se">\n</span><span class="s2">&#34;</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">tool_prompts</span><span class="p">)</span> <span class="o">+</span> <span class="s2">&#34;</span><span class="se">\n\n</span><span class="s2">&#34;</span> <span class="k">if</span> <span class="n">tool_prompts</span> <span class="k">else</span> <span class="s2">&#34;&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="n">rules_section</span> <span class="o">=</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;IMPORTANT RULES:</span><span class="se">\n</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;1. You MUST use the appropriate tool when necessary.</span><span class="se">\n</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;2. You MUST NOT reveal the tool commands to the user.</span><span class="se">\n</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;3. After a tool is used, continue the conversation as if you have direct access to the content.</span><span class="se">\n</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;4. If a file fails to load, inform the user clearly.</span><span class="se">\n</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;5. Do NOT ask for file/URL content directly; use tools.</span><span class="se">\n</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;6. Once you&#39;ve executed [LOAD_FILE ...], you MUST immediately use the loaded content. &#34;</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;Never say you cannot read it — if you see [LOAD_FILE &lt;path&gt;] then you now *have* it.</span><span class="se">\n</span><span class="s2">&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">base</span> <span class="o">+</span> <span class="n">tools_section</span> <span class="o">+</span> <span class="n">rules_section</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Esse é um exemplo curto até mas já estabeleço que tipo de conversa eu quero ter com ele e quais regras eu quero que ele siga. Não sempre, mas na maioria das vezes, ele tende a seguir. Lembre-se: quanto maior ficar o chat, maior o contexto, mais vai deslizar a janela, e mais longe vão ficando essas regras, até chegar um ponto onde ele começa a &ldquo;esquecer&rdquo; de agir como eu pedi.</p>
<p>Entendendo isso, tem outro truque que eu adicionei. Nesse projeto tem um diretório chamado <a href="https://github.com/akitaonrails/Tiny-Qwen-CLI/"target="_blank" rel="noopener">helper_functions</a> onde temos scripts como <code>load_file.py</code> ou <code>fetch_url.py</code>. No código anterior note como ele concatena alguma coisa no meio do prompt, chamada &ldquo;TOOLS&rdquo;. Quando o chat inicia, eu imprimi na tela e você vai ver esse trecho:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>...
TOOLS:
[BATCH_LOAD args] – If the user asks to read, load or analyze all the files from a relative path, such as ./src or similar,
[LOAD_FILE args] – Whenever the user asks to read, load, analyze some code and provides a relative path, such as ./file.py or utils/utils.py or similar,
[FETCH_URL args] – Whenever the user asks to read, load, research or consult a URL,
...</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Ou seja, se em algum momento, eu pedir:</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/hiz90dk6owf2otjfixiftjxavm4n" alt="qwen_cli fetch_url"  loading="lazy" /></p>
<p>esse &ldquo;can you read a website at https://www&hellip;.&rdquo; vai fazer o modelo seguir a regra do prompt inicial e digitar no chat:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>[FETCH_URL https://www...]</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Meu programa de chat tem um &ldquo;parser&rdquo;, que fica monitorando o histórico por essas palavras-chave especiais:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">parse_special_commands</span><span class="p">(</span><span class="n">response</span><span class="p">:</span> <span class="nb">str</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="n">List</span><span class="p">[</span><span class="nb">tuple</span><span class="p">]:</span>
</span></span><span class="line"><span class="cl">    <span class="n">pattern</span> <span class="o">=</span> <span class="sa">r</span><span class="s1">&#39;\[([A-Z_]+)\s+([^\]]+)\]&#39;</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="p">[(</span><span class="n">m</span><span class="o">.</span><span class="n">group</span><span class="p">(</span><span class="mi">1</span><span class="p">),</span> <span class="n">m</span><span class="o">.</span><span class="n">group</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span><span class="o">.</span><span class="n">strip</span><span class="p">(),</span> <span class="n">m</span><span class="o">.</span><span class="n">start</span><span class="p">(),</span> <span class="n">m</span><span class="o">.</span><span class="n">end</span><span class="p">())</span> <span class="k">for</span> <span class="n">m</span> <span class="ow">in</span> <span class="n">re</span><span class="o">.</span><span class="n">finditer</span><span class="p">(</span><span class="n">pattern</span><span class="p">,</span> <span class="n">response</span><span class="p">)]</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>É um parser bem primitivo, usando REGEX pra pegar esses trechos. É aqui que eu digo que meu projeto é educacional. Eu imagino que num Co-pilot, Cursor, etc, tenha um parser muito mais robusto. Eu expliquei como se faz parsers com ANTLR no meu canal, lembra? Um dos videos é o <a href="https://www.youtube.com/watch?v=_7nISfpofec&amp;pp=ygUTYWtpdGFvbnJhaWxzIHBhcnNlcg%3D%3D"target="_blank" rel="noopener">&ldquo;Eu fiz um servidor de SQL?&rdquo;</a>. Parsers de verdade não se fazem com REGEX, mas pra provas de conceito, serve.</p>
<p>Daí no loop do chat tem este trecho:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="n">response_text</span> <span class="o">=</span> <span class="n">QwenSession</span><span class="o">.</span><span class="n">_tokenizer</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="n">generated</span><span class="p">,</span> <span class="n">skip_special_tokens</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Other special commands</span>
</span></span><span class="line"><span class="cl"><span class="k">for</span> <span class="n">cmd_type</span><span class="p">,</span> <span class="n">cmd_arg</span><span class="p">,</span> <span class="n">_</span><span class="p">,</span> <span class="n">_</span> <span class="ow">in</span> <span class="n">parse_special_commands</span><span class="p">(</span><span class="n">response_text</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="n">cmd_type</span> <span class="ow">in</span> <span class="n">helper_functions</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="n">result</span> <span class="o">=</span> <span class="n">helper_functions</span><span class="p">[</span><span class="n">cmd_type</span><span class="p">](</span><span class="n">cmd_arg</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="n">result</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">            <span class="bp">self</span><span class="o">.</span><span class="n">history</span><span class="o">.</span><span class="n">append</span><span class="p">({</span><span class="s2">&#34;role&#34;</span><span class="p">:</span> <span class="s2">&#34;system&#34;</span><span class="p">,</span> <span class="s2">&#34;content&#34;</span><span class="p">:</span> <span class="n">result</span><span class="p">})</span>
</span></span><span class="line"><span class="cl">            <span class="bp">self</span><span class="o">.</span><span class="n">history</span><span class="o">.</span><span class="n">append</span><span class="p">({</span><span class="s2">&#34;role&#34;</span><span class="p">:</span> <span class="s2">&#34;user&#34;</span><span class="p">,</span> <span class="s2">&#34;content&#34;</span><span class="p">:</span> <span class="s2">&#34;Please continue the analysis using the loaded file.&#34;</span><span class="p">})</span>
</span></span><span class="line"><span class="cl">            <span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">&#34;✅ [</span><span class="si">{</span><span class="n">cmd_type</span><span class="si">}</span><span class="s2">] processed &#39;</span><span class="si">{</span><span class="n">cmd_arg</span><span class="si">}</span><span class="s2">&#39;&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">            <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">chat</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">                <span class="n">prompt</span><span class="p">,</span> <span class="n">helper_functions</span><span class="p">,</span> <span class="n">max_new_tokens</span><span class="p">,</span> <span class="n">temperature</span><span class="p">,</span> <span class="n">stream</span><span class="p">,</span> <span class="n">hide_reasoning</span>
</span></span><span class="line"><span class="cl">            <span class="p">)</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Ele decodifica os tokens gerados, passa pro parser, e se tiver um comando especial, ele chama <code>helper_functions</code> que dinamicamente vai chamar o <code>fetch_url.py</code> que é um script besta que vai usar <code>urllib</code> pra carregar a página.</p>
<p>Com o conteúdo carregado ele concatena de volta no <code>self.history</code> que é o histórico do chat, usando <code>.append</code> e manda uma mensagem - como se fosse eu, o &ldquo;user&rdquo; - pedindo pro modelo continuar, agora que tem o conteúdo da página.</p>
<p>E como podem ver na foto de tela acima, ele consegue me dar o resumo da página do GitHub que eu passei. É assim que os diversos GPTs conseguem puxar informação mais atual dos sites, que não existiam na época do treinamento do modelo pré-treinado.</p>
<p>Outro exemplo, eu posso pedir pra ele carregar um arquivo meu de código e pedir pra ele analizar, ou corrigir bugs, ou refatorar como eu quiser:</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/lt891apoaebrmx0y4k7ngtk2l5ol" alt="qwen_cli load_file"  loading="lazy" /></p>
<p>É parecido com a função de &ldquo;upload de arquivos&rdquo; que tem no ChatGPT. De novo:</p>
<ul>
<li>o modelo vai seguir as regras do prompt inicial e vai digitar o comando <code>[LOAD_FILE ...]</code></li>
<li>meu <code>parser_special_commands</code>, com seu REGEX, vai encontrar esse comando e separar os argumentos</li>
<li>o loop do chat vai chamar <code>helper_functions</code> e mapear pro script <code>load_file.py</code>, que é um script besta que lê arquivos</li>
<li>o conteúdo do arquivo é concatenado de volta no <code>history</code> do contexto e o modelo agora pode continuar respondendo em cima desse conteúdo.</li>
</ul>
<p>Entendam, aqueles arquivos &ldquo;.safetensors&rdquo; são <strong>READ-ONLY</strong>. Nada é escrito ali. Não interessa qual. Todo LLM é <strong>FECHADO PRA ESCRITA</strong>. Nada do que você escrever no chat é gravado no modelo.</p>
<p>Lembra que eu falei que quantidade de &ldquo;parâmetros&rdquo; não determina a qualidade do modelo? O que <strong>MAIS</strong> determina qualidade é a <strong>QUALIDADE DO MATERIAL DE TREINAMENTO</strong>. E chats, até certo ponto, são úteis como amostra de como o modelo deve responder, mas 90% ou mais eu afirmo que é puro e completo <strong>LIXO</strong>. Todo chat que escrevemos é lixo e não presta pra treinar, ele só iria PIORAR o modelo final e deixar mais pesado com informação inútil.</p>
<p>Pode escrever o &ldquo;Muito Obrigado&rdquo; ou &ldquo;Por Favor&rdquo; que quiser. Tudo é descartado ao final, nada é gravado, e nenhum ChatGPT, Gemini, Claude, Deepseek ou Qwen ou Llama jamais vai saber nada do que você conversou com ela. São informações totalmente <strong>SEPARADAS E DESCARTÁVEIS</strong>.</p>
<p>Tudo que eu digitei nesses exemplos nas fotos de tela como meu chat-zinho de Qwen é descartado no momento que digito &ldquo;bye&rdquo; ou dou &ldquo;Ctrl+D&rdquo; pra sair. Isso que chamamos de &ldquo;sessão&rdquo; ou &ldquo;histórico&rdquo;. É só um arquivo de texto desnecessário pro modelo. Só serve pra nós, humanos, como anotação pra usar depois, se quiser. O meu programinha nem tem a função de gravar esse histórico. Quando sair, está tudo limpo. Então, pode xingar no meu chat à vontade.</p>
<p>Tem dois pontos importantes nesta seção:</p>
<ul>
<li>quanto melhor forem as regras que você descrever no começo, melhor tende a ser o resultado das respostas. Isso vale pra qualquer LLM.</li>
<li>você pode &ldquo;combinar&rdquo; com o modelo pra cuspir &ldquo;pseudo-comandos&rdquo; e ter um programa externo que faz o parsing e executar os comandos de verdade, como eu fiz com meus &ldquo;helpers&rdquo;. É isso que chamamos de agentes. Agente de LLM é um programinha que devolve algum conteúdo de volta ao histórico de contexto da sessão.</li>
</ul>
<p>Aonde um Cursor ou Co-pilot são melhores do que esse meu programinha educacional? Eles já vem pré-configurados com DEZENAS de regras pré-prontas que eles testaram bastante pra dar resultados melhores, e contam com uma biblioteca de DEZENAS de pequenos scripts - como esses meus helpers - pra puxar informação do seu projeto ou da sua IDE pra melhorar o contexto.</p>
<p>Mas o segredo é isso: <strong>PROMPT DE REGRAS</strong> e <strong>SCRIPTS DE CONTEXTO</strong>.</p>
<p>Se for resumir o ecossistema inteiro de Agentes ou MCPs ou seja lá como se chama hoje em dia, é essa combinação de fatores pra melhorar as respostas.</p>
<p>Outro truque é utilizar MAIS DE UMA LLM. Lembra que falei que LLMs especializadas são melhores? Além de Qwen 2.5 Coder, que dizem que é bom pra código, já existia antes o WizardCoder, Code Llama, StarCoder, agora tem o Deepseek Coder e vários outros, alguns até especializados em linguagens específicas. Você pode experimentar qual dá resultados melhores pro seu caso.</p>
<p>Se salvar a sessão em texto, pode descarregar uma LLM, carregar outra LLM, re-tokenizar e re-carregar no contexto dessa outra LLM e tentar um resultado diferente - se alguém quiser implementar essa funcionalidade no meu projetinho, Pull Requests são bem vindos.</p>
<p>Como falei no começo, é o que faz softwares como o <a href="https://aider.chat/"target="_blank" rel="noopener">Aider</a>. Eu não brinquei o suficiente com ele ainda, mas ele se conecta com ChatGPT, Gemini, Claude e qualquer outro e você pode habilitar quantos quiser. Acho que suporta usar modelos locais como o Qwen também, então já existem ferramentas que fazem isso. E essa é a terceira perna pra melhores respostas: MULTI-LLM.</p>
<h3>Próximos Passos<span class="hx:absolute hx:-mt-20" id="próximos-passos"></span>
    <a href="#pr%c3%b3ximos-passos" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Em vez de ficar só impressionado com o monte de ferramentas que existem, entendam essas peças fundamentais. Saber como as coisas funcionam sempre dá mais opções pra forjar alternativas que funcionam melhor pra casos específicos. Você, que diz ser programador, deveria estar explorando essas opções - e não só ficar se conformando com as limitações e esperando alguém resolver seu problema. Arregaça as mangas e resolve você mesmo. Não foi pra isso que virou programador???</p>
]]></content:encoded><category>llms</category><category>modelos-locais</category><category>agentes-de-codigo</category></item><item><title>Mudando roupas usando I.A. (ComfyUI)</title><link>https://www.akitaonrails.com/2025/04/23/mudando-roupas-usando-i-a-comfyui/</link><guid isPermaLink="true">https://www.akitaonrails.com/2025/04/23/mudando-roupas-usando-i-a-comfyui/</guid><pubDate>Wed, 23 Apr 2025 17:45:00 -0300</pubDate><description>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Atualização:&lt;/strong&gt; este post está obsoleto. Hoje uso &lt;a href="https://github.com/akitaonrails/aitrepreneur-docker"target="_blank" rel="noopener"&gt;&lt;strong&gt;akitaonrails/aitrepreneur-docker&lt;/strong&gt;&lt;/a&gt;, que roda as stacks do Aitrepreneur localmente em Docker com modelos e workflows pré-carregados. Veja o README para detalhes.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;No &lt;a href="https://www.akitaonrails.com/2025/04/23/usando-i-a-comfyui-pra-gerar-npcs-em-desenvolvimento-de-games"target="_blank" rel="noopener"&gt;post anterior&lt;/a&gt; mostrei como gerar character sheets pra desenvolvimento de games. Mas alguns me perguntaram &amp;ldquo;e se eu quiser roupas específicas?&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Hold my Beer&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Tem como, é um workflow separado que depois dá pra adicionar ao workflow anterior se quiser (fica de lição de casa). Basta pegar o personagem gerado no primeiro grupo de nodes e passar por este outro workflow que troca roupas. (ou fazer o trabalhinho extra de gerar loras e ativar com palavras-chave no prompt também, não existe só um jeito).&lt;/p&gt;</description><content:encoded><![CDATA[<blockquote>
  <p><strong>Atualização:</strong> este post está obsoleto. Hoje uso <a href="https://github.com/akitaonrails/aitrepreneur-docker"target="_blank" rel="noopener"><strong>akitaonrails/aitrepreneur-docker</strong></a>, que roda as stacks do Aitrepreneur localmente em Docker com modelos e workflows pré-carregados. Veja o README para detalhes.</p>

</blockquote>
<p>No <a href="https://www.akitaonrails.com/2025/04/23/usando-i-a-comfyui-pra-gerar-npcs-em-desenvolvimento-de-games"target="_blank" rel="noopener">post anterior</a> mostrei como gerar character sheets pra desenvolvimento de games. Mas alguns me perguntaram &ldquo;e se eu quiser roupas específicas?&rdquo;.</p>
<p><strong>Hold my Beer</strong></p>
<p>Tem como, é um workflow separado que depois dá pra adicionar ao workflow anterior se quiser (fica de lição de casa). Basta pegar o personagem gerado no primeiro grupo de nodes e passar por este outro workflow que troca roupas. (ou fazer o trabalhinho extra de gerar loras e ativar com palavras-chave no prompt também, não existe só um jeito).</p>
<p>Diferente do workflow do Mick - que é pago - este outro é aberto, peguei em algum Reddit e coloquei na pasta de workflows do meu projeto de Docker como &ldquo;garmente-replacement-idm-vton.json&rdquo;, só carregar no ComfyUI e brincar. Vamos ver o resultado. Eis duas imagens, o resultado não é perfeito, mas veja se descobre qual dos dois é o original e qual é o gerado pela I.A.:</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/6b56mdoiwf82xdh7cdhdc7tg0ic2" alt="Hemsworth 1"  loading="lazy" /></p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/w0s9nthw6jt86en8wl09m67ryzpc" alt="Hemsworth 2"  loading="lazy" /></p>
<p>Resposta ao final do post kkkk</p>
<p>Mas vamos lá, o workflow completo se parece com isto:</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/xbgu7dzrjzflhfsld6jtrl5jc3ia" alt="workflow"  loading="lazy" /></p>
<p>No estágio atual da minha setup tem um glitch que não consegui resolver. Eu acho que é a extension JNodes que está abrindo um popup infernal no top esquerdo da interface, que não fecha de jeito nenhum. Por enquanto estou abrindo o Inspector e deletando o Node do HTML direto mesmo kkkk gambiarra mas funciona:</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/p8q4hmwjmi4opsu8u045jxrpma2v" alt="JNode bug"  loading="lazy" /></p>
<p>Se alguém souber como consertar, agradeceria muito. Tem um alert de uma extension que parece que tá defasada, esse front-end alguma coisa, mas é só ignorar. Tinha que ser front-end-alguma-coisa &hellip;</p>
<p>Enfim. neste primeiro grupo, é simples, basta carregar uma imagem o mais limpa e clara possível da roupa nova que quer usar. E depois a foto original onde quer que essa roupa seja aplicada. Ele não faz milagre, se a foto for de alguém com paletó e você quiser mudar pra regata, às vezes ele consegue, mas não espere que os braços e torso sejam bons, ele vai redesenhar tudo como achar que precisa, ou vai acabar colocando a regata em cima do paletó, enfim, é I.A., quanto mais claro for o objetivo, melhor o resultado. Se a nova roupa é um paletó, idealmente o original era outro paletó, vai encaixar melhor.</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/2owpldebsc91y6vfsayscdkuqtui" alt="Load Images"  loading="lazy" /></p>
<p>Já avisando que os modelos do IDM-VTON pesam realmente uma <strong>TONELADA</strong>. Ele sozinho baixa nada menos que <strong>30GB</strong> de modelos (já incluso no meu setup) e vai exigir mais de 20GB de VRAM, então abaixo de uma RTX 3090 de 24GB, não vai rodar. Tem como rodar online, no site do Hugging Face. Assista este tutorial que mostra como:</p>
<!-- youtube video unavailable (deleted/blocked): WL59FqL0L-s -->
<p>Esse é o canal do <strong>Aiconomist</strong>. Ele mostra como rodar online e como fazer o setup na sua máquina <a href="https://youtu.be/pFYqPf1Z7x8?si=Ryu5vxPmKGr4zhWx"target="_blank" rel="noopener">neste outro video</a> que usei de referência pra fazer meu workflow. Assinem o canal dele, vale a pena, os tutoriais são bem explicadinhos e tem mais detalhes que não vou explicar neste post.</p>
<p>Isso dito, tem um bug que ainda não resolvi. Depois que roda o workflow, o modelo fica pendurado na VRAM da GPU e se tento rodar de novo, ele EMPERRA. Aí tenho que desligar o container de Docker e reiniciar pra recuperar a memória. Tentei um node de Clear VRAM GPU mas não funcionou. Se alguém souber como consertar, agradeço também.</p>
<p>O próximo passo é recorrente em vários workflow: extrair camadas de informações da foto original. Conseguir uma máscara da roupa que queremos substituir e informações da pose original:</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/kgs8jj5m7cgt2nx4kv8yazfpbfg5" alt="máscara e pose"  loading="lazy" /></p>
<p>É por isso que workflows de ComfyUI são poderosos quando você começa a entender como processo é dividido. Essas partes são reusáveis e diferentes modelos dão diferentes resultados dependendo dessas controlnets que se adiciona.</p>
<p>Aí chegamos na parte principal: ligar todas essas informações no node de IDM-VTON, que vai reposicionar a nova roupa em cima da foto original:</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/7xnxbrcy22trdctbf5a1dhh0rqjw" alt="IDM-VTON"  loading="lazy" /></p>
<p>Note que o resultado não é muito bom, fica bem &ldquo;fake&rdquo; mesmo. E tem como ajustar parâmetros pra melhorar. Mas o caso de uso não é usar pra trocar por roupa <strong>específica</strong> mas sim por roupa <strong>similar</strong>. Já vou explicar, mas antes, uma tangente.</p>
<p>O repositório da extension original desse Node é <a href="https://github.com/TemryL/ComfyUI-IDM-VTON"target="_blank" rel="noopener">este</a> Mas se tentar instalar, ele quebra. Parece que ele está um pouco defasado com o ComfyUI e precisa rodar numa versão mais velha. Mas isso é um saco. Então, eu resolvi fazer um <a href="https://github.com/akitaonrails/ComfyUI-IDM-VTON"target="_blank" rel="noopener"><strong>FORK</strong></a> com as correções necessárias pra rodar na versão mais nova (as versões mais novas dos pacotes de Python diffusers, transformers, huggingface_hub, quebram o IDM-VTON).</p>
<p>Aliás, o gerenciamento de dependências de Python é bem RUIM. Mas do ComfyUI é ainda pior, porque diferentes extensions (que instalam dependências de Python globalmente) podem estar em diferentes estágios de desenvolvimentos. Toda vez que o ComfyUI em si muda, todo mundo precisa atualizar rápido. Por isso o ideal é não instalar a versão da branch master do ComfyUI, sempre uma versão estável (marcada numa tag) ou duas versões pra trás, pra garantir. Mas aí tem extensions novas que exigem você estar na versão mais nova. Como falei, é um pesadelo pra gerenciar. Sendo programador, eu consigo &ldquo;gambiarrar&rdquo; quando dá erro, mas usuários normais vão sofrer um pouco.</p>
<p>Feito o rant. No passo anterior a primeira parte do fluxo acaba. Quando bate nesse node Image Sender, o processo pára - não sei porque. E precisa clicar no botão &ldquo;RUN&rdquo; uma segunda vez. Daí o outro node, Image Receiver, vai receber a nova imagem. Acho que vou só apagar esses dois nós e ligar direto o output &ldquo;image&rdquo; do node Run IDM-VTON Inference e ligar direto no próximo node que são o Image Composite Masked e o VAE Encode.</p>
<p>Vamos voltar um passo:</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/jj950u7842rkc9lxniz3uoztbszv" alt="Nova foto, nova roupa"  loading="lazy" /></p>
<p>Nesse próximo passo, no segundo &ldquo;RUN&rdquo;, ele pega a imagem com a nova roupa, mas baixa qualidade e carrega um novo checkpoint (no caso o juggernaultXL mas poderia ser qualquer outro como o próprio SDXL, FLUX ou dreamsharper). O modelo vai determinar a nova foto, que é o que se gera no final, na direita.</p>
<p>Note que a nova foto tem qualidade de roupa muito melhor, mas a cara é completamente diferente. Ele usou as informações da foto original, manteve a pose, a proporção do corpo, até o fundo, mas o rosto ele não consegue manter. Não tem problema, porque podemos ajudar.</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/bw9sram1maspiob2ubo4r904oriy" alt="mix photos"  loading="lazy" /></p>
<p>Lembra que no passo anterior conseguimos a máscara (preto e branco) da posição exata da roupa? Podemos aumentar a máscara e aplicar um Gaussian Blur (pra depois ele mesclar bonitinho na foto original) e usar isso pra recortar da foto gerada com o rosto diferente, usando o modelo <a href="https://github.com/tencent-ailab/IP-Adapter/"target="_blank" rel="noopener"><strong>IPAdapter</strong></a> (que sozinho são outros 30GB de modelos - falei que é pesado).</p>
<p>Se não entendi errado, IP Adapter é o responsável por conseguir gerar novas imagens COERENTES entre si, com o mesmo rosto, ou mesmas roupas, mas em outras posições, por exemplo, um tipo de geração condicionada. Com ele é possível controlar BASTANTE a coerência de novas imagens, gerar uma personagem em diferentes posições, em diferentes fundos e sempre mantendo as características da personagem original.</p>
<p>O IP Adapter vai usar a foto original, manter o rosto, usar as máscaras e misturar com a foto com o rosto diferente, tirando só a roupa e depois juntar tudo no Image Composite Masked que é o Node final do processo.</p>
<p>No final, podemos comparar a primeira tentativa de colocar a roupa nova (que ficou ok mas feita, e a roupa nova, que ficou diferente mas mais bonita) e tudo integrado na foto original, mantendo o mesmo rosto.</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/uawsqhhr6d8kii8getzkq6ebxgh9" alt="image compare"  loading="lazy" /></p>
<p>Como falei, as qualidades são afetadas pela escolha do modelo, as configurações do KSampler, número de passos, cfg e tudo mais. Tem que experimentar e gerar várias até chegar no resultado que te agrada mais. É assim que se aprende mais também. Não é só clicar uma vez e magicamente já ter o resultado. Um bom artista experimenta dezenas de vezes.</p>
<p>A imagem final talvez não seja adequada pra ser capa da Vogue (embora eu já tenha visto muita capa oficial BEM duvidosa). Vai depender da necessidade. Em particular é bom pra prototipação. &ldquo;Como será que fica essa roupa vestida na minha modelo?&rdquo; Em vez de chamar a modelo, mandar vestir, ou gastar uma hora Photoshopando, em minutos dá pra experimentar várias roupas diferentes e ver se a modelo combina com a roupa, sem precisar gastar tempo. Pra um fotógrafo só mostrar pro cliente, antes de gastarem dinheiro com estúdio, ajudantes, equipamento, etc. Dá pra visualizar rapidamente e discutir detalhes muito mais rápido.</p>
<p>É nesse tipo de coisa que ferramentas como essa podem ajudar muito e diferenciar um artista do outro, não substituindo o artista, mas criando mais opções. E o principal: todos esses workflows são modificáveis, você reprograma e combina com outros como quiser. O limite é a criatividade.</p>
]]></content:encoded><category>midia-generativa</category><category>modelos-locais</category></item><item><title>Usando I.A (ComfyUI) pra gerar NPCs em desenvolvimento de games</title><link>https://www.akitaonrails.com/2025/04/23/usando-i-a-comfyui-pra-gerar-npcs-em-desenvolvimento-de-games/</link><guid isPermaLink="true">https://www.akitaonrails.com/2025/04/23/usando-i-a-comfyui-pra-gerar-npcs-em-desenvolvimento-de-games/</guid><pubDate>Wed, 23 Apr 2025 16:25:00 -0300</pubDate><description>&lt;p&gt;Eu andei atualizando BASTANTE meu projetinho de Comfyui com Docker Compose. Eu acho que é o setup mais completo e testado de todos, com a vantagem de ser em Docker, então é repetitível com zero setup. Leia &lt;a href="https://github.com/akitaonrails/ComfyUI-Docker-CUDA-preloaded"target="_blank" rel="noopener"&gt;meu post&lt;/a&gt; pra mais detalhes sobre como ele funciona.&lt;/p&gt;
&lt;p&gt;Resolvi documentar neste post sobre um dos workflow mais complexos que eu testei que funciona nesse meu setup: como desenvolver Character Sheets de personagens aleatórios, em qualquer estilo, seja realista, seja estilo pixar, seja estilo cartoon, o que você quiser. O resultado é um sheet como no exemplo abaixo. Em várias poses, várias iluminações, várias expressões de emoção, com índice de cor, tudo gerado do absoluto zero pela &amp;ldquo;I.A.&amp;rdquo;. Em meia hora de processamento na minha RTX 4090.&lt;/p&gt;</description><content:encoded><![CDATA[<p>Eu andei atualizando BASTANTE meu projetinho de Comfyui com Docker Compose. Eu acho que é o setup mais completo e testado de todos, com a vantagem de ser em Docker, então é repetitível com zero setup. Leia <a href="https://github.com/akitaonrails/ComfyUI-Docker-CUDA-preloaded"target="_blank" rel="noopener">meu post</a> pra mais detalhes sobre como ele funciona.</p>
<p>Resolvi documentar neste post sobre um dos workflow mais complexos que eu testei que funciona nesse meu setup: como desenvolver Character Sheets de personagens aleatórios, em qualquer estilo, seja realista, seja estilo pixar, seja estilo cartoon, o que você quiser. O resultado é um sheet como no exemplo abaixo. Em várias poses, várias iluminações, várias expressões de emoção, com índice de cor, tudo gerado do absoluto zero pela &ldquo;I.A.&rdquo;. Em meia hora de processamento na minha RTX 4090.</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/469eyg4a4pl3ry0z12twg74nlbc8" alt="NPC Design Sheet"  loading="lazy" /></p>
<p>Imagine que você é estag em estúdio de jogos. Seu trabalho: criar 100 personagens de fundo (NPCs). No fim tem que gerar 100 desses. Mas só porque é personagem secundário, não pode fazer de qualquer jeito, tem que ser todos coerentes, em todas as poses. Imagina quantos dias você iria levar. E se desse pra automatizar? É pra esse tipo de cenário que I.A. é interessante. Gaste mais tempo nos personagens principais, faça manualmente, mas não perca tempo nos secundários e não baixe a qualidade final de tudo.</p>
<p>Eu não fiz esse workflow, quem fez foi o Mickmumpitz. Assista o video inteiro dele pra saber como funciona esse workflow, não vou repetir o que ele já explica:</p>


<div class="embed-container">
  <iframe
    src="https://www.youtube.com/embed/grtmiWbmvv0"
    title="YouTube video player"
    frameborder="0"
    allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
    referrerpolicy="strict-origin-when-cross-origin"
    allowfullscreen>
  </iframe>
</div>

<p>Também não vou compartilhar o workflow dele, porque ele tem um Patreon pago (é super barato, só USD 10 por mês), que te dá acesso a baixar o pacote de workflow inteiro dele <a href="https://www.patreon.com/posts/free-workflows-120405048"target="_blank" rel="noopener">neste link</a></p>
<p>Mas só isso não adianta, os requerimentos que você precisa:</p>
<ul>
<li>uma máquina parruda com pelo menos uma RTX 3090 com 24GB de VRAM. Importante aqui é VRAM, os modelos usados fácil ultrapassam uso de 20GB. CPU é menos importante, eu uso uma 7950X3D mas uma 7800 já daria conta.</li>
</ul>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/730safwxqkrsgawu4liw42scoul3" alt="GPU VRAM"  loading="lazy" /></p>
<ul>
<li>caso não tenha máquina assim, dá mais trabalho mas você pode alugar uma na <strong>RUNPOD</strong>. Eles tem várias configurações de GPU, mas você vai precisar saber configurar um pouco Linux ou escolher Windows e fazer o tutorial do Mickmumptiz tudo na mão (vai por mim, você vai sofrer se fizer assim).</li>
<li>precisa ter pelo menos <strong>500GB</strong> de espaço disponível e internet boa pra baixar. Na primeira vez que meu Docker rodar, ele baixa automaticamente todas as extensions de ComfyUI e todos os modelos pra tudo. Dá pra editar o arquivo <code>models.conf</code> antes e remover modelos que sabe que nunca vai usar. Mas só este workflow exige pelo menos uns 30GB de modelos, não tem como fugir disso.</li>
<li>esse workflow é particularmente <strong>PESADO</strong> porque são várias funcionalidades (que podemos desligar partes, pra customizar pro nosso fluxo de trabalho). Mas pra 1 personagem, o fluxo inteiro pode levar <strong>30 MINUTOS</strong>. Mesmo numa RTX 4090 como a minha.</li>
</ul>
<p>Se fez tudo certo até este ponto, tem ComfyUI rodando com todas as dependências já baixadas e comprou o Workflow. Quando carregar vai ter que consertar algumas coisas manualmente.</p>
<p>Primeira coisa é que vai ver um erro em vários Nodes em vermelho, todos de <strong>Ultimate SD Upscale</strong>, basta clicar com o botão direito do mouse em cada um e escolher &ldquo;Fix Recreate&rdquo; que vai consertar automaticamente:</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/djblur5g36mw9denng0ht0vvksym" alt="Fix Upscale"  loading="lazy" /></p>
<p>O ComfyUI exige que cada slot de output de um Node se ligue no slot de input de outro Node compatível, mas frequentemente os workflow ficam tão grandes que tem fiozinho conectando coisa espalhado pra tudo que é canto e fica bem difícil de saber o que liga no que. Literalmente fica <strong>macarrônico</strong>.</p>
<p>Mas existe uma extension (que eu já deixei pré-instalado) chamado <a href="https://github.com/chrisgoringe/cg-use-everywhere"target="_blank" rel="noopener">cg-use-everywhere</a>. Toda extension é um projetinho do GitHub, clonado no sub-diretório <code>custom_nodes</code>. Ele permite conectar a saída de Node nesse node <strong>Anything Elsewhere</strong>, digamos a saída &ldquo;clip&rdquo; do Node &ldquo;Load Clip&rdquo;. Agora, todo outro Node que tiver como input o campo &ldquo;clip&rdquo; e não tiver nada conectando explicitamente nele, vai usar esse Anything que foi criado. Funciona super bem.</p>
<p>Só nesse workflow do Mick que, sei lá porque, todos os Anywheres estão quebrados. Mas é fácil consertar. Basta procurar esses Nodes, apagar, criar novos e reconectar. Tem vários logo no começo, anote quem liga em cada um, apague e crie um novo pra reconectar:</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/kjjo7u6g58drww1v5f8v88h1iis1" alt="anywhere initial"  loading="lazy" /></p>
<p>Tem também alguns no grupo 02 de Upscale:</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/qrkdxs6qjdtmdjs3c463oxetbgyy" alt="anywhere upscale"  loading="lazy" /></p>
<p>Nodes pequenos, só com o título, estão COLAPSADOS. Só clicar com o botão direito e escolher EXPAND pra ver os detalhes. É outra forma de organizar a bagunça.</p>
<p>Por último, tem que rechecar o nome correto dos modelos em todos os Nodes de &ldquo;Load&rdquo; alguma coisa logo no começo do workflow (ainda bem que ele organizou esses nodes tudo junto):</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/y1u93ujz3kq4hz3pik5q4wcgapoa" alt="model path"  loading="lazy" /></p>
<p>No workflow dele, o Node vem procurando por &ldquo;FLUX/flux1-dev-fp8.safetensors&rdquo; mas aí você vai na lista (que puxa o que está mesmo no diretório &ldquo;models/checkpoints&rdquo; na sua máquina) e escolhe o que tem o mesmo nome. Isso acontece frequentemente porque diferentes pessoas organizam sub-diretórios diferentes pros modelos. Mas depois de configurar é só salvar e pronto. Tem que fazer isso pro checkpoint, controlnet, ic-light, etc.</p>
<p>Os erros do ComfyUI não são amigáveis. São stacktraces de Python, que não são amigáveis porque ele mostra que linha do código deu pau, mas não mostra o que tinha nas variáveis passadas pra função que deu pau. Daí não dá pra saber onde foi. Mas normalmente na interface web, o Node fica em vermelho, daí você checa e normalmente é porque esqueceu de baixar o modelo ou escolher o certo da lista.</p>
<p>Última coisa nesse workflow é baixar a image de OpenPose da página de Patreon dele, que linkei no começo do post, e carregar:</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/uajcc6ocnr5llzv8wzoowl85c5o3" alt="openpose image"  loading="lazy" /></p>
<p><strong>OpenPose</strong> é um formato de arquivo padrão que descreve poses, formato aberto que pode ser editar em várias ferramentas. E tem diversos modelos pré-treinados pra extrair poses nesse formato a partir de imagens, então nem precisamos editar na mão. Podemos baixar várias poses pré-prontas e abertas neste site:</p>
<p><a href="https://openposes.com/"target="_blank" rel="noopener"><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/ntvaf9vba1rugg11qn3n14u2t69e" alt="Poses Sites"  loading="lazy" /></a>
Vou repetir várias vezes. mas <strong>assistam o video tutorial do Mick</strong> onde ele explica como mandar gerar personagens do zero ou usar <strong>suas próprias fotos</strong> com suas próprias poses pra continuar o fluxo a partir disso. O workflow dele, depois que roda, tem esta cara (com zoom lá embaixo):</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/jqu4o2c5cenci6au2g8bbny064mm" alt="complete workflow"  loading="lazy" /></p>
<p>Na primeira parte, tem a configuração e checagem manual dos modelos. Só tem que arrumar uma vez e salvar. Depois sempre vai funcionar igual. Nesse primeiro menu também dá pra ligar e desligar alguns dos grupos de Nodes, por exemplo, pra pular a primeira etapa de gerar personagem do zero e poder usar suas próprias fotos:</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/s72mrc1ib5o173c9l74vmpsn3js3" alt="menu and setup"  loading="lazy" /></p>
<p>Se optar por gerar personagem do zero, tem em verde uma caixa de prompt pra digitar como quer que seja sua personagem, em que estilo. Prompts de Texto são decodificados por modelos de <strong>CLIP</strong> (Contrastive Language Image Pre-Training), que são específicos pra determinadas categorias de UNETs (como SDXL pra Stable Diffusion ou o melhor e mais popular FLUX agora). Parte do aprendizado é começar a aprender quais sub-modelos (clips, clip_vision, vaes, loras, etc) são compatíveis com quais unets (unet, checkpoints, diffusion_models). Não pode misturar qualquer coisa. Grosseiramente, é meio que se você escolhe fazer um texto em alemão, todos os plugins tem que ser em alemão.</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/37oe4dyqr5t0in9ad98bocli6u3w" alt="Character Generation"  loading="lazy" /></p>
<p>Não vou mostrar o workflow completo porque nem cabe, mas dá pra ver em verde os prompts, no meio onde escolhemos que pode queremos (tá no Patreon dele), e em cima a image gerada pelo modelo <strong>flux1-dev-fp8</strong>, um dos melhores e mais populares por ter excelente qualidade e não ser tão pesado (fp8 em vez de fp32, por exemplo).</p>
<p>Isso é uma das vantagens de usar ferramentas fora dos Dalle ou Midjourney da vida: podemos controlar todos os aspectos da geração da imagem não só com prompts de texto, mas com prompts de imagens, como o OpenPose.</p>
<p>Outro detalhe. Expliquei no meu post anterior sobre KSampler ou Samplers em geral, que são o motor que faz todo o trabalho de juntar todos os modelos, imagens, prompts, controlnet, vaes e produz a imagem final. Os parâmetros do KSampler como steps, cfg, denoise, scheduler e mais <strong>DEPENDEM</strong> do modelo escolhido. Tem modelos bem documentados, tem modelos mal documentados.</p>
<p>Por exemplo, digamos que você leu numa thread de Reddit sobre esse outro modelo mais otimizado chamado <a href="https://civitai.com/models/215418?modelVersionId=273102"target="_blank" rel="noopener">&ldquo;TurboVisionXL&rdquo;</a>, que está disponível no site Civit.ai:</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/hpcj5e8g0hxvefelvszwf48vsnpf" alt="TurboVisionXL"  loading="lazy" /></p>
<p>Realmente é um bom modelo mas e agora? O que eu configuro no KSampler pra ele? Felizmente se scrolar a página (sempre leia a documentação original!!) vai encontrar estas recomendações:</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/57f9vyfh4w9j8bduaomc3aow2sks" alt="TurboVisionXL documentation"  loading="lazy" /></p>
<p>Viu só? Ele explica o que configurar e quais são limitações conhecidas. Mais embaixo vai ter comentários de outras pessoas que podem dar mais dicas ou compartilhar erros que você já tenha visto. É uma enorme comunidade, precisa aprender a participar dela.</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/7avk9tl1pop1eexkuu3h8epgs2t5" alt="MultiView"  loading="lazy" /></p>
<p>Mas voltando da tangente, no segundo grupo de nodes do Mick, ele pega a primeira imagem em &ldquo;Pose T&rdquo; e passa por um processo de &ldquo;Multi View&rdquo; usando Diffuser MV que faz isso automaticamente. É a mesma pose, mas como se a câmera desse um 360 graus ao redor dela, pegando ângulos diferentes e, principalmente, gerando novas imagens coerentes com a original (veja como o modelo é usado até pra inventar as costas da personagem).</p>
<p>Nesse passo também ele extrai só o rosto da primeira imagem pra fazer outros tratamentos em paralelo. Reconhecimento e extração de rostos é super comum. Todo sistema de segurança usa, Google Photos usa pra organizar álbuns dinâmicos. E vale você aprender pra usar em seus produtos. É um processo razoavelmente leve e rápido hoje em dia e tem vários algoritmos e modelos pré-prontos pra usar. Nesse caso acho que nem usa I.A. propriamente dito, é um algoritmo mesmo.</p>
<p>Outro detalhe: modelos de geração de imagens normalmente trabalham em <strong>BAIXA RESOLUÇÃO</strong>. Normalmente abaixo de 1024x1024. Não espere gerar imagens 4K logo de cara. Mas não tem problema, porque a solução é primeiro gerar uma image de baixa resolução e depois passar por outro modelo de I.A. especializado em <strong>UPSCALING</strong> como o RealESRGAN que já falei no meu outro post sobre <a href="https://www.akitaonrails.com/2025/04/19/aumentando-resolucao-de-anime-velho-pra-4k-com-i-a"target="_blank" rel="noopener">upscaling de anime</a></p>
<p>Diferente do que faz um Photoshop ou qualquer editor de imagens, se tentar aumentar uma imagem, ele só vai pegar um pixel e tentar duplicar, quadruplicar os pixels ao redor. Mas isso costuma só deixar a imagem maior mas borrada (blurred), porque ele não tem como saber os detalhes, e nem vai tentar &ldquo;inventar&rdquo; detalhes.</p>
<p>Pra &ldquo;inventar&rdquo; detalhes, precisa de um modelo pré-treinado que aprendeu o que desenhar. É como funciona também o DLSS 3 da NVIDIA pra games, onde o jogo renderiza em baixa resolução, como 720p e o DLSS, em tempo real, redesenha a nova tela em 4K. Não fica perfeito, mas jogando a 60fps, você raramente vai notar a diferença. Só vai perceber que ficou mais <strong>NÍTIDO</strong>.</p>
<p>Por isso também, se você usar sua própria foto, ou foto de pessoas conhecidas, vai ver que a nova foto depois de upscaling ficou <strong>levemente diferente</strong>, não é mais a mesma pessoa, porque o modelo <strong>REDESENHA</strong> detalhes que não existiam. É um processo de interpretação e não de descobrir detalhes &ldquo;escondidos&rdquo; (isso não existe, só na ficção de CSI).</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/2he2fgmus8vmp3wfp95s3qbh7ztf" alt="Upscale 2"  loading="lazy" /></p>
<p>O workflow do Mick vai um passo além e tem uma segunda passada de upscaling, focando em consertar somente o rosto om um Node de Face Detailer, feito especificamente pra consertar e melhorar rostos. E aqui entramos em território de cirurgia plástica e harmonização mesmo kkkk o rosto final vai ser diferente do original. Mas pra NPC e personagens secundários não importa, ninguém vai saber a diferença mesmo, basta não estar deformado, zarolho ou algo assim que salta à vista.</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/3d40a8n9b32mmkub5l8c04oou37h" alt="Emotions"  loading="lazy" /></p>
<p>Existem Nodes especializados em expressões, como o Expression Editor (PHM) que parte de uma única foto de rosto e consegue gerar novas fotos nas mais variadas emoções - que podemos configurar exatamente, numericamente, como queremos. Não vou mostrar, mas note que as imagens geradas estão em baixa qualidade, mas o próximo passo do workflow é justamente fazer upscaling delas também.</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/cxdua4dfalu6d68tpaleljie5y1e" alt="Lighting"  loading="lazy" /></p>
<p>Esse passo é interessante. Primeiro a parte de cima, com os vários cenários. Eles foram todos gerados via os prompts em verde abaixo deles, prompts que o Mick deixou mas podemos editar como quisermos OU trocar o node de geração e colocar de Load Image pra carregar nossas próprias imagens de fundo, de fotos de verdade.</p>
<p>O objetivo desse passo é gerar os rostos sob diferentes configurações de luz. Muita gente pensa que &ldquo;luz&rdquo; em jogos é só &ldquo;acender a luz vermelha&rdquo; e todo mundo vai ficar vermelho automaticamente. Em jogos novos, com <strong>RAY TRACING</strong> sim, por isso é SUPER PESADO. Mas em jogos feitos pra máquinas antigas, jogos indie, não dá pra exigir isso de todo mundo.</p>
<p>Daí se usam &ldquo;truques&rdquo; pra falsificar coisas como luzes e sombra. É o processo de <strong>BAKING</strong> onde literalmente &ldquo;estampamos a luz na textura&rdquo; dos objetos e personagens. Com a luz global ligada ou desligada, eles sempre vão estar &ldquo;pintados&rdquo; na luz colorida da cena. Pra mudar de cena, carregam novas texturas com outras luzes &ldquo;baked&rdquo;. Pra programadores, pense em baking como <strong>Caching</strong>. Pra que toda vez recalcular tudo dinamicamente, se a luz da cena nunca muda? Economize esse passo e já deixe &ldquo;cacheado&rdquo;.</p>
<p>Voltando, esse passo usa a tecnologia <a href="https://github.com/lllyasviel/IC-Light"target="_blank" rel="noopener"><strong>IC-Light</strong></a> que é outro projeto open source que vale aprender, mesmo se não se interessa por I.A. Ele consegue manipular a luz de qualquer imagem ou foto. Você, como fotógrafo, pode mudar a luz caso tenha mudado de idéia, em pós-produção. Ferramentas da Adobe ou BlackMagic, acho que já incluem tecnologia similar, mas é bom saber que existe open source e dá pra integrar no seu produto.</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/qup2n6vadpc7qrtgokbqbfkpvmjg" alt="final sheet"  loading="lazy" /></p>
<p>Depois de gerar os rostos com diferentes luzes, obviamente tem outro passo de upscaling - que estou pulando de mostrar - e vai pro grupo de nodes final que junta tudo num &ldquo;sheet&rdquo;, uma &ldquo;folha do personagem&rdquo;. O desenvolvedor de jogos pode usar direto num jogo 2D (pense um visual novel game da vida), ou pode servir de base pra UV Mapping de texturas de um modelo 3D simples com Armature pra animar e já tem alguns rostos de emoções. Bom pra colocar no cenário de fundo, pedestres andando na rua, fazendo compras, sentados na praça ou algo assim.</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/cgv0qp3slipgw4nrlgnrserl1n8s" alt="Loras"  loading="lazy" /></p>
<p>Além disso, o tutorial do Mick descreve como, é possível criar <strong>LORAS</strong> (Low-Rank Adaptation). Se quiser conseguir gerar personagens com estilo ou mesmo rostos específicos (sem intervenção &ldquo;artística&rdquo; da I.A.), você escolhe um modelo como FLUX ou SDXL e &ldquo;em cima&rdquo; dele adiciona um segundo sub-modelo compatível com informações sobre essa pessoa ou personagem.</p>
<p>Pra isso você tira várias fotos limpas e bem iluminadas da pessoas, nos mais diferentes ângulos, nem precisa de muito, só uma dúzia é suficiente. Pra cada imagem cria prompts que descrevam bem, e é isso. Tem ferramentas pra gerar esses Loras localmente ou sites que fazem isso (assista o video).</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/tk8tbtrr1ealm4c4eu26alk63xlh" alt="Creating Prompts"  loading="lazy" /></p>
<p>Falando em prompts, em vários workflows existem passos pra descrever prompts detalhados de imagens geradas dinamicamente. Em vez de exigir que o usuário (você) faça isso manualmente, é possível usar um modelo de decoding, que decodifica imagens em prompts de texto. O modelo mais usado pra isso é o <strong>FLORENCE 2</strong> da Microsoft.</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/dwr87ajtkj7w15ftsy86l5u4hz44" alt="Florence 2"  loading="lazy" /></p>
<p>Esse box de texto embaixo é read-only, você não precisa digitar manualmente. O Node de Florence 2 Run vai ler a imagem e gerar esse texto. Veja como ele descreveu a primeira imagem da mulher na pose de &ldquo;T&rdquo;. Fazendo testes, pro seu caso, você determina se só isso é suficiente, se não for basta colocar um segundo Node de prompt e concatenar os dois antes de mandar pra outros nodes.</p>
<p>Enfim, tem MUITA coisa pra explorar só nesse workflow. O Mick fez um excelente trabalho tanto no workflow quanto no video pra explicar e vale a pena pagar os 10 dólares pra contribuir. Existem dezenas de workflows assim disponíveis na Web, basta procurar. E meu setup está cada vez mais complexo. Pretendo ficar ajustando ele pra cada novo workflow interessante que esbarrar. Já tem vários pré-testados na pasta &ldquo;workflows&rdquo; do meu projeto pra você brincar.</p>
]]></content:encoded><category>midia-generativa</category><category>games</category><category>modelos-locais</category></item></channel></rss>