My Architect, część 10: pamięć repozytorium — graf kodu, któremu nie wolno wierzyć na słowo
To dziesiąty artykuł serii o My Architect i ciąg dalszy dziewiątego — historii skilla recursive-context, który uczy agenta dyscypliny pracy z danymi większymi niż okno kontekstowe. Tym razem: cztery wydania w jeden dzień (v1.14.0 → v1.16.1), które dały agentowi trwały graf kodu, oraz kontrolowany eksperyment, który zmierzył jego realną wartość zamiast powtarzać cudzy marketing. Wszystkie liczby z naszych przebiegów pochodzą z telemetrii realnych uruchomień z 2026-07-03; fakty o Graphify — z jego README i żywej instalacji; szacunki są oznaczone jawnie. Nic nie jest zmyślone.
Prolog. Agent z amnezją
Agent LLM nie ma pamięci między sesjami. Każda nowa rozmowa o twoim repozytorium zaczyna się od czystej kartki: agent znów grepuje, znów czyta te same pliki, znów buduje w głowie tę samą mapę — i wyrzuca ją na końcu sesji. Zmierzyliśmy to na własnym repozytorium: zebranie faktów o dobrze znanej bazie kodu kosztowało ~77 tysięcy tokenów i 20 wywołań narzędzi — i tak każdą sesję, w kółko.
Osobna bolączka to pytania w rodzaju „co się zepsuje, jeśli zmienię sygnaturę tej funkcji?". Odpowiedź wymaga znajomości wszystkich konsumentów symbolu. Grep ich znajdzie, ale za cenę pełnego przejścia; a co ważniejsze — agent nie ma miejsca, w którym ta wiedza mogłaby przeżyć sesję.
Wokół tego bólu wyrósł już rynek narzędzi „kod-jako-graf" — z hasłami w stylu „90% oszczędności tokenów" i „agent 500 razy mądrzejszy". Nam nie były potrzebne hasła, tylko odpowiedź na dwa pytania: jakie klasy błędów to usuwa i gdzie oszczędność jest prawdziwa, a gdzie to populizm. Spoiler: na końcu artykułu — tabela ze zmierzonymi liczbami.
Rozdział 1. Nie budować swojego: Graphify
Narzędzie do trwałej pamięci o kodzie już istnieje — Graphify (open source, MIT, ~77k gwiazdek): parsuje kod tree-sitterem w pełni lokalnie (36 języków, bez wywołań API i telemetrii), składa graf symboli i powiązań do graphify-out/graph.json, obok kładzie interaktywną mapę HTML i gotowy tekstowy raport o repozytorium. Świeżość indeksu utrzymują git-hooki; do pracy zespołowej jest merge-driver, żeby graf dało się commitować bez konfliktów.
Świadomie nie zaczęliśmy budować własnego silnika grafowego. Zamiast tego nauczyliśmy nasz skill dyscypliny dekompozycji rozpoznawać leżący w repozytorium indeks i poprawnie z niego korzystać. Cała funkcja to markdownowy przewodnik z regułami plus kilka linijek-drogowskazów: plugin nie zyskał ani jednej twardej zależności. Nie ma grafu — wszystko działa jak dawniej; nie ma grafu, a zadanie jest ciężkie — agent raz zaproponuje właścicielowi instalację i budowę (i zrobi to sam po „tak"; ciche instalacje są zakazane).
Jak tanie jest to w praktyce: na naszym repozytorium (351 plików) budowa grafu zajęła sekundy i dała 2655 węzłów, 4948 krawędzi i 153 automatycznie znalezione klastry — 6.1 MB na dysku, zero kosztów API. Jedno zapytanie po tym wygląda tak:
$ graphify explain "getBlockers"
Node: getBlockers() Source: src/hierarchy/model.ts L419 Degree: 9
<-- NodePropertiesPanel.tsx [imports]
<-- validator.ts [imports]
<-- validateHierarchy() [calls]
...
Sekunda — i agent ma wszystkich konsumentów funkcji ze ścieżkami. To, co wcześniej wymagało pełnego przejścia po repozytorium, stało się zapytaniem. Ważny szczegół dla całej dalszej rozmowy o ekonomii: sam graf w ogóle nie zużywa tokenów — i budowa, i zapytania to lokalne narzędzia, a nie wywołania modelu.
Rozdział 2. Reguła, dla której to wszystko powstało
Indeks to cache, a cache umie kłamać: zostaje w tyle za kodem między przebudowami. Dlatego w centrum funkcji nie stoją komendy, lecz trzy reguły:
- Graf to nawigacja i kandydaci, NIE źródło faktów. Odpowiedź grafu mówi, GDZIE patrzeć; faktem staje się tylko to, co potwierdził żywy plik. Kontrakt faktów (
{twierdzenie, ścieżka-dowód, pewność}) nie zmienił się ani na jotę. - Freshness-check jest obowiązkowy. Pierwszym krokiem agent porównuje czas budowy indeksu z czasem ostatniego commita. Przeterminowany — powiedzieć głośno, zaproponować właścicielowi przebudowę (a jeśli git-hooka nie ma — raz zaproponować też hooka, żeby przeterminowanie nie powtarzało się co sesję), kandydatów oznaczać „według nieaktualnego indeksu".
- Verify — oszczędnie, regionami. Kandydat przychodzi ze
ścieżką:linią— weryfikacja czyta ±30 linii wokół, a nie cały plik. Bez tej reguły weryfikacja zjada wszystko, co graf zaoszczędził na wyszukiwaniu.
Rozdział 3. Jak to testowaliśmy
Skill to tekst, który ma zmieniać zachowanie modelu. Tekstu nie skompilujesz i unit-testem nie pokryjesz, dlatego weryfikacja jest warstwowa, a każda warstwa łapie swoją klasę błędów. Żelazna zasada wszystkich wydań: testy pisze się przed poprawkami, a najpierw utrwala się, „jak agent myli się bez nich".
Warstwa 1 — triggery: czy skill załaduje się sam. Sędziowie widzą tylko nazwę i opis skilla (dokładnie to, co widzi Claude Code przy wyborze) plus zapytanie; trzech niezależnych sędziów na case; w zestawie — pozytywy w dwóch językach, podstępne prawie-trafienia („zmień nazwę zmiennej" — wygląda jak pytanie o impact, ale skill ma milczeć) i cross-check, że nowy skill nie kradnie triggerów sąsiedniego. Finalny przebieg: 30/30 głosów dokładnie według klucza.
Warstwa 2 — zachowanie: czy dyscyplina jest przestrzegana. Wykonawca czyta skill z dysku i pisze plan według scenariusza; niezależny grader linijka po linijce porównuje go z zapisanymi wcześniej twierdzeniami. Każdy scenariusz uderza w konkretny sposób zepsucia się: świeży graf → zapytania przed grepem, ale fakty z plików; przeterminowany → zauważyć i nie ufać; grafu nie ma → zaproponować raz i nie instalować po cichu.
Warstwa 3 — złośliwe review samego tekstu. Osobny recenzent nie czyta skilla uprzejmie — on go wykonuje: uruchamia shellowe snippety na wejściach brzegowych, podnosi dockera, sprawdza każdą nazwę komendy z README upstreamu.
Warstwa 4 — przebiegi live: prawdziwa instalacja (za zgodą), prawdziwy graf, prawdziwy agent na prawdziwym repozytorium.
Warstwa 5 — kontrolowany eksperyment (rozdział 5): gdy jakościowe sprawdzenia są zaliczone, zostaje pytanie „a ile to kosztuje?" — i na nie odpowiada tylko A/B z telemetrią.
Rozdział 4. Co złapaliśmy przed eksperymentem. Cztery historie
Pułapka warunkowego routingu. Dodaliśmy gałąź „grafu nie ma → zaproponuj właścicielowi budowę" — a review pokazało, że jest nieosiągalna: wszystkie drogowskazy prowadzące agenta do przewodnika grafu zaczynały się od słów „jeśli graf JEST". Agent bez grafu po prostu nie otworzyłby pliku z propozycją. Klasyczny błąd: nowa gałąź istnieje, ale wszystkie drogi do niej są bramkowane na stan, w którym nie jest potrzebna.
Bug, który żyłby tylko na Linuksie. Snippet sprawdzania świeżości używał macOS-owej składni stat z fallbackiem na wariant GNU. Recenzent podniósł dockera i pokazał: w GNU stat ten sam flag znaczy co innego — komenda „pomyślnie" drukuje śmieci, a bramka wiecznie odpowiada STALE. Na każdej maszynie linuksowej graph-first nie włączyłby się nigdy, po cichu.
Upstream wie lepiej. Radziliśmy chować graphify-out/ w .gitignore — a README Graphify rekomenduje zespołom commitowanie grafu (wspólna mapa; konflikty rozwiązuje ich merge-driver). Nasza rada po cichu wyrzucała scenariusz zespołowy; teraz propozycja uczciwie daje wybór. Przy okazji wyszło, że pakiet na PyPI nazywa się graphifyy — z dwoma „y".
Wykonawca, który nie uwierzył warunkom zadania. Scenariusz testu behawioralnego twierdził „graf jest świeży" — agent sprawdził mtime naprawdę, odkrył, że graf jego realnego środowiska się przeterminował (nasze własne commity zdążyły go wyprzedzić), i obsłużył rozbieżność według przepisu. Dyscyplina „nie ufaj, sprawdzaj" zadziałała nawet przeciw tekstowi zadania.
Osobnym podejściem przeprowadziliśmy audyt „na co agent sam nie wpadnie" — i zamknęliśmy trzy luki: pytania o impact dostały własne wejście do skilla (wcześniej „co się zepsuje, jeśli zmienię X?" poza trackowanym zadaniem nie prowadziło do grafu w ogóle), gotowy raport GRAPH_REPORT.md jest teraz czytany jako pierwszy przy audycie (podsumowanie jest już policzone — głupio zbierać je zapytaniami), a przy przeterminowanym grafie bez hooka agent proponuje hooka.
Rozdział 5. Eksperyment: mierzymy sami, a nie cytujemy marketing
Projekt. Nieznane obu grupom duże repozytorium — NestJS, 2125 plików (o rząd wielkości więcej niż nasze). Dwa identyczne klony: ramię A — z grafem (12309 węzłów, 22895 krawędzi, 738 klastrów; zbudowany tree-sitterem w sekundy, $0), ramię B — bez. Trzy klasy pytań, prompty identyczne co do ścieżki, o grafie — ani słowa (ramię A ma obowiązek znaleźć go samo, według dyscypliny skilla), jeden model (Sonnet), jeden kontrakt wyjścia (≥8 faktów ze ścieżkami + uczciwe pokrycie + dziennik wszystkich wywołań). Metryki — tokeny i wywołania z telemetrii, jakość — liczba faktów i wyrywkowa weryfikacja ścieżek.
Wyniki:
| Klasa pytania | A (graf) | B (bez grafu) | Delta |
|---|---|---|---|
| Impact: „konsumenci ModuleRef, co się zepsuje przy zmianie get()" | 71.4k tokenów · 26 wywołań · solo · 12 faktów | 71.9k · 31 wywołań · solo · 14 faktów | parytet (A: −16% wywołań i czasu) |
| Zrozumienie: „HTTP-request lifecycle end-to-end" | 75.9k · solo · 13 faktów | 56.1k na wierzchu + 205.8k w ukrytych sub-agentach (zmierzone) ≈ 262k · 21 faktów | graf tańszy ~3.5× (−71%) |
| Mapa: „packages/core: moduły, API, ryzyka" | ≈445k (8 sub-agentów, szacunek*) · 24 fakty | ≈500k (9 sub-agentów, szacunek*) · 25 faktów | ~parytet |
\* Szacunek kosztu zagnieżdżonego jest skalibrowany na jedynej dokładnie zmierzonej komórce (Q2-B: 205 759 tokenów na 5 agentów) i oznaczony jako szacunek; kierunek błędu jest nieznany.
Wniosek 1: oszczędność jest prawdziwa, ale selektywna. W klasie „zrozumieć podsystem" — zmierzone −71%: nie dlatego, że zapytania grafu są tańsze od grepa, lecz dlatego, że graf od razu zawęził korpus i jeden agent poradził sobie tam, gdzie ramię B było zmuszone po cichu rozwinąć piątkę. W klasie impact oszczędność — zero (grep po dosłownej nazwie symbolu jest tani nawet na 2125 plikach; wygrana grafu to tam kompletność i szybkość, nie tokeny). Na pełnym audycie — zero (orkiestracja jest potrzebna obu; graf daje gotowe partycjonowanie i kandydatów, nie zniżkę). Uniwersalny mnożnik „90%" nie istnieje — istnieje klasa zadań.
Wniosek 2, metodologiczny — ukryta orkiestracja. Po wierzchnich liczbach ramię B na „zrozumieniu" wyglądało taniej (56k wobec 76k) — dopóki nie odkryliśmy w jego transkryptach 205.8k tokenów zagnieżdżonych agentów. Ładne porównania „agent z narzędziem kontra agent bez" systematycznie niedoszacowują ramię, które po cichu deleguje. Uczciwy pomiar ma obowiązek liczyć całe drzewo agentów — podejrzewamy, że niemała część popularnych liczb tego nie robi.
Dyscyplina nieufności wobec grafu zadziałała także wewnątrz eksperymentu: ramię A na pytaniu o impact odsiało fałszywie pozytywnych kandydatów grafu (barrel-importy) żywym grepem, a na audycie zweryfikowało cykle z grafu i dwa fałszywe odrzuciło. Graf dawał tropy — faktami stawało się tylko potwierdzone.
Bonus, którego nikt nie zamawiał: audytowe agenty ramienia B znalazły w rdzeniu NestJS dwa wyglądające na prawdziwe bugi (przypisanie do Map przez indeks, które nigdy nie jest czytane; return zamiast continue, po cichu ucinający rejestrację middleware) — nieplanowane potwierdzenie, że głębia audytu tej dyscypliny jest prawdziwa.
Ograniczenia — uczciwie: n=1 na komórkę (bez wariancji), jedno repozytorium, jeden tier modelu; koszt zagnieżdżony dwóch komórek oszacowano przez kalibrację na jedynej dokładnie zmierzonej (i oznaczono jako szacunek); ramiona same wybierały strategię — to cecha projektu (mierzymy system jako całość), ale miesza ona „graf" z „decyzją, by nie orkiestrować"; długoterminowa amortyzacja powtarzanych sesji nie została tym eksperymentem zmierzona — pozostaje hipotezą z mocnym a priori (~77k „podatku na ponowne uczenie się" za sesję wobec jednorazowej darmowej budowy).
Rozdział 6. Było → jest
| Było | Jest | |
|---|---|---|
| Wiedza o repo między sesjami | nie przeżywa sesji; ~77k tokenów na ponowne uczenie się | graf buduje się w sekundy jeden raz; kandydaci — natychmiastowe darmowe zapytania |
| „Kto woła X / co się zepsuje?" | pełne przejście grepem; poza trackowanym zadaniem agent o grafie nie wiedział | samodzielne wejście: graphify affected "X" → verify regionami ±30 linii — łącznie z powiązaniami indirect, których grep nie widzi |
| Zrozumienie podsystemu na dużym repo | ukryty fan-out sub-agentów, ~262k tokenów | jeden agent z grafem, ~76k — −71%, zmierzone |
| Audyt repozytorium | rekonesans od zera | GRAPH_REPORT.md (gotowe huby, klastry, nieoczekiwane powiązania) czytany jako pierwszy |
| Zaufanie do indeksu | — | bramka freshness; przeterminowany → głośno + propozycja przebudowy i hooka; fakty — tylko z żywych plików |
| Instalacja | — | za jawnym „tak" właściciela: agent proponuje raz, instaluje i buduje sam; odmowa jest respektowana całą sesję |
Epilog
Formuła funkcji: istniejące narzędzie (Graphify) + dyscyplina obchodzenia się z nim (nasz skill) + warstwowe testy, które nie wierzą nikomu — ani autorowi tekstu, ani ładnemu snippetowi, ani warunkom własnego scenariusza, ani — jak się okazało w rozdziale 5 — wierzchniej linijce cudzego benchmarku. Ze wszystkich istotnych znalezisk czterech wydań ani jedno nie zostało znalezione „wyczytaniem" — wszystkie przyniosło wykonanie: sędziowie, graderzy, docker, żywe przebiegi i telemetria eksperymentu.
Świadomie odłożone: triage pull requestów przez graf (nie mamy procesu PR — narzędzia bez konsumenta nie robimy), pętla pamięci zapytań save-result/reflect (osobna rozmowa) i powtórzenie eksperymentu z n>1 i drugim repozytorium — jeśli selektywna oszczędność potwierdzi się i tam, „hajp 90%" dostanie uczciwego zastępcę: „−70% w klasie «zrozumienie», zero w pozostałych".
Jak wypróbować
Graf kodu jedzie w pluginie my-architect od v1.14.0 (aktualna — v1.16.1). Nie trzeba go wołać ręcznie: skill sam rozpoznaje graf w repozytorium, a przy ciężkim zadaniu bez grafu — sam zaproponuje instalację.
- Załóż konto na my-architect.app, weź token na stronie API Keys i wyeksportuj go w tej samej sesji, w której uruchamiasz Claude Code:
``bash export MCP_API_KEY=mcp_TWÓJ_TOKEN ``
- Dodaj marketplace i postaw plugin:
`` /plugin marketplace add d7561985/my-architect-marketplace /plugin install my-architect@my-architect-marketplace ``
- Graf można zbudować i ręcznie, nie czekając na propozycję agenta:
``bash pip install graphifyy && graphify build . ``
- Masz już plugin?
/plugin marketplace update my-architect-marketplace→/plugin update my-architect.
---
Fakty i linki
- Skill-przewodnik:
plugins/my-architect/skills/recursive-context/references/code-graph.mdw otwartym repozytorium github.com/d7561985/my-architect-marketplace; wydania v1.14.0 (f2ca07d), v1.15.0 (1257064), v1.16.0 (ee0a711), v1.16.1 (6f35623), wszystkie — 2026-07-03. - Pełne dane testów i eksperymentu:
skills/recursive-context/evals/RESULTS.md(RED/GREEN/LIVE/A/B); datasetytrigger-evals.json(13 case'ów),behavior-evals.json(10 case'ów). - Graphify: github.com/safishamsi/graphify (MIT; pakiet PyPI
graphifyy); nasza instalacja: 0.9.5. - Poprzednia część cyklu: My Architect, część 9 — o samym skillu dyscypliny dekompozycji.