Onde a IA realmente entra no Alembic — e por que o sistema trata todo modelo como I/O não confiável. Você vai seguir uma decisão atravessando as cinco camadas — adapters → council → swarm → harness → factory — e aprender a pensar em evals, observabilidade e guardrails antes de soltar qualquer autonomia.
packages/adapters, packages/council, packages/swarm, packages/harness.
Result que nunca lança, park do T4, MCP read-only, budget."Agêntico" não é soltar um agente e torcer. No Alembic é o oposto: contrato, budget, park, verifier e barramento de eventos vêm antes da autonomia. O modelo é a peça mais barata e mais substituível — o valor está no harness em volta dele.
Se um modelo devolve um JSON malformado no meio de uma extração, o que o Alembic faz?
Result de falha (um err) que o chamador trata explicitamente — e a etapa de parse (Zod) tenta recuperar o JSON antes de desistir. Errar é um estado de dado previsto, não um acidente.Um modelo é uma chamada de rede que pode dar timeout, devolver lixo, custar dinheiro ou mudar de comportamento entre versões. Tratá-lo como I/O significa: tipos na entrada e na saída, retry e circuit breaker no meio, e um Result<T, Error> em vez de throw. O @alembic/contracts é dono dos schemas; nenhuma biblioteca lança em código de produção.
Modelos entram por adapters; o julgamento contestado passa pelo council; o trabalho autônomo é particionado pelo swarm; tudo é observado pelo harness; e o próprio Alembic é construído por agentes na factory.
Mapa agêntico: o modelo é a peça que entra por baixo; o harness é a espinha que observa tudo.
Decore estas cinco e você entende 80% do módulo. Cada uma é um pacote real do monorepo, com uma responsabilidade única.
packages/adapterspackages/councilpackages/swarmpackages/harnesspackages/factoryImagine que cada modelo é um fornecedor terceirizado meio instável. Você não deixa um fornecedor assim falar direto com o cliente: você coloca um balcão de atendimento na frente, que valida o pedido, tenta de novo se cair, corta o fornecedor se ele começar a falhar em série, e nunca repassa um erro cru. Esse balcão é o adapter.
Três comportamentos do adapter que sustentam tudo o que vem depois:
Result de falha. O run continua de pé e decide o que fazer com o erro.O Alembic não fixa um modelo gigante para tudo. Ele escolhe por tier (faixa de risco/qualidade) e, dentro do tier, pega o mais barato com pickCheapestForTier. Clique em uma faixa e veja para onde a decisão vai:
err em vez de cair num substituto. "Falhar barulhento" é uma decisão de segurança, não um bug.O wrapper que nunca lança está em packages/adapters/src/adapter-core.ts (retry, circuit, validação). O registro de modelos — tier, custo por 1k tokens de entrada/saída e capacidade — está em packages/contracts/src/registry.ts, junto com pickCheapestForTier. O gateway real é o cliproxyapi (um proxy puro); modelos locais MLX rodam por baixo.
# o caminho offline / $0: nenhum token sai, nenhuma rede é tocada alembic distill <corpus> --offline # quando o gateway está fora do ar, é exatamente este o fallback honesto
Custou caro ou o gateway está indisponível? O caminho --offline usa um adapter determinístico que entrega o resultado por US$ 0, sem fingir que chamou um modelo.
Quando a decisão é ambígua — vale a pena lançar isto? este escopo é viável? — uma única chamada de modelo não basta. O council roda um debate multi-perspectiva e pontua o resultado em cinco eixos fixos, transformando o julgamento em algo inspecionável.
A nota é calculada sobre cinco eixos, cada um de 0 a 10, combinados por pesos fixos:
| Eixo | O que mede | Peso |
|---|---|---|
| feasibility | Conseguimos construir isto de fato? | 0,25 |
| revenue | Há receita plausível do outro lado? | 0,25 |
| cx | A experiência do cliente melhora? | 0,20 |
| ttm | Time-to-market: chega rápido o bastante? | 0,15 |
| risk | Risco — pontuado ao contrário: 10 = risco baixo | 0,15 |
risk (onde 10 = risco baixo). Não existe inversão GO/NO_GO. Aplicar um 1 - risk em qualquer lugar é justamente o que quebra o veredito — por isso os pesos somam 1 e a nota fica entre 0 e 10.Os eixos, os pesos e o computeAggregateScore (puro) estão em packages/council/src/scoring.ts — a fonte única da verdade da ordem canônica dos eixos. O input do modelo é montado em context-pack.ts a partir do contexto e das fases anteriores. O debate com sequenciamento contrário (contrarian) é real, não só um adjetivo no prompt.
# sequência típica de gates num run 1. Scope Gate # copia GOAL.md + plano + contrato para o run-dir 2. Council Gate # GO/NO_GO opcional de pré-flight 3. Proof Gate # roda unit.proof[]; falha fecha o run 4. Validator Gate # escrutínio independente por milestone
O swarm é onde o trabalho autônomo de verdade acontece — e é exatamente por isso que ele é o mais cercado. Autonomia segura precisa de identidade, dependência, profundidade máxima, park e journal durável antes de uma única task rodar.
Nem toda task pode rodar sozinha. Tarefas irreversíveis, com implicação legal ou de segurança, ou marcadas T4, são roteadas para um ledger (o "park") e nunca executam sem um humano. O swarm registra cada uma num journal durável; depois você usa alembic approve, reject ou propose.
O freio de mão do swarm: o que é perigoso demais não roda — espera um humano no ledger.
Uma task "apagar a tabela de produção de billing" chega ao swarm. Para onde ela vai?
classifyPark a desvia para o ledger e ela só prossegue depois de alembic approve por um humano. O swarm nunca executa esse tipo de task por conta própria.O orquestrador está em packages/swarm/src/orchestrator.ts: ele monta a fila, parka o que precisa ser parkado e drena as tasks ready por dependência. A classificação e a entrada de ledger ficam em park.ts (classifyPark, makeParkEntry). O comentário do próprio arquivo é explícito: "irreversible/legal/security/T4 tasks are routed to the ledger and never run". A contagem de profundidade e as semânticas de resume/park são de verdade nesta fatia.
O HarnessCore é o maestro, e ele é neutro em transporte. Ele faz start, poll, fanout e report, e empurra cada acontecimento para um barramento de eventos. Quem assiste — uma página HTTP, um stream SSE, um host MCP, uma futura tela de console — é só uma casca por cima. A lógica de negócio mora num lugar só.
A consequência prática: observabilidade não é UI. A UI é descartável; o que importa é o fluxo de eventos com sequência, lane e snapshots.
alembic serve sobe um servidor com estado; POST /runs aceita goal/plan/contract e os eventos saem como frames.start nem fanout. Um host pode observar, não disparar.alembic runs list, tui <run-id>, tail -f — tudo lê o mesmo events.jsonl.alembic replay <run-id> reconstrói o run a partir dos eventos + cache. O histórico é a fonte da verdade.POST /runs — superfícies sob seu controle.O condutor é packages/harness/src/core.ts (start, poll, fanout, report, event bus). O servidor e os frames SSE estão em server.ts e http.ts; a superfície de tools em mcp.ts, cujo cabeçalho diz: "The MCP READ-ONLY tool surface… there is deliberately no start/fanout tool here". Cada tool valida os args por Zod antes de rodar e devolve um ToolResult tipado; nenhuma lança.
Uma decisão não vira ação porque o modelo disse que sim. Ela atravessa um pipeline de gates, e o Proof Gate falha fechado: comando com saída diferente de zero derruba o run. É aqui que "agêntico com disciplina" deixa de ser slogan.
Copia GOAL.md, o módulo de plano e o contrato para o diretório do run.
GO/NO_GO de pré-flight, opcional. Julgamento contestado antes de gastar.
Roda cada unit.proof[] como comando. Exit ≠ 0 → run falha. Sem perdão.
Escrutínio independente por milestone. O validador nunca é quem construiu.
Gera o curso visual-teach e publica gist/Pages. Só ao convergir.
verifier acompanha council e swarm fazendo a checagem independente; o Validator Gate repete isso por milestone. É retrieval de confiança, não cortesia.Cada string em unit.proof[] vira uma task bash -c que depende da unidade, e os resultados são persistidos em units/<id>/proof-results.jsonl. A prova é um artefato, não uma promessa.
Onde colocar evals? Exatamente nas costuras onde um contrato tipado encontra a saída imprevisível de um modelo. Esses são os pontos onde o sistema pode mentir para si mesmo — e onde um teste paga sozinho.
| Alvo do eval | O que ele protege | Modo de falha que pega |
|---|---|---|
| Recuperação de JSON na extração | O parser conserta JSON malformado em vez de quebrar. | Modelo devolve JSON quase válido e o run cai. |
| Redação de PII | Dados sensíveis são removidos antes de sair. | Vazamento de PII num log ou numa resposta. |
| Limiares de scoring | A nota agregada cruza o limiar certo. | Inversão de risk flipando GO/NO_GO. |
| Claims do verifier | O checker afirma só o que pode provar. | Verifier aprova sem evidência. |
| Retry do adapter | Falha transitória é re-tentada; em série, o circuito abre. | Retry infinito ou desistência cedo demais. |
| Bloqueio por budget | Gasto além do teto é barrado. | Run estoura o orçamento sem ninguém ver. |
| Resume do swarm | Retomar um run não duplica nem perde tasks. | Task roda duas vezes após um resume. |
--offline torna evals herméticos e $0: o adapter determinístico dá saídas estáveis, então você testa o harness (parse, gate, score, park, resume) sem depender de um modelo de verdade. É assim que a suíte fica rápida e reproduzível.Vamos desenhar, do zero, um eval para o ponto mais escorregadio do council — a inversão do eixo de risco.
risk = 9 (risco baixo) e os demais altos. Esperado: nota alta → GO.computeAggregateScore ≥ limiar; veredito = GO. Documente o número exato.1 - risk, o risco baixo viraria penalidade e o GO viraria NO_GO. O teste tem que vermelhar nesse caso.packages/council/src/scoring.ts — a fonte única da ordem dos eixos e dos pesos.pnpm --filter @alembic/council test roda verde. Sem verde, sem mérito.HarnessCore é o condutor neutro em transporte. UI, HTTP e MCP são cascas por cima dele.risk = 10 é risco baixo e soma. Inverter é o que quebra o veredito.packages/harness/src/mcp.ts) não tem tool de start nem fanout — um host pode observar, nunca disparar. Já a CLI e o POST /runs são superfícies sob seu controle e podem iniciar trabalho.Passe pelos cinco cartões — é a lição inteira em síntese. Use as setas do teclado.
Result?Três perguntas. Cada uma tem uma evidência no código que você pode conferir depois.
Result de falha em vez de lançar uma exceção?Result mantém o run vivo e o erro explícito — em vez de derrubar tudo. Veja adapter-core.ts.risk = 10?risk (10 = baixo). Não há inversão; aplicar 1 - risk é o bug clássico. Veja scoring.ts.classifyPark roteia irreversible/legal/security/T4 para o ledger; nada é descartado nem roda sozinho. Depois vem alembic approve/reject/propose. Veja orchestrator.ts + park.ts.