<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Vitoria AYRES</title>
    <description>The latest articles on DEV Community by Vitoria AYRES (@vitoriaayres).</description>
    <link>https://dev.to/vitoriaayres</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4228897%2F690b34e9-c4a0-4e0b-99ea-75a6a6fcefec.jpg</url>
      <title>DEV Community: Vitoria AYRES</title>
      <link>https://dev.to/vitoriaayres</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/vitoriaayres"/>
    <language>en</language>
    <item>
      <title>Testei LLMs no ENEM 2024, e o ranking não diz o que parece</title>
      <dc:creator>Vitoria AYRES</dc:creator>
      <pubDate>Thu, 05 Nov 2026 15:41:01 +0000</pubDate>
      <link>https://dev.to/vitoriaayres/testei-llms-no-enem-2024-e-o-ranking-nao-diz-o-que-parece-2ong</link>
      <guid>https://dev.to/vitoriaayres/testei-llms-no-enem-2024-e-o-ranking-nao-diz-o-que-parece-2ong</guid>
      <description>&lt;p&gt;Um modelo de 27 bilhões de parâmetros ficou à frente de um de 120 bilhões no meu benchmark de ENEM. A manchete seria fácil, mas ela estaria errada: os três modelos que testei empataram estatisticamente.&lt;/p&gt;

&lt;p&gt;Construí um benchmark aberto para medir LLMs em português do Brasil. O resultado mais útil da primeira rodada não foi quem ganhou, mas o que a ordem do placar não permite concluir.&lt;/p&gt;

&lt;h2&gt;
  
  
  Por que medir em português do Brasil
&lt;/h2&gt;

&lt;p&gt;Os placares populares são em inglês, e traduzir questões não mede o que importa para quem usa esses modelos aqui. Entender a norma culta, a cultura, as instituições e os implícitos da língua exige perguntas feitas no nosso contexto.&lt;/p&gt;

&lt;p&gt;Comecei pelo ENEM porque é uma prova pública, conhecida e com gabarito oficial.&lt;/p&gt;

&lt;h2&gt;
  
  
  Como medi
&lt;/h2&gt;

&lt;p&gt;A primeira rodada usou 73 questões de texto do ENEM 2024: 40 de conhecimento e 33 de interpretação. Questões que dependem de imagem ficaram de fora, porque um modelo só de texto não teria como resolvê-las.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Condições iguais:&lt;/strong&gt; mesmo prompt, temperatura 0 e o mesmo limite de 2048 tokens de saída para todos.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Intervalo de confiança em toda nota:&lt;/strong&gt; com dezenas de questões, alguns pontos de diferença podem ser só ruído.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sem chute na correção:&lt;/strong&gt; se a resposta não indica uma alternativa válida, ela vale 0.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Custo zero:&lt;/strong&gt; tudo rodou em planos gratuitos, inclusive o limite de cerca de 1 requisição por minuto do Qwen.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Do PDF ao dataset
&lt;/h2&gt;

&lt;p&gt;As questões saem direto dos PDFs oficiais do INEP, e essa foi a parte menos glamorosa do projeto. O texto extraído com pypdf vem cheio de ruído: marca d'água, código de barras da página, cabeçalhos e até o rodapé da gráfica colado no fim da última linha.&lt;/p&gt;

&lt;p&gt;O extrator limpa esse ruído com expressões regulares e separa a prova pelos marcadores de questão. Um detalhe: alguns títulos usam espaços especiais do Unicode, como U+2003, e um espaço comum na regex não os encontra.&lt;/p&gt;

&lt;p&gt;Depois vêm os filtros, que deixam cada questão fora em vez de arriscar um item quebrado:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Questões 1 a 5 saem, porque são de inglês ou espanhol.&lt;/li&gt;
&lt;li&gt;Questões sem gabarito saem, porque foram anuladas.&lt;/li&gt;
&lt;li&gt;Questões sem as 5 alternativas lidas por inteiro saem.&lt;/li&gt;
&lt;li&gt;Questões que provavelmente dependem de imagem saem.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Das 85 questões em português do primeiro dia, 73 passaram. O filtro de imagem é uma heurística: procura palavras como "charge", "gráfico" e "mapa", e também desconfia de enunciados com menos de 80 caracteres. Ele pode errar nos dois sentidos, e isso é uma limitação conhecida.&lt;/p&gt;

&lt;p&gt;As questões não ficam no repositório, porque os textos de apoio têm direitos de terceiros. O projeto distribui o extrator, e cada pessoa gera os itens a partir dos PDFs públicos.&lt;/p&gt;

&lt;h2&gt;
  
  
  Rodar de graça sem perder resultado
&lt;/h2&gt;

&lt;p&gt;O avaliador é uma CLI em Python, com cerca de 2.700 linhas de código e 194 testes automatizados. As chamadas passam por um cliente único via LiteLLM, então adicionar um modelo é acrescentar uma entrada em um arquivo YAML.&lt;/p&gt;

&lt;p&gt;No plano gratuito, o maior desafio é a cota. Três mecanismos tornam a execução segura de interromper e retomar:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Cache em SQLite:&lt;/strong&gt; a chave é um SHA-256 de modelo, item, tentativa, mensagens e parâmetros. Se qualquer um deles muda, a chave muda, e uma resposta antiga nunca é reaproveitada por engano.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Limitador de taxa:&lt;/strong&gt; respeita limites por provedor e por modelo. Na Groq, configurei 30 requisições por minuto e 1.000 por dia, e o Qwen ficou em 1 por minuto.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Novas tentativas só para erros transitórios:&lt;/strong&gt; são até 3, com espera exponencial a partir de 2 segundos, respeitando o &lt;code&gt;retry-after&lt;/code&gt; do provedor, com teto de 60 segundos.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;O cache guarda apenas respostas bem-sucedidas, então uma nova execução refaz só o que falhou. Isso tem um custo: como &lt;code&gt;max_tokens&lt;/code&gt; faz parte da chave, mudar esse parâmetro refaz todas as respostas do modelo. Com o Qwen, 76 chamadas levam cerca de 76 minutos.&lt;/p&gt;

&lt;h2&gt;
  
  
  Como a resposta vira nota
&lt;/h2&gt;

&lt;p&gt;O prompt deixa o modelo raciocinar, mas exige que ele termine com &lt;code&gt;Resposta: X&lt;/code&gt;. O extrator pega a última ocorrência desse marcador, ignorando negrito, maiúsculas e acentos. Sem o marcador, só aceita a última linha se ela for exatamente uma alternativa.&lt;/p&gt;

&lt;p&gt;Essa regra é propositalmente rígida. Ela favorece modelos que seguem formato, e essa escolha é consciente: seguir instrução faz parte de ser útil. O ponto mais delicado é separar a falha do modelo da falha da infraestrutura:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Situação&lt;/th&gt;
&lt;th&gt;Nota&lt;/th&gt;
&lt;th&gt;Entra na média&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Correta&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;Sim&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Incorreta&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;Sim&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Sem resposta: recusa, resposta vazia, alternativa inexistente ou ambígua&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;Sim&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Erro de API que persistiu após as novas tentativas&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;Não&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Um timeout do provedor não diz nada sobre o modelo, então fica fora da média. Na rodada publicada, nenhum modelo teve erro de API.&lt;/p&gt;

&lt;h2&gt;
  
  
  Como calculei a incerteza
&lt;/h2&gt;

&lt;p&gt;A nota geral é uma média macro: primeiro a média de cada categoria, depois a média das categorias. Assim, a categoria com mais itens não domina o placar. Por isso o Qwen aparece com 97,0%, embora tenha acertado 71 de 73 questões, ou 97,3%.&lt;/p&gt;

&lt;p&gt;O intervalo de confiança vem de um bootstrap por percentil. São 2.000 reamostragens com reposição, feitas dentro de cada categoria e com semente fixa, então qualquer pessoa obtém o mesmo intervalo.&lt;/p&gt;

&lt;p&gt;Comparar intervalos sobrepostos não basta para decidir um empate, porque todos os modelos responderam às mesmas questões. Por isso, calculo a diferença questão a questão e faço o bootstrap sobre essa diferença. Se o intervalo inclui 0, os modelos estão empatados.&lt;/p&gt;

&lt;h2&gt;
  
  
  O resultado
&lt;/h2&gt;

&lt;p&gt;O Qwen3.8 27B ficou em primeiro com 97,0%, mas os intervalos dos três modelos se sobrepõem. Com 73 questões, a ordem não aponta um vencedor.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Posição&lt;/th&gt;
&lt;th&gt;Modelo&lt;/th&gt;
&lt;th&gt;Geral&lt;/th&gt;
&lt;th&gt;IC de 95%&lt;/th&gt;
&lt;th&gt;Conhecimento&lt;/th&gt;
&lt;th&gt;Interpretação&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;Qwen3.8 27B&lt;/td&gt;
&lt;td&gt;97,0%&lt;/td&gt;
&lt;td&gt;92,4 a 100&lt;/td&gt;
&lt;td&gt;100%&lt;/td&gt;
&lt;td&gt;93,9%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;GPT-OSS 120B&lt;/td&gt;
&lt;td&gt;93,9%&lt;/td&gt;
&lt;td&gt;87,9 a 98,5&lt;/td&gt;
&lt;td&gt;100%&lt;/td&gt;
&lt;td&gt;87,9%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;GPT-OSS 20B&lt;/td&gt;
&lt;td&gt;89,7%&lt;/td&gt;
&lt;td&gt;82,1 a 95,7&lt;/td&gt;
&lt;td&gt;97,5%&lt;/td&gt;
&lt;td&gt;81,8%&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;A nota geral é a média das duas categorias. Em conhecimento, dois modelos gabaritaram, então é a interpretação de texto que separa os resultados.&lt;/p&gt;

&lt;h2&gt;
  
  
  O que aparece quando se olha questão a questão
&lt;/h2&gt;

&lt;p&gt;Nenhuma das três comparações pareadas exclui o zero. As diferenças abaixo usam as 73 questões, com o mesmo peso para cada uma:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Comparação&lt;/th&gt;
&lt;th&gt;Diferença (pontos percentuais)&lt;/th&gt;
&lt;th&gt;IC de 95%&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Qwen3.8 27B e GPT-OSS 20B&lt;/td&gt;
&lt;td&gt;+6,8&lt;/td&gt;
&lt;td&gt;-1,4 a +15,1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GPT-OSS 120B e GPT-OSS 20B&lt;/td&gt;
&lt;td&gt;+4,1&lt;/td&gt;
&lt;td&gt;-1,4 a +11,0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Qwen3.8 27B e GPT-OSS 120B&lt;/td&gt;
&lt;td&gt;+2,7&lt;/td&gt;
&lt;td&gt;-4,1 a +8,2&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Os erros contam uma história mais interessante que a média:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;A prova está perto do teto:&lt;/strong&gt; os três acertaram 63 das 73 questões, ou 86%. Só 10 questões separam os modelos, e nenhuma derrubou os três.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Os erros se concentram:&lt;/strong&gt; 8 das 10 questões estão entre a 22 e a 31, e 9 das 10 são de interpretação.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;As famílias erram em lugares diferentes:&lt;/strong&gt; o Qwen errou as questões 25 e 26, e nenhum GPT-OSS errou essas duas. Os dois GPT-OSS compartilham 3 erros: 22, 28 e 29.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Não vi preferência por letra:&lt;/strong&gt; a distribuição das alternativas escolhidas ficou a no máximo 3 questões do gabarito em cada letra.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Quantas questões seriam necessárias para separar o Qwen do GPT-OSS 120B? Eles discordaram em 6 das 73 questões, com saldo de 2 para o Qwen. Se essa proporção se mantivesse, seriam cerca de 420 questões para o intervalo de 95% excluir o zero. É uma conta aproximada, mas mostra a escala: o próximo ganho vem de mais itens e itens mais difíceis, não de mais modelos.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tokens e latência
&lt;/h2&gt;

&lt;p&gt;O modelo maior foi o mais econômico: o GPT-OSS 120B gastou metade dos tokens de saída do 20B, com nota numericamente maior.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Modelo&lt;/th&gt;
&lt;th&gt;Tokens de saída (total)&lt;/th&gt;
&lt;th&gt;Mediana por resposta&lt;/th&gt;
&lt;th&gt;Máximo&lt;/th&gt;
&lt;th&gt;Latência mediana&lt;/th&gt;
&lt;th&gt;Latência p95&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;GPT-OSS 120B&lt;/td&gt;
&lt;td&gt;17.187&lt;/td&gt;
&lt;td&gt;218&lt;/td&gt;
&lt;td&gt;706&lt;/td&gt;
&lt;td&gt;0,83 s&lt;/td&gt;
&lt;td&gt;1,55 s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Qwen3.8 27B&lt;/td&gt;
&lt;td&gt;34.255&lt;/td&gt;
&lt;td&gt;448&lt;/td&gt;
&lt;td&gt;934&lt;/td&gt;
&lt;td&gt;1,25 s&lt;/td&gt;
&lt;td&gt;2,10 s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GPT-OSS 20B&lt;/td&gt;
&lt;td&gt;34.813&lt;/td&gt;
&lt;td&gt;367&lt;/td&gt;
&lt;td&gt;2.048&lt;/td&gt;
&lt;td&gt;1,07 s&lt;/td&gt;
&lt;td&gt;2,84 s&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Dois cuidados na leitura. Tokens não são diretamente comparáveis entre famílias: o mesmo conjunto de prompts deu 29.385 tokens de entrada nos GPT-OSS e 26.301 no Qwen, porque os tokenizadores são diferentes. E a latência depende da fila do provedor no plano gratuito, por isso ela é registrada, mas não entra na nota.&lt;/p&gt;

&lt;h2&gt;
  
  
  O modelo que raciocinou até acabar os tokens
&lt;/h2&gt;

&lt;p&gt;O achado mais curioso veio de um erro. Em 2 questões, o GPT-OSS 20B devolveu uma resposta vazia.&lt;/p&gt;

&lt;p&gt;A primeira suspeita foi o limite de 2048 tokens, e o uso registrado confirmava: ele gastou tudo raciocinando. Testei de novo com 4096 e 6500 tokens, e ele continuou sem responder. Nas mesmas questões, o GPT-OSS 120B e o Qwen resolveram com menos de 800 tokens.&lt;/p&gt;

&lt;p&gt;Essa investigação expôs uma falha no meu próprio benchmark. A regra tratava a resposta vazia como erro de infraestrutura, que fica fora da média. Na prática, o modelo deixava de responder e não perdia ponto. Mudei a regra: resposta vazia agora é falha do modelo e vale 0.&lt;/p&gt;

&lt;p&gt;Também decidi não aumentar o limite só para esse modelo. A metodologia exige o mesmo &lt;code&gt;max_tokens&lt;/code&gt; para todos, e o aumento não resolveu. Com a regra nova, as 2 questões contam como &lt;code&gt;sem_resposta&lt;/code&gt;, e a nota do 20B reflete o custo real desse comportamento. Ainda não sei por que ele raciocina tanto nelas: isso segue como pergunta aberta.&lt;/p&gt;

&lt;h2&gt;
  
  
  O que este resultado não prova
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Que o Qwen é melhor:&lt;/strong&gt; a diferença cabe dentro do intervalo de confiança.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Que os modelos dominam o português:&lt;/strong&gt; a rodada cobre só questões de texto do ENEM, sem redação ou pragmática.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Que não houve contaminação:&lt;/strong&gt; as questões são posteriores à data de corte dos GPT-OSS, mas falta um grupo anterior para comparar, e o Qwen não tem data de corte configurada.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Que os modelos sabem o conteúdo, e não a posição:&lt;/strong&gt; o benchmark gira as alternativas para testar isso, mas essa verificação não rodou nesta primeira rodada.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Que o resultado se repete:&lt;/strong&gt; cada questão foi respondida uma vez, e temperatura 0 não garante respostas idênticas entre execuções.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Os próximos passos são criar questões mais difíceis, incluir ironia e vida prática no Brasil e avaliar redações. Em um benchmark, saber o que o número não diz vale tanto quanto o número.&lt;/p&gt;

&lt;p&gt;O código, a metodologia e o placar estão no &lt;a href="https://github.com/vitoriaayres/avaliacao-llm-ptbr" rel="noopener noreferrer"&gt;repositório do projeto no GitHub&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>python</category>
      <category>llm</category>
      <category>benchmark</category>
    </item>
  </channel>
</rss>
