Postmortem: o eval que derrubou o chat

Em 12/10/2026, o chat deste site parou de responder perguntas sobre mim a partir das 17:45. Nenhuma métrica do Worker acusou erro. A causa foi o meu próprio eval, que gastou a cota grátis do Workers AI que a busca do chat também usa.

Os gráficos com os dados do dia estão no dashboard do incidente. Os horários aqui são de Brasília.

Resumo

  • Impacto: perguntas sobre mim, o site ou o chat voltavam sem texto, com qualquer modelo do seletor. Perguntas gerais continuavam funcionando.
  • Causa: a ferramenta buscar_site calcula o embedding da pergunta no Workers AI. Com a cota diária esgotada, essa chamada voltava com o erro 4006 e a ferramenta falhava inteira.
  • Por que ninguém viu: o /chat responde 200 antes de a busca rodar, e a falha da ferramenta não escrevia nada no log.
  • Correção: a #287, mergeada às 18:24. Sem embedding, a busca cai para o ranking BM25 e registra o fallback no log.

Linha do tempo

  • 11:45 e 12:45. Duas rodadas do eval do chat com o Llama 3.3 70B no Workers AI. Cada rodada manda as mesmas perguntas para todos os modelos do seletor.
  • 17:30 a 17:40. Terceira rodada. O consumo do dia passa de 10.000 neurons, que é a cota grátis diária.
  • 17:45. Primeiras chamadas recusadas com 4006, no embedding e no modelo. Daqui em diante a busca do chat falha.
  • 18:24. A #287 é mergeada.
  • 21:00. A cota é renovada (meia-noite UTC).

No dia, foram 11.877 neurons e 182 chamadas recusadas pelo Workers AI.

O que as métricas mostravam

Nas métricas do Worker, o dia inteiro aparece limpo: 0 erros em 632 requisições, todas com status success. Isso acontece porque o /chat abre o stream e responde 200 logo no começo. A busca roda depois, dentro do stream, e o erro dela vira um evento do protocolo do chat. Para o runtime, a requisição deu certo.

O único sinal no Worker era a latência. A mediana caiu de 2,9 s para 0,5 s quando os erros começaram. A resposta ficou mais rápida porque o modelo não recebia o resultado da busca e não tinha o que escrever. Num chat, uma queda brusca de latência sem mudança de código merece tanta atenção quanto um pico.

Como chegar na causa

Com os dados que existiam, o caminho é este:

  1. Reproduzir o sintoma. Uma pergunta sobre mim volta sem texto. Uma conta simples funciona. Então o problema está na busca, não no modelo nem no Worker inteiro.
  2. Olhar o stream, não o status. O stream da resposta tem um evento de erro da ferramenta com a mensagem do Workers AI: 4006: you have used up your daily free allocation of 10,000 neurons.
  3. Confirmar na Analytics do Workers AI. As chamadas com errorCode 4006 começam às 17:45, no embedding e no modelo.
  4. Achar quem gastou a cota. Somando os neurons do dia por modelo, quase tudo é do Llama 3.3 70B, em três blocos que batem com as rodadas do eval. O embedding da busca gastou poucos neurons.

O passo 2 é o que mais demora quando não há log. Sem uma linha no Worker dizendo que a ferramenta falhou, a única forma de ver o erro era abrir o stream de uma resposta.

Causa raiz

Três coisas juntas:

  • O eval e o chat de produção usam a mesma conta e a mesma cota grátis do Workers AI.
  • A busca dependia do embedding da pergunta e não tinha alternativa quando ele falhava. O ranking BM25 já existia em memória e ficava sem uso.
  • A falha da ferramenta não deixava nenhuma linha no log.

A correção e a reprodução

A #287 faz o retrieval seguir só com o BM25 quando o embedding falha e escreve embed_error_bm25_fallback no log. Para conferir, rodei o bundle do Worker de antes e de depois do fix com o embedding devolvendo o erro de cota. Foram 8 perguntas do eval do chat que dependem da busca, corrigidas pelas mesmas verificações do benchmark:

FaseAprovadasErro de ferramentaCom linha de logMediana
Normal7/8005,5 s
Sem cota, antes do fix0/8801,6 s
Sem cota, com o fix8/8085,4 s

Antes do fix, todas as perguntas falham e nenhuma deixa log. Com o fix, 8 de 8 passam e cada fallback aparece no log. Na rodada normal, uma pergunta falhou por variação do modelo, sem relação com o incidente. A reprodução também mostra a queda de latência: 1,6 s de mediana sem a busca, contra 5,4 s com ela.

O que ainda falta

A correção resolve o sintoma. Ainda estão em aberto:

  • Separar o eval da produção. Rodar o eval com outro limite ou outra conta, para que ele não consuma a cota de que o chat depende.
  • Alerta de consumo. Avisar quando os neurons do dia passarem de 80% da cota. Às 13:00 o consumo já passava de 9,5 mil.
  • Logs persistidos. Os Workers Logs não estão ligados neste Worker. No dia, só havia métricas agregadas.
  • Medir respostas vazias. Contar respostas sem texto como métrica. Essa contagem teria mostrado o incidente na hora.
  • O modelo do Workers AI no seletor continua indisponível quando a cota acaba. Só o plano pago resolve isso.

Ver os dados

Voltar ao início