Przejdź do treści
ArchXS

Realizacje · Produkty własne, open source

Trener, który podaje źródła i odmawia zmyślania reszty

Jak zbudować silnik dziedzinowy, w którym każda rekomendacja jest identyfikowalna co do publikacji, i który odmawia policzenia trzech liczb najchętniej kupowanych na rynku, bo dowody ich nie bronią.

Każda reguła z ID źródła, ~60 pozycji
Identyfikowalność
CLI, 19 narzędzi MCP, plugin Claude Code i Codex na jednym zestawie handlerów
Interfejsy
641 testów, backtest na 50 realnych planach
Dowody
Trener, który podaje źródła i odmawia zmyślania reszty
Rys. 01Produkty własne, open source

Kontekst

trainctl to trener biegowy mieszkający w terminalu. Plan treningowy jest plikiem YAML w repozytorium git, każdą jednostkę można zapytać, dlaczego istnieje, a ten sam silnik wystawiony jest agentowi jako narzędzia MCP. Nie ma konta, nie ma bazy i nie ma backendu: pliki w katalogu roboczym są całym systemem, więc historia czyjegoś treningu jest jego historią w git. Opublikowany na npm na licencji MIT.

Problem

Porada treningowa to dziedzina o nietypowej własności: rekomendacje są pewne siebie, szeroko powtarzane i często nie mają oparcia w literaturze, do której się odwołują. Stosunek obciążenia ostrego do przewlekłego, reguła dziesięciu procent, wskaźniki gotowości liczone z nocnej zmienności rytmu serca, wszystko to sprzedaje się jako pomiar. Budowa trenera oznacza rozstrzygnięcie przy każdej z tych rzeczy, czy wydać liczbę, której użytkownik oczekuje, czy powiedzieć wprost, że nie da się jej policzyć.

Drugi problem pojawia się, kiedy do takiej dziedziny dokłada się model językowy. Model zapytany o tydzień treningowy wyprodukuje tempa, objętości i uzasadnienia z pełną płynnością, bez żadnej możliwości sprawdzenia przez czytelnika, które z nich skądkolwiek pochodzą. Efekt brzmi jak produkt trenerski, nie wykonując jego pracy.

Podejście

Silnik jest deterministyczny, a reguły mają przypisane źródła. Każda reguła niesie identyfikator wskazujący do dokumentu podstaw z około sześćdziesięcioma pozycjami, a polecenie why cytuje je dla dowolnej jednostki. Tam, gdzie wartość nie ma źródła, jest w kodzie oznaczona jako decyzja inżynierska, a nie przebrana za naukę. Tam, gdzie dowody są naprawdę słabe, why to mówi: trening siłowy uzasadniam ekonomią biegu, a nie profilaktyką kontuzji, bo metaanaliza na biegaczach wyszła nieistotna statystycznie.

Trzech rzeczy silnik odmawia wprost, każdej z udokumentowanym powodem. Nie liczy ryzyka kontuzji, bo używane do tego metryki obciążenia nie wytrzymują konfrontacji z literaturą. Nie liczy gotowości ze zmienności rytmu serca, bo wielkość efektu jest mała, a przedział ufności przechodzi przez zero; strefy biorą się z wyników startowych. I nigdy nie przepisuje po cichu własnego planu: adapt zwraca propozycje, zastosowanie każdej najpierw pokazuje dokładne zmiany, a wynik jest diffem, który zatwierdził właściciel planu. Na krawędziach też raczej odmawia, niż zgaduje: poniżej czterech tygodni historii nie wyprowadzi profilu, a bez wyniku startowego nie skalibruje stref z odczytów zegarka.

Korpus pięćdziesięciu realnych planów trenerskich, około tysiąca trzystu dni od jednego trenera, stoi obok badań jako osobne źródło i służy stylowi, a nie fizjologii. Zapracował na swoje miejsce, obalając założenia: trener przez tysiąc dwieście dni ani razu nie zaplanował sprawdzianu na czas, co zmieniło sposób kalibracji, a zmierzenie rzeczywistych wybiegań usunęło karę w solverze, która spychała generowane plany od tego, co trener naprawdę robi. Korpus zawiera dane osobowe i nie jest dystrybuowany.

Integracja z agentem wynika z tego samego podziału. Dziewiętnaście narzędzi MCP stoi na tych samych handlerach, które wywołuje CLI, więc terminal i agent nie mogą się rozjechać, a plik instrukcji pisany przy inicjalizacji każe agentowi pytać przed przegenerowaniem planu i nigdy nie wymyślać liczb. Ten sam silnik jest też pluginem Claude Code i Codex, ze skillami trenera generowanymi z tego samego tekstu instrukcji i hookiem, który po każdej edycji planu uruchamia jego lint. Model wnosi rozmowę i kontekst, co mówi kalendarz, dlaczego tydzień wypadł; silnik wnosi każdą liczbę.

Obietnica braku cichych zmian okazała się mieć dziurę, a jej znalezienie wymagało uruchomienia każdego polecenia na świeżym katalogu. Przegenerowanie planu, udokumentowany krok po dopisaniu nowego wyniku startowego, po cichu porzucało przypięte dni, ręczne edycje i zatwierdzone adaptacje. Od wersji 0.4 regeneracja dotyka tylko przyszłości, zachowuje wszystko, o czym zdecydował właściciel planu, i odmawia nadpisania pliku, którego nie potrafi odczytać, a diff pokazuje dokładnie to, co regeneracja by zmieniła.

Rezultat

Produkt jest publiczny na npm na licencji MIT, ma ponad sześćset testów i backtest na korpusie trenerskim. Działa jako CLI, jako serwer MCP dla dowolnego klienta agentowego i jako plugin agenta. Planuje z uwzględnieniem tygodniowego budżetu czasu, tygodni po starcie i treningu bez startu docelowego, synchronizuje się z platformą treningową albo czyta wyeksportowane pliki z zegarka bez żadnego konta, i eksportuje na zegarki, do kalendarza i na papier. Repozytorium: github.com/dithiothreitol/trainctl.

Koszt tych odmów warto nazwać uczciwie: konkurent, który wyda wskaźnik ryzyka kontuzji i procent gotowości, wypadnie lepiej w porównaniu funkcji. Przyjęte tu stanowisko brzmi, że liczba, której nikt nie potrafi obronić, jest gorsza niż jej brak, a ten brak jest opisany, więc użytkownik może się spierać z uzasadnieniem, a nie z pominięciem.

Wnioski

To ten sam wzorzec, który stosuję w systemach regulowanych, dojście do niego tylko biegnie z przeciwnej strony. Model bierze intencję i kontekst; wszystko, na czym czytelnik mógłby oprzeć działanie, produkuje deterministyczny kod i opatruje cytowaniem. To, że dziedziną jest tu tydzień treningowy, a nie deklaracja podatkowa, zmienia konsekwencje, a nie architekturę.

Drugi wniosek: odmowa policzenia jest wynikiem projektowym, a nie luką. Lista rzeczy, których system nie wyprodukuje, z podanymi powodami, mówi użytkownikowi o jego osądzie więcej niż lista funkcji.

← Wróć do realizacje