Um motor que distribui uma requisição a muitos modelos precisa de dois botões ortogonais: autonomia (quanta supervisão humana um trabalho exige) e custo (quanto dinheiro uma chamada de modelo 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 os conecta: 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".
O enum Tier tem exatamente cinco degraus, autonomia-mais-barata primeiro. Eles são sobre supervisão humana, não dinheiro:
| Tier | Significado | Autônomo? |
|---|---|---|
| T0 | silencioso / totalmente autônomo, sem humano no loop | sim (o caminho silencioso) |
| T1 | autônomo com logging leve | sim |
| T2 | autônomo, um revisor notificado | sim |
| T3 | autônomo, revisão de council exigida | sim |
| T4 | PARK — retido da execução autônoma; precisa de council + humano | não |
Dois fatos tornam isto fail-closed: DEFAULT_TIER = T4 (trabalho não classificado estaciona, Lição 26) e isAutonomous retorna true só para T1–T3 (tier.ts:59). Separadamente, LOCAL não é um sexto tier — é um marcador ortogonal marcando trabalho que deve ficar em modelos local/$0 "independente do tier" (caminhos sensíveis a privacidade ou custo, tier.ts:36). Uma unidade pode ser T2 e LOCAL.
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 (o mapa registra isso; valores representativos mostrados):
// 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 pelos tiers (conforme o complete-map §6): 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 T0 em $0 para CI hermético. (Ids/preços de modelos são dados ilustrativos do registro e evoluem; a forma é o contrato.)
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:
// packages/contracts/src/registry.ts:145-156 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; }); };
O roteamento decide qual modelo; o BudgetGuard decide se a chamada pode acontecer. Ele é criado com um teto rígido em USD e três métodos — e seus defaults são deliberadamente paranoicos:
// packages/etl/src/budget.ts:120-152 (condensado) export const createBudgetGuard = (capUsd) => { const cap = Math.max(0, capUsd); // um 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, … }; // 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; }, }; };
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, que é por que o funil inteiro pode rodar $0 e hermético no adapter LOCAL (Lição 15).
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.
createBudgetGuard(0) (ou um teto negativo). O que pode rodar?cap = Math.max(0, capUsd) vira 0, e check passa uma chamada só se projectedUsd === 0 ou se ela cabe no teto. Com teto 0, só chamadas grátis (LOCAL/T0) passam — fail-closed: o default não gasta nada.modelId fixado, qual pickCheapestForTier(T2) retorna, e a escolha é determinística?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).ModelRunResult de um modelo free-tier carrega um costUsd: 0.01 perdido. Como record o mede?costOf (budget.ts:109-111) 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 medição é autoritativa, não consultiva.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.