Guillaume Duvernay

Experiment

CSV-Import-Mapping mit einem einzigen Modellaufruf (Jev)

Die Frage
Wie viel eines CSV-Spaltenmappings kann im Code erledigt werden, und kann ein einziger Modellaufruf alles andere handhaben?
Ergebnis
Striktes Matching erledigt bis zu 7 von 10 Spalten kostenlos, und ein Aufruf bewertet jedes verbleibende Paar in unter 700 ms für einen Zehntel-Cent.

AI efficiencyproductsdata

Was ich herausfinden wollte

Jedes Produkt, das CSV-Importe unterstützt, hat diesen Bildschirm. Der Nutzer lädt eine Datei hoch, und etwas muss entscheiden, dass dessen Lineitem sku Ihr sku entspricht.

Man könnte dies bereits mit einem kleinen LLM tun, und es funktionierte einigermaßen gut. Das Ergebnis war jedoch ein String, dem man vertrauen musste, nach einer Wartezeit, die der Nutzer spürte. Ich wollte wissen, ob derselbe Job in drei Teile gesplittet werden kann: einfacher Code erledigt alles, was er korrekt lösen kann, ein Modell wird einmalig für den gesamten Rest gefragt, und die Entscheidung wird zurück in den Code geholt, wo ich sie einsehen und ändern kann.

Jev macht den mittleren Teil günstig. Es schreibt keinen Text, es beantwortet Fragen, die mein Code definiert, und gibt eine Wahrscheinlichkeit für jede zurück. Die Frage ist also nicht wirklich, ob ein Modell Spalten mappen kann, sondern wie viel der Arbeit das Modell gar nicht benötigt und ob ein Aufruf den Rest bewältigen kann.

Wie getestet wurde

Drei Zieltabellen mit jeweils zehn Spalten und neun Beispiel-CSVs, die eher nach schwierigen als nach einfachen Fällen ausgewählt wurden: ein sauberer HubSpot-Export, eine Datei vollständig auf Französisch mit zwei verschiedenen E-Mail-Spalten, ein Shopify-Export, bei dem Name die Bestellnummer ist, und ein Lager-Export mit 23 Spalten, von denen 13 nirgendwo hingehören.

Jede Zielspalte hat eine einzeilige Beschreibung, und diese Beschreibung ist keine Dekoration. Sie ist das, was zwei benachbarte Felder voneinander trennt.

Die zehn Spalten der E-Commerce-Bestellungs-Tabelle, jede mit einem Typ und einem Satz, der beschreibt, was sie enthält. unit_price heißt: Preis einer Einheit, vor Steuern und Rabatt, nicht die Bestellsumme.
Das Zielschema. Jede nachfolgende Mapping-Entscheidung erfolgt auf Basis dieser Sätze; so wird bei unit_price explizit gesagt, dass es nicht die Bestellsumme ist.

Der Durchlauf selbst besteht aus drei Stufen, wobei nur die mittlere ein Modell ist.

Das Modell sitzt in der Mitte und entscheidet nichts. Es liefert Zahlen; der Code davor reduziert die Arbeit, und der Code danach trifft die Entscheidung.

Die Form der Fragen ist die einzige Designentscheidung, über die es sich zu streiten lohnt. Pro eingehender Spalte eine Multiple-Choice-Frage zu stellen, wäre der offensichtliche Weg, aber es ist der falsche.

Eine Wahl muss einen Gewinner benennen. Eine Matrix darf sagen, dass hier nichts passt, was bei einem echten Export in etwa jedem dritten Fall die Antwort ist.

Das Ergebnis

Alle neun Dateien wurden in einem einzigen Aufruf verarbeitet.

DateiSpaltenIn Code geklärtFragenJev-ZeitKosten
crm-hubspot-export.csv10712464 ms$0.000077
hr-bamboo.csv8518374 ms$0.000109
orders-minimal.csv5055409 ms$0.000287
hr-payroll.csv10272484 ms$0.000362
crm-eventbrite.csv7077514 ms$0.000395
orders-shopify.csv10190437 ms$0.000462
crm-french-crm.csv100110495 ms$0.000549
hr-identity-provider.csv120132474 ms$0.000658
orders-warehouse-export.csv230253675 ms$0.001231

Die Kosten für die Fragen sind 21-mal höher als die Wartezeit, steigen aber nur um das 1,45-fache. Zwölf Fragen benötigen 464 ms, 253 Fragen 675 ms. Auf dieser Eigenschaft basiert das gesamte Design: Wäre jede Frage ein eigener Aufruf, würde die Warehouse-Datei zwei Minuten statt zwei Drittel einer Sekunde dauern, und die Matrix wäre unbrauchbar.

Beim HubSpot-Export erreichen 7 der 10 Spalten das Modell überhaupt nicht. Dieser Teil ist kostenlos, sofortig und kann nicht falsch sein.

Das Shopify-Mapping. Währung ist als EXACT markiert, ohne Prozentwert. Name wird zu 95 Prozent auf order_ref abgebildet, Lineitem sku auf sku zu 98 Prozent.
Währung wird im Code geklärt und hat keinen Score, da es hier keine Unsicherheit gibt. Name ist die Shopify-Falle: Es ist die Bestellnummer, keine Person, und landet bei 95 % auf order_ref, da die drei Beispielwerte #1042 lauten.

Die Beispielwerte erledigen den Großteil der Arbeit. Jede Spalte wird mit drei ihrer Werte gesendet, gekürzt auf die ersten und letzten 100 Zeichen. Das signalisiert dem Modell, dass Shopifys Name eine Bestellreferenz ist, und ist der Grund, warum derselbe Spaltenname unterschiedlich zugeordnet wird, je nachdem, was darunter steht.

Ein Aufruf, und dann gehören alle Zahlen Ihnen

Der Aufruf gibt die gesamte Matrix zurück, nicht eine einzelne Entscheidung. Das Öffnen der Matrix ist die klarste Darstellung dessen, was tatsächlich passiert ist.

Ein 23x10-Gitter aus Wahrscheinlichkeiten. Die meisten Zellen zeigen 1 oder 2 Prozent. Eine dunkle Zelle pro Zeile markiert die Übereinstimmung. unit_amount zeigt 81 bei unit_price, während total_amount, tax_amount und discount_amount in derselben Spalte bei 2 oder 3 liegen.
253 Zahlen aus einem Aufruf. Betrachten Sie die Spalte unit_price: unit_amount liegt bei 81, während total_amount, tax_amount und discount_amount bei 2 und 3 liegen. Vier Währungsspalten mit fast identischen Namen, die durch die Beschreibung im Schema unterschieden werden.

Da der Code jeden Wert enthält, ist der Schwellenwert eine Konstante, die ich verschieben kann, statt eines Verhaltens, das ich per Prompt erzwingen muss. Bei 0,75 bildet der Warehouse-Export alle zehn Ziele ab und verwirft die richtigen dreizehn Spalten.

Das Mapping des Warehouse-Exports. Zehn von zehn Zielen wurden aus 253 Fragen in einem Aufruf zugeordnet. Unten sind dreizehn Spalten als nicht importiert aufgeführt, jede mit einem Prozentwert zwischen 96 und 98.
Die dreizehn Spalten, die nirgends hingehören, erhalten ihren eigenen Score aus der Guard-Frage. 96 bis 98 % bedeutet, dass das Modell sicher ist, dass es keinen Platz für sie gibt. Das Verwerfen ist somit eine dokumentierte Entscheidung und kein bloßes Ausbleiben einer Antwort.

Wo es zurückgegeben wird

Die Eventbrite-Datei ist diejenige, die das Modell nicht abschließen kann, und genau das macht diesen Bildschirm interessant.

Das Eventbrite-Mapping. Vier Ziele sind mit Scores von 97 und 98 Prozent zugeordnet. Sechs sind Dropdowns mit der Markierung MANUAL, die jeweils den besten gefundenen Score von 1 bis 9 Prozent zeigen. Ein gelber Hinweis besagt, dass eine Spalte verworfen wurde, obwohl jev besagt, dass sie einen Platz im Schema hat.
Vier gelöst, sechs zurückgegeben. Jede ungelöste Zeile zeigt immer noch den besten vom Modell gefundenen Score, sodass first_name mit maximal 9 % als 'nichts hier ist nah dran' und nicht als Leerwert erscheint.

Die Guard-Frage hat ihren Platz am unteren Ende dieses Bildschirms. Attendee erzielt 9 % bei “gehört dies nirgendwo hin”, das heißt, das Modell sagt, dass es einen Platz hat, aber das beste Paar lag unter 0,75. Die Benutzeroberfläche markiert dies gelb, anstatt es stillschweigend zu verwerfen. Ticket Type mit 95 % und Registered At mit 97 % werden ohne Kommentar verworfen, da sie tatsächlich nirgendwo hingehören.

Was ich daraus ziehe

Erledigen Sie den deterministischen Teil und nichts weiter als den deterministischen Teil. Die erste Version normalisierte Groß-/Kleinschreibung, Akzente und Trennzeichen und enthielt zudem eine kleine Alias-Tabelle: attendeecountry zu country, site zu location, team zu department. Das sind Urteile, die sich als Normalisierung tarnen, und sie führen doppelt zu Fehlern. Eine Datei, die sowohl Attendee Country als auch Company Country enthält, erzeugt zwei exakte Treffer für ein Ziel, und der Code sperrt stillschweigend den Wert, der in der Spaltenreihenfolge zuerst kam. Und team ist nicht department, sondern eine Frage. Das Entfernen der Aliase ließ das Problem verschwinden, anstatt mich zur Schiedsrichterrolle zu zwingen, da nun keine Spalte mehr exakt passt und das Modell sie anhand ihrer Werte trennt.

Einmal fragen, danach entscheiden. Ein Aufruf liefert 253 kalibrierte Zahlen in 675 ms, und alles danach ist Arithmetik, die ich nachvollziehen kann: der Schwellenwert, die Reihenfolge der Auflösung, was als Warnung zählt. Nichts am Ergebnis hängt davon ab, dass ein Modell dazu überredet wurde, sich so zu verhalten. Das Verschieben der Grenze von 0,75 auf 0,80 ist eine Konstante, kein Prompt.

Eine Wahrscheinlichkeit ist es wert, gezeigt zu werden. Da die Zahl ohnehin existiert, gibt die Benutzeroberfläche sie aus, und eine Zuordnung mit 81 % wirkt anders als eine mit 98 %. Das Modell führt einen Vorlauf durch, trifft aber nicht die Entscheidung: Der Nutzer sieht, wie sicher es sich war, bei welcher Spalte, und übernimmt die Kontrolle über die wichtigen Fälle. Das ist ein besseres Angebot als ein selbstbewusstes Dropdown ohne Zahlenwert.

Dies wurde mit neun Dateien getestet, jeweils mit drei Tabellen und zehn Zielspalten. Ein Schema mit 40 Spalten würde mehr als einen Aufruf erfordern, und das zweite Budget im Kontextfenster von jev wird nicht aufgeteilt. Somit ist eine sehr breite Zieltabelle ein echtes Limit und kein bloßer Performance-Einbruch. Ich habe es nicht ausprobiert.