Lição 27 · Curso de Fusão · Parte 5 · Engenharia · Tiers, custo e orçamento
Parte 5 · Engenharia · Lição 27

Tiers, custo e orçamento

Um motor que envia uma requisição a muitos modelos precisa de dois botões independentes: autonomia (quanta supervisão humana um trabalho exige) e custo (quanto dinheiro uma chamada pode gastar). O Alembic codifica o primeiro como a escada de tiers T0→T4, e o segundo como um registry de modelos precificado por 1k tokens, mais um BudgetGuard que falha fechado (fail-closed). Esta lição liga os dois: como um tier roteia para o modelo mais barato que qualifica, como toda chamada paga é medida, e por que um teto não-positivo significa "só o free tier" — nunca "ilimitado".

Cada bloco tem uma versão Simples e uma Técnica. Abra a técnica quando quiser o código real.
O que presumimos de você
  • Que você já viu a cintura estreita (lição 14) — é por ela, o run(), que toda chamada de modelo passa. É ali que o orçamento entra.
  • Que "tier" pode soar técnico. Não é: pense num nível de confiança — quanto de revisão humana aquele trabalho precisa antes de ser aceito.
  • Nada além disso. Não é preciso entender de finanças para enxergar por que "sem teto definido" deve significar "gaste zero", e não "gaste à vontade".
1

A grande ideia: dois mostradores ortogonais


Ao terminar esta lição você consegue
  • Separar os dois eixos do motor: autonomia (tier) e custo (preço + guard).
  • Recitar a escada T0→T4 e dizer por que DEFAULT_TIER = T4 é fail-closed.
  • Explicar por que LOCAL é um marcador ortogonal, não um sexto tier.
  • Descrever como pickCheapestForTier escolhe um modelo e por que a escolha é determinística.
  • Explicar a jogada fail-closed cap = Math.max(0, capUsd) e por que um free call sempre passa.

A maioria dos sistemas mistura "quão importante é isto" com "quanto posso gastar nisto" num único botão. O Alembic os mantém separados: um trabalho pode ser de baixa autonomia mas caro, ou de alta autonomia mas grátis. São dois mostradores num painel — e mexer num não mexe no outro.

Pense num hospital. Triagem decide quanta supervisão um caso precisa — uma enfermeira, um médico, uma junta. Orçamento decide quanto se pode gastar com exames. São perguntas diferentes: um caso urgentíssimo pode ser barato de tratar; um caso simples pode exigir um exame caro. Confundir os dois é gerir mal o hospital — e o motor.
Infográfico com dois grandes mostradores circulares lado a lado, como dois medidores de um painel. À esquerda, aro terracota, 'AUTONOMIA — quanta supervisão humana', com cinco marcas T0 silencioso, T1 log leve, T2 um revisor, T3 council, T4 PARK (humano), e uma etiqueta 'default = T4 (fail-closed)'. À direita, aro oliva, 'CUSTO — quanto dinheiro a chamada pode gastar', escala de US$ 0 (free tier) a US$ 0,18, ponteiro perto do zero com etiqueta 'default seguro = só o free tier'. Selo central: 'ortogonais — um botão NÃO mexe no outro'.

Dois mostradores ortogonais: Tier decide quem revisa; o BudgetGuard decide se a chamada paga acontece.

Por que separar? Porque as duas perguntas têm respostas diferentes para o mesmo trabalho. "Isto pode rodar sozinho?" é sobre risco e supervisão. "Esta chamada pode custar US$ 0,04?" é sobre dinheiro. Amarrá-las num botão só forçaria você a pagar caro só porque algo é autônomo — ou a parar algo barato só porque ele precisa de revisão. Dois eixos, dois mecanismos.
Os dois eixos são independentes — qualquer canto é possível autonomia (tier) → custo (US$) → alta autonomia · barato T3 num modelo local $0 alta autonomia · caro T3 no par frontier (gpt-5.5 / opus) baixa autonomia · barato T1 log leve num modelo barato baixa autonomia · caro T1 que decidiu chamar um modelo caro
Se os eixos fossem o mesmo botão, só a diagonal existiria. Como são ortogonais, os quatro cantos são trabalhos legítimos.
2

A escada de autonomia: T0 → T4


O Tier é uma escada de exatamente cinco degraus, do mais autônomo (T0) ao parado (T4). Ela é sobre supervisão humana, não sobre dinheiro. Quanto mais alto o degrau, mais olhos humanos o trabalho exige antes de ser aceito.

É como a alçada de aprovação numa empresa: uma despesa pequena o funcionário aprova sozinho; uma maior precisa do gerente; a maior de todas vai ao conselho. O degrau não diz "quanto custa" — diz "quem precisa dar o aval".
TierSignificadoAutônomo?
T0silencioso / totalmente autônomo, sem humano no loopsim (o caminho silencioso)
T1autônomo com logging levesim
T2autônomo, um único revisor é notificadosim
T3autônomo, revisão por council exigidasim
T4PARK — retido da execução autônoma; precisa de council + humanonão
Preveja antes de continuar

Se um trabalho chega ao motor sem ser classificado em nenhum tier, em qual degrau você acha que ele cai por padrão?

T4 — parado. DEFAULT_TIER = T4. O padrão é a postura mais cautelosa possível: trabalho não classificado não roda sozinho, espera por council + humano. É o mesmo espírito fail-closed da lição 26.

dois fatos tornam isto fail-closed

Primeiro, DEFAULT_TIER = Tier.T4 — trabalho não classificado para (lição 26). Segundo, isAutonomous só retorna true para T1–T3; T0 é autônomo por definição (o caminho silencioso) e T4 para. O enum é exatamente cinco entradas, e há uma escada ordenada (TIER_LADDER) que codifica a profundidade de escalonamento.

packages/contracts/src/tier.ts
export const Tier = { T0:'T0', T1:'T1', T2:'T2', T3:'T3', T4:'T4' } as const;

/** O tier padrão para trabalho não classificado: parado, não auto-executado. */
export const DEFAULT_TIER: Tier = Tier.T4;

// T1–T3 são autônomos (true); T0 é o caminho silencioso; T4 para (false).
export const isAutonomous = (tier: Tier): boolean =>
  tier === Tier.T1 || tier === Tier.T2 || tier === Tier.T3;
A escada de autonomia — do silencioso (T0) ao parado (T4) T0 silencioso T1 log leve T2 um revisor T3 council T4 · PARK council + humano isAutonomous = true (T1–T3) · T0 é o caminho silencioso DEFAULT_TIER = T4
Sobe à esquerda → direita: cada degrau adiciona supervisão. O não classificado começa lá no topo, parado.
isAutonomous(tier) — só T1, T2 e T3 retornam true T0 false · silencioso T1 true ✓ T2 true ✓ T3 true ✓ T4 false · PARK as duas pontas (T0 reservado, T4 parado) ficam de fora da porta autônoma principal
A função é literal: tier === T1 || tier === T2 || tier === T3. Sem inversões, sem casos implícitos.
Guarde isto Tier = supervisão, não dinheiro. Um tier alto significa "mais olhos humanos", não "mais caro". O custo é o outro mostrador.
3

LOCAL não é um sexto tier — é um marcador ortogonal


Há um quinto rótulo no código, LOCAL — mas ele não é um degrau da escada. É um marcador separado que prende o trabalho a modelos locais/$0 "independentemente do tier": caminhos sensíveis a privacidade ou a custo. Uma unidade pode ser T2 E LOCAL ao mesmo tempo — autônoma com um revisor, mas mantida em modelos locais.

Pense numa etiqueta "confidencial — não sai do prédio" colada numa pasta. Ela não muda a alçada de aprovação (o tier) — só diz "isto fica na máquina local". As duas coisas convivem na mesma pasta.

ortogonal por construção

LOCAL é declarado como uma constante separada do enum Tier — "marker for work pinned to local / $0 execution, orthogonal to Tier" (packages/contracts/src/tier.ts). O tierSchema aceita apenas T0..T4; LOCAL nunca aparece nele. Por isso ele compõe com qualquer tier em vez de competir com eles.

/** Marcador para trabalho preso a execução local / $0, ortogonal a Tier. */
export const LOCAL = 'LOCAL' as const;
export type LocalMarker = typeof LOCAL;

// o enum de tiers NÃO inclui LOCAL — são eixos diferentes:
export const tierSchema = z.enum([T0, T1, T2, T3, T4]);   // sem LOCAL
Por que isto importa. Tratar LOCAL como "o tier mais barato" seria um erro de modelagem: você perderia a capacidade de dizer "este trabalho precisa de um revisor (T2) e tem que ficar local (privacidade)". Como marcador ortogonal, as duas restrições coexistem sem conflito.
Dois planos · uma unidade ocupa um ponto em cada eixo Tier: T0 T1 T2 T3 T4 eixo LOCAL: (sem marcador) LOCAL ($0) unidade = T2 · LOCAL autônoma c/ revisor, presa a modelo $0 cinco valores num eixo, dois noutro · combiná-los é legítimo; transformar LOCAL em “T5” misturaria os planos
O tierSchema é um z.enum de T0–T4 — 'LOCAL' nunca passa nele. O marcador viaja por fora, num campo próprio.
4

O registry de modelos: preço por 1k tokens


Cada modelo roteável é uma entrada no registry com um adapterId (qual adapter o serve), um tier e dois preços: costPer1kInputUsd e costPer1kOutputUsd. São 11 entradas espalhadas pelos tiers. Os modelos local-* custam zero — é isso que deixa o CI rodar de graça e hermético.

É um cardápio: cada prato (modelo) tem um preço de entrada e um de saída, e está numa seção (tier). O motor escolhe do cardápio; o cardápio não escolhe sozinho.

a forma da entrada + linhas representativas

O modelRegistryEntrySchema exige modelId, adapterId, tier e os dois preços não-negativos (mais capabilities opcional). O DEFAULT_MODEL_ID é 'glm-5.2' (um T2). Ids e preços são dados que evoluem — a forma é o contrato.

packages/contracts/src/registry.ts — forma + linhas representativas
{ modelId:'local-default', adapterId:'local',       tier:T0, in:0,      out:0 }      // $0 hermético
{ modelId:'local-extract', adapterId:'local',       tier:T1, in:0,      out:0 }
{ modelId:'qwen3.7-plus',  adapterId:'cliproxyapi', tier:T1, in:0.00015,out:0.0005 }
{ modelId:'glm-5.2',       adapterId:'cliproxyapi', tier:T2, in:0.0002, out:0.0006 }  // DEFAULT_MODEL_ID
{ modelId:'gpt-5.5-xhigh', adapterId:'cliproxyapi', tier:T3, in:0.015,  out:0.045 }
{ modelId:'claude-opus-4-8-max', adapterId:'cliproxyapi', tier:T3, in:0.03, out:0.15 }

A divisão por tier (registry as-built): T0 = local-default, local-extract ($0); T1 = kimi-k2.7-code-highspeed, grok-composer-2.5-fast, qwen3.7-plus; T2 = deepseek-v4-pro, gemini-3.5-flash, glm-5.2, qwen3.7-max; T3 = gpt-5.5-xhigh, claude-opus-4-8-max. O adapter local mantém o T0 em $0 para o CI hermético. (Ids/preços são dados ilustrativos e evoluem; a forma é o contrato.)

11 entradas, agrupadas por tier · preço (in+out) por 1k tokens T0 · $0 local-default (adapter local) hermético · CI grátis T1 local-extract ($0) kimi-k2.7-… grok-composer-2.5 qwen3.7-plus T2 deepseek-v4-pro gemini-3.5-flash glm-5.2 (default) qwen3.7-max T3 · frontier gpt-5.5-xhigh claude-opus-4-8-max caro · só adjudicação preço cresce → $0 $0,18 (in+out)
O tier não é o preço — mas, na prática, os tiers altos abrigam os modelos caros. Os locais ficam em $0.
Anatomia de uma entrada do registry obrigatórios — o contrato (roteamento + preço) modelId : string adapterId : string tier : T0..T4 costPer1kInputUsd costPer1kOutputUsd os dois preços ≥ 0 alimentam pickCheapestForTier e a medição .optional() — só p/ o doctor provider · baseUrl maxOutputTokens · noImageSupport aditivos · nunca mexem no roteamento nem no preço
Cinco campos formam o contrato; os quatro metadados de gateway são .optional() e existem só para o smoke check do alembic doctor --client-stack.
5

Roteamento: escolher o modelo mais barato que qualifica


Quando um tier é escolhido mas nenhum modelId específico é fixado, pickCheapestForTier seleciona a entrada mais barata daquele tier, pelo custo combinado (entrada + saída) por 1k. É uma função pura sobre o registry — um fold determinístico, sem efeitos colaterais.

Dentro da seção do cardápio que você escolheu, você pede o prato mais barato — somando entrada e prato principal, não só um dos dois. Mesma seção, mesma conta, sempre a mesma escolha.

um reduce puro sobre o registry

Filtra os candidatos do tier, e dobra (reduce) mantendo a entrada de menor costPer1kInputUsd + costPer1kOutputUsd. Sem Date, sem random — o mesmo registry sempre devolve a mesma escolha, o que importa para o replay (lição 28).

packages/contracts/src/registry.ts — fold de custo combinado
export const pickCheapestForTier = (tier, registry = MODEL_REGISTRY) => {
  const candidates = Object.values(registry).filter((e) => e.tier === tier);
  if (candidates.length === 0) return undefined;
  return candidates.reduce((cheapest, entry) => {
    const entryCost = entry.costPer1kInputUsd + entry.costPer1kOutputUsd;  // in + out
    const bestCost  = cheapest.costPer1kInputUsd + cheapest.costPer1kOutputUsd;
    return entryCost < bestCost ? entry : cheapest;
  });
};
Por que somar entrada + saída?

Um modelo barato na entrada mas caro na saída poderia perder numa tarefa de muita geração. Somar os dois dá um único escalar comparável. O ADR-0006 põe um piso de profundidade por cima: o roteador deve pegar o mais barato acima de um piso de qualidade, reservando os modelos frontier (o par T3) para adjudicação difícil — então "mais barato" nunca quer dizer "fraco demais".

Cartão · roteamento
Dois modelos T2 custam (in+out) US$ 0,0006 e US$ 0,0009. Sem modelId fixado, qual pickCheapestForTier(T2) devolve?
clique para virar
resposta
O de US$ 0,0006, e de forma determinística — é um fold puro sobre o registry comparando o custo combinado. O mesmo registry sempre dá a mesma escolha.
O fold percorre os candidatos do tier · carrega o menor (in+out) deepseekin+out 0,0005 geminiin+out 0,0005 glm-5.2in+out 0,0008 qwen-maxin+out 0,00095 reduce: entryCost < bestCost ? entry : cheapest vencedoro de 0,0005 comparação é < estrita: num empate, o primeiro encontrado vence · ordem estável → desempate determinístico
Sem Date nem random: o mesmo registry sempre devolve a mesma escolha — a pré-condição do replay (lição 28).

Veja a mesma ideia em duas alturas — escolha a sua:

Você diz “quero algo do nível T2” e a função pega o mais barato da prateleira T2 — somando entrada e saída. É sempre a mesma escolha, porque ela só olha a prateleira, nunca o relógio nem um sorteio. Se quiser um modelo específico, é só fixá-lo: aí o roteador nem precisa escolher.

A comparação é estritamente menor (<): num empate de custo combinado, o primeiro candidato encontrado vence. Como Object.values(MODEL_REGISTRY) tem ordem de iteração estável, o desempate é determinístico. A pureza (sem IO, sem Date.now(), sem Math.random()) é o que deixa o replay reproduzir exatamente a mesma rota a partir do mesmo estado.

modelId fixado pula o roteador · sem ele, pickCheapest decide modelId fixado? sim usa esse modelo direto não pickCheapestForTier(tier)
6

O budget guard: medição que falha fechada


O roteamento decide qual modelo; o BudgetGuard decide se a chamada pode acontecer. Ele nasce com um teto rígido em dólares e tem métodos para check (cabe?) e record (registra o gasto) — e os padrões são deliberadamente paranoicos.

É o limite do cartão pré-pago: antes de cada compra, ele confere "isto cabe no que sobra?". Se não cabe, a compra é recusada — não há "deixa passar e a gente vê depois".

cap clampado + check/record

O construtor faz cap = Math.max(0, capUsd). O check deixa passar de graça qualquer chamada com projectedUsd === 0; bloqueia (budget_exceeded) se spent + projectedUsd ultrapassaria o teto; senão aprova e informa o restante. O record soma o gasto real ao acumulado spent.

packages/etl/src/budget.ts (condensado)
export const createBudgetGuard = (capUsd) => {
  const cap = Math.max(0, capUsd);   // teto não-positivo vira 0 = só free tier
  let spent = 0;
  return {
    check(estimate) {
      const projectedUsd = priceEstimate(estimate);
      if (projectedUsd === 0) return { ok:true, projectedUsd:0, … };  // free sempre passa
      if (roundUsd(spent + projectedUsd) > cap)
        return { ok:false, reason:'budget_exceeded', … };      // estouraria ⇒ BLOQUEIA
      return { ok:true, projectedUsd, remainingUsd: remaining() };
    },
    record(spend) { spent = roundUsd(spent + costOf(spend)); return spent; },
  };
};
A jogada fail-closed: cap = Math.max(0, capUsd)

Um teto não-positivo não quer dizer "sem limite" — ele é clampado para 0, que significa só o free tier: qualquer chamada com projectedUsd > 0 é bloqueada, porque 0 + qualquer coisa > 0. Assim, o padrão seguro (nenhum orçamento definido) é a postura mais barata possível, nunca um gasto desgovernado. Chamadas free (projectedUsd === 0) sempre passam — é por isso que o funil inteiro pode rodar a $0 e hermético no adapter LOCAL (lição 15).

Fluxograma com um portão central. À esquerda, caixa 'chamada estimada' com projectedUsd = priceEstimate(estimate). Seta para um grande portão hexagonal 'guard.check(estimate)' cujas regras são: free (projectedUsd === 0) sempre passa; cabe no teto se spent + projectedUsd ≤ cap; estoura bloqueia. Dois caminhos saem: caminho oliva para run() e depois guard.record() (mede pelo preço do registry); caminho vermelho-rust para uma caixa carimbada budget_exceeded — a chamada nunca roda. Quadro destacado com o código const cap = Math.max(0, capUsd).

O BudgetGuard como portão: free passa sempre, paga só passa se couber, estouro bloqueia — e medir vem depois de rodar.

o caminho de uma chamada: rotear → checar → rodar → medir
pickCheapestForTier in+out, ≥ piso (ADR-0006) guard.check(est) cabe no teto? run() (cintura) só se ok guard.record() mede pelo preço check falha ⇒ budget_exceeded · a chamada nunca roda
A régra do check: spent + projected ≤ cap ? cabe: cap spent projected ✓ ok estoura: cap spent projected ✗ budget_exceeded a barra que cruza o teto nunca roda — o BudgetExceeded é devolvido tipado, sem exceção lançada
O guard projeta a barra antes de gastar. Se a ponta de projected passa do cap, a chamada é recusada inteira — não pela metade.
Cuidado "Teto 0 = ilimitado" é a intuição errada e perigosa. Aqui, 0 = gaste nada. O sistema prefere não gastar a gastar sem querer.
7

A precificação é sempre aplicada na medição


Há uma invariante sutil: a precificação é sempre aplicada. Mesmo que um resultado de modelo (ModelRunResult) já traga um costUsd, um modelo free-tier é medido como 0 — o costOf checa isFreeTierModel primeiro. E um modelo pago, sem custo explícito, cai para costForModel(modelId, usage). O gasto nunca é adivinhado e nunca é pulado.

A caixa registradora recalcula o preço pela etiqueta da loja, não pelo número que o cliente escreveu à mão no produto. Um rótulo torto não infla a conta.

costOf: free-tier medido como 0, antes de ler o campo

Para um ModelRunResult, costOf retorna 0 se isFreeTierModel(modelId); só então usa costUsd ?? costForModel(modelId, usage) ?? 0. Resultado: chamadas T2/T3 são medidas ao preço do registry, e o acumulado spent é autoritativo — não advisório.

packages/etl/src/budget.ts
const costOf = (spend) => {
  if (typeof spend === 'number') return Math.max(0, spend);
  // … UsageEstimate → priceEstimate(spend) …
  if (isFreeTierModel(spend.modelId)) return 0;        // free SEMPRE 0, ignora o campo
  return spend.costUsd ?? costForModel(spend.modelId, spend.usage) ?? 0;
};
costOf — a medição recalcula, não confia no campo ModelRunResult traz modelId + costUsd? free-tier? (checa 1º) sim custo = 0 não costUsd ?? costForModel(modelId, usage) ?? 0 um rótulo torto não infla spent é autoritativo — medido pelo tier/registry, nunca adivinhado nem pulado
A ordem importa: free-tier é resolvido antes de qualquer campo de custo — por isso um costUsd errado num modelo $0 não conta.
8

Explorador: o que acontece em cada tier


Clique num tier para ver, lado a lado, quem revisa, se roda sozinho e quais modelos o roteador escolheria por padrão (o mais barato do tier). Lembre: o custo segue o tier na prática, mas é o BudgetGuard — não o tier — que barra uma chamada cara.

T0 T1 T2 T3 T4 mais supervisão →
autônomo

T0 · silencioso

Totalmente autônomo, sem humano no loop — o caminho silencioso.

Modelos do tier: local-default ($0).

tier.ts · isAutonomous = true para T1–T3
9

Simulador: o teto decide se a chamada roda


Mexa nos controles: defina o teto (que será clampado por Math.max(0, …)), o quanto já foi gasto, e o custo projetado da próxima chamada. O veredicto reproduz a lógica real de check — incluindo "free sempre passa" e "0 = só free tier".

cap = Math.max(0, capUsd) — o grampo que torna o default seguro capUsd = -5 capUsd = 0 capUsd = 0,05 (positivo) Math.max(0, x)grampeia o piso cap = 0 → só free tier cap = 0,05 → até esse limite nenhum teto negativo sobrevive · "sem orçamento" colapsa para "gaste nada", jamais "ilimitado"
Três entradas, o mesmo grampo: -5 e 0 caem para 0 (só free); um positivo passa inteiro. É uma linha de código que define toda a postura padrão.

cap = Math.max(0, 10) = 10 · spent = 6 · projected = 3

Agora você: arraste o teto para -1 (ou 0) e o custo projetado para qualquer valor maior que zero. Veja o bloqueio. Depois zere o custo projetado: passa. Isso é o fail-closed em ação — sem teto positivo, só o $0 anda.
10

Exemplo guiado — uma chamada, do tier ao gasto


Um passo de council quer rodar em T2, sem modelId fixado, com um guard de teto US$ 5,00 e US$ 4,90 já gastos. A chamada projeta US$ 0,03. O que acontece?

Uma chamada T2 com teto US$ 5,00 e US$ 4,90 já gastos 1 rotear pickCheapest(T2) 2 checar 4,90+0,03=4,93 ≤ 5 ✓ 3 rodar run() · só agora 4 medir spent → ~4,93
O run() só dispara depois de o check aprovar. Reprovado no passo 2, a chamada nunca chegaria ao passo 3.
Passo a passo
1
Roteia. pickCheapestForTier(T2) dobra sobre as entradas T2 e devolve a de menor (in+out) — determinístico, mesmo registry, mesma escolha.
2
Checa. guard.check: projetado é 0,03 > 0, então não é free; 4,90 + 0,03 = 4,93 ≤ 5,00cabe. Retorna ok:true, restante 0,07.
3
Roda. Só agora o run() (a cintura estreita) chama o modelo de fato. Se o check tivesse falhado, a chamada nunca aconteceria.
4
Mede. guard.record: o modelo é T2 (pago), então costOf usa o custo real → spent sobe para ~4,93. A próxima chamada cara será barrada.
Agora você: repita o exercício, mas com o guard criado como createBudgetGuard(0). Em que passo a chamada para — e por quê? (Resposta: no passo 2. Com cap = 0, qualquer projetado > 0budget_exceeded; só uma chamada $0 passaria.)
11

Confusões comuns


"LOCAL é o sexto tier." Não — o Tier é exatamente T0–T4. LOCAL é um marcador ortogonal que prende o trabalho a modelos $0 independentemente do tier. Uma unidade pode ser T2 e LOCAL: autônoma com um revisor, mas mantida em modelos locais por privacidade ou custo.
"O tier define o custo." Eles são relacionados, mas separados. Tier é autonomia/supervisão; custo é o preço por modelo. Um passo de council T3 usa modelos mais caros, sim — mas é o budget guard, não o tier, que impede uma chamada paga de estourar o teto. Dois mostradores, dois mecanismos.
"Teto zero ou negativo significa ilimitado." Exatamente o contrário. cap = Math.max(0, capUsd) clampa para 0, e 0 significa só free tier. O padrão sem orçamento é a postura que gasta nada.
AfirmaçãoMito?Realidade
"Teto 0 = ilimitado"mito0 = só free tier
"O tier alto barra a chamada cara"mitoquem barra é o BudgetGuard
"O costUsd do resultado é a verdade"mitofree-tier vira 0; pago recalcula
"pickCheapest compara só a saída"mitocompara in + out somados
Duas perguntas, dois mecanismos — nunca o mesmo pergunta: “quem revisa?” → o Tier (T0–T4) supervisão · DEFAULT_TIER = T4 pergunta: “a chamada paga roda?” → o BudgetGuard dinheiro · cap = Math.max(0, capUsd)
Quando algo cair entre “autonomia” e “custo”, pergunte: é supervisão (tier) ou é dinheiro (guard)? A resposta diz qual mecanismo mexer.
12

Recapitulando


Dois eixos

Autonomia ≠ custo

Tier = quanta supervisão (T0→T4). Preço + guard = quanto dinheiro. Dois mostradores ortogonais — um não mexe no outro.

autonomia custo ortogonais
1
A escada

T0 → T4, default T4

Cinco degraus de supervisão. isAutonomous só para T1–T3; DEFAULT_TIER = T4 — o não classificado para. Fail-closed.

2
O registry

11 entradas, preço por 1k

Cada modelo: adapter, tier, custo de entrada e saída. Os local-* em $0. pickCheapestForTier dobra pelo (in+out) — puro e determinístico.

3
O guard

cap = Math.max(0, capUsd)

Teto não-positivo vira 0 = só free tier. Free passa sempre; pago só se couber; estouro = budget_exceeded, a chamada nunca roda.

4
A medição

spent é autoritativo

A precificação é sempre aplicada: free-tier vira 0 antes de ler o campo; pago recalcula pelo registry. O gasto nunca é adivinhado nem pulado.

5
Slide 1 / 5 use
13

Verifique


Revisão acumulada

Três perguntas. A pontuação corre conforme você responde — acerte as três e a revisão fecha.

1. Você cria createBudgetGuard(0) (ou um teto negativo). O que pode rodar?
Correto: c. cap = Math.max(0, capUsd) clampa para 0, e o check só aprova se projectedUsd === 0 ou se couber no teto. Com cap 0, só passam as chamadas free (LOCAL/T0) — fail-closed: o padrão gasta nada.
2. Dois modelos T2 custam (in+out) US$ 0,0006 e US$ 0,0009 por 1k. Sem modelId fixado, qual pickCheapestForTier(T2) devolve, e a escolha é determinística?
Correto: b. O reduce mantém a entrada de menor costPer1kInputUsd + costPer1kOutputUsd. É puro sobre MODEL_REGISTRY (sem Date/random), então o mesmo registry sempre dá a mesma escolha — o que importa para o replay (lição 28).
3. Um ModelRunResult de um modelo free-tier traz um costUsd: 0.01 perdido. Como o record o mede?
Correto: d. costOf retorna 0 para um modelo free-tier antes de ler costUsd. A precificação é sempre aplicada pelo tier/registry, então um campo perdido não infla o total — a medição é autoritativa, não advisória.
Acertos: 0/3
Você é o aluno e também o auditor: olhe qualquer chamada de modelo no monorepo e pergunte — "qual tier roteia isto, e qual teto o guard impõe?". Se a resposta for "nenhum teto", lembre que isso significa $0, não "à vontade". A seguir (lição 28): aprofundamos a invariante ③ — determinismo e replay: por que o id de um run é o hash do seu spec, e por que a VM bane Date.now(), new Date() e Math.random().