Curso / Lição 27
Lição 27 · Avançado · controle de custo

Tiers, custo & orçamento

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

Objetivos desta lição
  • Distinguir os dois botões: Tier (autonomia/supervisão) vs custo (preço por modelo) — relacionados, mas mecanismos separados.
  • Ler a escada T0→T4 e o marcador ortogonal LOCAL, e por que DEFAULT_TIER = T4 é fail-closed.
  • Aplicar pickCheapestForTier: o fold puro que escolhe a entrada mais barata de um tier por custo in+out combinado.
  • Explicar o golpe fail-closed do BudgetGuard: cap = Math.max(0, capUsd) ⇒ teto não-positivo vira 0 = só free tier.
0
degraus do enum Tier (T0–T4)
0
entradas no MODEL_REGISTRY
$0
teto seguro padrão (só free tier)
0
modelos $0 (adapter local, T0)
ANATOMIA DE UMA ENTRADA · os 5 campos do contrato (sempre) + 4 opcionais só p/ o doctor
obrigatórios (a FORMA · validados por Zod) — alimentam roteamento + preço modelIdstring.min(1) adapterIdlocal | cliproxyapi tierT0–T4 (autonomia) costPer1kInputUsd≥ 0 (preço in) costPer1kOutputUsd≥ 0 (preço out) opcionais (.optional() · ADITIVOS) — alimentam SÓ o alembic doctor --client-stack provider · baseUrl maxOutputTokens · noImageSupport capabilities[] (default []) toda entrada antigasegue válida sem eles ↑ estes dois, somados, são o escalar que pickCheapestForTier compara A FORMA é o contrato imutável; os IDs e preços abaixo são DADOS do registro e evoluem (refinar contra faturas reais — registry.ts:54).

01 · Dois botões ortogonais: autonomia e custo

Antes de qualquer código, fixe a ideia central — porque quase toda confusão desta lição vem de fundi-la. Tier e custo são dois botões que giram de forma independente. Um diz quanta supervisão humana o trabalho precisa; o outro diz quanto dinheiro uma chamada pode gastar. Eles se cruzam no roteamento, mas nunca são a mesma coisa.

Analogia simples. Pense num escritório. O Tier é o nível de aprovação: tarefa rotineira o estagiário faz sozinho (T0–T1); tarefa importante precisa de um revisor (T2); decisão crítica vai ao comitê (T3); irreversível espera o diretor humano (T4). O custo é o salário de quem você chama: dá para resolver com o estagiário barato ou pagar o consultor caro — e existe um teto de gastos que barra a conta antes dela acontecer. Dois controles, dois mecanismos.
OS DOIS BOTÕES · autonomia (Tier) × custo (registro + BudgetGuard) — eixos independentes
autonomia (Tier) custo por chamada (USD/1k) T4 T3 T2 T1 T0 T1 barato (qwen3.7-plus) T2 barato (glm-5.2 default) T3 caro (claude-opus-4-8-max) T0 grátis (local-default) subir o eixo NÃO obriga subir o custo · descer o custo NÃO muda a autonomia

02 · A escada de autonomia: T0 → T4

O enum Tier tem exatamente cinco degraus, autonomia-mais-barata primeiro. Eles são sobre supervisão humana, não sobre dinheiro. Esta é a primeira tabela comparativa — leia cada degrau como "quem precisa aprovar":

TierSignificadoAutônomo?
T0silencioso / totalmente autônomo, sem humano no loopsim (o caminho silencioso)
T1autônomo com logging levesim
T2autônomo, um revisor notificadosim
T3autônomo, revisão de council exigidasim
T4PARK — retido da execução autônoma; precisa de council + humanonão

Dois fatos tornam isto fail-closed: DEFAULT_TIER = T4 (trabalho não classificado estaciona, em vez de rodar sozinho — Lição 26) e isAutonomous retorna true só para T1–T3 (tier.ts:59). Veja a escada como degraus, com a fronteira da autonomia desenhada entre T3 e T4:

A ESCADA T0→T4 · subir = mais supervisão · a linha entre T3 e T4 separa "roda sozinho" de "espera o humano"
T0 · silencioso T1 · logging leve T2 · 1 revisor T3 · council T4 · PARK ✋ council + humano · não roda só fronteira da autonomia · isAutonomous = false a partir daqui ↑ + supervisão → DEFAULT_TIER = T4: trabalho não classificado nasce parado (fail-closed) · só uma classificação explícita o desce até um degrau autônomo.

03 · O marcador LOCAL não é um sexto tier

Erro clássico: contar "T0, T1, T2, T3, T4, LOCAL — seis tiers". Não. Tier é exatamente T0–T4. LOCAL é um marcador ortogonal que marca trabalho que deve ficar em modelos local/$0 "independente do tier" (caminhos sensíveis a privacidade ou custo, tier.ts:36). Por ser ortogonal, ele se combina com qualquer degrau: uma unidade pode ser T2 e LOCAL ao mesmo tempo — autônoma com um revisor, mas presa a modelos locais.

TIER × LOCAL · o marcador é uma segunda dimensão, não um degrau a mais
T0 T1 T2 T3 T4 a escada Tier (5 degraus) → marcador LOCAL ($0) ortogonal · não é degrau ↖ esta unidade = T2 E LOCAL

04 · O registro de modelos: custo por 1k tokens

Cada modelo roteável é uma entrada de registro com um adapterId, um tier e dois preços — costPer1kInputUsd e costPer1kOutputUsd. Há 11 entradas espalhadas pelos tiers. A forma é o contrato (validado por Zod); ids e preços são dados do registro e evoluem:

// packages/contracts/src/registry.ts:8-13 — a FORMA (o contrato)
export const modelRegistryEntrySchema = z.object({
  modelId:            z.string().min(1),
  adapterId:          z.string().min(1),
  tier:               tierSchema,
  costPer1kInputUsd:  z.number().nonnegative(),
  costPer1kOutputUsd: z.number().nonnegative(),
});
// packages/contracts/src/registry.ts:57-173 — entradas representativas (in / out por 1k)
{ 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 }   // + barato T1
{ 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 completa pelos tiers (do bloco-doc do registro, linhas 39–53): T0 = local-default; T1 = local-extract, 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 os $0 para CI hermético. (Preços são aproximados/relativos no código e devem ser refinados contra faturas reais — comentário do próprio arquivo, linha 54.)

DOIS ADAPTERS, DUAS ILHAS DE CUSTO · local ($0, hermético) vs cliproxyapi (precificado)
adapterId: 'local' · $0 2 modelos · CI hermético, sem rede local-default · T0 · in 0 / out 0 local-extract · T1 · in 0 / out 0 adapterId: 'cliproxyapi' · precificado 9 modelos pagos · gateway 127.0.0.1:8317 T1: kimi · grok · qwen3.7-plusT2: deepseek · geminiT2: glm-5.2 · qwen3.7-max T3 (frontier):gpt-5.5-xhighclaude-opus-4-8-max A ilha 'local' é o que deixa o funil inteiro rodar $0 e offline (Lição 15) — o adapter, não o tier, é o que garante o preço zero.

Comparativo: custo combinado (in+out) por tier

Esta é a segunda tabela comparativa — os números reais de registry.ts, somados (in + out) por 1k tokens, que é exatamente o escalar que o roteador compara. Verde = o mais barato do tier (o que pickCheapestForTier escolhe); vermelho = a faixa frontier.

modelIdTierin / 1kout / 1kin + out / 1k
local-defaultT0000
local-extractT1000
qwen3.7-plusT10.000150.00050.00065 ← + barato T1*
kimi-k2.7-code-highspeedT10.00020.00060.0008
grok-composer-2.5-fastT10.000250.000750.001
deepseek-v4-proT20.00010.00040.0005 ← + barato T2
gemini-3.5-flashT20.00010.00040.0005 (empate)
glm-5.2 DEFAULTT20.00020.00060.0008
qwen3.7-maxT20.000250.00070.00095
gpt-5.5-xhighT30.0150.0450.06 ← + barato T3
claude-opus-4-8-maxT30.030.150.18

* Entre os modelos T1, local-extract (0) é o mais barato em absoluto; entre os pagos T1, qwen3.7-plus (0.00065) lidera. O empate em T2 (0.0005) entre deepseek-v4-pro e gemini-3.5-flash é resolvido pela ordem de iteração — ver §05.

O salto de preço entre os tiers baratos e o frontier é brutal — e é ele que justifica tanto o roteamento quanto o teto. Veja as barras em escala log (o eixo dobra a cada passo); o T3 fica numa ordem de grandeza inteiramente diferente:

CUSTO COMBINADO (in+out / 1k) POR MODELO · escala logarítmica — T3 vive ~100× acima dos baratos
qwen3.7-plus T10.00065 deepseek T2 ✓0.0005 glm-5.2 T20.0008 grok-fast T10.001 gpt-5.5-xhigh T30.06 claude-opus-4-8-max T30.18 eixo log10 · cada ~120px = ×10 · o salto T2→T3 é de ~0.0005 para 0.06 (≈120×): por isso o frontier é reservado p/ adjudicação difícil.

05 · Roteamento: pickCheapestForTier

Quando um tier é escolhido mas nenhum modelId específico está fixado, pickCheapestForTier seleciona a entrada mais barata daquele tier pelo custo por-1k combinado. É pura sobre o registro — um fold determinístico, sem efeitos colaterais, sem Date/random:

// packages/contracts/src/registry.ts:187-200
export const pickCheapestForTier = (
  tier: Tier,
  registry = MODEL_REGISTRY,
): ModelRegistryEntry | undefined => {
  const candidates = Object.values(registry).filter((entry) => entry.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;   // strict < ⇒ mantém o 1º em empate
  });
};
Por que somar input+output? Um modelo barato no input mas caro no output poderia perder numa tarefa pesada de geração. Somar os dois dá um único escalar comparável. Repare no < estrito: num empate (como os dois T2 a 0.0005), o reduce mantém o candidato já visto — ou seja, a ordem de iteração do registro decide. Por isso o resultado é determinístico: o mesmo MODEL_REGISTRY sempre produz a mesma escolha, o que importa para replay (Lição 28).
O EMPATE EM T2 · por que o < ESTRITO mantém o primeiro visto (deepseek vs gemini, ambos 0.0005)
cheapest =deepseek · 0.0005 1º candidato (seed) gemini 0.0005 < 0.0005 ? NÃO (igual) mantém deepseek(o 1º visto fica) depois: glm-5.2 (0.0008) < 0.0005 ? NÃO → também perde vencedor final: deepseek-v4-pro Se fosse <=, o ÚLTIMO empatado venceria — e a escolha dependeria da ordem de inserção. O < estrito fixa "o 1º visto", tornando o resultado determinístico p/ replay.

Compare lado a lado os caminhos do fold em três tiers — o vencedor é o menor in+out de cada coluna:

pickCheapestForTier · o fold por tier (números reais de registry.ts) — vence o menor in+out
T1 (pagos) qwen3.7-plus · 0.00065 ✓ kimi-…-highspeed · 0.0008 grok-…-fast · 0.001 T2 deepseek-v4-pro · 0.0005 ✓ gemini-3.5-flash · 0.0005 (empate) glm-5.2 · 0.0008 qwen3.7-max · 0.00095 empate 0.0005 → strict < mantém o 1º visto T3 (frontier) gpt-5.5-xhigh · 0.06 ✓ claude-opus-4-8-max · 0.18 reservado p/ adjudicação difícil (ADR-0006) O fold percorre só os candidatos do tier pedido e guarda o de menor in+out. Tier T0 (local) fica em $0 — sempre grátis. ADR-0006 sobrepõe um piso de profundidade: "mais barato" significa o + barato acima de um piso de qualidade — nunca "fraco demais".

06 · Calculadora de custo ao vivo

Hora de sentir os números. Escolha um tier, mexa os tokens de input e output — a calculadora aplica pickCheapestForTier de verdade (preços reais de registry.ts), mostra qual modelo ganha, recomputa o custo em USD e redesenha a barra. Verde = cabe no teto; vermelho = o BudgetGuard bloquearia (próxima seção).

$0 teto $5 $0.00

Escala do eixo: 0 → $10 · a linha vermelha é o teto. Em T3 (frontier) basta um punhado de milhares de tokens para a barra cruzar a linha — é exatamente o que o BudgetGuard checa antes de a chamada rodar.

Preveja antes de revelar
Você pede uma chamada T3 com 10.000 tokens de input e 10.000 de output. O roteador pega o T3 mais barato (gpt-5.5-xhigh: 0.015 in / 0.045 out por 1k). Quanto custa essa única chamada? E ela passa num teto de $0.50?
(10000/1000)×0.015 + (10000/1000)×0.045 = 0.15 + 0.45 = $0.60. Com teto $0.50: 0 + 0.60 > 0.50BLOQUEIA (budget_exceeded), a chamada nunca roda. A mesma chamada em T2 com glm-5.2 (0.0002/0.0006) custaria 0.002 + 0.006 = $0.008 — 75× mais barata. É o salto de preço do frontier que o teto existe para conter.

07 · O BudgetGuard fail-closed

O roteamento decide qual modelo; o BudgetGuard decide se a chamada pode acontecer. Ele é criado com um teto rígido em USD, e seus defaults são deliberadamente paranoicos. Esta é a peça central da lição:

// packages/etl/src/budget.ts:120-149 (condensado)
export const createBudgetGuard = (capUsd) => {
  const cap = Math.max(0, capUsd);   // teto não-positivo → 0 = só free tier
  let spent = 0;
  return {
    check(estimate) {
      const projectedUsd = priceEstimate(estimate);
      if (projectedUsd === 0)
        return { ok: true, projectedUsd: 0, … };          // chamada grátis 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; },
  };
};
O golpe fail-closed: cap = Math.max(0, capUsd)

Um teto não-positivo não significa "sem limite" — ele vira 0, que significa só free tier: qualquer chamada com projectedUsd > 0 é bloqueada, porque 0 + qualquer > 0. Então o default seguro (sem orçamento definido) é a postura mais barata possível, nunca um gasto descontrolado. Chamadas free-tier (projectedUsd === 0) sempre passam — é por isso que o funil inteiro pode rodar $0 e hermético no adapter local (Lição 15).

0 -5 · -1 (entradas não-positivas) todas grudam em 0 = só free tier $1 $5 $10 (passam = teto real) positivos passam inalterados → viram o cap efetivo
FAIL-OPEN vs FAIL-CLOSED · o que Math.max(0, capUsd) escolhe diante de um teto não definido
FAIL-OPEN (perigoso) — NÃO é o Alembic teto 0 / negativo → tratado como "ilimitado" qualquer chamada paga LIBERA ✗ esquecer o orçamento = gasto descontrolado o erro silencioso que mais dói na fatura FAIL-CLOSED — o Alembic cap = Math.max(0, capUsd) → vira 0 paga BLOQUEIA · grátis passa ✓0 + qualquer > 0 esquecer o orçamento = postura mais barata o default seguro nunca gasta sozinho a mesma entrada (teto não definido), dois mundos opostos — uma linha de Math.max escolhe o seguro.

Simples vs Técnico: o que "fail-closed" quer dizer aqui

Em uma frase: quando em dúvida, o sistema escolhe não gastar. Esqueceu de definir um orçamento? Em vez de liberar gastos infinitos (fail-open, perigoso), ele trava em $0 e só deixa rodar o que é de graça. O caminho "seguro por padrão" é o mais barato, não o mais caro.
Mecanismo: Math.max(0, capUsd) elimina tetos negativos e o "não definido" (0). check tem duas portas: projectedUsd === 0 passa imediatamente; senão, compara roundUsd(spent + projectedUsd) > cap e devolve { ok:false, reason:'budget_exceeded' } se estouraria. O record só soma via costOf, que mede modelos free-tier como 0 (budget.ts:110) — a medição é a fonte da verdade, não o estimador.

O preço é sempre aplicado na medição

O mapa declara um invariante sutil: o preço é sempre aplicado. Olhe o helper costOf do record — mesmo quando um ModelRunResult carrega um costUsd, um modelo free-tier é medido como 0 (isFreeTierModel(spend.modelId), budget.ts:110), e um modelo pago recorre a costForModel(modelId, usage) se nenhum custo explícito estiver presente. O gasto nunca é adivinhado e nunca pulado: chamadas T2/T3 são medidas ao preço do registro, então o total corrente spent é autoritativo.

costOf(spend) · como o record decide o que somar ao spent — o free-tier é zerado ANTES do campo
costOf(spend) é ModelRunResult free-tier? SIM = $0campo ignorado (budget.ts:110) NÃO (pago) costUsd ?? costForModel(modelId, usage) ?? 0mede ao preço do registro number → Math.max(0, n) UsageEstimate → priceEstimate A ordem importa: free-tier é testado 1º, então um costUsd perdido nunca infla.
PIPELINE DE UMA CHAMADA · roteia → check → roda só se ok → mede ao preço
pickCheapestForTierin+out, ≥ piso guard.check(est)cabe no teto? run() (cintura)só se ok guard.record()mede ao preço check falha ⇒ budget_exceeded · a chamada nunca roda

08 · Decisão: "essa chamada paga roda?" (fluxograma)

Junte tudo num único fluxograma. Diante de qualquer chamada, o check segue este caminho — cada losango é uma pergunta que escolhe a rota até "roda" ou "bloqueia". Siga as setas com o caso do teto-zero destacado:

FLUXOGRAMA · guard.check(estimate) — da estimativa ao veredito (o caminho do teto-zero destacado)
check(estimate) projectedUsd = priceEstimate(est) projectedUsd === 0 ? SIM ✓ ok free tier passa NÃO spent + projectedUsd > cap ? NÃO ✓ ok cabe · roda SIM ✗ budget_exceeded a chamada nunca roda (fail-closed) teto = 0 (default): só o ramo grátis passa · 0+qualquer>0 → todo pago bloqueia

09 · Meça uma chamada na mão (passo a passo → agora você)

Você já viu a calculadora girar. Agora faça a conta na mão, devagar — recuperar o procedimento (não só ver o número) é o que fixa. Caso de referência: uma chamada T2 com o modelo default.

Exemplo resolvido · uma chamada T2 com glm-5.2 passa num teto de $1?
1
Escolha o modelo. Tier T2, sem modelId fixado. pickCheapestForTier(T2) percorre os candidatos e retorna o de menor in+out: deepseek-v4-pro (0.0005). Mas suponha que a unidade fixou o default glm-5.2 (0.0002 in / 0.0006 out).
2
Pegue os tokens. Digamos 4000 de input e 3000 de output.
3
Aplique a fórmula de preço (priceEstimate/costForModel): (in/1000)×inUsd + (out/1000)×outUsd.
4
Some. (4000/1000)×0.0002 + (3000/1000)×0.0006 = 0.0008 + 0.0018 = $0.0026 de custo projetado.
5
Veredito do check. Com spent = 0 e teto $1: 0 + 0.0026 = 0.0026 ≤ 1ok ✓, a chamada roda, sobram $0.9974.
Agora você: a mesma chamada (4000 in / 3000 out) em T3 com gpt-5.5-xhigh (0.015 in / 0.045 out). Quanto custa? Passa num teto de $0.10? Faça antes de revelar.
(4000/1000)×0.015 + (3000/1000)×0.045 = 0.06 + 0.135 = $0.195. Com teto $0.10: 0 + 0.195 > 0.10BLOQUEIA (budget_exceeded). Repare: a mesma chamada saltou de $0.0026 (T2) para $0.195 (T3) — ~75× — porque o frontier é caro. O procedimento é sempre o mesmo; só trocam os preços do modelo.

Confusões comuns: Tier ≠ custo

"LOCAL é o sexto tier." Não — Tier é exatamente T0–T4. LOCAL é um marcador ortogonal que fixa trabalho em modelos $0 independente do seu tier. Uma unidade pode ser T2 e LOCAL ao mesmo tempo: autônoma-com-um-revisor, mas mantida em modelos locais por privacidade ou custo.
"Tier define o custo." São relacionados mas separados. Tier é autonomia/supervisão; custo é precificação por-modelo. Um passo de council T3 usa modelos mais caros, sim — mas o budget guard, não o tier, é o que impede uma chamada paga de exceder o teto. Dois botões, dois mecanismos.
DUAS PERGUNTAS, DOIS MECANISMOS · "posso rodar sozinho?" (Tier) vs "posso pagar?" (BudgetGuard)
"posso rodar sozinho?" → isAutonomous(tier) · escada T0–T4 T1–T3 sim · T4 estaciona "posso pagar?" → guard.check(est) · registro de preços cabe no teto sim · senão budget_exceeded são botões independentes — responder "sim" a uma não responde a outra

Como isso se encaixa

Os dois botões desta lição não vivem isolados — eles formam a cintura econômica da máquina inteira. Toda chamada de modelo, venha de onde vier (council, swarm, funil, gates), passa por dois pontos de estrangulamento: o roteador escolhe qual modelo pelo tier (pickCheapestForTier), e o BudgetGuard decide se ela roda pelo custo projetado. Veja o fluxo de cima para baixo — o que alimenta tiers+custo (upstream), a peça desta lição (destacada, no centro), e o que ela alimenta (downstream):

ONDE TIERS+CUSTO SE ENCAIXAM · upstream → a cintura econômica (esta lição) → downstream
↑ UPSTREAM · o que alimenta a decisão classificador de tier enum T0–T4 · DEFAULT_TIER=T4 MODEL_REGISTRY 11 entradas · custo in/out por 1k A CINTURA ECONÔMICA · esta lição (todo modelo passa por aqui) pickCheapestForTier QUAL modelo · menor in+out do tier (fold puro) BudgetGuard.check SE roda · cabe no teto? fail-closed (cap=max(0,…)) ↓ DOWNSTREAM · quem consome a chamada barata-e-aprovada (e a medição) council T3 arma o painel c/ frontier swarm / harness cada worker roteia por tier funil tiered (T0→T3) T0 local = $0 hermético gates (Proof…) proof[] rodam ao preço a MEDIÇÃO fecha o loop · guard.record → UsageStore (Lição 14) spent autoritativo · custos viram dados → curador + replay determinístico spent realimenta o cap → Dica: use o passo-a-passo abaixo para acender cada faixa do fluxo, de cima (o que decide) para baixo (quem consome + a volta da medição).
PASSO A PASSO · acenda o fluxo, camada por camada
passo 0 / 4 — clique para começar a montante

Onde você está na metodologia. Tiers+custo são a camada de economia que recobre a cintura estreita (Lição 14): a cintura padroniza uma forma de chamada (usage/costUsd); esta lição decide qual modelo a atende e se ela cabe no orçamento. Nada roda fora desse par. Para ver a peça no mapa interativo completo — escopo → council → proof → validator → publish, com o funil e o loop de aprendizado — abra o mapa da metodologia.

Conecta com

Na prática

Você não precisa fazer uma chamada paga para inspecionar esta camada. O comando alembic doctor --client-stack valida o registro inteiro offline e $0 (sem rede): confere a forma de cada entrada contra o schema Zod e a coerência de cada adapterId com um adapter que o sistema sabe construir. É a verificação direta da peça que você acabou de estudar.

# valida a FORMA do MODEL_REGISTRY + coerência de adapters · offline, $0, SEM rede
$ alembic doctor --client-stack

# saída esperada (offline — só a forma; passe --online p/ o smoke real no gateway):
# doctor (client-stack):
#   [OK] model-registry: 11 model(s) validated; 0 invalid, 0 with unknown adapter
#   [OK] client-stack: offline mode: validated registry shape only; pass --online for live model smoke ($0 without it)
# summary: 2 ok, 0 warn, 0 fail
Como ler a saída. 11 model(s) validated = as 11 entradas do registro passaram no modelRegistryEntrySchema. 0 invalid = nenhuma quebrou a forma (os 5 campos obrigatórios). 0 with unknown adapter = todo adapterId (local, cliproxyapi) é um que o sistema sabe construir — um adapter desconhecido viraria um [WARN], não falha. O comando é fail-closed: qualquer [FAIL] faz o processo sair com código diferente de zero (o CI enxerga). Acrescente --online só quando o gateway cliproxyapi (127.0.0.1:8317) estiver de pé — aí ele roda um smoke de completação por modelo. [uncertain] os números exatos (11, summary: 2 ok) acompanham o estado de registry.ts no seu checkout; a forma da saída é a que o código produz.

A fonte desses números é um único arquivo — os preços que o roteador e o guard usam vivem em packages/contracts/src/registry.ts (a forma é o contrato; ids e preços são dados que evoluem contra faturas reais, comentário da linha 54). Se quiser ver o fold e o guard exercitados por testes, sem nenhuma chamada de rede:

# o fold de roteamento (pickCheapestForTier) é coberto pelos testes de custo dos adapters
$ pnpm --filter @alembic/adapters test
# o BudgetGuard fail-closed (cap=Math.max(0,…), check/record) é coberto pelos testes da ETL
$ pnpm --filter @alembic/etl test
Experimente · validar o registro na sua máquina (offline, $0)
1
Entre no repo. cd alembic (a raiz do monorepo, onde fica o package.json de workspace).
2
Garanta os tipos compilados. pnpm -r build — assim o CLI enxerga os .d.ts de @alembic/contracts e @alembic/etl.
3
Rode a validação. alembic doctor --client-stack. Procure na saída a linha model-registry: N model(s) validated e o summary: … 0 fail — é o sinal de que a forma do registro está sã, sem tocar a rede.
4
Prove o roteamento. Abra packages/contracts/src/registry.ts, ache os dois modelos T3 (gpt-5.5-xhigh 0.06 e claude-opus-4-8-max 0.18, combinados) e confirme à mão: pickCheapestForTier(T3) tem de devolver gpt-5.5-xhigh. Confira contra a calculadora ao vivo (§06): escolha T3 e veja qual modelo a linha pickCheapestForTier(T3) reporta.
Opcional (precisa do gateway): só se o cliproxyapi estiver de pé em 127.0.0.1:8317, rode alembic doctor --client-stack --online para o smoke de completação por modelo. Sem o gateway, o comando offline acima já prova a peça — e custa $0.

Fixe os conceitos (flashcards)

Clique pra virar. Tente lembrar a resposta antes de virar — recuperação ativa fixa mais que reler.

Escada
Quantos degraus tem o enum Tier, e qual é o default?
clique pra virar ↻
Resposta
Exatamente 5: T0–T4. DEFAULT_TIER = T4 (PARK) — trabalho não classificado estaciona, fail-closed.
Roteamento
Por qual critério pickCheapestForTier escolhe?
clique pra virar ↻
Resposta
Menor costPer1kInputUsd + costPer1kOutputUsd do tier. Fold puro, < estrito (mantém o 1º em empate) ⇒ determinístico.
BudgetGuard
O que um teto não-positivo significa?
clique pra virar ↻
Resposta
cap = Math.max(0, capUsd) → vira 0 = só free tier. Nunca "ilimitado". Toda chamada paga bloqueia (0 + qualquer > 0).
Medição
Como record mede um modelo free-tier com costUsd perdido?
clique pra virar ↻
Resposta
Como 0: costOf checa isFreeTierModel primeiro (budget.ts:110), antes de ler o campo. Medição é autoritativa.

Revisão cumulativa — recupere de memória

Antes de clicar: responda de cabeça. As opções têm tamanho parecido de propósito — sem pista pela forma.

1. Você cria createBudgetGuard(0) (ou um teto negativo). O que pode rodar?
Correto: c. cap = Math.max(0, capUsd) vira 0; check passa uma chamada só se projectedUsd === 0 (porta 1) ou se ela cabe no teto. Com teto 0, só grátis passa — fail-closed. a erra na porta 1: chamadas gratuitas sempre passam, mesmo com teto 0. b é o erro perigoso (fail-open) que o Math.max existe justamente para evitar — zero nunca vira ilimitado. d confunde os botões: o bloqueio é por preço (projectedUsd), não por tier — um T0 grátis e um LOCAL pago passam/bloqueiam pelo custo, não pelo degrau.
2. Dois modelos T2 custam (in+out) 0.0005 e 0.0009 por 1k. Sem modelId fixado, o que pickCheapestForTier(T2) retorna, e a escolha é determinística?
Correto: b. O reduce mantém a entrada com o menor costPer1kInputUsd + costPer1kOutputUsd. É puro sobre MODEL_REGISTRY (sem Date/random), então o mesmo registro sempre dá a mesma escolha — o que importa para replay (Lição 28). a inverte tudo: ele escolhe o menor, e nada ali é aleatório. c inventa uma medição de latência que a função não faz — ela só lê preços do registro. d confunde com unicidade: dois (ou mais) candidatos são o caso normal — o fold é justamente o que os compara.
3. Um ModelRunResult de um modelo free-tier carrega um costUsd: 0.01 perdido. Como record o mede?
Correto: d. costOf (budget.ts:110) retorna 0 para um modelo free-tier antes de ler costUsd. O preço é sempre aplicado a partir do tier/registro do modelo, então um campo perdido não pode inflar o total de gasto. a trataria a medição como consultiva — mas ela é autoritativa. b contradiz a regra fail-closed (o código não lança em biblioteca; devolve Result); ele simplesmente normaliza para 0. c também erra: a chamada não é ignorada — ela é medida, só que como 0.
💬 Travou em algo? Eu sou seu professor neste curso — pergunte. "Por que < estrito e não <= no fold?", "Como o piso de profundidade da ADR-0006 muda a escolha?", "O que acontece se eu fixar um modelId de outro tier?". É só dizer.