Trocar de provedor por preço é o argumento de venda de todo LLM gateway. É também o motivo mais fraco pra instalar um. API gateway roteia por rota. LLM gateway roteia por modelo, e modelo não é peça intercambiável: trocar o destino troca a resposta junto.
O ganho real não está na portabilidade. Está em ter um único lugar onde teto de gasto, fallback e atribuição viram política versionada, fora do código da aplicação. O artefato pra levar pra equipe é essa política de roteamento, com a coluna que nenhum tutorial traz: o tráfego que nunca faz fallback.
O que a analogia entrega de graça
O API gateway resolveu um problema conhecido. Um endpoint na frente de várias upstreams. Auth centralizada, com a credencial do backend fora do código do cliente. Rate limit, retry, log e métrica num ponto só.
O LLM gateway herda tudo isso intacto. A chave da OpenAI sai do .env de cada serviço e vira uma chave virtual por time. O rate limit deixa de ser uma constante espalhada em três repositórios. O log de request vira base de custo, que é o assunto do artigo de atribuição. Até aqui, nada novo sob o sol.
O interessante começa onde a analogia quebra. São cinco pontos, e é neles que mora a decisão de projeto.
Ruptura 1: upstream não é peça intercambiável
No API gateway, duas instâncias atrás do mesmo pool respondem a mesma coisa. Se não respondem, é bug. Em LLM gateway, a diferença entre dois destinos é o produto.
Dois modelos diferentes devolvem respostas diferentes pro mesmo prompt. Isso é óbvio. O que pega gente desprevenida é a variação dentro do mesmo nome de modelo. A doc de provider routing do OpenRouter expõe o campo quantizations. O aviso que acompanha o campo é literal, em tradução nossa. Modelos quantizados podem apresentar performance degradada pra certos prompts, dependendo do método usado. Nem todo par de provedores difere na precisão numérica. O ponto é que pode diferir, e o mesmo slug de modelo não te avisa.
O mesmo doc traz require_parameters, um booleano que restringe o roteamento a provedores que suportam todos os parâmetros do seu request. Ele existe porque o default não faz isso. Sem ele, um provedor que ignora o seu parâmetro ainda é candidato válido.
Ruptura 2: falha de LLM não vem só como 5xx
Circuit breaker de HTTP tem vida fácil. Status code diz se deu errado. Em LLM, o erro se distribui em famílias que pedem tratamento diferente, e a doc de fallbacks do LiteLLM reconhece isso na própria API. Existem três listas separadas.
| Chave de config | Dispara quando |
|---|---|
fallbacks |
erro genérico, cobre 429, 500 e afins |
context_window_fallbacks |
o prompt estourou a janela do modelo |
content_policy_fallbacks |
o provedor barrou por política de conteúdo |
A distinção não é decorativa. Prompt que estourou janela precisa de modelo com janela maior, não de outro provedor. Bloqueio por política de conteúdo pode significar que o request está certo e o filtro é que é rígido demais. Ou o oposto, e aí o fallback vira contorno de guardrail.
E fica de fora dessa lista a falha mais cara de todas. O modelo responde 200, em JSON válido, com conteúdo errado. Nenhum gatilho de fallback pega isso. Health check de gateway confirma que o provedor está de pé, não que ele está certo.
Ruptura 3: fallback custa dinheiro, não só milissegundo
Retry em API gateway custa CPU e latência. Aqui custa fatura. A doc de fallbacks do Portkey avisa em três linhas secas, na seção de considerações. Um único request pode invocar múltiplos LLMs. Cada LLM tem latência e preço diferentes.
Some retry com fallback e a conta cresce rápido. No LiteLLM, num_retries roda no modelo primário antes de a cadeia de fallback começar. Três destinos na cadeia, três tentativas em cada, e um único request chega a nove chamadas ao provedor.
Nem toda tentativa vira fatura, e vale separar os casos. Request barrado por 429 não gera token nenhum. O que cobra é a tentativa que completa a geração e é descartada, por timeout do cliente ou por resposta rejeitada depois. E cobra o degrau final da cadeia, quando o destino do fallback é um modelo mais caro que o primário. O pior dia do provedor primário vira o seu pior dia de fatura por esse caminho.
O antídoto é o teto que já discutimos no artigo do token budget. Cadeia de fallback sem teto de gasto é um amplificador de incidente.
Ruptura 4: normalizar é achatar
Todo gateway promete API compatível com OpenAI. A tradução funciona pro caminho comum e vaza nas bordas. O LiteLLM documenta o dilema numa flag só, o drop_params.
O comportamento padrão está na doc. Por padrão o LiteLLM levanta exceção se você manda um parâmetro que o modelo não suporta. Com drop_params=True, ele descarta o parâmetro em vez de levantar erro. A doc explica a razão. Assim o seu código roda em vários provedores sem customização por provedor.
Repare no que a flag decide. Ligada, o request passa e o parâmetro some. Você pediu temperature=0 e recebeu uma resposta que não honra isso, sem nenhum aviso. Desligada, você tem erro explícito e alguém precisa tratar a diferença.
Existe meio-termo, e ele mora no config. O additional_drop_params aceita uma lista de parâmetros pra descartar, por deployment, inclusive campos aninhados. A decisão sai do código da aplicação e vira linha declarada no gateway. O que não muda é o silêncio. Parâmetro descartado não vira aviso na resposta, então a lista de descarte precisa de review como qualquer regra de negócio.
Ruptura 5: balancear por preço briga com o cache do prompt
Prompt caching é desconto por prefixo repetido no mesmo provedor. O cache não atravessa provedor. Um roteador que escolhe o mais barato a cada request espalha o tráfego, e prefixo espalhado não acumula releitura.
O default do OpenRouter é esse balanceamento. A doc descreve a mecânica. Provedores estáveis nos últimos 30 segundos entram no sorteio, com peso pelo inverso do quadrado do preço. A tensão é tão real que virou feature dedicada. Quando o request usa caching, o OpenRouter aplica provider sticky routing. Ele lembra qual provedor atendeu e manda os requests seguintes do mesmo modelo pro mesmo endpoint, pra manter o cache quente. A doc registra o gatilho. O sticky só ativa quando a leitura de cache do provedor sai mais barata que o preço normal de prompt.
Guarde a lição, não o nome da feature. Roteamento por preço e cache de prefixo puxam pra lados opostos. LLM gateway hospedado já resolve isso por você. LLM gateway próprio não resolve sozinho, e a mecânica de afinidade passa a ser código seu.
O custo do hop
Um proxy a mais no caminho preocupa. O projeto LiteLLM publica os próprios benchmarks e declara 8ms de latência P95 a 1k RPS. Duas ressalvas vêm da mesma página. O teste roda contra um endpoint OpenAI falso, então o número mede overhead de proxy, não chamada real. E o claim aponta pro cenário de quatro instâncias, não pra uma caixa solitária.
Compare com a ordem de grandeza do outro lado. Time-to-first-token de modelo grande vive na casa das centenas de milissegundos. Oito milissegundos de overhead não é o seu problema de latência. Número de vendor merece ceticismo, e a régua certa é medir no seu tráfego. Ainda assim, a conclusão de projeto se sustenta. O gateway se paga em controle muito antes de aparecer no P95 do usuário.
O artefato: a política de roteamento
Config de LLM gateway espalhada em YAML não é política. Política é a tabela que o time lê e discute antes de virar YAML. Esta é uma proposta nossa, e cada campo carrega obrigatoriedade explícita.
| Campo | Obrigatório | Definição operacional |
|---|---|---|
| classe de tráfego | sim | fatia do tráfego que a linha governa, nomeada por feature |
| modelo primário | sim | quem atende no caminho feliz |
| gatilho de fallback | sim | lista fechada de erros que autorizam a troca |
| destino do fallback | sim | quem atende quando o gatilho dispara |
| trava de fallback | sim | condição em que nenhuma troca é permitida |
| teto de gasto | sim | limite da classe na janela declarada |
| tag de atribuição | sim | rótulo que liga o gasto a um dono |
| tentativas no primário | não | quantas vezes insistir antes de descer a cadeia |
A trava é o campo que muda a conversa. Fallback é bom pra tráfego onde qualquer resposta razoável serve. É péssimo pra tráfego onde a forma da resposta é contrato. Exemplo ilustrativo, com quatro classes de um produto de atendimento:
| Classe | Primário | Gatilho | Destino | Trava | Teto | Tag |
|---|---|---|---|---|---|---|
| chat de atendimento | Claude Sonnet | 429, 5xx, timeout 20s | GPT flagship | nunca por content policy | $200/24h | feature:chat |
| extração de JSON | GPT flagship | 429, 5xx | mesmo modelo, outra região | nunca muda de família | $50/24h | feature:extracao |
| classificação em lote | Gemini Flash | 429, 5xx, janela estourada | modelo de janela maior | nenhuma | $30/24h | feature:batch |
| ação que move dinheiro | Claude Sonnet | nenhum | nenhum | sempre, falha alto | $20/24h | feature:pagamento |
Leia a última linha de novo. Ação que cobra alguém não faz fallback. Um segundo modelo tentando a mesma chamada de ferramenta é retry disfarçado, e retry de cobrança sem chave de idempotência é cobrança dobrada. A idempotência de ação de agente ganha artigo próprio mais à frente na série.
A linha de extração também merece atenção. O fallback existe, mas amarrado à mesma família de modelo. Trocar de família num caminho que depende de schema válido troca um erro de disponibilidade por um erro de formato, que é mais difícil de detectar.
A política em config
A tabela vira YAML sem mistério. Duas classes, em config de LiteLLM, com comentário no que importa:
model_list:
# classe: extracao. Fallback so entre regioes da mesma familia.
- model_name: extracao
litellm_params:
model: openai/gpt-5.6
api_key: os.environ/OPENAI_API_KEY
- model_name: extracao-backup
litellm_params:
model: azure/nome-do-deployment
api_base: os.environ/AZURE_API_BASE
api_key: os.environ/AZURE_API_KEY
# classe: pagamento. Sem backup declarado, por decisao de projeto.
- model_name: pagamento
litellm_params:
model: anthropic/claude-sonnet-5
api_key: os.environ/ANTHROPIC_API_KEY
router_settings:
num_retries: 2
fallbacks: [{"extracao": ["extracao-backup"]}]
allowed_fails: 3
cooldown_time: 30
litellm_settings:
request_timeout: 20
Duas notas de leitura. A entrada Azure precisa de api_base, e o nome depois de azure/ é o deployment criado na sua instância, não o slug público do modelo. E allowed_fails com cooldown_time estão escritos de propósito. O default do router é 3 falhas por minuto e cooldown de 5 segundos. Cooldown curto devolve tráfego rápido demais a um provedor que ainda está mal. Escrever o número no config é mais barato que descobrir o default num incidente.
Falta a parte incômoda. O grupo pagamento não aparece em nenhuma lista de fallback, e isso sozinho não é trava. O num_retries: 2 é do router e alcança todo grupo, então a chamada ainda repete duas vezes. O LiteLLM também aceita fallbacks no corpo do request, o que devolve a decisão pro cliente. Trava de verdade se fecha em três pontos. Uma chave virtual dedicada pra classe, num_retries zerado nessa rota e rejeição de fallback vindo do cliente. Config sozinha declara intenção. A garantia exige fechar as três portas.
O teto de gasto mora em outro lugar, na chave virtual. O LiteLLM aceita janelas múltiplas na mesma chave, por exemplo $10 por 24h e $100 por 30 dias, cada janela com reset próprio. É o mecanismo direto pro campo de teto da nossa tabela.
Fechar o loop: da política ao relatório
Config sem leitura de volta é fé. O loop fecha em quatro passos.
- Request entra no LLM gateway com a tag da classe.
- Política escolhe o destino, aplica retry, cai pro fallback se o gatilho disparar.
- Resposta volta com o header
x-litellm-model-id, que identifica o deployment que de fato atendeu. O valor é o ID do deployment, então declaremodel_info.idse quiser um nome legível no log. - Log de spend guarda custo, chave, time e a metadata que você anexou ao request.
Com esses quatro passos gravados, uma pergunta nova fica disponível. Qual a taxa de fallback por classe de tráfego? É a métrica que quase ninguém coleta, e ela destrava duas decisões concretas. Classe com fallback acima de alguns por cento tem primário errado, ou teto de retry frouxo. Classe com fallback em zero há meses tem um destino de backup que nunca foi exercitado, e backup não testado não é backup.
O degrau pago
Gateway open source tem fronteira comercial, e ela merece leitura antes do desenho. No LiteLLM, a doc marca “Enterprise” no roteamento por tag de time e na geração de relatórios de spend. Outras peças que a demo mostra podem ter marcação parecida em outras páginas. A recomendação prática é conferir o rótulo de cada recurso do seu desenho na doc, um por um, antes de escrever o config.
O impacto disso é de arquitetura, não de licença. Suponha que a sua política seja por time, e que o roteamento por tag de time esteja atrás do plano pago. O caminho alternativo é uma chave virtual por classe de tráfego. Fica igualmente funcional, com mais chaves pra administrar.
Do lado hospedado, a conta muda de forma. A FAQ do OpenRouter declara repasse do preço do provedor, com a mesma tabela que você pagaria direto, mais o pooling de uptime. A cobrança aparece na compra de crédito, e a FAQ registra que taxas de plataforma não são reembolsáveis. Na Cloudflare o desenho é outro. A página de preços diz que os recursos centrais de hoje são gratuitos, e lista analytics, caching e rate limiting entre eles. A cobrança aparece no Unified Billing, com taxa de 5% sobre o crédito comprado e inferência repassada sem markup. O dynamic routing entrega os nós de condicional, percentual, rate limit e budget, com versões e rollback. Modelos de cobrança diferentes, e nenhum deles cobra por onde você espera.
Fechando
O LLM gateway não é infra neutra na frente do provedor. Ele é onde o produto decide quem atende cada request, e trocar quem atende troca a resposta. Quem instala um só pra perseguir preço leva a portabilidade e deixa o resto na mesa. O valor está na política. Comece pela tabela de campos obrigatórios, preencha a trava antes do gatilho e meça a taxa de fallback por classe. Quando o provedor primário tiver o dia ruim dele, a diferença entre um incidente e um susto vai estar escrita nesse YAML.
Fontes
- LiteLLM: Fallbacks (Provider Failover)
- LiteLLM: Router, Load Balancing
- LiteLLM: Drop Unsupported Params
- LiteLLM: Benchmarks
- LiteLLM: Tag Based Routing
- LiteLLM: Budgets, Rate Limits
- LiteLLM: Spend Tracking
- OpenRouter: Provider Routing
- OpenRouter: Prompt Caching
- OpenRouter: FAQ
- Portkey: Fallbacks
- Cloudflare: Dynamic routing
- Cloudflare: AI Gateway pricing





Deixe um comentário