Construindo um Chatbot
Custos e Rate Limiting
Evitar abuso, limitar chamadas por usuário/IP, cache de FAQ e métricas de custo.
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.