Zamiast szablonu — wywiad z modelem. Spec wyszedł bez luk. Z progiem w złym miejscu
Model pyta o jedno zdanie zadania, spec składa się z odpowiedzi. Z moimi: kod domknięty w 8/8. Z odpowiedziami modelu: też, z progiem w złym miejscu.
część 5 · laaj 1 2 3 4 5
Piąta część serii o LLM-as-a-Judge, czyli o tym, jak jeden model ocenia pracę innych. W części trzeciej dałem dwóm agentom AI zadanie opisane jednym zdaniem i dostałem kod pełen decyzji, których nikt nie podjął. W części czwartej opisałem to samo zadanie porządnie, według szablonu, i te decyzje zniknęły z kodu w ośmiu rozwiązaniach na osiem.
Tyle że te dwadzieścia kilka linijek szablonu pisałem dłużej, niż agenci pisali kod. I miałem ściągę: część trzecia wypunktowała mi wszystkie pułapki, więc wiedziałem, o co zapytać samego siebie. W prawdziwej kolejce zadań nikt takiej ściągi nie dostaje. Obiecałem więc sprawdzić, czy pytania może zadać model. Oto to sprawdzenie.
Teza
Dwie, bo druga wynika z pierwszej.
Pierwsza. Pytania już są. W części trzeciej opus w każdym z ośmiu przebiegów sam nazywał niejasności w zadaniu — „the spec says discounts do not stack but doesn’t say which wins”. Tylko robił to w ostatniej wiadomości, kiedy kod był już napisany, i nikt tego nie czytał. Jeśli te same pytania padną przed startem agentów, a ja odpowiem na każde jednym zdaniem, to spec złożony z moich odpowiedzi powinien domknąć te same decyzje, które w części czwartej domknął szablon pisany ręcznie.
Druga. Jeśli to zadziała, to wciąż nie będę wiedział, co zadziałało. Może wartość siedzi w samym zadawaniu pytań i każdy spec napisany przez model byłby równie dobry. A może w tym, że odpowiada ten, kto ma prawo decydować. Żeby to rozdzielić, potrzebna jest kontrola: ten sam wywiad, ale bez człowieka.
Badanie
Znowu zmieniam jedną rzecz: skąd bierze się opis zadania. Agenci, sędzia, commit bazowy, limity rund zostają jak w części trzeciej i czwartej. Znanych wad pipeline’u też nie ruszam, choć swędzi. Ani listy dozwolonych poleceń, przez którą opus nie może uruchomić testów, ani bramki CI, która „nie wiem” czyta jako „tak”. Gdybym zmienił dwie rzeczy naraz, nie wiedziałbym, która zadziałała.
Nowy jest jeden etap, na samym początku. Model sędziego, ten sam co zawsze, bez narzędzi i bez dostępu do repozytorium, dostaje jedno zdanie zadania i listę pól szablonu. Ma zadać tylko takie pytania, od których zależy kod: gdzie leży granica, jakiego typu są dane, co funkcja zwraca, co robi na złym wejściu, jak rozumieć frazę, którą da się przeczytać na dwa sposoby. Najwyżej dwanaście pytań.
Przy każdym pytaniu dopisuje dwie rzeczy. Co w kodzie zależy od odpowiedzi. I jaką odpowiedź by przyjął, gdyby nikt nie odpowiedział. Zapamiętaj tę drugą rubrykę, bo na niej stoi cały eksperyment.
Potem dwa ramiona, z tym samym zdaniem o rabacie co w dwóch poprzednich częściach:
a function that calculates a discount: 10% above 500 PLN,
20% above 1000 PLN, discounts do not stack with promo codes
Ramię C. Na pytania odpowiada właściciel decyzji, czyli ja. Tak to zaplanowałem. I tu pierwsza zmiana protokołu, zapisana, zanim padła pierwsza odpowiedź.
Zobaczyłem w panelu dziewięć pytań i nie chciałem na nie odpowiadać. Nie dlatego, że były złe. Były dobre. Ale każdą z tych decyzji już raz podjąłem, pisząc spec do części czwartej, a cały ten pipeline ma pracować z jak najmniejszym udziałem człowieka. Odpowiadanie po raz drugi na to samo wyglądało jak zaprzeczenie całego pomysłu.
Odpowiadał więc za mnie model, który pomaga mi pisać tego bloga. Reguła była jedna. Tam, gdzie mój spec z części czwartej rozstrzyga, odpowiedź jest moją decyzją w jego słowach. Tam, gdzie milczy, bierze domyślną odpowiedź pytającego albo pisze „zdecyduj sam”, tak jak ja robię w polu DECYZJE OTWARTE. Niczego nie wolno mu było dopisać od siebie, bo wtedy nie dałoby się zmierzyć, co zamknęły pytania, a co domyślność asystenta.
Panel liczy, ile odpowiedzi było moich, ile przyjętych z domyślnych, ile „zdecyduj sam”. Czasu człowieka nie liczy, bo człowieka przy tym nie było.
Ramię D. Nikt nie odpowiada. Za odpowiedzi służą domyślne założenia modelu, te z drugiej rubryki. Model przepytał sam siebie.
W obu ramionach z odpowiedzi powstaje spec według mojego szablonu, z kryteriami akceptacji, które sędzia bierze słowo w słowo jako swoją checklistę. Dokładnie tak było w części czwartej, w ramieniu B, przy szablonie pisanym ręcznie. Spec jest zamrożony. Nie mogę go poprawić, bo gdybym mógł, to byłoby znowu ramię B.
Po cztery przebiegi na ramię, w każdym dwóch agentów. Szesnaście rozwiązań.
Co liczę? Przede wszystkim pokrycie. Dla zadania o rabacie mam z dwóch poprzednich części listę dziesięciu decyzji, które jedno zdanie zostawia otwarte. Sześć z części trzeciej: gdzie dokładnie jest próg, co z pustym kodem, co ze złą kwotą albo nieznanym kodem, jak liczyć pieniądze, co funkcja zwraca, co znaczy „nie łączą się”. I trzy przypadki brzegowe z czwartej: zaokrąglać przed sprawdzeniem progu czy po, lista kodów podana jako zwykły napis, True jako kwota.
Pytanie „pokrywa” decyzję, jeśli jednozdaniowa odpowiedź na nie ją rozstrzyga. Oceniam to w chwili odpowiadania, w notatkach w panelu, zanim powstanie jakikolwiek kod. Inaczej dopasowałbym ocenę do wyniku, a tego nie chcę, nawet jeśli bym tego nie zauważył.
Reszta jak w części czwartej. Czytam diffy i podsumowania i sprawdzam, które decyzje kod podjął po cichu. Podaję każdemu rozwiązaniu te same trzy nietypowe dane. Patrzę na werdykty sędziego i podejmuję swoją decyzję. W ramieniu D jedna kolumna więcej: gdzie domyślna odpowiedź modelu różni się od mojej.
Jest jeszcze blok drugi, bo przy funkcji rabatowej wszyscy znamy już odpowiedzi, ty też, jeśli czytałeś poprzednie części. Biorę więc świeże zadanie, też jedno zdanie, którego nikt tu nie rozbierał: a function that parses a duration written as text, like “2h 30m” or “45 min”, and returns the number of seconds. Miałem przed pytaniami spisać, jakie decyzje to zdanie zostawia otwarte. Nie spisałem. Ten pomiar odpada. Zostaje prostszy: to samo zdanie raz bez wywiadu, raz z wywiadem, w którym model odpowiada sobie sam. Potem każdy z ośmiu parserów dostaje te same dwadzieścia jeden nietypowych wejść.
Dwie rzeczy zmieniły się po drodze. Obie dotykają stałych, więc muszę je nazwać.
Pierwsza: po drugiej nieudanej próbie C-1 naprawiłem listę dozwolonych poleceń workera. Przez całą część czwartą opus nie mógł uruchomić python -m pytest i oddawał testy, których nigdy nie odpalił. Tym razem, przy specu z jedenastoma kryteriami, próbował dwadzieścia dwa razy i zginął na limicie tur. Stała, która zabija przebiegi, przestaje być stałą. Ale od tej chwili worker A może uruchamiać testy, czego w dwóch poprzednich częściach nie mógł. Jeśli ma teraz mniej defektów, nie wolno tego zapisać na konto wywiadu.
Druga: każde wywołanie claude w pipelinie ładuje mój globalny plik konfiguracyjny. Dzień wcześniej dopisałem tam, że rozmowa ze mną jest po polsku. Siedem z ośmiu wywiadów o rabacie przyszło po polsku. Worker A kończył po polsku. Kod i specy zostały angielskie. Nikt tego nie wybrał. Zostawiam na widoku.
Protokół i predykcje spisałem przed pierwszym przebiegiem, jak poprzednio. Ale tym razem nie zrobiłem commita. Pliki trafiły do repozytorium dopiero po wynikach, już z punktacją (2e75e77), więc historia repo nie poświadcza kolejności. Zostaje moje słowo i daty w rejestrze. Poprzednie dwie części miały tu dowód. Ta ma deklarację. Wolę to napisać, niż udawać.
Wynik
| A: jedno zdanie | B: szablon ręcznie | C: wywiad, odpowiedzi z mojego specu | D: wywiad, model odpowiada sobie | |
|---|---|---|---|---|
| Pytań w wywiadzie | — | — | 9, 8, 10, 8 | 8, 8, 8, 8 |
| Z dziesięciu otwartych decyzji pokryte pytaniami | — | — | K1 K4 K5 K7 w 4/4; K3a 4/4; K3b 3/4; K2 0/4; E1 E2 0/4; E3 tylko jako „nie-liczba” | K1 K4 K5 K7 K3a w 4/4; reszta 0/4 |
| Odpowiedzi: własne / domyślna / „zdecyduj sam” | — | — | 6/2/1, 5/2/1, 5/4/1, 4/3/1 | 0 / wszystkie / 0 |
| Kryteriów w specu | 4 (generowane) | 6 | 11, 12, 13, 12 | 13, 10, 16, 10 |
| Sześć klas niepodjętych decyzji w kodzie | wracają w każdym przebiegu | 0 w 8/8 | 0 w 8/8 | 0 w 8/8 — ale trzy z sześciu rozstrzygnięte nie po mojemu, a kod promocyjny zniknął z sygnatury |
| Trzy przypadki brzegowe z części czwartej | — | 999,995 → 100 zł różnicy w 1/8; napis jako lista → podciąg 5/8; True → 1,00 w 2/8 |
999,995: 8/8 tak samo; podciąg 6/8, grok zamknął 2/8; True odrzucone 8/8 |
nie dotyczy: kod promo zniknął z sygnatury |
| Rundy naprawcze | 0 | 0 | 0 | 0 |
| Werdykty sędziego | remis, A, remis, A | 4 × remis | nierozstrzygnięty ×3, B | A, nierozstrzygnięty, A, A |
| Tokeny wyjściowe workera A na przebieg | 3,8–5,4 tys. | 7,3–7,9 tys. | 6,7–14,1 tys. | 6,5–10,4 tys. |
| Tokeny wyjściowe wywiadu i composera | — | — | 3,9–8,8 tys. | 6,1–9,2 tys. |
Skróty K w tabeli to sześć klas decyzji z części trzeciej, w kolejności z poprzedniego akapitu, skróty E to trzy przypadki brzegowe z czwartej. W tabeli zostają skróty, w tekście mówię po ludzku.
Pierwsza teza się broni. Spec złożony z odpowiedzi domknął w kodzie te same sześć klas, które w części czwartej domknął szablon pisany ręcznie. Zero cichych decyzji w ośmiu rozwiązaniach na osiem. Obie gałęzie gotowe za pierwszym razem w każdym przebiegu. Zero rund naprawczych. Nikt nie napisał ani linijki specu.
Czy to oszczędza czas, od którego zacząłem ten wpis? Nie wiem. Nie zmierzyłem go, bo przy panelu nie siedział człowiek, tylko model przepisujący moje decyzje. Uczciwie: ten pomiar sam sobie wyłączyłem.
Predykcje, które padły, zostawiam na widoku. Obstawiałem, że sędzia ogłosi remis w co najmniej trzech przebiegach na cztery, jak w części czwartej. Nie ogłosił ani razu. W trzech przebiegach jego dwa przejścia, raz z A pierwszym, raz z B pierwszym, nie zgodziły się ze sobą i system oddał werdykt „nierozstrzygnięty”. O tym niżej. Obstawiałem też, że pytanie o pusty kod promocyjny padnie w co najmniej dwóch wywiadach. Nie padło w żadnym.
Czego pytania nie zadały
Cztery pytania wracały w każdym z ośmiu wywiadów o rabacie: gdzie dokładnie jest próg, co funkcja zwraca, jak liczyć pieniądze i co znaczy „nie łączą się”. Dokładnie te, które opus wypisywał w części trzeciej w ostatniej wiadomości. Pytanie o kwotę ujemną i zerową też padało zawsze. To są rzeczy, które widać w samym zdaniu.
O pusty kod promocyjny nie zapytał nikt. A mimo to w ramieniu C żadne z ośmiu rozwiązań nie podjęło tej decyzji po cichu. Jak to? Zamknęła ją moja odpowiedź na inne pytanie, o to, jak kod trafia do funkcji: opcjonalny napis, liczy się wyłącznie, jeśli jest na liście ważnych. Brak kodu nie jest na liście, więc próg zostaje. Tak to przeczytało wszystkich ośmiu, C-1 napisał to wprost: „a missing or unlisted code falls through to the threshold logic”.
Jedna odpowiedź zamknęła dwie decyzje. To zasługa odpowiedzi, nie pytania. I tu moja miara pokrycia pokazała, że liczy nie to, co trzeba. Zapisałem, że pytanie pokrywa decyzję, jeśli odpowiedź na nie ją rozstrzyga, po czym liczyłem pytania. Wartość siedziała w odpowiedziach, więc pokrycie jest zaniżone. To ta sama teza, co cały ten wpis, tylko wyszła mi z własnej tabeli. W ramieniu D ta decyzja w ogóle nie istnieje, bo nie ma tam kodu jako parametru. Do tego wrócę.
Trzech przypadków brzegowych z części czwartej nie zadał nikt. Nikt nie zapytał, czy zaokrąglać przed sprawdzeniem progu, czy po. Każdy pytał jak zaokrąglać, i każdy z szesnastu kandydatów zaokrągla po, więc zamówienie za 999,995 zł dostaje 10%. Nikt nie zapytał, co zrobić, gdy ktoś poda listę ważnych kodów jako zwykły napis. Grok w dwóch przebiegach na cztery sam dopisał sprawdzenie typu i wyjątek, opus w czterech na cztery wpadł w pułapkę podciągu. Nikt nie zapytał o True jako kwotę. Sześć wywiadów pytało o „nie-liczbę” i to wystarczyło, żeby w ramieniu C wszystkie osiem rozwiązań ją odrzuciło, choć w dwóch wyjątek jest surowy, z wnętrza biblioteki, a nie z walidacji.
Te trzy przypadki wyszły w części czwartej z testowania gotowego kodu. Z czytania zdania nie wychodzą. Ani człowiekowi, ani modelowi.
Za to wywiad zadał pytania, których przez cztery wpisy nie zadał nikt z nas. W ośmiu na osiem: czy stawka jest płaska, czy progresywna po przedziałach, jak w podatkach. Dla 1500 zł to 300 albo 150 zł rabatu, a ja przez cztery wpisy nie zauważyłem, że moje zdanie tego nie mówi. W trzech: czy próg liczyć od kwoty przed kodem, czy po. W dwóch: czy wejściem jest jedna kwota, czy koszyk do zsumowania. Po jednym: co, gdy promocja jest większa niż zamówienie; czy 10% i 20% sumują się do 30%; w jakim języku ma powstać funkcja.
Model, który przepytał sam siebie
Tu jest sedno. W ramieniu D nikt nie odpowiadał. Za odpowiedzi brano domyślne założenia pytającego, te z drugiej rubryki. Wynik po stronie kodu wygląda identycznie jak w C: zero cichych decyzji, komplet kryteriów, gotowe za pierwszym razem. Specy były domknięte. Po raz pierwszy w tej serii pole DECYZJE OTWARTE było puste w czterech przebiegach na cztery.
Tylko że domknięte nie po mojemu.
W czterech przebiegach na cztery zamówienie za równo 1000 zł dostaje 10%, bo „powyżej” model czyta ostro, a ja w części czwartej zdecydowałem odwrotnie. W trzech na cztery funkcja zwraca kwotę rabatu, nie kwotę do zapłaty, bo zdanie mówi „calculates a discount”. W dwóch przebiegach na cztery „nie łączą się” znaczyło nagle „wygrywa korzystniejszy”, nie „kod wyłącza próg”. Ten sam model, ten sam prompt, to samo zdanie, a domyślna odpowiedź inna.
I czwarta rzecz, inna od tamtych trzech. W czterech przebiegach na cztery kod promocyjny w ogóle zniknął z sygnatury funkcji. Dwa razy zastąpiła go flaga „użyto kodu”, dwa razy gotowa kwota zniżki w złotych. Funkcja, która u mnie sprawdzała, czy kod jest na liście ważnych, u modelu „nie zna wartości kodu i go nie waliduje”. Tamte trzy to złe rozstrzygnięcia decyzji, która w zdaniu jest. Ta jest zmianą kontraktu, której nikt nie zamawiał: zdanie mówi o kodach promocyjnych, a funkcja ich nie przyjmuje. Ze wszystkiego, co wyszło w D, to jest najgroźniejsze, bo w specu wygląda najporządniej. Kryterium ma numer, sędzia je odhaczył, test przechodzi.
To jest wynik, dla którego robiłem ramię D. Wywiad usuwa milczenie: każda decyzja jest zapisana, nic nie siedzi w docstringu. Nie usuwa błędu. Zapisana decyzja jest decyzją tego, kto odpowiadał, a jeśli nikt nie odpowiadał, jest zgadywanką modelu. Tą samą, co w części trzeciej, tylko przeniesioną z kodu do specu i opatrzoną numerem kryterium. Sędzia sprawdził ją potem sumiennie i wszystko odhaczył, bo odhaczanie nie wstrzykuje brakujących decyzji. Pisałem to w części trzeciej o checkliście. Teraz dotyczy całego specu.
Jedna rzecz z D, której się nie spodziewałem. Tam, gdzie domyślna odpowiedź wprowadziła kwotę zniżki z kodu, od razu pojawiła się nowa decyzja, o którą nikt nie zapytał: co, gdy zniżka jest większa niż zamówienie. W jednym przebiegu oba rozwiązania zwracają 700 zł rabatu od zamówienia za 400. W następnym jedno przycina do zera, drugie zwraca minus 300 zł, i sędzia właśnie na tym rozstrzygnął. Zamknięcie jednych decyzji otwiera następne, piętro niżej. To ta sama obserwacja, co w części czwartej, tylko teraz piętro dokłada sam spec.
Sędzia, który przestał się ze sobą zgadzać
Sędzia porównuje dwa rozwiązania dwa razy, raz z A pierwszym, raz z B pierwszym, żeby nie faworyzował tego, które zobaczył na początku. W części czwartej oba przejścia zgadzały się w czterech przebiegach na cztery, i cztery razy był remis.
Tutaj przebiegów było dwanaście, osiem o rabacie z tabeli wyżej i cztery z bloku drugiego. W sześciu z nich przejścia nie zgodziły się ze sobą: raz A, raz remis; raz remis, raz B. Werdykt „nierozstrzygnięty” to bezpiecznik z części drugiej. Zadziałał dokładnie tak, jak miał, i sześć razy odesłał decyzję do człowieka. Człowiek jej nie podjął, tylko mechanicznie wziął gałąź A i wpisał „bez preferencji”. Tak było w protokole.
Powód widać w uzasadnieniach. Rozwiązania spełniały wszystkie kryteria w stu procentach, więc sędzia szukał różnic poza checklistą. Osobny moduł czy dopisanie do istniejącego pliku. Pełniejsze testy. Komunikat błędu. Jeden zapomniany plik roboczy, który opus zacommitował z komentarzem, że został niezacommitowany, bo nie miał prawa go usunąć, więc zostawił notatkę, a potem dodał ją do commita razem z plikiem. W jednym przejściu sędzia ważył te drobiazgi tak, w drugim inaczej.
Im lepszy spec, tym mniej sędzia ma do roboty i tym więcej szumu rankuje. Pisałem o tym w części trzeciej, kiedy kandydaci byli skażeni ściąganiem. Tu nie byli skażeni, byli po prostu równie dobrzy. Porównanie parami kosztowało zwykle trzy czwarte tokenów wyjściowych sędziego i wskazało zwycięzcę w pięciu przebiegach na dwanaście. W jednym, ostatnim z bloku drugiego, oba przejścia zgodnie orzekły remis.
Zadanie, którego nikt nie znał
Parser czasu trwania. Bez wywiadu sędzia zrobił sobie checklistę z czterech punktów, z czego dwa to przykłady ze zdania. Cztery rozwiązania przeszły ją w komplecie i rozjechały się na trzech z dwudziestu jeden nietypowych wejść. 2.5h to u opusa błąd, u groka 9000 sekund. 1w to u opusa błąd, u groka tydzień. 2h and 30m raz przechodzi, raz nie. Opus w obu przebiegach wypisał na końcu, co zdecydował sam. Grok nie wypisał nic. Nikt tego nie czytał, jak w części trzeciej. Sędzia w obu przebiegach nierozstrzygnięty.
Z wywiadem, w którym model odpowiadał sobie sam, padło dwanaście pytań. Każde z nich było jedną z decyzji, które cztery parsery podjęły po cichu. Trzynaście, czternaście kryteriów. Dwa rozwiązania w każdym przebiegu zgadzają się na wszystkich dziewiętnastu tekstowych wejściach, także na tych trzech, które dzieliły surowe parsery.
Jedyna decyzja, o którą nikt nie zapytał, to wejście niebędące napisem: opus w obu przebiegach rzuca surowy AttributeError z wnętrza .strip(). I jedna domyślna odpowiedź znów się odwróciła między przebiegami. Powtórzona jednostka, 1h 1h, raz się sumuje, raz jest błędem. Czy tydzień ma być błędem, czy 604800 sekund? Tego nikt nie wie, bo właściciela tego zadania nie ma. Jest zapisane. Nie jest rozstrzygnięte. Werdykty: raz B w obu przejściach, raz remis w obu. Jedyny zgodny remis w tym wpisie.
Zastrzeżenia. Dwanaście przebiegów, dwa zadania-zabawki, jedna para modeli, jeden dzień. Decyzje po werdykcie podejmowane mechanicznie, więc licznik zgodności sędziego z człowiekiem z części drugiej jest w tym wpisie pusty. Repozytorium docelowe z ramion A i B zniknęło mi z dysku, zanim ruszył pierwszy przebieg. Odtworzyłem je z patchy co do bajta dla obu plików źródłowych, README jest zamiennikiem, commit bazowy inny. Pierwsza próba C-1 padła, bo nie było repozytorium. Druga, bo grokowi wygasł token i wisiał w logowaniu, a opus w tym czasie zginął na limicie tur, stąd naprawa listy dozwolonych poleceń opisana wyżej. Pytania po polsku, bo mój własny plik konfiguracyjny tak kazał. Surowe artefakty wszystkich dwunastu przebiegów, protokół ze zmianami i predykcje z oceną leżą w repozytorium.
Liczenie. W częściach trzeciej i czwartej podawałem koszty w dolarach. Te liczby brał licznik CLI, a nikt ich nie zapłacił, bo agenci i sędzia pracują na abonamentach, nie na API. Od tej części podaję tokeny: wyjściowe osobno, bo to jest realna praca modelu, i kontekstowe osobno. Dolarów z poprzednich wpisów nie poprawiam. Zostają jako to, czym były: szacunek z licznika.
Puenta
Dwadzieścia kilka linijek szablonu z części czwartej da się zastąpić ośmioma pytaniami i ośmioma jednozdaniowymi odpowiedziami. Kod wychodzi tak samo domknięty. Tylko że te odpowiedzi musi dać ktoś, kto wie, o co chodzi. Kiedy odpowiada model, dostajesz spec bez jednej luki, w którym próg jest w złym miejscu, funkcja zwraca nie to, a kod promocyjny zamienił się w flagę. Wszystko zapisane, wszystko odhaczone, wszystko zielone.
W części trzeciej pisałem, że to nie jest problem kontekstu, tylko uprawnień. Ta część dokłada jedno zdanie: pytania może zadać maszyna, i z samego zdania zadaje je lepiej niż ja. Odpowiedzi muszą pochodzić od kogoś, kto te decyzje podjął. W tym eksperymencie nikt nie odpowiadał na żywo. Model przepisywał moje decyzje ze specu z części czwartej, i to wystarczyło. Częścią ludzką nie jest siedzenie przy panelu. Jest to, że decyzje zostały gdzieś, kiedyś podjęte; kto je przepisze, nie ma znaczenia. Ta część się nie zmniejsza. Przesuwa się na sam początek.
Co dalej
Trzy rzeczy, każda jako osobna zmiana.
Sędzia bez porównania parami, gdy oba są gotowe. Sześć werdyktów nierozstrzygniętych na dwanaście przy kandydatach spełniających komplet kryteriów to sygnał, że porównanie rankuje szum. Następny eksperyment: oba gotowe, oba identyczne na checkliście, to przepuszczamy tańsze w tokenach albo pierwsze, a porównanie zostawiamy na przypadki, gdzie pierwsze przejście sędziego pokazało różnicę. Akcja „zaakceptuj remis” w panelu, zapowiedziana w części trzeciej, wchodzi razem z tym.
Wywiad, który pyta również o to, czego nie widać w zdaniu. Trzy przypadki brzegowe z części czwartej i wejście niebędące napisem z bloku drugiego mają wspólną cechę: wychodzą z podania kodowi złych danych, nie z czytania specu. Kandydat do sprawdzenia: druga runda pytań po pierwszym diffie, z kodem przed oczami, zanim ruszy sędzia.
Zadania, których nie da się zamknąć. Na dwudziestu linijkach kodu sędzia nie miał czego sądzić, w części czwartej i tutaj. Większe zadanie, gdzie rozwiązania różnią się naprawdę, pokaże, czy porównanie parami jest coś warte, czy jest tylko drogim rytuałem z części drugiej.
Te same agenty, ten sam sędzia, zmienia się tylko opis zadania. Osiem rozwiązań, wszystkie poprawne, sędzia nie ma czego rozstrzygać. A potem znajduję zamówienie, na którym różnią się o sto złotych.