Kiedy ostatnio pisałem o benchmarku (kod, który zabija przeglądarkę), przemilczałem nudną część. Szesnaście modeli, po sześć prób, jedno zadanie – to jest 96 plików HTML, które ktoś musi otworzyć, obejrzeć, poklikać i wystawić ocenę w pięciu kryteriach. Tym ktosiem byłem ja. Z kawą, w kilka wieczorów. Dziś tych prób jest w benchmarku 381.
No i w pewnym momencie odpaliłem czwarte zadanie i zrobiło się tego tyle, że trza było coś z tym zrobić.
Dołożyłem więc sędziego. A właściwie dwóch.
Sędzia nie dostaje pytania, tylko teczkę z dowodami
Najprostsza wersja „LLM as a judge” wygląda tak: wklejasz model kod, pytasz „czy to dobre, oceń w skali 1-10”, dostajesz siódemkę. Zawsze siódemkę… :P
To nie działa, bo zadanie polega na wygenerowaniu działającej strony, a nie ładnego kodu. Kod może wyglądać wzorowo i wywalać się w konsoli w pierwszej sekundzie. Może być brzydki jak noc i śmigać.
Więc zamiast pytać, buduję folder z dowodami. Pod spodem siedzi Playwright, który odpala artefakt headlessowo i zbiera:
shot-1.png,shot-2.png,shot-3.png– ten sam kawałek ekranu (1280×800) po 1, 6 i 12 sekundach od załadowania,shot-full.png– cała strona od góry do dołu, robiona po przescrollowaniu jej w dół,interact-1.png…interact-3.png– strona po jednej interakcji każdy, każdy zrzut na świeżo załadowanej stronie,console-errors.json– wszystko, co poleciało jakopageerroriconsole.error,interactions.json– opis słowami, co dokładnie zrobił każdy krok interakcji,- no i sam
artifact.html, do przeczytania.
Sędzia dostaje ten folder na prawach read only i ma ocenić wyłącznie to, co w nim widzi. Żadnego „wydaje mi się, że taki model raczej dobrze sobie radzi”.
Wcześniejsza wersja robiła dwa zrzuty i to było za mało. Trzy rzeczy się przez to przeciskały:
Zamrożona pierwsza klatka. Symulacja, która renderuje pierwszą klatkę i umiera, na jednym screenie wygląda identycznie jak działająca. Dopiero rozstrzał 1s / 6s / 12s pokazuje, czy tam się cokolwiek rusza. Odwrotność też jest ważna: przy stronie, która ma być statyczna, trzy identyczne zrzuty to nie jest defekt – i sędzia ma to w promptcie napisane wprost, żeby nie karał strony za to, że nie miga.
Wszystko poniżej zgięcia. Model robi ładny hero i zapomina o reszcie, albo odwrotnie: pierwszy ekran blady, a niżej jest cała robota. Ocena wyglądu na podstawie jednego viewportu to loteria, więc kryterium look sędzia ocenia z shot-full.png.
Przyciski, które nic nie robią. To był najgorszy przypadek. Strona się ładuje, konsola czysta, wygląda skończenie, klikasz – i nic. Z samego zrzutu tego nie widać.
Klikanie na ślepo jest trudniejsze, niż wygląda
Krok z interakcjami zajął mi najwięcej czasu, bo trzeba odpowiedzieć na pytanie: w co właściwie kliknąć, skoro nie wiadomo, co model wygenerował?
Domyślny scenariusz klika trzy pierwsze kontrolki, które przechodzą test klikalności. Z dwoma zastrzeżeniami:
- Prawdziwe kontrolki przed kotwicami.
<button>,[role=button],submit,summaryidą pierwsze, a linkihref="#"dopiero potem. Kotwica tylko scrolluje, więc gdyby wszystkie trzy sloty zjadła nawigacja, nie udowodniłbym niczego. - Kolejność z DOM-u. Nie losowo, nie „najbardziej wyglądający na główny” – po prostu kolejność w dokumencie. Dzięki temu ta sama strona zawsze daje ten sam wybór i wynik da się powtórzyć.
Samo „widoczny i włączony” nie wystarczy. Przycisk w zamkniętym modalu spełnia oba warunki, siedząc pod nakładką – kliknięcie w niego kończy się timeoutem. Playwright ma na to click({ trial: true }): robi pełny test klikalności (widoczny, stabilny, przyjmuje zdarzenia myszy) bez odpalania zdarzenia. Czyli dokładnie ta sama poprzeczka, którą musi przeskoczyć prawdziwe kliknięcie.
Dwa zadania mają własne scenariusze, bo klikanie nic by w nich nie wykazało: snake (gra na klawiaturę – trzeba wysłać strzałki) i todo-app (pusta lista – najpierw trzeba coś wpisać i dodać). A interactions.json zapisuje też kroki, które nic nie znalazły – „strona nie ma działającego elementu interaktywnego nr 2” to też dowód, i to mocny.

Dwóch sędziów, na ślepo, z obowiązkiem uzasadnienia
Sędziami są Claude Code i Codex, każdy odpalany jako CLI w trybie tylko do odczytu. Kilka reguł, które uważam za ważniejsze od wyboru modelu:
- Oceniają te same kryteria co ja (zgodność z wymaganiami, działa od razu, wygląd, jakość kodu) plus dwa dodatkowe, których ręcznie nie ciągnę: struktura kodu i poprawność logiki.
- Na ślepo. Sędzia nie widzi ani moich ocen, ani ocen drugiego sędziego.
- Wartości są przypinane do skali kryterium. Jak skala ma 0 / 0.5 / 1, to nie ma „0.7, bo tak czuję”.
- Uzasadnienie jest obowiązkowe przy każdej ocenie 0 albo 0.5. Ma być konkretny defekt, nie „mogłoby być lepiej”. Przy najwyższej ocenie uzasadnienie jest opcjonalne – i to nie przypadek, bo interesuje mnie, dlaczego coś jest złe.
- Mediana z sędziów liczy się w widoku i nigdy nie ląduje w pliku. W danych siedzą surowe oceny, agregat to kwestia prezentacji.
- Rozjazd jest za duży. Jeśli człowiek i sędziowie różnią się o co najmniej 0.5 na wspólnym kryterium, w tabeli zapala się badge. To jest lista rzeczy do obejrzenia przeze mnie, nie alarm.
Osobno chodzi sędzia od powtarzalności: bierze wszystkie próby jednego modelu na jednym zadaniu, liczy wariancję ocen i podobieństwo kodu (Jaccard na tokenach), i dokłada do tego werdykt słowny. Potrzebuje minimum dwóch prób.
Efekt, którego się nie spodziewałem
Kiedy sędziowie przemielili cały zestaw, usiadłem i porównałem to z moimi ręcznymi ocenami. Materiał: 381 prób, 4 zadania, 16 modeli, dwóch sędziów – czyli 762 werdykty maszyn na 381 ocen człowieka. Porównuję tylko kryteria, które oceniamy oboje (logika agenta odpada, sędzia nie widzi przebiegu pracy agenta, tylko gotowy artefakt).
I tu jest ta nudna, ale ważna różnica, którą łatwo przegapić: inaczej wygląda to na poziomie modelu, a inaczej na poziomie pojedynczej próby.
Na poziomie modelu jest bardzo zgodnie. Uśredniam wszystkie próby danego modelu i porównuję ze średnią sędziów:
| Model | mój wynik | sędziowie | różnica |
|---|---|---|---|
| Anthropic Sonnet 4.6 | 91,0% | 87,6% | -3,5 p.p. |
| GLM 5 Turbo | 87,6% | 84,6% | -3,0 p.p. |
| Qwen 3.5 122B MoE | 86,7% | 70,5% | -16,2 p.p. |
| GPT 5.4 mini | 84,0% | 73,1% | -10,9 p.p. |
| … | |||
| Qwen 3.5 9B | 59,9% | 49,4% | -10,6 p.p. |
| GPT OSS 20B | 52,8% | 52,2% | -0,6 p.p. |
Średnia różnica na modelu to 9,8 punktu procentowego, największa 18,4. Korelacja rankingów (Spearman) wychodzi 0,90 – czołówka i dół stawki się zgadzają, przestawia się środek: Qwen 3.5 122B spada u sędziów z trzeciego miejsca na siódme, Gemma4 31B awansuje z szóstego na trzecie.
Na poziomie pojedynczej próby robi się dużo brudniej. Ta sama miara, tylko liczona per plik:
- średnia różnica: 15,8 p.p., mediana 11,5
- 38% prób mieści się w 10 p.p. – ale 19% prób rozjeżdża się o ponad 30 p.p.
- rekord: 86 p.p. na jednej próbie
Czyli uśrednienie po sześciu próbach zjada szum, który na pojedynczym pliku jest gigantyczny. Ranking modeli jest wspólny, ocena konkretnego artefaktu bywa zupełnie różna.
Dwie rzeczy, które wyszły przy okazji i które zmieniają mój obrazek:
Sędzia jest systematycznie surowszy ode mnie. W 232 z 381 prób (61%) daje niżej, średnio o 9,8 p.p. To nie jest szum, to przesunięcie. Ja oceniam „działa i wygląda ok”, sędzia dostaje folder z dowodami i wypisuje każdy błąd w konsoli.
Sędziowie różnią się od siebie mniej więcej tyle, co ode mnie. Claude i Codex rozjeżdżają się między sobą średnio o 10,2 p.p. (rekord 42,3). Więc to nie jest układ „człowiek kontra maszyny” – to trzech oceniających z podobnym rozrzutem.
A teraz najciekawsze. Rozbicie po zadaniach:
| Zadanie | średni rozjazd | przesunięcie |
|---|---|---|
| todo-app | 4,2 p.p. | +2,5 |
| snake | 15,6 p.p. | -0,9 |
| symulacja rozrostu sieci neuronowej | 20,3 p.p. | -19,0 |
| strona wydarzenia (długa specyfikacja) | 23,4 p.p. | -22,2 |
To jest odpowiedź na moje własne pytanie. Miałem trzy hipotezy, dlaczego wychodzi tak zgodnie:
- Na małych, dobrze opisanych zadaniach „czy to jest dobre” nie jest na tyle wieloznaczne, żeby się różnić.
- Po kilkunastu latach pewne rzeczy widzi się z automatu – i wtedy oceniam podobnie do tego, jak te modele nauczono oceniać, więc to nie są trzy niezależne opinie, tylko trzy wersje tej samej szkoły.
- Testuję łatwy koniec.
Dane mówią wyraźnie: głównie pierwsza i trzecia. Todo app – rozjazd 4 punkty, zgodność prawie idealna. Strona z długą specyfikacją – rozjazd sześć razy większy i sędzia konsekwentnie 22 punkty niżej ode mnie. Im więcej rzeczy jest do sprawdzenia, tym bardziej się rozjeżdżamy. Zgodność, która mnie zaskoczyła, była w dużej mierze zgodnością na łatwych zadaniach.
Rozjazd siedzi też w konkretnych kryteriach: najczęściej wygląd (125 przypadków) i zgodność z wymaganiami (89), rzadziej „działa od razu” (44). Czyli kłócimy się o rzeczy z natury ocenne i o odhaczanie długiej listy wymagań, a nie o to, czy strona wystartowała.
Model wizyjny umie ocenić wygląd. I to jest dokładnie ten moment, w którym trzeba uważać
Rzecz, która mnie w tym wszystkim najbardziej zastanawia: modele wizyjne naprawdę potrafią to oceniać. Dostają zrzut całej strony i wyłapują, że nagłówek nachodzi na tekst, że kontrast jest do bani, że układ się rozjeżdża przy trzeciej sekcji. W czasach GPT-3.5 to by bylo nie do pomyślenia. Dziś sędzia potrafi mi znaleźć defekt wizualny, który sam bym przegapił o 23:40 przy siedemdziesiątym piątym pliku.
I tu jest haczyk.
Bo można spokojnie zbudować pętlę, w której model pisze stronę, drugi model ją ocenia, trzeci poprawia według uwag – i człowiek nie zagląda tam ani razu. Technicznie to działa. Kusi, bo jest szybkie i skalowalne.
Tylko że kryterium „wygląda dobrze” nie jest własnością pliku HTML. To jest zdanie o tym, jak coś działa na człowieku. Czytelne dla kogo? Wygodne dla kogo? Sędzia-model jest dobrym przybliżeniem tego zdania, dopóki jest zakotwiczony w ocenie ludzi – w danych, na których się uczył, i w tym, że ktoś regularnie sprawdza, czy dalej się z nim zgadza.
Wypnij tę kotwicę i po kilku obrotach pętli zostaje ci system, który optymalizuje coś, co kiedyś było gustem człowieka, a teraz jest własną, zdryfowaną wersją. Rzeczy zaczynają powstawać dla oceniającego modelu, nie dla użytkownika. I to jest moment, w którym robi się przejebane – nie dlatego, że AI się zbuntuje, tylko dlatego, że nikt nie zauważy, kiedy miara przestała mierzyć to, co miała.
Dlatego u mnie sędzia jest doradcą, a nie sędzią ostatecznym – mimo nazwy. Bierze na siebie mielenie 381 prób i mówi mi, gdzie patrzeć – a jak widać wyżej, czasem łapie mnie na tym, że sam oceniałem po łebkach. Ranking i tak podpisuję ja.
Wszystkie wyniki są tutaj: benchmark.refio.dev. Jak robisz własny harness do oceniania – napisz, czy u ciebie też widać ten wzorzec: zgodność na małych zadaniach i rozjazd rosnący razem z długością specyfikacji. Ciekawi mnie, gdzie u innych leży ta granica.
Tyle na dziś :P
0 komentarzy