Skip to main content
Reflexion fecha o loop de aprendizado: quando um agente falha ou produz saída de baixa qualidade, em vez de perder a experiência, o pipeline gera uma Lesson estruturada e persiste na memória de longo prazo. Na próxima tarefa similar, essa lição emerge naturalmente via RAG+HyDE.
Reflexion é o único post-hook ligado por default — porque só dispara em condições excepcionais (erro, discrepância) e o trabalho de geração de lição nunca bloqueia o turn do usuário.
Modo durável (default desde Abr/2026): triggers passam por uma fila WAL-backed com worker pool e dead-letter queue. Lições sobrevivem a crash do processo via replay no próximo boot. Ver Fila Durável.

O que é uma Lesson

Uma Lesson é um registro de quatro linhas:
Ao persistir em memory.Fact, o Content fica:
A categoria do Fact é lesson e as tags incluem reflexion + trigger:<x> + os tags específicos do domínio. Isso permite queries precisas: “me mostre todas as lições sobre edit-file” se torna uma pesquisa normal da memória.

Quatro gatilhos

O worker retornou Error != nil. Exemplos: timeout, tool call inválido, crash do provedor. Default: ON.

Fluxo — modo durável (default)

Com CHATCLI_QUALITY_REFLEXION_QUEUE_ENABLED=true (default), o trigger vai pra uma fila persistente. O hook não bloqueia o turn e o processo pode crashar sem perder a lição:
1

PostRun inspeciona o trigger

ReflexionHook.PostRun(ctx, hc, result) olha result.Metadata + result.Error — se nenhum gate bate, retorna em μs.
2

WAL Append (síncrono, sub-ms)

O hook chama enqueuer.Enqueue(req). O Runner calcula um JobID = sha256(task|trigger|attempt)[:16], escreve um record no WAL (~/.chatcli/reflexion/wal/<id>.wal) via tmp → fsync → atomic rename → dir fsync, então empilha em memory.
3

Retorno imediato ao pipeline

PostRun retorna nil; o turn do usuário continua sem espera. A latência adicional é o fsync (tipicamente < 1 ms).
4

Worker pool processa async

Um dos N workers (default 2) deenfileira, chama GenerateLesson com timeout per-job (default 2 min) e persiste em memory.Fact se o LLM não emitir <skip>.
5

Classificação do outcome

Sucesso ou Skipped → ACK (delete WAL record). Transient error (timeout, 429/503) → reschedule com backoff exponencial + jitter. Permanent (parser error) → move pra DLQ imediatamente.
6

Replay on boot

Na próxima sessão, Runner.Replay() roda async e reenfileira todo record pendente do WAL (descartando os mais velhos que StaleAfter, default 7 dias).
Latência observável no turn: um fsync local típico é < 1 ms em SSD. A geração de lição em si (chamada LLM) acontece depois do turn responder — o usuário nunca espera.

Fallback: modo legado (goroutine detached)

Se CHATCLI_QUALITY_REFLEXION_QUEUE_ENABLED=false, o hook volta ao comportamento original:
Zero dependência de filesystem, mas lições em vôo somem se o processo for morto. Mantido para compatibilidade e para usuários que preferem simplicidade sobre durabilidade.

Fila Durável — WAL + Worker Pool + DLQ

A fila é implementada em cli/agent/quality/lessonq/ com garantias enterprise:

WAL (Write-Ahead Log)

Cada lição pendente é um arquivo .wal em ~/.chatcli/reflexion/wal/ — um por Job ID. Layout binário:
  • CRC duplo detecta torn writes (crash no meio do fsync). Records corruptos são descartados no replay + chatcli_lessonq_wal_corruption_total incrementa.
  • Atomic rename: escrita em <id>.tmp.<pid>.<seq> → fsync → rename → dir fsync. Nunca um leitor vê record parcial.
  • O(1) ACK: um único unlink remove o record. Sem compactação em background.

Worker Pool

Cada worker:
  • Dequeue bloqueante (espera por NextAttemptAt ≤ now).
  • Per-job timeout bounded (não herda ctx do turn — reflexion outlive o turn por design).
  • Panic recovery: se o processor panica, vai direto pra DLQ (retry não ajuda bug).
  • Métrica chatcli_lessonq_processing_duration_seconds{outcome} emitida.

Dead Letter Queue

Failures permanentes ou exaustão de retries vão pra ~/.chatcli/reflexion/dlq/ (mesmo formato WAL, read-only pro processo). Operador inspeciona e decide:

Retry com Jitter

Transient errors (ctx timeout, provider 429/503, temp fs error) viram reschedule:
Defaults: 1s inicial, 5min cap, 2.0 multiplier, ±20% jitter, 5 tentativas. Full jitter previne thundering herd quando provider volta do fora-do-ar.

Idempotência

JobID é conteúdo-endereçado: sha256(normalized(task) | trigger | attempt | outcome)[:16]. Re-trigger da mesma situação enquanto o job está in-flight é no-op (WAL existe → Runner pula queue insert). Whitespace é normalizado pra evitar inflação por churn trivial.

Drain + Graceful Shutdown

Na saída (cli.cleanup()), o Runner fica em DrainAndShutdown(30s):
  1. Queue fecha — sem novos dequeues.
  2. Workers terminam in-flight (ou são cancelados no timeout).
  3. WAL/DLQ fecham.
Jobs ainda enfileirados sobrevivem no WAL e reprocessam no próximo boot. Zero perda de dado em SIGTERM ou kill -9.

/reflect — Comandos

Todos os subcomandos têm autocomplete via Tab. /reflect retry e /reflect purge listam IDs reais vivos da DLQ com preview da task + último erro.

Arquivos e layout

Caminho configurável via CHATCLI_QUALITY_REFLEXION_QUEUE_BASE_DIR (default: <workspace>/.chatcli/reflexion).
Operadores podem ls o diretório pra triagem rápida sem tools especiais. Cada record é um JSON dentro do framing binário — xxd + o protocolo na doc do lessonq ajudam em forense.

Protocolo do lesson generator

O system prompt instrui o modelo a ser geral, não one-off:
O bloco <skip> existe justamente para evitar pollution da memória com “lições” de falhas transientes. O modelo pode recusar gerar lição com custo zero de persistência.

/reflect — caminho manual sem LLM

Quando você sabe a lição e não precisa do LLM destilando:
Isso entra direto em memory.Fact:
Tags geradas: ["reflexion", "trigger:manual", "user-supplied"].
O caminho manual não faz chamada LLM — é barato, síncrono e ideal para capturar aprendizados durante a sessão.

Como a lição “volta”

Uma vez persistida, a lesson é um fact normal no índice. Ela emerge via:
  1. Retrieval por hints: se a próxima task mencionar keywords em Tags, o scorer relevance-based a surfaceia.
  2. HyDE amplifica: com CHATCLI_QUALITY_HYDE_ENABLED=true, a hipótese gerada cobre conceitos semelhantes, aumentando chance de match.
  3. Vector search: com embeddings configurados, a lesson é buscada por proximidade cosseno.
O system prompt do turn seguinte contém a seção ## Long-term Memory com o texto da lesson, e o modelo tem todas as pistas para não repetir o erro.

Variáveis de ambiente

Gates (quando disparar)

Fila durável (WAL + worker pool + DLQ)

Métricas Prometheus

A fila emite 10 métricas em chatcli_lessonq_*:

Exemplo de ciclo completo

1

Usuário pede task que falha

/coder refactor pkg/engine to extract Close method
2

CoderAgent tenta rewrite total

Arquivo tem 2000 linhas, provider responde com timeout.
3

PostRun detecta result.Error != nil

OnError trigger matched.
4

goroutine: GenerateLesson

Model emite:
5

Persiste em memory.Fact

Categoria=lesson, workspace=current project.
6

Próxima semana, usuário pede refactor similar

/coder refactor pkg/auth/manager.go split into smaller files
7

RAG+HyDE traz a lesson

Tags refactor + large-file matchem. Lesson aparece no system prompt.
8

Coder escolhe abordagem correta de primeira

Emite múltiplos @coder patch ao invés de write. Task concluída sem timeout.

Inspecionar lições armazenadas

Prometheus snapshots úteis


Leia também

#4 RAG + HyDE

Como as lições são recuperadas em tarefas futuras via retrieval semântico.

#6 CoVe

O verifier gera o signal verified_with_discrepancy que Reflexion consome.

Bootstrap Memory

Como a memória de longo prazo foi estruturada pré-pipeline.

Memory Commands

/memory load, /memory show, /memory longterm.

Configuração quality

Todos os CHATCLI_QUALITY_REFLEXION_QUEUE_* + presets.