Roteamento de Modelos: Pare de Usar Um Único Modelo para Tudo

O modelo certo para a tarefa certa.

Conteúdo da página

Executar um modelo de 70 bilhões de parâmetros para resumir um e-mail de 200 palavras é um desperdício. Executar um modelo de 3 bilhões de parâmetros para revisar código de produção é imprudente. A maioria dos sistemas fica em algum ponto intermediário — e é aí que o roteamento de modelos entra.

Ele alinha a complexidade da tarefa à capacidade do modelo. Os compromissos (trade-offs) são reais, mas as economias também.

Diagrama de estratégias de roteamento de modelos LLM

O problema do roteamento

As pessoas geralmente começam com um modelo e se mantêm nele. Isso funciona até que você perceba o custo, ou a latência, ou ambos. A alternativa é construir um roteador — algo que decida qual modelo lida com qual solicitação.

Quatro estratégias funcionam na prática:

  1. Baseado em capacidade — rotear pelo que o modelo pode fazer
  2. Com consciência de custo — rotear pelo quanto você está disposto a gastar
  3. Com consciência de latência — rotear pela velocidade necessária
  4. Híbrido — combiná-los

Cada uma otimiza algo diferente. Escolher uma geralmente é uma decisão sobre o que mais dói.

Roteamento baseado em capacidade

A abordagem mais simples. Classifique a tarefa, envie-a para o modelo que a lida.

Tarefa Tamanho do modelo Exemplos
Classificação, etiquetagem 1-3B Qwen2.5-1.5B, Gemma-2-2B
Resumo, extração 3-7B Qwen2.5-7B, Llama-3.1-8B
Geração de código 7-14B Qwen2.5-Coder-7B, DeepSeek-Coder-V2
Raciocínio complexo 14-32B Qwen2.5-32B, Llama-3.1-70B
Escrita criativa, análise 32B+ Qwen2.5-72B, Claude, GPT-4

Se a tarefa não precisa do modelo maior, não o use. Um modelo de 1.5B lida bem com classificação de sentimento. Ele simplesmente não escreverá um ensaio coerente.

A implementação é direta:

REGRAS_DE_ROTEAMENTO = {
    "classificar": {"modelo": "qwen2.5-1.5b", "max_tokens": 100},
    "resumir": {"modelo": "qwen2.5-7b", "max_tokens": 500},
    "revisao_de_codigo": {"modelo": "qwen2.5-coder-7b", "max_tokens": 2000},
    "raciocinar": {"modelo": "qwen2.5-32b", "max_tokens": 4000},
    "criativo": {"modelo": "claude-sonnet-4", "max_tokens": 8000},
}

def rotear_solicitacao(tipo_tarefa: str) -> dict:
    return REGRAS_DE_ROTEAMENTO.get(tipo_tarefa, REGRAS_DE_ROTEAMENTO["raciocinar"])

O problema é a própria classificação. Se você errar o tipo de tarefa, roteará para o modelo errado. Vi sistemas classificar revisão de código como “resumo” e perder qualidade silenciosamente.

Roteamento com consciência de custo

A inferência local brilha aqui. Modelos locais são efetivamente gratuitos após a amortização do hardware. Uma RTX 5080 se paga em cerca de seis meses com um uso moderado de API.

Modelo Entrada ($/M tokens) Saída ($/M tokens) Custo local/hora
GPT-4o $2.50 $10.00
Claude Sonnet 4 $3.00 $15.00
Qwen2.5-72B (API) $0.50 $2.00
Qwen2.5-32B (local) $0.00 $0.00 ~$0.10
Qwen2.5-7B (local) $0.00 $0.00 ~$0.05

Se você está processando milhares de solicitações por sessão, até $0.05 em eletricidade supera $15/M tokens.

O roteamento baseado em orçamento recua conforme você gasta:

class RoteadorComConscienciaDeCusto:
    def __init__(self, orcamento_por_sessao: float = 0.10):
        self.orcamento = orcamento_por_sessao
        self.gasto = 0.0
        self.modelos = {
            "barato": {"modelo": "qwen2.5-7b", "custo": 0.0},
            "medio": {"modelo": "qwen2.5-32b", "custo": 0.0},
            "caro": {"modelo": "claude-sonnet-4", "custo": 0.000015},
        }

    def rotear(self, tarefa: str) -> str:
        proporcao = self.gasto / self.orcamento
        if proporcao < 0.5:
            return self.modelos["caro"]["modelo"]
        elif proporcao < 0.8:
            return self.modelos["medio"]["modelo"]
        return self.modelos["barato"]["modelo"]

A qualidade se degrada conforme você recua. Você começa com Claude, move-se para Qwen-32B, depois para Qwen-7B. No final de uma sessão longa, a saída é notavelmente pior. Se isso importa depende do que você está construindo.

Roteamento com consciência de latência

Ferramentas interativas precisam de primeiros tokens rápidos. Tarefas em lote podem esperar. A diferença geralmente é um fator de cinco no tamanho do modelo.

Caso de uso Primeiro token Completo Tamanho máximo do modelo
Chat em tempo real < 200ms < 2s < 7B
Ferramentas interativas < 500ms < 5s < 14B
Processamento em lote < 1s < 30s Qualquer
Pesquisa/análise < 2s < 60s Qualquer

Quando você está transmitindo tokens para um usuário, a latência do primeiro token é o que eles sentem. Um modelo de 32B levando meio segundo para começar parece lento em comparação com um modelo de 1.5B que dispara instantaneamente.

class RoteadorComConscienciaDeLatencia:
    def __init__(self):
        self.latencias_modelos = {
            "qwen2.5-1.5b": {"primeiro_token": 0.05, "completo": 0.5},
            "qwen2.5-7b": {"primeiro_token": 0.15, "completo": 2.0},
            "qwen2.5-32b": {"primeiro_token": 0.5, "completo": 10.0},
            "claude-sonnet-4": {"primeiro_token": 0.3, "completo": 5.0},
        }

    def rotear(self, latencia_alvo: float) -> str:
        for modelo, latencias in sorted(
            self.latencias_modelos.items(),
            key=lambda x: x[1]["completo"]
        ):
            if latencias["completo"] <= latencia_alvo:
                return modelo
        return "qwen2.5-1.5b"

Os números de latência são aproximados — eles dependem do seu hardware, quantização e tamanho do lote. Meça na sua própria configuração.

Estratégias de fallback

Modelos falham. APIs limitam taxa. Timeouts acontecem. O padrão que funciona é uma cadeia de fallback, ordenada do melhor ao mais confiável:

class RoteadorComFallback:
    def __init__(self):
        self.cadeia_fallback = [
            {"modelo": "claude-sonnet-4", "timeout": 30},
            {"modelo": "qwen2.5-72b", "timeout": 60},
            {"modelo": "qwen2.5-32b", "timeout": 120},
            {"modelo": "qwen2.5-7b", "timeout": 300},
        ]

    def rotear_com_fallback(self, prompt: str) -> str:
        for config in self.cadeia_fallback:
            try:
                return self.chamar_modelo(
                    config["modelo"], prompt,
                    timeout=config["timeout"]
                )
            except (TimeoutError, APIError) as e:
                log.warning(f"Modelo {config['modelo']} falhou: {e}")
                continue
        raise RuntimeError("Todos os modelos de fallback falharam")

O último modelo na cadeia deve ser local. É mais lento, mas não falhará devido a um problema de rede ou chave de API.

Quando o roteamento ajuda

O roteamento faz sentido quando sua carga de trabalho é mista. Se você está fazendo classificação, resumo e raciocínio no mesmo sistema, um roteador economiza dinheiro e latência.

Não faz sentido quando tudo o que você faz tem a mesma complexidade. Apenas use o modelo que é bom nessa tarefa. O roteador adiciona complexidade que você não precisa.

Prototipagem inicial é outra razão para ignorá-lo. Faça a tarefa funcionar com um modelo, então adicione roteamento quando custo ou latência realmente se tornarem um problema.

Compromissos (Trade-offs)

Cada estratégia de roteamento otimiza algo e sacrifica outra coisa:

  • Modelo único — mais simples, mais caro, qualidade consistente
  • Baseado em capacidade — melhor custo, qualidade mais alta por tarefa, complexidade moderada
  • Com consciência de custo — mais barato, qualidade varia, complexidade moderada
  • Com consciência de latência — mais rápido, pode sacrificar qualidade, complexidade moderada
  • Híbrido — o melhor de todos, mais complexo de implementar

Sistemas de produção geralmente convergem para híbrido. Comece com roteamento baseado em capacidade, adicione consciência de custo quando a conta chegar, adicione consciência de latência quando os usuários reclamarem da lentidão.

Relacionado

Assinar

Receba novos artigos sobre sistemas, infraestrutura e engenharia de IA.