Files

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:

  1. receber texto e informações estruturais;
  2. decidir se o texto é conteúdo jornalístico substancial ou apenas descrição/contextualização da mídia;
  3. 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:

  1. classificar text ou media;
  2. media significa que o texto é essencialmente uma introdução ou descrição da mídia;
  3. text significa que o artigo possui conteúdo jornalístico textual substancial;
  4. quando media, selecionar video, image, images, embed ou mixed;
  5. 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.