Zum Inhalt springen

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-classifier

Auf 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 here

Fü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_match

Ausgaben 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

KriteriumEvaluatorBraucht expectedWas er misst
exact_labelexact_match auf labeljaAufgabenerfolg: das Label ist das richtige
format_validjson_schema über die ganze AusgabeneinFormat: das Objekt hat genau die zwei String-Felder
pii_freeregex auf answer, pass_if: no_matchneineine 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: 0

exact-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 run

Echte 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 miss

So 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_04

RUN_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: failed
case 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: passed

Das 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 run
Gate: 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.10
oloproof compare CANDIDATE_RUN_ID BASELINE_RUN_ID --policy compare.yaml
Comparison 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

SymptomUrsache und Abhilfe
ModuleNotFoundError für appFü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 erzeugtDie 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 wiederverwendetDer 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 fehlendDiese 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ätzungDas 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).