# Der Test war grün. Das Bild zeigte zwei Köpfe.

Author: Adrian Föhl
Published: 2026-09-24
Language: de
Canonical: https://www.adrianfoehl.com/de/blog/the-test-was-green

> Automatische Prüfungen melden oft Erfolg, obwohl das Ergebnis falsch ist. An einem kleinen Projekt auf meiner Website habe ich gesehen, auf welche zwei Arten das passiert, und was daraus für die Abnahme von KI-Ergebnissen folgt.

Eine automatische Prüfung kann grün sein, obwohl das Ergebnis falsch ist. Ich habe das an einem kleinen Projekt auf meiner Website erlebt, gleich zweimal und aus zwei verschiedenen Gründen.

Ich glaube, das wird wichtiger, je mehr Arbeit KI-Agenten übernehmen. Dann verlassen wir uns auf Prüfungen, die ebenfalls automatisch laufen, und eine grüne Prüfung ersetzt den Blick eines Menschen. Das geht nur gut, wenn die Prüfung das Richtige prüft und es auch erkennen kann.

Das Projekt ist das Porträt oben auf meiner [Hauptseite](/de). Es folgt dem Mauszeiger mit Kopf und Augen. Gebaut habe ich es mit zwei KI-Agenten, aus einem einzigen Foto und zwölf generierten Posen. Dabei sind drei Fehler passiert, die ich jeweils in wenigen Sekunden mit bloßem Auge gesehen habe. Zwei davon hatte eine automatische Prüfung vorher als in Ordnung gemeldet.

## Die Idee stammt von Claire Vo

Die Idee stammt von Claire Vo, der Gründerin von ChatPRD. Sie hat Mitte September ein Porträt veröffentlicht, das dem Cursor in vier Richtungen folgt und beim Klick zwinkert. Den Weg dahin hat sie [als Anleitung](https://clairevo.com/ai-workflows/build-a-cursor-following-portrait-with-flora-and-codex) beschrieben. Ihre sechs Bilder hat Codex erzeugt, der Coding-Agent von OpenAI. Ihr Porträt ist schwarzweiß. Ich wollte wissen, ob das auch mit meinem eigenen Bild funktioniert, in Farbe und mit dem Hintergrund, den die Seite schon hatte.

Gearbeitet habe ich mit Claude Code, dem Coding-Agenten von Anthropic. Er beauftragte Codex mit der Bildgenerierung und erledigte alles Weitere lokal auf meinem Mac. Mein Porträt ging dabei als einziges Bild an OpenAI, mit meiner ausdrücklichen Freigabe. Meine Rolle war die Abnahme. Ich habe mehrmals Nein gesagt: zur ersten Fassung in Schwarzweiß, zum falschen Hintergrund, zum zugekniffenen Auge, zu den Sprenkeln und zu den überlagerten Bildern. Live ging erst, was ich selbst angesehen hatte.

## Zwölf Posen aus einem Foto

Am Ende entstanden zwölf Bilder: die unveränderte Frontalpose, acht Blickrichtungen, Blinzeln, Zwinkern und ein nachdenklicher Blick. Den nachdenklichen Blick zeigt das Porträt, wenn ein Besucher ihm eine Frage stellt. Beantwortet wird sie ausschließlich aus meinen veröffentlichten Texten.

Alle Posen entstanden aus demselben Foto und mit denselben Vorgaben. Nur Kopf und Augen durften sich bewegen, Farben, Ausschnitt und Schultern sollten gleich bleiben. Das Bildmodell hielt sich daran nur ungefähr. Deshalb lief nach jeder Generierung eine lokale Nachbearbeitung: freistellen, verschieben, skalieren und einfärben, bis jede Pose deckungsgleich auf dem Original lag. Einmal hatte Codex den ganzen Körper um zehn Pixel verschoben. Diesen Fehler fand eine Messung, nicht ich. Danach stimmten die Umrisse der Schultern bei allen Posen zu mindestens 99,8 Prozent überein.

## Das Modell ändert, was niemand erwähnt hat

Beim Blick nach links wirkte das abgewandte Auge leicht zugekniffen, fast wie ein Zwinkern. Ein Porträt, das jedem Besucher zuzwinkert, wollte ich nicht. Es brauchte drei Anläufe. Geholfen haben zwei Änderungen im Prompt: eine kleinere Drehung des Kopfes und die ausdrückliche Vorgabe, dass beide Lider gleich hoch stehen. Ein Teil des Effekts ist vermutlich Perspektive. Mit dieser Vorgabe gelangen danach aber auch die vier diagonalen Blickrichtungen im ersten Anlauf.

Für diesen Fehler gab es keine automatische Prüfung. Ich finde ihn trotzdem lehrreich, denn das Modell erfüllte die Vorgabe „Kopf nach links drehen“ und veränderte dabei etwas, das niemand erwähnt hatte.

## Das Bildmodell meldet „sauber“

Beim Drehen des Kopfes sah ich in den Brillengläsern einzelne weiße und schwarze Pixel. Der Agent suchte danach und fand nichts. Er ließ ein lokales Bildmodell die vergrößerten Augenpartien beschreiben. Das Modell meldete für alle sechs Ausschnitte „sauber“, obwohl der Fehler bereits feststand.

Die Ursache lag in der Nachbearbeitung, nicht bei Codex. Für die Farbangleichung hatte der Agent eine Tabelle aus meinem Frontalbild abgeleitet. Sie ordnete jedem Farbton einen Farbton des Originals zu, allerdings nur für Farben, die im Frontalbild vorkamen. Für Glanzlichter in den Gläsern oder Kanten des Brillengestells setzte sie falsche Werte ein. Der Agent ersetzte die Tabelle durch eine Formel, die auch für Farben sinnvolle Werte liefert, die im Frontalbild nicht vorkommen. Danach waren die Sprenkel weg.

Das Bildmodell hatte die richtige Frage gestellt. Es hatte aber nie gezeigt, dass es einen solchen Fehler überhaupt erkennen kann.

## Der Test ist grün, das Bild zeigt zwei Köpfe

Damit der Wechsel zwischen den Posen weicher wirkt, blendete die erste Version die Bilder kurz ineinander. Der Agent hatte dafür automatische Browsertests geschrieben. Sie bewegten die Maus wie ein Besucher in alle acht Richtungen und prüften jeweils, ob die richtige Pose eingeblendet war. Alle Tests waren grün. Beim ersten Ausprobieren sah ich trotzdem zwei Köpfe übereinander.

Während einer Überblendung sind zwei Bilder gleichzeitig halb sichtbar. Weil die Posen freigestellt sind, schien der gerade Kopf überall dort durch, wo der gedrehte ihn nicht verdeckte. So entstand eine doppelte Kontur an Ohr, Haaren und Brille. Der Test hatte korrekt gemessen, welches Bild eingeblendet war. Ob das Ergebnis gut aussah, gehörte nicht zu seiner Frage.

Heute schaltet das Porträt hart um, so wie bei Claire. Es ist immer genau ein Bild sichtbar. Zusätzlich speichert der Testlauf einen Screenshot pro Pose, der vor jeder Veröffentlichung angesehen wird.

## Falscher Prüfer oder falsche Frage

Die beiden Prüfungen sind auf unterschiedliche Weise gescheitert. Das Bildmodell war ein ungeprüfter Prüfer: Es stellte die richtige Frage, konnte die Antwort aber nicht zuverlässig erkennen. Der Browsertest war dagegen zuverlässig, stellte aber die falsche Frage. Er prüfte den Zustand der Seite, nicht das Bild.

Daraus ergeben sich für mich zwei Fragen an jede Prüfung. Deckt sie den Fehler überhaupt ab? Und kann sie ihn erkennen? Die zweite Frage beantwortet man mit absichtlich eingebauten Fehlern. Findet die Prüfung sie nicht, reicht sie für diese Art von Fehler nicht aus. Die erste Frage beantwortet man, indem man aufschreibt, was die Prüfung eigentlich misst, und das mit dem vergleicht, was der Nutzer sieht. Eine automatische Prüfung auf Sprenkel habe ich bis heute nicht. Wenn ich eine baue, lasse ich sie zuerst gegen die alten, fehlerhaften Bilder laufen.

## Fazit

Dieselben zwei Fragen stelle ich bei jedem KI-System, das Menschen Antworten gibt. Als ich [den Chat auf meiner Website](/de/blog/testing-the-ai-that-speaks-for-me) von einer KI bewerten ließ, musste diese Bewertung zuerst meine eigenen Urteile reproduzieren. Dazu gehörten auch Antworten mit absichtlich eingebauten Fehlern. Erst danach durfte sie neue Antworten bewerten.

Die zweite Frage ist schwieriger, weil sie sich nicht automatisch beantworten lässt. Misst die Bewertung, was bei den Nutzern ankommt, oder nur, ob das System seine Aufgabe ausgeführt hat? Wer ein KI-System freigibt, sollte beide Antworten kennen. Ich würde die zweite Frage jemandem geben, der das Ergebnis mit den Augen der Nutzer sieht. Das Team, das die Tests geschrieben hat, ist dafür oft zu nah dran.

Auf automatische Prüfungen würde ich trotzdem nicht verzichten. Ohne sie hätte ich zwölf Posen in acht Richtungen nie so oft durchgespielt. Aber ich vertraue einer grünen Prüfung erst, wenn ich ihre Frage kenne und weiß, dass sie Fehler findet.

Welche deiner Prüfungen hat schon einmal einen Fehler gefunden, den du absichtlich eingebaut hast? Und prüft sie, was deine Nutzer sehen, oder nur, ob dein System gelaufen ist?

## Zum Nachbauen

Das ist der Prompt, mit dem der dritte Anlauf für den Blick nach links gelang. Das Porträt hing als Bild an, und die Regel zur Lidhöhe steht im letzten Absatz.

```prompt
Use your image generation tool. The attached image (reference.png) is my current website portrait, in color. Generate ONE edited frame from it and save it as ./gen/left.png. Do not touch any other file.

Edit this exact image. Preserve this exact man: identity, facial proportions, skin tone, expression, hairstyle, clothing, colors, color grading, lighting, background, shoulders, torso and crop. Keep the full color look of the reference exactly. Keep the same square 1:1 canvas, same subject scale, head center at the same position, body in the same position. Do not shift, mirror, zoom, recolor, or change camera position. No text, no props.

The ONLY change: head turned only slightly, about 8 degrees, toward the LEFT edge of the image, and eyes looking toward the left edge. IMPORTANT: both eyes exactly as wide open as in the reference image, identical eyelid height on both sides, the eye nearer the left edge must NOT look smaller, narrower or squinted. No wink, no squint, no half-closed eyelid, no smile-squint. Move mainly the irises toward the left edge. Same friendly expression as the reference.

At the end, report the saved file and its pixel size.
```

Und das ist der Kern der Komponente. Der Winkel zwischen Gesicht und Mauszeiger wählt eine von acht Richtungen. Nahe an der Grenze zwischen zwei Richtungen bleibt die bisherige stehen, sonst flackert der Kopf hin und her. Ist der Mauszeiger nah am Gesicht, schaut das Porträt geradeaus, das passiert vor dem Aufruf. Darunter steht die Zeile, die nach der doppelten Kontur übrig blieb.

```tsx
const DIRECTIONS = ["right", "down-right", "down",
  "down-left", "left", "up-left", "up", "up-right"] as const;
// Near a sector edge the previous direction is kept,
// otherwise the head flickers.
const HYSTERESIS_DEG = 8;

function directionFor(dx: number, dy: number, current: Pose) {
  const angle =
    ((Math.atan2(dy, dx) * 180) / Math.PI + 360) % 360;
  const sector = Math.round(angle / 45) % 8;
  if (current !== "front") {
    // Angular distance to the centre of the current sector.
    const index = DIRECTIONS.indexOf(current);
    const offset =
      Math.abs(((angle - index * 45 + 540) % 360) - 180);
    if (offset < 22.5 + HYSTERESIS_DEG) return current;
  }
  return DIRECTIONS[sector];
}

// In the render loop: all twelve frames are stacked and
// exactly one is visible. No cross-fade, because two
// cut-outs at once show a double outline.
style={{ opacity: shown === name ? 1 : 0 }}
```
