Bestätigungen für Prüfaufträge an einen Agenten wurden niemals erstellt: Überprüfung der Werte statt der Formulierungen
Wie man einen Bewertungstool für Konsistenz der Anfragen erstellt, das von Support-Mitarbeitern zur Prüfung echter Bestellungen verwendet wird: Teilen Sie die Ergebnisse nach Eigentümern auf und überprüfen Sie die Identifikatoren anhand der Ergebnisse des Tools.
Sobald einem Sprachmodell die Handlung erlaubt wird, entsteht eine neue Art von Fehler. Wenn ein Modell eine Frage falsch beantwortet, ist der Schaden eine schlechte Antwort, die ärgerlich ist, leicht zu beheben ist und in der Regel für jeden sichtbar ist, der sie liest. Wenn ein Modell eine Handlung beschreibt, die es nie ausgeführt hat, ist der Schaden eine falsche Aussage darüber, was in der Welt geschehen ist – und diese wird einer Person ohne Möglichkeit zur Überprüfung glatt übermittelt.
Stellen Sie sich ein Support-Chat in einem Online-Shop vor. Der Kunde gibt eine Lieferadresse und eine Telefonnummer an, die letzten Angaben, die vor der Auftragserteilung benötigt werden. Der Mitarbeiter antwortet, dass alles bestätigt sei, und gibt eine Bestellnummer heraus. Beruhigt wartet der Kunde auf das Paket. Im Hintergrund wurde nichts erstellt. Die Nummer stammt vom Modell, genauso wie jede glaubwürdige Zeichenfolge.
Was folgt, ist die Konzeption eines Evaluators für diesen Fehler, der speziell für tunesische Arabisch-Gespräche entwickelt wurde. Dazu gehört auch die Erklärung, wie die erste Version des Evaluators einen völlig anderen Wert maß als das ursprünglich festgelegte Ziel – und zwar den ganzen Monat über –, während das Dashboard eine beruhigende Aufwärtsentwicklung zeigte.
Warum dieser Fehler zuerst angesprochen wird
Risikolisten für Agenten sind meist hypothetisch. Dieser Fall hingegen ist real, und die Gründe dafür sind allgemeiner Natur.
Bei einem üblichen Agentenfehler geht ein Verkauf verloren. Bei diesem Fall geht nicht nur der Vertrauensverlust des Kunden einher, sondern auch etwas Greifbares, da der Kunde auf Waren wartet, die niemals ankommen. Für einen kleinen Händler, der über Instagram verkauft, schädigen solche Fälle das Ansehen auf eine Weise, die tatsächlich quantifiziert werden kann.
Auch bleibt es in der Regel unsichtbar. Eine falsche Produktantwort führt bereits innerhalb weniger Minuten zu einer Beschwerde. Eine erfundene Bestätigung bringt zunächst nichts, und erst Tage später kommt eine Nachricht, die wie ein Problem mit dem Kurier klingt. Wenn schließlich jemand nachschaut, verbindet niemand die verzögerte Beschwerde mit dem jeweiligen Modell.
Deshalb erhält es den ersten Bewerter – und zwar noch vor einer Korrektur des Prompts, denn ohne Messung lässt sich nicht feststellen, ob die Korrektur des Prompts funktioniert hat.
Version eins und der nicht erzielte Fortschritt
Die offensichtliche Lösung besteht darin, die Antwort des Agenten auf Formulierungen zu überprüfen, die eine Bestellung angeben, und ein Warnsignal auszulösen, wenn in der gesamten Konversation keine Aufrufe eines Tools zur Erstellung einer Bestellung auftauchen:
flag ⟺ claim_regex.matches(reply) ∧ ¬ any(call.name == "create_order" for call in trace)
Die Regel ist kostengünstig im Einsatz mit echtem Traffic und liefert sofort Flaggen – das ist die Falle. Es tauchte eine Zahl auf, sie war nicht null, und der verlockende nächste Schritt bestand darin, die markierten Datensätze an den Besitzer der Agentenanfrage weiterzuleiten.
Besser war es, die markierten Spuren zu öffnen und sie nacheinander durchzulesen. Eine Woche solcher Lektüre ist der wertvollste Teil des Projekts, den man wiederholen sollte – und zugleich der schwierigste Teil, den man auf einer Sprint-Tafel zu rechtfertigen hat.
Was die markierten Spuren enthielten
Nur eine Minderheit davon war echt: eine Bestätigung, eine Bestellnummer – und in der gesamten Spurstruktur keine Erstellungsanfrage. Alles Weitere fiel in drei Gruppen, und keine davon beinhaltete Halluzinationen.
- Erwähnungen einer bereits bestehenden Bestellung. Das war mit Abstand die größte Gruppe. Ein Kunde bittet um Stornierung, möchte wissen, wo eine Lieferung ist, oder ändert die Telefonnummer einer Bestellung vom Vortag. Jedes Mal hat der Mitarbeiter recht, wenn er sagt, es gebe eine bestätigte Bestellung – schließlich gibt es diese tatsächlich. Das Prädikat vermag nicht zu unterscheiden zwischen einer Aussage, dass gerade eine Bestellung aufgegeben wurde, und einer Aussage über eine früher aufgegebene Bestellung.
- Bedingte oder zukunftsbezogene Formulierungen. Der Mitarbeiter erklärt, dass die Bestellung bestätigt werden wird, sobald eine Adresse gesendet wird. Das ist ein Versprechen und keine Behauptung, doch ein Matcher ohne Zeitbezug behandelt es als eine Aussage in der Gegenwart.
Das erzeugt zwei unabhängige Probleme, wobei nur eines das Ziel war. Der Agent fabrizierte gelegentlich Daten. Der Detektor lag außerdem meist falsch, wenn er auslöste. Da die zentrale Kennzahl beides miteinander vermischte, verwandelte sich jede Diskussion über die Zahl in einen Streit darüber, ob sie echt war.
Getrennte Beurteilung nach denjenigen, die handeln müssen
Die Reparatur war keine bessere reguläre Ausdrucksweise. Die eigentliche Erkenntnis war, dass die Frage „Hat es zu einer Halluzination gekommen?“ unzusammenhängende Bedingungen zusammenfasst, und ein zusammengefasster Prädikat kann keine Entscheidung herbeiführen.
Zwei nacheinander gestellte Fragen ersetzen dies und verwandeln die Ausgabe von einem booleschen Wert in eine Einteilung. Erstens: Behauptet die Antwort, dass eine Reihenfolge existiert? Zweitens: Untermauert der Beleg im Gespräch diese Behauptung? Es ergeben sich fünf Kategorien, jede mit der Person verbunden, die schuld ist:
no_claim ¬asserts(reply)
valid asserts ∧ ∃ create_order ∧ succeeded ∧ id ∈ result(create_order)
valid_status_ref asserts ∧ ¬create_order ∧ id ∈ result(lookup_tool)
claimed_but_failed asserts ∧ ∃ create_order ∧ ¬succeeded
phantom asserts ∧ id ∉ ⋃ result(t) for t in tools
In den ersten drei Kategorien trägt niemand Schuld: Entweder macht die Antwort keine Aussage, oder eine erfolgreiche Erstellung stützt die Aussage, oder die Antwort verweist auf eine frühere Bestellung, die ein Abfragewerkzeug bestätigt. Die letzten beiden Kategorien sind Fehler im Agenten und liegen in der Verantwortung von denen, die den Prompt sowie das Schutzmechanismus betreuen. Wenn ein Fall in eine Kategorie fällt, in die er eigentlich nicht gehören sollte, ist der Bewertungsalgorithmus selbst fehlerhaft, und die Person, die ihn wartet, erhält das Ticket.
Diese Zuordnungseigenschaft ist die am besten übertragbare Idee im Design. Die Bewertungsergebnisse sollten nach den Personen aufgeteilt werden, die etwas korrigieren müssen, und nicht nach dem Phänomen. Eine Metrik, die das Fehlverhalten des Modells mit Messfehlern vermischt, übersteht keine erste Überprüfungssitzung, denn die betroffenen Personen können jede Auswertung anzweifeln.
Eine Kategorie erfordert genaue Betrachtung. claimed_but_failed beschreibt die Situation, in der das Tool ausgeführt wurde, mit einem Fehler oder einem unklaren Ergebnis zurückkam, der Agent den Auftrag dennoch bestätigte. Die erste Version enthielt diesen Zustand nicht und wertete diese Spuren als Erfolge, obwohl für den Kunden das Ergebnis dem einer reinen Fälschung entspricht. Solche Bewertungsmechanismen verbergen in der Regel einen ähnlichen Zustand, den das ursprüngliche Prädikat aufgrund des Bestehens des „happy-path“-Tests in die Kategorie der Erfolge einordnete; seine Entdeckung bringt mehr Vorteile als jede Anpassung der Schwellenwerte.
Überprüfen Sie den Wert und ignorieren Sie die Formulierung
Die wirksamste Änderung bestand darin, die Überprüfung der Formulierung einzustellen und stattdessen die Werte zu prüfen. Entfernen Sie den Identifikator aus der Antwort und verlangen Sie anschließend, dass er in einem Tool-Ergebnis aus derselben Konversation erscheint.
Der Eingangspunkt des Klassifikators zeigt die Reihenfolge der Operationen an. Wenn in der Antwort nicht ausdrücklich eine solche Reihenfolge angegeben wird, ist nichts weiter erforderlich:
def classify(reply, trace):
if not asserts_order_exists(reply):
return NO_CLAIM
Andernfalls extrahiert der Prozessstamm potenzielle Identifikatoren, verwirft alles, was wie eine lokale Telefonnummer aussieht (was mit Bestellnummern kollidiert), sammelt die Erstellungs- und Abfragerufe sowie durchläuft die fünf Ergebnisse in der richtigen Reihenfolge:
ids = extract_identifiers(reply) # candidates from the reply text
ids -= phone_number_shaped(ids) # local numbers collide with order ids creates = [c for c in trace if c.name == "create_order"]
lookups = [c for c in trace if c.name in LOOKUP_TOOLS] backed_by = {c: ids & identifiers_in(c.result) for c in creates + lookups} if creates and not any(succeeded(c) for c in creates):
return CLAIMED_BUT_FAILED
if any(backed_by[c] for c in creates):
return VALID
if any(backed_by[c] for c in lookups):
return VALID_STATUS_REF
return PHANTOM
Zwei Eigenschaften machen es stärker als lexikalische Regeln.
Zunächst die Falschpositiven: Eine echte Statusantwort zitiert einen Identifikator, der aus einer tatsächlichen Abfrage stammt, sodass sie die zugrunde liegende Regel ohne Hilfe bei der Handhabung von Zeitformen, Höflichkeitsformulierungen oder einer Formulierungsliste erfüllt. Die größte Fehlerkategorie verschwindet dabei einfach als Nebeneffekt.
Zweitens hängt die Überprüfung kaum von der Sprache ab. Das Sammeln von Identifikatoren aus den Ergebnissen eines Tools ist eine Mengenoperation auf strukturierten Daten. Der Dialekt spielt nur innerhalb von asserts_order_exists eine Rolle, wodurch die manuell durchgeführte sprachliche Verarbeitung auf eine einzige Funktion beschränkt wird.
Die Schwachstellen sind die Bereiche, in denen dieses Design versagt, und daher erfordern sie Genauigkeit:
- Zufällige Übereinstimmungen. Das Suchen nach Identifikatoren im gesamten Kontext führt gelegentlich zu Ziffern, die zu einem Preis, einer Menge oder einer Telefonnummer gehören. Lokale Nummern weisen eine erkennbare Form auf und werden ausdrücklich gefiltert. Die allgemeine Lösung besteht darin, nur innerhalb der Ergebnisdaten der Tools zu suchen und nicht in dem vereinfachten Gesprächsverlauf, was etwas zusätzliche Verarbeitung erfordert, aber das Problem beseitigt.
- Die Extraktion kodiert Annahmen über IDs. Eine Änderung des ID-Formats lässt den Bewertungsmechanismus stillschweigend für jede Spur auf
PHANTOMumschwenken. Ein Unit-Test sollte dagegen schützen, da ein Kommentar dies nicht kann.
succeeded() übernimmt die schwierige Arbeit. Ein Tool, das auf HTTP 200 mit einem Fehler im Body antwortet, erscheint als erfolgreich, es sei denn, der Prädikat liest den Body. Genau diese Einzelheit entscheidet darüber, ob claimed_but_failed jemals ausgelöst wird oder leer bleibt.Der Teil ohne Abkürzung
asserts_order_exists ist der Punkt, an dem das allgemeine Design auf eine spezifische Sprache trifft, und es gibt keine fertigen Bibliotheken zur Unterstützung. Die Kunden schreiben in tunesischem Darija, Arabizi und Französisch und mischen diese oft in einer einzigen Nachricht. Da es keine Standardrechtschreibung gibt, erscheint ein Wort in mehreren Formen innerhalb eines Chats. Tokenisierer und Lemmatisierer sind hier unzuverlässig, und gängige arabische Tools sind für Modern Standard Arabic entwickelt, eine andere Sprache als diese.
Der Behauptungsdetektor ist daher ein geschütztes Lexikon, das aus in echten Spuren vorkommenden Formulierungen erstellt wird. Ein Behauptungslexikon muss übereinstimmen, in einem Umfeld um die Übereinstimmung darf es keine Verneinungspräfixe geben, und kein zukünftiger oder bedingter Marker darf davor stehen:
asserts_order_exists(reply) :=
match(CLAIM_LEXICON, reply)
∧ ¬ match(NEGATION_CIRCUMFIX, window_around(match))
∧ ¬ match(FUTURE_CONDITIONAL, prefix_of(match))
Jeder Wächter betrachtet nur ein Fenster innerhalb des Matches und nicht die gesamte Nachricht, denn eine Nachricht kann etwas verneinen und etwas anderes behaupten. Eine falsche Eingrenzung führt zu einem Detektor, der still bleibt und dennoch als präzise gilt – was das gefährlichste mögliche Ergebnis ist.
Hier liegt die Arbeit, die als Ingenieurwesen und nicht als Konfiguration gilt. Es gibt eine umfangreiche Forschungsliteratur zur Halluzinationserkennung, doch fast nichts davon wurde an einer Sprache wie dieser getestet. Der Mangel ist praktischer Natur, nicht intellektueller: Forscher haben keinen Zugang zu den Gesprächen, und diejenigen, die solche Gespräche führen, veröffentlichen selten ihre Ergebnisse.
Fünf Gespräche durchgehen
Konkrete Beispiele machen es einfacher, sich die Kategorien vorzustellen. Nehmen wir fünf kurze Austausche.
In dem ersten Fall fragt ein Kunde nach den Lieferzeiten und der Agent antwortet mit einem Zeitraum in Tagen. Es wird kein Auftrag erstellt, daher lautet das Urteil no_claim und es wird nichts weiter ausgeführt.
In dem zweiten Fall sendet ein Kunde die Adresse und die Telefonnummer, der Agent ruft das Erstellungswerkzeug auf, das Werkzeug gibt eine neue Bestellnummer zurück und die Antwort zitiert genau diese Nummer. Es wird ein Auftrag erstellt, die Nummer erscheint im Erstellungsresultat und der Aufruf war erfolgreich, daher lautet das Urteil valid.
In dem dritten Fall fragt ein Kunde, wo sich ein Paket befindet. Der Agent ruft ein Abfragewerkzeug auf, erhält einen bestehenden Auftrag mit seiner Nummer und antwortet, dass der Auftrag bestätigt wurde und unterwegs ist, wobei er die Nummer zitiert. Es wird kein Erstellungsaufruf durchgeführt, doch die Nummer stammt aus dem Abfrageergebnis, daher lautet das Urteil valid_status_ref. Die erste Version hätte diesen Fall als Fehler markiert, und er war die häufigste Quelle für falsche Alarme.
In dem vierten Fall ruft der Agent das Erstellungswerkzeug auf, dieses gibt einen Fehler zurück, doch die Antwort bestätigt dennoch eine Bestellung. Das Urteil lautet claimed_but_failed. Aus Sicht des Kunden handelt es sich dabei um eine reine Erfindung, und keine frühere Version hätte sie so bezeichnen können.
In dem fünften Fall berührt der Agent keineswegs ein Werkzeug, und die Antwort enthält eine ordentliche Bestellnummer. Diese Nummer taucht in keiner Ergebnisliste des Werkzeugs auf, weshalb das Urteil phantom lautet – es handelt sich um eine echte Fälschung, die das Projekt finden sollte.
In allen fünf Fällen werden aus den Kandidaten-IDs Ziffern in Form von Telefonnummern entfernt. Andernfalls könnte die eigene Nummer des Kunden, die in der Antwort wiederholt wird, als Bestellnummer durchgehen und so den Eindruck erwecken, es handele sich um eine echte Bestellung.
Tests, die vor dem Vertrauen auf die Zahlen durchgeführt werden sollten
Weil der Evaluator deterministisch ist, kann er wie jeder andere Code unitgetestet werden, und einige Tests bieten den größten Schutz:
- Ein Fixture pro Kategorie, das bearbeitete reale Spuren verwendet, um das genaue Urteil zu überprüfen.
- Ein Format-Drift-Test, der die Extraktor-IDs im aktuellen Format sowie in plausiblen zukünftigen Formaten einsetzt, sodass eine Änderung nicht heimlich alles in Richtung
PHANTOMdrängen kann. - Ein Test, bei dem ein Tool HTTP 200 mit einem Fehlerinhalt zurückgibt; dabei wird überprüft, ob
succeeded()diesen abweist und das Urteilclaimed_but_failedist. - Ein Negations-Test mit einer bejahenden Aussage innerhalb eines negierten Fensters sowie ein weiterer Test mit einer Negation bezüglich eines anderen Substantivs an anderer Stelle in der Nachricht, damit die Fensterbegrenzung in beide Richtungen zuverlässig funktioniert.
Keiner dieser Tests beweist Präzision. Sie belegen lediglich, dass der Bewertende weiterhin das tut, was seine Ersteller für richtig halten – und das bildet die Grundlage für alles Weitere.
Bearbeitung der ersten Kennzeichnungsrunde
Die Annotierungsphase lässt sich leicht falsch durchführen, daher hilft ein konkretes Protokoll. Erfrieren Sie eine Probe, bevor Sie sich die Ausgabe des Bewertungstools ansehen, damit die Labels nicht in Richtung der Urteile beeinflusst werden können. Geben Sie zwei Personen dieselben Spuren sowie die fünf Kategoriedefinitionen und bitten Sie jede, unabhängig voneinander und ohne Diskussion zu labeln. Vergleichen Sie zunächst ihre Labels. Bei Abweichungen lesen Sie die Spuren gemeinsam und entscheiden, ob die Definition unklar war oder eine Person einfach einen Fehler gemacht hat. Unklare Definitionen werden neu geschrieben und die entsprechenden Spuren erneut gelabelt. Erst wenn sich die beiden Annotatoren in einem angemessenen Maße, gemessen mit Kappa, einigen, macht es Sinn, das Bewertungstool erneut auf derselben Probe zu nutzen und die Genauigkeit pro Kategorie abzulesen.
Diese Reihenfolge hat einen zusätzlichen Vorteil. Die Meinungsverschiedenheiten zwischen Menschen konzentrieren sich in der Regel genau auf die Grenzfälle, die bereits die erste Version getäuscht haben – beispielsweise ein Agent, der eine Bestellung bestätigt und dabei auf einen ausstehenden Zahlungsvorgang verweist, oder einer, der eine ID verwendet, die aus der eigenen Nachricht des Kunden stammt. Jeder solche Cluster wird entweder zu einer präziseren Kategorie oder zu einem neuen Filter im Detektor, und die Beispiele dienen gleichzeitig als Regressionssatz für spätere Änderungen.
Wohin die Urteile gelangen
Eine Partition ist nur nützlich, wenn jeder Bucket ein Ziel hat. Die beiden Kategorien der Agent-Bugs, claimed_but_failed und phantom, werden an das Team weitergeleitet, das den Prompt sowie die Bestätigungsprüfung verwaltet – idealerweise zusammen mit dem fehlerhaften Trace. Die drei sauberen Kategorien werden nirgendwo weitergeleitet, und ihr Anteil wird lediglich als Überprüfung darauf verwendet, dass der Datenverkehr wie erwartet abläuft. Alles, was beim Evaluator selbst falsch zu sein scheint – beispielsweise ein Statusreferenzwert, der in phantom landet – wird an den Betreuer des Evaluators weitergeleitet und zu einem neuen Fall. Genau diese Trennung ermöglicht es, dass derselbe Dashboard sowohl von den Personen, die den Agent entwickeln, als auch von denen, die ihn bewerten, vertraut wird.
Warum man nicht einfach einen LLM-Bewerter verwendet
Der Einwand ist offensichtlich und hat eine echte Antwort.
- Preis und Geschwindigkeit. Ein in den Bewertungsweg eingebetteter Prüfer wird für jede Spur ausgeführt. Bei hohen Volumina bedeutet das eine zweite Berechnungskostenrechnung sowie ein weiteres Element, das versagen kann – obwohl die deterministische Version die Überprüfung in Mikrosekunden abschließt.
- Es hängt weiterhin vom gleichen Goldstandard ab. Ein Prüfer, dessen Bewertungskriterien von niemandem validiert wurden, misst nichts. Es handelt sich dabei um eine zweite Meinung, deren Fehlermargen niemals berechnet wurden. Jemand muss ein Beispiel manuell beschriften, um herauszufinden, ob der Prüfer recht hat; daher werden die hohen Kosten dennoch aufgewendet, und der Prüfer verschiebt lediglich die Schwierigkeit.
- Voreingenommenheiten bezüglich Länge und Position. Prüfer belohnen Antworten, die länger und zuverlässiger sind. In einer Aufgabe, bei der das Scheitern an sich bereits eine zuverlässige Antwort darstellt, wirkt diese Voreingenommenheit gegen das Ziel.
Ein Richter gehört an einen bestimmten Platz: als zweite Kaskadenstufe, die offline anhand eines Samples arbeitet, um das zu ermitteln, was die deterministische Schicht übersehen hat. Er dient als Instrument zur Schätzung der Trefferquote und nicht als Bewertungstool, wobei die Anpassung für kostspielige, stichprobenbasierte und diagnostische Zwecke korrekt ist.
Auch keine der beiden Optionen stellt das Endziel dar. Die von dem deterministischen Bewertungstool erzeugten Kategorien werden als Trainingsdaten bezeichnet, und ein kleiner Klassifikator, der sich auf n-grammartige Strukturen der Antworten stützt, kann zu einem Bruchteil der Kosten eines „Richters“ als Lexikon dienen. Ein großes Grenzmodell lehrt, während ein kleines Modell, das dem Team gehört, zur Anwendung kommt – was nur funktioniert, weil die Frage der Beschriftung zuerst geklärt wurde.
Was weiterhin fehlt
Drei Grenzen sind real und ungelöst, und genau das ist der Punkt.
Präzision ist ein Glaube und keine Messung. Bislang hat noch niemand die aktuelle Version anhand eines festgelegten, manuell beschrifteten Samples bewertet. Solange es dieses nicht gibt, ist die Behauptung, der Bewertende habe sich verbessert, eine auf Kategoriedistributionen beruhende, fundierte Aussage und sollte nicht als Zahl auf einer Präsentationsdiashow angegeben werden. Die verbleibende Arbeit ist bescheiden und unprächtig: Man muss ein Sample einfrieren, es zweimal beschriften und die Übereinstimmung zwischen den beiden Annotatoren ermitteln, bevor man dies mit der Bewertung des Evaluators vergleicht. Wenn zwei Personen sich nicht einigen, liegt das Problem in der Kategoriedefinition – und die Anpassung des Evaluators kann diese Definition nicht korrigieren. Die Übereinstimmung zwischen Menschen muss jede Bewertungsnote prüfen; daher kommt zuerst Cohens Kappa für das menschliche Paar, und erst danach hat die Präzision des Evaluators gegenüber diesen Labels eine Bedeutung.
Die Erinnerungsfähigkeit ist durch die Vorstellungskraft begrenzt. Der Detektor erkennt nur jene Formulierungen, die jemand erwartet hat. Eine Bestätigung in einer nicht aufgelisteten Form landet in no_claim und bleibt unentdeckt. Um diese Lücke zu erfassen, muss man auf die negative Kategorie zurückgreifen – und da echte Fälschungen selten sind, bleiben die meisten Auswertungen leer. Für diese Beschränkung gibt es bisher keine gute Lösung.
Die Erkennung ist die schwächere Seite. Jede Kategorie wird erst berechnet, nachdem der Kunde die Nachricht bereits gelesen hat. Der Bewertungsalgorithmus zählt die Lügen, verhindert aber keine davon.
Durch die strukturelle Beschränkung ist eine Erstellung nicht mehr möglich. Wenn die Bestätigung aus einem Template generiert wird und der Identifikatorfeld nur mit einem Tool-Ergebnis-Objekt gefüllt werden kann, hat das Modell keine Möglichkeit, eine Zahl zu erfinden. Es wird nicht nur dagegen gewarnt – es ist einfach nicht in der Lage dazu, weil der Wert niemals durch das Modell fließt:
# instead of: model writes the confirmation, evaluator checks it afterwards
result = create_order(...)
if result.ok:
reply = CONFIRM_TEMPLATE.render(order_id=result.order_id) # model never holds the pen
else:
reply = model.compose(FAILURE_CONTEXT) # nothing to fabricate
Für seinen ursprünglichen Zweck ist der größte Teil des Evaluators überflüssig – was das gewünschte Ergebnis ist. Er ändert seine Rolle: Von einem Sicherheitsmechanismus wird er zum Instrument, das zeigt, ob der eigentliche Schutz noch wirkt, sowie zum Lieferanten von Labels für den Klassifikator, der das Lexikon aufräumt.
Dieselben Komponenten nebeneinander
Weil das Design mehrere bewegliche Teile aufweist, hilft eine kompakte Referenz. Die Flaggeregel der Version eins, das naive Prädikat, dessen Mängel zu diesem Problem führten, lautet:
flag ⟺ claim_regex.matches(reply) ∧ ¬ any(call.name == "create_order" for call in trace)
Die fünf Ergebnisse, erneut in der Reihenfolge und in Verbindung mit demjenigen, der falsch liegt:
no_claim ¬asserts(reply)
valid asserts ∧ ∃ create_order ∧ succeeded ∧ id ∈ result(create_order)
valid_status_ref asserts ∧ ¬create_order ∧ id ∈ result(lookup_tool)
claimed_but_failed asserts ∧ ∃ create_order ∧ ¬succeeded
phantom asserts ∧ id ∉ ⋃ result(t) for t in tools
Der Eingangsfilter des Klassifikators:
def classify(reply, trace):
if not asserts_order_exists(reply):
return NO_CLAIM
Der Teil, der die Werte anhand der Tool-Evidenz überprüft:
ids = extract_identifiers(reply) # candidates from the reply text
ids -= phone_number_shaped(ids) # local numbers collide with order ids creates = [c for c in trace if c.name == "create_order"]
lookups = [c for c in trace if c.name in LOOKUP_TOOLS] backed_by = {c: ids & identifiers_in(c.result) for c in creates + lookups} if creates and not any(succeeded(c) for c in creates):
return CLAIMED_BUT_FAILED
if any(backed_by[c] for c in creates):
return VALID
if any(backed_by[c] for c in lookups):
return VALID_STATUS_REF
return PHANTOM
Der überwachte Assertion-Detektor:
asserts_order_exists(reply) :=
match(CLAIM_LEXICON, reply)
∧ ¬ match(NEGATION_CIRCUMFIX, window_around(match))
∧ ¬ match(FUTURE_CONDITIONAL, prefix_of(match))
Und das Präventionsmuster, bei dem der Identifikator vom Tool-Ergebnis in ein Template fließt:
# instead of: model writes the confirmation, evaluator checks it afterwards
result = create_order(...)
if result.ok:
reply = CONFIRM_TEMPLATE.render(order_id=result.order_id) # model never holds the pen
else:
reply = model.compose(FAILURE_CONTEXT) # nothing to fabricate
Wenn man die Sequenz in dieser Reihenfolge liest, wird der gesamte Ablauf sichtbar: eine einfache Regel, eine Aufteilung, eine Wertüberprüfung, ein sprachspezifischer Schutzmechanismus und schließlich eine Konstruktion, in der ein Fehler nicht auftreten kann.
Fünf Faktoren, die halfen
- Lesen Sie die Spuren selbst. Überspringen Sie die vom Modell erstellten Zusammenfassungen der Beispiele und lesen Sie die Gespräche manuell, mit den unübersichtlichen Zahlen. Die nützlichste Erkenntnis war hier eine Formatierungsregelmäßigkeit in den künstlich erstellten Identifikatoren, die weder durch Aggregationen noch durch Anfragen aufgedeckt werden konnte.
- Trennen Sie die Ergebnisse nach den Personen, die sie bearbeiten. Eine Kennzahl, die Agentenfehler und Messfehler kombiniert, bricht bereits beim ersten Blick des zuständigen Teams zusammen.
Die Entwicklung des Bewerters war der einfachere Teil. Die eigentliche Herausforderung bestand darin, eine korrekte Antwort in einer Sprache zu definieren, die über keine geeigneten Werkzeuge verfügt – und zwar so präzise, dass eine Maschine sie testen sowie zwei Menschen sich darauf einigen können.