O loop que aprende|INEMA.CLUB-PRO

Aula 2 · 20 min

Experimento: manter ou descartar por uma métrica só

O desenho mais limpo de loop que melhora sozinho é curto: o agente muda uma coisa, um comando dá a nota, melhorou fica com commit, não melhorou volta com git reset, e tudo vira uma linha num arquivo. Nesta aula você roda esse ciclo com agentes falsos e depois com o Claude Code de verdade.

Começar a aula
Duas placas de circuito lado a lado ligadas a medidores; uma com etiqueta amarela, a outra indo para a caixa de descarte.

Nesta aula você vai

  1. 1Keep = commit, discard = reset: o git como desfazer
  2. 2Uma métrica só, com orçamento fixo por ciclo
  3. 3results.tsv: toda tentativa vira uma linha, inclusive as ruins
  4. 4O mesmo ciclo com agente falso e com o Claude Code

Objetivo: rodar um experimento no estilo autoresearch: o agente muda, uma métrica decide, git commit se melhorou e git reset se não.

01Um ciclo do experimento

O desenho do autoresearch, descrito em fontes secundárias. Clique em cada bloco.

Igual não é melhor: empate vai para discard. Senão o sistema acumula mudanças que não fazem nada.

No autoresearch o sistema é um script de treino e a métrica é a perda na validação; aqui, um classificador de triagem com 40 casos sintéticos.

02Uma fila de experimentos

Quatro ideias, uma por ciclo. Uma delas quebra na primeira execução (o agente fica sem resposta). Rode, interrompa e retome.

ideias

medindo

results.tsv

erro

Simulação no navegador. O ensaio de verdade está logo abaixo.

03Ensaio: três agentes falsos

O kit cria um repo de triagem (v1) e roda três ciclos: um agente que melhora, um que piora e um que não muda a nota. Nenhum modelo é chamado.

Terminal · ensaio sem custo
K=~/projetos/loop-r-eng-aprende-v72/kit
E=${TMPDIR:-/tmp}/ensaio-aprende/a2
bash $K/novo-sistema.sh $E/sis
for m in sinonimos estraga comenta; do
  AGENTE="bash $K/agente-falso.sh $m" bash $K/experimento.sh $E/sis "$m"; echo "saída: $?"
done
echo "## results.tsv"; cut -f1,3- $E/sis/results.tsv
echo "## git log"; git -C $E/sis log --format=%s
Exemplo do repositório
sistema criado em /tmp/ensaio-aprende/a2/sis (acerto: 0.700 (28/40))
ciclo 1 · keep · 0.700 → 0.800 · melhorou
saída: 0
ciclo 2 · discard · 0.800 → 0.700 · não melhorou
saída: 1
ciclo 3 · discard · 0.800 → 0.800 · não melhorou
saída: 1
## results.tsv
ciclo	base	novo	status	descricao
1	0.700	0.800	keep	sinonimos
2	0.800	0.700	discard	estraga
3	0.800	0.800	discard	comenta
## git log
keep: sinonimos (0.700 → 0.800)
v1: regras de base

Coluna do hash do commit omitida (muda a cada rodada). O código de saída do experimento.sh diz o que aconteceu: 0 keep, 1 discard, 2 erro.

04O mesmo ciclo com o Claude Code

Uma chamada pela sua assinatura (sai da cota): claude -p sem ferramentas recebe as regras e os casos errados e devolve um regras.txt novo. Quem decide é a medida, não o modelo.

Terminal · real, uma chamada
K=~/projetos/loop-r-eng-aprende-v72/kit
E=${TMPDIR:-/tmp}/ensaio-aprende/a2      # o repo do ensaio acima, já com o keep dos sinônimos
AGENTE="bash $K/agente-claude.sh" bash $K/experimento.sh $E/sis "claude: uma mudança"; echo "saída: $?"
echo "status da última linha: $(tail -1 $E/sis/results.tsv | cut -f5)"
Arquivo · kit/agente-claude.sh
#!/usr/bin/env bash
# agente-claude.sh — o agente REAL do experimento: o Claude Code (sem ferramentas) propõe um regras.txt novo.
# Roda na pasta do repo, chamado pelo experimento.sh. Sai da cota da sua assinatura (uma chamada).
# Resposta vazia (limite da sessão, erro) = ERRO, saída 1: nunca vira "não mudou nada".
set -uo pipefail
K=$(cd "$(dirname "$0")" && pwd)
PEDIDO="Você melhora as regras de um classificador de mensagens de suporte. Formato de cada linha: 'categoria: palavra, palavra'; a primeira categoria cuja palavra aparece no texto vence; sem nenhuma, 'outros'.
Regras atuais:
$(cat regras.txt)

Casos que o classificador errou:
$(python3 "$K/medir.py" . --erros | tail -n +2)

Proponha UMA mudança que generalize (palavras que pessoas reais usariam), sem copiar frases inteiras dos casos.
Responda SÓ com o conteúdo completo do novo regras.txt, sem explicação e sem cercas de código."
J=$(mktemp)
echo "$PEDIDO" | claude -p --tools "" --output-format json > "$J" 2>/dev/null
python3 - "$J" <<'PY'
import json, os, sys
try:
    d = json.load(open(sys.argv[1], encoding="utf-8"))
except Exception:
    print("ERRO: resposta do claude não é JSON"); sys.exit(1)
r = (d.get("result") or "").strip()
linhas = [l for l in r.splitlines() if not l.startswith("```")]
if d.get("is_error") or not any(":" in l for l in linhas):
    print("ERRO: sem regras na resposta (" + (r[:80] or "vazia") + ")"); sys.exit(1)
u = d.get("usage") or {}
ent = u.get("input_tokens", 0) + u.get("cache_read_input_tokens", 0) + u.get("cache_creation_input_tokens", 0)
if os.environ.get("TOKENS_ARQ"):
    open(os.environ["TOKENS_ARQ"], "w").write(f"{ent} {u.get('output_tokens', 0)}\n")
open("regras.txt", "w", encoding="utf-8").write("\n".join(linhas).strip() + "\n")
print(f"regras.txt reescrito ({len(linhas)} linhas)")
PY
rc=$?; rm -f "$J"; exit $rc
Exemplo do repositório
ciclo 4 · keep · 0.800 → 0.900 · melhorou
saída: 0
status da última linha: keep

Uma rodada. O modelo acrescentou palavras de entrega (pacote, encomenda, transportadora, frete…) e a nota visível foi de 0.800 a 0.900. A sua rodada pode dar keep ou discard: o "pronto quando" confere só que a linha foi registrada. Na aula 6, esta mesma mudança é reprovada pelos casos escondidos.

05Adaptar ao seu repo

O experimento.sh do kit mede o classificador de triagem. No seu repo, duas variáveis trocam o medidor; o results.tsv nasce sozinho e fica fora do git. Ensaio sem custo num repo de treino.

Terminal · ensaio, um repo seu
K=~/projetos/loop-r-eng-aprende-v72/kit
E=${TMPDIR:-/tmp}/ensaio-aprende/a2-adaptar; rm -rf $E; mkdir -p $E/repo; cd $E/repo
# um repo "seu": sem results.tsv, sem .gitignore, com o próprio medidor
printf 'def dobro(x):\n    return x + 2\n' > calc.py
printf '1 2\n2 4\n5 10\n0 0\n' > casos.txt                      # entrada e esperado
cat > medir.sh <<'FIM'
# imprime "<acerto> <ok> <total>" (o experimento.sh lê o 1º número)
python3 -c "
import calc
c = [l.split() for l in open('casos.txt') if l.strip()]
ok = sum(calc.dobro(int(a)) == int(b) for a, b in c)
print(f'{ok / len(c):.3f} {ok} {len(c)}')"
FIM
git init -q -b main; git add -A; git -c user.name=treino -c user.email=treino@exemplo.invalid commit -qm "v1"
export MEDIR="bash medir.sh" MEDIDOR="casos.txt medir.sh"      # as duas variáveis da adaptação
bash $K/experimento.sh --congelar . $E/medidor.sha256; export GUARDA=$E/medidor.sha256
AGENTE="echo '# revisado' >> calc.py" bash $K/experimento.sh . "comenta"; echo "saída: $?"
AGENTE="printf 'def dobro(x):\n    return x * 2\n' > calc.py" bash $K/experimento.sh . "corrige"; echo "saída: $?"
AGENTE="echo '3 7' >> casos.txt" bash $K/experimento.sh . "mexe nos casos"; echo "saída: $?"
echo "## results.tsv (criado pelo experimento.sh)"; cut -f1,3- results.tsv
echo "## git status (vazio = limpo)"; git status --short
Exemplo do repositório
medidor congelado em /tmp/ensaio-aprende/a2-adaptar/medidor.sha256
ciclo 1 · discard · 0.250 → 0.250 · não melhorou
saída: 1
ciclo 2 · keep · 0.250 → 1.000 · melhorou
saída: 0
ciclo 3 · discard · 1.000 → 1.000 · DESCARTADO: mexeu no medidor (casos.txt medir.sh)
saída: 1
## results.tsv (criado pelo experimento.sh)
ciclo	base	novo	status	descricao
1	0.250	0.250	discard	comenta
2	0.250	1.000	keep	corrige
3	1.000	1.000	discard	mexe nos casos
## git status (vazio = limpo)

Coluna do hash do commit omitida. O ciclo 3 mostra a guarda da aula 6 valendo também para o medidor seu.

06Prática

Ensaio, real e o seu sistema.

E4 · Três ciclos sem custo

10 min

Rode o ensaio.

Pronto quando: tail -n +2 $E/sis/results.tsv | cut -f5 | sort | uniq -c → 1 keep e 2 discard; git -C $E/sis log --oneline | wc -l → 2.

E5 · Um ciclo real

5 min

Rode o bloco real logo depois do ensaio.

Pronto quando: wc -l < $E/sis/results.tsv → 5 (cabeçalho + 4 ciclos) e a última coluna de status é keep, discard ou erro, nunca vazia.

E6 · O seu medidor

30 min

No seu sistema, escreva o comando que imprime a métrica da sua hipótese (aula 1) como número no começo da linha, no formato do medir.py --valor. Ligue-o no experimento.sh pelas variáveis MEDIR e MEDIDOR, como no bloco "Adaptar ao seu repo" acima.

Pronto quando: o seu comando de medida, rodado duas vezes seguidas sem mudança, imprime o mesmo número; e AGENTE="echo '# revisado' >> [um arquivo do repo]" bash $K/experimento.sh . "comenta"; echo "saída: $?", com MEDIR e MEDIDOR exportados, termina em discard e saída: 1.

07Teste da aula

Pergunta 1 de 4

A mudança deu exatamente a mesma nota da base. O que o ciclo faz?competência

08Em três frases

Uma mudança, uma notaCada ciclo mede a base, deixa o agente mudar uma coisa e mede de novo com o mesmo comando.
Git desfazMelhorou é commit; igual ou pior é reset. Nada fica pela metade.
Tudo registradoresults.tsv guarda keep, discard e erro; resposta vazia do modelo é erro.

Para ir além

Aula anteriorPróxima aula