Anleitungen
Tutorial: ein Klassifikator oder strukturierte Ausgabe
Evaluieren Sie eine Python-Funktion, die Support-Fragen mit Labels versieht, lesen Sie, warum das Release blockiert ist, beheben Sie die Fehltreffer und vergleichen Sie die Korrektur mit dem Original, alles auf Ihrem eigenen Rechner, ohne Konto, ohne Netzwerk und ohne Modell.
Was Sie bauen
Einen Support-Bot, der ein JSON-Objekt mit einer answer und einem label (refund, account oder other) zurückgibt. Sie messen ihn an drei Anforderungen: Das Label stimmt oft genug, die Ausgabe hat immer die richtige Form, und keine Antwort gibt etwas preis, das wie eine US-Sozialversicherungsnummer aussieht. Zwei davon sind Formatprüfungen, die keine Referenzantwort brauchen; eine misst den Aufgabenerfolg gegen ein Referenzlabel. Der Unterschied ist wichtig, und diese Seite hält beides auseinander.
Die unten verwendeten Begriffe (Fall, Lauf, Metrik, Intervall, Regel, Gate) sind in Kernkonzepte definiert.
Voraussetzungen
- Python 3.11 oder neuer.
- Oloproof, in eine virtuelle Umgebung installiert:
python3 -m venv .venv
. .venv/bin/activate
pip install oloproof- Das Beispielprojekt und die Kandidatenänderung, die mit dem Paket ausgeliefert werden. Kopieren Sie beide in neue Verzeichnisse und arbeiten Sie im ersten; jede Datei ist auch unten aufgeführt, sodass Sie sie auch abtippen können:
oloproof init --example support_bot support-classifier
oloproof init --example classification support-change
cd support-classifierAuf dieser Seite werden weder API-Schlüssel noch Provider-Konto noch Netzwerkzugriff verwendet.
Die Dateien
support-classifier/
app.py the application under test (a Python callable)
oloproof.yaml the suite: dataset, system, evaluators
release.yaml the release policy: rules the run is decided against
data/support.jsonl 18 cases, one JSON object per line
rubrics/helpful.md a judge rubric, unused hereFühren Sie jeden Befehl im Verzeichnis support-classifier/ aus. Oloproof legt seinen Speicher dort in .oloproof/ ab; löschen Sie dieses Verzeichnis, um wieder bei null anzufangen.
Die Anwendung und ihr Adapter
Ihre Anwendung wird über einen Adapter erreicht. Bei einer Python-Anwendung ist der Adapter die Funktion selbst: Oloproof importiert sie, ruft sie einmal pro Fall mit dem input des Falls auf und speichert das zurückgegebene Dictionary als Ausgabe dieses Falls.
# app.py
from typing import Any
from oloproof import system
@system(name="support-bot", version="slice-a-example")
def answer(case: dict[str, Any]) -> dict[str, str]:
question = str(case["question"]).lower()
if "refund" in question:
return {"answer": "Refunds are available within 30 days when the order is eligible.",
"label": "refund"}
if "password" in question or "login" in question:
return {"answer": "Use password reset, then contact support if the login still fails.",
"label": "account"}
return {"answer": "A support specialist will follow up with the next step.", "label": "other"}Um Ihren eigenen Klassifikator zu evaluieren, lassen Sie seinen Code, wo er ist, und schreiben Sie eine schlanke Funktion wie diese, die ihn aufruft und ein Dictionary zurückgibt. Die Funktion darf async sein. Oloproof ruft sie auf; es hostet, isoliert oder setzt Ihre Anwendung nicht zurück, daher verwalten Sie jeden Zustand, den Ihre Anwendung zwischen Aufrufen hält, selbst.
oloproof.yaml benennt diese Funktion und die Evaluatoren:
version: 1
project: support-bot-example
dataset: data/support.jsonl
system:
name: support-bot
version: slice-a-example
callable: app:answer
timeout_s: 30
evaluators:
- type: exact_match
criterion: exact_label
field: label
- type: json_schema
criterion: format_valid
field: null
schema:
type: object
required: [answer, label]
properties:
answer: {type: string}
label: {type: string}
additionalProperties: false
- type: regex
criterion: pii_free
field: answer
pattern: '\b\d{3}-\d{2}-\d{4}\b'
pass_if: no_matchAusgaben werden anhand des Quellcodes der Funktion, der deklarierten version und der config gecacht. Liest die Funktion weitere Dateien (einen Prompt, eine Regeltabelle), führen Sie diese unter system.code_paths auf, damit eine Änderung daran das System erneut ausführt.
Der Datensatz
Ein Fall pro Zeile. input ist genau das, was Ihre Funktion als case erhält; expected ist die Referenz, mit der der Evaluator exact_match vergleicht:
{"id":"refund_00","input":{"question":"Can I get a refund for yesterday's order?"},"expected":{"label":"refund"}}
{"id":"account_04","input":{"question":"I can't sign in on my new phone."},"expected":{"label":"account"}}
{"id":"other_04","input":{"question":"I don't want a refund, I just need a copy of my receipt."},"expected":{"label":"other"}}Die Funktion gibt für jeden Fall ein Objekt zurück wie {"answer": "Use password reset, ...", "label": "account"}.
Die Evaluatoren wählen
| Kriterium | Evaluator | Braucht expected | Was er misst |
|---|---|---|---|
| exact_label | exact_match auf label | ja | Aufgabenerfolg: das Label ist das richtige |
| format_valid | json_schema über die ganze Ausgabe | nein | Format: das Objekt hat genau die zwei String-Felder |
| pii_free | regex auf answer, pass_if: no_match | nein | eine Sicherheitseigenschaft des Textes |
Eine Formatprüfung lässt eine wohlgeformte falsche Antwort durch, daher kann sie nie den Aufgabenerfolg ersetzen. Eine Aufgabenprüfung braucht für jeden Fall eine Referenz; wo ein Fall keine hat, kann exact_match ihn nicht bewerten. Deterministische Evaluatoren brauchen keine Validierung gegen Menschen: Zweimal ausgeführt, liefern sie dasselbe Urteil.
Die Policy
release.yaml ist das, wogegen der Lauf entschieden wird:
version: 1
confidence_level: 0.95
block_on: [FAIL, INSUFFICIENT_EVIDENCE, MANUAL_REVIEW]
warn_on: []
rules:
- id: exact-label-floor
metric: exact_label
min: 0.70
- id: valid-format
metric: format_valid
kind: observed_count
max_failures: 0
- id: pii-free
metric: pii_free
kind: observed_count
max_failures: 0exact-label-floor besagt, dass das Label in mindestens 70% der Fälle stimmen muss, und gilt nur dann als bestanden, wenn das gesamte 95%-Intervall bei oder über 0,70 liegt. Die beiden observed_count-Regeln erlauben auf den ausgeführten Fällen überhaupt keinen Fehlschlag; sie beschreiben diese Fälle, nicht jede Frage, die Nutzer stellen werden.
Ausführen
oloproof runEchte Ausgabe, gekürzt:
Run run_01M4... [DECIDED/COMPLETE]
Gate: BLOCK (exit 3)
│ exact-label-floor │ exact_label │ INSUFFICIENT_EVIDENCE │ interval_overlaps_threshold │
│ valid-format │ format_valid │ PASS │ observed_failures_within_limit │
│ pii-free │ pii_free │ PASS │ observed_failures_within_limit │
│ exact_label │ 72.2% │ [46.5%, 90.4%] │ 13 / 18 observed · 0 missing · 0 excluded │
│ format_valid │ 100.0% │ [81.4%, 100.0%] │ 18 / 18 observed · 0 missing · 0 excluded │
│ pii_free │ 100.0% │ [81.4%, 100.0%] │ 18 / 18 observed · 0 missing · 0 excluded │
Cache: execution 0 hit/18 miss; judgment 0 hit/54 missSo lesen Sie das:
- 13 von 18 Labels stimmen, 72,2%. Das liegt über 0,70, aber das Intervall reicht bis 46,5% hinunter: 18 Fälle können nicht zeigen, dass die wahre Rate mindestens 0,70 beträgt. Daher ist die Regel INSUFFICIENT_EVIDENCE, weder PASS noch FAIL.
- Jede Ausgabe hat die richtige Form, und keine enthält eine SSN-ähnliche Nummer, daher bestehen beide Formatregeln.
- block_on enthält INSUFFICIENT_EVIDENCE, also blockiert das Gate, und der Befehl endet mit 3. Exit 0 hieße, dass nichts vorliegt, worauf die Policy blockiert; Gating in der CI listet jeden Code auf.
Führen Sie es erneut aus, und die Cache-Zeile lautet execution 18 hit/0 miss: Nichts hat sich geändert, also wird die Funktion nicht aufgerufen.
Die Fehlschläge untersuchen
oloproof inspect RUN_ID --failures
oloproof inspect RUN_ID --case refund_04RUN_ID ist die ID in der ersten Zeile der Ausgabe des Laufs.
5 of 18 cases failed, errored or did not finish
refund_04
output: {"answer": "A support specialist will follow up with the next step.", "label": "other"}
exact_label: failed
...
other_04
output: {"answer": "Refunds are available within 30 days when the order is eligible.", "label": "refund"}
exact_label: failedcase refund_04
input: {
"question": "I was charged twice this month and want my money back."
}
expected: {
"label": "refund"
}
execution: OK, 1 ms
output: {
"answer": "A support specialist will follow up with the next step.",
"label": "other"
}
judgments:
exact_label: failed
format_valid: passed
pii_free: passedDas Muster ist offensichtlich, sobald Sie die Eingaben lesen: "money back", "reverse the payment", "sign in" und "two-factor" stehen nicht in den Schlüsselwortlisten, und other_04 sagt "I don't want a refund", worauf das Wort "refund" trotzdem passt. Beachten Sie, dass refund_04 beide Formatprüfungen besteht und dennoch falsch ist: Das ist die Lücke zwischen dem Prüfen des Formats und dem Messen des Erfolgs.
Zwei nächste Schritte sind hier sinnvoll. Beheben Sie die Fehltreffer (unten), oder ergänzen Sie Fälle: Mit mehr Fällen bei gleicher Accuracy wird das Intervall schmaler, und oloproof plan RUN_ID --run schätzt, wie viele es braucht.
Eine echte Änderung vornehmen
Kopieren Sie ../support-change/app.py über app.py. Es ergänzt die verpassten Formulierungen:
REFUND_WORDS = ("refund", "money back", "reverse the payment")
ACCOUNT_WORDS = ("password", "login", "sign in", "two-factor")
@system(name="support-bot", version="keywords-v2")
def answer(case: dict[str, Any]) -> dict[str, str]:
question = str(case["question"]).lower()
if any(word in question for word in REFUND_WORDS):
...und setzen Sie version: keywords-v2 unter system in oloproof.yaml, damit der Lauf als neue Version erfasst wird. Dann:
oloproof runGate: ALLOW (exit 0)
│ exact-label-floor │ exact_label │ PASS │ lower_bound_meets_minimum │
│ exact_label │ 94.4% │ [72.7%, 99.9%] │ 17 / 18 observed · 0 missing · 0 excluded │17 von 18 stimmen, und die untere Grenze des Intervalls, 72,7%, liegt über 0,70, also besteht die Regel, und der Befehl endet mit 0. other_04 schlägt weiterhin fehl: Die Korrektur hat die Verneinung nicht berührt.
Den Kandidaten mit der Baseline vergleichen
Die Laufregel fragt, ob der Kandidat Ihre Untergrenze erfüllt. Ein Vergleich fragt, wie er sich Fall für Fall von der Baseline unterscheidet. Kopieren Sie ../support-change/compare.yaml in das Projekt; es enthält eine Vergleichsregel:
version: 1
confidence_level: 0.95
block_on: [FAIL, INSUFFICIENT_EVIDENCE, MANUAL_REVIEW]
rules:
- id: label-no-regression
kind: non_inferiority
metric: exact_label
margin: 0.10oloproof compare CANDIDATE_RUN_ID BASELINE_RUN_ID --policy compare.yamlComparison sha256:de76... of run_01M4...TJAD against run_01M4...ECVEF · 18 paired cases
exact_label: +22.2 points [-12.9, +57.0] · 18 paired · 0 missing · 0 excluded
format_valid: +0.0 points [-25.8, +25.8] · 18 paired · 0 missing · 0 excluded
pii_free: +0.0 points [-25.8, +25.8] · 18 paired · 0 missing · 0 excluded
Decisions
label-no-regression exact_label non-inferiority, margin 10.0 points INSUFFICIENT_EVIDENCE interval_overlaps_margin
about 3 more paired cases would decide it, if the difference holds (21 in total at 22% discordance)
Gate: BLOCK (exit 3)Der Kandidat hat vier Fälle behoben und keinen kaputt gemacht, ein geschätzter Gewinn von 22 Punkten. Aber nur vier Fälle haben sich geändert, und 18 gepaarte Fälle lassen ein Intervall von 12,9 Punkten schlechter bis 57 Punkten besser, das die Marge von 10 Punkten überschreitet. Der Vergleich kann noch nicht ausschließen, dass der Kandidat um mehr schlechter ist, als Sie akzeptieren, also ist er INSUFFICIENT_EVIDENCE und endet mit 3. Die Zeile darunter ist die Schätzung des Stichprobenumfangs. Ohne --policy gibt compare die Unterschiede aus, meldet, dass die release.yaml des Projekts keine Vergleichsregel deklariert, und endet mit 0, weil nichts entschieden wurde.
Einen Kandidaten mit einer Baseline vergleichen erklärt die Marge und die anderen Regelarten.
Fehlerbehebung
| Symptom | Ursache und Abhilfe |
|---|---|
| ModuleNotFoundError für app | Führen Sie den Befehl im Verzeichnis mit app.py aus, oder geben Sie callable einen Modulpfad, der von dort importierbar ist. |
| Eine Regel nennt eine Metrik, die kein Evaluator erzeugt | Die metric der Regel muss dem criterion eines Evaluators entsprechen; die Fehlermeldung listet die vorhandenen Metriken auf. |
| Sie haben den Klassifikator geändert, und der Lauf hat jede Ausgabe wiederverwendet | Der Cache folgt dem Quellcode des Callables; eine Hilfsdatei, die er liest, muss unter system.code_paths aufgeführt sein. |
| exact_label meldet Fälle als fehlend | Diese Ausführungen haben eine Ausnahme ausgelöst oder ein Timeout erreicht; oloproof inspect RUN_ID --failures zeigt jeden Fehler. |
| Der Lauf endet mit 3 trotz hoher Schätzung | Das Intervall entscheidet, nicht die Schätzung. Ergänzen Sie Fälle oder akzeptieren Sie eine niedrigere Untergrenze, festgelegt vor dem Lauf. |
Einschränkungen
- Das SDK und YAML melden Bestehensraten pro Evaluator. Es gibt keine Konfusionsmatrix und keine Precision und Recall pro Klasse für einen Klassifikator wie diesen; ein predictive:-Block liefert diese für ein Modell mit Scores (Klassifikatoren und Regressoren).
- observed_count-Regeln beschreiben die ausgeführten Fälle; sie treffen keine Aussage über ungesehene Eingaben.
- Ein Vergleich über 18 Fälle löst nur große Unterschiede auf. Fünfzig oder mehr echte Fälle sind eine nützlichere Untergrenze.
- oloproof.yaml kann keinen eigenen @evaluator benennen; dafür braucht es das SDK (Die Python-API).