Construindo um Chatbot

Custos e Rate Limiting

Evitar abuso, limitar chamadas por usuário/IP, cache de FAQ e métricas de custo.

Intermediário 25 min 25 pontos Leitura 0%

Nesta aula você vai

  • Implementar rate limit por IP ou usuário
  • Cachear respostas de perguntas frequentes
  • Monitorar tokens e custo estimado

Custos e Rate Limiting

Objetivos

  • Preparar chatbot para tráfego real sem susto na fatura
  • Bloquear abuso antes que vire incidente financeiro

O bot que quase faliu a startup

História fictícia, mas comum: time lança chatbot público sem limite. Um usuário cola o mesmo prompt de 4.000 tokens em loop. Outro script automatiza 500 requisições por hora. Na segunda-feira, a fatura da API assusta mais que qualquer bug de código.

LLM cobra por uso. Bot público sem proteção é torneira aberta. Esta aula ensina a fechar a torneira sem matar a experiência de quem usa de verdade.

Rate limiting

Regra simples: N mensagens por janela por identificador.

Identificador Limite sugerido (início)
IP anônimo 10 req / hora
Usuário logado 50 req / hora
API key interna 1000 req / hora

Mensagem amigável quando bloqueia:

"Você atingiu o limite de mensagens por hora. Tente novamente mais tarde ou fale com nosso suporte."

Não exponha "429 rate_limit_exceeded" cru — isso é detalhe interno.

Redis (contador)

async function checkRateLimit(key, limit, windowSec) {
  const count = await redis.incr(key);
  if (count === 1) await redis.expire(key, windowSec);
  return count <= limit;
}

// uso
const ok = await checkRateLimit(`chat:ip:${ip}`, 10, 3600);
if (!ok) return res.status(429).json({ error: 'Limite atingido. Aguarde.' });

Alternativa sem Redis: memória local (dev) ou Cloudflare Rate Limiting na borda — útil para MVP antes de infraestrutura completa.

Cache de respostas

Perguntas FAQ são determinísticas — não precisam de LLM toda vez.

"Qual o horário de atendimento?"
"Vocês entregam para todo o Brasil?"
"Como rastrear pedido?"

Se a pergunta é igual (ou quase), devolva resposta cacheada.

import { createHash } from 'crypto';

function cacheKey(message) {
  return 'faq:' + createHash('sha256').update(message.trim().toLowerCase()).digest('hex');
}

const cached = await redis.get(cacheKey(message));
if (cached) return res.json({ reply: cached, cached: true });

const reply = await chat(...);
await redis.setex(cacheKey(message), 86400, reply); // 24h

Invalidação: TTL ou bump de versão no prefixo faq:v2: quando política mudar.

Impacto real: em bots de suporte, 30–50% das perguntas repetem FAQ — cache pode cortar custo pela metade.

Quotas por usuário autenticado

Plano free: 20 mensagens/dia. Persista contador em DB:

UPDATE user_quota SET messages_used = messages_used + 1
WHERE user_id = ? AND date = CURRENT_DATE AND messages_used < daily_limit;

Diálogo produto ↔ engenharia:

Produto: "Queremos plano free com limite."
Dev: "20 msg/dia no banco, ilimitado no plano pago."
Produto: "Mostra contador na UI: '15 de 20 mensagens hoje'."

Transparência reduz reclamação quando o limite chega.

Métricas essenciais

Log por requisição:

{
  "event": "llm_completion",
  "userId": "u_123",
  "model": "gpt-4o-mini",
  "promptTokens": 450,
  "completionTokens": 89,
  "latencyMs": 1200,
  "estimatedUsd": 0.00012
}

Dashboard: custo/dia, tokens/dia, p95 latência, taxa 429.

Sem métrica, você descobre o problema pela fatura — tarde demais.

Kill switch

Variável LLM_ENABLED=false no env — desliga LLM, retorna mensagem estática ou fila. Use em incidente de billing ou outage do provedor.

Cenário de plantão:

Provedor fora do ar às 2h. Kill switch ativo. Bot responde: "Estamos em manutenção, tente pela manhã." Sem retry infinito, sem custo, sem cascata de timeout.

Resumo

  • Rate limit = proteção financeira e operacional
  • Cache FAQ reduz tokens drasticamente
  • Logue tokens em toda chamada
  • Kill switch para emergências

Com chatbot protegido, falta o ingrediente que transforma conversa solta em experiência contínua: memória. Na próxima matéria entendemos por que o modelo "esquece" — e como resolver do lado do seu código.