El loop que aprende|INEMA.CLUB-PRO

Clase 2 · 20 min

Experimento: mantener o descartar con una sola métrica

El diseño más limpio de un loop que mejora solo es corto: el agente cambia una cosa, un comando da la nota, si mejoró se queda con commit, si no mejoró vuelve con git reset, y todo se vuelve una línea en un archivo. En esta clase ejecutas ese ciclo con agentes falsos y después con Claude Code de verdad.

Empezar la clase
Dos placas de circuito lado a lado conectadas a medidores; una con etiqueta amarilla, la otra yendo a la caja de descarte.

En esta clase vas a

  1. 1Keep = commit, discard = reset: git como deshacer
  2. 2Una sola métrica, con presupuesto fijo por ciclo
  3. 3results.tsv: cada intento se vuelve una línea, incluso los malos
  4. 4El mismo ciclo con agente falso y con Claude Code

Objetivo: ejecutar un experimento al estilo autoresearch: el agente cambia, una métrica decide, git commit si mejoró y git reset si no.

01Un ciclo del experimento

El diseño de autoresearch, descrito en fuentes secundarias. Haz clic en cada bloque.

Igual no es mejor: el empate va a discard. Si no, el sistema acumula cambios que no hacen nada.

En autoresearch el sistema es un script de entrenamiento y la métrica es la pérdida en validación; aquí, un clasificador de triaje con 40 casos sintéticos.

02Una cola de experimentos

Cuatro ideas, una por ciclo. Una de ellas se rompe en la primera ejecución (el agente se queda sin respuesta). Ejecuta, interrumpe y retoma.

ideas

midiendo

results.tsv

erro

Simulación en el navegador. El ensayo de verdad está justo abajo.

03Ensayo: tres agentes falsos

El kit crea un repo de triaje (v1) y corre tres ciclos: un agente que mejora, uno que empeora y uno que no cambia la nota. No se llama a ningún modelo.

Terminal · ensayo sin costo
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
Ejemplo del repositorio
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

La columna del hash del commit se omite (cambia en cada ejecución). El código de salida del experimento.sh dice lo que pasó: 0 keep, 1 discard, 2 error.

04El mismo ciclo con Claude Code

Una llamada por tu suscripción (sale de la cuota de tu suscripción): claude -p sin herramientas recibe las reglas y los casos errados y devuelve un regras.txt nuevo. Quien decide es la medida, no el modelo.

Terminal · real, una llamada
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)"
Archivo · kit/agente-claude.sh
#!/usr/bin/env bash
# agente-claude.sh — el agente REAL del experimento: Claude Code (sin herramientas) propone un regras.txt nuevo.
# Se ejecuta en la carpeta del repo, llamado por experimento.sh. Sale de la cuota de tu suscripción (una llamada).
# Respuesta vacía (límite de la sesión, error) = ERRO, salida 1: nunca se convierte en "no cambió 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
Ejemplo del repositorio
ciclo 4 · keep · 0.800 → 0.900 · melhorou
saída: 0
status da última linha: keep

Una ejecución. El modelo agregó palabras de entrega (pacote, encomenda, transportadora, frete…) y la nota visible pasó de 0.800 a 0.900. Tu ejecución puede dar keep o discard: el "listo cuando" solo comprueba que la línea quedó registrada. En la clase 6, este mismo cambio es reprobado por los casos ocultos.

05Adaptar a tu repo

El experimento.sh del kit mide el clasificador de triaje. En tu repo, dos variables cambian el medidor; el results.tsv se crea solo y queda fuera de git. Ensayo sin costo en un repo de práctica.

Terminal · ensayo, un repo tuyo
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
Ejemplo del repositorio
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áctica

Ensayo, real y tu sistema.

E4 · Tres ciclos sin costo

10 min

Corre el ensayo.

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

E5 · Un ciclo real

5 min

Corre el bloque real justo después del ensayo.

Listo cuando: wc -l < $E/sis/results.tsv → 5 (encabezado + 4 ciclos) y la última columna de estado es keep, discard o erro, nunca vacía.

E6 · Tu medidor

30 min

En tu sistema, escribe el comando que imprime la métrica de tu hipótesis (clase 1) como número al comienzo de la línea, en el formato del medir.py --valor. Conéctalo al experimento.sh con las variables MEDIR y MEDIDOR, como en el bloque "Adaptar a tu repo" de arriba.

Listo cuando: tu comando de medida, corrido dos veces seguidas sin cambios, imprime el mismo número; y AGENTE="echo '# revisado' >> [un archivo del repo]" bash $K/experimento.sh . "comenta"; echo "saída: $?", con MEDIR y MEDIDOR exportados, termina en discard y saída: 1.

07Prueba de la clase

Pregunta 1 de 4

El cambio dio exactamente la misma nota que la base. ¿Qué hace el ciclo?competencia

08En tres frases

Un cambio, una notaCada ciclo mide la base, deja que el agente cambie una cosa y mide de nuevo con el mismo comando.
Git deshaceSi mejoró, es commit; si queda igual o peor, es reset. Nada queda a medias.
Todo registradoresults.tsv guarda keep, discard y erro; una respuesta vacía del modelo es un error.

Para ir más allá

Clase anteriorSiguiente clase