Curso / Lição 4  ·  EN
Lição 04 · O resultado-chave

O Ciclo Fechado de Auto-aperfeiçoamento

A Lição 3 nomeou o ciclo de aprendizado como pedra angular. Aqui está como ele realmente é entregue: três subsistemas — memory, learning, curator — que juntos permitem que uma run finalizada torne a próxima mais esperta, sem nunca auto-escrever uma lição não validada na memória durável.

Três partes, um ciclo

1 · memory/ snapshot congelado no início 2 · learning/ propor→portão→aplicar 3 · curator/ active→stale→archived resumo do turno telemetria escritas aprovadas sedimentam → snapshot da próxima sessão mais rico o curador mantém o lado de skills limpo (nunca deleta)

1 · Memory — o snapshot congelado

Dois stores limitados, em arquivo, persistem entre sessões: MEMORY.md (as próprias notas do agente) e USER.md (o que ele sabe sobre o usuário). Ambos são injetados no system prompt como um snapshot congelado no início da sessão. A disciplina que importa:

// packages/hermes/src/memory/memory-store.ts:50-57
export const ENTRY_DELIMITER = '\n§\n';
/** Limite de caracteres padrão para o store MEMORY.md (padrão do Hermes). */
export const DEFAULT_MEMORY_CHAR_LIMIT = 2200;
/** Limite de caracteres padrão para o store USER.md (padrão do Hermes). */
export const DEFAULT_USER_CHAR_LIMIT = 1375;

Este subsistema é um CLONE fiel de tools/memory_tool.py (1089 LOC). Os desvios são deliberados: IO é injetado via FsPort, e toda op falível retorna Result<T,Error> em vez de um dict Python.

2 · Learning — propor, e o Validador dispõe

A escolha de design mais importante — ADR-0018

O Hermes auto-escreve na memória após um turno. O Alembic não. O revisor apenas propõe; o Validador existente do Alembic dispõe. As escritas são com portão do Validador, nunca auto-aplicadas.

Por que a mudança? Duas razões do ADR, ambas fundamentadas:

Então o ciclo são três portas injetadas e um kernel:

PortaPapel
ReviewProposerRetorna ReviewProposals a partir do resumo do turno — cada um um { target, op, rationale, score }. Em produção encapsula uma chamada de ModelAdapter; em testes, um fake.
ReviewGateDispõe cada proposta (aprova/rejeita). O padrão é scoreThresholdGate(0.7); o Validador real do coda conecta depois fornecendo seu próprio gate — sem mudar o kernel.
MemoryStoreO store onde escritas aprovadas se aplicam — reusando seu dedup, então rever um fato reforça em vez de duplicar.
// packages/hermes/src/learning/review.ts:54-69 — o kernel
export const reviewAndLearn = async (summary, deps) => {
  if (summary.trim().length === 0) return ok(emptyOutcome());   // "Nada a salvar."
  const proposed = await deps.proposer(summary);
  if (!proposed.ok) return proposed;                          // erro do proposer → falha fechada
  if (proposed.value.length === 0) return ok(emptyOutcome());
  const acc = { applied: [], rejected: [], failed: [] };
  for (const raw of proposed.value) {
    const stepErr = await processOne(raw, deps, acc);   // validar → portão → aplicar
    if (stepErr) return stepErr;                            // erro do portão → falha fechada
  }
  return ok({ applied: acc.applied, rejected: acc.rejected, failed: acc.failed });
};

Três baldes de resultado — applied / rejected / failed — então nada é descartado em silêncio. A saída do proposer é validada por Zod na fronteira (é saída de modelo não confiável em produção). Um erro de proposer ou portão falha o passo inteiro fechado; uma rejeição do store a uma escrita aprovada é registrada em failed, nunca lançada.

// packages/hermes/src/learning/gate.ts:24-36 — o portão conservador padrão
export const scoreThresholdGate = (min = DEFAULT_REVIEW_SCORE_THRESHOLD) => {
  return async (proposal) => {
    const approved = proposal.score >= min;          // limite inclusivo: score === min aprova
    const reason = approved
      ? `score ${proposal.score} ≥ threshold ${min}`
      : `score ${proposal.score} < threshold ${min} (learn only from validated wins)`;
    return ok({ approved, reason });                  // puro + total: ok(verdict) para toda entrada
  };
};

O limite padrão é 0.7 — a codificação mecânica da regra do hermes-mini-loop "aprender só com vitórias validadas". Note que a decisão vive em verdict.approved, não no Result: uma rejeição é um ok(...) normal, não um erro.

3 · Curator — a metade do descarte

O agente cria skills; telemetria se acumula; o curador é o passo determinístico que mantém a biblioteca de skills limpa. É um CLONE fiel de agent/curator.py:apply_automatic_transitions, com quatro regras clonadas exatamente:

O tempo é um Clock injetado — nunca Date.now() (a regra de determinismo do motor, e o que torna os testes de transição reprodutíveis). O curador é o mesmo Clock com que o usage store foi construído, então um evento registrado "agora" e uma transição decidida "agora" concordam.

active stale archived passa staleAfter usada de novo → reativa passa archiveAfter active passa archiveAfter → archived (pula stale)

Por que com portão, não auto-aplicado — a ideia para guardar

Auto-aplicar seria mais rápido. Foi rejeitado de propósito. A ADR-0018 considerou "auto-aplicar escritas após cada run (comportamento literal do Hermes)" e rejeitou: burla o Validator Gate e deixa lições não validadas endurecerem na memória durável — exatamente o modo de falha que a ADR-0006 existe para prevenir. O ponto inteiro da fusão é que o ciclo compõe com o pipeline de portões em vez de contorná-lo.
1. Uma escrita de memória no meio da sessão tem sucesso. O system prompt muda pelo resto daquela sessão?
Correto: b. O snapshot é congelado no início da sessão. As escritas são duráveis imediatamente mas não invalidam o prefixo do prompt — esse é o ponto inteiro. "Próxima run mais esperta" é literal: o refresh acontece no load da próxima sessão.
2. O revisor propõe uma escrita com score: 0.6 e o portão padrão está em uso. O que acontece?
Correto: d. O padrão scoreThresholdGate(0.7) retorna ok({approved:false, reason}) — uma rejeição é um resultado normal, não um erro. Vai para rejected; só um erro de proposer/portão falha o passo fechado.
3. O curador encontra uma skill há muito sem uso com pinned: true e createdBy: 'user'. O que ele faz?
Correto: c. Duas guardas se aplicam: o portão de proveniência só toca skills createdBy === 'agent', e skills pinadas nunca são transicionadas. E o estado terminal é archived — não há caminho de delete nenhum.

Confusões comuns

"O revisor é um daemon de fundo, como no Hermes." Não — no Alembic é um passo síncrono pós-unidade sobre portas injetadas (ADR-0018). Sem thread, sem fork; é isso que o torna testável e componível com o harness.
"Com portão significa lento / humano no loop em toda escrita." Não — o portão padrão é um check puro de score ≥ 0.7 sem humano e sem I/O. "Com portão" significa um piso de qualidade precisa ser passado; o piso pode depois ser o Validador completo do coda injetando um portão diferente — o kernel nunca muda.