No caminho da nossa unidade de trabalho — pedido → tier → adapter → gates → custo → park/ship — esta lição abre o passo que decide o que entra na janela de contexto antes da chamada do modelo. Você vai entender o pack de 8 camadas, por que o Alembic indexa mas não recupera (o gap do RAG), por que a memória é composta e não recuperada, e quando RAG é a ferramenta errada.
Esta lição destila a Parte III do mapa — o gap principal (retrieval) — contra três arquivos reais do repo: council/src/layered-context-pack.ts, embeddings/src/{offline-backend,index-builder}.ts e hermes/src/memory/multi-store/composer.ts. Importa para a MISSÃO porque contexto é a maior alavanca depois que o modelo está fixo — e saber onde o "RAG" do Alembic para é saber onde o roadmap começa.
Leia a versão simples, ou abra a camada técnica em qualquer seção.
1
A grande ideia
A janela de contexto é finita. A pergunta da engenharia de contexto não é "o que o modelo sabe?" — é o que cabe na janela, em que ordem, dentro de um orçamento. Essa é a maior alavanca de qualidade depois que o modelo está escolhido. O Alembic trata isso a sério em uma camada (o pack L0–L7) e, com honestidade, não trata em outra (o retrieval vetorial).
Ao fim desta lição você consegue
Explicar as 8 camadas do pack de contexto (L0→L7) e por que "orçamento" ali é comparação, não corte.
Nomear o gap principal do Alembic: ele indexa embeddings mas não recupera (não há query vetorial, topk nem rerank).
Entender por que o vetor offline é um hash de char-codes de 16 dimensões — e por que isso não é semântico.
Distinguir memória composta (lida das stores ligadas) de memória recuperada (por similaridade) — e por que uma store não-ligada nunca vaza.
Escolher entre fine-tune · in-context · RAG · destilação — e reconhecer quando RAG é a ferramenta errada.
Suposições tolas (o que presumimos de você)
Você sabe que um LLM recebe um texto de entrada (o "contexto") e devolve texto. Só isso.
Você não precisa saber o que é "embedding", "vetor" ou "cosseno" — a lição constrói cada termo.
Você leu (ou viu de relance) as lições 0001–0005: a unidade de trabalho que atravessa o harness é o nosso fio condutor.
Pense na nossa unidade de trabalho chegando no harness do Alembic. Antes de gastar um token, alguém precisa montar a "ficha" que o modelo vai ler: a tarefa em uma linha, o mapa do repositório, os arquivos certos, trechos relevantes, resumos, o histórico do debate, os artefatos e um inventário com hash. Isso é o pack de contexto. Não é "jogar tudo no prompt" — é estratificar, do mais essencial ao mais volumoso, para poder descartar de baixo pra cima quando o orçamento aperta.
Pense como… preparar uma pasta para um advogado substituto que vai pegar o caso amanhã: a capa com o pedido (1 linha), o índice dos autos, as peças inteiras que importam, os trechos grifados, os resumos, o andamento, os anexos e a lista carimbada de tudo. Se a pasta não cabe na mochila, você tira os anexos volumosos primeiro — nunca a capa. Onde a analogia quebra: a pasta jurídica é montada por uma pessoa que entende o caso; o pack do Alembic é montado por código determinístico que mede tamanho com uma régua grosseira (≈4 caracteres por token), não com leitura.
As três peças desta lição, e a etiqueta honesta de cada uma. O pack de contexto é owned (o Alembic implementa). O retrieval vetorial é gap (não construído). A memória multi-store é owned — mas composta, não recuperada, então a parte de "recuperar por similaridade" também é gap. Essa honestidade é o valor do curso: separar o que é confiável hoje do que é roadmap.
Esta lição vive nas caixas Harness (o pack que monta o contexto) e Context (a memória). Repare: Context é "owned, mas composto" — o ponto da §4.
Por baixo do capô
"Engenharia de contexto" no mapa é o princípio #2. O Alembic o realiza com buildLayeredContextPack (packages/council/src/layered-context-pack.ts): uma função pura que recebe brief, repoMap, arquivos, snippets, resumos, estado de debate, artefatos/constraints e um manifesto com hash de conteúdo, valida tudo com Zod e devolve um Result — nunca lança. O id do pack e do manifesto são SHA-256, então o bundle é reprodutível a partir do hash.
O "RAG" de hoje (princípio #13) é, no mapa, o gap principal: o subsistema @alembic/embeddings tem o lado de escrita (constrói índices) mas não o de leitura (não há cosine/topK/rerank/queryIndex em nenhum arquivo de packages/ — um grep confirma vazio). Logo, "RAG" hoje = o funil de destilação offline + o pack de contexto montado à mão, não recuperação por similaridade.
An educational diagram of an eight-tier pyramid of context layers labeled L0 at the narrow top widening to L7
2
O pack de 8 camadas (L0→L7)
O pack estratifica o contexto do mais estreito (L0, uma linha) ao mais largo (L7, o manifesto inteiro com hash). A ideia: sob pressão de orçamento, um chamador pode jogar fora as camadas de baixo valor — de baixo pra cima — mantendo o pack reproduzível pelo hash. É a "pirâmide" do contexto.
Da nossa unidade de trabalho: L0 é o pedido; L7 é o carimbo. Largura = volume de tokens. Aperta o orçamento → some L7, depois L6… L0 é a última a cair.
As camadas, em uma frase cada
Camada
O que carrega
L0
brief — a tarefa em uma linha
L1
repoMap — lista de caminhos
L2
arquivos inteiros (verbatim)
L3
snippets + um reason de por que importam
L4
resumos comprimidos
L5
estado de debate/fase
L6
artefatos (referência) + constraints
L7
manifesto: cada arquivo com SHA-256, tamanho, nº de linhas
Por que estratificar (e não "jogar tudo")
Um prompt achatado não te deixa escolher o que sacrificar. Estratificado, o descarte vira política: "se estourar, tire L7, depois L6". E o L7 sendo hash de conteúdo, qualquer byte que muda em qualquer arquivo muda o id do manifesto — detecção de mudança grátis, e reprodutibilidade pelo hash.
owned Um primitivo de contexto endereçável por conteúdo e ciente de orçamento. CLI: alembic context-pack.
Endereçamento por conteúdo: o id do manifesto (e do pack) deriva dos hashes dos arquivos, ordenados por caminho. Reproduzível pelo hash, e qualquer alteração é visível.
Orçamento é uma comparação, não um corte
Este é o ponto mais incompreendido. O pack não trunca nada sozinho. Ele mede o custo estimado, compara com o orçamento e te entrega os dois números mais um booleano withinBudget. Você decide: falhar fechado ou descartar camadas. O harness te dá o velocímetro; quem pisa no freio é o chamador.
Faça sua aposta antes de revelar
O orçamento é em tokens. Como o pack estima quantos tokens um bundle gasta, sem chamar um tokenizer de verdade?
Ele serializa o valor em JSON e divide o comprimento por 4: Math.ceil(JSON.stringify(x).length / 4), no mínimo 1. É uma régua grosseira (≈4 caracteres por token) — ótima para decidir o que descartar, ruim para encaixe exato. O próprio mapa marca isso como uma nuance: a decisão de shedding é confiável; o packing exato não.
Brinque com a política de descarte abaixo. Cada camada tem um peso de tokens fictício; clique para tirar/pôr e veja o medidor cruzar (ou não) o orçamento.
Demo: o pack mede e compara; você escolhe descartar. Repare que L0 (o brief) é minúsculo e L7 (o manifesto) é o mais pesado — exatamente por isso o descarte começa por baixo.
O cálculo do orçamento, do arquivo real
packages/council/src/layered-context-pack.ts
// estima tokens pela serialização JSON (~4 chars/token), no mínimo 1export const estimateTokens = (value: unknown): number =>
Math.max(1, Math.ceil(JSON.stringify(value).length / 4));
// ... dentro de buildLayeredContextPack:const estimatedTokens = estimateTokens(layers);
tokenBudget: {
estimatedTokens,
maxTokens: input.tokenBudget,
withinBudget: estimatedTokens <= input.tokenBudget, // <- comparação, não corte
},
Note: o pack carrega os dois números e o booleano e devolve. Nenhuma camada é removida aqui. "Fail closed ou shed layers" é decisão do chamador. E selectTokenBudget resolve o teto por modelo > papel > default — orçamentos diferentes para modelos diferentes.
3
O gap do retrieval (o titular)
Agora o ponto mais honesto do curso. Quando as pessoas dizem "RAG" (Retrieval-Augmented Generation), imaginam: fatiar um corpus em pedaços, transformar cada pedaço em um vetor, e na hora da pergunta buscar os pedaços mais parecidos com a pergunta, ordenar e enfiar no contexto. São duas metades: escrever o índice e ler o índice.
O Alembic tem só a primeira metade. Ele constrói índices de embeddings (buildChunkIndex → writeChunkIndex, append-only, dedupe por chunk_id). Mas não existe lado de leitura: nenhum cosine, topK, rerank, queryIndex ou knn em packages/. Ele indexa, mas não recupera.
O índice é construído e fica no disco — mas nada nunca o lê para responder. A linha tracejada vermelha é o roadmap, não o código.
Atenção — o detalhe que mais confundeOs vetores offline não são semânticos. No caminho offline (o default, $0, hermético), o vetor de um chunk é um hash polinomial de 16 dimensões derivado só dos códigos dos caracteres da string. É um pseudo-vetor: estável e reproduzível (serve para encanar índice, cache, testes), mas não mede significado. Dois textos com o mesmo sentido e palavras diferentes geram vetores distantes. Um vetor semântico de verdade só vem do caminho --online (fetch-backend.ts). Ou seja: offline, mesmo que existisse uma query, não haveria nada semanticamente significativo para recuperar.
Embedding semântico (de verdade)
"cachorro" e "cão" ficam perto no espaço vetorial; "cachorro" e "planilha" ficam longe. A proximidade mede sentido. É o que o caminho --online produz.
Pseudo-vetor offline (char-code hash)
"cachorro" e "cão" ficam longe (caracteres diferentes); "cachorro" e "carchoro" (typo) podem ficar perto. A proximidade mede bytes, não sentido. Serve de impressão digital, não de busca.
Mesmo par de palavras, dois espaços. À esquerda a distância significa algo; à direita ela só reflete os caracteres. Por isso o backend offline avisa, no próprio cabeçalho, que não é um embedding semântico.
Veja o backend offline declarando isso ele mesmo — o cabeçalho do arquivo é a fonte mais honesta:
packages/embeddings/src/offline-backend.ts
/**
* O vetor NÃO é um embedding semântico real — é uma impressão digital
* estável e reproduzível, adequada para encanamento, cache e índice sob
* condições herméticas. Nunca lança através da fronteira.
*
* Derivação: para cada um dos OFFLINE_DIMENSION (=16) slots, dobra cada
* char code com um multiplicador por-slot e a posição corrente em um
* acumulador unsigned de 32 bits (hash polinomial), depois espreme em [-1,1).
*/export const OFFLINE_DIMENSION = 16; // 16 dimensões, fixas
16 componentes, cada um um squash de um acumulador de 32 bits por-slot. Acima/abaixo de zero = sinal do componente. É uma impressão digital estável — não um mapa de sentido.O lado de escrita que existe: writeChunkIndex, append-only, dedupe por chunk_id. Note o que falta na figura: nenhuma seta de volta "ler para responder".Um efeito colateral curioso do gap: sem recuperador, não há "retrieval velho" para dar errado. A frescura vira responsabilidade do corpus (append-only + re-sync), não de um índice de busca.
Por que isto é uma decisão, não um descuido. O Alembic é uma camada de orquestração. Construir o índice é encanamento barato e determinístico; o lado de leitura (query vetorial, rerank, freshness) é um subsistema inteiro. O mapa o lista como o gap nº 1 exatamente para não fingir que existe. "RAG hoje" no Alembic = o funil de destilação (corpus → LEARNINGS/SIGNALS) + o pack de contexto montado à mão. owned construir índice · gap recuperar.
An educational two-row diagram contrasting the two halves of retrieval-augmented generation. Top row, drawn so
4
Memória: composta, não recuperada
O Alembic tem memória multi-store: cinco "gavetas" em JSONL append-only — episodic (o que aconteceu), semantic (conceitos), procedural (procedimentos), decision (decisões) e transcript (conversas). Mas repare na palavra-chave: a memória é composta no prompt, não recuperada por similaridade. O composeMemoryContext lê cada gaveta, filtra por pisos (salience/confidence), ordena por data (mais recente primeiro) e corta por um limite por gaveta. Em momento nenhum ele ordena por "parecido com a pergunta".
A diferença prática: a memória do Alembic te dá o recente, não necessariamente o mais pertinente ao tópico de agora. Para o segundo, faltaria o lado de leitura do §3.Da nossa unidade: se ela for tocada por um AI Employee, é assim que a memória dele vira contexto. Filtra → ordena por recência → corta. Determinístico: re-rodar sobre as mesmas gavetas dá um bundle byte-idêntico.
"Composta" quer dizer: o sistema junta os registros mais recentes e relevantes de cada gaveta e os escreve no prompt. "Recuperada" (que o Alembic não faz aqui) quer dizer: o sistema buscaria os registros mais parecidos com a tarefa atual, por similaridade vetorial. A diferença prática: a memória do Alembic te dá o recente, não necessariamente o mais pertinente ao tópico de agora.
O composeMemoryContext é uma função pura async (não uma classe). As cinco stores entram por trás de uma porta estreita RecordReader (só .query(BaseQuery) é exigido), então produção injeta logs reais e teste injeta fakes em memória. Nunca lança: uma store que falha degrada para vazio ("memória nunca quebra a chamada"). O taskHint opcional vira um filtro search de string nas reads semantic/procedural — não uma query vetorial. Ranking: sort((a,b) => (b.at - a.at) || (a.id < b.id ? -1 : 1)) — data desc, id como desempate estável.
Cada gaveta tem seu piso e seu teto: episodic (sal.≥0.2, max 10), semantic (conf.≥0.3, max 10), procedural (max 5), decision (max 5), transcript (max 20). Filtra → ordena por data → corta.
Uma gaveta não-ligada nunca vaza
Cada AI Employee declara quais gavetas ele usa. Se ele não declarou "transcript", o sistema não constrói um leitor para "transcript". Não há o que consultar — então não há como vazar. A proteção não é um filtro que poderia ser burlado; é ausência estrutural: sem leitor, sem leitura. (É a mesma postura do MCP read-only: capacidade por ausência.)
resolveMemoryBinding devolve só as stores declaradas. As demais recebem um leitor vazio (retorna []) — contribuem nada, vazam nada.
O chamador (a camada de run) constrói leitores apenas para stores. Para as gavetas de fora, passa um leitor vazio ao composeMemoryContext. O composer lê exatamente as MemoryStores que recebe — uma gaveta sem registros (ou sem leitor) some do prompt. Sem binding nenhum, a memória inteira é um no-op e a chamada segue normal.
5
Fine-tune · in-context · RAG · destilação
Quatro formas de fazer o modelo se comportar do jeito que você precisa. Não são intercambiáveis — cada uma resolve um problema e é a ferramenta errada para os outros. O Alembic é quase inteiramente in-context por decisão de arquitetura.
As quatro, em uma frase
Estratégia
O que muda
Fine-tune
os pesos do modelo (treino)
In-context
o prompt (exemplos/instruções na janela)
RAG
o prompt, mas preenchido por busca em tempo real
Destilação
treina um modelo menor (aluno) a partir de um maior
Onde o Alembic fica
ownedIn-context: pack L0–L7, memória composta, soul + skills do AI Employee. É a aposta dominante.
delegatedFine-tune: de quem treina os pesos (gateway/MLX). Fora da cintura.
gapRAG: índice construído, nunca lido (§3).
gap*Destilação de modelo: não feito. Cuidado: o Alembic usa "distill" para destilar um corpus (T0→T3), não um modelo — palavra sobrecarregada.
Armadilha de vocabulárioNo Alembic, alembic distillnão é destilação de modelo (ML). É o funil de destilação de conhecimento: pega um corpus e o destila em LEARNINGS + SIGNALS. Mesma palavra, técnica completamente diferente. Quando alguém disser "o Alembic faz distillation", pergunte de quê.
Árvore de decisão. A pergunta certa primeiro: o conhecimento muda? é específico? cabe em poucos exemplos? precisa ser menor?A aposta é clara: o Alembic é quase todo in-context. Fine-tune é delegado; RAG existe só pela metade; destilação de modelo não existe. Uma aposta coerente para uma camada de orquestração sobre modelos hospedados.
Exemplo guiado — escolhendo a ferramenta para a nossa unidade
1
Tarefa: a unidade precisa redigir uma petição no estilo de um escritório específico, citando teses recentes daquele escritório.
2
O estilo é estável e cabe em exemplos? Sim → in-context (soul + skills do AI Employee). Não justifica fine-tune.
3
As teses recentes mudam e são específicas do escritório? Sim → o caso de uso de RAG. Mas no Alembic hoje isso é gap: você as traria via funil de destilação ou as colocaria à mão no pack (L2/L3), não por busca vetorial.
4
Agora você tenta: e se a unidade tivesse que rodar 10.000×/dia, barata e com comportamento idêntico? Qual estratégia entra em cena? (Dica: pense em treinar um aluno menor.)
flashcard
Por que o orçamento do pack é "comparação, não corte"?
clique para virar
O pack mede o custo estimado, compara com o teto e devolve os dois números + withinBudget. Quem descarta camadas (ou falha fechado) é o chamador — o pack nunca trunca sozinho.
flashcard
Qual a "metade" do RAG que o Alembic não tem?
clique para virar
A metade de leitura: embed-da-query + cosine/topK + rerank. Ele constrói o índice (escrita) mas não há query vetorial em packages/.
flashcard
Vetor offline: semântico ou não?
clique para virar
Não. É um hash polinomial de 16 dimensões dos char-codes. Estável e reproduzível, mas mede bytes, não sentido. Semântico só no caminho --online.
flashcard
Memória: composta ou recuperada?
clique para virar
Composta. Filtra por pisos, ordena por data (recência) e corta por limite. Nunca ordena por similaridade à pergunta.
flashcard
Como uma gaveta não-ligada "nunca vaza"?
clique para virar
Por ausência estrutural: resolveMemoryBinding só entrega as stores declaradas; as demais recebem um leitor vazio. Sem leitor, não há o que consultar.
flashcard
alembic distill destila o quê?
clique para virar
Um corpus (T0→T3 → LEARNINGS/SIGNALS), não um modelo. "Distillation" aqui é metáfora de domínio, não a técnica de ML.
Use o seletor abaixo para descrever a sua necessidade e ver qual das quatro estratégias se encaixa — e o que o Alembic faz nesse caso.
1. O conhecimento que falta muda com frequência (é fresco)?
2. Ele cabe em poucos exemplos/instruções na janela?
3. Sua prioridade é custo/latência mínimos em altíssima escala?
responda acima
Escolha as opções para ver a recomendação
Demo: heurística de decisão. Não há "resposta universal" — a ferramenta certa depende das três perguntas. E o rótulo do Alembic (owned/delegated/gap) aparece junto.
An educational decision-tree illustration for choosing among four model-specialization strategies. A single ro
6
Recapitule em slides
Os seis beats da lição, em sequência. Use as setas ←→ ou os pontos.
Engenharia de contexto
A pergunta certa não é "o que o modelo sabe?"
É o que cabe na janela, em que ordem, sob orçamento. A maior alavanca depois que o modelo está fixo.
1
O pack L0→L7
Estratifica para poder descartar
Do brief (L0) ao manifesto com hash (L7). Sob pressão, some de baixo pra cima. Orçamento é comparação, não corte.
2
O gap titular
Indexa, mas não recupera
Existe a metade de escrita (constrói o índice). Não existe a de leitura: zero cosine/topK/rerank em packages/.
3
Cuidado
O vetor offline não é semântico
Hash de char-codes, 16 dimensões. Mede bytes, não sentido. Semântico só no caminho --online.
4
Memória
Composta, não recuperada
Filtra → ordena por recência → corta. Gaveta não-ligada não tem leitor → nunca vaza.
5
Estratégia
Quatro ferramentas, não uma
Fine-tune · in-context (a aposta do Alembic) · RAG (gap) · destilação. Errar a ferramenta custa caro.
6
1 / 6setas ←→
A foto da lição inteira. Esquerda: o que confiar hoje. Centro: o que é de outra camada. Direita: onde o roadmap começa — e onde "self-learning" ainda não chegou.
As Dez sobre contexto, memória e RAG no Alembic
Contexto é a maior alavanca de qualidade depois que o modelo está escolhido.
O pack tem 8 camadas: L0 (brief) → L7 (manifesto com hash). Largura = volume.
Orçamento é comparação (withinBudget), não truncamento automático.
A estimativa de tokens é length/4 — régua grosseira, boa p/ descartar, ruim p/ encaixe exato.
O L7 é hash de conteúdo → detecção de mudança e reprodutibilidade grátis.
RAG tem duas metades; o Alembic só tem a de escrita (indexa, não recupera). É o gap nº 1.
O vetor offline é um hash de char-codes de 16 dim — não é semântico.
Memória é composta (recência), não recuperada (similaridade).
Gaveta não-ligada não tem leitor → vaza nada (capacidade por ausência).
Quatro estratégias: in-context (owned) · fine-tune (delegated) · RAG (gap) · destilação (de corpus, não de modelo).
Leve isto com você"RAG" no Alembic hoje = funil de destilação + pack de contexto montado à mão. Não é busca por similaridade. Saber onde o retrieval para é saber onde o roadmap começa — e essa honestidade é a diferença entre um harness confiável e marketing.
Você é o professor agora: pegue o backend offline e calcule à mão o primeiro componente do vetor de "oi" — convença-se de que é determinístico e não semântico. Dúvida boa para levar à próxima lição: se não medimos similaridade, como medimos qualidade? É exatamente o gap de evals — e os spans, e o custo — que a lição 0007 abre.
7
Revisão (verifique no boundary)
Três perguntas. A pontuação corre sozinha. Cada opção explica por que.
Checagem cumulativa
1. No pack de contexto, o que acontece quando estimatedTokens > maxTokens?
Correto: b. O pack mede e compara — carrega os dois números e o booleano. Ele nunca trunca sozinho (a) nem lança (c): a casa toda devolve Result, never-throws. Quem pisa no freio é o chamador.
2. Por que "o Alembic indexa mas não recupera"?
Correto: c. Um grep por cosine|topK|rerank|queryIndex|knn em packages/ volta vazio. Não é criptografia (a) nem delegação ao gateway (b) — a query vetorial simplesmente não foi construída. É o gap nº 1 do mapa.
3. Como a memória multi-store garante que uma gaveta não-ligada nunca vaze?
Correto: a. resolveMemoryBinding entrega só as stores declaradas; as demais recebem um leitor vazio. Não é um filtro em runtime que poderia falhar (b), nem criptografia (c) — é a mesma postura do MCP read-only: capacidade por ausência.
Rode no repo: grep -rE "cosine|topK|rerank|queryIndex|knn" packages/ → vazio (confirma o gap). Abra packages/embeddings/src/offline-backend.ts → OFFLINE_DIMENSION = 16 e o cabeçalho "NÃO é um embedding semântico real". Abra packages/hermes/src/memory/multi-store/composer.ts → o rank ordena por at (data), não por similaridade.