Handleidingen
Tutorial: een classifier of gestructureerde uitvoer
Evalueer een Python-functie die supportvragen labelt, lees waarom de release geblokkeerd is, los de missers op en vergelijk de oplossing met het origineel, allemaal op je eigen machine zonder account, zonder netwerk en zonder model.
Wat je gaat bouwen
Een supportbot die een JSON-object teruggeeft met een answer en een label (refund, account of other). Je houdt hem aan drie eisen: het label is vaak genoeg juist, de uitvoer heeft altijd de juiste vorm, en geen enkel antwoord lekt iets dat op een Amerikaans burgerservicenummer (SSN) lijkt. Twee daarvan zijn formaatcontroles die geen referentieantwoord nodig hebben; één meet taaksucces tegen een referentielabel. Het verschil doet ertoe, en deze pagina houdt ze gescheiden.
De termen hieronder (case, run, metriek, interval, regel, gate) worden gedefinieerd in Kernbegrippen.
Vereisten
- Python 3.11 of nieuwer.
- Oloproof, geïnstalleerd in een virtuele omgeving:
python3 -m venv .venv
. .venv/bin/activate
pip install oloproof- Het voorbeeldproject en de kandidaatwijziging, die met het pakket meekomen. Kopieer beide naar nieuwe mappen en werk in de eerste; elk bestand staat ook hieronder, zodat je ze ook kunt overtypen:
oloproof init --example support_bot support-classifier
oloproof init --example classification support-change
cd support-classifierOp deze pagina wordt nergens een API-sleutel, provideraccount of netwerktoegang gebruikt.
De bestanden
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 hereVoer elk commando uit vanuit de map support-classifier/. Oloproof bewaart zijn store daar in .oloproof/; verwijder die map om weer bij nul te beginnen.
De applicatie en haar adapter
Je applicatie wordt bereikt via een adapter. Voor een Python-applicatie is de adapter de functie zelf: Oloproof importeert haar, roept haar één keer per case aan met de input van de case, en legt de dictionary die ze teruggeeft vast als uitvoer van die case.
# 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"}Om je eigen classifier te evalueren laat je zijn code staan waar die staat en schrijf je een dunne functie zoals deze die hem aanroept en een dictionary teruggeeft. De functie mag async zijn. Oloproof roept haar aan; het host, sandboxt of reset je applicatie niet, dus elke toestand die je applicatie tussen aanroepen bewaart beheer je zelf.
oloproof.yaml noemt die functie en de evaluators:
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_matchUitvoer wordt gecachet op de broncode van de functie, de gedeclareerde version en config. Als de functie andere bestanden leest (een prompt, een regeltabel), zet die dan onder system.code_paths, zodat het systeem opnieuw draait wanneer je ze bewerkt.
De dataset
Eén case per regel. input is precies wat je functie als case ontvangt; expected is de referentie waarmee de evaluator exact_match vergelijkt:
{"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"}}De functie geeft voor elke case een object terug zoals {"answer": "Use password reset, ...", "label": "account"}.
De evaluators kiezen
| Criterium | Evaluator | Heeft expected nodig | Wat het meet |
|---|---|---|---|
| exact_label | exact_match op label | ja | taaksucces: het label is het juiste |
| format_valid | json_schema over de hele uitvoer | nee | formaat: het object heeft precies de twee tekstvelden |
| pii_free | regex op answer, pass_if: no_match | nee | een veiligheidseigenschap van de tekst |
Een formaatcontrole laat een goedgevormd fout antwoord door, dus ze kan nooit in de plaats komen van taaksucces. Een taakcontrole heeft voor elke case een referentie nodig; waar een case er geen heeft, kan exact_match haar niet scoren. Deterministische evaluators hoeven niet tegen mensen gevalideerd te worden: twee keer draaien geeft hetzelfde oordeel.
De policy
release.yaml is waartegen de run wordt beslist:
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 zegt dat het label in minstens 70% van de gevallen juist moet zijn, en slaagt alleen als het hele 95%-interval op of boven 0.70 ligt. De twee observed_count-regels staan geen enkele mislukking toe op de cases die je draaide; ze beschrijven deze cases, niet elke vraag die gebruikers zullen stellen.
Draaien
oloproof runEchte uitvoer, ingekort:
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 missZo lees je het:
- 13 van de 18 labels zijn juist, 72.2%. Dat is boven 0.70, maar het interval reikt tot 46.5%: 18 cases kunnen niet aantonen dat het werkelijke percentage minstens 0.70 is. Dus de regel is INSUFFICIENT_EVIDENCE, niet PASS en niet FAIL.
- Elke uitvoer heeft de juiste vorm en geen bevat een SSN-achtig nummer, dus beide formaatregels slagen.
- block_on noemt INSUFFICIENT_EVIDENCE, dus de gate blokkeert en het commando eindigt met 3. Exit 0 zou betekenen dat er niets is waarop de policy blokkeert; CI gaten noemt elke code.
Draai het opnieuw en de cacheregel luidt execution 18 hit/0 miss: er is niets veranderd, dus de functie wordt niet aangeroepen.
De mislukkingen bekijken
oloproof inspect RUN_ID --failures
oloproof inspect RUN_ID --case refund_04RUN_ID is het id op de eerste regel van de uitvoer van de run.
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: passedHet patroon is duidelijk zodra je de inputs leest: "money back", "reverse the payment", "sign in" en "two-factor" staan niet in de trefwoordlijsten, en other_04 zegt "I don't want a refund", waar het woord "refund" toch op past. Merk op dat refund_04 beide formaatcontroles haalt terwijl het fout is: dat is het gat tussen formaat controleren en succes meten.
Twee vervolgstappen zijn hier zinvol. Los de missers op (hieronder), of voeg cases toe: met meer cases bij dezelfde nauwkeurigheid wordt het interval smaller, en oloproof plan RUN_ID --run schat hoeveel.
Een echte wijziging maken
Kopieer ../support-change/app.py over app.py. Het voegt de gemiste formuleringen toe:
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):
...en zet version: keywords-v2 onder system in oloproof.yaml, zodat de run als de nieuwe versie wordt vastgelegd. Daarna:
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 van de 18 zijn juist en de ondergrens van het interval, 72.7%, haalt 0.70, dus de regel slaagt en het commando eindigt met 0. other_04 mislukt nog steeds: de oplossing raakte ontkenning niet.
De kandidaat met de baseline vergelijken
De runregel vraagt of de kandidaat je ondergrens haalt. Een vergelijking vraagt hoe hij verschilt van de baseline, case voor case. Kopieer ../support-change/compare.yaml naar het project; het bevat één vergelijkingsregel:
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)De kandidaat repareerde vier cases en brak er geen, een geschatte winst van 22 punten. Maar slechts vier cases veranderden, en 18 gepaarde cases laten een interval over van 12.9 punten slechter tot 57 punten beter, dat de marge van 10 punten kruist. De vergelijking kan nog niet uitsluiten dat de kandidaat slechter is dan je accepteert, dus is ze INSUFFICIENT_EVIDENCE en eindigt met 3. De regel eronder is de schatting van de omvang. Zonder --policy print compare de verschillen, zegt dat de release.yaml van het project geen vergelijkingsregel declareert, en eindigt met 0 omdat er niets werd beslist.
Een kandidaat met een baseline vergelijken legt de marge en de andere soorten regels uit.
Problemen oplossen
| Symptoom | Oorzaak en oplossing |
|---|---|
| ModuleNotFoundError voor app | Draai vanuit de map met app.py, of geef callable een modulepad dat van daaruit importeerbaar is. |
| Een regel noemt een metriek die geen evaluator produceert | De metric van de regel moet gelijk zijn aan het criterion van een evaluator; de fout noemt de metrieken die er zijn. |
| Je bewerkte de classifier en de run hergebruikte elke uitvoer | De cache volgt de broncode van de callable; een hulpbestand dat hij leest moet onder system.code_paths staan. |
| exact_label meldt cases als ontbrekend | Die uitvoeringen gaven een fout of time-out; oloproof inspect RUN_ID --failures toont elke fout. |
| De run eindigt met 3 bij een hoge schatting | Het interval beslist, niet de schatting. Voeg cases toe of accepteer een lagere ondergrens, besloten vóór de run. |
Beperkingen
- De SDK en YAML rapporteren slagingspercentages per evaluator. Er is geen confusion matrix of precision en recall per klasse voor een classifier als deze; een predictive:-blok doet dat voor een model met scores (Classifiers en regressors).
- observed_count-regels beschrijven de cases die je draaide; ze doen geen uitspraak over ongeziene inputs.
- Een vergelijking over 18 cases onderscheidt alleen grote verschillen. Vijftig of meer echte cases zijn een bruikbaardere ondergrens.
- oloproof.yaml kan geen eigen @evaluator noemen; daarvoor is de SDK nodig (De Python-API).