Przewodniki
Samouczek: obrazy, ze sprawdzeniami i sędzią, który je widzi
Oceń funkcję, która tworzy obrazy, tutaj generator miniatur produktów, który pisze też podpis. Sprawdź miniatury pomiarami, które nie wymagają modelu, oceniaj podpisy modelem, który widzi każdą miniaturę, zmierz tego sędziego względem człowieka, a potem wdróż prawdziwą zmianę. Każdy krok działa na tej maszynie bez modelu i bez sieci, a opcjonalny krok podstawia prawdziwy model wizyjny.
Co zbudujesz
Usługa katalogowa przyjmuje zdjęcie produktu, zapisuje miniaturę i pisze jednowierszowy podpis podający kolor produktu. O tym, czy wersja może zostać wydana, decydują trzy pytania:
- Czy każda miniatura pasuje do katalogu: format webowy, najwyżej 256 na 256 pikseli, najwyżej 50 kB? To pomiar, wykonywany przez media_check na bajtach miniatury.
- Czy każda miniatura jest nadal zdjęciem produktu, a nie pustym obrazem, wycinkiem lub innym produktem? image_matches_reference porównuje ją ze zdjęciem za pomocą hasza percepcyjnego.
- Czy każdy podpis podaje kolor, który widzi kupujący? To wymaga oczu, więc sędziemu z rubryką pokazuje się miniaturę.
Zdjęcia i miniatury zostają w folderze projektu. System zapisuje je przez odwołanie (media/v1): gdzie jest każdy plik, jego typ, rozmiar i sha256. Terminy takie jak przypadek, przebieg, sędzia i bramka są zdefiniowane w Pojęciach; wszystko o multimediach jest w Obrazy, dźwięk i wideo.
Wymagania wstępne
- Python 3.11 lub nowszy oraz Oloproof z dodatkiem media, który dodaje Pillow do porównywania obrazów:
python3 -m venv .venv
. .venv/bin/activate
pip install 'oloproof[media]'- Przykładowy projekt, dostarczany z pakietem. Skopiuj go do nowego katalogu i pracuj tam:
oloproof init --example media product-thumbnails
cd product-thumbnails- Wolny port 8798 dla zastępczego sędziego (jeśli nie, zmień go w obu miejscach).
Każdy krok aż do "Opcjonalnie: prawdziwy model wizyjny" działa offline i deterministycznie: bez klucza API, bez konta u dostawcy, bez kosztów.
Pliki
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 sheetUruchamiaj każde polecenie z product-thumbnails/.
Aplikacja zapisuje swoje obrazy
# 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) opisuje plik: jego lokalizację file://, typ MIME, rozmiar i sha256, nazwany od nazwy pliku. role="input" oznacza zdjęcie, które system otrzymał; miniatura jest wynikiem, co jest wartością domyślną. current_case().media(...) zapisuje oba jako jeden artefakt media/v1, a records: [media/v1] w oloproof.yaml deklaruje, że system go zapisuje. Nic nie jest kopiowane: sprawdzenie lub sędzia czyta później każdy plik ponownie i odrzuca taki, którego bajty nie zgadzają się już z zapisem.
Ten punkt odniesienia ma dwie wady. Jego miniatury mają szerokość 320 pikseli, a jego podpis podaje kolor środkowego piksela zdjęcia, którym jest tło za każdym razem, gdy produkt stoi z boku. Każda wersja zapisuje swoje miniatury pod thumbs/<version>/, więc późniejszy przebieg nigdy nie nadpisuje plików zapisanych przez wcześniejszy przebieg.
Zbiór danych
{"id": "p03", "input": {"photo": "photos/p03.png"}, "expected": {"photo": "photos/p03.png", "colour": "yellow", "shape": "triangle"}}input to to, co otrzymuje system. expected.photo to wzorzec, z którym porównuje się miniaturę; colour i shape są tam dla osoby czytającej przypadek, a żaden ewaluator ich nie czyta.
Wybór ewaluatorów
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 mierzy każdy obraz wyjściowy: format z jego początkowych bajtów, rozmiar z jego nagłówka, nigdy na podstawie tego, co zadeklarował system. image_matches_reference haszuje miniaturę i zdjęcie wskazane przez expected.photo i zalicza, gdy hasze różnią się co najwyżej 10 z 64 bitów; przeskalowanie lub ponowna kompresja przesuwa hasz o kilka bitów, inny obraz przesuwa go o wiele. Żadne z nich nie wywołuje modelu, więc oba rozstrzygają od pierwszego przebiegu.
media: output dołącza obrazy wyjściowe przypadku do żądania sędziego, każdy nazwany w jego prompcie. Sędzia nie rozstrzyga niczego, dopóki etykiety nadane przez ludzi nie pokażą, że się z nimi zgadza, więc trzecia reguła na to czeka.
Zastępczy sędzia
Sędzia z rubryką wysyła swój prompt i dołączone obrazy do modelu i odczytuje {"pass": true|false, "rationale": "..."}. Tutaj modelem jest judge_server.py, który mówi tym samym API na 127.0.0.1 i nie jest modelem: dekoduje dołączoną miniaturę, znajduje jej najczęstszy kolor inny niż biel i szarość tła i przepuszcza podpis, który go podaje. Uruchom go w drugim terminalu i zostaw włączonego:
python judge_server.py --port 8798Polityka
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.80Każda miniatura musi pasować i musi być swoim zdjęciem. Co najmniej 80% podpisów musi podawać właściwy kolor, co jest rozstrzygane dopiero wtedy, gdy dolna granica przedziału przekroczy 80%.
Uruchom
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 │Żadna miniatura nie pasuje, każda miniatura jest swoim zdjęciem, a 12 z 20 podpisów podaje właściwy kolor. Reguła sędziego jest nierozstrzygnięta, ponieważ nikt jeszcze nie zmierzył sędziego.
Przejrzyj niepowodzenia
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.'Powodem sprawdzenia jest pomiar, oznaczony measured; powodem sędziego są jego własne słowa, których nikt nie sprawdził. Zdjęcia i miniatury pojawiają się też obok każdego werdyktu na stronie przypadku w workbenchu, po oloproof export RUN_ID, czytane z tego folderu; w hostowanym obszarze roboczym pobiera je Twoja przeglądarka z oloproof collect, a obszar roboczy nigdy ich nie przechowuje (Obrazy, dźwięk i wideo).
Zmierz sędziego względem człowieka
Wylosuj próbę przypadków przebiegu do arkusza, bez werdyktów sędziego, poproś osobę, by obejrzała każdą miniaturę i podpis, i zaimportuj to, co powiedziała. Tutaj fill_labels.py zastępuje tę osobę, kopiując jej werdykty z 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.csvNastępnie zmierz sędziego względem tych etykiet i zapisz, kto to zrobił:
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.000Sędzia, który widzi obrazy, jest mierzony dokładnie tak jak sędzia czytający tekst. W hostowanym obszarze roboczym kolejka przeglądu pokazuje osobie przeglądającej obrazy każdego przypadku, pobierane przez jej przeglądarkę z Twojego kolektora (Sędziowie).
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)Sędzia może teraz rozstrzygać, a 60% to za mało.
Wprowadź prawdziwą zmianę
app_v2.py tworzy miniatury o szerokości 256 pikseli i podaje najczęstszy kolor zdjęcia inny niż tło, gdziekolwiek jest produkt. Skopiuj go na miejsce app.py, ustaw version: main-colour pod system w oloproof.yaml, nie wyłączaj sędziego i:
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 │Sędzia to ta sama wersja ewaluatora, która została zwalidowana, więc jego werdykty się liczą.
Porównaj kandydata z punktem odniesienia
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_zeroOpcjonalnie: prawdziwy model wizyjny
Ten krok opuszcza ścieżkę offline. Wymaga modelu, który czyta obrazy: lokalnie Ollama z modelem wizyjnym (ollama pull qwen2.5vl), bez klucza i bez kosztów; w chmurze provider: anthropic lub openai z api_key_env, rozliczane za każdy obraz, tak jak za każde słowo.
# 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.yamlTo ocenia prawdziwym modelem przypadki, które oetykietował Twój recenzent, i podaje jego zgodność, bez przyjmowania go. Aby go przyjąć, umieść go w oloproof.yaml zamiast zastępczego sędziego: nowy model to nowa wersja ewaluatora, więc zwaliduj go względem etykiet, jak wyżej.
Rozwiązywanie problemów
| Objaw | Przyczyna i rozwiązanie |
|---|---|
| caption_names_colour w całości brakujące | Serwer sędziego nie działa albo nie jest pod base_url; oloproof inspect RUN_ID --failures pokazuje dlaczego. |
| image_matches_reference zatrzymuje przebieg, wskazując Pillow | Zainstaluj dodatek media: pip install 'oloproof[media]'. |
| Przypadek jest brakujący z media_unavailable | Zapisany plik został przeniesiony lub usunięty po przebiegu. Przechowuj pliki zapisane przez przebieg tak długo, jak przechowujesz przebieg. |
| Przebieg zatrzymuje się z is not the recorded content | Zapisany plik został zmieniony po przebiegu. Sprawdzenie ani sędzia nigdy nie czytają innych bajtów jako zapisanych; zapisuj pliki każdej wersji w osobnym miejscu. |
| evaluator_not_validated po walidacji | Zmieniłeś sędziego (model, punkt końcowy, port, rubrykę, media) i powstała nowa wersja. Zwaliduj tę. |
Ograniczenia
- Zastępczy sędzia liczy piksele. Pokazuje przebieg pracy, a nie jakość oceniania.
- Hasz percepcyjny odczytuje układ i światło, a nie kolor: odróżnia miniaturę zdjęcia od pustego obrazu lub innej sceny, ale nie jeden produkt od innego z tego samego studia. Płaskie grafiki z dużymi pustymi obszarami poruszają go bardziej niż fotografie.
- Dwadzieścia etykiet daje szeroki przedział zgodności. Dla sędziego, na którym polegasz, etykietuj więcej, losowo i na ślepo.
- Dźwięk i wideo działają tak samo, z innymi ustawieniami: zob. Obrazy, dźwięk i wideo.