Triagem de Chamados com IA
resiliênciaNo arO que acontece com a triagem quando o provedor de IA demora 30 segundos, cai, estoura a cota ou responde algo fora do formato pedido?
O projeto não é sobre prompt. É sobre o backend em volta de uma dependência que demora, falha, cobra por uso e às vezes responde algo fora do combinado. A versão ingênua chama o modelo dentro da requisição e devolve o que vier: funciona com o provedor no ar, e só. A latência do modelo vira a latência da API, uma queda vira 500 e o chamado se perde, e um JSON fora do formato chega ao banco.
Aqui o POST grava o chamado e responde 202 sem tocar em IA. A triagem acontece depois, num worker que pega os chamados com SKIP LOCKED e não chama o modelo dentro da transação: isso seguraria uma conexão do pool durante os segundos de resposta, e com o provedor lento até o POST pararia.
POST /api/chamados -- grava RECEBIDO e responde 202 worker: SELECT ... FOR UPDATE SKIP LOCKED -- reserva com prazo Gemini → nova tentativa só no transitório (5xx, 429, tempo esgotado) → circuito aberto? pula sem requisição nenhuma → resposta validada; a inválida ganha uma correção Groq → a mesma pilha, se o Gemini falhar todos fora → PENDENTE_TRIAGEM_MANUAL, revisto em 5min, 30min e 2h
Repetir sem critério piora a situação. Um 400 nunca se repete, porque a resposta seria a mesma recusa, e um 429 espera o tempo que o provedor pede. O Gemini não manda Retry-After: a espera vem no corpo do erro, e sem lê-la a aplicação tentaria de novo em 1 segundo quando ele pediu 26.
O custo também é controlado: o mesmo texto reaproveita a classificação do cache, cada provedor tem um orçamento diário contado no fuso da cota dele, e cada cliente tem um limite por hora. O IP desse limite foi medido em produção, porque o primeiro endereço do X-Forwarded-For, o que quase todo exemplo usa, é justamente o que o cliente controla.
Cada uma dessas falhas tem um teste, com o WireMock no papel dos provedores. O do circuit breaker confere zero requisições ao provedor principal enquanto o circuito está aberto. Nenhum teste chama um provedor real, e o CI roda sem chave nenhuma.
- Java 21
- Spring Boot 4
- Spring AI
- Resilience4j
- PostgreSQL
- Testcontainers
- WireMock