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.
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.
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
Tier
O que faz
Custo
T0
runT0Pipeline: walk → SHA-256 dedupe → contract-validate → score de 6 dimensões → emite residue, sobre 100% do corpus (excluindo Repos/Models + Repos/Prompts)
$0
T1
runT1Extraction: um BusinessSignal por item de residue via o adapter LOCAL injetado; free-tier, então na prática nunca é bloqueado por budget
~$0
T2
runT2Shortlist: uma shortlist FRONTIER gated por budget refina os sinais T1 mais fortes em lotes; toda call paga é metered
metered
T3
runT3Council: um council sintético de 3 membros (optimist/analyst/pessimist, atende MIN_VALID_AGENTS=3) + o painel verifier N-lens
metered
O T0 é cinco passos puros sobre o corpus inteiro; sua saída, o residue, é o que o T1 vai extrair.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.
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 é GOe 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.
A emissão é uma conjunção de duas travas independentes — consenso e verificaçã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ãoconst verified =
consensus.decision === 'GO' &&
isPanelEmissionApproved(report); // painel N-lens: verified, NÃO parkedif (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
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.
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.
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.
Uma entrada, duas cadeias de valor — e ambas só crescem (append-only), nunca sobrescrevem.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)
funil na harness (certo)
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 umBusinessSignal. 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.
01
Recap · 2 de 6
Quatro tiers
T0$0 determinístico → T1 ~$0 local → T2 shortlist frontier metered → T3 council + painel N-lens.
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.
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.
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.
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.
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
O tier mais barato vê 100% do corpus; o gasto afunila, o corpus não.
Quatro tiers: T0 $0 → T1 ~$0 → T2 metered → T3 council + painel.
Emitir exige duas travas: GO de consenso e painel aprovado.
Três invariantes intocáveis: PII antes do egress, budget fail-closed, writes append-only.
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).