8.0 KiB
ADR-002 — Utilizar Qwen3.5 2B local com fallback operacional Groq e OmniRoute
Status
Accepted
Contexto
A classificação entre:
text
e:
media
exige uma pequena decisão semântica multilíngue.
O problema não exige um modelo grande.
A tarefa é extremamente restrita:
- receber texto e informações estruturais;
- decidir se o texto é conteúdo jornalístico substancial ou apenas descrição/contextualização da mídia;
- quando for mídia, identificar uma entre cinco categorias.
O sistema precisa ser production-ready sem introduzir infraestrutura ou complexidade desnecessária.
Decisão
Utilizar a seguinte cadeia sequencial:
Qwen3.5 2B / Ollama
↓ falha operacional
GPT-OSS 20B / Groq
↓ falha operacional
cgpt-web/gpt-5.5 / OmniRoute
Provider primário
Qwen3.5 2B
Runtime:
Ollama
Motivos da decisão:
- execução local;
- modelo pequeno;
- suficiente para classificação restrita;
- adequado ao cenário multilíngue;
- elimina custo por chamada no caminho normal;
- não depende de serviço externo durante operação normal.
Primeiro fallback
GPT-OSS 20B
Provider:
Groq
O Groq somente é chamado quando o provider primário não consegue fornecer uma classificação tecnicamente utilizável.
Segundo fallback
Modelo:
cgpt-web/gpt-5.5
Provider:
OmniRoute
O OmniRoute somente é utilizado quando:
Ollama falhou
E
Groq falhou
Tipo de fallback
O fallback é exclusivamente operacional.
Exemplos de falha que justificam fallback:
conexão recusada
timeout
erro HTTP
provider indisponível
erro de execução
resposta que não pode ser consumida
resposta incompatível com o schema obrigatório
O que NÃO provoca fallback
Não executar fallback quando:
modelo retorna text
modelo retorna media
resultado parece improvável
resultado parece ambíguo
modelo parece estar inseguro
outro modelo talvez respondesse diferente
Não existe:
- score de confiança;
- votação;
- consenso;
- LLM-as-a-judge;
- segunda opinião;
- comparação de respostas.
Uma resposta válida do provider atual encerra a cadeia.
Execução sequencial
A cadeia deve ser executada sequencialmente.
try Ollama
se resposta válida:
usar resposta
finalizar
se falha operacional:
try Groq
se resposta válida:
usar resposta
finalizar
se falha operacional:
try OmniRoute
Não executar providers em paralelo.
Contrato do modelo
Todos os providers devem produzir o mesmo contrato lógico.
{
"content_type": "text",
"media_type": null
}
ou:
{
"content_type": "media",
"media_type": "video"
}
Valores permitidos:
content_type:
- text
- media
media_type:
- video
- image
- images
- embed
- mixed
- null
Regra:
text → media_type = null
media → media_type obrigatório
Structured output
Sempre que suportado pelo provider, a resposta deve utilizar JSON Schema/structured output nativo.
Independentemente do mecanismo do provider, a aplicação deve validar o resultado antes de aceitá-lo.
Não adicionar parsing tolerante complexo.
O contrato é pequeno e fechado.
Se a resposta não puder ser validada, aquela tentativa é considerada falha do provider e a cadeia avança.
Prompt
O prompt deve ser único e pequeno.
Não criar prompts diferentes por idioma.
A instrução deve explicar apenas:
- classificar
textoumedia; mediasignifica que o texto é essencialmente uma introdução ou descrição da mídia;textsignifica que o artigo possui conteúdo jornalístico textual substancial;- quando
media, selecionarvideo,image,images,embedoumixed; - retornar exclusivamente o schema estabelecido.
Não solicitar:
- resumo;
- justificativa;
- reasoning;
- confiança;
- evidências;
- tradução;
- keywords.
Configuração do modelo
A classificação deve utilizar comportamento determinístico sempre que o provider permitir.
Não habilitar reasoning desnecessário.
A configuração precisa privilegiar:
baixa variabilidade
structured output
baixa latência
resposta curta
Falha dos três providers
Se:
Ollama falhar
Groq falhar
OmniRoute falhar
o sistema deve retornar internamente um estado de falha de classificação.
Exemplo lógico:
{
"classification_status": "failed",
"error_message": "media classification providers unavailable"
}
Não gerar automaticamente:
text
Não gerar automaticamente:
media
Destino da falha
O artigo com falha total:
- permanece registrado no JSON principal;
- não passa pelo multimotor;
- não entra no
*_media.json; - não interrompe os outros artigos do lote.
Não criar:
*_failed.json
Não criar:
- dead-letter queue;
- banco para falhas;
- worker de retry;
- processo assíncrono de recuperação.
Configuração e secrets
As informações específicas de cada provider devem ser externas ao código.
Incluem, quando aplicável:
endpoint
model
API key
timeout
Credenciais não podem aparecer:
- no código-fonte;
- no JSON de saída;
- nos logs;
- nas mensagens de erro persistidas.
Observabilidade
Para cada classificação deve ser possível identificar operacionalmente qual caminho foi utilizado:
ollama
groq
omniroute
failed
Devem existir logs para:
fallback Ollama → Groq
fallback Groq → OmniRoute
falha final
O conteúdo integral do artigo não deve ser logado por padrão.
Não criar um novo sistema de observabilidade especificamente para esta feature.
Testabilidade
A cadeia de providers deve ser testável sem depender dos serviços externos reais.
Os testes devem conseguir simular:
Ollama success
Ollama fail + Groq success
Ollama fail + Groq fail + OmniRoute success
todos falham
provider retorna schema inválido
Não é necessário executar chamadas reais aos três providers na suíte normal de CI.
Alternativas consideradas
Apenas Ollama
Rejeitada.
O sistema é production-ready e precisa continuar funcionando caso o runtime local esteja indisponível.
Ollama + Groq apenas
Rejeitada.
Foi definido um terceiro fallback já disponível através do OmniRoute.
Chamar vários modelos e escolher maioria
Rejeitada.
Não existe requisito que justifique consenso.
Aumentaria:
- custo;
- código;
- latência;
- pontos de falha.
Fallback baseado em confidence
Rejeitada.
Não existe requisito de score de confiança e não deve existir interpretação adicional da resposta.
Modelo grande como principal
Rejeitada.
A tarefa é pequena e fechada.
Qwen3.5 2B atende ao objetivo com custo operacional mínimo.
Criar serviço separado de classificação
Rejeitada.
A classificação pertence ao fluxo atual e não exige um novo deployable.
Consequências positivas
- custo marginal mínimo no caminho principal;
- operação local normalmente;
- alta disponibilidade através de dois fallbacks externos;
- implementação pequena;
- comportamento previsível;
- nenhum acoplamento a framework de agentes;
- fácil teste;
- fácil troca futura de provider através da mesma interface lógica.
Trade-off aceito
Em uma indisponibilidade simultânea de Ollama, Groq e OmniRoute, o artigo não será processado como texto nem mídia.
Essa é uma decisão intencional.
É preferível registrar explicitamente:
classification_failed
a mascarar uma falha operacional tomando uma decisão que o sistema não conseguiu realizar.
Invariantes
Ollama é sempre o primeiro provider.
Groq só é chamado após falha operacional do Ollama.
OmniRoute só é chamado após falha operacional de Ollama e Groq.
Resposta válida encerra a cadeia.
Nunca existe votação.
Nunca existe fallback por confiança.
Falha dos três nunca gera classificação presumida.