Wie man eine Text-zu-SQL-Funktion testet
Ein Evaluationsset für Text zu SQL ist eine Liste von Fragen mit bekannten richtigen Antworten, die bei jeder Änderung an Prompt oder Modell läuft. Es zeigt Ihnen, ob eine Änderung etwas verbessert oder verschlechtert hat.
Die Module 68 und 85 bewerten Antworten in Worten. Diese Lektion bewertet SQL an den Zeilen, die es liefert.
Am Ende dieser Lektion
Notiz
Ich kann Kandidatenabfragen an einem Set von Fragen mit bekannten Antworten bewerten.
Schlüsselwörter
| Begriff | Was er bedeutet |
|---|---|
| Evaluationsset | Fragen mit bekannten Antworten, bei jeder Änderung ausgeführt |
| Referenzabfrage | eine von Hand geprüfte Abfrage, die die richtigen Zeilen liefert |
| Ergebnisgleichheit | dieselben Zeilen wie die Referenzabfrage |
| Regression | eine Frage, die vorher bestand und jetzt scheitert |
| Schranke | eine Regel, die eine Änderung blockiert |
Die Zellen, die Lernende ausfüllen, bleiben in dieser Vorschau leer.
Jeder Fall hat eine Frage und eine Referenzabfrage
Ein Fall enthält eine Frage, eine Referenzabfrage und die Angabe, ob die Reihenfolge zählt. Schreiben Sie die Referenzabfrage selbst und prüfen Sie ihre Zeilen von Hand auf den Testdaten.
Führen Sie die Referenzabfrage aus, um die erwarteten Zeilen zu bekommen. Dann funktioniert das Set auch, wenn die Daten wachsen, solange die Referenzabfragen richtig bleiben.
Beispiel: fünf Fälle für die Bibliothek
| Fall | Frage | Referenzzeilen |
|---|---|---|
| 1 | Ausleihen im Monat 3 | 4 |
| 2 | Verschiedene Mitglieder im Monat 3 | 3 |
| 3 | Noch ausgeliehene Bücher | 5 |
| 4 | Titel, die Mitglied 2 geliehen hat | Eine Karte aus Regen, Leise Maschinen, Sieben Kessel |
| 5 | Nie geliehene Bücher | Nördlich vom Mittag |
Die Zellen, die Lernende ausfüllen, bleiben in dieser Vorschau leer.
Nach Ergebnis bewerten, nicht nach Text
COUNT(id) und COUNT(*) liefern hier dieselbe Zahl, und ein Join lässt sich als Unterabfrage schreiben. Zwei verschiedene Texte können beide richtig sein.
Führen Sie also jeden Kandidaten aus und vergleichen Sie seine Zeilen mit den Referenzzeilen. Ein genauer Textvergleich wertet richtige Antworten als falsch, wie der Run zeigt.
Beispiel: Text und Ergebnis widersprechen sich
| Kandidat | Gleicher Text wie Referenz | Gleiche Zeilen wie Referenz |
|---|---|---|
| SELECT COUNT(id) FROM ausleihen WHERE monat = 3 | Nein | Ja |
| Ein Join als IN (SELECT buch_id ...) geschrieben | Nein | Ja |
| SELECT COUNT(*) FROM ausleihen | Nein | Nein |
Die Zellen, die Lernende ausfüllen, bleiben in dieser Vorschau leer.
Eine Regression versteckt sich hinter derselben Quote
Ein neuer Prompt behebt Frage 2 und bricht Frage 3. Beide Versionen bestehen vier von fünf, die Quote hat sich also nicht bewegt.
Vergleichen Sie Fall für Fall. Eine Frage, die vorher bestand und jetzt scheitert, ist eine Regression, und die Schranke blockiert die Änderung, bis sie behoben ist.
Ein häufiger Fehler
Warnung
Viele denken, eine gleiche Erfolgsquote heiße, dass nichts schlechter wurde. Eine Frage kann behoben werden, während eine andere bricht. Vergleichen Sie jeden Fall mit dem letzten Lauf und blockieren Sie jede Änderung, die ein Bestehen verliert.
Die Modellhilfe schreiben, Teil 1
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'const basis = process.env.AI_URL || \"http://ai:4000\";' '// Die Token einer Antwort oder null, wenn sie keine nennt.' 'function zaehlen(daten, name) {' ' return daten.usage && typeof daten.usage[name] === \"number\" ? daten.usage[name] : null;' '}' '// Eine Antwort kann die Abfrage in einen umzäunten Block setzen: Wir behalten das Innere.' 'function sqlVon(text) {' ' const teile = text.split(String.fromCharCode(96).repeat(3));' ' const innen = (teile.length > 2 ? teile[1] : text).trim();' ' return innen.toLowerCase().startsWith(\"sql\") ? innen.slice(3).trim() : innen;' '}' > model.js"Hier angezeigt, nicht ausgeführt. Erstellen Sie ein kostenloses Konto, um „Ausführen“ zu drücken und ihn im Terminal Ihres eigenen Labs einzugeben.
Die Modellhilfe schreiben, Teil 2
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'async function fragen(nachrichten, probe, max) {' ' try {' ' const res = await fetch(basis + \"/v1/chat/completions\", {' ' method: \"POST\",' ' headers: { \"content-type\": \"application/json\", authorization: \"Bearer lab\" },' ' body: JSON.stringify({ model: \"lab\", messages: nachrichten, max_tokens: max || 100 }),' ' signal: AbortSignal.timeout(30000),' ' });' ' if (res.ok === false) throw new Error(\"Status \" + res.status);' ' const d = await res.json();' ' return { text: d.choices[0].message.content, eingabe: zaehlen(d, \"prompt_tokens\"), ausgabe: zaehlen(d, \"completion_tokens\"), aufgezeichnet: false };' ' } catch (fehler) {' ' console.log(\"Aufgezeichnete Probe: Kein Modell hat geantwortet (\" + fehler.message + \").\");' ' return { text: probe, eingabe: null, ausgabe: null, aufgezeichnet: true };' ' }' '}' 'module.exports = { fragen, sqlVon };' >> model.js"Hier angezeigt, nicht ausgeführt. Erstellen Sie ein kostenloses Konto, um „Ausführen“ zu drücken und ihn im Terminal Ihres eigenen Labs einzugeben.
Die kleine Bibliotheksdatenbank schreiben, Teil 1
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'const { DatabaseSync } = require(\"node:sqlite\");' '// Eine erfundene Bibliothek: sechs Bücher, vier Mitglieder, neun Ausleihen.' 'const BUECHER = [[1, \"Der Salzweg\", 2019], [2, \"Hafen aus Glas\", 2021], [3, \"Eine Karte aus Regen\", 2015],' ' [4, \"Leise Maschinen\", 2023], [5, \"Sieben Kessel\", 2018], [6, \"Nördlich vom Mittag\", 2022]];' 'const MITGLIEDER = [[1, \"Anke\"], [2, \"Bernd\"], [3, \"Carla\"], [4, \"Dirk\"]];' '// id, buch_id, mitglied_id, monat, zurueck (1 zurück, 0 noch draußen)' 'const AUSLEIHEN = [[1, 1, 1, 3, 1], [2, 2, 1, 3, 1], [3, 3, 2, 3, 0], [4, 1, 3, 3, 1], [5, 1, 1, 4, 0],' ' [6, 4, 2, 4, 0], [7, 2, 3, 4, 1], [8, 5, 2, 5, 0], [9, 2, 4, 5, 0]];' > bibliothek.js"Hier angezeigt, nicht ausgeführt. Erstellen Sie ein kostenloses Konto, um „Ausführen“ zu drücken und ihn im Terminal Ihres eigenen Labs einzugeben.
Die kleine Bibliotheksdatenbank schreiben, Teil 2
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'function fuellen(db, tabelle, zeilen) {' ' const marken = zeilen[0].map(function () { return \"?\"; }).join(\", \");' ' const einfuegen = db.prepare(\"INSERT INTO \" + tabelle + \" VALUES (\" + marken + \")\");' ' zeilen.forEach(function (zeile) { einfuegen.run(...zeile); });' '}' 'function oeffnen(datei) {' ' const db = new DatabaseSync(datei || \":memory:\");' ' db.exec(\"CREATE TABLE buecher (id INTEGER PRIMARY KEY, titel TEXT, jahr INTEGER)\");' ' db.exec(\"CREATE TABLE mitglieder (id INTEGER PRIMARY KEY, name TEXT)\");' ' db.exec(\"CREATE TABLE ausleihen (id INTEGER PRIMARY KEY, buch_id INTEGER REFERENCES buecher(id), \"' ' + \"mitglied_id INTEGER REFERENCES mitglieder(id), monat INTEGER, zurueck INTEGER)\");' ' fuellen(db, \"buecher\", BUECHER);' ' fuellen(db, \"mitglieder\", MITGLIEDER);' ' fuellen(db, \"ausleihen\", AUSLEIHEN);' ' return db;' '}' 'module.exports = { oeffnen };' >> bibliothek.js"Hier angezeigt, nicht ausgeführt. Erstellen Sie ein kostenloses Konto, um „Ausführen“ zu drücken und ihn im Terminal Ihres eigenen Labs einzugeben.
Das Evaluationsskript schreiben, Teil 1
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'const { fragen, sqlVon } = require(\"./model\");' 'const { oeffnen } = require(\"./bibliothek\");' 'const db = oeffnen();' 'const faelle = [' ' { referenz: \"SELECT COUNT(*) FROM ausleihen WHERE monat = 3\" },' ' { referenz: \"SELECT COUNT(DISTINCT mitglied_id) FROM ausleihen WHERE monat = 3\" },' ' { referenz: \"SELECT COUNT(*) FROM ausleihen WHERE zurueck = 0\" },' ' { referenz: \"SELECT b.titel FROM ausleihen a JOIN buecher b ON a.buch_id = b.id WHERE a.mitglied_id = 2\" },' ' { referenz: \"SELECT titel FROM buecher WHERE id NOT IN (SELECT buch_id FROM ausleihen)\" },' '];' > evaluieren.js"Hier angezeigt, nicht ausgeführt. Erstellen Sie ein kostenloses Konto, um „Ausführen“ zu drücken und ihn im Terminal Ihres eigenen Labs einzugeben.
Das Evaluationsskript schreiben, Teil 2
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'const A = [\"SELECT COUNT(id) FROM ausleihen WHERE monat = 3\",' ' \"SELECT COUNT(mitglied_id) FROM ausleihen WHERE monat = 3\", faelle[2].referenz,' ' \"SELECT b.titel FROM buecher b JOIN ausleihen a ON a.buch_id = b.id WHERE a.mitglied_id = 2\",' ' \"SELECT b.titel FROM buecher b LEFT JOIN ausleihen a ON a.buch_id = b.id WHERE a.id IS NULL\"];' 'const B = [faelle[0].referenz, faelle[1].referenz, \"SELECT COUNT(*) FROM ausleihen\",' ' \"SELECT titel FROM buecher WHERE id IN (SELECT buch_id FROM ausleihen WHERE mitglied_id = 2)\", faelle[4].referenz];' >> evaluieren.js"Hier angezeigt, nicht ausgeführt. Erstellen Sie ein kostenloses Konto, um „Ausführen“ zu drücken und ihn im Terminal Ihres eigenen Labs einzugeben.
Das Evaluationsskript schreiben, Teil 3
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'function zeilen(sql) {' ' try { return db.prepare(sql).all().map(function (r) { return JSON.stringify(Object.values(r)); }).sort().join(\"|\"); }' ' catch (fehler) { return null; }' '}' 'function bewerten(sql, i) {' ' const ist = zeilen(sql);' ' return ist === null ? \"Fehler\" : ist === zeilen(faelle[i].referenz) ? \"bestanden\" : \"gescheitert\";' '}' 'function quote(liste, test) {' ' return Math.floor(100 * liste.filter(test).length / liste.length) + \" Prozent\";' '}' 'const a = A.map(bewerten);' 'const b = B.map(bewerten);' 'const gleich = function (sql, i) { return sql === faelle[i].referenz; };' >> evaluieren.js"Hier angezeigt, nicht ausgeführt. Erstellen Sie ein kostenloses Konto, um „Ausführen“ zu drücken und ihn im Terminal Ihres eigenen Labs einzugeben.
Das Evaluationsskript schreiben, Teil 4
docker exec -w /home/node/project agent sh -c "printf '%s\n' '// Eine Regression: in A bestanden, in B nicht mehr.' 'const verloren = [];' 'a.forEach(function (v, i) { if (v === \"bestanden\" && (b[i] === \"gescheitert\" || b[i] === \"Fehler\")) verloren.push(i + 1); });' 'async function main() {' ' a.forEach(function (v, i) { console.log(\"Frage \" + (i + 1) + \": A \" + v + \", B \" + b[i]); });' ' const ok = function (v) { return v === \"bestanden\"; };' ' console.log(\"nach Ergebnis: A \" + quote(a, ok) + \", B \" + quote(b, ok));' ' console.log(\"nach genauem Text: A \" + quote(A, gleich) + \", B \" + quote(B, gleich));' ' console.log(\"Regressionen in B: \" + (verloren.length ? verloren.join(\", \") : \"keine\"));' ' console.log(\"Schranke: \" + (verloren.length ? \"blockieren, eine bestandene Frage scheitert jetzt\" : \"ausliefern\"));' ' await live();' '}' >> evaluieren.js"Hier angezeigt, nicht ausgeführt. Erstellen Sie ein kostenloses Konto, um „Ausführen“ zu drücken und ihn im Terminal Ihres eigenen Labs einzugeben.
Das Evaluationsskript schreiben, Teil 5
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'async function live() {' ' const f = \"ausleihen(id, buch_id, mitglied_id, monat, zurueck)\nFrage: Wie viele verschiedene Mitglieder haben im Monat 3 ein Buch geliehen?\";' ' const probe = \"SELECT COUNT(DISTINCT mitglied_id) FROM ausleihen WHERE monat = 3;\";' ' const antwort = await fragen([{ role: \"system\", content: \"Antworten Sie mit genau einer SQLite-SELECT-Abfrage und sonst nichts.\" },' ' { role: \"user\", content: f }], probe, 120);' ' const sql = sqlVon(antwort.text);' ' console.log(\"Das Modell schrieb: \" + sql);' ' console.log(\" getrennt von den Summen bewertet: \" + bewerten(sql, 1));' '}' 'main();' >> evaluieren.js"Hier angezeigt, nicht ausgeführt. Erstellen Sie ein kostenloses Konto, um „Ausführen“ zu drücken und ihn im Terminal Ihres eigenen Labs einzugeben.
Zwei Versionen an fünf Fragen bewerten
docker exec -w /home/node/project agent node --experimental-sqlite --no-warnings evaluieren.jsHier angezeigt, nicht ausgeführt. Erstellen Sie ein kostenloses Konto, um „Ausführen“ zu drücken und ihn im Terminal Ihres eigenen Labs einzugeben.
Was der Run ausgibt
| Zeile | Was sie zeigt |
|---|---|
| Frage 2: A gescheitert, B bestanden | Der neue Prompt hat Frage 2 behoben |
| Frage 3: A bestanden, B gescheitert | Der neue Prompt hat Frage 3 gebrochen |
| nach Ergebnis: A 80 Prozent, B 80 Prozent | Die Quote hat sich nicht bewegt |
| nach genauem Text: A 20 Prozent, B 60 Prozent | Der Textvergleich wertet richtige Antworten als falsch |
| Regressionen in B: 3 | Die verlorene Frage |
| Schranke: blockieren, eine bestandene Frage scheitert jetzt | Die Entscheidung |
Die Zellen, die Lernende ausfüllen, bleiben in dieser Vorschau leer.