Handleidingen
Tutorial: afbeeldingen, met controles en een judge die ze ziet
Evalueer een functie die afbeeldingen maakt, hier een thumbnailmaker voor producten die ook een bijschrift schrijft. Controleer de thumbnails met metingen die geen model nodig hebben, beoordeel de bijschriften met een model dat elke thumbnail ziet, meet die judge tegen een mens en lever daarna een echte wijziging op. Elke stap draait op deze machine zonder model en zonder netwerk, en een optionele stap zet er een echt visiemodel voor in de plaats.
Wat je gaat bouwen
Een catalogusdienst ontvangt een productfoto, slaat een thumbnail op en schrijft een bijschrift van één regel dat de kleur van het product noemt. Drie vragen beslissen of een versie mag worden uitgebracht:
- Past elke thumbnail in de catalogus: een webformaat, hoogstens 256 bij 256 pixels, hoogstens 50 kB? Dat is een meting, gedaan door media_check aan de bytes van de thumbnail.
- Is elke thumbnail nog de foto van het product, en geen lege afbeelding, een uitsnede of een ander product? image_matches_reference vergelijkt hem met de foto op perceptuele hash.
- Noemt elk bijschrift de kleur die een klant ziet? Daar zijn ogen voor nodig, dus een rubric-judge krijgt de thumbnail te zien.
De foto's en thumbnails blijven in de projectmap. Het systeem legt ze bij referentie vast (media/v1): waar elk bestand staat, zijn type, grootte en sha256. Termen als case, run, judge en gate worden gedefinieerd in Kernbegrippen; alles over media staat in Afbeeldingen, audio en video.
Vereisten
- Python 3.11 of nieuwer, en Oloproof met zijn media-extra, dat Pillow toevoegt om afbeeldingen te vergelijken:
python3 -m venv .venv
. .venv/bin/activate
pip install 'oloproof[media]'- Het voorbeeldproject, dat met het pakket meekomt. Kopieer het naar een nieuwe map en werk daar:
oloproof init --example media product-thumbnails
cd product-thumbnails- Poort 8798 vrij voor de vervangende judge (pas hem anders op beide plaatsen aan).
Elke stap tot "Optioneel: een echt visiemodel" is offline en deterministisch: geen API-sleutel, geen provideraccount, geen kosten.
De bestanden
product-thumbnails/
app.py the thumbnailer under test (baseline)
app_v2.py the candidate change
judge_server.py a stand-in vision judge speaking the OpenAI API on 127.0.0.1
rubrics/caption_names_colour.md the judge's rubric
oloproof.yaml the suite
release.yaml rules for a run
compare.yaml a rule for a comparison
data/products.jsonl 20 cases
photos/ the 20 product photos the cases name
make_photos.py how the photos were drawn
labels/reviewer_verdicts.csv one person's verdicts on the baseline's captions
fill_labels.py copies those verdicts into a labelling sheetVoer elk commando uit vanuit product-thumbnails/.
De applicatie legt haar afbeeldingen vast
# app.py (excerpt)
@system(name="thumbnailer", version=VERSION, records=("media/v1",))
def describe(case: dict[str, Any]) -> dict[str, str]:
photo_path = HERE / case["photo"]
photo = Image.open(photo_path).convert("RGB")
thumb = photo.copy()
thumb.thumbnail((320, 320))
out = HERE / "thumbs" / VERSION / photo_path.name
out.parent.mkdir(parents=True, exist_ok=True)
thumb.save(out)
given = media_file(photo_path, role="input")
made = media_file(out, width=thumb.width, height=thumb.height)
current_case().media([given, made])
centre = photo.getpixel((photo.width // 2, photo.height // 2))
return {"caption": f"A {colour_name(centre)} product."}media_file(path) beschrijft een bestand: zijn file://-locatie, MIME-type, grootte en sha256, genoemd naar het bestand. role="input" markeert de foto die het systeem kreeg; de thumbnail is een output, de standaard. current_case().media(...) legt beide vast als één media/v1-artefact, en records: [media/v1] in oloproof.yaml declareert dat het systeem dat vastlegt. Er wordt niets gekopieerd: een controle of een judge leest elk bestand later terug en weigert er een waarvan de bytes niet meer met het record overeenkomen.
Deze baseline heeft twee fouten. Zijn thumbnails zijn 320 pixels breed, en zijn bijschrift noemt de kleur van de middelste pixel van de foto, en dat is de achtergrond zodra het product aan een kant staat. Elke versie schrijft haar thumbnails onder thumbs/<version>/, dus een latere run overschrijft nooit de bestanden die een eerdere run vastlegde.
De dataset
{"id": "p03", "input": {"photo": "photos/p03.png"}, "expected": {"photo": "photos/p03.png", "colour": "yellow", "shape": "triangle"}}input is wat het systeem ontvangt. expected.photo is de referentie waarmee de thumbnail wordt vergeleken; colour en shape staan er voor een mens die de case leest, en geen evaluator leest ze.
De evaluators kiezen
evaluators:
# The catalogue's limits, measured from the thumbnail's bytes: a web format, at most 256 by
# 256 pixels, at most 50 kB.
- type: media_check
criterion: thumbnail_fits
kind: image
formats: [png, jpeg, webp]
max_width: 256
max_height: 256
max_bytes: 50000
# The thumbnail is still the product's photo: a perceptual hash within 10 of 64 bits.
- type: image_matches_reference
criterion: same_photo
reference_field: photo
max_distance: 10
# The caption, judged by a model that sees the thumbnail.
- type: rubric_judge
criterion: caption_names_colour
provider: openai_compatible
model: stand-in-vision
base_url: http://127.0.0.1:8798/v1
rubric_file: rubrics/caption_names_colour.md
media: outputmedia_check meet elke uitvoerafbeelding: het formaat uit de eerste bytes, de afmetingen uit de header, nooit uit wat het systeem declareerde. image_matches_reference hasht de thumbnail en de foto die expected.photo noemt, en slaagt wanneer de hashes in hoogstens 10 van hun 64 bits verschillen; verkleinen of opnieuw comprimeren verschuift een hash een paar bits, een ander beeld verschuift hem er veel. Geen van beide roept een model aan, dus beide beslissen vanaf de eerste run.
media: output voegt de uitvoerafbeeldingen van de case bij het verzoek aan de judge, elk bij naam genoemd in zijn prompt. Een judge beslist niets totdat labels van mensen laten zien dat hij het met hen eens is, dus de derde regel wacht daarop.
De vervangende judge
Een rubric-judge stuurt zijn prompt en de bijgevoegde afbeeldingen naar een model en leest {"pass": true|false, "rationale": "..."} terug. Hier is het model judge_server.py, dat dezelfde API spreekt op 127.0.0.1 en geen model is: hij decodeert de bijgevoegde thumbnail, zoekt de meest voorkomende kleur buiten het wit en grijs van de achtergrond, en laat een bijschrift slagen dat die kleur noemt. Start hem in een tweede terminal en laat hem draaien:
python judge_server.py --port 8798De policy
version: 1
confidence_level: 0.95
block_on: [FAIL, INSUFFICIENT_EVIDENCE, MANUAL_REVIEW]
require_validated_evaluators: true
rules:
- id: thumbnails-fit
metric: thumbnail_fits
kind: observed_count
max_failures: 0
- id: same-photo
metric: same_photo
kind: observed_count
max_failures: 0
- id: caption-floor
metric: caption_names_colour
min: 0.80Elke thumbnail moet passen en moet zijn foto zijn. Minstens 80% van de bijschriften moet de juiste kleur noemen, alleen beslist wanneer de ondergrens van het interval boven 80% uitkomt.
Draaien
oloproof runGate: BLOCK (exit 1)
│ thumbnails-fit │ thumbnail_fits │ FAIL │ observed_failures_exceed_limit │
│ same-photo │ same_photo │ PASS │ observed_failures_within_limit │
│ caption-floor │ caption_names_colour │ INSUFFICIENT_EVIDENCE │ evaluator_not_validated │
│ thumbnail_fits │ 0.0% │ [0.0%, 16.9%] │ 0 / 20 observed · 0 missing · 0 excluded │
│ same_photo │ 100.0% │ [83.1%, 100.0%] │ 20 / 20 observed · 0 missing · 0 excluded │
│ caption_names_colour │ 60.0% │ [36.0%, 80.9%] │ 12 / 20 observed · 0 missing · 0 excluded │Geen enkele thumbnail past, elke thumbnail is zijn foto, en 12 van de 20 bijschriften noemen de juiste kleur. De regel van de judge is onbeslist omdat niemand de judge al heeft gemeten.
De mislukkingen bekijken
oloproof inspect RUN_ID --failuresp03
output: {"caption": "A white product."}
thumbnail_fits: failed
measured: p03.png: width 320 px is above 256 px
caption_names_colour: failed
judge text, not verified: the product is yellow; the caption says 'a white product.'De reden van een controle is een meting, gemarkeerd als measured; die van een judge zijn zijn eigen woorden, die niemand heeft gecontroleerd. De foto's en thumbnails verschijnen ook naast elk oordeel op de pagina van de case in de workbench, na oloproof export RUN_ID, gelezen uit deze map; een gehoste werkruimte laat je browser ze ophalen bij oloproof collect en bewaart ze nooit (Afbeeldingen, audio en video).
De judge tegen een mens meten
Trek een willekeurige steekproef van de cases van de run in een sheet, zonder de oordelen van de judge, laat een mens elke thumbnail en elk bijschrift bekijken, en importeer wat die zei. Hier speelt fill_labels.py die persoon en kopieert zijn oordelen uit labels/reviewer_verdicts.csv:
oloproof labels export RUN_ID --criterion caption_names_colour --sample 20 --local --out sample.csv
python fill_labels.py sample.csv
oloproof labels import sample.csvMeet daarna de judge tegen die labels, en leg vast wie dat deed:
oloproof evaluators list
oloproof evaluators validate EVALUATOR_ID --by alicecaption_names_colour: sha256:53605dd6... is now VALIDATED
agreement 100.0% [83.1%, 100.0%] · 20 of 20 labelled cases agreed · 0 labelled but not judged · kappa 1.000Een judge die afbeeldingen ziet, wordt precies zo gemeten als een judge die tekst leest. In een gehoste werkruimte toont de reviewwachtrij de afbeeldingen van elke case aan de reviewer, opgehaald door diens browser bij je collector (Judges).
oloproof gate RUN_ID --policy release.yamlthumbnails-fit: FAIL (observed_failures_exceed_limit)
same-photo: PASS (observed_failures_within_limit)
caption-floor: INSUFFICIENT_EVIDENCE (interval_overlaps_threshold)
Gate: BLOCK (exit 1)De judge mag nu beslissen, en 60% is niet genoeg.
Een echte wijziging maken
app_v2.py maakt thumbnails van 256 pixels en noemt de meest voorkomende kleur van de foto buiten de achtergrond, waar het product ook staat. Kopieer het over app.py, zet version: main-colour onder system in oloproof.yaml, laat de judge draaien, en:
oloproof runGate: ALLOW (exit 0)
│ thumbnails-fit │ thumbnail_fits │ PASS │ observed_failures_within_limit │
│ same-photo │ same_photo │ PASS │ observed_failures_within_limit │
│ caption-floor │ caption_names_colour │ PASS │ lower_bound_meets_minimum │De judge is dezelfde evaluatorversie die gevalideerd werd, dus zijn oordelen tellen.
De kandidaat met de baseline vergelijken
oloproof compare CANDIDATE_RUN_ID BASELINE_RUN_ID --policy compare.yamlthumbnail_fits: +100.0 points [+52.9, +100.0] · 20 paired · 0 missing · 0 excluded
caption_names_colour: +40.0 points [+1.8, +72.7] · 20 paired · 0 missing · 0 excluded
captions-better caption_names_colour superiority PASS difference_above_zeroOptioneel: een echt visiemodel
Deze stap verlaat het offline pad. Hij heeft een model nodig dat afbeeldingen leest: lokaal Ollama met een visiemodel (ollama pull qwen2.5vl), geen sleutel en geen kosten; in de cloud provider: anthropic of openai met api_key_env, gefactureerd per afbeelding en per woord.
# live_judge.yaml
type: rubric_judge
criterion: caption_names_colour
provider: openai_compatible
model: qwen2.5vl
base_url: http://localhost:11434/v1
rubric_file: rubrics/caption_names_colour.md
media: output
timeout_s: 300oloproof evaluators try live_judge.yamlDat beoordeelt de cases die je reviewer labelde met het echte model en meldt de overeenstemming, zonder het over te nemen. Om het over te nemen, zet je het in oloproof.yaml in plaats van de vervanger: een nieuw model is een nieuwe evaluatorversie, dus valideer hem tegen de labels, zoals hierboven.
Problemen oplossen
| Symptoom | Oorzaak en oplossing |
|---|---|
| caption_names_colour helemaal ontbrekend | De judgeserver draait niet of niet op base_url; oloproof inspect RUN_ID --failures toont waarom. |
| image_matches_reference stopt de run en noemt Pillow | Installeer de media-extra: pip install 'oloproof[media]'. |
| Een case is ontbrekend met media_unavailable | Een vastgelegd bestand is na de run verplaatst of verwijderd. Bewaar de bestanden die een run vastlegde zolang je de run bewaart. |
| Een run stopt met is not the recorded content | Een vastgelegd bestand is na de run gewijzigd. Een controle of judge leest nooit andere bytes als de vastgelegde; schrijf de bestanden van elke versie naar een eigen plek. |
| evaluator_not_validated nadat je valideerde | Je wijzigde de judge (model, endpoint, poort, rubric, media) en maakte een nieuwe versie. Valideer die. |
Beperkingen
- De vervangende judge telt pixels. Hij demonstreert de werkwijze, niet de kwaliteit van beoordelen.
- De perceptuele hash leest compositie en licht, geen kleur: hij onderscheidt een thumbnail van de foto van een lege afbeelding of een andere scène, niet het ene product van het andere uit dezelfde studio. Vlakke graphics met grote lege vlakken verschuiven hem meer dan foto's.
- Twintig labels geven een breed overeenstemmingsinterval. Label er meer, willekeurig en blind, voor een judge waarop je vertrouwt.
- Audio en video werken op dezelfde manier met andere instellingen: zie Afbeeldingen, audio en video.