Skąd wiem, że mój ekosystem AI pomaga, a nie przeszkadza?
Context Observatory to lokalne narzędzie pokazujące z czego składa się kontekst Claude Code. Proxy przechwytuje treść, OTLP zbiera telemetrię - razem dają obraz pozwalający ocenić czy ekosystem działa prawidłowo.
Cortex rośnie. Każdy projekt ma własny CLAUDE.md. Hook uruchamiany na początku sesji ładuje dane z Vault - indeks wiedzy, korekty, aktywne plany. Do tego dochodzą skille, pamięć automatyczna, definicje narzędzi MCP, orkiestry agentów.
Każdy element może być przydatny. Każdy zajmuje miejsce w oknie kontekstowym. Ile? Nie wiem. Jaki procent kontekstu to rozmowa, a jaki infrastruktura zbudowana wokół niej?
Narzędzia na rynku - ccusage, Helicone, Langfuse - mierzą tokeny i koszty. Odpowiadają na pytanie “ile wydałeś”. Prawie żadne nie odpowiada na pytanie “z czego składa się Twój kontekst”. Znalazłem Context Lens, open-source proxy z widokiem treemap - jedyny projekt, który podszedł do problemu podobnie.
Potrzeba była konkretna, a gotowe narzędzia jej nie pokrywały. Zbudowałem Context Observatory - lokalny obserwator, który ma pokazać co faktycznie trafia do kontekstu Claude Code i czy to, co tam ląduje, pomaga czy przeszkadza.
Co chcę wiedzieć
Nie potrzebuję kolejnego licznika tokenów. Potrzebuję odpowiedzi na trzy pytania: z czego składa się kontekst, czy Vault dostarcza właściwe dane i jak dynamiczne zmiany wpływają na prompt caching.
Z czego składa się kontekst
Observer klasyfikuje każdy fragment kontekstu do jednej z pięciu kategorii:
- Instrukcje - system prompt, CLAUDE.md - jak model ma się zachowywać
- Narzędzia - definicje narzędzi, MCP, skille - co model może robić
- Wiedza - dane z Vault, pamięć - co model wie o kontekście pracy
- Rozmowa - właściwa praca, historia sesji
- Nieznane - niesklasyfikowane fragmenty
Taki podział pozwala zobaczyć różnicę między kontekstem potrzebnym do rozmowy a kosztem infrastruktury.
Sam session-context hook potrafi załadować kilka tysięcy tokenów. Przy Sonnet i oknie 200k to 1-2% pojemności. Brzmi niewinnie. Ale sytuacja zmienia się podczas gęstej sesji z wieloma wynikami narzędzi, fragmentami kodu i długą historią decyzji. Stały koszt infrastruktury zaczyna konkurować z materiałem roboczym. Każdy dodatkowy skill lub dokument może przyspieszyć kompakcję i skrócić czas, przez który model zachowuje szczegóły wcześniejszej pracy.
Rozbudowany ekosystem może zacząć ograniczać model, któremu miał pomagać. Chcę widzieć ten moment na wykresie, zamiast zgadywać na podstawie zachowania modelu.
Czy Vault ładuje to co powinien
Hooki nie siedzą w kontekście na stałe. Uruchamiają się, dostarczają materiały i znikają. Session-context hook przy starcie sesji wstrzykuje materiały potrzebne do pracy w danym projekcie, a także indeksy z Vault - tak aby LLM wiedział gdzie szukać pozostałych danych, gdy zajdzie taka potrzeba.
Samo potwierdzenie, że hook zadziałał, ma małą wartość. Kluczowe pytanie brzmi: czy załadował właściwe informacje?
Korekta dotycząca jednego projektu nie powinna trafiać do sesji prowadzonej w innym repozytorium. Nieaktualne wpisy nie powinny zajmować miejsca tylko dlatego, że nadal istnieją w katalogu. Indeks Vault może być przydatny, ale po przekroczeniu pewnego rozmiaru jego pełne ładowanie stanie się droższe niż selektywny retrieval.
Observer zapisuje surową treść przechwyconego kontekstu i pozwala ją przeszukiwać. Mogę sprawdzić, czy konkretna korekta pojawiła się w sesji, skąd pochodzi i ile miejsca zajęła. To zmienia sposób oceny Vault - nie patrzę już wyłącznie na liczbę zapisanych notatek. Mogę mierzyć, czy protokół wyszukiwania dostarcza materiały pasujące do bieżącej pracy.
Prompt caching i dynamiczny kontekst
Anthropic cache’uje prefiks kontekstu. Jeśli początek promptu nie zmienia się między requestami, API nie przelicza go od nowa - cache hit. Ekosystem w stylu Cortex zmienia skład kontekstu dynamicznie. Nowy skill dopisuje się do system promptu. Kompakcja przetasowuje kolejność. Hook dorzuca dane, które wczoraj nie istniały. Każda taka zmiana potencjalnie łamie cache prefix.
Na razie Observer śledzi proporcje tokenów cache_read do cache_write. Widzę, kiedy cache działa, a kiedy nie. Ale nie mam jeszcze narzędzia, które powie: “ta zmiana w hooku złamała cache prefix i kosztowała Cię X tokenów w tej sesji”. To następny krok, nie obecna funkcjonalność.
Skąd Observer bierze dane
Observer zbiera dane z dwóch źródeł. Odbiornik OTLP zbiera telemetrię z Claude Code - prawdziwe tokeny, zdarzenia narzędzi, kompakcje. Proxy HTTP przechwytuje requesty lecące do API Anthropic - widzi pełną treść system promptu, parsuje ją na komponenty i klasyfikuje.
OTLP zna liczby, proxy zna treść. Observer łączy oba - bierze proporcje komponentów z proxy i skaluje je do realnych tokenów z OTLP. Razem dają pełny obraz jakości, wydajności i ekonomii pracy systemu.
Decyzje i trudności po drodze
Budowa Observera nie była skomplikowana technicznie. Trudniejsze okazały się decyzje projektowe i problemy, które pojawiły się dopiero przy zderzeniu z prawdziwymi danymi. Poniżej kilka z nich:
Fire-and-forget - Observer nigdy nie blokuje API
Najważniejsza zasada projektowa. Proxy przechwytuje requesty, ale nigdy nie może ich opóźnić ani zablokować. Nawet gdyby baza danych padła, SQLite się zapchał albo dashboard przestał odpowiadać - Claude Code ma działać normalnie. Wolę stracić punkt danych niż spowolnić pracę. Obserwacja jest pomocnicza. Nie może stać się nowym pojedynczym punktem awarii.
Granica między obserwacją a ingerencją
Observer musi być pasywny. Im mniej zmienia badany system, tym bardziej wiarygodne są wyniki. Jednocześnie dokładna klasyfikacja wymaga markerów - etykiet w system prompcie, które mówią “tu zaczyna się skill X”, “tu kończy się output hooka Y”. Każdy marker to dodatkowe tokeny. Ile możesz dodać do kontekstu w ramach monitoringu, zanim sam monitoring staje się problemem, który miał rozwiązywać?
Fałszywe klasyfikacje - kod który wygląda jak skill
Klasyfikacja opiera się na heurystykach i wyrażeniach regularnych. Działa poprawnie dla 85-90% treści. Ale kontekst jest dynamiczny - tool result może zawierać kod źródłowy ze stringiem “skills”, a parser klasyfikuje go jako komponent skill. Kompakcja przebudowuje strukturę promptu i zaciera granice między sekcjami. Konkretny fix: zmiana kolejności reguł w parserze - tool result cache musi być sprawdzany jako pierwszy, zanim inne reguły dostaną szansę. Kategoria “unknown” jest celowa - wolę widzieć niepewność niż przypisywać fragment na podstawie słabego dopasowania.
Korelacja dwóch źródeł bez wspólnego ID
OTLP i proxy mają osobne systemy identyfikatorów. Nie da się ich zmatchować 1:1 - prompt.id z telemetrii to nie to samo co UUID generowany przez proxy. Obejście: korelacja po timestampach (okno +-60 sekund) i heurystyka wzrostu tokenów. Skok o ponad 500 tokenów między kolejnymi wywołaniami sugeruje, że między nimi był tool_use. Nie jest to idealne, ale dla diagnostyki wystarcza.
Estymacja tokenów - polski zjada więcej niż angielski
Proxy estymuje tokeny dzieląc liczbę znaków przez cztery. To grube przybliżenie - tokenizerowi zajmuje mniej znaków zakodowanie angielskiego “the” niż polskiego “również”. Kod tokenizuje się inaczej niż proza. Skalowanie wyników do realnych danych z OTLP ratuje agregaty - sumaryczne proporcje się zgadzają. Ale per-komponent rozrzut to +-10-15%. Dla diagnostyki wystarcza. Dla dokładnego billingu nie.
Write Buffer - żeby nie zabić SQLite
Eventy nie lecą jeden po jednym do bazy. Bufor zbiera je i flushuje co 2 sekundy albo po 500 eventach - co nastąpi pierwsze. Bez tego SQLite padłby przy intensywnej sesji z wieloma tool_use. Dodatkowe zabezpieczenie: jeśli bufor przekroczy 5000 eventów (flush nie nadąża), najstarsze 50% zostaje odrzucone. Lepiej stracić dane niż zapchać pamięć.
Multi-session - wiele sesji i subagenty jednocześnie
Wiele sesji Claude Code może działać jednocześnie. Do tego dochodzą subagenty - orkiestry uruchamiające kilkanaście agentów równolegle. Każdy event niesie session_id, subagenty mają parent_session_id. Jeden Observer obsługuje wszystko - rozdzielenie następuje na poziomie danych, nie transportu.
Podłączenie proxy do Claude Code
Claude Code pozwala przekierować ruch API przez zmienną środowiskową ANTHROPIC_BASE_URL. Observer ustawia ją automatycznie przy starcie i usuwa przy zamknięciu. Problem: CTRL+C nie zawsze triggerował cleanup - zmienna zostawała w rejestrze, a Claude Code przy następnej sesji próbował łączyć się z proxy, którego już nie było. Fix: process.on('exit') + process.on('SIGINT') z jawnym usuwaniem klucza.
Prompt caching - otwarte wyzwanie
Widzę tokeny cache_read i cache_write, ale nie potrafię jeszcze połączyć konkretnej zmiany w kontekście z utratą cache’u. Żeby to zrobić, potrzebowałbym narzędzia które porównuje prefiksy między kolejnymi wywołaniami, znajduje punkt rozbieżności i atrybucje koszt utraty cache do konkretnej zmiany - “ten nowy skill złamał prefiks i kosztował Cię 12k tokenów w następnych dwudziestu requestach”. Tego jeszcze nie mam. To najbardziej otwarty problem w całym projekcie.
Od diagnostyki do pętli zwrotnej
Pierwsze co zrobiłem po uruchomieniu to sprawdzenie kompozycji sesji. Wykres pokazał proporcje kategorii w czasie - na starcie dominuje infrastruktura (hook, CLAUDE.md, narzędzia), po kilkunastu wymianach rozmowa ją przebija, a po kompakcji - infrastruktura znów rośnie procentowo. Wcześniej zgadywałem kiedy kontekst się zapełnia. Teraz to widzę.
Drugie zastosowanie: porównanie rozmiaru skilli z częstotliwością użycia. Jedne ładują 200 tokenów metadanych, inne 3000 tokenów instrukcji. Skill który zjada dużo miejsca i jest uruchamiany raz na dziesięć sesji to kandydat do przycięcia albo lazy loadingu.
Trzecie to kontrola jakości Vault. Sprawdzam, czy retrieval protocol trafia w materiały związane z bieżącym projektem. Jeżeli session-context regularnie ładuje nieaktualne korekty albo dane z innych projektów, mam konkretny sygnał do zmiany reguł wyszukiwania.
Rozważam też dedykowany hook instrumentacyjny. Zamiast klasyfikować kontekst po fakcie regexami, mogę go oznaczyć u źródła - hook wstrzykujący markery w stylu <!-- observatory:component type=skill name=content-writer tokens=1847 -->. Instrumentacja zamiast klasyfikacji. Zamiana heurystyk na deklaracje. Koszt: kilkadziesiąt tokenów na marker. Zysk: pewna klasyfikacja zamiast 85-90%.
Docelowo Observer ma być częścią pętli zwrotnej. Dane z Observera prowadzą do decyzji: przyciąć skille, zmienić co ładuje session-context, zoptymalizować retrieval. Sam Observer nie wykonuje tych zmian. Dostarcza podstawę do podjęcia decyzji. To łączy go z pozostałymi elementami serii - Observer pokazuje co dzieje się w kontekście, Vault przechowuje wiedzę, skille, orkiestry i hooki reagują na potrzeby projektu. Bez pomiaru łatwo rozbudowywać każdy z tych elementów, nie widząc ich łącznego kosztu.
Co Observer robi, a czego jeszcze nie robi
Observer pokazuje co się dzieje. Nie mówi jeszcze co z tym zrobić automatycznie. Buduję narzędzie do obserwacji systemu, który sam buduję. Rekurencja jest zamierzona.
Budowanie to seria wpisów o tym, jak powstają moje projekty. Jeśli chcesz więcej - zapisz się na newsletter.