Zum Inhalt springen
coden

Programmieren mit KI-Agenten: Auftrag, Tests und Kontrolle

Wie du einem Agenten einen Auftrag mit Akzeptanzkriterien schreibst, warum du in kleinen Schritten arbeiten solltest, ein Experiment mit Mutationstesten (wie viele Fehler Happy-Path-Tests, Randfälle und eigenschaftsbasierte Tests entdecken), wer die Tests schreiben sollte und worauf du achten solltest.

10 Minuten Matěj Žalmánek 2026-10-16

Serie KI-Grundlagen Teil 51 von 52
  1. Landkarte der künstlichen Intelligenz: was was ist und wie alles zusammenpasst
  2. Eine kurze Geschichte der künstlichen Intelligenz: von Turing bis zu Sprachmodellen
  3. Daten, Modell, Training, Inferenz: die Grundbegriffe an einem einzigen Beispiel
  4. Mathematische Notation lesen: Symbole, Indizes, Summen und Quantoren
  5. Mengen und Relationen: der Baukasten, aus dem Definitionen in der KI bestehen
  6. Funktionen und Abbildungen: warum jedes KI-Modell eine Funktion ist
  7. Logik und Beweise: die Regeln, nach denen KI schließt
  8. Graphen und Bäume: die Struktur, auf der Suche und neuronale Netze stehen
  9. Lineare Algebra für maschinelles Lernen: Vektoren, Matrizen und warum KI vor allem multipliziert
  10. Ableitungen, Gradient und Kettenregel: wie ein Modell herausfindet, in welche Richtung es lernen soll
  11. Gradientenabstieg: wie ein Modell lernt
  12. Was ist Wahrscheinlichkeit: vom Würfelwurf zu den Kolmogorow-Axiomen
  13. Bedingte Wahrscheinlichkeit und der Satz von Bayes: wie du deine Einschätzung änderst, wenn du etwas Neues erfährst
  14. Zufallsvariable und Verteilungsfunktion: eine Funktion, die alles über eine Verteilung verrät
  15. Dichtefunktion und Wahrscheinlichkeitsfunktion: warum eine stetige Zufallsvariable in einem Punkt die Wahrscheinlichkeit null hat
  16. Erwartungswert, Varianz und Kovarianz: wo eine Verteilung liegt, wie breit sie ist und was womit zusammenhängt
  17. Überblick über Wahrscheinlichkeitsverteilungen: Bernoulli-, Binomial-, Poisson-, Gleich-, Normal- und Exponentialverteilung
  18. Gesetz der großen Zahlen und zentraler Grenzwertsatz: Warum sich Durchschnitte so vorhersagbar verhalten
  19. Statistik und Parameterschätzung: Maximum Likelihood, MAP und Konfidenzintervalle
  20. Informationstheorie: Entropie, Kreuzentropie und KL-Divergenz — woher Verlustfunktionen kommen
  21. Zustandsraumsuche: BFS, DFS, UCS und A* mit Animationen
  22. Spiele und Gegner: Minimax, Alpha-Beta-Suche und Monte Carlo Tree Search
  23. Constraint Satisfaction: Backtracking, Constraint-Propagierung und AC-3 an Sudoku und einer Karte tschechischer Regionen
  24. Logik und wissensbasierte Systeme: Vorwärts- und Rückwärtsverkettung, Resolution und Expertensysteme
  25. Planung: STRIPS, die Blockwelt, Heuristiken aus Relaxierung und Planung in KI-Agenten
  26. Lokale Suche und simulierte Abkühlung: ein Handlungsreisender durch die tschechischen Regionalhauptstädte
  27. Genetische Algorithmen: Population, Kreuzung, Mutation und Selektion am Rucksack für die Wanderung
  28. Evolutionsstrategien und genetische Programmierung: von der 1/5-Erfolgsregel zum Keplerschen Gesetz
  29. Ameisenkolonieoptimierung: wie eine Pheromonspur einen kurzen Weg findet
  30. Partikelschwarmoptimierung: Trägheit, eigene Erfahrung und die Erfahrung des Schwarms
  31. Neuroevolution: Evolution der Gewichte und der Gestalt neuronaler Netze und der Algorithmus NEAT
  32. Fuzzy-Mengen und Zugehörigkeitsfunktionen: wie man „etwas warm“ aufschreibt
  33. Fuzzy-Logik und Wahrscheinlichkeit: warum „etwas warm“ nicht „wahrscheinlich warm“ bedeutet
  34. Fuzzy-Logik: t-Normen, Implikationen und WENN–DANN-Regeln
  35. Fuzzy-Regelung: Mamdani und Sugeno vom Ventilator bis zum Pendel auf dem Wagen
  36. Fuzzy-Clustering: der Algorithmus fuzzy c-means und Punkte zwischen den Clustern
  37. Neuro-Fuzzy-Systeme (ANFIS): Fuzzy-Regeln, die aus Daten lernen
  38. Markow-Ketten: Übergangsmatrix, stationäre Verteilung und PageRank
  39. Hidden-Markov-Modelle: der Viterbi-Algorithmus und das unehrliche Kasino
  40. Bayessche Netze: warum das Gras nass ist und was das für den Rasensprenger bedeutet
  41. Der Kalman-Filter: wie man aus ungenauen Messungen schätzt, wo ein Auto ist und wie schnell es fährt
  42. Arten des maschinellen Lernens: überwacht, unüberwacht, halbüberwacht und mehr
  43. Prompts, Kontext und RAG: wie man einem Modell die richtigen Informationen gibt
  44. Variational Autoencoder (VAE): neue Bilder aus einem stetigen latenten Raum
  45. Generative Adversarial Networks (GAN): der Fälscher gegen den Detektiv
  46. Diffusionsmodelle: wie aus reinem Rauschen ein Bild wird
  47. Markow-Entscheidungsprozesse: Zustände, Aktionen, Belohnungen und Bellman-Gleichungen
  48. Q-Learning und DQN: den Wert von Aktionen lernen, von der Tabelle bis zum neuronalen Netz
  49. Policy Gradient und PPO: die Strategie direkt lernen
  50. Wie ein KI-Agent funktioniert: ein Sprachmodell in einer Schleife mit Werkzeugen
  51. Programmieren mit KI-Agenten: Auftrag, Tests und Kontrolle
  52. Bewertung und Sicherheit von KI-Systemen: wie man misst und wie man überwacht

Der ganze Lehrplan der Serie 52 von 83 Themen fertig

Was du mitnimmst
  • Was in einen Auftrag für einen Agenten gehört, damit er weiß, wann er fertig ist.
  • Wie viele Fehler unterschiedlich gründliche Tests entdecken und warum es auf Randfälle ankommt.
  • Welche Fehler Agenten machen und wie du sie bei der Kontrolle findest.

Vorher gut zu wissen: Wie ein KI-Agent funktioniert: ein Sprachmodell in einer Schleife mit Werkzeugen, Wie man an die Entwicklung mit KI-Agenten herangeht

Im letzten Teil haben wir zerlegt, wie ein Agent im Inneren funktioniert. Dieser Teil ist praktisch: wie du ihm Arbeit gibst, wie du sie kontrollierst und wie du den Überblick behältst, was im Projekt passiert. Die meisten Ratschläge lassen sich in einem Satz zusammenfassen — du musst dem Agenten einen Weg geben, selbst zu erkennen, dass er fertig ist, und du brauchst einen Weg, das zu überprüfen. An einem Experiment zeigen wir, wie sehr es darauf ankommt.

Der Teil knüpft an Wie ein KI-Agent funktioniert und Wie man an die Entwicklung mit KI-Agenten herangeht an, wo mehr über die Rolle des Menschen steht und darüber, warum du den Agenten zuerst eine Lösung vorschlagen lassen solltest.

1. Der Auftrag: was, wo und woran man erkennt, dass es fertig ist

Definition — Akzeptanzkriterien

Akzeptanzkriterien sind überprüfbare Bedingungen, die das Ergebnis erfüllen muss, damit die Aufgabe erledigt ist: konkretes Verhalten („gibt für einen leeren Warenkorb 0 CZK zurück“), Tests, die bestehen müssen, ein Befehl, der ohne Fehler durchlaufen muss. Anders als bei einer Zielbeschreibung lässt sich bei ihnen eindeutig entscheiden, ob sie gelten.

Einfach gesagt

„Räum dein Zimmer auf“ ist ein Ziel. „Auf dem Boden liegt nichts, das Bett ist gemacht und der Mülleimer ist geleert“ sind Akzeptanzkriterien. Erst mit ihnen lässt sich sagen, ob aufgeräumt ist — und das Kind (oder der Agent) weiß, wann es aufhören kann.

Definition — Spezifikation als Vorbedingung und Nachbedingung

Präzise lassen sich Akzeptanzkriterien als Hoare-Tripel aufschreiben (C. A. R. Hoare, 1969; die heutige Schreibweise mit geschweiften Klammern): {P} C {Q} besagt: Wenn vor dem Start des Programms C die Bedingung P gilt und C terminiert, gilt danach die Bedingung Q. P ist die Vorbedingung (was das Programm von der Eingabe erwarten darf), Q die Nachbedingung (was für das Ergebnis gelten muss). Ein Programm ist bezüglich der Spezifikation korrekt, wenn das Tripel für jeden Zustand gilt, der P erfüllt.

{ castka ≥ 0 }   s := sazba(castka)   { s = 0 für castka < 1 000;  s = 0,05 für 1 000 ≤ castka < 5 000;  s = 0,1 sonst }

castka ≥ 0
Vorbedingung: Für einen negativen Betrag (castka) übernimmt die Funktion keine Gewähr — sie steht da, damit klar ist, was wir nicht testen müssen und worauf der Aufrufer achten muss
s := sazba(castka)
das Programm, über das wir sprechen (die Satzfunktion); die Spezifikation sagt nichts darüber, wie es geschrieben ist, also kann der Agent sie beliebig erfüllen
s = 0 / 0,05 / 0,1
Nachbedingung: genau drei Stufen und ihre Grenzen (≤ oder <) — genau hier passieren die meisten Fehler, deshalb gehören die Werte 999, 1 000, 4 999 und 5 000 in die Akzeptanzkriterien
Die Zeile „Fertig, wenn“ im Auftrag unten ist dieselbe Nachbedingung, so aufgeschrieben, dass sie sich mit einem Test überprüfen lässt.

Ein guter Auftrag für einen Agenten hat vier Teile. Nicht weil das ein Ritual wäre, sondern weil jeder Teil einer Sache entspricht, die der Agent sonst raten müsste:

Ziel: Zeige auf der Warenkorbseite einen Rabatt je nach Betrag an (ab 1 000 CZK 5 %, ab 5 000 CZK 10 %).

Kontext: Die Preisberechnung ist in src/kosik.js, die Vorlage in pohledy/kosik.ejs.
Ähnlich wird der Versand in der Funktion doprava() berechnet — halte dich an denselben Stil.

Einschränkungen: Ändere das Datenformat in der Datenbank nicht. Keine neuen Bibliotheken.

Fertig, wenn:
- die Funktion sazba(castka) 0 / 0,05 / 0,1 zurückgibt und die Tests in test/kosik.test.js bestehen,
- du Tests für die Grenzen 999, 1 000, 4 999 und 5 000 CZK hinzufügst,
- npm test ohne Fehler durchläuft und du mir den Unterschied zeigst (git diff).

Der Kontext erspart dem Agenten die Suche (und dir Tokens — siehe Kosten des Kontexts). Die Einschränkungen verhindern, dass er das Problem anders „löst“, als du willst. Und die Akzeptanzkriterien geben ihm die Rückmeldung, ohne die lange Aufgaben zerfallen.

2. Kleine Schritte, häufige Kontrollen

Im Teil über Agenten haben wir ausgerechnet, dass eine Aufgabe aus zwanzig Schritten, von denen jeder zu 90 % gelingt, ohne Kontrolle nur in 12 % der Fälle klappt. Dieselbe Regel gilt für dich: Je größer das Stück Arbeit, das der Agent auf einmal abliefert, desto schwerer lässt es sich kontrollieren und desto teurer ist die Korrektur. Praktische Folgen:

  • Eine Änderung = eine Aufgabe. Teile eine große Sache in Schritte, die sich einzeln kontrollieren lassen (erst die Berechnung, dann die Anzeige, dann die Änderung der Daten).
  • Versionsverwaltung als Sicherheitsnetz. Vor jeder Aufgabe ein sauberer Stand in git, danach ein Commit. Wenn etwas schiefgeht, kehrst du mit einem einzigen Befehl zurück.
  • Lies den Diff, nicht die Zusammenfassung. Das „fertig, alles funktioniert“ des Agenten ist eine Behauptung. Der Unterschied im Code (git diff) und die Testausgabe sind Beweise.

PErfolg = pn

p
Wahrscheinlichkeit, dass ein einzelner Schritt gelingt; sie steht da, weil jeder Schritt eine weitere Gelegenheit für einen Fehler ist
n
Anzahl der Schritte zwischen zwei Kontrollen; sie steht im Exponenten, weil sich die Wahrscheinlichkeiten unabhängiger Schritte multiplizieren — und mit jedem weiteren Schritt ohne Kontrolle sinkt die Chance geometrisch, nicht linear
PErfolg
Chance, dass der ganze Abschnitt zwischen zwei Kontrollen gut ausgeht und nichts neu gemacht werden muss
Vereinfacht: Schritte scheitern unabhängig voneinander, und ein Fehler wird erst bei der Kontrolle bemerkt.
Beispiel — wie viel Arbeit eine Kontrolle nach jeweils fünf Schritten spart

Ohne Kontrolle: 0,920 = 0,1216, also 12 %. Mit einer Kontrolle nach jeweils fünf Schritten: 0,95 = 0,5905 für jeden der vier Abschnitte.

Nehmen wir an, die Kontrolle erkennt den Fehler immer und ein misslungener Abschnitt wird neu gemacht. Die Zahl der Versuche bis zum ersten Erfolg ist geometrisch verteilt mit dem Erwartungswert 1 / PErfolg. Die ganze Aufgabe ohne Kontrolle kostet also im Schnitt 20 / 0,1216 ≈ 165 Schritte, mit einer Kontrolle nach jeweils fünf 4 · 5 / 0,5905 ≈ 34 Schritte und mit einer Kontrolle nach jedem Schritt 20 / 0,9 ≈ 22 Schritte. Kleine Schritte erhöhen nicht die Chance, dass ein Schritt gelingt — sie verkleinern, wie viel Arbeit bei einem Fehler weggeworfen wird.

3. Tests als Sicherung: ein Experiment

Wie sehr helfen Tests? Wir nehmen fünf kleine Funktionen, wie sie ein Agent ständig schreibt — Durchschnitt, Schaltjahr, Kürzen eines Textes, binäre Suche und Rabatt je nach Betrag — und machen daraus absichtlich kaputte Versionen.

Definition — Mutationstesten

Ein Mutant ist eine Kopie des Programms mit einer kleinen automatischen Änderung: < statt <=, - 1 statt + 1, || statt &&, eine andere Konstante. Eine Testsuite entdeckt einen Mutanten, wenn mindestens ein Test an ihm fehlschlägt. Der Anteil entdeckter Mutanten misst, wie gut die Tests vor Fehlern schützen. Mutanten, die sich genauso verhalten wie das Original (äquivalente), werden nicht gezählt.

Einfach gesagt

Die Probe beim Rauchmelder: Du zündest absichtlich eine kleine Kerze an und schaust, ob er piept. Hier baust du absichtlich einen kleinen Fehler in den Code ein — genau so einen, wie ihn Menschen und Agenten machen — und schaust, ob die Tests es merken.

Aus dem Quellcode der fünf Funktionen sind automatisch 32 Mutanten entstanden. 3 davon haben sich auf 2 000 zufälligen Eingaben nie vom Original unterschieden (etwa a[s] <= x statt a[s] < x in der binären Suche, wo die vorige Zeile die Gleichheit schon behandelt hat), es bleiben also 29 echte Fehler. Gegen sie treten vier Teststufen an, jede enthält auch die vorherigen:

  • Happy Path — ein oder zwei typische Beispiele pro Funktion. Solche Tests entstehen, wenn man nur „füge Tests hinzu“ sagt.
  • Randfälle — leeres Array, Stufengrenzen (999, 1 000, 4 999, 5 000 CZK), die Jahre 1900, 2000 und 2100, ein Text genau an der Längengrenze.
  • Eigenschaften — 200 zufällige Eingaben und Regeln, die immer gelten müssen: Das Ergebnis des Kürzens hat höchstens max Zeichen und ist ein Anfang des Textes, der gefundene Index zeigt wirklich auf die gesuchte Zahl, der Durchschnitt stimmt mit einer anders geschriebenen Berechnung überein.
0 %25 %50 %75 %100 % 0 von 29 keine 15 von 29 Happy Path 26 von 29 + Randfälle 28 von 29 + Eigenschaften Teststufe (kumulativ, jede enthält die vorherige) entdeckte Fehler (% von 29)
Wie viele von 29 Fehlern die Tests entdecken

Happy-Path-Tests fangen nur 15 von 29 Fehlern (52 %). Sie bestehen auch mit einer Funktion, die für ein leeres Array 1 zurückgibt, mit einem Rabatt, der erst ab 1 001 CZK beginnt, oder mit einer binären Suche, die in einem Array mit einem Element nichts findet. Randfälle heben den Anteil auf 90 %, Eigenschaftstests auf 97 %.

FunktionMutantenHappy Path+ Randfälle+ Eigenschaften
prumer5455
jePrestupny8557
zkrat3233
najdi6366
sazba7177

MS = |O| / ( |M| − |E| )

MS
Mutation Score — der Anteil echter Fehler, den die Testsuite fängt — die Höhe eines Balkens im Diagramm
|O|
Anzahl entdeckter Mutanten (mindestens ein Test ist an ihnen fehlgeschlagen, oder sie sind abgestürzt bzw. in eine Endlosschleife geraten); das, was die Tests können
|M|
alle erzeugten Mutanten — der Vorrat an Fehlern, an dem wir die Tests messen
|E|
äquivalente Mutanten; sie werden abgezogen, weil sie sich vom Original nicht unterscheiden und daher kein Test sie entdecken kann — sonst kämen nicht einmal perfekte Tests auf 100 %

Bei uns ist |M| = 32 und |E| = 3, der Nenner ist also 29. Happy Path: 15 / 29 = 0,517; mit Randfällen 26 / 29 = 0,897; mit Eigenschaften 28 / 29 = 0,966. Das Verfahren, mit dem die Zahlen entstanden sind:

für jede Funktion f:
    original = kompiliere(Code von f)
    für jeden Operator (von → nach) und jedes Vorkommen von von im Code:
        mutant = kompiliere(Code, in dem dieses Vorkommen von von durch nach ersetzt ist)
        wenn sich der Mutant auf 2 000 zufälligen Eingaben nie vom Original unterscheidet:
            äquivalent — wird nicht gezählt, weiter
        für jede Teststufe u:
            entdeckt[u] = irgendein Test der Stufe u schlägt am Mutanten fehl,
                          stürzt ab oder gerät in eine Endlosschleife (Abbruch nach 10 000 Durchläufen)
MS(u) = Anzahl entdeckter auf Stufe u / Anzahl nicht äquivalenter

Am interessantesten ist das Schaltjahr. Durch die Randfälle kommen Mutanten durch, die jedes gerade Jahr für ein Schaltjahr halten oder die durch zehn teilbare Jahre ausschließen — also auch das Jahr 2020. Tests mit den Jahren 1900, 2000 und 2100 merken das nicht, weil die Mutanten auf diese zufällig richtig antworten. Der Eigenschaftstest mit zufälligen Jahren findet beide Fehler. Der einzige Mutant, der alles überlebt hat, ist (rok % 4 === 0 && rok % 100 !== 0) || rok % 200 === 0: Er unterscheidet sich nur bei Jahren, die durch 200, aber nicht durch 400 teilbar sind (200, 600, 1000…), und unter den 200 zufälligen Jahren war kein solches. Auch zufällige Tests sind nicht allmächtig.

Püberlebt = ( 1 − q )n

q
Anteil der Eingaben, auf denen sich der Mutant vom Original unterscheidet; 1 − q ist die Chance, dass eine einzelne zufällige Eingabe ihn nicht erkennt
n
Anzahl zufälliger Eingaben; die Eingaben werden unabhängig gezogen, also multiplizieren sich die Chancen für „nicht erkannt“
Püberlebt
Wahrscheinlichkeit, dass alle n Eingaben den Mutanten verfehlen — der Fehler kommt durch, obwohl die Eigenschaft richtig geschrieben ist
Zufällige Tests fangen zuverlässig Fehler, die sich oft zeigen; seltene Fehler nur mit sehr vielen Eingaben.
Beispiel — warum der Mutant mit dem durch 200 teilbaren Jahr überlebt hat

Der Eigenschaftstest zieht Jahre von 0 bis 2 399. Der Mutant rok % 200 unterscheidet sich vom Original nur bei 6 davon (200, 600, 1000, 1400, 1800, 2200), also ist q = 6 / 2400 = 0,0025, und bei n = 200 überlebt er mit der Wahrscheinlichkeit (1 − 0,0025)200 = 0,606 — ungefähr in sechs von zehn Versuchen. Dass er überlebt hat, war also kein Pech, sondern das erwartete Ergebnis.

Der Mutant rok % 2 unterscheidet sich bei 600 Jahren (q = 0,25): Er überlebt mit der Wahrscheinlichkeit 0,75200 ≈ 10-25, also praktisch nie. Der Mutant rok % 10 unterscheidet sich bei 96 Jahren (q = 0,04) und überlebt mit der Wahrscheinlichkeit 0,96200 = 0,00028.

Wie viele Eingaben bräuchte man, um den Mutanten mit dem Jahr 200 mit einer Wahrscheinlichkeit von 95 % zu fangen? Aus (1 − q)n ≤ 0,05 folgt n ≥ ln 0,05 / ln(1 − q) = −2,996 / −0,00250 ≈ 1 196,8, also 1 197 Eingaben. Billiger ist es, den Randfall Jahr 200 oder 600 hinzuzufügen — wer die Kalenderregel kennt, weiß, wo er suchen muss.

4. Wer die Tests schreiben sollte

Die Tests im Experiment haben wir nach der Spezifikation geschrieben (was die Funktion tun soll), nicht nach dem Code. Das ist wesentlich. Wenn ein Agent Code schreibt und gleich die Tests dazu, besteht die Gefahr, dass die Tests nur wiederholen, was der Code tut — samt seinen Fehlern. Sie bestehen und beweisen nichts. Was hilft:

  • Tests (oder wenigstens ihre Liste) zuerst. Lass den Agenten zuerst die Tests nach dem Auftrag schreiben, lies sie, und erst dann den Code. Die Akzeptanzkriterien aus dem Auftrag sind genau diese Liste.
  • Randfälle aufzählen. Leere Eingabe, ein einziges Element, Stufengrenzen, negative Zahlen, diakritische Zeichen. Der Agent ergänzt sie oft selbst, aber nur, wenn du sie willst.
  • Eigenschaften, wo es geht. „Ein sortiertes Array bleibt sortiert“, „kodieren und dekodieren = ursprünglicher Text“, „die neue Berechnung liefert dasselbe wie die alte“. Bibliotheken wie fast-check (JavaScript) oder Hypothesis (Python) erzeugen die zufälligen Eingaben selbst.
Definition — eigenschaftsbasiertes Testen (property-based testing)

Statt konkreter Paare aus Eingabe und Ausgabe schreibt man eine Eigenschaft auf, die für alle Eingaben gelten muss, und ein Werkzeug prüft sie an vielen zufällig erzeugten Eingaben. Findet es ein Gegenbeispiel, verkleinert es dieses meist auf den einfachsten fehlschlagenden Fall. Bekannt gemacht hat die Idee die Bibliothek QuickCheck (Koen Claessen und John Hughes, 2000).

Einfach gesagt

Du fragst nicht „wie viel ist 7 + 5“, sondern „stimmt es, dass a + b immer dasselbe ist wie b + a?“ — und lässt den Computer tausend zufällige Paare ausprobieren. Er findet Fälle, an die du nicht gedacht hättest.

Eine Eigenschaft ist eine Aussage mit dem Quantor „für alle“. Für die Funktion zkrat (kürzen) aus dem Experiment schreibt der Test sie so:

∀ t, m ≥ 1: |v| ≤ m ∧ ( |t| ≤ m ⇒ v = t ) ∧ ( |t| > m ⇒ v = t[0, |v| − 1) + „…“ ),  wobei v = zkrat(t, m)

∀ t, m ≥ 1
für jeden Text und jede Länge — deshalb lässt sie sich an zufälligen Eingaben prüfen, und wir müssen das richtige Ergebnis nicht im Voraus kennen
|v| ≤ m
das Hauptversprechen der Funktion: Das Ergebnis passt hinein; fängt den Mutanten, der vergisst, die „…“ mitzuzählen (slice(0, max))
|t| ≤ m ⇒ v = t
ein kurzer Text bleibt unverändert; ohne das käme eine Funktion durch, die immer kürzt
v = t[0, |v| − 1) + „…“
das gekürzte Ergebnis ist der Anfang des ursprünglichen Textes und endet mit einer Auslassung; ohne das käme eine Funktion durch, die zum Beispiel einen leeren Text zurückgibt
t[0, k) sind die ersten k Zeichen des Textes t. Die Eigenschaft sagt nicht, wie viele Zeichen genau übrig bleiben — nur, was gelten muss; dadurch ist sie allgemeiner als ein einzelnes konkretes Beispiel.

Ein Werkzeug für eigenschaftsbasiertes Testen macht dann ungefähr Folgendes:

für i = 1 … N:                       # N liegt meist bei 100 bis 1 000
    x = erzeuge eine zufällige Eingabe
    wenn eigenschaft(x, f(x)) nicht gilt:
        # Verkleinern: suche eine einfachere Eingabe, an der es immer noch scheitert
        solange es y = vereinfache(x) gibt, an dem die Eigenschaft nicht gilt:
            x = y                    # kürzerer Text, kleinere Zahl, kürzeres Array
        melde Gegenbeispiel x
melde „N Eingaben bestanden“         # kein Beweis, nur eine gute Nachricht

Die letzte Zeile ist wesentlich. Tests, auch zufällige, beweisen keine Korrektheit — sie suchen nur Gegenbeispiele. Edsger Dijkstra hat es 1969 so gesagt: Testen kann die Anwesenheit von Fehlern zeigen, nie ihre Abwesenheit. Die Wahrscheinlichkeit, dass ein seltener Fehler durchrutscht, haben wir oben ausgerechnet.

5. Worauf du achten solltest

Agenten machen andere Fehler als Menschen. Die häufigsten, nach denen es sich bei der Kontrolle zu suchen lohnt:

  • „Fertig“ ohne Überprüfung. Der Agent meldet Erfolg, ohne die Tests ausgeführt zu haben, oder hat sie nur teilweise ausgeführt. Verlange die Testausgabe.
  • Tests ändern statt Code. Wenn ein Test fehlschlägt, ist die einfachste „Korrektur“, den erwarteten Wert zu ändern oder den Test zu löschen. Such im Diff nach Änderungen an Tests.
  • Unterdrückte Fehler. Ein leerer catch, ein Standardwert statt eines Fehlers, ein fest eingetragenes Ergebnis für die Testeingabe.
  • Arbeit, um die niemand gebeten hat. Eine umgeschriebene Datei, die mit der Aufgabe nichts zu tun hat, eine neue Bibliothek, ein „verbessertes“ Datenformat. Einschränkungen im Auftrag und kleine Schritte halten das in Grenzen.
  • Geheimnisse und unumkehrbare Schritte. Passwörter im Code, Löschen von Dateien, Schreiben in die Produktionsdatenbank, Pushen in ein entferntes Repository. Lass dir diese Aktionen zur Freigabe vorlegen.

6. Den Überblick behalten

Ein Agent erinnert sich zwischen Sitzungen an nichts. Was er jedes Mal wissen soll — wie das Projekt gestartet und getestet wird, welche Konventionen gelten, was zu vermeiden ist — gehört in eine Datei mit Anweisungen im Wurzelverzeichnis des Projekts, die der Agent zu Beginn liest. Schreib dort hinein, worauf du den Agenten mehr als einmal hinweisen musstest. Und was nur du weißt (warum etwas so gemacht ist, was ausprobiert wurde und nicht funktioniert hat), gehört in Commits und Dokumentation — für den Agenten und für dich selbst in einem halben Jahr.

Der letzte Rat ist der einfachste: Bleib dran. Nicht unbedingt am Computer — der Agent kann allein arbeiten, und du schaust nach, wenn er eine Entscheidung braucht. Dafür ist coden entstanden: Auftrag, Freigabe von Schritten und Kontrolle des Ergebnisses hast du auf dem Handy, während der Agent auf deinem Computer arbeitet.

7. Zusammenfassung

  • Ein guter Auftrag: Ziel, Kontext (Dateien, Vorbilder), Einschränkungen und überprüfbare Akzeptanzkriterien.
  • Kleine Schritte, nach jedem ein Commit, und Kontrolle des Diffs im Code und der Testausgabe, nicht der Zusammenfassung.
  • Im Experiment entdeckten Happy-Path-Tests 52 % der Fehler, mit Randfällen 90 %, mit Eigenschaften 97 %.
  • Schreib (oder genehmige) Tests nach der Spezifikation und am besten vor dem Code, sonst wiederholen sie nur die Fehler der Implementierung.
  • Achte auf geänderte Tests, unterdrückte Fehler, Arbeit, um die niemand gebeten hat, und unumkehrbare Schritte.

Willst du es selbst ausprobieren? Bitte einen KI-Agenten, auf dein Projekt ein Werkzeug für Mutationstesten loszulassen (Stryker für JavaScript, mutmut für Python) und dir die Mutanten zu zeigen, die überlebt haben. Jeder von ihnen ist ein Test, der fehlt.

Siehst du das anders? Schreib an support@coden-app.com.

Ein Agent auf deinem Rechner, vom Telefon aus gesteuert