Lição 15 · Curso de Fusão · Parte 3 · A arquitetura · O funil
Parte 3 · A arquitetura · Lição 15

O funil — um ETL de 4 tiers com piso de $0

O funil é como o Alembic transforma um corpus de fontes cruas em business signals validados e em learnings — a um custo que começa em exatamente $0 e só sobe quando um candidato prova que vale pagar por ele. É uma cascata de quatro tiers: T0 determinístico (grátis) → T1 local (quase grátis) → T2 shortlist frontier metered → T3 council + painel verifier. Só um verified-GO emite. Fonte: packages/harness/src/funnel.ts.

Vários blocos têm uma versão Simples e uma Técnica. Abra a técnica quando quiser o código real.
1

A cascata — afunile o gasto, não o corpus


Ao terminar esta lição você vai saber
  • Por que o tier mais barato vê 100% do corpus e cada tier abaixo vê só os sobreviventes.
  • O que cada tier faz — runT0Pipeline, runT1Extraction, runT2Shortlist, runT3Council — e quanto custa.
  • Por que a emissão exige duas travas: GO de consenso e painel aprovado.
  • As três invariantes de segurança que o funil nunca pode regredir: PII, budget, append-only.
  • Por que o orquestrador do funil vive na L4 harness, e não em etl.
Suposições tolas (assumimos pouco de você)
  • Você sabe o que é um corpus: uma pilha de fontes cruas (transcripts, bookmarks, repos, mensagens).
  • Você já viu a ideia de ETL (extrair → transformar → carregar). Aqui ele é em quatro estágios.
  • Você não precisa ter lido a Lição 18 — o painel verifier é só citado aqui e detalhado lá.

A intuição cabe numa frase: o tier mais barato toca o corpus inteiro; cada tier seguinte é mais caro, mas só enxerga quem sobreviveu ao anterior. Assim a curva de custo entorta para o lado certo — você paga preço de fronteira só pelos poucos candidatos que já passaram por dois filtros baratos.

Pense num garimpo de ouro. Você não manda toda a montanha para a fundição. Primeiro peneira toda a terra na água (grátis), depois lava só o que ficou na peneira, depois examina à mão só as pepitas promissoras, e só então funde o pouquíssimo que sobrou. O funil faz isso com gasto de modelo: peneira tudo de graça e só queima dinheiro no fim.
Funil de quatro faixas que estreita de T0 (100% do corpus, $0) até T3 (council + painel, metered), com selos de custo crescente à direita e uma gota verified-GO que emite no bico

A cascata: a faixa mais larga (T0) vê 100% do corpus por $0; cada faixa abaixo vê só os sobreviventes, e o custo sobe enquanto o volume cai. Só um verified-GO sai pelo bico.

T0 · walk determinístico → SHA-256 dedupe → contract-validate → score 6-dim → residue 100% do corpus $0 T1 · um BusinessSignal por item de residue · adapter LOCAL (free tier) ~$0 T2 · shortlist FRONTIER gated por budget refina os T1 mais fortes metered T3 · council + painel N-lens metered só um verified-GO (GO ∧ painel aprovado) sobrevive até o fundo e emite
O custo é metered apenas dos T2/T3 para baixo — o corpus inteiro passou de graça por T0/T1.
Primeiro, um palpite

Um corpus tem 10.000 itens. Quantos, mais ou menos, chegam ao tier pago T2?

Só os mais fortes sobreviventes de T0+T1. T0 (grátis) toca os 10.000; T1 (local free-tier) extrai um sinal por item de residue; T2 (metered) refina em lotes apenas os T1 mais fortes. O funil estreita o gasto, não o corpus — você paga fronteira só por quem já passou por dois filtros baratos.
2

Os quatro tiers, lado a lado


TierO que fazCusto
T0runT0Pipeline: walk → SHA-256 dedupe → contract-validate → score de 6 dimensões → emite residue, sobre 100% do corpus (excluindo Repos/Models + Repos/Prompts)$0
T1runT1Extraction: um BusinessSignal por item de residue via o adapter LOCAL injetado; free-tier, então na prática nunca é bloqueado por budget~$0
T2runT2Shortlist: uma shortlist FRONTIER gated por budget refina os sinais T1 mais fortes em lotes; toda call paga é meteredmetered
T3runT3Council: um council sintético de 3 membros (optimist/analyst/pessimist, atende MIN_VALID_AGENTS=3) + o painel verifier N-lensmetered
Dentro do T0 — tudo determinístico, tudo $0 walk o corpus SHA-256 dedupe contract- validate score 6 dimensões residue a saída do T0 excluindo Repos/Models + Repos/Prompts · runT0Pipeline vive em @alembic/etl (puro) nenhum modelo é chamado aqui — é a peneira grátis
O T0 é cinco passos puros sobre o corpus inteiro; sua saída, o residue, é o que o T1 vai extrair.
O custo por tier — sobe só quando o candidato já provou valor T0 $0 · 100% do corpus T1 ~$0 · adapter LOCAL, um sinal por item T2 metered · shortlist frontier dos T1 mais fortes T3 metered · council + painel N-lens
As barras crescem para baixo: cada tier custa mais, mas vê muito menos — o gasto afunila.
Flashcard · T0
O que T0 emite, e a que custo?
clique para virar
Residue: walk determinístico → SHA-256 dedupe → contract-validate → score de 6 dimensões, sobre 100% do corpus, por $0. É runT0Pipeline, puro, em @alembic/etl.
Flashcard · T1
Por que T1 quase nunca é bloqueado por budget?
clique para virar
Porque usa o adapter LOCAL injetado (free-tier): um BusinessSignal por item de residue sem call paga. O gasto real só começa em T2.
Flashcard · T3
Quem forma o council de T3?
clique para virar
Três membros sintéticos — optimist / analyst / pessimist — atendendo MIN_VALID_AGENTS=3, somados ao painel verifier N-lens.
O score de 6 dimensões do T0 — 0–5 por eixo, 0–30 no total eixo 1 0–5 eixo 20–5 eixo 30–5 eixo 40–5 eixo 50–5 Σ total0–30 o score ordena o residue — os mais fortes é que sobem para o T2 metered
Seis eixos de 0 a 5 (0–30 no total) dão a cada item de residue uma força — e a força é o que decide quem chega a pagar fronteira.
3

O sinal verified-GO — duas travas, não uma


Um resultado de T3 só emite quando as duas coisas acontecem: a decisão de consenso é GO e o painel N-lens aprovou a emissão (verified, não parked). Um GO sozinho não basta.

É como liberar um pagamento alto que exige duas assinaturas. Uma sozinha não move o dinheiro — precisa do gerente e do auditor. Aqui: o consenso assina GO, e o painel independente assina "verificado". Falhou uma? Nada sai.
trava 1 consensus.decision === 'GO' trava 2 isPanelEmissionApproved(report) painel N-lens: verified, NÃO parked E emite → FunnelReport.verifiedSignals opportunity edges + learnings (PII-safe) qualquer trava falha ⇒ nada emitido
A emissão é uma conjunção de duas travas independentes — consenso e verificação.
A tabela-verdade da emissão consensus painel emite? GOaprovado SIM ✓ GOparked não NO_GOaprovado não NO_GOparked não
Uma única linha emite — a primeira. É a definição de conjunção: tudo o mais é silêncio.

O funil exige consenso e verificação independente antes de gastar esforço a jusante ou emitir qualquer coisa para fora. Um GO com painel parked não emite — fica retido.

// packages/harness/src/funnel.ts (condensado) — o gate de emissão
const verified =
  consensus.decision === 'GO' &&
  isPanelEmissionApproved(report);     // painel N-lens: verified, NÃO parked
if (verified) {
  // emite opportunity edges + learnings; aparece em verifiedSignals (PII-safe)
}

Os BusinessSignal[] verified-GO aparecem em FunnelReport.verifiedSignals — a ponte PII-safe para a marketing factory (distillAndMarket). A Lição 18 cobre o painel; o ponto aqui é que o funil exige consenso e verificação independente antes de qualquer gasto ou emissão.

Guarde istoverified-GO = GO ∧ painel aprovado. Duas travas, não uma. É o único caminho de saída do funil.
4

Três invariantes de segurança que o funil nunca pode regredir


Um portão de duas travas (GO E painel-aprovado) que emite em verifiedSignals ou bloqueia, ao lado de três cartões de invariantes: PII antes do egress, budget fail-closed e append-only

As duas travas do verified-GO à esquerda; as três invariantes que protegem o funil à direita — PII antes do egress, budget fail-closed e writes append-only.

① PII antes do egress

Um sinal vindo de um canal PRIVADO (whatsapp, discord, skool, circle) é redigido antes do model call (extractionInput redige) e gated de novo por assertRedactedForEmit antes de qualquer write (emitSafeSignal). Um sinal de canal privado não redigido é descartado, nunca emitido. FunnelReport.t1PiiBlocked é um alarme diferente de zero. Governado pela ADR-0011.

canal privado whatsapp… redige extractionInput model call T1 / T2 / T3 re-checa assertRedactedForEmit write ✓ não redigido ⇒ dropado · t1PiiBlocked++
Defesa em profundidade: a PII é redigida antes do modelo ver e re-checada antes de qualquer write — duas barreiras, não uma.
② Budget fail-closed

Toda call paga (T2/T3) é embrulhada num BudgetGuard.check fail-closed antes do dispatch; uma violação projetada bloqueia a call e o tier degrada em vez de estourar o orçamento. O preço sempre usa a tier rate do registry (pricingModelId), nunca um override de catálogo — então um modelo de gateway sobrescrito, fora do registry, ainda é metered contra o cap pela tier rate dele. Você não consegue contornar o budget por acidente.

vou fazer uma call paga T2 ou T3 BudgetGuard.check custo projetado pela pricingModelId cabe ⇒ chama e mede o gasto estoura ⇒ bloqueia, degrada cap não-positivo (≤ 0) ⇒ só free tier passa
O guard projeta o custo antes de gastar. Sem orçamento, a call nem acontece — fail-closed, não estouro.
③ Append-only

Os resultados fluem para os dois stores via writes append-only, content-addressed e schema-validated; as leituras das fontes permanecem read-only. As duas saídas são o grafo de oportunidades de NEGÓCIO (Business/opportunity-graph.jsonl) e o store de LEARNINGS (Skills/learning/learnings.jsonl) — as duas cadeias de valor da ADR-0002.

verified-GO o que sai do funil NEGÓCIO Business/opportunity-graph.jsonl LEARNINGS Skills/learning/learnings.jsonl append-only · content-addressed · schema-validated · fontes ficam read-only (ADR-0002)
Uma entrada, duas cadeias de valor — e ambas só crescem (append-only), nunca sobrescrevem.
① PII antes do egress redige antes do call re-checa antes do write não redigido ⇒ dropado ② budget fail-closed BudgetGuard.check preço = tier rate do registry cap ≤ 0 ⇒ só free tier ③ append-only content-addressed opportunity-graph.jsonl learnings.jsonl três guardas independentes — nenhuma pode regredir
PII, budget e append-only: três guardas que protegem o que entra no modelo, o que se gasta e o que se escreve.
Por que o funil vive na L4 harness, e não em etl. Ele orquestra adapters L1 + council L2 + etl L0. Pôr em etl forçaria etl a depender para cima de adapters/council, invertendo o grafo de camadas. Então o T0 determinístico fica em @alembic/etl (puro, só contracts) e o orquestrador que chama T0→T3 vive em @alembic/harness. A camada é preservada por onde o código mora, não por convenção.
se o funil morasse em etl (errado)
etl (L0) adapters (L1) council (L2) L0 apontando para cima — proibido
funil na harness (certo)
harness (L4) · funil etl (L0) adapters council só dependências para baixo — válido
5

Exemplo guiado — um item atravessando o funil


Vamos seguir um item — um transcript do YouTube — descendo a cascata até decidir se emite.

runFunnel · um item de ponta a ponta
1
T0 ($0). O walk determinístico encontra o transcript, calcula seu SHA-256 (não é duplicata), valida contra o contrato e dá um score de 6 dimensões. Ele entra no residue.
2
T1 (~$0). runT1Extraction roda o adapter LOCAL sobre o item de residue e produz um BusinessSignal. Sem call paga; nenhum budget tocado.
3
T2 (metered). O sinal está entre os mais fortes do lote, então runT2Shortlist o seleciona. Antes do dispatch, BudgetGuard.check projeta o custo pela pricingModelId (tier rate do registry). Cabe no cap → a call FRONTIER refina o sinal.
4
T3 (metered). O council 3-membros vota e chega a GO; o painel N-lens roda. Se o painel aprovar (verified, não parked), verified é verdadeiro.
5
Emissão. Como o item NÃO veio de canal privado, assertRedactedForEmit passa. O sinal é escrito append-only no opportunity-graph.jsonl e o learning no learnings.jsonl, e aparece em FunnelReport.verifiedSignals.
Agora você. Refaça os mesmos cinco passos para um item vindo do whatsapp cujo painel parkou uma lente. Em que passo exato ele para? Dica: há duas razões possíveis para nada ser emitido — uma no passo 4 (a trava do painel) e uma no passo 5 (a trava de PII, se a redação falhasse). Identifique qual dispara primeiro neste caso.
6

Confusões comuns


"Offline significa resultado degradado." Para T0/T1 significa hermético, determinístico e $0 — os tiers locais são o piso do design, não um fallback. alembic distill <corpus> --offline roda o pipeline inteiro com um registry de adapter offline e nunca toca uma API paga.
"Dá para furar o cap de budget com um override de modelo." Não — o preço sempre resolve pela tier rate do registry (pricingModelId), então até um modelo de gateway sobrescrito é metered pelo seu tier. O budget guard vê o custo projetado independentemente do nome concreto de modelo que foi pinado.
"T2 vê o corpus inteiro." Não. T0 vê 100%; T1 extrai um sinal por item de residue; T2 é uma shortlist gated por budget só dos T1 mais fortes. É justamente o estreitamento do gasto.
CuidadoUm GO de consenso não é emissão. Sem isPanelEmissionApproved(report) verdadeiro, o sinal fica retido e nada chega a verifiedSignals.
7

Recapitulando


Recap · 1 de 6

Afunile o gasto, não o corpus

O tier mais barato vê 100% do corpus; cada tier abaixo vê só os sobreviventes. A curva de custo entorta para o lado certo.

100% · $0os mais fortes · metered
01

Recap · 2 de 6

Quatro tiers

T0 $0 determinístico → T1 ~$0 local → T2 shortlist frontier metered → T3 council + painel N-lens.

T0 $0T1 ~$0T2T3
02

Recap · 3 de 6

Duas travas para emitir

verified-GO = GO ∧ painel aprovado. Um GO com painel parked não emite. Saída única do funil.

GOpainel aprovadoEemite
03

Recap · 4 de 6

PII antes do egress

Canais privados redigidos antes do call e re-checados antes do write. Não redigido ⇒ dropado. t1PiiBlocked é alarme. ADR-0011.

privadoredigere-checa
04

Recap · 5 de 6

Budget fail-closed

BudgetGuard.check antes de cada call paga; preço pela tier rate do registry (pricingModelId). Cap ≤ 0 ⇒ só free tier.

checkcabe ⇒ chamaestoura ⇒ bloqueia
05

Recap · 6 de 6

Append-only, na L4

Saídas para opportunity-graph.jsonl + learnings.jsonl (ADR-0002). O orquestrador vive na harness; o T0 puro fica em etl.

verified-GOopportunity-graphlearnings
06
Slide 1 / 6 navegam
Em uma frase: o funil peneira tudo de graça e só queima dinheiro no fim, e o pouco que sobra só emite com consenso e verificação — sem nunca furar PII, budget ou o append-only.
8

Verifique seu entendimento


Três perguntas — sua pontuação aparece embaixo
1. Um corpus tem 10.000 itens. Quantos, mais ou menos, chegam ao tier pago T2?
Correto: b. T0 (grátis) toca 100%; T1 (local free-tier) extrai um sinal por item de residue; T2 (metered) refina em lotes só os T1 mais fortes. O funil estreita o gasto, pagando fronteira só por quem já passou por dois filtros baratos.
2. O consenso é GO, mas o painel N-lens parkou uma lente. O funil emite o sinal?
Correto: d. O verified-GO é uma conjunção de duas travas independentes: GO de consenso e um painel aprovado (verified, não parked). Falhou uma trava, nada vai para verifiedSignals.
3. Um sinal de origem WhatsApp chega ao passo de emissão ainda não redigido. O que acontece?
Correto: a. PII é redigida antes do model call e re-checada antes de qualquer write. Um sinal de canal privado não redigido nunca é escrito; o contador o sinaliza. Fail-closed, não crash — invariante 1 do funil.
Acertos: 0/3
As cinco coisas para levar do funil
  1. O tier mais barato vê 100% do corpus; o gasto afunila, o corpus não.
  2. Quatro tiers: T0 $0 → T1 ~$0 → T2 metered → T3 council + painel.
  3. Emitir exige duas travas: GO de consenso e painel aprovado.
  4. Três invariantes intocáveis: PII antes do egress, budget fail-closed, writes append-only.
  5. O orquestrador vive na L4 harness; o T0 puro fica em @alembic/etl — a camada vem de onde o código mora.
A fonte para ler
packages/harness/src/funnel.ts — os quatro tiers, o gate verified-GO, a redação de PII, o pricing de budget pelo registry e o tipo FunnelReport com seus contadores. Os deep dives do funil estão nas Lições 16 (invariantes) e 18 (council + verifier).