Zum Hauptinhalt springen

ralph-loop – Deep-Dive-Guide

Was ist ralph-loop?

ralph-loop ist Anthropics offizielle Implementierung des Ralph-Wiggum-Patterns als Claude-Code-Plugin. Es nutzt einen Stop-Hook, der denselben Prompt erneut einspeist, sobald Claude die Session beenden will – kein externes Skript, kein zweites Terminal, keine Abhängigkeiten. Ein Plugin-Install und du hast eine autonome Schleife.

Repo: anthropics/claude-plugins-official → ralph-loop


1. Wie der Stop-Hook funktioniert

Die meisten Ralph-Varianten laufen als externer Bash-/Bun-/Go-Prozess, der den Agent in einer Schleife aufruft. ralph-loop ist anders: Der Loop läuft innerhalb der Claude-Code-Session.

┌─────────────────────────────────────────────────────────┐
│ Claude-Code-Session │
│ │
│ 1. /ralph-loop "…" startet die Session │
│ 2. Claude arbeitet am Prompt │
│ 3. Claude versucht, die Session zu beenden │
│ └─ Stop-Hook (stop-hook.sh) fängt den Exit ab │
│ 4. Hook speist den GLEICHEN Prompt erneut ein │
│ 5. Claude liest seinen eigenen Git-Diff + Log → weiter │
│ 6. Wiederholen bis: │
│ • Output enthält das Completion-Promise │
│ • ODER max-iterations erreicht │
│ • ODER /cancel-ralph aufgerufen │
└─────────────────────────────────────────────────────────┘

Da der Zustand im Filesystem und in der Git-History liegt, ist jeder Neustart kein „von vorn" – Claude liest, was bereits gebaut wurde, und macht dort weiter.


2. Plugin-Struktur

    • README.md

3. Installation & Grundbedienung

# Plugin installieren
/plugin install ralph-loop

# Loop starten
/ralph-loop "<dein Prompt>" \
--completion-promise "COMPLETE" \
--max-iterations 50

# Loop jederzeit abbrechen
/cancel-ralph

Pflichtparameter

ParameterBedeutung
<prompt>Die Aufgabe – wird in jeder Iteration erneut ausgeführt
--completion-promiseString, den Claude ausgeben muss, um den Loop zu stoppen
--max-iterationsHartes Limit für Wiederholungen (immer setzen)

Optionale Parameter

ParameterStandardBedeutung
--branchaktuellGit-Branch für die Arbeit
--no-commitfalseAuto-Commit pro Iteration deaktivieren
--modelclaude-sonnet-4-5Modell überschreiben

4. Windows-Stolperfalle

Auf Windows kann der Stop-Hook mit wsl: Unknown key 'automount.crossDistro' oder execvpe(/bin/bash) failed scheitern.

Fix: ~/.claude/plugins/cache/.../hooks/hooks.json anpassen und den Hook-Command explizit auf Git Bash setzen:

"command": "\"C:/Program Files/Git/bin/bash.exe\" ${CLAUDE_PLUGIN_ROOT}/hooks/stop-hook.sh"

Git/bin/bash.exe verwenden (mit PATH-Wrappern), nicht Git/usr/bin/bash.exe (raw MinGW).


5. Prompt-Schreib-Handbuch

Das ist die Kernkompetenz. Ein ralph-loop-Prompt läuft dutzende Male. Ein vager Prompt produziert dutzende falsche Iterationen. Ein präziser Prompt liefert ein funktionierendes Ergebnis, meist in 5–15 Iterationen.

Aufbau eines guten ralph-loop-Prompts

[1] KONTEXT      — was bereits existiert, welcher Stack, welche Constraints
[2] AUFGABE — was genau gebaut/geändert werden muss
[3] KRITERIEN — spezifische, testbare Abnahmekriterien
[4] VALIDIERUNG — der/die Befehl(e), die Erfolg beweisen
[5] MEMORY — Claude auffordern, AGENTS.md / progress.txt zu lesen
[6] ABSCHLUSS — wann das Promise ausgegeben werden soll

Nicht jeder Prompt braucht alle sechs, aber je komplexer die Aufgabe, desto mehr davon werden gebraucht.


6. Schlechte Prompts vs. gute Prompts

6.1 REST-API

Schlechter Prompt
Bau eine API für Benutzer.
Output COMPLETE wenn fertig.

Warum er scheitert:
Keine Endpunkte spezifiziert. Keine Authentifizierung. Keine Validierung. Keine Test-Anforderung. Claude produziert zwar etwas – aber jede Iteration produziert etwas anderes, und „fertig" ist undefiniert.

Guter Prompt
Stack: Laravel 11, Sanctum, PostgreSQL, PHPUnit.

Baue eine User-Management-REST-API:
POST /api/users — User anlegen (name, email, password; E-Mail eindeutig)
GET /api/users/{id} — User anzeigen (Auth erforderlich)
PUT /api/users/{id} — User updaten (Auth erforderlich, nur eigener User)
DELETE /api/users/{id} — Soft-Delete (Auth erforderlich, nur eigener User)

Abnahmekriterien:
1. Alle Routen antworten mit den korrekten HTTP-Status-Codes (201, 200, 403, 404).
2. Validierungsfehler geben 422 mit einem "errors"-Key zurück.
3. `php artisan test --filter UserApiTest` besteht mit ≥ 90 % Coverage auf dem Controller.
4. Eine OpenAPI-Doku wird unter /api/documentation erzeugt.

Vor jeder Iteration AGENTS.md lesen.
Wenn alle Kriterien erfüllt → <promise>COMPLETE</promise> ausgeben.

6.2 Laravel-Feature

Schlechter Prompt
Füge meiner Laravel-App Authentifizierung hinzu.
COMPLETE wenn fertig.

Warum er scheitert:
„Authentifizierung" könnte alles bedeuten: Sanctum, Passport, Breeze, Fortify, eigenes JWT. Keine Routen, keine Tests, kein Session-vs.-Token-Unterschied definiert.

Guter Prompt
Stack: Laravel 11, Sanctum, MySQL, Pest.

Token-basierte API-Authentifizierung einbauen:
POST /api/auth/register — name, email, password; gibt Bearer-Token zurück
POST /api/auth/login — email, password; gibt Bearer-Token zurück
POST /api/auth/logout — macht aktuellen Token ungültig (auth:sanctum)
GET /api/auth/me — gibt den authentifizierten User zurück (auth:sanctum)

Regeln:
- E-Mail muss eindeutig sein; doppelte Registrierung → 409.
- Passwort: min. 8 Zeichen, mindestens ein Großbuchstabe, eine Ziffer.
- Tokens laufen nach 24 h ab (in Sanctum-Config setzen).
- Bestehende Routen oder Migrationen NICHT anfassen.

Tests: `php artisan test --filter AuthTest` muss bestehen (Test erstellen falls nicht vorhanden).

Vor jeder Iteration AGENTS.md lesen.
Wenn alle Tests grün → <promise>COMPLETE</promise> ausgeben.

6.3 Laravel-Refactoring

Schlechter Prompt
Refactore den UserController, damit er sauberer wird.
Output COMPLETE wenn fertig.

Warum er scheitert:
„Sauberer" ist subjektiv. Der Agent wird endlos refactoren und in der nächsten Iteration seine eigene Arbeit wieder rückgängig machen.

Guter Prompt
Refactore UserController (app/Http/Controllers/UserController.php) nach diesen Regeln:
1. Business-Logik in UserService auslagern (app/Services/UserService.php).
2. Validierungsregeln in UserRequest auslagern (app/Http/Requests/UserRequest.php).
3. Jede Controller-Methode darf max. 10 Zeilen haben.
4. Keine direkten Eloquent-Calls im Controller – nur über UserService.
5. Alle bestehenden Tests in tests/Feature/UserTest.php müssen weiterhin bestehen.

Die öffentlichen Methodensignaturen von UserController NICHT ändern.
Keine neuen Routen anlegen.

Validierungsbefehl: `php artisan test --filter UserTest`

Wenn alle Tests bestehen und der Controller alle 5 Regeln erfüllt → <promise>COMPLETE</promise> ausgeben.

6.4 TypeScript / Node.js API

Schlechter Prompt
Erstelle eine Todo-API in TypeScript.
Sag DONE wenn fertig.

Warum er scheitert:
Framework? Validierung? Datenbank? Tests? Fehlerbehandlung? Alles undefiniert.

Guter Prompt
Stack: Hono, Zod, Drizzle ORM, SQLite (Dev), Vitest.

Todo-API bauen:
POST /todos — { title: string (min 3), done?: boolean }
GET /todos — alle Todos auflisten, optional ?done=true|false Filter
GET /todos/:id — einzelnes Todo oder 404
PATCH /todos/:id — partielles Update { title?, done? }
DELETE /todos/:id — 204 oder 404

Regeln:
- Alle Eingaben mit Zod validieren; ungültig → 400 mit { error, details }.
- Timestamps: created_at, updated_at (ISO 8601 in Responses).
- Drizzle-Migration in /drizzle/migrations/.
- Tests: `pnpm vitest run` muss bestehen; ≥ 80 % Branch-Coverage auf den Route-Handlern.

Vor jeder Iteration AGENTS.md lesen.
Wenn alle Tests bestehen → <promise>COMPLETE</promise> ausgeben.

6.5 Business-Ziel / Performance

Schlechter Prompt
Mach die App schneller.
Output COMPLETE wenn sie schnell ist.

Warum er scheitert:
„Schneller" ist nicht messbar. Der Loop weiß nie, wann er fertig ist.

Guter Prompt
Performance-Ziel: Antwortzeit von GET /api/products (bei 10.000 Zeilen) von aktuell ~1.200 ms auf < 200 ms reduzieren.

Kontext:
- Stack: Laravel 11, PostgreSQL, Redis vorhanden.
- Aktuelle Query: Product::with('category','tags')->paginate(20) — keine Indizes.

Erlaubte Änderungen:
1. DB-Indizes über eine neue Migration hinzufügen.
2. Redis-Caching mit 5-Minuten-TTL auf das Query-Ergebnis implementieren.
3. Eager Loading durch gezieltes select() ersetzen falls nötig.

Bestehende Tests oder Response-Shape NICHT ändern.

Validierung:
`php artisan tinker --execute="echo app(\App\Services\ProductBenchmark::class)->measure();"``
muss eine Zahl unter 200 ausgeben.

Wenn der Benchmark < 200 liefert → <promise>COMPLETE</promise> ausgeben.

6.6 Hosting / Infrastruktur / DevOps

Schlechter Prompt
Füge Docker zu diesem Projekt hinzu.
COMPLETE wenn fertig.

Warum er scheitert:
Dev-Docker oder Prod-Docker? Welche Services? Welche Ports? Welche Umgebungsvariablen?

Guter Prompt
Docker Compose für lokale Entwicklung dieser Laravel-11-App einrichten.

Benötigte Services:
app — PHP 8.3-FPM, intern auf Port 9000
nginx — bedient app auf Host-Port 8080, Config in docker/nginx/default.conf
db — Postgres 16, user=laravel, password=secret, db=laravel, Port 5432
redis — Redis 7, Port 6379

Zu erstellende Dateien:
Dockerfile — php:8.3-fpm-alpine, composer install, APP_ENV=local
docker-compose.yml — alle vier Services mit Named Volumes
docker/nginx/default.conf

Regeln:
- `docker compose up -d` muss alle Services ohne Fehler starten.
- `docker compose exec app php artisan migrate --force` muss erfolgreich laufen.
- Keine bestehenden Quelldateien dürfen verändert werden.
- .env.example muss Einträge für DB_HOST=db, REDIS_HOST=redis erhalten.

Validierung: `docker compose up -d && sleep 5 && curl -s http://localhost:8080 | grep -q "Laravel" && echo OK`

Wenn Validierung OK ausgibt → <promise>COMPLETE</promise> ausgeben.

6.7 KI-Agent / Prompt Engineering

Schlechter Prompt
Bau einen KI-Agent, der Fragen zu unserer Doku beantworten kann.
COMPLETE wenn fertig.

Warum er scheitert:
Welches LLM? Welches Retrieval? Welche Tools? Keine Evaluierungskriterien. Kein Test für „es funktioniert."

Guter Prompt
RAG-Agent für unsere Markdown-Dokumentation (docs/-Ordner) bauen.

Stack: Python 3.12, LangChain 0.3, OpenAI (gpt-4o-mini für Embeddings + gpt-4o für Antworten), Chroma (lokal).

Schritte:
1. Alle .mdx/.md-Dateien in docs/ rekursiv chunken (chunk_size=800, overlap=80).
2. Via text-embedding-3-small embedden, in chroma_db/ persistieren (lokal).
3. Bei Anfrage: Top-5-Chunks holen, als Kontext an gpt-4o übergeben, Antwort + Quellen ausgeben.
4. CLI: `python agent.py "Was ist ralph-loop?"` → Antwort gedruckt, Quellen aufgelistet.

Abnahmekriterien:
1. `python agent.py "Was ist das Completion-Promise bei ralph-loop?"` gibt eine Antwort zurück, die das Wort „COMPLETE" enthält.
2. `python agent.py "Was ist snarktank/ralph?"` gibt eine Antwort zurück, die „Bash" erwähnt.
3. `pytest tests/test_agent.py -v` besteht (Tests schreiben falls nicht vorhanden).

Vor jeder Iteration AGENTS.md lesen.
Wenn alle drei Kriterien erfüllt → <promise>COMPLETE</promise> ausgeben.

6.8 Frontend / React-Komponente

Schlechter Prompt
Bau eine Datentabellen-Komponente in React.
COMPLETE wenn sie gut aussieht.

Warum er scheitert:
„Gut aussieht" ist kein Test. Ralph kann keinen visuellen Check durchführen. Jede Iteration produziert eine andere UI und der Loop endet nie.

Guter Prompt
Stack: React 19, TypeScript, TanStack Table v8, Tailwind CSS 4, Vitest + Testing Library.

<DataTable<T>>-Komponente in src/components/DataTable.tsx bauen:

Props:
data: T[]
columns: ColumnDef<T>[]
pageSize?: number (Standard 20)
onRowClick?: (row: T) => void

Features:
- Client-seitige Paginierung (pageSize Zeilen pro Seite, Zurück/Weiter-Buttons)
- Klick auf Spaltenheader → aufsteigend sortieren; zweiter Klick → absteigend
- Suchinput über der Tabelle filtert alle String-Spalten (case-insensitiv)
- Leerzustand: „Keine Ergebnisse gefunden" zentriert im Tabellenkörper

Tests in src/components/DataTable.test.tsx:
1. Rendert die korrekte Anzahl Zeilen pro Seite.
2. Sortierung auf Spalte „name": aufsteigende Reihenfolge entspricht Array.sort().
3. Suche „alice" mit Fixture-Daten gibt nur die Alice-Zeile zurück.
4. onRowClick wird mit der korrekten Zeile aufgerufen beim Klick.

`pnpm vitest run DataTable` muss alle 4 Tests bestehen.
TypeScript muss ohne Fehler kompilieren: `pnpm tsc --noEmit`.

Vor jeder Iteration AGENTS.md lesen.
Wenn Tests + Typecheck bestehen → <promise>COMPLETE</promise> ausgeben.

6.9 Datenbank-Migration

Schlechter Prompt
Füge eine Payments-Tabelle hinzu.
COMPLETE wenn fertig.

Warum er scheitert:
Welche Spalten? Welche Foreign Keys? Welche Indizes? Welche Validierung im Model?

Guter Prompt
Stack: Laravel 11, PostgreSQL.

Payments-Tabelle über eine neue Migration erstellen:

Spalten:
id uuid, Primary Key
order_id uuid, Foreign Key → orders.id (Cascade Delete)
amount decimal(10,2), not null
currency char(3), not null, Standard 'EUR'
status enum('pending','completed','failed','refunded'), Standard 'pending'
provider varchar(50), not null (z. B. "stripe", "paypal")
provider_ref varchar(255), nullable, unique
paid_at timestamp, nullable
created_at / updated_at timestamps

Indizes:
- orders(order_id)
- (status, created_at) Composite
- provider_ref unique

Model: app/Models/Payment.php
- $guarded = []
- Casts: amount → decimal:2, status → enum, paid_at → datetime
- Beziehung: belongsTo(Order::class)
- Scopes: scopePending(), scopeCompleted()

Tests: `php artisan test --filter PaymentTest` muss bestehen (Test erstellen).

`php artisan migrate --pretend` muss ohne Fehler laufen.
Wenn Migration + Tests bestehen → <promise>COMPLETE</promise> ausgeben.

6.10 Ziel ohne Code – Planung / Recherche

Schlechter Prompt
Schreibe ein technisches Spec für unser neues Feature.
COMPLETE wenn es fertig ist.

Warum er scheitert:
Keine Länge, keine Pflichtabschnitte, keine Definition von „fertig." Der Agent gibt in der ersten Iteration etwas Beliebiges aus und gibt sofort COMPLETE aus.

Guter Prompt
Technisches Spec für ein „Warteliste"-Feature in SPEC.md schreiben.

Pflichtabschnitte (alle müssen vorhanden und nicht leer sein):
## Überblick — 2–3 Sätze, das User-Problem
## User-Stories — ≥ 3 Abnahmekriterien-Geschichten
## Datenmodell — Tabellendefinitionen mit Spaltenname und Typ
## API-Endpunkte — HTTP-Methode, Pfad, Request-Body, Response
## Edge-Cases — ≥ 5 konkrete Edge-Cases mit Lösung
## Nicht im Scope — explizite Liste was NICHT enthalten ist
## Offene Fragen — ≥ 2 ungelöste Entscheidungen, die ein Mensch treffen muss

Regeln:
- Nur das Warteliste-Feature beschreiben; kein anderes Feature specen.
- Keinen Code schreiben.
- Gesamtlänge: 400–800 Wörter.

Validierung: `wc -w SPEC.md` muss eine Zahl zwischen 400 und 800 ausgeben.
Alle 7 Abschnitte müssen vorhanden sein: `grep -c "^## " SPEC.md` muss 7 ausgeben.

Wenn beide Validierungsbefehle bestehen → <promise>COMPLETE</promise> ausgeben.

7. Prompt-Checkliste

Vor dem Starten eines Loops diese Fragen mit Ja beantworten:

  • Stack angegeben — Sprache, Framework, Version, Test-Tool
  • Scope begrenzt — was NICHT geändert werden darf, ist explizit genannt
  • Abnahmekriterien sind Aussagen, keine Adjektive — „Antwortzeit < 200 ms" nicht „schnell"
  • Ein Shell-Befehl beweist Completion — Tests, grep, curl, wc
  • AGENTS.md referenziert — „Vor jeder Iteration AGENTS.md lesen" im Prompt
  • Completion-Promise ist eindeutig<promise>COMPLETE</promise>, nicht „fertig" oder „done"
  • --max-iterations gesetzt — verhindert endlose Loops bei kaputten Tasks

8. Token- und Kostenkontrolle

TechnikErsparnis
--model claude-haiku-4-5 für Explorations-Iterationen, Sonnet/Opus nur für finale5–10× günstiger pro Iteration
--max-iterations 10 beim ersten Lauf; nur bei Bedarf erhöhenVerhindert unkontrollierte Kosten
Prompt unter 2.000 Token haltenKürzerer Kontext = günstiger pro Aufruf
--no-commit + später squashenSaubere History, kein Kosteneinfluss
Große Features in 3–5 kleinere Loops aufteilenSchnellere Konvergenz, mehr Kontrolle

9. ralph-loop mit anderen Tools kombinieren

# 1. Mit snarktank/ralph prd.json aus einer Konversation generieren
/skill prd

# 2. Die Stories mit ralph-loop abarbeiten
/ralph-loop "$(cat prd.json)" --completion-promise "COMPLETE" --max-iterations 30

# 3. Ergebnis zur automatisierten Review an ralphex übergeben
ralphex --review --branch feature/mein-feature

# 4. Für parallele Tasks auf demselben Ergebnis → ralphy
ralphy --prd prd.json --parallel --max-parallel 3 --create-pr

Den empfohlenen Tages-Stack findest du in der Ralph-Übersicht.


10. Weiterführend