Blog

Detectar deriva de qualidade em bot de atendimento antes do cliente reclamar

O bot não cai. Ele continua respondendo, o tempo de resposta segue normal, o painel está todo verde, e a taxa de erro é zero. Só que a resposta que ele dá hoje é pior do que a que dava há três semanas, e ninguém percebeu porque nada quebrou. Deriva de qualidade é o modo de falha mais caro de um sistema de atendimento com IA justamente porque é silencioso: quando o sinal finalmente chega, ele chega pela reclamação do cliente, pelo aumento de escalonamento humano ou pela queda de conversão, três indicadores que só se movem depois que o estrago já foi feito e que nunca apontam a causa. Este artigo trata de como detectar essa degradação antes disso: quais sinais se movem cedo, como construir um conjunto de referência que não envelhece junto com o problema, como separar deriva real de flutuação normal sem disparar alarme toda semana, e como transformar o alerta em diagnóstico que diz o que mudou em vez de apenas que algo mudou.

2026-07-29 / IA Aplicada / 14 min

01

Deriva não é queda: por que o painel verde engana

A observabilidade tradicional foi construída para detectar indisponibilidade e erro, e faz isso bem. Latência, taxa de erro HTTP, throughput e saturação capturam qualquer coisa que quebre de forma binária. O problema é que qualidade de resposta não é binária e não gera exceção. Um bot que passou a responder de forma vaga, que parou de citar a fonte, que ficou mais propenso a inventar um prazo de entrega ou que começou a escalar para humano casos que resolvia sozinho não produz um único erro. Ele produz respostas duzentas, bem formadas, dentro do tempo, e piores. Nenhum dos quatro sinais clássicos se move, e é por isso que a operação inteira pode degradar por semanas com o painel intacto.

Vale separar três causas que produzem o mesmo sintoma, porque a resposta a cada uma é diferente. A primeira é deriva do modelo: o provedor atualizou o ponto de acesso, ou você trocou de versão, e o comportamento mudou nas bordas que o seu prompt nunca especificou. A segunda é deriva do conhecimento: a base de RAG envelheceu, a política mudou e o documento não, e o bot passou a responder com confiança algo que deixou de ser verdade. A terceira é deriva de entrada: o perfil das perguntas mudou porque entrou uma campanha nova, um canal novo ou uma safra de clientes com outro vocabulário, e o sistema continua igual enquanto o mundo ao redor mudou. Confundir as três leva a ajustar o prompt quando o problema estava na base, que é o retrabalho mais comum nesse tipo de investigação.

Causa da derivaSinal que se move primeiroO que corrige
Modelo mudou de versão ou comportamentoDistribuição de formato e tamanho da respostaGate de paridade e ajuste cirúrgico do prompt
Base de conhecimento envelheceuQueda na taxa de resposta com citação válidaReindexação e curadoria do corpus, não o prompt
Perfil das perguntas mudouAumento de intenções fora do catálogo conhecidoAmpliar cobertura, criar intenção nova ou rota de escalonamento
Prompt alterado sem avaliaçãoRuptura brusca no dia exato da alteraçãoRollback da versão e gate de eval antes do rollout
Contexto recuperado piorouQueda de relevância no top-k antes da queda de respostaAjuste de retrieval e reranking, sem tocar no modelo

02

Os sinais que se movem antes do cliente reclamar

Existem dois tipos de sinal e eles se complementam. O sinal direto mede a qualidade da resposta e exige um julgamento, humano ou automatizado, sendo mais fiel e mais caro. O sinal indireto mede propriedades observáveis da resposta e do comportamento do usuário sem julgar conteúdo, sendo mais barato, contínuo e disponível para cem por cento do tráfego. A estratégia que funciona é usar o indireto como sentinela de alta cobertura e o direto como confirmação sob demanda: o indireto grita, o direto explica. Quem monta só o direto acaba avaliando uma amostra pequena demais para detectar deriva cedo; quem monta só o indireto detecta a mudança mas nunca sabe se ela foi para melhor ou pior.

Entre os sinais indiretos, os mais informativos costumam ser os comportamentais, porque carregam julgamento implícito do usuário sem custar nada para coletar. A taxa de reformulação, que mede quantas vezes o cliente reescreve a mesma pergunta, sobe assim que a resposta deixa de resolver. A taxa de escalonamento por intenção separa o caso complexo, que sempre escalou, da regressão nova. O número de turnos até a resolução é sensível e cedo. E vale registrar a taxa de abandono no meio da conversa, que é o cliente desistindo em silêncio, o único grupo que nunca vai reclamar e por isso nunca aparece no canal de suporte.

SinalTipoO que denuncia
Taxa de reformulação da mesma perguntaIndireto, comportamentalResposta deixou de resolver, ainda que continue bem formada
Turnos até resoluçãoIndireto, comportamentalPerda de eficiência antes de qualquer queda visível de qualidade
Escalonamento por intençãoIndireto, operacionalRegressão localizada em um tipo de caso, não no bot inteiro
Distribuição do tamanho da respostaIndireto, estruturalMudança de comportamento do modelo, tipicamente por troca de versão
Taxa de resposta com citação válidaIndireto, estruturalRetrieval degradado ou base envelhecida
Nota de juiz automático no conjunto fixoDireto, avaliadoQueda real de qualidade, com o caso concreto que regrediu
// drift-signals.js
// Sinais indiretos calculados sobre 100% do trafego, sem julgar conteudo.
// Baratos o suficiente para rodar sempre, sensiveis o suficiente para
// se mover antes de a reclamacao chegar ao suporte.

export function conversationSignals(turns) {
  const userTurns = turns.filter((t) => t.role === 'user');
  const botTurns = turns.filter((t) => t.role === 'assistant');

  return {
    // Reformulacao: pergunta muito parecida com a anterior do MESMO usuario.
    // Sinal mais forte de "a resposta nao resolveu" que existe de graca.
    reformulations: userTurns.reduce((count, turn, i) => {
      if (i === 0) return count;
      return count + (similarity(turn.text, userTurns[i - 1].text) > 0.8 ? 1 : 0);
    }, 0),

    turnsToResolve: turns.length,
    escalated: turns.some((t) => t.event === 'handoff'),

    // Abandono: usuario parou de responder apos o bot. O cliente que
    // desiste em silencio nunca abre chamado e por isso some das metricas.
    abandoned: turns.at(-1)?.role === 'assistant' && !turns.some((t) => t.event === 'resolved'),

    avgAnswerLength: botTurns.reduce((s, t) => s + t.text.length, 0) / (botTurns.length || 1),
    citedRate: botTurns.filter((t) => t.citations?.length > 0).length / (botTurns.length || 1),
  };
}

// Jaccard sobre bigramas de caractere: barato, sem dependencia externa
// e suficiente para "e a mesma pergunta escrita de outro jeito".
function similarity(a, b) {
  const grams = (s) => {
    const norm = s.toLowerCase().replace(/\s+/g, ' ').trim();
    return new Set(Array.from({ length: Math.max(norm.length - 1, 0) }, (_, i) => norm.slice(i, i + 2)));
  };
  const [ga, gb] = [grams(a), grams(b)];
  if (!ga.size || !gb.size) return 0;
  const inter = [...ga].filter((g) => gb.has(g)).length;
  return inter / (ga.size + gb.size - inter);
}

03

O conjunto de referência que não envelhece junto com o problema

Detectar deriva exige um ponto fixo de comparação, e é aí que a maioria das implementações se sabota. O erro é usar a média móvel do próprio tráfego recente como referência: se a qualidade cai dois por cento por semana, a média móvel cai junto, o desvio nunca aparece e depois de seis meses o sistema degradou trinta por cento sem disparar um único alerta. A referência precisa ser um conjunto congelado de casos com resposta esperada revisada por gente, executado contra a versão atual em cadência fixa. Ele não é o tráfego, ele é a régua, e uma régua que se ajusta ao que mede não mede nada.

Um bom conjunto de referência tem três camadas. A primeira é o núcleo estável: os casos mais frequentes, que representam o volume e cuja regressão é inaceitável. A segunda é a borda conhecida: os casos que já quebraram no passado, cada incidente virando um item permanente, o que impede a mesma regressão de voltar duas vezes. A terceira é a amostra rotativa: casos reais recentes, revisados e adicionados periodicamente, que impedem o conjunto de virar um museu descolado do que os clientes perguntam hoje. As duas primeiras camadas são congeladas e só crescem; a terceira gira, mas a rotação precisa ser deliberada e registrada, nunca automática, senão a régua volta a derreter.

  CONJUNTO DE REFERENCIA (a regua, nao o trafego)
  +----------------------------------------------+
  | nucleo estavel   ~60%  congelado, so cresce  |
  | borda conhecida  ~30%  1 item por incidente  |
  | amostra rotativa ~10%  revisada e datada     |
  +----------------------------------------------+
                    |
                    v  execucao em cadencia fixa
        +-------------------------+
        | versao atual em producao|
        +-------------------------+
                    |
                    v
        nota por caso + agregada por intencao
                    |
        +-----------+-----------+
        |                       |
        v                       v
  compara com LINHA BASE   compara com JANELA ANTERIOR
  (congelada na v1)        (detecta queda gradual)
        |                       |
        +-----------+-----------+
                    v
        alerta com o CASO que regrediu
        (nao apenas "a nota caiu")

Um cuidado que separa um conjunto útil de um enganoso: a resposta esperada não deve ser uma string exata na maioria dos casos, porque comparação literal transforma qualquer variação de redação em falso alarme e o time desliga o alerta em duas semanas. O que se compara é a propriedade que importa em cada caso: o fato correto aparece, o valor numérico bate, a recusa acontece quando deve, a citação aponta para o documento certo, o formato de saída é válido. Escrever a expectativa como uma asserção verificável em vez de um texto ideal é o que torna o conjunto sustentável ao longo de meses.

04

Separar deriva real de flutuação normal

Todo sinal de qualidade oscila, e um detector ingênuo que alerta a cada queda vira ruído que ninguém lê. Três disciplinas resolvem isso. A primeira é o tamanho de amostra: uma queda de cinco pontos sobre trinta conversas não é sinal nenhum, e o alerta precisa exigir volume mínimo por segmento antes de considerar qualquer desvio. A segunda é a comparação dupla: contra a linha de base congelada, que captura degradação acumulada, e contra a janela imediatamente anterior, que captura ruptura brusca. Cada uma pega um tipo de deriva que a outra deixa passar, e alertar apenas em uma delas garante um ponto cego permanente.

A terceira disciplina é segmentar antes de agregar, e é a que mais evita o falso negativo. Uma regressão que atinge apenas uma intenção específica, um único idioma ou um canal desaparece na média global: o número agregado se move um ponto e ninguém liga, enquanto aquele segmento caiu quinze. Como segmentar multiplica o número de comparações e portanto a chance de falso positivo por acaso, o par correto é sempre segmentação com correção para múltiplas comparações e volume mínimo por célula. Alertar por segmento sem esses dois freios produz alarme diário e treina o time a ignorar o canal, que é o pior resultado possível.

// drift-detector.js
// Alerta so quando ha VOLUME suficiente e o desvio persiste.
// Comparacao dupla: linha de base congelada (deriva lenta)
// e janela anterior (ruptura brusca). Uma pega o que a outra perde.

const MIN_SAMPLE = 50;        // abaixo disso, qualquer desvio e ruido
const SLOW_DRIFT_PP = 5;      // pontos percentuais contra a linha de base
const SUDDEN_DROP_PP = 8;     // ruptura contra a janela anterior
const PERSIST_WINDOWS = 2;    // precisa se manter para virar alerta

export function detectDrift({ segment, current, previous, baseline, history }) {
  if (current.n < MIN_SAMPLE) {
    return { status: 'insufficient_sample', segment, n: current.n };
  }

  const vsBaseline = (baseline.score - current.score) * 100;
  const vsPrevious = (previous.score - current.score) * 100;

  // Bonferroni simples: segmentar multiplica as comparacoes, entao o
  // limiar sobe com o numero de segmentos para nao gerar alarme diario.
  const adjust = Math.log2(Math.max(history.segmentCount, 2));
  const slowLimit = SLOW_DRIFT_PP * adjust;
  const suddenLimit = SUDDEN_DROP_PP * adjust;

  const slow = vsBaseline > slowLimit;
  const sudden = vsPrevious > suddenLimit;
  if (!slow && !sudden) return { status: 'stable', segment };

  // Persistencia: uma janela ruim e variacao, duas seguidas e deriva.
  const persisted = history.recent
    .slice(-PERSIST_WINDOWS)
    .every((w) => (baseline.score - w.score) * 100 > slowLimit / 2);

  if (sudden) {
    return { status: 'alert', kind: 'sudden', segment, deltaPp: vsPrevious };
  }
  return persisted
    ? { status: 'alert', kind: 'gradual', segment, deltaPp: vsBaseline }
    : { status: 'watching', segment, deltaPp: vsBaseline };
}

05

Do alerta ao diagnóstico: o que mudou, não só que mudou

Um alerta que diz "a nota caiu seis pontos" gera uma investigação do zero e costuma consumir um dia de trabalho. O alerta útil chega com o recorte já feito, e isso exige gravar contexto suficiente no momento da execução, não depois. Três coisas precisam vir junto: os casos concretos que regrediram, com a resposta anterior e a atual lado a lado, porque ler duas respostas resolve metade das investigações em cinco minutos; a lista de mudanças que entraram na janela, incluindo versão de prompt, versão de modelo, reindexação de base e alteração de configuração de retrieval; e a distribuição do desvio pelos segmentos, que diz se a regressão é geral ou localizada.

Essa correlação com mudanças é o que transforma detecção em causa raiz, e ela depende de uma prática simples que quase ninguém tem: registrar toda alteração relevante num mesmo fluxo de eventos, com data e identificador de versão, incluindo as que não são deploy de código. Reindexar a base, editar um documento do corpus, mudar o parâmetro de temperatura ou o top-k do retrieval são alterações invisíveis para o histórico do repositório e capazes de mover a qualidade tanto quanto um deploy. Sem esse registro, a investigação vira arqueologia. Com ele, o alerta já chega dizendo que a queda começou na mesma hora em que a base foi reindexada.

  1. Confirmar volume e persistência antes de mobilizar gente, porque uma janela ruim isolada quase sempre é variação.
  2. Verificar se a queda é geral ou concentrada em um segmento, o que já elimina metade das hipóteses.
  3. Ler lado a lado três casos que regrediram, comparando a resposta anterior com a atual antes de qualquer teoria.
  4. Cruzar o início da queda com o fluxo de mudanças, incluindo reindexação e ajuste de parâmetro, não apenas deploy.
  5. Isolar a camada reexecutando os mesmos casos com o retrieval anterior, o que separa deriva de contexto de deriva de modelo.
  6. Corrigir, adicionar o caso que regrediu à borda conhecida do conjunto de referência e só então encerrar.

O último passo é o que mais se pula e o que mais rende com o tempo. Todo incidente de deriva que não vira caso permanente no conjunto de referência é um incidente que pode acontecer de novo, e sistemas de IA regridem em direções repetidas com frequência incômoda. Transformar cada investigação num item congelado da régua faz o custo da próxima ocorrência cair para zero, porque a regressão passa a ser pega no gate antes de chegar à produção, em vez de ser redescoberta pelo mesmo caminho caro.

06

Colocar em pé sem virar projeto de seis meses

A tentação é começar pelo juiz automático e por um conjunto grande de referência, e isso costuma atrasar o primeiro sinal em semanas. A ordem que entrega valor cedo começa pelos sinais indiretos, porque eles não exigem julgamento, rodam sobre todo o tráfego e já capturam a maior parte das derivas visíveis. Taxa de reformulação, turnos até resolução e escalonamento por intenção podem estar registrados em poucos dias e já dizem quando algo mudou, mesmo sem dizer o quê. O conjunto de referência entra logo depois e começa pequeno: trinta casos bem escolhidos por intenção principal detectam mais regressão do que trezentos casos genéricos.

  1. Registrar os sinais indiretos por conversa e por intenção, começando por reformulação, turnos até resolução e escalonamento.
  2. Congelar uma linha de base do comportamento atual antes de qualquer mudança, porque sem ela não existe comparação possível.
  3. Montar um conjunto de referência pequeno, com o núcleo estável das intenções de maior volume e expectativa escrita como asserção verificável.
  4. Rodar o conjunto em cadência fixa e a cada rollout de prompt ou troca de modelo, guardando a resposta completa e não só a nota.
  5. Ligar o detector com volume mínimo, comparação dupla e correção para múltiplas comparações, começando com limiar conservador.
  6. Registrar todas as mudanças relevantes num fluxo único de eventos, incluindo reindexação e ajuste de parâmetro de retrieval.
  7. Fechar o ciclo transformando cada incidente investigado em caso permanente da borda conhecida.

Uma calibração final que evita frustração: no primeiro mês o detector vai gerar mais alarme do que deveria, e a reação correta não é desligá-lo, é ajustar limiar e volume mínimo com os dados reais que ele produziu. Anotar cada disparo como verdadeiro ou falso positivo durante quatro semanas dá exatamente a informação necessária para calibrar, e converte o detector de uma suposição sobre o sistema numa ferramenta que o time confia. Sem essa fase, o resultado previsível é o canal de alerta silenciado, e um detector silenciado é indistinguível de não ter detector nenhum.

FAQ

Perguntas frequentes

Por que não usar a média do tráfego recente como referência de qualidade?

Porque a média móvel do próprio tráfego envelhece junto com o problema e por isso não detecta exatamente o caso mais perigoso. Se a qualidade cai dois por cento por semana, a referência cai na mesma velocidade, o desvio relativo permanece próximo de zero e o alerta nunca dispara, mesmo depois de seis meses e trinta por cento de degradação acumulada. Uma régua que se ajusta ao que mede não mede nada. A referência precisa ser um conjunto congelado de casos com expectativa revisada por gente, executado contra a versão atual em cadência fixa, e a comparação precisa ser dupla: contra essa linha de base, que captura a deriva lenta, e contra a janela imediatamente anterior, que captura a ruptura brusca de um deploy ou troca de modelo. Cada comparação pega um tipo de deriva que a outra deixa passar.

Quais sinais detectam deriva sem precisar avaliar o conteúdo da resposta?

Os comportamentais são os mais informativos porque carregam julgamento implícito do usuário e não custam nada para coletar sobre cem por cento do tráfego. A taxa de reformulação, medindo quantas vezes o cliente reescreve a mesma pergunta, sobe assim que a resposta deixa de resolver, mesmo que continue bem escrita. O número de turnos até a resolução é sensível e se move cedo. A taxa de escalonamento segmentada por intenção separa o caso complexo que sempre escalou da regressão nova. A taxa de abandono no meio da conversa captura o cliente que desiste em silêncio, o grupo que nunca reclama e por isso nunca aparece no suporte. Aos comportamentais somam-se dois estruturais úteis: a distribuição do tamanho da resposta, que denuncia troca de versão do modelo, e a taxa de resposta com citação válida, que denuncia retrieval degradado antes de a qualidade cair.

Como evitar que o detector de deriva vire ruído que o time ignora?

Com três freios aplicados juntos e uma fase de calibração. O primeiro freio é volume mínimo por segmento, porque uma queda de cinco pontos sobre trinta conversas não é sinal e alertar sobre isso treina o time a desconfiar do canal. O segundo é persistência: uma janela ruim é variação, duas janelas seguidas na mesma direção são deriva, e exigir persistência elimina a maior parte dos falsos positivos sem atrasar a detecção de forma relevante. O terceiro é correção para múltiplas comparações, obrigatória porque segmentar por intenção, canal e idioma multiplica o número de testes e portanto a chance de um desvio por acaso. Além disso, no primeiro mês o correto não é desligar o alerta ruidoso, é anotar cada disparo como verdadeiro ou falso positivo e usar esses dados para ajustar limiar e volume mínimo, porque um detector silenciado é indistinguível de não ter detector.

A pior falha é a que não gera erro

Um bot que degrada não cai, não estoura latência e não aparece em nenhum painel clássico: ele continua respondendo, cada vez pior, até que o sinal chegue pela reclamação do cliente, quando o estrago já está feito e a causa já se perdeu. Sinais indiretos sobre todo o tráfego para detectar cedo, um conjunto de referência congelado que serve de régua em vez de média móvel que derrete, comparação dupla com volume mínimo e correção para múltiplas comparações, e alerta que já chega com o caso concreto e as mudanças da janela transformam deriva de surpresa em evento gerenciado. Posso montar essa detecção no seu bot de atendimento, dos sinais comportamentais ao gate de eval no rollout, para que a regressão apareça no seu canal antes de aparecer no do cliente.