Ralph Loop — Iteración Automática con Checks

El Ralph Loop ejecuta un agente de forma iterativa hasta que un conjunto de verificaciones (checks) pasen. Cada iteración recibe un contexto completamente limpio — no arrastra historial de iteraciones anteriores.


Concepto

El patrón Ralph Loop resuelve un problema común: ejecutar un agente que modifica código y luego verificar que las modificaciones son correctas (tests, lint, type check). Si los checks fallan, el agente debe intentar de nuevo con información sobre los errores.

Iteración 1:  Agent → modifica código → checks fallan (test error en line 42)
Iteración 2:  Agent → recibe error + diff → corrige → checks fallan (lint warning)
Iteración 3:  Agent → recibe error + diff → corrige → checks pasan ✓

La clave es el contexto limpio: cada iteración crea un AgentLoop completamente nuevo. El agente solo recibe:

  1. La spec original (archivo o prompt)
  2. El diff acumulado de todas las iteraciones anteriores
  3. Los errores de los checks de la iteración anterior
  4. Un progress.md auto-generado con el historial

Esto evita la contaminación del contexto y permite iteraciones largas sin degradación.


Uso básico

# Iterar hasta que tests pasen
architect loop "implementa autenticación JWT" \
  --check "pytest tests/test_auth.py"

# Múltiples checks — TODOS deben pasar
architect loop "refactoriza el módulo de pagos" \
  --check "pytest tests/" \
  --check "ruff check src/" \
  --check "mypy src/"

Con spec file

Para tareas complejas, puedes definir la especificación en un archivo Markdown:

architect loop "implementar según spec" \
  --spec requirements/auth-spec.md \
  --check "pytest tests/test_auth.py" \
  --check "ruff check src/auth/"

El contenido del spec file se inyecta en el prompt de cada iteración en lugar del texto del argumento TASK.

Con worktree aislado

architect loop "migrar de SQLAlchemy a Tortoise ORM" \
  --check "pytest tests/" \
  --worktree \
  --max-iterations 10

Con --worktree, el loop crea un git worktree aislado (.architect-ralph-worktree). Si todos los checks pasan, el resultado incluye la ruta al worktree para inspección o merge manual.


Opciones

OpciónDefaultDescripción
--check CMD(requerido)Comando de verificación shell. Repetible. Todos deben pasar (exit 0)
--spec PATHArchivo de especificación. Se usa en vez del argumento TASK
--max-iterations N25Máximo de iteraciones antes de parar
--max-cost FLOATLímite de coste en USD. Se detiene si se supera
--max-time INTLímite de tiempo total en segundos
--completion-tag TAGCOMPLETETag que el agente debe emitir cuando considere que terminó
--agent NAMEbuildAgente a usar en cada iteración
--model MODELModelo LLM (override del config)
-c, --config PATHArchivo de configuración YAML
--worktreefalseEjecutar en un git worktree aislado
--quietfalseSolo resultado final, sin logs de iteraciones

Cómo funciona internamente

Flujo de una ejecución

architect loop "task" --check "pytest" --check "ruff check src/"

  ├── 1. Capturar ref inicial: git rev-parse HEAD → initial_ref
  ├── 2. (Opcional) Crear worktree: .architect-ralph-worktree

  ├── LOOP:
  │   ├── 3. Construir prompt limpio (_build_iteration_prompt)
  │   │       ├── Spec/task original
  │   │       ├── Instrucciones de iteración (nº, checks, completion_tag)
  │   │       ├── Diff acumulado: git diff <initial_ref> (truncado 5000 chars)
  │   │       ├── Errores de checks previos (truncado 2000 chars/check)
  │   │       └── Contenido de .architect/ralph-progress.md
  │   │
  │   ├── 4. agent_factory() → AgentLoop fresco
  │   ├── 5. agent.run(prompt) → AgentState
  │   │
  │   ├── 6. _run_checks() → ejecutar cada check como subprocess
  │   │       └── subprocess.run(cmd, shell=True, timeout=120, cwd=workspace)
  │   │
  │   ├── 7. _update_progress() → escribir iteración a .architect/ralph-progress.md
  │   │
  │   ├── 8. ¿Todos los checks pasaron Y completion_tag encontrado?
  │   │       ├── SÍ → success, salir del loop
  │   │       └── NO → ¿Safety nets?
  │   │               ├── max_iterations alcanzado → stop
  │   │               ├── max_cost superado → stop
  │   │               ├── max_time excedido → stop
  │   │               └── Continuar al siguiente iteration
  │   └── (vuelve a 3)

  ├── 9. (Opcional) Resultado incluye worktree_path
  └── 10. Retornar RalphLoopResult

Contexto limpio por iteración

Cada iteración invoca agent_factory() que crea un AgentLoop completamente nuevo. No se comparte ningún estado entre iteraciones:

  • Nuevo ContextBuilder (sin mensajes previos)
  • Nuevo CostTracker (el coste se acumula externamente)
  • Nuevo ExecutionEngine con guardrails frescos

Lo único que conecta iteraciones es el filesystem (los archivos modificados persisten) y el prompt (que incluye el diff acumulado y los errores).

Checks

Los checks son comandos shell ejecutados como subprocesos:

subprocess.run(cmd, shell=True, capture_output=True, timeout=120, cwd=workspace_root)
  • Exit code 0 = check pasó
  • Cualquier otro exit code = check falló
  • Timeout (120s por defecto) = check falló con mensaje “Timeout”
  • El output (stdout + stderr) se trunca a los últimos 2000 caracteres
  • Los checks se ejecutan en el directorio del workspace (o worktree)

Progress file

Después de cada iteración, se escribe .architect/ralph-progress.md:

## Iteración 1 — FAIL
- Steps: 8
- Coste: $0.0234
- Duración: 15.2s
- Checks:
  - pytest tests/: PASS
  - ruff check src/: FAIL — src/auth.py:42:1: F841 local variable 'x' is assigned to but never used

## Iteración 2 — PASS
- Steps: 3
- Coste: $0.0089
- Duración: 5.1s
- Checks:
  - pytest tests/: PASS
  - ruff check src/: PASS

Este archivo se incluye en el prompt de iteraciones posteriores para que el agente tenga visibilidad del progreso.


Safety nets

El loop se detiene automáticamente si se cumple alguna de estas condiciones:

CondiciónConfiguraciónComportamiento
Iteraciones--max-iterations 25Para después de N iteraciones sin éxito
Coste--max-cost 5.0Para si el coste acumulado supera el límite
Tiempo--max-time 600Para si la duración total supera N segundos

El resultado (RalphLoopResult) indica la razón de parada en stop_reason:

  • "all_checks_passed" — todos los checks pasaron (éxito)
  • "max_iterations" — se agotaron las iteraciones
  • "budget_exhausted" — se superó el presupuesto
  • "timeout" — se superó el tiempo máximo

Worktrees

Con --worktree, el loop crea un git worktree aislado:

  1. Creación: git worktree add -b architect/ralph-loop .architect-ralph-worktree HEAD
  2. Ejecución: Todas las iteraciones trabajan en el worktree
  3. Resultado: Si los checks pasan, result.worktree_path contiene la ruta

El worktree no se elimina automáticamente — puedes inspeccionarlo, hacer cherry-pick, o merge manual:

# Ver qué cambió
cd .architect-ralph-worktree
git diff HEAD~1

# Merge al branch principal
git checkout main
git merge architect/ralph-loop

# Limpiar manualmente
git worktree remove .architect-ralph-worktree
git branch -D architect/ralph-loop

Configuración YAML

ralph_loop:
  max_iterations: 25        # 1-100
  max_cost: null             # USD, null = sin límite
  max_time: null             # segundos, null = sin límite
  completion_tag: "COMPLETE" # tag que el agente emite
  agent: build               # agente por defecto

API Python

RalphConfig

@dataclass
class RalphConfig:
    task: str                           # Descripción de la tarea
    checks: list[str]                   # Comandos de verificación (deben retornar exit 0)
    spec_file: str | None = None        # Archivo de especificación (sustituye task en prompt)
    completion_tag: str = "COMPLETE"     # Tag de completación
    max_iterations: int = 25            # Máximo de iteraciones
    max_cost: float | None = None       # Límite USD
    max_time: int | None = None         # Límite en segundos
    agent: str = "build"                # Agente a usar
    model: str | None = None            # Modelo LLM
    use_worktree: bool = False          # Usar git worktree

RalphLoop

class RalphLoop:
    def __init__(
        self,
        config: RalphConfig,
        agent_factory: Callable[..., Any],  # (**kwargs) → AgentLoop
        workspace_root: str | None = None,
    ) -> None: ...

    def run(self) -> RalphLoopResult: ...
    def cleanup_worktree(self) -> bool: ...
    def cleanup_progress(self) -> None: ...

RalphLoopResult

@dataclass
class RalphLoopResult:
    iterations: list[LoopIteration]     # Todas las iteraciones
    total_cost: float = 0.0             # Coste acumulado USD
    total_duration: float = 0.0         # Duración total en segundos
    success: bool = False               # True si checks pasaron
    stop_reason: str = ""               # "all_checks_passed", "max_iterations", etc.
    worktree_path: str = ""             # Ruta al worktree (si se usó)

    @property
    def total_iterations(self) -> int: ...

LoopIteration

@dataclass
class LoopIteration:
    iteration: int                      # Número (1-based)
    steps_taken: int                    # Pasos del agente
    cost: float                         # Coste USD de esta iteración
    duration: float                     # Duración en segundos
    check_results: list[dict]           # [{name, passed, output}]
    all_checks_passed: bool             # True si todos pasaron
    completion_tag_found: bool          # True si el agente emitió el tag
    error: str | None = None            # Error de ejecución (si hubo)

Ejemplos avanzados

Loop con budget y timeout

architect loop "implementa feature X según la spec" \
  --spec spec.md \
  --check "pytest tests/ -x" \
  --check "ruff check src/" \
  --max-iterations 10 \
  --max-cost 3.0 \
  --max-time 600 \
  --model gpt-4o

Loop en CI/CD

# GitHub Actions
- name: Implementar y verificar
  run: |
    architect loop "${{ github.event.issue.body }}" \
      --spec spec.md \
      --check "pytest tests/ -q" \
      --check "ruff check src/" \
      --max-iterations 5 \
      --max-cost 2.0 \
      --quiet

Loop con worktree para experimentar

# Ejecutar en worktree aislado
architect loop "migra a async/await" \
  --check "pytest tests/" \
  --worktree \
  --max-iterations 15

# Si tuvo éxito, inspeccionar y merge
git diff main...architect/ralph-loop
git merge architect/ralph-loop