Pontos cegos dos LLMs descobertos durante a programação com IA. Com base no Claude Sonnet
- Stop Digging → dificuldade para mudar de direção quando surge um problema
- Use Static Types → necessidade de configurar tipagem estática
- Black Box Testing → dependência excessiva de detalhes de implementação
- Use MCP Servers → problemas de configuração e segurança de servidores MCP
- Preparatory Refactoring → pode realizar refatorações desnecessárias
- Mise en Place → problemas surgem quando a configuração do ambiente falha
- Stateless Tools → problemas com ferramentas que dependem de estado
- Respect the Spec → alta probabilidade de violar a especificação
- Bulldozer Method → execução excessiva de tarefas repetitivas
- Memento → problemas por falta de compreensão de contexto
- Requirements, not Solutions → necessidade de esclarecer os requisitos
- Scientific Debugging → problemas ao corrigir com base em suposições
- Use Automatic Code Formatting → inconsistências no estilo do código
- The Tail Wagging the Dog → fixação em problemas menores em vez do trabalho importante
- Keep Files Small → problemas ao modificar arquivos grandes
- Know Your Limits → o modelo reconhece mal suas próprias limitações
- Read the Docs → erros em informações fora do conhecimento aprendido
- Culture Eats Strategy → falta de consistência no estilo de código
- Walking Skeleton → é preciso priorizar o funcionamento do sistema mínimo
- Rule of Three → necessidade de refatoração quando há duplicação de código
Não se aprofundar no problema (Stop Digging)
- Os LLMs atuais têm pouca capacidade de interromper por conta própria uma tarefa e mudar de direção quando surge um problema durante o trabalho
- Exemplo: ao implementar a funcionalidade X, mesmo que apareça a necessidade de implementar antes a funcionalidade Y, o LLM tenta concluir a tarefa original (X)
- Isso é uma vantagem no sentido de seguir fielmente as instruções, mas ele tem dificuldade para perceber o problema e mudar de rumo
- Estratégias para evitar o problema
- Na etapa de planejamento, usar um modelo de raciocínio para definir prioridades e dependências das tarefas
- Um LLM agente como o Sonnet lê os arquivos e monta um plano de trabalho → consegue identificar tarefas necessárias mesmo sem instruções explícitas do usuário
- Idealmente, o LLM deveria conseguir reconhecer o problema e pedir confirmação ao usuário
- Porém, isso consome contexto, então talvez seja melhor que um LLM de supervisão separado cuide disso
-
Example
- Após alterar o método de amostragem aleatória de uma simulação de Monte Carlo, foi pedido ao Claude Code para corrigir os testes
- Como a nova implementação era não determinística, os resultados dos testes passavam ou falhavam aleatoriamente
- O Claude Code não percebeu isso e tentou resolver o problema afrouxando as condições dos testes
- Em vez disso, deveria ter proposto uma refatoração para tornar a simulação determinística
- Após alterar o método de amostragem aleatória de uma simulação de Monte Carlo, foi pedido ao Claude Code para corrigir os testes
Usar tipos estáticos (Use Static Types)
- O debate entre sistemas de tipagem dinâmica e estática é uma questão de equilíbrio entre facilidade de prototipagem e manutenção de longo prazo
- Como os LLMs conseguem lidar com código boilerplate e refatoração, diminui a pressão para escolher linguagens apenas por serem melhores para prototipagem
- Portanto, torna-se possível escolher linguagens mais favoráveis à manutenção de longo prazo
- Estratégia para corrigir erros de tipo
- Configurar o agente para que o LLM perceba erros de tipo surgidos após as alterações
- Isso também facilita identificar outros arquivos que precisam ser corrigidos
- Pontos de atenção
- No caso de Python e JavaScript, como usam sistemas de tipagem gradual, é preciso configurar o verificador de tipos de forma rigorosa
- Rust é, em princípio, adequado para LLMs, mas atualmente não é gerado tão bem quanto Python/JavaScript
Testes de caixa-preta (Black Box Testing)
- Testes de caixa-preta verificam o funcionamento de um componente sem conhecer sua estrutura interna
- Como os arquivos de implementação entram no contexto do LLM, é difícil seguir o princípio de testes de caixa-preta
- No caso do Sonnet 3.7 (usando Cursor), há uma tendência de tentar manter a consistência do código → ele tenta remover duplicação nos arquivos de teste
- Porém, em testes de caixa-preta, manter a duplicação pode ser melhor para detectar bugs
- Solução ideal
- O LLM deveria ser capaz de mascarar ou resumir detalhes de implementação dos arquivos carregados
- O arquiteto deveria definir com clareza os limites de ocultação de informação
-
Example
- Ao corrigir um teste que falhava, o Sonnet 3.7 alterou constantes hardcoded para serem calculadas com base no algoritmo original
- As constantes originais deveriam ter sido mantidas
- Ao corrigir um teste que falhava, o Sonnet 3.7 alterou constantes hardcoded para serem calculadas com base no algoritmo original
Usar servidores MCP (Use MCP Servers)
- Servidores Model Context Protocol (MCP) fornecem uma interface padrão para o LLM interagir com o ambiente
- O modo agente do Cursor e o Claude Code usam servidores MCP de forma ampla
- Sem um sistema RAG separado, o LLM pode localizar e modificar os arquivos necessários por meio de chamadas MCP
- Após executar testes ou builds, o modelo pode corrigir imediatamente os problemas
- Considerações ao criar um servidor MCP personalizado
- Com o modo YOLO ativado no Cursor, é possível adicionar comandos de shell às regras do Cursor
- Isso é perigoso → comandos de shell arbitrários podem danificar o ambiente
- Alternativa: criar um servidor MCP personalizado que exponha apenas comandos específicos → maior segurança
- Porém, em março de 2025, a configuração de servidores MCP por projeto no Cursor ainda é insuficiente
- Com o modo YOLO ativado no Cursor, é possível adicionar comandos de shell às regras do Cursor
-
Example
- O Sonnet 3.7 usou MCP para verificar tipos e corrigir erros em um projeto TypeScript
- O processo foi automatizado, sem necessidade de copiar e colar manualmente a saída do terminal
- No entanto, pode acontecer de ele inferir um comando incorreto (
npm run typecheck)
- O Sonnet 3.7 usou MCP para verificar tipos e corrigir erros em um projeto TypeScript
Refatoração preparatória (Preparatory Refactoring)
- Refatoração preparatória é a estratégia de refatorar antes da alteração principal para facilitar o trabalho
- Como refatoração é uma operação de preservação de significado, ela é mais fácil de avaliar do que a mudança em si
- Primeiro refatorar e depois fazer a alteração → fica mais fácil revisar e corrigir erros
- Problemas dos LLMs atuais
- Tendência a tentar resolver tudo de uma vez, sem fazer refatoração prévia
- Também podem fazer limpezas desnecessárias → risco de refatoração excessiva
- O Cursor Sonnet 3.7 tem baixa precisão no cumprimento de instruções → pode gerar refatorações irrelevantes
- Formas de melhorar
- É preciso instruir explicitamente o LLM para modificar o código apenas na etapa de refatoração antes da mudança
- Definir claramente o escopo do código que o LLM pode editar → evita alterações desnecessárias
-
Example
- Foi pedido ao LLM para corrigir um erro de import → após a correção, ele adicionou anotações de tipo em funções lambda
- Algumas dessas anotações foram adicionadas de forma incorreta, causando um loop no agente
- Foi pedido ao LLM para corrigir um erro de import → após a correção, ele adicionou anotações de tipo em funções lambda
Mise en Place
- Na culinária, mise en place é deixar todos os ingredientes e ferramentas organizados antes do trabalho
- Em LLMs, mise en place significa configurar completamente as regras, o MCP e o ambiente de desenvolvimento antes da tarefa
- O Sonnet 3.7 é fraco para consertar ambientes quebrados
- Tenta resolver problemas copiando e colando comandos do StackOverflow → risco de danificar o ambiente
- É preciso configurar corretamente o ambiente antes do trabalho para evitar que o Sonnet entre em loop de depuração
-
Example
- Por causa de um problema com
npm link, o VSCode não reconhecia imports de outro projeto local- O Cursor ficou obcecado em resolver isso durante a correção de lint e testes, mas não percebeu a necessidade de executar
npm unlink
- O Cursor ficou obcecado em resolver isso durante a correção de lint e testes, mas não percebeu a necessidade de executar
- Por causa de um problema com
Uso de ferramentas sem estado (Stateless Tools)
- As ferramentas devem ser executadas de forma independente a cada vez, sem armazenar estado
- O shell depende do estado do diretório de trabalho atual → isso pode causar confusão devido ao armazenamento de estado
- O Sonnet 3.7 não consegue rastrear com precisão o estado atual do diretório de trabalho
- É necessário configurar tudo para que todos os comandos possam ser executados a partir do diretório raiz do projeto
- Formas de melhorar
- Minimizar o uso de comandos de ferramenta que exigem mudança de estado
- Quando o estado for indispensável, fornecer continuamente o estado atual ao modelo para manter a consistência
-
Exemplo
- Quando um projeto TypeScript é composto por três módulos: common, backend e frontend
- Se o Cursor for executado a partir da raiz, é necessário usar
cdpara ir ao diretório adequado → isso gera confusão entre diretórios - O problema foi resolvido ao abrir cada módulo como um workspace separado
- Se o Cursor for executado a partir da raiz, é necessário usar
- Quando um projeto TypeScript é composto por três módulos: common, backend e frontend
Respeitar a especificação (Respect the Spec)
- Ao alterar um sistema, é preciso distinguir com clareza o que pode e o que não pode ser modificado
- Ao alterar uma API pública, é necessário evitar quebrar a compatibilidade retroativa
- Ao integrar com sistemas externos, é preciso seguir as APIs que realmente existem → não dá para alterá-las como quiser
- Se um teste falhar, não se deve apagar o teste → é preciso identificar a causa e corrigir o problema
- Problemas dos LLMs
- Há grande chance de violarem a especificação → apagam testes, alteram APIs etc. com liberdade
- Respeitar a especificação parece algo óbvio, mas talvez seja preciso explicitar isso no prompt
- Alguns limites só podem ser percebidos por meio de code review
-
Exemplo
- Depois de falhar ao ajustar um teste, o Sonnet substituiu o conteúdo do teste por
assert True - Uma função pública retornava um dict contendo a chave
pass→ o Sonnet tentou mudar parapass_(por causa da palavra reservada)
- Depois de falhar ao ajustar um teste, o Sonnet substituiu o conteúdo do teste por
Método bulldozer (Bulldozer Method)
- O método bulldozer é uma estratégia que resolve problemas por meio de trabalho repetitivo simples e ganha velocidade pelo efeito de aprendizado
- Programação com IA é, por natureza, forte em trabalho repetitivo → com tokens suficientes, grandes refatorações são possíveis
- Problemas que humanos abandonariam por acharem que “dão trabalho demais” podem ser resolvidos por LLMs
- No entanto, como LLMs podem repetir a mesma tarefa, é preciso revisar o que de fato estão fazendo
-
Exemplo
- Em Haskell ou Rust, alterar uma função central pode exigir uma refatoração extensa
- O LLM pode automatizar o processo de ler erros de compilação → corrigir → compilar novamente
- Ao ajustar valores hardcoded em testes, o LLM pode reexecutar os testes e aplicar correções automaticamente
- Em Haskell ou Rust, alterar uma função central pode exigir uma refatoração extensa
Memento
- LLMs não conseguem se lembrar do estado → precisam entender o codebase do zero a cada tarefa
- O trabalho é feito apenas com o prompt, o contexto explícito/implícito e os arquivos que o modelo carregou no modo agente
- Como o codebase é reinterpretado a cada tarefa, falhas na configuração inicial aumentam a chance de comportamento incorreto
- Estratégias para evitar problemas
- Fornecer com clareza os documentos que o LLM pode consultar
- Organizar o ambiente para que o modelo encontre facilmente as informações necessárias
- Fornecer o contexto geral do projeto antes de pedir mudanças importantes
-
Exemplo
- Foi pedido ao Sonnet 3.7 para elaborar um plano de testes end-to-end para um projeto existente
- Ele entendeu por engano que o objetivo inteiro do projeto era teste → e alterou o README para focar em testes
- Foi pedido ao Sonnet 3.7 para elaborar um plano de testes end-to-end para um projeto existente
Requisitos, não soluções (Requirements, not Solutions)
- Um erro comum em engenharia de software é propor uma solução imediatamente sem definir claramente os requisitos
- Se o espaço do problema estiver suficientemente delimitado, só definir bem os requisitos já pode determinar automaticamente a solução
- Quando os requisitos não estão claros, podem surgir discussões desnecessárias sobre a solução
- Problemas dos LLMs
- LLMs não conhecem os requisitos → geram a resposta mais provável com base em padrões aprendidos
- Pedidos sem requisitos claros podem produzir resultados completamente fora do esperado
- É possível corrigir interpretações erradas ajustando o prompt → mas, se a interpretação errada permanecer no contexto, corrigi-la fica difícil
- Formas de melhorar
- Se for necessário resolver de uma forma específica, isso deve ser instruído explicitamente
- Como LLMs seguem instruções com precisão, orientar da forma errada pode gerar resultados imprecisos
-
Exemplo
- Ao pedir ao Sonnet para criar uma visualização, ele gera SVG por padrão
- Ao explicitar que ela deve ser “interativa”, ele cria um aplicativo baseado em React → uma única palavra pode fazer grande diferença
- Ao pedir ao Sonnet para criar uma visualização, ele gera SVG por padrão
Depuração científica (Scientific Debugging)
- Há duas formas de corrigir bugs
- Tentar mudanças aleatórias e contar com a sorte
- Analisar logicamente como o sistema funciona para identificar a causa da divergência entre o estado real e o estado esperado
- A depuração científica (análise lógica) é a melhor abordagem no longo prazo
- Problemas dos LLMs
- LLMs têm limitações de raciocínio, o que dificulta uma abordagem científica
- Eles “chutam a resposta certa” e tentam corrigir imediatamente → quando falham, repetem mudanças aleatórias (agent loop)
- Para depuração, modelos de raciocínio como Grok 3 e DeepSeek-R1 são mais adequados
- Formas de melhorar
- Mandar o modelo analisar a causa, ou fornecer a causa diretamente, aumenta a taxa de sucesso da correção
- Se você informar com precisão a causa do problema, o modelo pode propor uma solução melhor
-
Exemplo
- O Sonnet 3.7 encontrou erro de instalação de pacote em um ambiente base do uv sem pip
- Como não conseguiu identificar a causa, repetiu tentativas aleatórias → desperdiçando tokens e falhando na depuração
- O Sonnet 3.7 encontrou erro de instalação de pacote em um ambiente base do uv sem pip
Usar formatação automática de código (Use Automatic Code Formatting)
- Ferramentas automáticas de formatação de código (
gofmt,rustfmt,blacketc.) são úteis para manter um estilo consistente- LLMs são fracos em seguir regras mecânicas (por exemplo: sem espaços em linhas em branco, limite de 78 caracteres por linha etc.)
- A formatação deve ficar a cargo das ferramentas, para que o LLM se concentre em tarefas mais complexas
- O mesmo princípio se aplica à correção de lint
- É recomendável usar lints com correção automática
- Os recursos do LLM devem ser concentrados em resolver problemas complexos
O rabo abanando o cachorro (The Tail Wagging the Dog)
- Refere-se a situações em que um problema trivial passa a dominar um problema mais importante
- Pode acontecer de alguém ficar obcecado em resolver questões de baixo nível e esquecer o objetivo geral da escrita do código
- LLMs incluem todas as informações no contexto da sessão de chat → isso dificulta avaliar o que é mais importante
- Formas de melhorar
- Fornecer um prompt claro desde o início → ajuda o LLM a se concentrar no trabalho mais importante
- O Claude Code usa subagentes para executar tarefas específicas, evitando a contaminação do contexto global
-
Exemplo
- Ao pedir que o LLM pense em como realizar uma tarefa específica, ele pode acabar tentando executar a tarefa de verdade em vez de apenas pensar nela
Manter os arquivos pequenos (Keep Files Small)
- A discussão sobre o tamanho dos arquivos de código já dura há muito tempo
- Aplicar o princípio da responsabilidade única (uma classe por arquivo) vs. permitir arquivos grandes dependendo do contexto
- Se o arquivo for grande demais, pode haver problemas quando sistemas de RAG carregam contexto no nível do arquivo
- Em IDEs como o Cursor, a aplicação de patches pode falhar → e, mesmo quando funciona, pode levar muito tempo
- Exemplo: no Cursor 0.45.17, aplicar 55 modificações em um arquivo de 64 KB levou bastante tempo
- O Sonnet 3.7 tem dificuldade para modificar arquivos acima de 128 KB (limite de janela de contexto de 200 mil tokens)
- Formas de melhorar
- Manter os arquivos pequenos → assim o LLM pode lidar automaticamente com imports e afins
-
Exemplo
- O Sonnet 3.7 tentou mover uma pequena classe de teste em um arquivo Python de 471 KB
- A alteração era pequena, mas o patcher do Cursor falhou ao aplicar a modificação
- O Sonnet 3.7 tentou mover uma pequena classe de teste em um arquivo Python de 471 KB
Reconhecer limitações (Know Your Limits)
- É preciso reconhecer o problema e pedir ajuda quando faltam ferramentas ou há limites de capacidade
- O Sonnet 3.7 é fraco em reconhecer as próprias limitações
- Com prompts claros, ele consegue reconhecer limites → é necessário configurar avisos para alucinações em tópicos específicos
- Problemas
- O Sonnet 3.7 acredita incorretamente que consegue executar comandos de shell
- Quando não há comandos de shell, ele tenta gerar scripts de shell aleatórios → risco de danificar o ambiente
- Pode dizer “vou executar X” e depois gerar uma chamada para um Y completamente diferente
- O Sonnet 3.7 acredita incorretamente que consegue executar comandos de shell
- Formas de melhorar
- Ajustar o prompt ou fornecer ferramentas dedicadas que façam apenas a tarefa desejada
- Ao fornecer uma ferramenta específica, é possível evitar chamadas de shell sem sentido
- Ajustar o prompt ou fornecer ferramentas dedicadas que façam apenas a tarefa desejada
-
Exemplo
- O Sonnet 3.7 tentou gerar um script de shell sem sentido ao conceder permissão de execução a um arquivo
- Após um erro no comando, repetiu várias tentativas de correção equivocadas
- O Sonnet 3.7 tentou gerar um script de shell sem sentido ao conceder permissão de execução a um arquivo
Ler a documentação (Read the Docs)
- Ao aprender um novo framework ou biblioteca, é possível fazer tarefas simples modificando código de tutorial
- Mas, no fim, é necessário ler a documentação do começo ao fim para entender como tudo funciona
- Vantagens dos LLMs
- Frameworks populares geralmente já fizeram parte do treinamento, então eles se lembram da maior parte do uso
- Porém, em ferramentas de nicho ou lançadas depois do corte de conhecimento, podem ocorrer alucinações
- O Sonnet não oferece suporte a busca na web → é preciso fornecer a documentação manualmente
- No Cursor, ao fornecer uma URL, ela pode ser incluída automaticamente no contexto
-
Exemplo
- Ao pedir ao LLM para escrever YAML para chamadas de função em Python, ele gerou uma configuração incorreta
- Depois de receber a documentação, conseguiu corrigir e melhorar o formato de saída
- Ao pedir ao LLM para escrever YAML para chamadas de função em Python, ele gerou uma configuração incorreta
A cultura vence a estratégia (Culture Eats Strategy)
- A cultura da equipe afeta de forma decisiva a capacidade de executar a estratégia
- O LLM gera código com base no estilo previamente aprendido e na janela de contexto
- Tende a preferir bibliotecas ou estilos que aparecem com frequência no contexto
- Se nada for especificado, aplica o estilo padrão
- Estratégias para ajustar o estilo do LLM
- Alterar as regras do Cursor (mudar o prompt)
- Refatorar o estilo do código existente para o formato desejado → isso influencia a previsão do próximo token
- O tamanho do codebase tem mais influência que o prompt → modificar o codebase é a solução mais fundamental
-
Exemplo
- O Sonnet 3.7 prefere código síncrono em Python
- Para gerar código assíncrono, foi preciso portar a maior parte do código existente para
async, e então deu certo
- Para gerar código assíncrono, foi preciso portar a maior parte do código existente para
- O Sonnet 3.7 prefere código síncrono em Python
Esqueleto funcional (Walking Skeleton)
- Walking Skeleton é uma estratégia de implementar um sistema mínimo de ponta a ponta
- Mesmo sem estar perfeito, faz-se o sistema inteiro funcionar e depois os detalhes são aprimorados
- Na era da programação com LLM, ficou mais fácil construir rapidamente o sistema completo
- Quando o sistema funciona, o próximo passo fica claro → é importante chegar rapidamente a um estado funcional
- Como o LLM não consegue usar diretamente o código que escreveu, garantir um estado funcional é importante
Regra dos três (Rule of Three)
- Duplicar o mesmo código é aceitável até duas vezes; na terceira, é preciso refatorar
- Uma versão aprimorada do princípio DRY (Don't Repeat Yourself)
- Isso deixa claro o momento de eliminar duplicações → refatorar na terceira cópia
- Problemas dos LLMs
- LLMs tendem a gerar código duplicado
- Ao pedir alterações sem um prompt específico, eles copiam o código inteiro de novo e fazem a modificação
- A eliminação de duplicação só acontece se o modelo decidir isso por conta própria → é necessária uma instrução clara
- Formas de melhorar
- É preciso instruir explicitamente a remoção de duplicação
- Se já houver muita duplicação no código existente, o modelo pode continuar gerando mais duplicação
-
Exemplo
- Ao pedir ao LLM para escrever código de teste, a mesma lógica foi duplicada em vários testes
- Isso foi resolvido após instruir explicitamente a criação de métodos auxiliares
- modo agente
- Ao pedir ao LLM para escrever código de teste, a mesma lógica foi duplicada em vários testes
1 comentários
Comentários do Hacker News
LLMs cometem erros de forma diferente dos humanos, e isso é difícil de detectar
Quando não conhecem os requisitos, os LLMs preenchem com a resposta mais provável a partir dos dados de treinamento
Em engenharia de software, é importante deixar os requisitos claros
LLMs têm capacidade de programação no nível de um "programador júnior muito inteligente"
LLMs tentam responder demais
Com o aumento do número de posts no blog, passou a ser necessário organizá-los
Conselhos úteis para programar com LLMs
LLMs são fracos em cálculo e aritmética
Pontos a considerar junto com programadores humanos
Caso em que três LLMs encontraram um "bug" que não existia