Zurück zum Blog

Fehler zählen, ohne sich selbst zu täuschen: nach Auftrag gruppieren, nicht nach Tag

Fünf gleiche Fehler an einem Tag, und ich nannte es eine Eskalation. Die Nachmessung nach Auftrag statt nach Tag zeigte das echte Signal. Dazu: warum eine Konstante keine Streuung erklärt und warum „zuletzt“ max() heißt und nicht das letzte Listenelement.

4 Min. Lesezeitvon Daniel AlischEngineering, KI-Agenten, Monitoring

An einem Tag tauchte in den Läufen meiner Agenten-Plattform fünfmal dieselbe Fehlermeldung auf: Der Prompt sei zu lang. An den Tagen davor waren es ein oder zwei gewesen. Ich habe die Klasse daraufhin in einem Ticket als „eskalierend" bezeichnet. Am nächsten Tag musste ich das zurücknehmen. Nicht, weil die Fehler verschwunden wären, sondern weil meine Messung die falsche Achse hatte.

Die Kalenderachse erzeugt Trends aus Zufall

Die Nachmessung lief über ein Fenster von knapp sieben Tagen und 153 Läufe, die ein auswertbares Ergebnisfeld hatten. Nach Tagen sortiert ergab sich die Reihe 2, 1, 0, 1, 2, 5, 1. Der Tag mit den fünf Treffern war ein Ausreißer, kein Anfang. Wer auf diese Reihe schaut, während die Fünf gerade frisch ist, sieht eine Eskalation. Wer einen Tag später schaut, sieht Rauschen.

Das ist kein Zufall der Daten, sondern eine Eigenschaft der Achse. Die Zeit ist die Dimension, auf der Zufall am leichtesten wie ein Trend aussieht, weil wir sie automatisch als Verlauf lesen. Kleine Zahlen pro Tag schwanken stark, und jede Schwankung wirkt wie eine Richtung.

Die Achse, auf der das Signal lag

Ich habe dieselben Treffer anschließend nach dem Auftrag gruppiert, der den Lauf ausgelöst hatte, und für jede Gruppe den Nenner mitgeführt. Das Bild war ein völlig anderes:

  • tägliche Podcast-Erstellung: 5 von 7 Läufen
  • nächtliche Verwaltung: 2 von 6 Läufen
  • Bericht zum Tagesende: 1 von 4 Läufen
  • über alle Läufe hinweg: 12 von 153, also rund 7,8 Prozent

Kurze, gleichförmige Läufe hatten keinen einzigen Treffer. Der Fehler war also kein zeitliches Phänomen, sondern ein strukturelles: Er traf genau die Aufträge, die viel Material einsammeln und lange arbeiten. Damit war auch klar, wo man ansetzen muss — nicht bei einer allgemeinen Beobachtung, sondern bei einem bestimmten Auftrag mit einer Quote von über zwei Dritteln.

Die Regel, die ich daraus mitgenommen habe: Bevor ich eine Aussage der Form „X wird schlimmer" veröffentliche, frage ich, was außer der Zeit noch variiert, und gruppiere danach. Und ich nenne zu jeder Gruppe das n. Eine Gruppe mit einem einzigen Lauf und hundert Prozent Fehlern ist kein Befund.

Eine Konstante erklärt keine Streuung

Dieselbe Messung hat noch eine zweite Vermutung von mir erledigt. Ich hatte das Vorladen gespeicherter Erinnerungen in den Kontext im Verdacht, den Prompt zu sprengen. Dieses Vorladen ist aber gedeckelt: eine feste Zahl wichtiger Einträge, zusammen grob 95.000 Zeichen. Es ist also für jeden Lauf annähernd gleich groß.

Eine Größe, die in allen Läufen gleich ist, kann nicht erklären, warum ein Auftrag in fast drei Viertel der Fälle scheitert und ein anderer nie. Sie kann eine Untergrenze erklären, aber keine Streuung zwischen null und gut siebzig Prozent. Diese Prüfung ist billig und erstaunlich wirksam: Für jede Ursache, die ich verdächtige, frage ich, ob sie überhaupt variiert. Wenn nicht, ist sie als Erklärung für Unterschiede raus.

Zwei Messfehler, die keine Fehlermeldung erzeugen

Beim Zählen gibt es zwei Fallen, in die ich beide getreten bin. Die erste: Ich habe die Treffer zunächst per Teilstring gesucht. Meine eigenen Berichte über das Phänomen enthielten aber genau die gesuchte Formulierung, und so zählte die Suche Läufe als Fehler, die nur über den Fehler geschrieben hatten. Seitdem siebe ich über Gleichheit mit dem Wert, den das System im Fehlerfall tatsächlich einträgt, nicht über ein Textfragment.

Die zweite Falle betrifft das Wort „zuletzt". Ein Auswertungsskript sollte melden, wann der schlimmste hängende Lauf zuletzt aufgetreten war, und nannte ein Datum aus dem Juli. Tatsächlich lag der letzte Fall zwei Tage zurück. Das Skript hatte das letzte Element der Liste genommen, in der Reihenfolge, in der die Schnittstelle sie geliefert hatte — und diese Reihenfolge ist nicht nach Zeit sortiert.

# falsch: Reihenfolge der API ist kein Zeitstrahl
zuletzt = hängend[-1]["started_at"]

# richtig: "zuletzt" ist das Maximum der Zeitstempel
zuletzt = max(l["started_at"] for l in hängend)

Ein Datum aus dem Juli liest sich wie „längst vorbei, nicht mein Problem". Der Befund wäre liegen geblieben, ohne dass irgendetwas einen Fehler gemeldet hätte. Nach der Korrektur habe ich im selben Verzeichnis nach dem gleichen Muster gesucht und in derselben Datei sofort eine zweite Stelle gefunden, die auf dieselbe Weise den letzten unfertigen Lauf bestimmte.

Die Grenze der Messung gehört in den Befund

Ein letzter Punkt, den ich inzwischen immer ausdrücklich dazuschreibe: In meinem Fall verlieren Laufdatensätze nach knapp sieben Tagen das Ergebnisfeld. Über längere Zeiträume lässt sich mit dieser Quelle also schlicht nichts sagen. Das ist keine Schwäche des Berichts, sondern seine Reichweite, und wer sie verschweigt, lädt dazu ein, eine Wochenmessung als Monatstrend zu lesen.

Fazit

Die Rücknahme im Ticket war unangenehm, aber lehrreich. Gruppieren nach dem, was außer der Zeit variiert, den Nenner mitführen, über Gleichheit sieben, „zuletzt" als Maximum berechnen und die Messgrenze benennen: Keiner dieser Schritte ist aufwendig. Zusammen machen sie aus einer Beobachtung, die sich richtig anfühlt, einen Befund, der auch am nächsten Tag noch stimmt.

Weiterlesen