Wszystkie wpisy

Prompt engineering do przypadków testowych - luty 2023
Prompt engineering do przypadków testowych - luty 2023

English

Prompt engineering do przypadków testowych - luty 2023

Luty 2023: rola, ograniczenia i format wyjścia w ChatGPT. Plus skraca kolejkę. Bez API i bez GPT-4.

Po co drugi wpis o czacie

Jest 15 lutego i mam za sobą kilka tygodni, w których w styczniu szkicowałem przypadki testowe z czatu i wyciągnąłem z tego jeden wniosek: model potrafi napisać listę, która wygląda jak lista z ticketu, i potrafi ją napisać w trzydzieści sekund. To był wniosek o narzędziu. Dzisiejszy wpis jest o czymś innym, bo problem, który mi po styczniu został, nie ma nic wspólnego z tym, czy czat umie pisać przypadki.

Problem brzmi tak: dwa razy zadałem prawie to samo pytanie i dostałem dwie różne jakości odpowiedzi. Raz wyszła tabela, którą po jednej korekcie wkleiłem do ticketu. Raz wyszedł esej o tym, jak ważne jest testowanie formularzy, z pięcioma przypadkami na końcu, z czego trzy dotyczyły pól, których w tym formularzu nie ma. Różnica nie była w modelu, bo model był ten sam. Różnica była w tym, co ja napisałem.

Dlatego nie piszę dziś drugiego wstępu do ChatGPT i nie piszę o żadnej nowej wersji. Piszę o promptcie jako o rzeczy, którą się układa raz, zapisuje w pliku i potem tylko podmienia w niej kontekst. Teza całego wpisu jest jedna: w lutym 2023 jakość listy przypadków z czatu zależy mniej od tego, czy mam wykupiony plan płatny, a bardziej od tego, czy w mojej wiadomości jest rola, czy są twarde ograniczenia, czy jest format wyjścia do wklejenia gdzie indziej niż okno przeglądarki, i czy zrobiłem drugą turę na dziury, zamiast wziąć pierwszy dump.

Jedna uwaga porządkowa na start, bo bez niej reszta wpisu jest nieuczciwa. Wszystko poniżej dzieje się w okienku rozmowy, w przeglądarce, ręcznie, i kończy się skopiowaniem tekstu do ticketu. Nie ma tu ani jednej linii kodu, który by cokolwiek wołał. To nie jest wpis o wpinaniu modelu w pipeline i takiego wpisu w lutym u mnie nie będzie, bo nie mam do tego ani interfejsu, ani powodu.

Cztery klocki promptu

Prompt, którego dzisiaj używam, ma cztery części i pilnuję, żeby każda była napisana osobno, nawet kiedy wiadomość robi się przez to długa. Kolejność ma znaczenie mniejsze niż obecność.

Pierwszy klocek to rola i to jest miejsce, w którym najczęściej widzę cudze błędy. Rola ma być wąska i nudna. “Jesteś inżynierem QA, który pisze przypadki testowe do ręcznego wykonania” działa u mnie lepiej niż “jesteś senior test architect z dwudziestoletnim doświadczeniem”, bo drugie zdanie prosi model o ton, a nie o zawartość, i dostaję za nie akapit o strategii jakości zamiast wiersza w tabeli. Im bardziej rola brzmi jak nazwa stanowiska z LinkedIna, tym więcej dostaję prozy.

Drugi klocek to kontekst i tutaj obowiązuje reguła, od której nie robię wyjątków: kontekst jest syntetyczny. Nie wklejam nazw klientów, nie wklejam prawdziwych adresów URL środowisk, nie wklejam danych osobowych z bazy testowej. Cały ten wpis stoi na wymyślonym koszyku wymyślonego sklepu i to nie jest ostrożność dla ostrożności. Rozmowa w przeglądarce jest po cudzej stronie i nie mam nad nią żadnej kontroli, więc wchodzi do niej tylko to, co mógłbym opublikować na blogu. Konsekwencja jest taka, że muszę opisać ekran słowami, i to jest praca, której czat za mnie nie wykona.

Trzeci klocek to ograniczenia i on decyduje o tym, ile korekt zrobię potem. Model bez ograniczeń wypełnia dziury najbardziej prawdopodobną treścią, a nie treścią prawdziwą, i to jest ta sama obserwacja, którą zapisałem sobie w marcu zeszłego roku przy podpowiedziach w edytorze: systematycznie prawdopodobne zamiast poprawnego. Dlatego mam trzy zdania, które trafiają do każdego promptu: nie wymyślaj pól, których nie podałem; nie wymyślaj nazw endpointów ani kodów odpowiedzi; jeżeli czegoś nie wiesz, wypisz to jako pytanie, nie zgaduj. Ta ostatnia instrukcja jest najbardziej wartościowa z całej trójki, bo zamienia halucynację w listę rzeczy, które sam muszę dopytać w zespole.

Czwarty klocek to format wyjścia i test na niego jest banalny: czy da się to wkleić do ticketu bez przepisywania. Jeżeli muszę ręcznie rozbijać akapity na kroki, to prompt był zły, nie odpowiedź. Proszę o tabelę Markdown ze stałymi kolumnami i o osobną sekcję na pytania.

Złożone razem, mój szablon na dziś wygląda tak:

Rola: jesteś inżynierem QA, który pisze przypadki testowe do wykonania ręcznego.
Nie pisz wstępu ani podsumowania.

Kontekst: krok "podsumowanie zamówienia" w koszyku sklepu internetowego.
Formularz ma dokładnie te pola: e-mail, imię, nazwisko, ulica z numerem,
kod pocztowy, miasto, wybór dostawy (kurier albo paczkomat),
wybór płatności (karta, przelew, za pobraniem), zgoda na regulamin (checkbox).
Przycisk "Zapłać" jest nieaktywny do czasu wypełnienia pól wymaganych.
Aplikacja webowa, przeglądarki Chromium i WebKit, wersja desktop i mobilna.

Ograniczenia:
- nie wymyślaj pól, których nie ma na liście powyżej,
- nie wymyślaj nazw endpointów, kodów odpowiedzi ani komunikatów walidacji,
- jeżeli czegoś nie wiesz, dopisz to w sekcji "pytania" i nie zgaduj,
- żaden przypadek nie może zależeć od wyniku poprzedniego przypadku,
- maksymalnie 12 przypadków w tej turze.

Format: tabela Markdown, kolumny
id | warunek wstępny | kroki | oczekiwany rezultat.
Po tabeli sekcja "pytania" jako lista punktowana.

Rzeczy, których w tym szablonie świadomie nie ma, są dla mnie równie ważne jak te, które są. Nie ma prośby o priorytet, bo priorytet nie jest własnością formularza, tylko własnością ryzyka w konkretnym projekcie, i model nie ma o nim żadnych danych. Nie ma prośby o gotowy kod testu, bo wtedy dostaję kod pod wymyślone lokatory i recenzja trwa dłużej niż napisanie tego samemu. Nie ma liczby w stylu “wygeneruj 50 przypadków”, bo dwunasty wiersz jest już zwykle wariantem trzeciego, a przy pięćdziesiątym czytam wyłącznie duplikaty.

Iteracja zamiast jednego strzału

Pierwsza odpowiedź na powyższy prompt jest u mnie zawsze podobna z charakteru. Dostaję pokrycie ścieżki pozytywnej, po jednym przypadku na każdą opcję dostawy i płatności, dwa lub trzy przypadki na pusty e-mail i zły format e-maila, i sekcję pytań, w której model sensownie pyta o to, czy paczkomat wymaga wyboru punktu na mapie. To jest dobry materiał wejściowy i zła lista końcowa.

Zła, bo brakuje w niej dokładnie tego, co zwykle wysadza checkout w produkcji. Dlatego druga wiadomość jest u mnie stałym elementem procesu, nie awarią pierwszego promptu:

Ta lista pokrywa ścieżkę pozytywną i walidację e-maila.
Dopisz tylko nowe wiersze, w tym samym formacie, dla:
- pól wymaganych pozostawionych pustych, każde osobno,
- kodu pocztowego w złym formacie i o złej długości,
- braku zgody na regulamin przy poprawnie wypełnionej reszcie,
- wartości na granicy dopuszczalnej długości pola i o jeden znak dłuższej,
- dwóch jednoczesnych zakupów ostatniej sztuki tego samego produktu,
- powrotu przyciskiem "wstecz" po zatwierdzeniu formularza,
- odświeżenia strony w trakcie wypełniania.
Nie powtarzaj przypadków, które już wypisałeś. Jeżeli któryś z punktów
wymaga wiedzy o backendzie, której nie masz, wpisz go do sekcji "pytania".

Wartość tej wiadomości nie polega na tym, że model sam by na to nie wpadł. Polega na tym, że kategorie luk wypisuję ja, z głowy, i one są za każdym razem te same: negatywne, puste, granice, współbieżność, nawigacja i stan przeglądarki. Mam je zapisane obok szablonu jako zwykłą listę kontrolną i nie udaję, że to sztuczna inteligencja mi ich dostarcza. Czat wypełnia kategorie treścią pasującą do opisanego ekranu i robi to szybciej, niż ja bym to sklikał, a to nie jest ta sama umiejętność.

Dwa nawyki wokół drugiej tury, których się trzymam. Nie resetuję rozmowy bez powodu, bo cały kontekst formularza jest już wyżej i nowy czat oznacza wklejenie go od nowa razem z ryzykiem, że coś przy tym uproszczę. Reset robię tylko wtedy, kiedy model zakotwiczył się na złym założeniu i uparcie do niego wraca w każdej kolejnej odpowiedzi, mimo poprawki; wtedy taniej jest zacząć od czystego szablonu z dopisanym jednym zdaniem, które to założenie wyklucza. Drugi nawyk: sekcję pytań czytam pierwszą, przed tabelą. Ona jest jedynym miejscem, w którym rozmowa mówi mi coś o moim własnym opisie systemu, a nie o formularzu.

I zdanie, które muszę postawić wprost, bo bez niego cała ta technika brzmi lepiej, niż działa. Trzecia i czwarta tura zwykle nic nie dodają. Po drugiej turze krzywa się kładzie, wiersze zaczynają się powtarzać innymi słowami, a ja zaczynam kasować więcej, niż dopisuję. Kończę na dwóch turach i dalej pracuję już w ticketach.

Plus i darmowy, i inny czat obok

Od 1 lutego istnieje płatny plan ChatGPT Plus za 20 dolarów miesięcznie, a od 10 lutego jest dostępny poza Stanami, więc od pięciu dni mogę go realnie kupić z Polski. Napiszę o nim dokładnie tyle, ile jest wart w kontekście tego wpisu.

Plus kupuje dostępność: dostęp w godzinach, w których darmowa wersja odbija się od komunikatu o obciążeniu, szybsze odpowiedzi i pierwszeństwo w nowych funkcjach. To jest zauważalna różnica, kiedy chcę popracować w środku dnia. Nie kupuje natomiast ani jednej rzeczy z tego, o czym pisałem wyżej. Ten sam prompt bez ograniczeń i bez formatu daje w płatnym planie równie rozlazłą odpowiedź, tylko dostaję ją wcześniej. Nie zmienia się też podział pracy: to, co zostaje do recenzji, zostaje do recenzji w obu planach.

Dla porządku, bo w tym tygodniu nie da się tego pominąć: 7 lutego Microsoft pokazał nowe Bing i Edge z czatem podłączonym do wyszukiwarki, czyli obok mojego okienka pojawił się drugi czat o innym profilu. Odnotowuję to jako fakt z kalendarza i nie mieszam go do tego wpisu, bo generowanie listy przypadków z opisu formularza, który sam podaję, nie jest zadaniem, w którym dostęp do świeżych wyników wyszukiwania cokolwiek zmienia.

Co zostaje po mojej stronie

Trzy rzeczy z tego procesu nie przeszły na model i nie widzę drogi, którą by przeszły w tym roku.

Priorytet i to, co z listy w ogóle wchodzi do zakresu. Model dał mi dwadzieścia kilka wierszy po dwóch turach i wszystkie są formalnie poprawne. Które z nich wykonujemy przed wypuszczeniem zmiany, a które lądują w koszyku “kiedyś”, zależy od tego, gdzie ten sklep realnie traci pieniądze i co się w nim ostatnio psuło. Czat nie ma tych danych i nie mam ich jak podać, nie łamiąc reguły o kontekście syntetycznym. To jest ta sama granica, o którą rozbijają się wszystkie narzędzia opisywane na tym blogu: decyzja siedzi w procesie, nie w narzędziu.

Dane testowe. Tabela mówi “wprowadź kod pocztowy o złej długości” i to jest instrukcja, nie dane. Skąd bierzemy konto z historią zamówień, kto czyści koszyk po nieudanej płatności i czy zestaw danych da się odtworzyć dwa razy z tym samym wynikiem, to pytania, na które odpowiada strategia zarządzania danymi testowymi, i nic z lutego jej nie ruszyło. Mam też własny, starszy eksperyment z generowaniem samych danych przez interfejs HTTP w Completions i jego wniosek nadal stoi: dwa wywołania z tym samym wejściem nie muszą dać tego samego wyniku, więc to jest narzędzie na etapie wymyślania, a nie źródło prawdy w przebiegu CI. Rozmowa w przeglądarce jest pod tym względem gorsza, nie lepsza, bo nawet nie mam w niej parametrów do przypięcia.

Czy przypadek jest wart automatyzacji. Wiersz w tabeli kosztuje jedno kliknięcie kopiowania. Ten sam wiersz przepisany na test w Playwrighcie, u mnie dziś na 1.30.0 z 25 stycznia, kosztuje lokatory, dane, czas na bramce i utrzymanie przez najbliższe dwa lata. Łatwość generowania listy przesuwa tu pokusę w złą stronę i to jest dla mnie najbardziej realne ryzyko całej tej techniki: nagle mam trzy razy więcej kandydatów do automatyzacji przy tym samym budżecie na utrzymanie. Podział na to, co idzie na pull request, a co do nocnego przebiegu, opisałem we wrześniu i on obowiązuje bez zmian, niezależnie od tego, skąd wziął się tekst przypadku.

Do tego dochodzi kontrast, który powtarzam po raz kolejny, bo w rozmowach ciągle się zlewa. Copilot w edytorze to szary tekst przed kursorem, bez okna do zadawania pytań, i jego efektem jest mój kod. Rozmowa w przeglądarce to osobne narzędzie, którego efektem jest tekst do ticketu. Dwa różne miejsca w moim dniu, dwa różne rodzaje ryzyka i żadne z nich nie zastępuje drugiego.

Podsumowanie

Prompt engineering brzmi jak dziedzina, a w lutym 2023 jest u mnie zwykłą dyscypliną pisania jednej wiadomości: rola wąska, kontekst syntetyczny i wypisany ręcznie, ograniczenia zabraniające zgadywania, format do wklejenia. Plus tego nie zmienia, tylko skraca kolejkę. Druga tura na kategoriach luk daje więcej niż jakikolwiek nowy model, bo kategorie i tak przychodzą z mojej głowy.

Efekt po miesiącu pracy tym sposobem umiem opisać tylko jakościowo i nie będę udawał liczby: czas między “mam opisany ekran” a “mam listę przypadków w tickecie” spadł zauważalnie, a czas potrzebny na decyzję, które z tych przypadków cokolwiek znaczą, nie spadł ani o minutę. W podsumowaniu 2022 zapisałem, że sztuczna inteligencja weszła u mnie do edytora jako przewidywanie kolejnej linii i nigdzie więcej. Po lutym dopisuję do tego jedno zdanie: weszła też do okna przeglądarki jako narzędzie do szkicowania tekstu, który potem sam recenzuję, i nadal nie weszła do żadnego pliku, od którego zależy merge do main.

Szablon zostaje u mnie w repozytorium z notatkami, obok listy kategorii luk. Jeżeli za kilka miesięcy pojawi się sposób, żeby ten sam prompt wołać inaczej niż palcami w przeglądarce, to i tak zacznę od tych samych czterech klocków, bo one nie są własnością żadnego interfejsu.