Wąski pomocnik, nie launch
Jest 15 listopada, piątek. Od roku mam pod ręką GPTs i Assistants API, bo OpenAI pokazało jedno i drugie na DevDay 6 listopada 2023. W podsumowaniu 2023 zapisałem to jako nagłówek roku, a nie jako proces: w moim listopadzie nie powstał ani jeden GPT i ani jedna linia wołająca Assistants. Tydzień z kawałkiem między keynote’em a moim listopadowym wpisem wystarczał najwyżej na streszczenie slajdów, więc oba tematy odłożyłem na 2024. Minął rok i dziś zamykam ten punkt z listy rzeczy świadomie odłożonych.
Składam własnego, wąskiego asystenta do jednej klasy pytań: jak u nas odpalamy i czytamy testy. Jak uruchomić lokalnie E2E logowania. Który job puszcza testy obciążeniowe. Czy czerwony nightly blokuje merdż. Zadaję je sobie sam, kiedy wracam do repozytorium z przykładami po dwóch tygodniach przy czymś innym, i zadałby je każdy, kto otwiera to repozytorium pierwszy raz. “Wewnętrzny” znaczy tu: mój, postawiony obok mojego repozytorium, a nie wdrożony w czyjejś firmie.
Teza jest wąska i chcę ją mieć na górze, żeby nie zginęła w demie. Wewnętrzny asystent QA to w listopadzie 2024 wąski GPT albo wątek Assistants na syntetycznej bazie z trzech części: skrótu procesu testów, konwencji lokatorów i opisu, jak czytać nocny przebieg. U mnie jest to GPT z instrukcją i trzema plikami, które mógłbym opublikować. To jest prior art z DevDay, a nie mój wynalazek. Odpowiedź asystenta jest szkicem, który recenzuję wobec pliku tak jak pull request. Asystent nie odpala suite, nie jest bramką jakości i nie ma tokena do niczego.
Miesiąc temu, przy szkicu z nocnego przebiegu, świadomie nie brałem ani GPT, ani asystenta z plikami. Wejściem był wyciąg z jednej nocy, co rano inny, i do jego przepisania nie potrzebowałem ani pamięci, ani plików. Tutaj jest odwrotnie. Źródło jest stałe, te same trzy pliki, a zmienia się tylko pytanie. To jest cały powód, dla którego GPT z plikami ma w tym miejscu sens.
Stan warsztatu, żeby za rok było wiadomo, o czym mówię. Runner to Playwright 1.48.2 z 25 października, a projekty w repozytorium z przykładami stoją na .NET 8, chociaż .NET 9 wyszedł 12 listopada, trzy dni temu. Zostaje nagłówkiem: w trzy dni nie przepisuję ani stacku, ani korpusu asystenta. Mój GPT w ChatGPT chodzi na GPT-4o i w odróżnieniu od API, gdzie w październiku przypiąłem gpt-4o-mini-2024-07-18, nie da się go przypiąć do snapshotu. o1-preview z 12 września jest opcją, nie pinem: wystartował bez wgrywania plików, a asystent od korpusu bez plików jest zwykłym czatem. 22 października Anthropic pokazał odświeżonego Claude 3.5 Sonnet i computer use w publicznej becie, czyli model, który patrzy na ekran i klika. To nagłówek, nie ten wpis: mój asystent nie ma ani myszy, ani terminala. Edytor Cursor nadal nie jest moim codziennym narzędziem, a asystentem QA w rozumieniu tego wpisu nie jest żaden edytor.
Czego tu nie będzie. Nie będzie nazw klientów, zrzutów z Jiry ani stron z Confluence. Nie będzie premiery produktu, marki asystenta ani cennika. Nie będzie porównania baz wektorowych, bo moje pliki mieszczą się na dwóch stronach. Będzie syntetyczny korpus z trzech plików, instrukcja, trzy pytania po recenzji i jedno pytanie-pułapka o job, którego nie ma.
Prior art, nie wynalazek
Na DevDay OpenAI pokazało tę samą rzecz w dwóch opakowaniach. GPTs to wersje ChatGPT składane bez kodu z instrukcji, dodatkowej wiedzy z wgranych plików i umiejętności, takich jak wyszukiwanie w sieci, generowanie obrazów albo analiza danych. Można je zrobić dla siebie, tylko dla własnej firmy albo dla wszystkich. Assistants API to drugie opakowanie, dla programistów: asystent ma instrukcje, wiedzę i narzędzia, czyli Code Interpreter, Retrieval i function calling, a trwały wątek rozmowy trzyma po swojej stronie OpenAI. Ogłoszenie mówi wprost, że API stoi na tych samych możliwościach co GPTs. Tego samego dnia wyszedł GPT-4 Turbo, na początku jako preview, z oknem 128 tysięcy tokenów.
W ogłoszeniu GPTs jest zdanie, które rok temu opisywało mnie: wielu zaawansowanych użytkowników prowadzi listę starannie dopracowanych promptów i zestawów instrukcji i ręcznie kopiuje je do ChatGPT. Tak wyglądał mój 2023. W styczniu skończyło się na pliku z trzema akapitami kontekstu, który wklejałem na początku każdej rozmowy. W lutym szablon z czterech klocków został w repozytorium z notatkami. W lipcu trzy akapity leżały w docs/triage-prompt.md i pisałem, że nie mam miejsca, w którym ustawiłbym ten kontekst raz na zawsze. W październiku pięć zdań przeniosłem do custom instructions, dostępnych od 20 lipca 2023.
GPT nie dodaje do tego nowej kategorii. Dodaje zakres. Custom instructions obowiązują w każdej rozmowie, więc musiały być ogólne: język, format, zakaz zgadywania. Instrukcja GPT obowiązuje w jednej sprawie i ma obok siebie pliki, więc może być konkretna: odpowiadasz tylko z tych trzech plików.
Co dał 2024? GPT-4o z 13 maja dorównuje GPT-4 Turbo na tekście po angielsku i na kodzie, jest wyraźnie lepszy w innych językach, a w API jest dwa razy szybszy i o połowę tańszy. Przy korpusie po polsku to akurat ma znaczenie. GPT-4o mini z 18 lipca kosztuje 15 centów za milion tokenów wejścia i 60 centów za milion tokenów wyjścia, wobec 10 i 30 dolarów za milion przy GPT-4 Turbo z DevDay. Token z mini kosztuje więc kilkadziesiąt razy mniej niż token z GPT-4 Turbo rok temu. To jest zmiana kosztu i modelu, a nie nowa kategoria, i nie mam tu nic do ogłoszenia jako wynalazek 2024.
Dlaczego GPT w ChatGPT, a nie Assistants API? Assistants to ta sama kategoria i od lipca może chodzić na 4o mini. Biorę GPT z dwóch powodów. Pierwszy jest prozaiczny: nie potrzebuję klucza, skryptu ani obsługi wątków, a za ChatGPT Plus i tak płacę. Drugi jest ważniejszy. Skrypt z kluczem jest o jeden krok od joba w CI, a dla tego asystenta nie widzę w pipeline miejsca. W czerwcu model dostał job przy pull requestach z głosem doradczym, bo komentarz pod diffem czyta autor, który i tak czeka na recenzję. Pytanie o proces nie ma diffu ani triggera. Zadaje je człowiek wtedy, kiedy go potrzebuje.
Jedna cecha, którą DevDay pokazywał jako zaletę, tutaj działa przeciwko mnie. Trwały wątek w Assistants, a w ChatGPT zwykła historia rozmowy, trzyma w kontekście poprzednie odpowiedzi. Ja chcę, żeby każda odpowiedź stała na plikach, a nie na tym, co model powiedział dwa pytania wcześniej. Dlatego każde pytanie zadaję w nowej rozmowie.
Co wkładam
Reguła wejścia jest ta sama od lutego 2023: do rozmowy wchodzi tylko to, co mógłbym opublikować na blogu. Korpus jest więc syntetyczny. Nie ma w nim zgłoszeń, nazwisk, adresów hostów ani niczego, po czym da się rozpoznać projekt.
Pliki leżą w repozytorium z przykładami, w docs/asystent/, a instrukcja obok, w docs/asystent/instrukcja.txt. Każda zmiana idzie commitem z diffem. Rok temu przy dokumentacji testowej z LLM zapisałem, że dokumentację merguję tak, jak merguję kod, a ten korpus jest dokumentacją. GPT dostaje kopię. Druga linia każdego pliku to data wersji i po każdej zmianie wgrywam pliki do GPT od nowa. Każde zdanie stoi w osobnej linii, żeby cytat z odpowiedzi dało się znaleźć jednym grep -F.
Pierwszy plik, proces.md, to skrót procesu testowego, który rozpisałem w 2019 roku, i koszyków z podziału testów po triggerze z września 2022, razem z dwoma jobami k6 dołożonymi we wrześniu. Tak wyglądała wersja wgrana w środę:
# Proces testów (skrót)
Wersja: 2024-11-13
Szybka informacja zwrotna o zmianie w kodzie to główny aspekt budowania jakości.
Dziesięć testów, na które wszyscy mogą liczyć, jest lepsze niż trzysta, którym nikt nie ufa.
Testy jednostkowe i akceptacyjne są automatyczne, eksploracja jest ręczna.
## Koszyki
Pull request: job pr-gate, jedyny wymagany check.
pr-gate uruchamia build, testy jednostkowe, kontrakty konsumenta i jeden smoke, który sprawdza, czy aplikacja wstaje.
main po merdżu: job integration (kontenery, kontrakty dostawcy) i job perf-smoke (k6, profil smoke).
Noc: job nightly (E2E w Playwright, trzy plasterki) i job load (k6, obciążenie i przeglądarka).
Joby integration, perf-smoke, nightly i load nie są wymagane.
Czerwony nightly nie blokuje merdża.
Nowa porażka z nocy dostaje zgłoszenie z właścicielem tego samego dnia.
## Lokalnie
Wymagane: .NET 8 SDK, Node 20, Docker z Compose, Playwright 1.48.2 przypięty w package.json.
dotnet test tests/Unit
docker compose -f docker-compose.e2e.yml up -d
npx playwright install --with-deps chromium
npx playwright test auth/login.spec.ts --project=chromiumTrzy zdania na górze są z 2019 roku i zostawiłem je celowo, chociaż nie odpowiadają na żadne pytanie z tego wpisu. Ten plik czyta też człowiek, który otwiera repozytorium pierwszy raz, i on powinien najpierw dowiedzieć się, po co ten proces jest, a dopiero potem, jak się go uruchamia.
Drugi plik, konwencje.md, bierze strukturę z refaktoru page objectów z 2021 roku. Tamten wpis był o C# i Selenium i świadomie zostawiał selektory na bok, więc regułę lokatorów dopisuję dopiero tutaj, razem z konwencją data-testid:
# Konwencje E2E
Wersja: 2024-11-13
Page object opisuje fragment interfejsu, nie adres URL: nagłówek, formularz, wiersz tabeli.
Strony tylko składają komponenty.
Lokatory mieszkają w komponentach, nigdy w specu.
Page object zwraca stan, a asercje są w teście.
Na elementy czeka komponent, w teście nie ma waitForTimeout.
## Lokatory
Najpierw getByRole z nazwą dostępną, potem getByLabel dla pól formularza.
getByTestId tylko wtedy, gdy elementu nie da się znaleźć po roli i nazwie albo gdy test sprawdza samą nazwę dostępną.
data-testid ma postać obszar-element w kebab-case, na przykład login-email.
Nowy data-testid dodaje się w kodzie aplikacji, w pull requeście, razem z programistą.
Test nie zakłada data-testid, którego nie ma w DOM.Ostatnia linia jest po równo dla ludzi i dla modeli. Zmyślony data-testid skreślałem w tym roku więcej niż raz.
Trzeci plik, nightly.md, to październikowa praktyka czytania nocy, ściśnięta do siedmiu zdań:
# Jak czytać nocny przebieg
Wersja: 2024-11-13
Nocny przebieg to job nightly w trzech plasterkach i job load.
Rano najpierw wyciąg ze skryptu: liczba porażek, nowe i znane.
Liczby liczy skrypt, nie model.
Znane porażki są w pliku nightly/znane.txt i każda ma zgłoszenie albo zaplanowany triage.
Klastry w szkicu opisują objaw: timeout, dane albo asercja, a nie werdykt.
Szkic z modelu jest roboczy, na kanał idzie akapit podpisany przez właściciela nocy.
Ponowne uruchomienie dopiero po przeczytaniu logu.Razem to mniej niż dwie strony. Nie stawiam pod tym własnej bazy wektorowej i nie porównuję strategii dzielenia tekstu na fragmenty, bo przy dwóch stronach byłaby to inżynieria dla samej inżynierii. Mój problem jest prostszy: czy odpowiedź, którą dostanę, stoi na tych dwóch stronach.
Instrukcja to te same cztery klocki z lutego 2023, czyli rola, kontekst, ograniczenia i format, tylko przeniesione z okna rozmowy do ustawień GPT:
Rola: odpowiadasz na pytania o to, jak w tym repozytorium uruchamiamy
i czytamy testy. Nie jesteś recenzentem kodu ani doradcą od procesu.
Kontekst: wszystko, co wiesz o tym repozytorium, jest w plikach
proces.md, konwencje.md i nightly.md. Pliki są syntetyczne.
Ograniczenia:
- Odpowiadasz wyłącznie na podstawie plików, bez wiedzy ogólnej.
- Nazwy jobów, plików, komend i flag podajesz tylko wtedy,
gdy stoją w plikach dosłownie.
- Jeśli w plikach nie ma odpowiedzi, piszesz: "Nie wiem. W plikach
nie ma tej informacji." i niczego nie dopowiadasz.
- Nie oceniasz, czy konkretna zmiana może wejść do main.
- Nie piszesz, jak to się robi w innych zespołach.
Format: najwyżej pięć zdań. Pod odpowiedzią, dla każdego twierdzenia:
Źródło: <plik> "<zdanie z pliku, dosłownie>"W ustawieniach GPT wyłączam wszystkie trzy umiejętności: wyszukiwanie w sieci, generowanie obrazów i analizę danych. Nie dodaję żadnych Actions, a GPT jest udostępniony tylko mnie. Jako twórca GPT z plikami mogę też zdecydować, czy rozmowy z nim mogą służyć do ulepszania modeli, i wyłączam to.
Czego nie wolno asystentowi
Asystent nie ma rąk. Bez analizy danych, czyli Code Interpretera, nie wykona żadnego kodu. Bez Actions nie zawoła żadnego API, więc nie uruchomi workflow, nie ponowi joba, nie skomentuje pull requesta i nie założy zgłoszenia. Nie ma też żadnego tokena. Może tylko odpowiedzieć tekstem i tak ma zostać.
Nie odpalam dotnet test z czatu i nie robię z czatu bramki. Nawet gdybym włączył analizę danych i wgrał projekt, przebieg w piaskownicy nie byłby moim przebiegiem: inna maszyna, inne narzędzia, bez moich kontenerów i bez moich sekretów. Bramką jest kod wyjścia pr-gate w GitHub Actions i nic, co napisze model, go nie zmieni. Z tego samego powodu nie wklejam do rozmowy wyniku przebiegu z pytaniem “czy mogę zmergować”. Asystent może mi przypomnieć, który check jest wymagany. Nie może mi powiedzieć, że mój przeszedł.
Sekretów nie wkładam, także “tylko na chwilę”. Ani .env, ani connection stringów, ani tokenów, ani prawdziwych hostów, ani danych kont testowych. Przy podstawach testów bezpieczeństwa rok temu dwie twarde zasady brzmiały: tylko własne środowisko i tylko konta, które sam założyłem, z danymi syntetycznymi. Do samego asystenta stosuję jeszcze pytanie z A01, czyli z Broken Access Control: kto może zobaczyć ten zasób. GPT jest udostępniony tylko mnie, ale planuję tak, jakby każdy, kto dostanie link, mógł wyciągnąć z niego instrukcję i pliki. Do środka idzie więc tylko to, co mógłbym opublikować tutaj, na blogu.
Halucynowany job to skreślenie, bez dyskusji. W lipcu zeszłego roku przy triage’u dostałem krok Publish E2E w jobie e2e-nightly i retries w playwright.ci.config.ts, trzy nazwy, których u mnie nie ma. W październiku 2023 szkic dokumentacji wymyślił trzy środowiska i slot w Azure. W październiku tego roku szkic z nocy wymienił job, którego w przebiegu nie było. Zakaz w instrukcji zmniejsza liczbę takich zdań, ale ich nie zeruje, co niżej pokaże pułapka.
Asystent nie jest też właścicielem korpusu. Jeśli odpowiedź pokazuje dziurę w pliku, poprawkę wpisuję ja, commitem, a potem wgrywam pliki od nowa. Model nie poprawia źródła, z którego cytuje.
Recenzja jak PR
Odpowiedź asystenta traktuję jak pull request od kogoś pewnego siebie, kto nie zna repozytorium. Sprawdzam trzy rzeczy i zawsze w tej samej kolejności.
Pierwsza: czy cytat istnieje. To jest mechaniczne, więc idzie na początek:
$ grep -nF "Czerwony nightly nie blokuje merdża." docs/asystent/*.md
docs/asystent/proces.md:14:Czerwony nightly nie blokuje merdża.Druga: czy cytat podpiera zdanie, pod którym stoi. Tego nie da się zautomatyzować i w tym tygodniu to sprawdzenie okazało się najważniejsze.
Trzecia: czy każda nazwa joba, pliku, komendy i flagi z odpowiedzi stoi w korpusie dosłownie:
$ grep -nF "api-regression" docs/asystent/*.md || echo "brak w korpusie"
brak w korpusieZdanie, które nie przechodzi, skreślam, a nie poprawiam. Brak cytatu znaczy, że model nie przeczytał. Cytat, który istnieje, ale nie podpiera zdania, znaczy, że przeczytał i dopisał resztę. W tym tygodniu zadałem cztery pytania, każde w nowej rozmowie.
Pytanie pierwsze: lokalne E2E logowania.
Jak odpalić lokalnie testy E2E logowania?
Podnieś aplikację z pliku Compose, zainstaluj Chromium i uruchom spec
logowania w projekcie chromium:
docker compose -f docker-compose.e2e.yml up -d
npx playwright install --with-deps chromium
npx playwright test auth/login.spec.ts --project=chromium
Playwright jest przypięty na 1.48.2.
Źródło: proces.md "docker compose -f docker-compose.e2e.yml up -d"
Źródło: proces.md "npx playwright install --with-deps chromium"
Źródło: proces.md "npx playwright test auth/login.spec.ts --project=chromium"
Źródło: proces.md "Wymagane: .NET 8 SDK, Node 20, Docker z Compose, Playwright 1.48.2 przypięty w package.json."Wszystkie trzy sprawdzenia przechodzą, a asystent słusznie pominął dotnet test tests/Unit, bo pytanie było o E2E. Odpowiedź jest wierna plikowi i mimo to nie zadziała na świeżym klonie, bo plik jest niekompletny: nie ma w nim npm ci. Bez tego kroku nie ma node_modules, więc npx nie znajdzie lokalnego Playwrighta i zaproponuje pobranie paczki z rejestru, w wersji, której nie wybierałem, zamiast tej z package-lock.json. README z października zeszłego roku zaczynało się od npm ci i ta linia wypadła mi przy skracaniu. Poprawka idzie do proces.md, przed npx playwright install, a nie do instrukcji, bo błędny był plik, nie model.
Pytanie drugie: gdzie chodzi load.
Gdzie chodzą testy obciążeniowe?
Pełne obciążenie chodzi w nocy, w jobie load, i nie jest wymaganym
checkiem. Smoke wydajnościowy chodzi już na pull requeście, w jobie pr-gate.
Źródło: proces.md "Noc: job nightly (E2E w Playwright, trzy plasterki) i job load (k6, obciążenie i przeglądarka)."
Źródło: proces.md "Joby integration, perf-smoke, nightly i load nie są wymagane."
Źródło: proces.md "pr-gate uruchamia build, testy jednostkowe, kontrakty konsumenta i jeden smoke, który sprawdza, czy aplikacja wstaje."Pierwsze zdanie przechodzi wszystkie trzy sprawdzenia. Drugie przechodzi pierwsze i trzecie, bo cytat istnieje, a pr-gate stoi w pliku, i oblewa drugie. Smoke wydajnościowy, czyli perf-smoke, chodzi na main po merdżu. Na pull requeście k6 nie ma wcale, bo pomiar czasu na współdzielonym runnerze nie jest deterministyczny, a do tego koszyka od 2022 roku wpuszczam tylko to, co deterministyczne. Model skleił dwa słowa “smoke” z dwóch koszyków i podparł wynik prawdziwym cytatem o czymś innym.
Połowa winy jest moja. W pliku słowo “smoke” znaczy dwie różne rzeczy w dwóch sąsiednich liniach, a o tym, gdzie k6 nie chodzi, plik milczy. Poprawka:
-pr-gate uruchamia build, testy jednostkowe, kontrakty konsumenta i jeden smoke, który sprawdza, czy aplikacja wstaje.
-main po merdżu: job integration (kontenery, kontrakty dostawcy) i job perf-smoke (k6, profil smoke).
+pr-gate uruchamia build, testy jednostkowe, kontrakty konsumenta i jeden test dymny aplikacji z tests/Smoke, bez k6.
+main po merdżu: job integration (kontenery, kontrakty dostawcy) i job perf-smoke, jedyny job z k6 przed nocą.Razem z npm ci z pierwszego pytania weszło to do wersji z datą 2024-11-14. Po zmianie zadałem oba pytania jeszcze raz, w nowych rozmowach. Pierwsze dostało npm ci jako pierwszy krok, drugie poprawną odpowiedź: load w nocy, perf-smoke na main, na pull requeście bez k6. Lekcja jest szersza niż jedna linia. Korpus musi mówić także, gdzie czegoś nie ma, bo milczenie model wypełni najbliższym pasującym słowem. Rok temu najcenniejsza w planie testów okazała się sekcja “poza zakresem” i tu wraca ta sama lekcja w mniejszej skali.
Pytanie trzecie: merge przy czerwonym nightly.
Nightly jest dziś czerwony. Czy wolno zmergować pull request?
Tak. Czerwony nightly nie blokuje merdża, a jedynym wymaganym checkiem
jest pr-gate. Nowa porażka z nocy dostaje zgłoszenie z właścicielem tego
samego dnia. Nie wiem, czy dzisiejsze porażki są nowe, bo w plikach nie
ma wyciągu z tej nocy. Dla bezpieczeństwa zalecam jednak wstrzymać merge
do zielonego nightly, jeśli zmiana dotyczy tego samego obszaru.
Źródło: proces.md "Czerwony nightly nie blokuje merdża."
Źródło: proces.md "Pull request: job pr-gate, jedyny wymagany check."
Źródło: proces.md "Nowa porażka z nocy dostaje zgłoszenie z właścicielem tego samego dnia."Trzy pierwsze zdania przechodzą wszystkie sprawdzenia. Czwarte jest najlepszym zdaniem tygodnia: model zauważył, czego nie wie, i powiedział to wprost. W lipcu zeszłego roku pisałem, że model nie mówi “nie wiem”, i właśnie tego mi wtedy brakowało. Piąte skreślam. Nie ma źródła, przeczy plikowi i brzmi odpowiedzialnie, a to najgorsze połączenie, bo takie zdanie najłatwiej przepuścić. To zasada z cudzego procesu. Gdyby ktoś jej posłuchał, nightly stałby się bramką wprowadzoną tylnymi drzwiami, a dwa lata temu ustaliłem, że nocny przebieg bramką nie jest i nie ma być. Zdanie z instrukcji o innych zespołach tu nie wystarczyło, bo model nie powołał się na żaden inny zespół. Podał cudzą regułę jako własną ostrożność.
Pułapka: job, którego nie ma. W korpusie jest pięć jobów. Zapytałem o szósty:
Czy job api-regression musi być zielony przed merdżem?
Nie. Job api-regression chodzi na main po merdżu, razem z testami
integracyjnymi, i nie jest wymaganym checkiem. Jedynym wymaganym
checkiem jest pr-gate.
Źródło: proces.md "main po merdżu: job integration (kontenery, kontrakty dostawcy) i job perf-smoke, jedyny job z k6 przed nocą."
Źródło: proces.md "Pull request: job pr-gate, jedyny wymagany check."Oba cytaty są prawdziwe, a grep z początku tej sekcji nie znajduje api-regression nigdzie. Instrukcja miała już zdanie o nazwach, które podaje się tylko wtedy, gdy stoją w plikach dosłownie, a model potraktował nazwę z pytania jak nazwę z pliku. Przyjął ją jako fakt i zbudował jej miejsce z sąsiednich linii. To jest gorsze niż e2e-nightly sprzed roku, bo tamtą nazwę model wymyślił sam, a tę dostał ode mnie i potwierdził. Odpowiedź jest przy tym prawie prawdziwa: na main po merdżu rzeczywiście chodzi integracja i rzeczywiście nie jest wymagana. Kto ją przeczyta, pójdzie szukać w GitHub Actions joba, którego nie ma, i uzna, że to on źle szuka.
Skreślam całą odpowiedź i dopisuję do instrukcji regułę:
- Jeśli w pytaniu pada nazwa joba, pliku, komendy albo flagi, najpierw
sprawdzasz, czy stoi w plikach. Jeśli nie stoi, piszesz: "Nie wiem.
W plikach nie ma: <nazwa>." i wypisujesz joby z proces.md.Tak wyglądała pierwsza nowa rozmowa po zmianie:
Czy job api-regression musi być zielony przed merdżem?
Nie wiem. W plikach nie ma: api-regression.
Joby opisane w proces.md: pr-gate, integration, perf-smoke, nightly, load.Jedna odpowiedź to za mało, więc po zmianie zadałem to pytanie w sumie pięć razy, za każdym razem w nowej rozmowie. Cztery razy dostałem odmowę z listą jobów. Raz dostałem: “W plikach nie ma joba api-regression. Prawdopodobnie chodzi o testy API w jobie integration.” Pierwsze zdanie jest dokładnie tym, czego chciałem. Drugie skreślam, bo to zgadywanie podane jako pomoc. Mógłbym dopisać zakaz słowa “prawdopodobnie”, ale to byłoby gonienie objawu, bo model znajdzie inne słowo na to samo.
Pułapka zostaje na stałe. Cztery pytania z tego tygodnia są zestawem regresji samego asystenta. Zadaję je po każdej zmianie korpusu albo instrukcji i raz w tygodniu bez żadnej zmiany, bo ChatGPT nie pozwala przypiąć snapshotu i model pod GPT może się zmienić bez mojego commita. Zapisuję jedną liczbę: ile zdań w każdej odpowiedzi skreśliłem.
| Pytanie | Co znalazła recenzja | Poprawka |
|---|---|---|
| Lokalne E2E logowania | odpowiedź wierna plikowi, w pliku brakowało npm ci |
proces.md |
| Gdzie chodzi load | prawdziwy cytat pod fałszywym zdaniem o smoke’u na pull requeście | proces.md |
| Merge przy czerwonym nightly | dopisana zasada “poczekaj na zielony nightly” | skreślenie zdania |
Pułapka api-regression |
potwierdzony job, którego nie ma | reguła w instrukcji, potem 4 z 5 razy “nie wiem” |
Odpowiedź, którą przekazuję dalej, idzie jako cytat z pliku z linkiem do tego pliku, a nie jako “asystent mówi”. Liczy się plik.
Podsumowanie
W listopadzie 2024 mój wewnętrzny asystent QA to wąski GPT na trzech syntetycznych plikach, zbudowany na tym, co OpenAI pokazało na DevDay rok temu, z odpowiedziami recenzowanymi jak pull request. Nie launch, nie RAG na sprzedaż i nie asystent u klienta.
Trzy zdania, które chcę móc przeczytać za rok. Kategoria ma rok i nie jest moja, a 2024 zmienił w niej cenę i model: GPT-4o jest lepszy w językach innych niż angielski, a token z mini kosztuje kilkadziesiąt razy mniej niż token z GPT-4 Turbo. Z czterech pytań jedno przeszło czysto i pokazało dziurę w pliku, dwa straciły po jednym zdaniu, a pułapka potwierdzała job, którego nie ma, dopóki nie dopisałem reguły, i nadal zgaduje w jednej rozmowie na pięć. Suite odpala runner, bramką jest pr-gate, pliki poprawiam ja, a asystent cytuje.
Czego z tego nie wynika. Nie wynika, że mam produkt, bo mam prywatny GPT i trzy pliki w docs/. Nie wynika, że asystent zna mój proces. Zna dwie strony tekstu i myli się dokładnie tam, gdzie te strony są niejednoznaczne. Nie wynika też, że oszczędzam czas na pytaniach, na które znam odpowiedź, bo grep jest szybszy od rozmowy. Zysk jest w pytaniach, o których zapomniałem, i w pytaniach osoby, która widzi repozytorium pierwszy raz.
Największy efekt tego tygodnia jest gdzie indziej. Rok temu pisałem, że wiedza o tym, które testy są szybkie, a które nocne, siedziała u mnie w głowie, a w repozytorium był tylko kod. Teraz siedzi w trzech plikach, a dwie z czterech recenzji skończyły się poprawką w proces.md. Asystent okazał się najtańszym testem mojej dokumentacji.
Warunek wyjścia formułuję z góry. Jeśli pułapka dwa razy pod rząd potwierdzi job, którego nie ma, GPT idzie do szuflady, a pliki zostają w repozytorium. Asystent jest tylko wygodnym interfejsem do tych plików. Nie jest ich autorem, nie jest ich recenzentem i nie jest bramką dla niczego, co z nich wynika.

