Przewodniki
Samouczek: klasyfikator lub wynik ustrukturyzowany
Oceń funkcję w Pythonie, która nadaje etykiety pytaniom do wsparcia, przeczytaj, dlaczego wydanie jest zablokowane, napraw pomyłki i porównaj poprawkę z oryginałem, wszystko na własnej maszynie, bez konta, bez sieci i bez modelu.
Co zbudujesz
Bota wsparcia, który zwraca obiekt JSON z answer i label (refund, account lub other). Postawisz mu trzy wymagania: etykieta jest poprawna wystarczająco często, wynik zawsze ma właściwy kształt i żadna odpowiedź nie ujawnia niczego, co wygląda jak amerykański numer ubezpieczenia społecznego. Dwa z nich to sprawdzenia formatu, które nie potrzebują odpowiedzi wzorcowej; jedno mierzy sukces zadania względem etykiety wzorcowej. Ta różnica ma znaczenie i ta strona trzyma je osobno.
Terminy używane poniżej (przypadek, przebieg, metryka, przedział, reguła, bramka) są zdefiniowane w Pojęciach.
Wymagania wstępne
- Python 3.11 lub nowszy.
- Oloproof zainstalowany w środowisku wirtualnym:
python3 -m venv .venv
. .venv/bin/activate
pip install oloproof- Przykładowy projekt i zmiana kandydująca, dostarczane z pakietem. Skopiuj oba do nowych katalogów i pracuj w pierwszym; każdy plik jest też pokazany poniżej, więc możesz je przepisać:
oloproof init --example support_bot support-classifier
oloproof init --example classification support-change
cd support-classifierNigdzie na tej stronie nie są używane klucz API, konto u dostawcy ani dostęp do sieci.
Pliki
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 hereUruchamiaj każde polecenie z katalogu support-classifier/. Oloproof trzyma tam swój magazyn w .oloproof/; usuń ten katalog, aby zacząć od zera.
Aplikacja i jej adapter
Do Twojej aplikacji dociera się przez adapter. Dla aplikacji w Pythonie adapterem jest sama funkcja: Oloproof ją importuje, wywołuje raz na przypadek z input przypadku i zapisuje zwrócony słownik jako wynik tego przypadku.
# 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"}Aby ocenić własny klasyfikator, zostaw jego kod tam, gdzie jest, i napisz cienką funkcję taką jak ta, która go wywołuje i zwraca słownik. Funkcja może być async. Oloproof ją wywołuje; nie hostuje, nie izoluje ani nie resetuje Twojej aplikacji, więc każdy stan, który aplikacja przechowuje między wywołaniami, jest Twoim zmartwieniem.
oloproof.yaml wskazuje tę funkcję i ewaluatory:
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_matchWyniki są cache'owane według kodu źródłowego funkcji, zadeklarowanej version i config. Jeśli funkcja czyta inne pliki (prompt, tabelę reguł), wymień je w system.code_paths, aby ich edycja ponownie uruchamiała system.
Zbiór danych
Jeden przypadek na wiersz. input to dokładnie to, co Twoja funkcja otrzymuje jako case; expected to wzorzec, z którym porównuje ewaluator exact_match:
{"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"}}Funkcja zwraca dla każdego przypadku obiekt taki jak {"answer": "Use password reset, ...", "label": "account"}.
Wybór ewaluatorów
| Kryterium | Ewaluator | Wymaga expected | Co mierzy |
|---|---|---|---|
| exact_label | exact_match na label | tak | sukces zadania: etykieta jest właściwa |
| format_valid | json_schema na całym wyniku | nie | format: obiekt ma dokładnie dwa pola tekstowe |
| pii_free | regex na answer, pass_if: no_match | nie | własność bezpieczeństwa tekstu |
Sprawdzenie formatu przepuszcza poprawnie sformułowaną błędną odpowiedź, więc nigdy nie może zastąpić sukcesu zadania. Sprawdzenie zadania potrzebuje wzorca dla każdego przypadku; gdy przypadek go nie ma, exact_match nie może go ocenić. Ewaluatory deterministyczne nie wymagają walidacji względem ludzi: dwukrotne uruchomienie daje ten sam werdykt.
Polityka
release.yaml to to, względem czego rozstrzyga się przebieg:
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 mówi, że etykieta musi być poprawna w co najmniej 70% przypadków, i przechodzi tylko wtedy, gdy cały przedział 95% jest na poziomie 0.70 lub wyżej. Dwie reguły observed_count nie dopuszczają żadnego niepowodzenia na przypadkach, które uruchomiłeś; opisują te przypadki, a nie każde pytanie, jakie zadadzą użytkownicy.
Uruchom
oloproof runPrawdziwy wynik, skrócony:
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 missJak go czytać:
- 13 z 18 etykiet jest poprawnych, 72,2%. To powyżej 0.70, ale przedział sięga w dół do 46,5%: 18 przypadków nie może wykazać, że prawdziwy odsetek wynosi co najmniej 0.70. Dlatego reguła ma stan INSUFFICIENT_EVIDENCE, a nie PASS i nie FAIL.
- Każdy wynik ma właściwy kształt i żaden nie zawiera liczby przypominającej SSN, więc obie reguły formatu przechodzą.
- block_on wymienia INSUFFICIENT_EVIDENCE, więc bramka blokuje, a polecenie kończy się kodem 3. Kod 0 oznaczałby, że nie wystąpiło nic, na czym polityka blokuje; Bramkowanie CI wymienia wszystkie kody.
Uruchom ponownie, a wiersz cache pokaże execution 18 hit/0 miss: nic się nie zmieniło, więc funkcja nie jest wywoływana.
Przejrzyj niepowodzenia
oloproof inspect RUN_ID --failures
oloproof inspect RUN_ID --case refund_04RUN_ID to identyfikator z pierwszego wiersza wyniku przebiegu.
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: passedWzorzec jest oczywisty, gdy przeczytasz wejścia: "money back", "reverse the payment", "sign in" i "two-factor" nie ma na listach słów kluczowych, a other_04 mówi "I don't want a refund", co słowo "refund" i tak dopasowuje. Zauważ, że refund_04 przechodzi oba sprawdzenia formatu, będąc błędnym: to jest luka między sprawdzaniem formatu a mierzeniem sukcesu.
Sensowne są tu dwa kolejne działania. Napraw pomyłki (poniżej) albo dodaj przypadki: przy większej liczbie przypadków i tej samej trafności przedział się zwęża, a oloproof plan RUN_ID --run szacuje, ile ich trzeba.
Wprowadź prawdziwą zmianę
Skopiuj ../support-change/app.py na app.py. Dodaje on pominięte sformułowania:
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):
...i ustaw version: keywords-v2 pod system w oloproof.yaml, aby przebieg został zapisany jako nowa wersja. Następnie:
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 z 18 jest poprawnych, a dolna granica przedziału, 72,7%, przekracza 0.70, więc reguła przechodzi, a polecenie kończy się kodem 0. other_04 nadal zawodzi: poprawka nie dotknęła negacji.
Porównaj kandydata z punktem odniesienia
Reguła przebiegu pyta, czy kandydat spełnia Twój próg. Porównanie pyta, czym różni się od punktu odniesienia, przypadek po przypadku. Skopiuj ../support-change/compare.yaml do projektu; zawiera jedną regułę porównania:
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)Kandydat naprawił cztery przypadki i nie zepsuł żadnego, co daje szacowany zysk 22 punktów. Ale zmieniły się tylko cztery przypadki, a 18 sparowanych przypadków zostawia przedział od 12,9 punktu gorzej do 57 punktów lepiej, który przecina margines 10 punktów. Porównanie nie może jeszcze wykluczyć, że kandydat jest gorszy o więcej, niż akceptujesz, więc wynik to INSUFFICIENT_EVIDENCE i kod wyjścia 3. Wiersz pod nim to oszacowanie wielkości próby. Bez --policy polecenie compare wypisuje różnice, informuje, że release.yaml projektu nie deklaruje żadnej reguły porównania, i kończy się kodem 0, ponieważ niczego nie rozstrzygnięto.
Porównywanie kandydata z punktem odniesienia objaśnia margines i inne rodzaje reguł.
Rozwiązywanie problemów
| Objaw | Przyczyna i rozwiązanie |
|---|---|
| ModuleNotFoundError dla app | Uruchamiaj z katalogu zawierającego app.py albo podaj w callable ścieżkę modułu importowalną stamtąd. |
| Reguła nazywa metrykę, której nie produkuje żaden ewaluator | metric reguły musi być równe criterion ewaluatora; błąd wymienia istniejące metryki. |
| Edytowałeś klasyfikator, a przebieg ponownie użył każdego wyniku | Cache podąża za kodem źródłowym funkcji; plik pomocniczy, który czyta, musi być wymieniony w system.code_paths. |
| exact_label zgłasza przypadki jako brakujące | Te wykonania zgłosiły wyjątek lub przekroczyły limit czasu; oloproof inspect RUN_ID --failures pokazuje każdy błąd. |
| Przebieg kończy się kodem 3 przy wysokiej estymacie | Decyduje przedział, a nie estymata. Dodaj przypadki albo przyjmij niższy próg, ustalony przed przebiegiem. |
Ograniczenia
- SDK i YAML raportują odsetki zaliczeń dla każdego ewaluatora. Nie ma macierzy pomyłek ani precyzji i czułości dla każdej klasy dla klasyfikatora takiego jak ten; blok predictive: robi to dla modelu zwracającego wyniki liczbowe (Modele predykcyjne).
- Reguły observed_count opisują uruchomione przypadki; nie twierdzą niczego o niewidzianych wejściach.
- Porównanie na 18 przypadkach rozstrzyga tylko duże różnice. Pięćdziesiąt lub więcej prawdziwych przypadków to bardziej użyteczne minimum.
- oloproof.yaml nie może wskazać własnego @evaluator; to wymaga SDK (SDK).