Startseite / Artikel / Voice-Agents am Ergebnis messen: ein dreischichtiges Test-Harness

Voice-Agents am Ergebnis messen: ein dreischichtiges Test-Harness

Transkript-Metriken übersehen Voice-Agents, die die falsche Bestellung ändern oder veraltete Schreibvorgänge hinterlassen. So baut man ein Harness mit drei Schichten, sechs Achsen und einem Release-Gate.

1496 Wörter

Stellen Sie sich drei Telefonate vor, die im Review tadellos aussehen. Im ersten korrigiert eine Kundin mitten im Satz die Bestellnummer, und der Agent liest die korrigierte Nummer zurück. Im zweiten fällt der Kunde dem Agenten ins Wort, und der Agent verstummt sauber, niemand redet über den anderen hinweg. Im dritten sagt der Agent, die Bestellung sei aktualisiert.

Jetzt ein Blick ins Backend. Der erste Anruf hat die falsche Bestellung geändert, nämlich genau die, von der die Kundin gerade abgerückt war. Im zweiten lief der Tool-Aufruf, der beim Unterbrechen gerade aktiv war, trotzdem weiter. Im dritten ist das Update nie angekommen, und das System hält noch den alten Wert.

Kein Transkript-Review würde einen dieser Fälle markieren. Ein WER-Check oder eine Bewertungsmatrix für Gesprächsqualität auch nicht. Trotzdem haben alle drei genau das verfehlt, worauf es ankam. Im Folgenden geht es darum, wie man ein Test-Harness baut, das diese ganze Fehlerklasse erwischt, bevor ein Kunde es tut.

Warum Transkript-Metriken nicht reichen

Die üblichen Messgrößen für Voice-Agents sind Word Error Rate, Latenz, wie schnell der Agent bei Unterbrechungen nachgibt, Aussprache und natürlicher Gesprächsfluss. Alle sind es wert, verfolgt zu werden, und keine davon ist falsch. Sie beantworten aber eine engere Frage als die, die der Produktivbetrieb stellt. Sie zeigen, ob der Agent richtig geklungen hat. Ob tatsächlich etwas passiert ist, sagen sie nicht.

Ein Harness, das sich lohnt, hat drei Schichten:

  • Gespräch: Hat der Agent richtig verstanden und richtig geantwortet?
  • Ausführung: Wurden die richtigen Tools mit den richtigen Argumenten aufgerufen?
  • Zustand: Ist die beabsichtigte Änderung im externen System wirklich angekommen?

Übertragen auf die drei Anrufe: Der erste hat die Gesprächsschicht bestanden und bei der Ausführung versagt. Der zweite hat ebenfalls bei der Ausführung versagt, aber aus einem anderen Grund: Ein veralteter Aufruf wurde nie abgebrochen. Der dritte kam durch Gespräch und Ausführung und ist trotzdem am Zustand gescheitert, weil der Schreibvorgang im Backend nicht hängen blieb. Drei Fehler in drei verschiedenen Schichten, alle mit demselben Symptom: einem Transkript, das in Ordnung aussah.

Zuerst kartieren, was schiefgehen kann

Bewertet wird erst, wenn eine Matrix der Fehlermodi steht. Vier Kategorien decken den Großteil ab.

Unterbrechungen umfassen ein Dazwischenreden, bevor der Agent ausgesprochen hat, eines, während gerade ein Tool-Aufruf zusammengesetzt wird, eines direkt nachdem ein Tool-Aufruf losgelaufen ist, und versehentliche, ausgelöst durch Hintergrundgeräusche, die nie einen Stopp hätten auslösen dürfen.

Korrekturen betreffen eine geänderte Bestellnummer, ein neues Lieferdatum, eine andere Menge und Verneinung, etwa „schick es los“, gefolgt von „nein, doch nicht“.

Tool-Fehler reichen von Timeouts und leeren Ergebnissen über ein Tool, das den falschen Datensatz holt, Backend-500er und eine verspätete Erfolgsmeldung bis zu doppelten Requests durch einen nicht idempotenten Retry.

Zustandskonflikte entstehen, wenn drei Versionen der Wahrheit nebeneinander existieren: Das Transkript sagt das eine, der Live-Zustand etwas anderes und der tatsächlich ans Tool geschickte Payload ein Drittes.

In der Praxis überschneiden sich diese Kategorien. Eine Korrektur, die während eines Retrys nach einem Tool-Fehler eintrifft, ist ein eigenes Szenario. Die Einteilung erlaubt es trotzdem, jede Kategorie zunächst für sich zu testen.

Wie ein Testfall aussieht

Jeder Fall braucht ein Eingabeszenario, eine Beschreibung dessen, was ein korrektes System auf allen vier Ebenen tun sollte, und eine Struktur, die starr genug für automatisches Scoring ist, damit niemand von Hand lesen und urteilen muss. Ein minimales Schema sieht so aus:

{
  "id": "corr-02",
  "category": "correction",
  "setup": {
    "field": "order_number",
    "initial_value": "5590",
    "corrected_value": "5599",
    "interrupt_stage": "during_tool_prep"
  },
  "expected": {
    "captured": true,
    "active_state_correct": true,
    "tool_args_correct": true,
    "stale_write_prevented": true,
    "backend_correct": true,
    "confirmation_matches_backend": true,
    "should_execute": true
  }
}

Der Block setup ist bewusst locker gehalten, weil Unterbrechungen und Tool-Fehler andere Felder brauchen als Korrekturen. Der Block expected ist strikt. Jeder Fall wird auf denselben sechs Achsen bewertet, und genau dadurch lassen sich 30 sehr unterschiedliche Szenarien in einer einzigen Scorecard zusammenfassen.

Die sechs Achsen

  1. Ereignis erfasst. Hat das System die Korrektur oder Unterbrechung überhaupt bemerkt?
  2. Korrekter aktiver Zustand. Ist der Live-Arbeitszustand nach dem Ereignis entsprechend aktualisiert?
  3. Korrekte Tool-Argumente. Waren die Werte im Tool-Aufruf aktuell und konsistent?
  4. Veralteter Schreibvorgang verhindert. Wurde jede Aktion blockiert, die auf veraltetem Zustand beruhte?
  5. Backend korrekt. Entspricht das gespeicherte Ergebnis dem, was der Kunde wirklich wollte?
  6. Bestätigung entspricht Backend. Ist das, was der Agent dem Kunden gesagt hat, auch das, was tatsächlich gespeichert ist?

Eine Regel wiegt schwerer als alle anderen: Diese Achsen werden nie zu einer Zahl gemittelt. Ein System, das auf fünf Achsen richtig und bei backend_correct falsch liegt, hat das falsche Ergebnis geliefert, und fünf grüne Haken daneben ändern daran nichts. Ein Fall gilt nur dann als echter Pass, genannt End-to-End Task Integrity, wenn alle sechs gleichzeitig erfüllt sind.

Wie ein bestandener Trace aussieht

Nehmen wir ein Szenario, in dem die Korrektur früh kommt: Sie trifft ein, bevor überhaupt ein Tool-Aufruf existiert, also gibt es nichts Veraltetes abzubrechen.

Customer says 4821                    → state_version = 4
Correction: 4281                      → state_version = 5
Tool call built using version 5
Backend verified = 4281
Agent confirms 4281

Jeder Zustandsübergang trägt ein Versions-Tag, und der Tool-Aufruf wird erst gebaut, wenn eine stabile Version vorliegt. Auf Version 4 wird nie etwas aufgebaut, also muss später auch nichts verworfen werden. So sieht ein bestandener Fall aus. Nicht „es gab keine Korrektur“, sondern „die Korrektur hat sich vollständig durchgesetzt, bevor nachgelagert irgendetwas festgeschrieben wurde“.

Ein Lauf gegen einen fehlerhaften Mock

Um es greifbar zu machen, nehmen wir ein lauffähiges Harness (Fixtures, Schema und Scorer) und richten es auf eine regelbasierte Mock-Pipeline. Der Mock transkribiert präzise und beherrscht den Sprecherwechsel gut, invalidiert aber nie einen Tool-Aufruf, der schon gebaut oder abgeschickt ist, und prüft ein Tool-Ergebnis nur dann erneut, wenn er es als fehlerhaft erkennt. Genau so ein System steckt hinter den Fehlern oben, hier nur ausdrücklich gemacht, damit das Harness ein Ziel hat.

Der Lauf umfasste 30 synthetische Anrufe, gleichmäßig verteilt auf Korrektur-, Unterbrechungs- und Tool-Fehler-Szenarien.

  • Ereignis erfasst: 24 von 30
  • Korrekter aktiver Zustand: 24 von 30
  • Korrekte Tool-Argumente: 11 von 30
  • Veralteter Schreibvorgang verhindert: 8 von 30
  • Backend korrekt: 8 von 30
  • Bestätigung entsprach Backend: 7 von 30
  • End-to-End-Integrität der Aufgabe: 7 von 30

Die Gesprächsschicht wirkt solide: 24 von 30 Anrufen erfassen das Ereignis und aktualisieren den Zustand. Danach brechen Ausführung und Zustand ein. Nur 8 Anrufe verhinderten einen veralteten Schreibvorgang, und gerade einmal 7 lieferten eine gesprochene Bestätigung, die zu dem passte, was am Ende im Backend stand. Allein nach Transkriptqualität beurteilt, wirkte dieses System produktionsreif. Ist es aber nicht.

Aus Fehlern eine Fix-Liste machen

Ein rotes Ergebnis nützt nur, wenn es die Schicht benennt, die repariert werden muss. Sortiert man die 23 nicht bestandenen Fälle nach Ursache, ergibt sich diese Aufteilung:

  • 10 Fälle: Argumente für das Tool aus veralteten Werten gebaut
  • 4 Fälle: fehlerhaftes Tool-Ergebnis ohne jede Prüfung übernommen
  • 3 Fälle: bereits laufende Aufrufe nie abgebrochen
  • 2 Fälle: Hintergrundgeräusch fälschlich als Unterbrechung durch den Kunden gewertet
  • 2 Fälle: Backend-Fehler kombiniert mit einem Retry, der nicht gefahrlos wiederholbar war
  • 1 Fall: Bestätigung ausgesprochen, bevor ein asynchrones Ergebnis zurückkam
  • 1 Fall: kein Idempotenzschlüssel an der Aktion

Zehn dieser 23 Fehlschläge haben eine einzige Ursache. Ein Tool-Aufruf war bereits gebaut oder abgeschickt, als eine Korrektur oder Unterbrechung eintraf, und nachgelagert gab es keinen Mechanismus, ihn abzubrechen. Das ist ein Architekturloch, das in zehn Szenarien auftaucht, nicht zehn Bugs, und genau das zu erkennen ist der Sinn eines Harness. Ein nacktes „der Anruf ist fehlgeschlagen“ gibt einem Team nichts an die Hand, „der Argumentaufbau ist in 10 von 23 Fällen gescheitert“ dagegen setzt die Prioritäten für den nächsten Sprint.

Ein Release-Gate für folgenreiche Aktionen

Bevor man eine Änderung an einem Voice-Agent ausrollt, der folgenreiche Aktionen ausführt, ist „die Gesprächsqualität hat sich verbessert“ keine ausreichende Messlatte. Das Gate sollte lauten:

  • Keine Fehler durch veraltete Schreibvorgänge in den Korrektur- und Unterbrechungs-Suites
  • Keine Abweichung zwischen Bestätigung und Backend
  • Idempotente Tool-Retries
  • Kritische Aktionen werden nach der Ausführung gegen das Backend verifiziert, statt sie aus dem Rückgabewert des Tools abzuleiten

Produktionsreif ist ein Voice-Agent erst, wenn Gespräch, Tool-Trace und Backend-Zustand übereinstimmen, was passiert ist. Die meisten Systeme können das heute nicht von sich behaupten, und um es herauszufinden, muss man aufhören, das Transkript zu bewerten, und anfangen, das Ergebnis zu bewerten.