A política que controla custo, qualidade e falhas por request

English summary: Production LLM routing is an executable policy for choosing models under quality, cost, latency, privacy, and failure constraints. This guide builds a minimal router, then develops the evals, rollout controls, observability, ownership, and rollback mechanisms required to operate it safely.
Published: July 2026
Reading time: 21 minutes
Keywords: #LLMOps #LLMRouting #ModelRouting #Observability #Evals #AISafety
Às 10h, o time troca o modelo padrão para cortar custo. Às 10h20, a fatura por chamada cai. À tarde, chegam tickets sobre JSON inválido, respostas lentas e documentos internos enviados para uma rota que nunca deveria recebê-los. O dashboard financeiro continua verde.
O problema não é ter escolhido o “modelo errado”. É ter tratado uma decisão de produção como um nome em uma variável de ambiente.
A tese deste artigo é verificável: roteamento de LLM só reduz custo com segurança quando funciona como uma política versionada, testada e reversível — com limites de qualidade, latência e privacidade que continuam válidos durante fallback. Se o time não consegue reconstruir por que uma requisição usou determinada rota, medir o efeito da escolha e voltar à política anterior, ainda não tem um roteador. Tem um if caro.
Vamos construir esse if primeiro. Depois veremos o que falta para ele sobreviver à produção.
O exemplo abaixo é um roteador completo, determinístico e sem dependências. Ele não chama um provedor real: os modelos são adaptadores simulados, o que permite testar a política sem credenciais ou custo.
Salve como router.mjs e rode com Node.js 20 ou superior:
node router.mjs
const policy = {
version: "2026-07-01",
routes: {
economy: {
model: "mock/economy",
costUnits: 1,
latencyClass: "fast",
maxDataClass: "internal",
},
balanced: {
model: "mock/balanced",
costUnits: 2,
latencyClass: "standard",
maxDataClass: "internal",
},
quality: {
model: "mock/quality",
costUnits: 5,
latencyClass: "standard",
maxDataClass: "internal",
},
private: {
model: "mock/private",
costUnits: 4,
latencyClass: "standard",
maxDataClass: "restricted",
},
},
fallback: {
economy: ["balanced"],
balanced: ["economy"],
quality: ["balanced"],
private: [],
},
};
const dataRank = { public: 0, internal: 1, restricted: 2 };
const latencyRank = { fast: 0, standard: 1, batch: 2 };
class ProviderError extends Error {
constructor(status, message = `provider_${status}`) {
super(message);
this.status = status;
}
}
function classify(request) {
if (request.dataClass === "restricted") return "private";
if (request.task === "reasoning" || request.impact === "high") return "quality";
if (request.task === "extract") return "economy";
return "balanced";
}
function eligible(routeName, request) {
const route = policy.routes[routeName];
return (
route.costUnits <= request.budgetUnits &&
dataRank[request.dataClass] <= dataRank[route.maxDataClass] &&
latencyRank[route.latencyClass] <= latencyRank[request.maxLatencyClass]
);
}
function isRetryable(error) {
return error instanceof ProviderError &&
(error.status === 408 || error.status === 429 || error.status >= 500);
}
async function complete(routeName, request) {
// Falha determinística para provar retry + fallback no exemplo.
if (request.id === "demo-fallback" && routeName === "quality") {
throw new ProviderError(429);
}
return `${policy.routes[routeName].model}: ${request.input}`;
}
async function callWithRetry(routeName, request) {
const maxAttempts = 2;
for (let attempt = 1; attempt <= maxAttempts; attempt += 1) {
try {
return { text: await complete(routeName, request), attempts: attempt };
} catch (error) {
if (!isRetryable(error) || attempt === maxAttempts) {
if (error instanceof ProviderError) error.attempts = attempt;
throw error;
}
}
}
}
async function route(request) {
const requestedRoute = classify(request);
const candidates = [requestedRoute, ...policy.fallback[requestedRoute]];
let fallbackReason = null;
let totalAttempts = 0;
for (const [index, routeName] of candidates.entries()) {
if (!eligible(routeName, request)) {
fallbackReason ??= `route_ineligible:${routeName}`;
continue;
}
try {
const result = await callWithRetry(routeName, request);
totalAttempts += result.attempts;
const decision = {
event: "llm.route.decision",
requestId: request.id,
policyVersion: policy.version,
requestedRoute,
effectiveRoute: routeName,
model: policy.routes[routeName].model,
attempts: totalAttempts,
fallback: index > 0,
fallbackReason,
};
console.log(JSON.stringify(decision));
return { ...result, attempts: totalAttempts, route: routeName };
} catch (error) {
if (!isRetryable(error)) throw error;
totalAttempts += error.attempts ?? 1;
fallbackReason = `provider_${error.status}`;
}
}
throw new Error("no_policy_compliant_route");
}
const result = await route({
id: "demo-fallback",
task: "reasoning",
impact: "medium",
dataClass: "internal",
maxLatencyClass: "standard",
budgetUnits: 5,
input: "Plano de migração com trade-offs",
});
console.log(JSON.stringify(result));
Saída esperada:
{"event":"llm.route.decision","requestId":"demo-fallback","policyVersion":"2026-07-01","requestedRoute":"quality","effectiveRoute":"balanced","model":"mock/balanced","attempts":3,"fallback":true,"fallbackReason":"provider_429"}
{"text":"mock/balanced: Plano de migração com trade-offs","attempts":3,"route":"balanced"}
Os costUnits são unidades fictícias de política. Não representam preço, tokens ou benchmark. Em produção, o catálogo deve carregar preços vigentes, moeda, data de validade e fonte oficial do fornecedor.
O roteador tomou cinco decisões, nesta ordem:
quality porque ela exige raciocínio;429 foi classificado como transitório;quality;Altere dataClass para restricted. A rota inicial passa a ser private, que não tem fallback. Isso é intencional: disponibilidade não autoriza enviar dado restrito a outro provedor.
Altere budgetUnits para 1 mantendo a tarefa de raciocínio. Nenhuma rota permitida cabe no orçamento, então o programa termina com no_policy_compliant_route. O sistema falha antes de gastar e sem fingir que entregou a mesma qualidade.
Esse comportamento é a parte importante. A heurística de classify é só uma peça de ensino.
Pense no roteador como a central que despacha atendimentos, não como um ranking de profissionais. Um caso simples pode ir para a fila rápida. Um caso de alto impacto exige outra especialidade. Um documento restrito só pode entrar em uma sala autorizada. Se a equipe ideal está indisponível, a central não escolhe “qualquer pessoa livre”: consulta uma lista de substituições compatíveis.
A analogia ajuda porque separa três coisas confundidas com frequência:
O modelo pode ajudar a classificar intenção. Ele não deve ser a fonte de verdade para identidade, autorização, residência ou retenção de dados. Essas decisões pertencem a controles determinísticos fora do modelo.
O código ensina a fronteira da política, mas não é infraestrutura pronta. A diferença precisa estar explícita:
| No exemplo | Em produção |
|---|---|
| regras por campos já classificados | classificação de dados antes do roteador, com identidade e consentimento confiáveis |
| unidades de custo fixas | estimativa por entrada e teto de saída, catálogo vigente e reconciliação de uso real |
| duas tentativas sem espera | prazo total, Retry-After, backoff com jitter e orçamento de tentativas |
| falha simulada | adaptadores que preservam status, request_id, timeout e uso do provedor |
| fallback por nome de rota | compatibilidade por esquema, ferramentas, região, privacidade e qualidade mínima |
| log no console | traces, métricas, logs redigidos, retenção e correlação ponta a ponta |
| estado em um processo | circuit breaker com escopo conhecido e sonda half-open limitada |
| mudança direta | shadow, canary, promoção automática e rollback testado |
O salto não é adicionar um framework. É transformar cada suposição do brinquedo em contrato, evidência e dono.
Productos gratuitos y de pago para convertir ideas en una base que puedas lanzar.
13 productos disponiblesContinúa explorando temas similares

--- title: "Introdução ao LLMOps: O Ciclo de Vida de Aplicações de LLM em Produção" subtitle: "Como orquestrar modelos de fundação com gateway, roteamento, cache, guardrails e eval

English summary: LLM guardrails are not a bag of content filters. They are a governable runtime that separates model proposals from authorized effects through layered policy enforc

Observabilidade generativa não é log com texto bonito. É prompt completo, versão do modelo, tools usadas, latência, custo e veredito do eval no mesmo span. Fechamento da série LLMOps em Produção.
Un checklist práctico para validar seguridad, costos, evaluaciones y observabilidad antes de llevar tu app con IA a producción.
Templates probados en producción, usados por desarrolladores. Ahorra semanas de setup en tu próximo proyecto.
Comece pelo trabalho, não pelo catálogo de modelos. A mesma rota raramente otimiza qualidade, custo, latência e privacidade ao mesmo tempo.
| Fluxo | Impacto do erro | Custo alvo | Latência | Privacidade | Rota inicial | Fallback seguro | Evidência de sucesso |
|---|---|---|---|---|---|---|---|
| extração de campos não críticos | médio | baixo por item | interativa | dados públicos | economy | balanced, somente se o schema for preservado | validade do schema e acurácia por campo |
| rascunho de resposta de suporte | baixo | moderado | interativa | dados internos | balanced | economy ou fila, conforme confiança | taxa de edição e resolução |
| análise regulada | alto | subordinado à conformidade | moderada | dados restritos | private | revisão humana; nunca rota pública | rubrica de especialistas e zero violação de política |
| sumarização em lote | baixo | baixo por item | assíncrona | dados internos | economy | pausar e reprocessar | sucesso do job, custo por item e cobertura factual |
| ação com ferramenta | alto | subordinado ao risco | varia | depende da ação | política específica do fluxo | pedir confirmação ou falhar fechado | autorização, resultado da ação e trilha de auditoria |
| resposta determinística conhecida | baixo | zero gasto com modelo | baixa | qualquer classe | sem LLM | cache ou código | igualdade com a fonte de verdade |
“Sem LLM” é uma rota legítima. Se uma consulta ao banco, uma busca exata ou um template resolve o problema, chamar um modelo adiciona custo e incerteza sem criar valor.
Camadas como economy, balanced, quality e private devem ser papéis internos, não apelidos eternos para produtos comerciais. O catálogo liga esses papéis a modelos concretos por período de vigência. Assim, uma troca de fornecedor não exige reescrever a regra de negócio — e um nome de marketing novo não muda a política sem avaliação.
Fallback não significa “tente um modelo pior”. Significa executar uma alternativa previamente autorizada que ainda cumpre o contrato mínimo.
Cada aresta da cascata precisa declarar:
Uma regra útil é avaliar o fallback com o mesmo rigor da rota principal. Se quality → balanced nunca foi testado em casos difíceis, a seta no diagrama é esperança, não resiliência.
Retry, circuit breaker e fallback resolvem problemas diferentes. Retry pergunta se a mesma operação pode funcionar numa nova tentativa. O breaker impede que a aplicação continue pressionando uma dependência doente. Fallback escolhe outra execução compatível. A documentação do Azure Architecture Center também distingue retry de circuit breaker; o artigo da AWS sobre exponential backoff and jitter explica por que clientes sincronizados pioram uma recuperação.
Defina também um prazo total. Três tentativas de dez segundos mais um fallback de dez segundos não são uma rota interativa de baixa latência; são quarenta segundos escondidos atrás de uma configuração de retry.
Um roteador costuma falhar de maneira silenciosa. Estas quatro classes cobrem boa parte do risco operacional.
O classificador envia tarefas difíceis para uma rota econômica. A chamada retorna 200, a latência melhora e nenhum alerta técnico dispara. O usuário recebe uma resposta pior.
Como detectar: matriz de confusão por intenção em eval offline, confiança calibrada, taxa de aceitação por rota, edição ou regeneração por coorte e amostra humana dos casos de baixa confiança.
Resposta: caminho conservador para incerteza, regras determinísticas para casos de alto impacto e rollback da versão de política — não apenas troca do modelo.
A rota principal falha e a cascata escolhe um modelo sem o esquema, a ferramenta, a região ou a autorização exigida. A tentativa de “manter o serviço no ar” quebra o contrato.
Como detectar: contador de fallback_blocked_reason, testes de contrato por aresta, validação da saída antes do consumo e alerta para qualquer dado classificado que tente cruzar uma rota proibida.
Resposta: elegibilidade reavaliada em cada tentativa, fallback vazio para fluxos que devem falhar fechados e alternativa explícita para o usuário.
Um provedor degrada, cada réplica repete chamadas e todas voltam juntas após o mesmo intervalo. Custo, fila e latência crescem enquanto a dependência tenta se recuperar.
Como detectar: tentativas por requisição, razão entre chamadas ao provedor e requisições de usuário, tempo total gasto em retry, 429/5xx, estado do breaker e saturação por rota.
Resposta: prazo total, jitter, respeito a Retry-After, limite de tentativas, circuit breaker e uma única sonda half-open por domínio de falha.
O custo por chamada cai, mas usuários corrigem mais respostas, abandonam o fluxo ou acionam suporte. O roteador otimizou a métrica mais fácil de medir.
Como detectar: custo por tarefa concluída, não só por chamada; taxa de resolução, edição, reabertura e conversão; comparação simultânea entre controle e canary; análise segmentada por intenção.
Resposta: qualidade e resultado do usuário como guardrails de promoção. Uma política mais barata que viola o mínimo não é otimização; é transferência de custo para o cliente e para a operação.
Um teto por requisição evita apenas uma parte do problema. Antes da chamada, estime o pior caso permitido. Depois, reconcilie com o uso efetivo retornado pelo provedor.
estimated_max = input_units * input_rate + max_output_units * output_rate
authorize only when:
estimated_max <= request_reservation
AND estimated_max <= tenant_budget_remaining
AND route satisfies data, latency, and quality policy
after completion:
atomically replace reservation with actual usage
O catálogo de preço deve registrar moeda, unidade, versão, data de vigência e URL oficial. Preços e limites mudam; este artigo, verificado em 12 de agosto de 2026, deliberadamente não fixa números de fornecedores.
Reserve orçamento em níveis diferentes: requisição, processo, usuário ou organização e janela temporal. Sem isso, mil chamadas individualmente válidas podem romper o limite diário. Para streaming, faça reserva antecipada e defina o comportamento quando o teto se aproxima: interromper, resumir, pedir confirmação ou enfileirar. “Descobrir no fechamento da fatura” não é um controle.
Meça custo por resultado útil. Custo por token pode cair enquanto o custo por ticket resolvido sobe.
Não avalie “qual é o melhor modelo”. Avalie a política inteira: classificação, elegibilidade, execução, fallback e resultado.
O conjunto offline deve representar cada intenção, idioma, classe de dados e consequência de erro. Inclua entradas ambíguas, limites de contexto, saídas malformadas, timeouts, 429, 5xx, circuito aberto e tentativas de injeção de prompt.
| Eval | O que mede | O que bloqueia |
|---|---|---|
| classificação | classe correta e calibração de confiança | promoção indevida para rota fraca |
| qualidade | esquema, fatos e rubrica do domínio | economia abaixo do mínimo |
| resiliência | timeout, retry, breaker e fallback | loops e degradação quebrada |
| orçamento | estimativa, reserva e reconciliação | gasto fora do teto |
| segurança | autorização, dados sensíveis e ferramentas | vazamento ou ação indevida |
A eval precisa guardar entrada, resultado esperado, critério, severidade e rota máxima permitida. Casos de produção só entram no conjunto de dados depois de mascaramento e autorização compatíveis com a política de retenção.
Evals offline não capturam toda a distribuição real nem o comportamento posterior do usuário. Em produção, acompanhe por intenção e versão de política:
Nem todo sinal é imediato. Uma resposta de suporte pode parecer aceita e ser reaberta dias depois. Preserve a ligação entre decisão e outcome sem transformar prompts brutos em telemetria padrão.
A documentação oficial da OpenAI descrevia, na verificação de 12 de agosto de 2026, evals como testes contra critérios definidos pela aplicação e alertava para uma transição da plataforma de Evals. Isso reforça um princípio: seus casos, rubricas e histórico de decisão devem pertencer ao seu sistema, não à interface de um fornecedor. Consulte a documentação atual de evals antes de escolher a integração.
Uma política nova não deveria começar atendendo usuários.
No shadow, a política candidata recebe uma cópia autorizada da entrada, mas sua resposta não é servida. Compare escolha de rota, elegibilidade, qualidade, latência e custo estimado com a política atual. O shadow revela divergências sem alterar a experiência, mas não é “grátis”: duplica processamento e pode ampliar exposição de dados. Amostragem, mascaramento, retenção e permissão continuam valendo.
No canary, uma fração controlada do tráfego recebe de fato a nova política. Defina antes:
Compare canary e controle no mesmo período. Uma comparação “semana passada versus hoje” mistura a mudança do roteador com sazonalidade, campanha, incidentes e mudança de tráfego. O capítulo do Google SRE sobre canarying releases recomenda métricas representativas e atribuíveis e descreve estágios graduais para limitar impacto enquanto a confiança aumenta.
Só promova quando os resultados online confirmarem os gates definidos antes do experimento. Mudar o critério depois de ver os dados transforma avaliação em justificativa.
O trace deve permitir reconstruir a rota sem registrar conteúdo sensível por padrão. Ligue o span do roteador ao span do provedor e capture, quando aplicável:
route.id, policy.version e catalog.version;request_id, breaker e motivo do fallback;Prompts, respostas, documentos de RAG, chaves e PII não devem virar atributos comuns. Se conteúdo for indispensável para auditoria, use armazenamento separado com minimização, mascaramento, controle de acesso, justificativa e retenção definida.
As convenções de GenAI do OpenTelemetry estavam em migração para um repositório dedicado em 12 de agosto de 2026. Fixe a versão adotada e trate mudanças de nomes e estabilidade como migração de esquema, não como detalhe de painel.
Um painel útil responde perguntas operacionais:
Mandar uma tarefa sensível para um modelo “mais forte” não resolve autorização. Segurança atravessa todas as rotas.
A política precisa controlar, antes da chamada:
Multi-provedor aumenta opções de recuperação, mas também amplia chaves, contratos, subprocessadores, superfícies de exfiltração e trabalho de incidente. Uma rota alternativa só existe se foi aprovada para aquela classe de dados.
Transforme ameaças em casos de teste. A edição 2026 do OWASP GenAI LLM Top 10, publicada em 3 de agosto de 2026, é uma fonte atual para revisar riscos; adapte a taxonomia ao seu fluxo e ao poder real das ferramentas. Para decisões de governança e risco, o NIST AI RMF Generative AI Profile oferece uma referência primária complementar.
Política sem dono envelhece em silêncio. Distribua responsabilidades concretas:
| Artefato ou decisão | Dono | Evidência exigida | Reversão |
|---|---|---|---|
| taxonomia de tarefas e rubricas | produto + especialista do domínio | conjunto de dados e critérios revisados | voltar à taxonomia anterior |
| política e catálogo de rotas | plataforma de IA | eval offline e diff de configuração | reativar versão conhecida |
| classificação e residência de dados | segurança + privacidade | threat model e aprovação | bloquear rota ou provedor |
| orçamento e reconciliação | engenharia + FinOps | fatura reconciliada e alertas | reduzir teto ou pausar fluxo |
| SLOs e resposta a incidente | equipe operadora do serviço | painel, alertas e procedimento operacional | chave de desligamento e rota estável |
| promoção de canary | dono do produto + operação | gates online cumpridos | rollback automático ou manual |
Separe política, catálogo e adaptadores. A política diz que trabalho pode usar qual papel. O catálogo mapeia o papel para um modelo vigente. O adaptador fala com o fornecedor. Essa separação permite trocar um modelo, reverter uma regra ou isolar um provedor sem lançar tudo ao mesmo tempo.
Toda decisão deve carregar policy.version. Mantenha a versão anterior implantável, uma chave de desligamento fora do caminho do LLM e um procedimento operacional ensaiado. Rollback que depende de editar código sob incidente não é reversibilidade; é esperança com acesso ao repositório.
Use o artefato do início como laboratório.
Marque a requisição como restricted, faça mock/private retornar 503 e rode novamente.
Sucesso: nenhuma rota pública é chamada, o erro final é no_policy_compliant_route e o evento de bloqueio registra o motivo sem incluir a entrada.
Implemente um orçamento total de tempo compartilhado entre retries e fallback. Não reinicie o relógio ao mudar de rota.
Sucesso: um teste com adaptadores lentos termina dentro da tolerância definida e registra deadline_exhausted; o número de chamadas não supera o limite.
Monte ao menos um caso para cada célula da matriz de decisão e injete 429, saída inválida e orçamento insuficiente.
Sucesso: o teste falha quando uma rota viola dados, esquema, custo ou fallback, mesmo que o adaptador retorne 200.
Execute duas versões da política sobre o mesmo conjunto autorizado, sem servir a candidata. Depois promova a candidata para uma coorte local e force um guardrail a falhar.
Sucesso: você consegue explicar cada divergência por policy.version e restaurar a versão anterior sem alterar o código do adaptador.
Você entendeu o sistema quando consegue prever a rota, provar por que os fallbacks restantes são proibidos e reverter uma mudança usando evidência — não intuição.
Retry-After, jitter e prazo total.half-open definidos.O objetivo não é encontrar um modelo barato que “quase sempre funciona”. É construir uma política que saiba quando economizar, quando escalar, quando esperar e quando se recusar a continuar. O roteador maduro não esconde incerteza atrás de disponibilidade. Ele transforma cada escolha em uma decisão limitada, observável e reversível.
Links voláteis foram verificados em 12 de agosto de 2026.