Handbücher · Loop Engineering
Build: Der Zieltest
Lernziele
- Du schreibst „fertig“ als Shell-Funktion mit Exit-Code und lässt sie im Wrapper laufen, außerhalb der Reichweite des Agenten.
- Du baust einen Retry-Loop um
claude -p, der nur die Fehlerausgabe in den nächsten Versuch trägt und nach fünf Versuchen anhält. - Du erklärst, warum ein grüner Zieltest noch kein gutes Ergebnis beweist.
Drei Builds setzen einen funktionierenden Loop · engl. agent loopDie Schleife eines Agenten: denken, handeln, beobachten, wiederholen. Zwischen den Durchläufen wird der Kontext des Modells jedes Mal neu gebaut.Im Glossar um einen Coding-AgentEin System, in dem ein Sprachmodell seinen nächsten Schritt selbst wählt: Es ruft Werkzeuge auf, wertet das Ergebnis aus und macht weiter, bis das Ziel erreicht ist.Im Glossar zusammen, der ohne Oberfläche läuft. Dieser hier liefert das Gerüst. Du schreibst fertig als Skript, startest den Agenten, lässt den Zieltest · engl. goal testEin ausführbares Skript, das mit Exit-Code 0 endet, wenn die Arbeit fertig ist, etwa weil alle Tests laufen und der Linter nichts meldet. Es ist die wichtigste Stoppbedingung eines Loops.Im Glossar laufen, gibst Fehler zurück und begrenzt die Versuche. Die Beispiele nutzen den nicht-interaktiven Modus von Claude Code (claude -p). Jedes Kommandozeilen-Werkzeug, das einen Prompt annimmt und Dateien ändert, passt an dieselbe Stelle.
Du brauchst claude, jq, pytest und ruff auf dem Pfad und ein Repository mit Tests. pytest und ruff installierst du mit python3 -m pip install pytest ruff. Wir haben alles mit Claude Code 2.1.323 und dem Modell Haiku getestet (Stand: Nov. 2026).
Fertig als Skript
Der Zieltest ist der ganze Entwurf. Er muss ausführbar sein, binär antworten und außerhalb der Kontrolle des Agenten liegen. Hier besteht er aus Tests plus Linter:
goal_test() {
python3 -m pytest -q --tb=short && ruff check .
}
Exit-Code 0 heißt grün, alles andere heißt rot. Das ist die Regel „Der Loop prüft selbst“ aus Kritiker und Prüfung in ihrer kleinsten Form. Um diese Funktion herum steht der Rest. Speichere das Skript als ~/bin/goal-loop.sh, mach es mit chmod +x ~/bin/goal-loop.sh ausführbar und starte es im Ordner des Repositorys.
#!/usr/bin/env bash
# goal-loop.sh: lässt einen Coding-Agenten laufen, bis der Zieltest
# grün ist oder die Versuche aufgebraucht sind. Liegt außerhalb des
# Repos und startet im Repo-Ordner: ~/bin/goal-loop.sh
set -u
TASK="Make every test under tests/ pass. Do not delete, skip or weaken any test."
MAX_ATTEMPTS=5
BUDGET_PER_ATTEMPT=0.50 # USD, Schätzung des Clients
MODEL=haiku
goal_test() {
python3 -m pytest -q --tb=short && ruff check .
}
feedback=""
attempt=1
while [ "$attempt" -le "$MAX_ATTEMPTS" ]; do
printf '=== Versuch %s von %s ===\n' "$attempt" "$MAX_ATTEMPTS"
prompt="$TASK"
if [ -n "$feedback" ]; then
prompt="$TASK
The previous attempt failed the goal test with this output:
$feedback
Fix these specific failures."
fi
claude -p "$prompt" \
--model "$MODEL" \
--allowedTools "Read,Edit,Bash(python3 -m pytest *),Bash(ruff *)" \
--permission-mode acceptEdits \
--max-budget-usd "$BUDGET_PER_ATTEMPT" \
--setting-sources project,local \
--output-format json < /dev/null \
| jq -r '"Agent fertig: \(.num_turns) Runden, \(.total_cost_usd) USD"'
if feedback=$(goal_test 2>&1); then
printf 'PASS: Zieltest grün in Versuch %s\n' "$attempt"
exit 0
fi
printf 'FAIL: Zieltest noch rot\n'
attempt=$((attempt + 1))
done
printf 'STOP: %s Versuche verbraucht, zurück an den Menschen\n' "$MAX_ATTEMPTS"
exit 1Zeilen 7-10
Der Auftrag bleibt in jedem Versuch gleich und verbietet ausdrücklich, Tests zu löschen oder abzuschwächen. Darunter stehen die drei Stellschrauben: höchstens fünf Versuche, ein Budget pro Versuch und das Modell.
Den Auftrag lassen wir auf Englisch. Er geht an das Modell, und die Fehlerausgabe von pytest, die er mitbringt, ist ohnehin englisch. Alles, was du liest, schreibt das Skript auf Deutsch.
Was im Test-Repo passierte
Unser Test-Repo hat eine Datei rechnung.py mit drei Fehlern, einem überflüssigen Import und sechs Tests, von denen vier fehlschlagen. Der erste Lauf:
$ ~/bin/goal-loop.sh
=== Versuch 1 von 5 ===
Agent fertig: 12 Runden, 0.02132607 USD
PASS: Zieltest grün in Versuch 1
Haiku brauchte rund zwanzig Sekunden. Der Diff · engl. diffDie Gegenüberstellung zweier Stände einer Datei: Zeilen mit Plus kamen dazu, Zeilen mit Minus fielen weg. Git zeigt so, was ein Commit geändert hat.Im Glossar änderte nur rechnung.py, die Tests blieben unberührt. Für den zweiten Lauf haben wir einen Test ergänzt, der dem vorhandenen widerspricht: Ein Rabatt über 100 Prozent soll einmal einen Fehler auslösen und einmal auf 100 Prozent gedeckelt werden. Diesen Auftrag kann kein Agent erfüllen, ohne einen Test zu ändern. Für die Vorführung stand MAX_ATTEMPTS auf 2:
=== Versuch 1 von 2 ===
Agent fertig: 10 Runden, 0.016046519999999998 USD
FAIL: Zieltest noch rot
=== Versuch 2 von 2 ===
Agent fertig: 4 Runden, 0.01319012 USD
FAIL: Zieltest noch rot
STOP: 2 Versuche verbraucht, zurück an den Menschen
Der Agent reparierte brutto und summe und ließ beide Rabatt-Tests stehen. Das Skript hielt nach drei Cent an und meldete Exit-Code 1. Genau dafür ist die Obergrenze da: Ein Auftrag mit einem Widerspruch kommt zu dir zurück, bevor er über Nacht Geld verbrennt.
Warum jedes Teil da ist
Der Zieltest läuft im Wrapper. Der Agent startet pytest auch selbst, während er arbeitet. So orientiert er sich, und das ist in Ordnung. Das Urteil, das den Loop beendet, kommt aber aus deiner Shell, in einem Prozess, den das Modell nie berührt.
Nur die Fehlerausgabe wandert weiter. Jeder Versuch ist eine neue Session mit sauberem Kontext. Sie bekommt den Auftrag und das, was noch rot ist. Das Protokoll der Verwirrung aus dem letzten Versuch bleibt zurück. Das ist der frische Kontext pro Versuch aus Kontext-Hygiene.
Die Obergrenze ist klein, die Exit-Codes sind ehrlich. Fünf Versuche, dann Exit-Code 1 mit einem klaren Satz. Dadurch lässt sich das Skript einbauen: Eine CI-Pipeline kann darauf warten, cron kann bei Exit-Code 1 Alarm schlagen, und die nächsten beiden Builds legen sich darum.
Das Budget pro Versuch ist eine Schätzung. --max-budget-usd bricht eine Session ab, wenn die Kosten den Betrag erreichen. Laut Dokumentation rechnet Claude Code die Kosten selbst hoch, und die echten Ausgaben können über der Grenze liegen. Eine harte Grenze setzt du zusätzlich beim Anbieter, zum Beispiel als Ausgabenlimit für dein Konto.
Im selben Kontrolllauf wollte der Agent einmal zwei Befehle mit ; und | verketten. Die Freigabe Bash(python3 -m pytest *) deckt so eine Zeile nicht ab. Claude Code lehnte sie ab. Der Agent startete pytest und ruff danach einzeln und kam ohne Rückfrage ans Ziel. Abgelehnte Aufrufe stehen in der JSON-Ausgabe im Feld permission_denials. Wirf nach den ersten Läufen einen Blick hinein, bevor du Freigaben erweiterst.
Wer entscheidet das?
Ordne jede Entscheidung dem Teil des Loops zu, der sie trifft.
Noch offen (6)
- Ob der Loop nach diesem Versuch endet
- Welche Zeile in rechnung.py sich ändert
- Ob pytest zwischendurch läuft, um sich zu orientieren
- Was als fertig gilt
- Was nach STOP mit einem widersprüchlichen Auftrag passiert
- Welche Ausgabe in den nächsten Versuch wandert
Das Skript (0)
Der Agent (0)
Du (0)
Was es noch nicht kann
Dieser Loop nimmt alles an, was grün ist. Ein Agent, der die Tests auf hässlichem Weg besteht, kommt glatt durch: mit totem Code, verschluckten Fehlern oder einer Änderung knapp außerhalb dessen, was die Tests sehen. Auch ein geänderter Test fällt nicht auf, solange er grün ist. In unseren Läufen hat Haiku die Tests nicht angefasst. Ein Lauf ist allerdings kein Beweis für den nächsten. Build: Der Kritiker setzt einen Richter genau für diese Fälle dazu.
Merksätze
- Fertig ist eine Shell-Funktion mit Exit-Code. Sie läuft im Wrapper, nach dem Agenten.
- Jeder Versuch ist eine neue Session. Weiter wandert nur die Fehlerausgabe des letzten Zieltests.
- Fünf Versuche und ehrliche Exit-Codes machen das Skript zu einem Baustein für CI, cron und die nächsten Builds.
--max-budget-usdist eine Schätzung des Clients. Die harte Grenze setzt du beim Anbieter.- Das Skript liegt außerhalb des Repos. Die Tests liegen im Repo und brauchen einen eigenen Schutz.
Prüf dich selbst
Denkfrage
Warum genügt es nicht, den Agenten am Ende zu fragen, ob alle Tests grün sind?
Seine Antwort beschreibt die Arbeit. Gemessen hat sie niemand. Er kann die Tests gar nicht oder die falschen gestartet haben. Das Skript lässt den Zieltest selbst laufen und wertet nur den Exit-Code aus.
Denkfrage
Dein Loop hält dreimal hintereinander nach fünf Versuchen an. Was tust du?
Du liest die letzte Fehlerausgabe, bevor du die Obergrenze erhöhst. Oft steckt ein Widerspruch im Auftrag, ein kaputter Test oder eine fehlende Freigabe in permission_denials dahinter. Mehr Versuche ändern daran nichts, sie kosten nur mehr.
Prüf dich selbst
Selbst nachweisen
0 von 3 erledigt
Quellen
- Run Claude Code programmatically
claude -p, --output-format json mit total_cost_usd, --allowedTools, Hinweis zur Budgetgrenze.
- CLI reference
--permission-mode, --max-budget-usd, --max-turns, --setting-sources.