feat(media-routing): implement 007 media article routing, runtime architecture diagram and update graphify knowledge graph
This commit is contained in:
Binary file not shown.
|
Before Width: | Height: | Size: 178 KiB After Width: | Height: | Size: 178 KiB |
@@ -5,7 +5,7 @@
|
||||
"status": "pass",
|
||||
"visualReview": "pending",
|
||||
"artifact": {
|
||||
"path": "C:\\Users\\aferr\\Projects\\AFTech\\DunaMedia\\TextNLPClassifierApp\\docs\\runtime-architecture.html",
|
||||
"path": "C:\\Users\\aferr\\Projects\\AFTech\\DunaMedia\\TextNLPClassifierApp\\docs\\archify\\runtime-architecture.html",
|
||||
"sha256": "66284a667f9c3ec5afd8d275fdd83e5980425836788aec8d650c95cfffaa9d1f",
|
||||
"bytes": 693750
|
||||
},
|
||||
|
||||
@@ -0,0 +1,368 @@
|
||||
# ADR-001 — Classificar e rotear conteúdo predominantemente de mídia antes do multimotor
|
||||
|
||||
## Status
|
||||
|
||||
Accepted
|
||||
|
||||
---
|
||||
|
||||
# Contexto
|
||||
|
||||
O pipeline atual obtém a página completamente carregada através do crawler e, posteriormente, executa múltiplos motores de extração textual.
|
||||
|
||||
Esse desenho é adequado para artigos cujo conteúdo principal é texto.
|
||||
|
||||
Entretanto, alguns veículos publicam páginas onde:
|
||||
|
||||
* existe apenas uma breve introdução textual;
|
||||
* o conteúdo principal é um vídeo, imagem, conjunto de imagens ou conteúdo incorporado.
|
||||
|
||||
Processar essas páginas com os motores textuais é desnecessário e pode gerar resultados pobres ou irrelevantes.
|
||||
|
||||
A responsabilidade de identificar esses casos deve ser adicionada sem alterar o crawler e sem transformar o pipeline em uma nova arquitetura.
|
||||
|
||||
---
|
||||
|
||||
# Decisão
|
||||
|
||||
Adicionar uma etapa de classificação imediatamente após o crawl e antes do multimotor.
|
||||
|
||||
Fluxo:
|
||||
|
||||
```text
|
||||
Crawler
|
||||
↓
|
||||
DOM completamente carregada
|
||||
↓
|
||||
Media Candidate Detection
|
||||
↓
|
||||
├── sem mídia candidata
|
||||
│ ↓
|
||||
│ multimotor atual
|
||||
│
|
||||
└── com mídia candidata
|
||||
↓
|
||||
Media Content Classifier
|
||||
↓
|
||||
├── text
|
||||
│ ↓
|
||||
│ multimotor atual
|
||||
│
|
||||
└── media
|
||||
↓
|
||||
JSON separado
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# Detecção estrutural
|
||||
|
||||
Antes do LLM deve existir apenas uma análise estrutural simples da DOM.
|
||||
|
||||
Objetivo:
|
||||
|
||||
> verificar se existe algum elemento de mídia que justifique a classificação.
|
||||
|
||||
A análise deve operar sobre a DOM/HTML já carregada pelo crawler.
|
||||
|
||||
É permitido utilizar:
|
||||
|
||||
* parser HTML;
|
||||
* navegação por nós;
|
||||
* tags;
|
||||
* atributos estruturais;
|
||||
* relações entre elementos;
|
||||
* contagem de elementos.
|
||||
|
||||
É proibido utilizar:
|
||||
|
||||
* regex;
|
||||
* listas de palavras específicas por idioma;
|
||||
* heurísticas semânticas por idioma.
|
||||
|
||||
Essa etapa não decide se a publicação é `media`.
|
||||
|
||||
Ela decide apenas se há motivo para chamar o classificador.
|
||||
|
||||
---
|
||||
|
||||
# Conteúdo enviado ao modelo
|
||||
|
||||
O modelo deve receber uma representação compacta dos dados já existentes na página.
|
||||
|
||||
Devem ser utilizados somente dados necessários para a decisão, como:
|
||||
|
||||
```text
|
||||
título
|
||||
blocos textuais relevantes
|
||||
presença de imagem
|
||||
quantidade de imagens
|
||||
presença de vídeo
|
||||
presença de elementos incorporados
|
||||
```
|
||||
|
||||
Não enviar mídia binária.
|
||||
|
||||
Não realizar chamadas externas para compreender a mídia.
|
||||
|
||||
Não enviar HTML completo quando uma representação compacta da estrutura puder fornecer a mesma informação.
|
||||
|
||||
---
|
||||
|
||||
# Responsabilidade do classificador
|
||||
|
||||
O classificador responde uma única pergunta conceitual:
|
||||
|
||||
> O texto desta publicação possui conteúdo jornalístico substancial por si próprio ou funciona essencialmente como uma breve introdução, contextualização ou descrição da mídia presente?
|
||||
|
||||
Se o texto for substancial:
|
||||
|
||||
```json
|
||||
{
|
||||
"content_type": "text",
|
||||
"media_type": null
|
||||
}
|
||||
```
|
||||
|
||||
Se a mídia for o conteúdo principal:
|
||||
|
||||
```json
|
||||
{
|
||||
"content_type": "media",
|
||||
"media_type": "..."
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# Tipos permitidos
|
||||
|
||||
```text
|
||||
video
|
||||
image
|
||||
images
|
||||
embed
|
||||
mixed
|
||||
```
|
||||
|
||||
Não criar subtipos adicionais.
|
||||
|
||||
---
|
||||
|
||||
# Regra para múltiplas imagens
|
||||
|
||||
Não é necessário identificar tecnicamente um componente carousel.
|
||||
|
||||
Se múltiplas imagens constituem o conteúdo principal, o resultado deve ser:
|
||||
|
||||
```text
|
||||
images
|
||||
```
|
||||
|
||||
Independentemente de serem apresentadas como:
|
||||
|
||||
* carousel;
|
||||
* slideshow;
|
||||
* galeria;
|
||||
* sequência vertical;
|
||||
* qualquer outra composição visual.
|
||||
|
||||
---
|
||||
|
||||
# Regra para conteúdo misto
|
||||
|
||||
Quando mais de uma categoria de mídia constituir o conteúdo principal:
|
||||
|
||||
```text
|
||||
mixed
|
||||
```
|
||||
|
||||
Não criar precedência artificial como:
|
||||
|
||||
```text
|
||||
video > image
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# Artigos textuais
|
||||
|
||||
Um artigo continua sendo `text` mesmo contendo mídia quando existe conteúdo jornalístico textual substancial.
|
||||
|
||||
Portanto:
|
||||
|
||||
```text
|
||||
presença de mídia ≠ classificação media
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# Artigos muito curtos
|
||||
|
||||
Uma publicação extremamente curta com uma imagem pode ser classificada como `media/image`.
|
||||
|
||||
Não é necessário tentar preservar esse conteúdo como artigo textual apenas porque existe algum texto.
|
||||
|
||||
---
|
||||
|
||||
# Posicionamento arquitetural
|
||||
|
||||
A nova etapa deve permanecer fora de:
|
||||
|
||||
```text
|
||||
Trafilatura
|
||||
Newspaper4k
|
||||
Readability
|
||||
```
|
||||
|
||||
Nenhum dos três motores é responsável pela classificação.
|
||||
|
||||
O método equivalente ao atual `extract_all_engines()` somente deve ser executado depois que o novo roteamento determinar:
|
||||
|
||||
```text
|
||||
content_type = text
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# Saída física
|
||||
|
||||
Artigos textuais:
|
||||
|
||||
```text
|
||||
*_extracted.json
|
||||
```
|
||||
|
||||
Artigos predominantemente de mídia:
|
||||
|
||||
```text
|
||||
*_media.json
|
||||
```
|
||||
|
||||
A classificação de mídia não deve aparecer misturada aos artigos textuais processados com sucesso.
|
||||
|
||||
---
|
||||
|
||||
# Dados preservados para mídia
|
||||
|
||||
Cada registro de mídia deve preservar somente informações básicas necessárias à rastreabilidade:
|
||||
|
||||
```text
|
||||
input_meta
|
||||
crawled_url
|
||||
page_title
|
||||
http_status
|
||||
content_type
|
||||
media_type
|
||||
```
|
||||
|
||||
Não existe extração de mídia nesta feature.
|
||||
|
||||
---
|
||||
|
||||
# Alternativas consideradas
|
||||
|
||||
## Executar primeiro o multimotor e identificar mídia depois
|
||||
|
||||
Rejeitada.
|
||||
|
||||
Motivos:
|
||||
|
||||
* executa trabalho desnecessário;
|
||||
* mistura responsabilidades;
|
||||
* não evita custo de processamento;
|
||||
* mantém páginas inadequadas dentro do pipeline textual.
|
||||
|
||||
---
|
||||
|
||||
## Usar Newspaper4k para descobrir mídia
|
||||
|
||||
Rejeitada.
|
||||
|
||||
Embora Newspaper4k possa expor informações de imagens e vídeos, utilizá-lo significaria executar parte do multimotor justamente nos conteúdos que a nova feature pretende desviar antes do multimotor.
|
||||
|
||||
---
|
||||
|
||||
## Classificar tudo apenas com regras
|
||||
|
||||
Rejeitada.
|
||||
|
||||
A decisão:
|
||||
|
||||
```text
|
||||
texto jornalístico curto
|
||||
```
|
||||
|
||||
versus:
|
||||
|
||||
```text
|
||||
texto que apenas descreve uma mídia
|
||||
```
|
||||
|
||||
é semântica e precisa funcionar em até 10 idiomas.
|
||||
|
||||
Criar heurísticas específicas para resolver isso aumentaria código, manutenção e fragilidade.
|
||||
|
||||
---
|
||||
|
||||
## Utilizar regex
|
||||
|
||||
Rejeitada e proibida por requisito.
|
||||
|
||||
---
|
||||
|
||||
## Utilizar modelo multimodal
|
||||
|
||||
Rejeitada.
|
||||
|
||||
O sistema não precisa entender a mídia.
|
||||
|
||||
Precisa apenas determinar se o texto é o conteúdo principal ou se serve de introdução à mídia.
|
||||
|
||||
---
|
||||
|
||||
# Consequências positivas
|
||||
|
||||
* reduz processamento desnecessário;
|
||||
* mantém o multimotor focado em texto;
|
||||
* separa claramente conteúdos de naturezas diferentes;
|
||||
* mantém a implementação pequena;
|
||||
* evita regras linguísticas;
|
||||
* funciona de forma multilíngue;
|
||||
* não introduz nova infraestrutura;
|
||||
* permite evolução futura do pipeline de mídia de forma independente.
|
||||
|
||||
---
|
||||
|
||||
# Consequências aceitas
|
||||
|
||||
Alguns artigos textuais muito curtos acompanhados de imagem poderão ser classificados como `media/image`.
|
||||
|
||||
Esse comportamento é deliberado e aceito, pois conteúdos textuais extremamente pobres não são úteis para o objetivo do pipeline textual.
|
||||
|
||||
---
|
||||
|
||||
# Invariantes
|
||||
|
||||
A implementação deve sempre preservar:
|
||||
|
||||
```text
|
||||
MEDIA
|
||||
→ nunca executa multimotor
|
||||
|
||||
TEXT
|
||||
→ segue pipeline atual
|
||||
|
||||
classification_failed
|
||||
→ não assume TEXT
|
||||
→ não assume MEDIA
|
||||
```
|
||||
|
||||
E:
|
||||
|
||||
```text
|
||||
nenhuma regex
|
||||
nenhum download de mídia
|
||||
nenhuma análise multimodal
|
||||
nenhuma interação com carousel
|
||||
```
|
||||
@@ -0,0 +1,516 @@
|
||||
# ADR-002 — Utilizar Qwen3.5 2B local com fallback operacional Groq e OmniRoute
|
||||
|
||||
## Status
|
||||
|
||||
Accepted
|
||||
|
||||
---
|
||||
|
||||
# Contexto
|
||||
|
||||
A classificação entre:
|
||||
|
||||
```text
|
||||
text
|
||||
```
|
||||
|
||||
e:
|
||||
|
||||
```text
|
||||
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:
|
||||
|
||||
```text
|
||||
Qwen3.5 2B / Ollama
|
||||
↓ falha operacional
|
||||
GPT-OSS 20B / Groq
|
||||
↓ falha operacional
|
||||
cgpt-web/gpt-5.5 / OmniRoute
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# Provider primário
|
||||
|
||||
```text
|
||||
Qwen3.5 2B
|
||||
```
|
||||
|
||||
Runtime:
|
||||
|
||||
```text
|
||||
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
|
||||
|
||||
```text
|
||||
GPT-OSS 20B
|
||||
```
|
||||
|
||||
Provider:
|
||||
|
||||
```text
|
||||
Groq
|
||||
```
|
||||
|
||||
O Groq somente é chamado quando o provider primário não consegue fornecer uma classificação tecnicamente utilizável.
|
||||
|
||||
---
|
||||
|
||||
# Segundo fallback
|
||||
|
||||
Modelo:
|
||||
|
||||
```text
|
||||
cgpt-web/gpt-5.5
|
||||
```
|
||||
|
||||
Provider:
|
||||
|
||||
```text
|
||||
OmniRoute
|
||||
```
|
||||
|
||||
O OmniRoute somente é utilizado quando:
|
||||
|
||||
```text
|
||||
Ollama falhou
|
||||
E
|
||||
Groq falhou
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# Tipo de fallback
|
||||
|
||||
O fallback é exclusivamente operacional.
|
||||
|
||||
Exemplos de falha que justificam fallback:
|
||||
|
||||
```text
|
||||
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:
|
||||
|
||||
```text
|
||||
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.
|
||||
|
||||
```text
|
||||
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.
|
||||
|
||||
```json
|
||||
{
|
||||
"content_type": "text",
|
||||
"media_type": null
|
||||
}
|
||||
```
|
||||
|
||||
ou:
|
||||
|
||||
```json
|
||||
{
|
||||
"content_type": "media",
|
||||
"media_type": "video"
|
||||
}
|
||||
```
|
||||
|
||||
Valores permitidos:
|
||||
|
||||
```text
|
||||
content_type:
|
||||
- text
|
||||
- media
|
||||
```
|
||||
|
||||
```text
|
||||
media_type:
|
||||
- video
|
||||
- image
|
||||
- images
|
||||
- embed
|
||||
- mixed
|
||||
- null
|
||||
```
|
||||
|
||||
Regra:
|
||||
|
||||
```text
|
||||
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:
|
||||
|
||||
```text
|
||||
baixa variabilidade
|
||||
structured output
|
||||
baixa latência
|
||||
resposta curta
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# Falha dos três providers
|
||||
|
||||
Se:
|
||||
|
||||
```text
|
||||
Ollama falhar
|
||||
Groq falhar
|
||||
OmniRoute falhar
|
||||
```
|
||||
|
||||
o sistema deve retornar internamente um estado de falha de classificação.
|
||||
|
||||
Exemplo lógico:
|
||||
|
||||
```json
|
||||
{
|
||||
"classification_status": "failed",
|
||||
"error_message": "media classification providers unavailable"
|
||||
}
|
||||
```
|
||||
|
||||
Não gerar automaticamente:
|
||||
|
||||
```text
|
||||
text
|
||||
```
|
||||
|
||||
Não gerar automaticamente:
|
||||
|
||||
```text
|
||||
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:
|
||||
|
||||
```text
|
||||
*_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:
|
||||
|
||||
```text
|
||||
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:
|
||||
|
||||
```text
|
||||
ollama
|
||||
groq
|
||||
omniroute
|
||||
failed
|
||||
```
|
||||
|
||||
Devem existir logs para:
|
||||
|
||||
```text
|
||||
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:
|
||||
|
||||
```text
|
||||
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:
|
||||
|
||||
```text
|
||||
classification_failed
|
||||
```
|
||||
|
||||
a mascarar uma falha operacional tomando uma decisão que o sistema não conseguiu realizar.
|
||||
|
||||
---
|
||||
|
||||
# Invariantes
|
||||
|
||||
```text
|
||||
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.
|
||||
```
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user