Fehler

Der Agent meldet „fertig“, aber das Feature funktioniert nicht: warum das passiert und wie man die Arbeit eines KI-Agenten prüft

Aktualisiert: · Skillaria.Top

Der Agent schreibt „fertig“, weil er seine Arbeit selbst bewertet und nur sieht, was er geprüft hat: meist Unit-Tests, die die Nahtstellen zwischen Modulen, Migrationen, Konfiguration und das Ende-zu-Ende-Szenario nicht sehen. Behoben wird das durch Struktur, nicht durch Prompting: eine Definition of Done in AGENTS.md, ein verpflichtender E2E-Lauf, ein Prüfskript, das allein den Status passing setzen darf, und ein unabhängiger Bewerter mit sauberem Kontext.

Warum das passiert

Jeder, der mit Agenten arbeitet, kennt die Situation: Der Agent hat „fertig“ geschrieben, aber das Feature funktioniert nicht. Er lügt nicht absichtlich. Er meldet, was er geprüft hat, und ist aufrichtig überzeugt, dass das genügt. Die Lücke zwischen Behauptung und Wirklichkeit ist eine Eigenschaft des Schemas „der Ausführende bewertet seine eigene Arbeit“, kein Fehler eines bestimmten Modells.

Dafür gibt es drei Gründe. Erstens neigen Modelle zu übersteigertem Selbstvertrauen: Die Sicherheit einer Antwort ist größer als ihre Genauigkeit. Zweitens bewertet der Agent seine Arbeit im selben Kontext, in dem er sie erledigt hat: Er hat sich bereits überzeugt, dass die Lösung richtig ist, und die Bitte „sei objektiver“ ändert den Kontext nicht. Drittens, und das ist am praktischsten: Dem Agenten stehen nur Teilprüfungen zur Verfügung. Unit-Tests werden schnell grün, während das Ende-zu-Ende-Szenario niemand ausgeführt hat.

Chronik eines typischen falschen „fertig“

Der Agent bekam die Aufgabe, das Zurücksetzen des Passworts umzusetzen. Er ändert das Datenbankschema, ergänzt einen Endpunkt, bindet eine E-Mail-Vorlage ein und führt die Unit-Tests aus — alles grün. Im Chat erscheint ein selbstsicheres „Fertig“.

In Wirklichkeit ist folgendes passiert:

  • die Migration ist auf halbem Weg abgebrochen und hat das Schema in einem inkonsistenten Zustand hinterlassen;
  • die E-Mail-Vorlage existiert, aber der Mail-Dienst (SMTP) ist in der Umgebung nicht konfiguriert — es geht keine E-Mail hinaus;
  • das Szenario „Nutzer klickt den Link → gibt ein neues Passwort ein → meldet sich an“ wurde nie ausgeführt.

Keines dieser Probleme konnten die Unit-Tests finden: Die Datenbank war gemockt, der E-Mail-Versand war gemockt, und einen durchgängigen Lauf gab es gar nicht.

Warum grüne Tests nicht beweisen, dass das Feature funktioniert

Ein Unit-Test prüft ein Modul isoliert: Abhängigkeiten sind durch Mocks ersetzt, die Außenwelt ist abgeschaltet. Das bringt Geschwindigkeit und Präzision, aber auch eine systematische Blindheit. Die Blindheit ist kein Fehler der Tests, sondern eine Folge ihres Entwurfs. Grüne Unit-Tests beweisen nur, dass jedes Teil für sich funktioniert, nicht dass die Teile zusammen funktionieren.

Es gibt vier Klassen von Defekten, die ein Unit-Test grundsätzlich nicht sieht:

FehlerklasseBeispielWarum ein Unit-Test sie nicht findet
Nicht passende SchnittstellenEine Komponente liefert einen relativen Pfad, die andere erwartet einen absolutenJede wird mit ihrem eigenen Mock getestet, der jedem Format „zustimmt“
Ausbreitung des ZustandsDas Datenbankschema hat sich geändert, aber der ORM-Cache hält das alteIm Unit-Test gibt es weder eine echte Datenbank noch einen Cache
Lebenszyklus von RessourcenDateihandles und Verbindungen werden nicht freigegeben, bei großen Dateien läuft der Speicher vollDer Test arbeitet mit kleinen Daten und endet, bevor die Ressourcen ausgehen
Umgebung und KonfigurationEine Umgebungsvariable fehlt, SMTP ist nicht eingerichtet, die Migration wurde nicht eingespieltDer Test startet die Anwendung nicht in ihrer echten Konfiguration

Die Prüfebenen sind ineinander verschachtelt: Alles, was Unit-Tests finden, finden auch Integrationstests, und alles, was Integrationstests finden, findet ein durchgängiger (E2E-)Lauf. Jede höhere Ebene sieht streng mehr. Deshalb darf das Recht, „fertig“ zu sagen, nur die oberste Ebene haben.

Das Problem messen: die Verifikationslücke

Bevor Sie etwas reparieren, zählen Sie. Nehmen Sie die letzten 10 vom Agenten abgeschlossenen Aufgaben. Beantworten Sie zu jeder zwei Fragen: Hat der Agent „fertig“ gesagt, und hat eine unabhängige Prüfung es bestätigt (Sie haben das Szenario selbst durchgespielt, der E2E-Test ist grün)? Der Anteil der Abweichungen ist Ihre Verifikationslücke (verification gap).

Diese Zahl ist aus zwei Gründen nützlich. Sie zeigt das Ausmaß des Problems, ohne Streit im Stil von „na ja, es scheint ganz gut zu laufen“. Und sie liefert eine Ausgangsbasis: Nach jeder Änderung am Prozess berechnen Sie sie auf einem ähnlichen Satz von Aufgaben neu und sehen, ob es geholfen hat.

Wirklich prüfen: drei Schichten der Validierung

Ein passender Vergleich ist die Werkstatt. Ein Auto durchläuft drei Stationen, und keine davon darf übersprungen werden.

  1. Diagnose (Sekunden): Syntax, Typen, Linter. Sie fängt Tippfehler und grobe Fehler ab.
  2. Prüfstand (Minuten): Unit- und Integrationstests, die Anwendung startet. Der Motor läuft, aber das Auto steht noch auf dem Hof.
  3. Probefahrt: ein durchgängiges Szenario so, wie es ein Nutzer durchläuft — über die API, den Browser oder die CLI, mit echter Datenbank und echter Konfiguration.

Die Regel für den Ablauf: Man fährt nicht zur Probefahrt, solange die Diagnose Fehler anzeigt, und man gibt das Auto nicht heraus, ohne die Probefahrt gemacht zu haben. Grüne Unit-Tests sind die bestandene zweite Station, nicht die dritte.

Praktisch ist es, alles über einen Einstiegspunkt zu bündeln:

# Makefile
check:
	ruff check .
	mypy app
	pytest tests/unit tests/integration

e2e:
	docker compose up -d db mailhog
	alembic upgrade head
	pytest tests/e2e

verify: check e2e

Hier startet E2E eine echte Datenbank und einen Test-Mailserver, spielt die Migrationen ein und durchläuft das Szenario vollständig. Eine fehlgeschlagene Migration oder eine nicht eingerichtete Mail zeigt sich sofort und nicht erst beim Kunden.

Definition of Done in AGENTS.md

Der Agent soll nicht selbst entscheiden, was „fertig“ bedeutet. Das Kriterium steht in der Anweisungsdatei und ist prüfbar formuliert:

## Definition of Done
Ein Feature ist nur dann fertig, wenn ALLE Punkte erfüllt sind:
1. make check endete mit Exit-Code 0
2. make e2e endete mit Exit-Code 0, das Szenario des Features ist in tests/e2e vorhanden
3. Migrationen laufen auf einer sauberen Datenbank durch und lassen sich zurückrollen
4. Neue Umgebungsvariablen sind in .env.example beschrieben
5. In PROGRESS.md steht, was gemacht und wie geprüft wurde

Das gilt NICHT als Zeichen der Fertigstellung:
- „Der Code ist geschrieben und sieht richtig aus“
- „Die Unit-Tests laufen durch“
- „Bei mir lokal funktioniert es“

Eine ausdrückliche Liste dessen, was nicht als Fertigstellung zählt, wirkt besser, als man denkt: Der Agent hört seltener beim ersten grünen Lauf auf.

Wer den Status „passing“ setzen darf

Der nächste Schritt ist, den Fertigstellungsstatus ganz aus den Händen des Agenten zu nehmen. Features werden in einer Liste geführt (zum Beispiel feature_list.json) mit den Feldern „Verhalten“, „Prüfbefehl“ und „Zustand“. Den Status passing darf nur ein Skript setzen: Es führt das gemeinsame make check aus, dann den Prüfbefehl des konkreten Features und hält bei Erfolg den Status zusammen mit dem Commit-Hash als Beleg fest. Schlägt es fehl, bleibt das Feature aktiv — ein „fast geklappt“ gibt es nicht.

{
  "id": "F04",
  "behavior": "Der Nutzer erhält eine E-Mail mit Link und ändert das Passwort",
  "verification": "pytest tests/e2e/test_password_reset.py",
  "state": "active"
}

Hier lauert eine Falle: Das Skript prüft genau das, was im Feld verification steht. Gehört eine Architekturprüfung zur Definition of Done, aber nicht zum Prüfbefehl des Features, bleibt die Lücke unsichtbar. Deshalb muss das gemeinsame make check alle Pflichtprüfungen enthalten, nicht nur die Tests.

Ein unabhängiger Bewerter

Die Selbstauskunft des Ausführenden ist nie eine unabhängige Quelle der Wahrheit. Die strukturelle Lösung besteht darin, die Rollen zu trennen:

  • der Planer zerlegt die Anforderung in Features mit Abnahmekriterien und schreibt keinen Code;
  • der Generator setzt ein Feature um und führt die Tests aus, entscheidet aber nicht, ob es fertig ist;
  • der Bewerter fällt ein Urteil anhand einer Checkliste der Kriterien.

Der Bewerter darf die Überlegungen des Generators nicht sehen, sonst übernimmt er dessen Selbstsicherheit. Er erhält nur die Anforderung, den Diff und die Ergebnisse der Läufe. In Claude Code lässt sich das bequem über einen Sub-Agenten umsetzen: Er hat einen eigenen sauberen Kontext. Noch besser, wenn der Bewerter ein anderes Modell ist: Es hat andere blinde Flecken. Die Bewertung „sieht gut aus“ ist untersagt: Jeder Befund muss sich auf eine Datei, eine Zeile oder ein Log beziehen.

Fehlermeldungen, die der Agent selbst beheben kann

Eine Prüfung ist nur dann nützlich, wenn ihre Ausgabe zur Korrektur führt. „Test failed“ oder ein nacktes AssertionError sagen dem Agenten nichts. Eine gute Meldung sagt, was kaputt ist, warum die Regel existiert und wie man es behebt:

FEHLER arch-check: app/services/refund.py importiert app/db/session.py
WARUM: Services greifen nicht direkt auf die Datenbank zu, nur über Repositories
FIX: Nutzen Sie RefundRepository aus app/repositories/refund.py

Dieses Format macht aus einem Verbot eine Hilfestellung: Der Agent behebt den Verstoß ohne Ihr Zutun.

Manuelle Prüfung: wo sie noch nötig ist

Automatik ersetzt den Menschen nicht, sie verengt aber seine Rolle. Manuelle Prüfung ist dort nötig, wo sich ein Kriterium schwer formalisieren lässt: das Aussehen der Oberfläche, die Texte, die Bequemlichkeit eines Ablaufs. Prüfen Sie nach einer Checkliste der Abnahmekriterien, nicht durch „mal ausprobieren“. Wenn Sie dem Agenten zum zweiten Mal denselben Kommentar schreiben, ist das ein Signal, ihn in eine automatische Prüfung umzuwandeln: einen Test, eine Linter-Regel oder einen grep-Guard in make check.

Wie E2E das Verhalten des Agenten schon vor dem Lauf verändert

Der Wert eines durchgängigen Tests liegt nicht nur in den gefundenen Fehlern. Weiß der Agent im Voraus, dass sein Ergebnis durch ein E2E-Szenario geprüft wird, schreibt er den Code anders: Er denkt an den Vertrag mit der Nachbarkomponente und nicht nur an seine eigene Funktion, behandelt die Fehlerpfade und verletzt keine Architekturgrenzen, weil ein Verstoß im Lauf auffallen würde. Die Prüfung wirkt als Einschränkung am Eingang, nicht nur als Filter am Ausgang.

Dabei gibt es eine Bedingung: E2E ist sinnvoll, wenn das System klare Grenzen hat. Bei einer Spaghetti-Architektur beweist ein durchgängiger Test nur, dass die Spaghetti funktionieren, und zeigt nicht, wo der Entwurf verletzt wurde. Deshalb werden zuerst Schichten und Abhängigkeitsregeln festgelegt und erst danach durchgängige Szenarien aufgebaut. Agenten kopieren gern die bestehenden Muster eines Repositorys, auch die schlechten, daher führt man Einschränkungen am besten vom ersten Tag an ein.

Häufige Fehler

  • Den Agenten bitten, „genauer zu prüfen“. Der Kontext bleibt derselbe, die Bewertung bleibt überhöht.
  • 100 % Testabdeckung als Garantie ansehen. Die Abdeckung durch Unit-Tests erfasst die Nahtstellen nicht.
  • Vor der Bestätigung des Features refaktorieren. Die Reihenfolge ist streng: Korrektheit → Leistung → Stil.
  • Im E2E-Test genau das mocken, was am häufigsten bricht. Datenbank, Migrationen und Konfiguration müssen im durchgängigen Test echt laufen; externe kostenpflichtige APIs darf man durch einen Test-Server ersetzen.

Fazit

Wenn der Agent „fertig“ geschrieben hat, das Feature aber nicht funktioniert, liegt das Problem fast immer im Prüfprozess und nicht am Modell. Zählen Sie die Verifikationslücke, verankern Sie die Definition of Done in AGENTS.md, machen Sie E2E verpflichtend, übergeben Sie den Fertigstellungsstatus einem Skript und ergänzen Sie einen unabhängigen Bewerter. Dieses Schema wird im Kurs zum Harness Engineering praktisch ausführlich behandelt.

Häufige Fragen

Kann ich den Agenten selbst die E2E-Tests ausführen lassen?

Ja, wenn der Befehl als verpflichtender Teil der Definition of Done in AGENTS.md steht und dem Agenten die nötigen Werkzeuge zur Verfügung stehen. Zuverlässiger ist es zusätzlich, ihn über einen Hook oder im CI laufen zu lassen, damit Sie nicht vom Gedächtnis des Agenten abhängen.

E2E-Tests sind langsam, muss man sie nach jeder Änderung ausführen?

Nein. Lint, Typen und Unit-Tests immer; E2E bei Änderungen, die mehr als eine Schicht betreffen, und vor der Erklärung, dass das Feature fertig ist.

Wie prüft man das Frontend, das der Agent gebaut hat?

Mit einem durchgängigen Test im Browser (etwa Playwright), der die echte Oberfläche bedient. Das Aussehen wird manuell nach einer Checkliste oder anhand von Screenshots geprüft, die der Bewerter durchsieht.

Reicht ein Code-Review durch einen Menschen anstelle des Bewerters?

Ein Review durch einen Menschen ist nützlich, aber teuer und skaliert nicht auf Stunden autonomer Arbeit. Ein Bewerter mit Bewertungsraster beseitigt offensichtliche Probleme, bevor sie den Menschen erreichen, und der Mensch entscheidet die strittigen Fragen.