Model Tiering: Roteamento de Tarefas Simples para Haiku, Tarefas Difíceis para Opus
Enviar todas as requisições em um pipeline para o mesmo modelo é a coisa mais simples de construir e, em qualquer volume real, uma das escolhas mais caras que você pode fazer.
Um roteador que classifica a complexidade da tarefa primeiro e só escala para um modelo mais forte e caro quando a tarefa realmente precisa dele pode cortar drasticamente o custo misto sem afetar a qualidade da saída nas tarefas que importam.
Esta página constrói esse roteador: uma primeira passagem de classificação barata no Haiku 4.5, um caminho de escalonamento para o Opus 4.8 para qualquer coisa sinalizada como difícil e as salvaguardas que impedem que uma classificação ruim degrade silenciosamente a qualidade.
Receita
Cartão de receita de referência rápida, pronto para copiar e colar.
import anthropic
client = anthropic.Anthropic()
def classify_difficulty(client, task_text: str) -> str:
response = client.messages.create(
model="claude-haiku-4-5",
max_tokens=10,
messages=[{
"role": "user",
"content": f"Classifique esta tarefa como SIMPLES ou DIFÍCIL. Responda com uma palavra.\n\nTarefa: {task_text}",
}],
)
return response.content[0].text.strip().upper()
def route(client, task_text: str) -> str:
difficulty = classify_difficulty(client, task_text)
model = "claude-opus-4-8" if difficulty == "HARD" else "claude-sonnet-5"
response = client.messages.create(
model=model,
max_tokens=500,
messages=[{"role": "user", "content": task_text}],
)
return response.content[0].textQuando usar isso:
- Seu pipeline lida com uma mistura de dificuldades de tarefa, algumas genuinamente simples (classificação, extração, formatação), outras genuinamente difíceis (raciocínio multi-etapas, requisitos ambíguos).
- O volume é alto o suficiente para que a diferença de custo por chamada única se acumule em uma preocupação orçamentária real.
- Você pode tolerar uma pequena sobrecarga de classificação (uma chamada extra barata) em troca de precisão no roteamento.
- Você deseja um motivo defensável e auditável para qual modelo lidou com uma determinada requisição, não um padrão arbitrário.
Exemplo de Trabalho
import anthropic
from dataclasses import dataclass
client = anthropic.Anthropic()
MODELS = {
"haiku": "claude-haiku-4-5",
"sonnet": "claude-sonnet-5",
"opus": "claude-opus-4-8",
}
@dataclass
class RoutedResponse:
text: str
model_used: str
difficulty: str
def classify_difficulty(client, task_text: str) -> str:
"""Classificação de primeira passagem barata no Haiku. Retorna SIMPLES, MODERADO ou DIFÍCIL."""
response = client.messages.create(
model=MODELS["haiku"],
max_tokens=10,
messages=[{
"role": "user",
"content": (
"Classifique a dificuldade da seguinte tarefa com exatamente uma palavra: "
"SIMPLES, MODERADO ou DIFÍCIL.\n"
"SIMPLES: classificação, extração, formatação, consulta.\n"
"MODERADO: sumarização, rascunho a partir de requisitos claros.\n"
"DIFÍCIL: raciocínio multi-etapas, requisitos ambíguos, análise de trade-offs.\n\n"
f"Tarefa: {task_text}"
),
}],
)
label = response.content[0].text.strip().upper()
return label if label in {"SIMPLE", "MODERATE", "HARD"} else "MODERATE"
def route_task(client, task_text: str) -> RoutedResponse:
"""Classifica a dificuldade e, em seguida, envia para o nível de modelo correspondente."""
difficulty = classify_difficulty(client, task_text)
model = {
"SIMPLE": MODELS["haiku"],
"MODERATE": MODELS["sonnet"],
"HARD": MODELS["opus"],
}[difficulty]
response = client.messages.create(
model=model,
max_tokens=800,
messages=[{"role": "user", "content": task_text}],
)
return RoutedResponse(
text=response.content[0].text,
model_used=model,
difficulty=difficulty,
)
result = route_task(client, "Marque este ticket de suporte: 'Meu pacote chegou danificado.'")
print(result.model_used, result.difficulty)
result2 = route_task(
client,
"Precisamos decidir entre event sourcing e um log de auditoria simples para nosso "
"pipeline de pedidos. Pondere os trade-offs, considerando que temos 3 engenheiros e um prazo de 6 semanas.",
)
print(result2.model_used, result2.difficulty)O que isso demonstra:
- Uma classificação de três níveis (SIMPLES/MODERADO/DIFÍCIL) mapeia-se claramente para a linha de modelos de três níveis usada aqui (Haiku/Sonnet/Opus), em vez de uma divisão binária simples ou difícil.
- O prompt do classificador fornece exemplos concretos de cada categoria, o que é mais importante para a precisão da classificação do que o nível do modelo que realiza a classificação.
- A própria classificação é executada no Haiku, o nível mais barato, pois classificar a dificuldade é em si uma tarefa com formato SIMPLES.
- A dataclass
RoutedResponsecarregamodel_usededifficultyao lado da resposta, para que cada resposta roteada seja auditável posteriormente. - Um fallback padrão (
MODERATE) lida com o caso em que o classificador retorna algo fora dos três rótulos esperados, em vez de travar o pipeline.
Mergulho Profundo
Como Funciona
- O roteador adiciona exatamente uma chamada extra por requisição: uma chamada curta do Haiku com um
max_tokensminúsculo (10 neste exemplo), que custa uma fração de centavo, mesmo repetida em volume. - Essa sobrecarga de classificação é o preço da decisão de roteamento, e ela precisa permanecer barata em relação às economias que possibilita, razão pela qual o próprio classificador sempre é executado no nível mais barato.
- O caminho de escalonamento paga apenas as taxas do nível Opus para requisições efetivamente marcadas como DIFÍCIL, de modo que o gasto total acompanha a verdadeira mistura de dificuldade da sua carga de trabalho, em vez do seu pior caso.
- A precisão da classificação é o maior risco neste padrão: uma tarefa DIFÍCIL mal classificada como SIMPLES é roteada para um modelo que pode produzir uma resposta pior, silenciosamente, sem nenhum erro para capturar.
Protegendo Contra Classificação Incorreta
def route_with_confidence_check(client, task_text: str) -> RoutedResponse:
"""Escala automaticamente se a resposta do nível SIMPLES parecer muito curta ou evasiva."""
difficulty = classify_difficulty(client, task_text)
model = {"SIMPLE": MODELS["haiku"], "MODERATE": MODELS["sonnet"], "HARD": MODELS["opus"]}[difficulty]
response = client.messages.create(model=model, max_tokens=800, messages=[{"role": "user", "content": task_text}])
text = response.content[0].text
# Uma resposta SIMPLES que é muito evasiva é um sinal de que o classificador subestimou a dificuldade.
hedge_markers = ("depende", "não tenho certeza", "isso é complexo", "incerto")
if difficulty == "SIMPLE" and any(marker in text.lower() for marker in hedge_markers):
response = client.messages.create(model=MODELS["opus"], max_tokens=800, messages=[{"role": "user", "content": task_text}])
return RoutedResponse(text=response.content[0].text, model_used=MODELS["opus"], difficulty="HARD (escalado)")
return RoutedResponse(text=text, model_used=model, difficulty=difficulty)Esta é uma heurística barata, não uma pontuação de confiança rigorosa, mas ela captura o modo de falha específico em que uma tarefa parecia simples isoladamente, mas o próprio modelo sinaliza incerteza. Um sistema de produção que faz isso em escala normalmente registraria escalonamentos e os revisaria periodicamente para refinar as definições de categoria do classificador.
Comparação de Custos: Roteado vs. Plano
| Abordagem | 1.000 tarefas SIMPLES + 200 MODERADAS + 50 DIFÍCEIS (formato de token ilustrativo) | Custo relativo |
|---|---|---|
| Opus 4.8 plano para tudo | Todas as 1.250 tarefas a taxas do Opus | Mais alto |
| Sonnet 5 plano para tudo | Todas as 1.250 tarefas a taxas do Sonnet | Linha de base |
| Roteado (este padrão) | 1.000 no Haiku + 200 no Sonnet + 50 no Opus, mais 1.250 chamadas de classificação | Mais baixo, tipicamente bem abaixo da linha de base do Sonnet plano |
As economias exatas dependem da sua mistura de tarefas e dos formatos de token por tarefa, mas a direção se mantém amplamente: quanto mais desequilibrada for sua carga de trabalho em direção a tarefas simples e de alto volume, mais um roteador economizará em comparação com qualquer política plana de modelo único.
Parâmetros e Valores de Retorno
| Parâmetro | Tipo | Descrição |
|---|---|---|
task_text | str | A tarefa/prompt bruto a ser classificado e roteado. |
classify_difficulty(...) | str | Retorna um de SIMPLE, MODERATE, HARD (com um fallback MODERATE em um rótulo inesperado). |
route_task(...) | RoutedResponse | Agrupa a resposta do modelo com qual modelo o tratou e por quê, para auditabilidade. |
Armadilhas
- Permitir que erros de classificação degradem silenciosamente a qualidade da saída. Uma tarefa DIFÍCIL mal roteada para o Haiku não gera um erro, apenas retorna uma resposta pior que parece superficialmente boa. Correção: adicione uma verificação de confiança leve (veja acima) ou audite periodicamente as saídas roteadas em comparação com uma linha de base humana ou de modelo mais forte.
- Tornar o prompt do classificador muito vago. "Esta tarefa é simples ou difícil?" sem exemplos produz rótulos inconsistentes. Correção: dê ao classificador exemplos concretos de categorias, como no Exemplo de Trabalho, ajustados à sua distribuição real de tarefas.
- Esquecer que a própria chamada de classificação tem um custo. Em volume extremamente alto com custos por tarefa muito baixos, a sobrecarga de classificação pode se aproximar das economias que ela deveria gerar. Correção: meça o próprio custo do classificador em relação às economias que ele gera; para uma carga de trabalho onde cada tarefa já é conhecida por ter dificuldade uniforme, o roteamento pode não valer a pena a sobrecarga.
- Codificar categorias de dificuldade para corresponder apenas a dois níveis de modelo. Uma divisão binária SIMPLES/DIFÍCIL força uma escolha falsa quando uma tarefa é genuinamente MODERADA. Correção: use tantos níveis quanto sua linha de modelos suportar, mapeando categoria para modelo um a um.
- Não registrar qual modelo tratou de cada requisição. Sem
model_used(ou equivalente) em sua saída, você não pode auditar se as decisões de roteamento foram sólidas depois do fato. Correção: sempre carregue a decisão de roteamento ao lado da resposta, como fazRoutedResponseaqui. - Tratar o roteador como uma construção única. As distribuições de tarefas mudam à medida que um produto evolui, e um classificador ajustado para a mistura de tarefas do trimestre passado pode ficar desatualizado. Correção: revise periodicamente uma amostra de decisões de roteamento em comparação com a dificuldade real e reajuste os exemplos do classificador.
Alternativas
| Alternativa | Usar Quando | Não Usar Quando |
|---|---|---|
| Roteador classificador baseado em LLM (esta página) | A dificuldade da tarefa varia e não pode ser conhecida apenas por metadados estáticos | A dificuldade é totalmente determinada por um campo que você já possui (por exemplo, um tipo de tarefa conhecido) |
| Roteador baseado em regras (por exemplo, por tipo de tarefa ou comprimento de entrada) | A dificuldade se correlaciona claramente com algo que você pode verificar sem uma chamada de API extra | A dificuldade depende genuinamente do conteúdo, não apenas da forma ou categoria |
| Política de modelo único plano | O volume de tarefas é baixo o suficiente para que a sobrecarga de tiering não valha o custo de engenharia | A carga de trabalho é de alto volume e inclinada para tarefas simples, o tiering é a alavanca de maior impacto disponível |
| Sempre escalar para o modelo mais forte | A correção é crítica para a segurança e o custo é uma preocupação secundária | O custo importa e a maioria das tarefas não precisa da capacidade do modelo mais forte |
FAQs
Por que classificar com Haiku em vez de uma verificação baseada em regras?
Regras funcionam bem quando a dificuldade se correlaciona com algo estrutural, como o comprimento da entrada ou um tipo de tarefa conhecido. Quando a dificuldade depende do conteúdo real, um classificador LLM generaliza melhor do que um conjunto de regras escrito à mão.
A chamada de classificação não adiciona custo e latência?
Sim, ambos, mas uma chamada curta do Haiku é barata e rápida em relação às economias obtidas ao rotear corretamente para longe de um modelo caro. Meça o trade-off para seu volume específico e orçamento de latência.
O que acontece se o classificador rotular incorretamente uma tarefa DIFÍCIL como SIMPLES?
Ela é roteada para um modelo mais fraco e pode retornar uma resposta de menor qualidade sem um erro explícito. Este é o principal risco do padrão, mitigado por verificações de confiança, auditorias de amostragem ou aceitando alguma taxa de erro como um trade-off pelas economias.
Devo usar três níveis ou apenas dois (simples/difícil)?
Combine a contagem de níveis com sua linha de modelos e distribuição de tarefas. Três níveis (Haiku/Sonnet/Opus) mapeiam naturalmente para SIMPLES/MODERADO/DIFÍCIL; uma divisão de dois níveis funciona se suas tarefas realmente se agrupam em dois baldes limpos.
Este padrão também pode rotear para Fable 5?
Sim, adicione uma quarta categoria para tarefas que precisam da capacidade do nível superior e reserve-a para uma fatia genuinamente estreita do trabalho mais difícil e de maior risco, pois o multiplicador de custo do Fable é o maior na linha.
Vale a pena construir um roteador para uma ferramenta interna de baixo volume?
Geralmente não. O custo de engenharia de construir, ajustar e manter um roteador só compensa quando o volume é alto o suficiente para que as economias por chamada se somem a um número significativo.
Como sei se meu classificador é realmente preciso?
Amostre periodicamente requisições roteadas e compare a saída do modelo roteado com o que um modelo mais forte produziria para a mesma entrada, e acompanhe a taxa de desacordo ao longo do tempo como seu sinal de precisão.
O roteamento substitui a necessidade de cache de prompt ou batching?
Não, essas são alavancas independentes. Um roteador bem nivelado ainda pode se beneficiar de um prompt de sistema em cache ou processamento em lote para a parte não urgente de seu tráfego.
Qual é o maior risco de errar o tiering do modelo?
A degradação silenciosa da qualidade em tarefas difíceis mal roteadas é o maior risco, pois não aparece como um erro, apenas como uma saída sutilmente pior que pode não ser detectada até que um usuário ou revisor perceba.
Relacionado
- Sonnet 5 vs Opus 4.8 vs Fable 5: Uma Comparação de Custo por Tarefa - a matemática de custo por tarefa que este roteador foi projetado para explorar.
- Construindo um Calculador de Custo Pré-Requisição com a API de Contagem de Tokens - estimando o custo para qualquer nível que o roteador escolher.
- Modelo ADR: Escolhendo um Nível de Modelo Padrão para Sua Equipe - documentando a política de roteamento como uma decisão para toda a equipe.
- Economia da API em Lote: Quando 50% de Desconto Supera Chamadas em Tempo Real - uma segunda alavanca de custo que se sobrepõe ao tiering.
Versões da Stack: Escrito contra a linha de modelos Claude atual por volta de junho de 2026 - Claude Fable 5, Claude Opus 4.8, Claude Sonnet 5 (o padrão), e Claude Haiku 4.5 - e o SDK oficial
anthropicpara Python (última versão 0.x). Nomes de modelos, preços e versões de SDK mudam rapidamente - verifique os detalhes atuais em platform.claude.com/docs antes de confiar neles.