Wczoraj GPT-4, dziś moja spec
Jest 15 marca. Wczoraj wieczorem OpenAI ogłosiło GPT-4 i od tamtej pory widziałem w sieci głównie dwie rzeczy: tabelki z wynikami egzaminów prawniczych i zdania o tym, że tego zawodu już nie będzie. Ani jedno, ani drugie nie jest pytaniem, które mam do tego modelu.
Moje pytanie jest węższe i mieści się w jednym zdaniu: czy przełącznik modelu w interfejsie ChatGPT Plus zmienia cokolwiek w dwóch czynnościach, które faktycznie wykonuję przy biurku. Pierwsza to szkic listy przypadków testowych z dostarczonej specyfikacji. Druga to czytanie tej specyfikacji pod kątem dziur, zanim ktokolwiek zacznie kodować.
Piszę ten wpis dzień po ogłoszeniu i chcę, żeby to było widać. Nie mam za sobą sprintu z nowym modelem, nie mam wykresu z trzech miesięcy i nie mam zdania o tym, jak to wygląda w zespole dziesięciu osób. Mam jeden wieczór, jedną syntetyczną specyfikację przepuszczoną dwa razy i osiem różnic, które umiem policzyć. To jest wszystko, co mam, i tyle napiszę.
W podsumowaniu 2022 zapisałem sobie zdanie, do którego dziś wracam: sztuczna inteligencja weszła u mnie do edytora jako przewidywanie kolejnej linii i nie weszła nigdzie indziej. Trzy miesiące później to zdanie nadal jest prawdziwe, bo w edytorze mam dokładnie to samo co w grudniu: szary tekst przed kursorem, Tab i Esc. To, o czym piszę dzisiaj, dzieje się w oknie przeglądarki, w osobnej karcie, obok mojego IDE. Ta granica jest dla mnie ważna i wrócę do niej na końcu.
Co jest dostępne 15 marca
Zanim cokolwiek o zastosowaniach, ustalam stan faktyczny, bo w tym tygodniu wszystko się przesuwa i za pół roku nikt nie będzie pamiętał, co było za bramką, a co nie.
W ChatGPT Plus, czyli w abonamencie za 20 dolarów miesięcznie, mam w rozwijanej liście modeli nową pozycję z GPT-4. Wybieram ją per rozmowa. Jest limit liczby wiadomości w okienku czasowym i OpenAI od razu zapowiedziało, że będzie go korygować w zależności od obciążenia, więc nie traktuję żadnej konkretnej liczby jako trwałej. Praktyczny skutek jest taki, że nie da się na tym modelu pracować cały dzień w trybie ciągłego przerzucania się zdaniami. Trzeba mieć przygotowane wejście.
W API gpt-4 nie mam. Jest waitlista i wypełniłem formularz jak wszyscy. OpenAI opublikowało nazwy modeli i cennik z góry: snapshot gpt-4-0314, okno kontekstu 8192 tokenów w podstawowym wariancie i osobny wariant z 32768 tokenami, rozliczenie osobne za tokeny wejścia i wyjścia, wyraźnie drożej niż to, co mam dzisiaj w koszyku. Zapisuję to jako informację, nie jako coś, co mam w skrypcie. Do momentu, w którym dostanę dostęp, każdy mój kod wołający gpt-4 byłby fikcją.
To, co w API mam od dwóch tygodni, to gpt-3.5-turbo. Chat Completions wyszło 1 marca, ze snapshotem gpt-3.5-turbo-0301, oknem 4096 tokenów i ceną 0,002 dolara za tysiąc tokenów. To jest dziesięciokrotnie taniej niż linia text-davinci-003, z której korzystałem wcześniej, i to jest zmiana, która realnie przesuwa rachunek za eksperymenty z “muszę o tym pomyśleć” do “nie zauważę tego na wyciągu”. Nowa jest też struktura zapytania: zamiast jednego bloku tekstu wysyłam listę wiadomości z rolami, w tym rolę systemową, w której da się raz ustawić format odpowiedzi na całą rozmowę.
Endpoint Completions z davinci w adresie, ten, na którym stał mój eksperyment z danymi testowymi z 2021 roku, nadal działa. Nie przepisuję tamtego wpisu i nie zmieniam jego wniosków, bo one nie dotyczyły modelu, tylko niedeterminizmu i miejsca, w którym mieszka zatwierdzony fixture.
Dwie rzeczy odnotowuję i nie robię z nich tematu. GPT-4 przyjmuje obrazy na wejściu, ale ta część jest w ograniczonej alfie z jednym partnerem, więc nie jest to narzędzie, którym dziś pracuję, i nie napiszę o niej tutoriala. Tego samego dnia Anthropic ogłosiło swój model Claude i zostawiam to przy jednym zdaniu, bo nie mam do niego dostępu ani jednego przebiegu na własnych danych, a porównywanie cudzych tabelek z benchmarkami nie jest treścią, którą chcę produkować.
Gdzie GPT-4 pomaga w QA
Test zrobiłem najprostszy, jaki umiałem wymyślić, żeby porównanie nie było opowieścią o wrażeniach. Ta sama specyfikacja, ten sam prompt, dwie rozmowy w tym samym interfejsie, jedna na modelu domyślnym i jedna na GPT-4.
Specyfikacja jest syntetyczna i napisałem ją sam na potrzeby tego wpisu. Nie ma w niej ani jednego zdania z żadnego projektu, w którym pracuję, i nie będzie tu nazwy żadnego klienta. Wymyśliłem funkcję “kod rabatowy w koszyku” w sklepie, jakiego nie ma: dziewięć kryteriów akceptacji rozpisanych w stylu, w jakim dostaje się je w ticketach, plus cztery ograniczenia przekrojowe wciśnięte w akapity opisu, a nie w listę. Te cztery to jeden kod na zamówienie, minimalna wartość koszyka liczona przed rabatem, waluta zgodna z rynkiem sklepu i data wygaśnięcia w strefie czasowej sklepu, nie użytkownika. Ograniczenia schowałem celowo, bo w prawdziwej specyfikacji dokładnie tam siedzą i dokładnie one giną w szkicu.
Prompt w obu rozmowach był identyczny: rola testera, prośba o listę przypadków w tabeli z kolumnami warunek wstępny, kroki, oczekiwany rezultat, wyraźne polecenie oznaczenia miejsc, w których specyfikacja milczy, i zakaz wymyślania nazw elementów interfejsu. Potem w obu rozmowach cztery iteracje: dopisz przypadki brzegowe dla kwoty, dopisz negatywne dla wygasłego kodu, skróć do jednego zdania na krok, dodaj kolumnę z priorytetem.
Wynik zliczyłem ręcznie. To jest jeden przebieg na jednej specyfikacji, więc nie są to statystyki, tylko notatka z wieczoru.
| Kryterium | Model domyślny (3.5) | GPT-4 (Plus) |
|---|---|---|
| Liczba przypadków po pierwszym prompcie | 14 | 21 |
| Pokryte kryteria akceptacji (z 9) | 6 | 9 |
| Złapane ograniczenia przekrojowe (z 4) | 1 | 3 |
| Oznaczone luki w specyfikacji | 0 | 4 |
| Zmyślone nazwy elementów interfejsu | 3 | 2 |
| Zmyślone endpointy i pola API | 1 | 1 |
| Iteracja, po której rozjechał się format tabeli | druga | format utrzymany |
| Iteracja, po której wypadło wcześniejsze ograniczenie | trzecia | czwarta |
Dwie kolumny w tej tabeli mówią dwie różne rzeczy i nie chcę, żeby zlały się w jedno zdanie o tym, że nowy model jest lepszy.
Pierwsza rzecz, realna: dłuższe wejście przestaje się gubić. Model domyślny zachowywał się tak, jakby czytał moją specyfikację od góry i po drodze zapominał początek. Cztery ograniczenia wciśnięte w akapity opisu były dla niego niewidoczne, złapał jedno, i to najbardziej oczywiste. GPT-4 wyciągnął trzy z czterech, a przy dwóch z nich sam dopisał zdanie, że specyfikacja nie mówi, co się dzieje, kiedy koszyk spada pod minimum po usunięciu produktu. To jest dokładnie ta uwaga, po którą chodzi się do analityka, i dokładnie ta, której model domyślny nie postawił ani razu. Cztery oznaczone luki kontra zero to dla mnie najważniejszy wiersz w całej tabeli, ważniejszy od liczby przypadków, bo liczbę przypadków umiem sobie wyprodukować sam i nie jest ona żadnym osiągnięciem.
Druga rzecz, też realna: format trzyma się dłużej przy iteracji. Przy modelu domyślnym po dwóch poprawkach dostawałem tabelę o innej liczbie kolumn i przypadki, które wcześniej były w niej, a teraz zniknęły bez słowa. To jest ta klasa zachowania, przy której cała oszczędność czasu wyparowuje, bo zaczynam porównywać wersję trzecią z pierwszą. GPT-4 przy tych samych czterech poprawkach oddał piątą wersję z tymi samymi kolumnami i bez zniknięcia całych wierszy. Zgubione ograniczenie pojawiło się dopiero przy czwartej iteracji, czyli zdarzyło się później, a nie przestało się zdarzać.
I teraz rzecz, której nowy model nie zmienił, bo to jest sedno tego wpisu. Zmyślanie nie zniknęło. Trzy wymyślone nazwy elementów interfejsu spadły do dwóch, mimo że w prompcie miałem wyraźny zakaz. Model wciąż z siebie wyprodukował #discount-code-input i data-testid="apply-voucher", których nie ma nigdzie, bo ten sklep nie istnieje, a specyfikacja nie zawierała ani jednego selektora. Zmyślony endpoint był jeden i tam, i tam: POST /api/cart/voucher z polem, o którym nie ma słowa w moim tekście. Przy jednym z nich GPT-4 dorzucił zdanie, że nazwa jest przykładowa i wymaga potwierdzenia. Doceniam to i nadal jest to ten sam problem, bo w piątej wersji tabeli, po skróceniu kroków, to zastrzeżenie już nie wróciło, a selektor został.
Wniosek praktyczny jest ten sam, który zapisałem sobie w czerwcu przy Copilocie wychodzącym z technical preview, tylko na innej warstwie. Model jest dobry w kształcie i słaby w konkretach zależnych od rzeczywistości, której nie widzi. Kształt listy przypadków, podział na scenariusze pozytywne i negatywne, nazwanie klasy równoważności, przypomnienie o czymś, co mi umknęło - tak. Selektor, adres endpointu, nazwa pola, wartość graniczna wynikająca z reguły biznesowej - nie, i te cztery kategorie sprawdzam co do jednej. Selektory u mnie i tak nie powstają w czacie: pisze się je przy otwartej stronie i przy uruchomionym runnerze, w moim przypadku Playwrightcie w 1.31.2, i nie ma do tego skrótu.
Jest też koszt, o którym łatwo nie napisać. Przygotowanie wejścia do tego porównania zajęło mi więcej czasu niż same dwie rozmowy. Specyfikacja musiała być gotowa, prompt musiał być ten sam, a wynik trzeba było przeczytać w całości, żeby wyłapać cichy ubytek między wersją drugą a trzecią. Model, który daje mi dwadzieścia jeden przypadków w trzydzieści sekund, nie skraca czytania. On przenosi mój czas z pisania do recenzowania i to jest cała transakcja, którą tu robię.
Gdzie zostaję przy 3.5
Nie przełączam się na nowy model wszędzie i mam do tego trzy powody, wszystkie prozaiczne.
Limit wiadomości w Plus jest realną barierą przy pracy, która polega na dwudziestu krótkich pytaniach pod rząd. Kiedy mam do zrobienia szkic, z którego i tak wyrzucę połowę, na przykład pięć nazw dla parametryzowanego testu albo szybkie przypomnienie, jak brzmi klasa równoważności dla pola z kwotą, biorę model domyślny bez zastanowienia. Jest szybszy w odpowiedzi, a jakość, której potrzebuję, to “coś, od czego zacznę”.
Cena po stronie API idzie w tę samą stronę. Wszystko, co mam zautomatyzowane przy biurku, chodzi na gpt-3.5-turbo za 0,002 dolara za tysiąc tokenów i nie ma powodu, żeby przy stawkach zapowiedzianych dla gpt-4 przenosić tam zadania, w których i tak czytam wynik zdanie po zdaniu. Gdybym dostał dostęp do API dzisiaj, pierwszą rzeczą, którą bym zrobił, nie byłoby przepisanie skryptów, tylko wybranie tego jednego zadania, w którym dłuższy kontekst faktycznie coś zmienia.
Trzeci powód jest o mnie, nie o modelu. Krótkie zadanie z krótkim wejściem to jest dokładnie ten przypadek, w którym różnica z mojej tabeli nie występuje. Cała przewaga, którą wczoraj zobaczyłem, dotyczyła długiego wejścia i wielu reguł naraz. Przy trzech linijkach kontekstu oba modele dają mi to samo i płacenie za lepszy z nich jest po prostu marnowaniem limitu.
Czego nie robię tego tygodnia
Lista rzeczy, których świadomie nie robię, jest w tym tygodniu ważniejsza od listy tego, co zrobiłem.
Nie podpinam żadnego modelu pod CI, ani gpt-3.5-turbo, do którego mam dostęp, ani gpt-4, do którego dostępu nie mam. Wniosek z 2021 roku nie zmienił się ani o przecinek: dwa identyczne wywołania nie muszą dać tego samego wyniku, więc test, którego wynik zależy od odpowiedzi modelu, jest migotliwy z definicji, a czerwony przebieg zaczyna znaczyć “cudze API miało zły dzień” zamiast “aplikacja jest zepsuta”. Model może być narzędziem, które pomaga mi napisać przypadek. Nie jest krokiem w pipeline’ie, który ten przypadek wykonuje.
Nie wysyłam do czatu cudzej specyfikacji. To dlatego w tym wpisie jest specyfikacja wymyślona przeze mnie i będzie tak w każdym kolejnym wpisie o tych narzędziach. Wklejenie ticketu z projektu do zewnętrznej usługi jest decyzją właściciela tego ticketu, nie moją, i nie robi się jej po cichu, żeby zobaczyć, czy model sobie poradzi. Ta sama zasada, którą zapisałem przy generowaniu danych: nic produkcyjnego, ani jednego prawdziwego nazwiska, ani jednego fragmentu dumpa.
Nie zmieniam nic w edytorze. Copilot u mnie nadal robi to samo co w grudniu, czyli podpowiada kolejną linię, i nie ma w nim miejsca, w którym mógłbym o cokolwiek zapytać. Rozmowa o specyfikacji siedzi w karcie przeglądarki, kod powstaje w IDE, i te dwie rzeczy nie mają dziś ze sobą technicznego połączenia. Nie buduję sobie takiego połączenia na siłę i nie planuję pracy pod założenie, że wkrótce się pojawi.
Nie przepisuję też swoich wcześniejszych wpisów o tych narzędziach. Wpis z 2021 roku opisuje endpoint Completions z czerwca 2021 i tak zostaje. Grudniowe podsumowanie opisuje rok, w którym w moim edytorze pojawił się szary tekst, i tak zostaje. Dopisywanie do archiwum akapitów z dzisiejszą wiedzą sprawia tylko, że za rok nie da się odtworzyć, co kiedy było wiadomo.
Podsumowanie
Po jednym wieczorze mam wniosek, który mieści się w jednym zdaniu: GPT-4 w Plus jest wyraźnie lepszym szkicownikiem do długiej specyfikacji, i to jest cała zmiana.
Rozbijam to na trzy zdania, które chcę móc przeczytać za rok. Dłuższe i bardziej pogmatwane wejście przestaje się gubić w połowie, co przy czytaniu specyfikacji pod kątem dziur jest realną różnicą, bo model po raz pierwszy sam wskazał mi cztery miejsca, w których czegoś brakuje. Format odpowiedzi trzyma się dłużej przy iteracji, więc mniej czasu schodzi na porównywanie wersji tabeli. Zmyślanie selektorów, endpointów i wartości granicznych nie zniknęło, tylko rzadziej i ładniej się przyznaje, więc wszystkie cztery kategorie sprawdzam dalej tak samo jak w grudniu.
Czego z tego nie wynika. Nie wynika, że mam nowe narzędzie w pipeline’ie, bo nie mam. Nie wynika, że mam dostęp do API, bo mam formularz na waitliście. Nie wynika, że ktokolwiek przestał być potrzebny, bo model, który wymyśla data-testid dla sklepu, którego nie ma, nie jest kandydatem na właściciela zestawu regresji.
Ownerem przypadków testowych zostaję ja i to nie jest kokieteria. Podpisuję się pod listą, którą dostaje zespół, a podpisać się można tylko pod czymś, co się przeczytało. Nowy model skrócił mi drogę do pierwszej wersji tej listy i nie skrócił ani o minutę drogi od pierwszej wersji do wersji, którą wysyłam. To drugie zawsze było droższe i nadal jest.
Za trzy albo cztery tygodnie będę wiedział, czy przewaga z tabeli utrzymuje się na specyfikacjach, których sam nie napisałem, czy tylko na tej jednej, którą przygotowałem pod porównanie. Do tego czasu wracam do pytania, które zadaję przy każdym nowym narzędziu i które nie zmieniło się od 2019 roku: gdzie to się mieści w całym procesie testowym. Lepszy szkic listy przypadków nie naprawia procesu, w którym nikt tej listy nie czyta przed kodowaniem. Sprawia tylko, że lista jest gotowa wcześniej i że wcześniej widać, czy ktokolwiek na nią czeka.

