Optymalizacja Agentów LLM w Produkcji: Prompt Caching (KV-Cache), Telemetria TCX i Obniżenie Kosztów o 85%
W budowaniu zaawansowanych agentów AI (takich jak trener personalny czy nauczyciel językowy) szybko pojawia się problem skali. Aby agent podejmował trafne decyzje, potrzebuje potężnego kontekstu: profilu użytkownika, historii urazów, zasad biomechaniki, bazy PubMed, słowników i rejestru błędów gramatycznych.
Kontekst rzędu 25 000 - 30 000 tokenów na każde zapytanie oznacza dwa twarde problemy produkcyjne:
- Wysoki koszt tokenów wejściowych (Input Tokens) przy wielorundowych konwersacjach na Telegramie.
- Duże opóźnienie (Latency) sięgające 3-5 sekund na przetworzenie prefiksu (faza prefill).
Zaprojektowałem i wdrożyłem architekturę opartą o Prompt Caching (KV-Cache Reuse) oraz hierarchię Prefix Invariance, uzyskując ponad 85% oszczędności na tokenach wejściowych i sub-sekundowy czas odpowiedzi.
Problem: Dlaczego zwykły prompting niszczy pamięć podręczną modeli AI
Mechanizm Prompt Caching u dostawców modeli (DeepSeek, Anthropic, OpenAI, Google) działa na zasadzie dopasowywania identycznego prefiksu binarnego od lewej do prawej (exact prefix matching).
Można to porównać do czytania grubej książki: jeśli pierwsze 100 stron jest zawsze identyczne, model nie musi czytać ich od nowa za każdym razem - po prostu pamięta ich treść w podręcznej pamięci RAM karty graficznej (tzw. bufor KV-Cache).
Jeśli jednak w strukturze promptu pojawi się choćby jeden dynamiczny element na samym początku (np. bieżąca godzina z sekundami, losowy identyfikator sesji lub nieuporządkowana lista narzędzi), cała pamięć podręczna zostaje unieważniona (Cache Miss). Model za każdym razem musi od zera przeliczać macierze uwagi dla 30 000 tokenów, co trwa kilka sekund i kosztuje realne pieniądze.
Co zrobiłem: Czterowarstwowa hierarchia wiedzy (Prefix Invariance)
Przebudowałem strukturę ładowania promptu systemowego w silniku OpenClaw na deterministyczny, warstwowy model ułożony od elementów najbardziej stałych do najbardziej zmiennych:
- Warstwa 1: Niezmienna Tożsamość i Narzędzia (100% Statyczna)
- Definicje systemowe (
SOUL.md), deterministycznie posortowany rejestr narzędzi i anty-wzorce zachowań.
- Definicje systemowe (
- Warstwa 2: Baza Wiedzy Domenowej (Rzadko Zmienna)
- Podręczniki biomechaniki, zasady periodyzacji, fizjologia wysiłku, słowniki i reguły gramatyczne (
wiedza/*.md,USER.md).
- Podręczniki biomechaniki, zasady periodyzacji, fizjologia wysiłku, słowniki i reguły gramatyczne (
- Warstwa 3: Rejestr Stanu i Ostatnich Dni (Zmienna raz na dobę)
- Bieżące ciężary, otwarte wątki i stan regeneracji (
stan_obecny.md).
- Bieżące ciężary, otwarte wątki i stan regeneracji (
- Warstwa 4: Dynamiczny Czat (Na samym końcu)
- Bieżąca godzina, historia ostatnich kilku wypowiedzi oraz nowe wiadomości i pliki przesłane przez użytkownika.
Dzięki temu pierwsze 25 000 - 30 000 tokenów pozostaje w 100% nienaruszone między kolejnymi zapytaniami:
+-------------------------------------------------------------+
| Warstwa 1: SOUL.md + Sortowane Schematy Narzędzi (Statyczna)| -> CACHE HIT (Read)
+-------------------------------------------------------------+
| Warstwa 2: Baza Wiedzy, PubMed, Gramatyka (Statyczna) | -> CACHE HIT (Read)
+-------------------------------------------------------------+
| Warstwa 3: Dziennik Treningowy, stan_obecny.md (Doba) | -> CACHE HIT (Read)
+====================== GRANICA KV-CACHE =====================+
| Warstwa 4: Nowa wiadomość + plik TCX (Dynamiczna) | -> INPUT TOKENS (Nowe)
+-------------------------------------------------------------+
Dlaczego to ważne (Use Cases w praktyce)
1. Trener Sportowy i telemetria Garmin (.TCX)
W bocie trenerskim @PabloGymHQBot kluczowym wyzwaniem była interpretacja surowych plików XML (.tcx) przesyłanych bezpośrednio z zegarka sportowego:
- Obsługa specyfiki formatu: Garmin eksportuje sesje siłowe jako
Sport="Other"z dystansem 0 m. Zabezpieczyłem reguły bota przed błędną interpretacją treningu siłowego jako cardio. - Ekstrakcja stref tętna: Bot parsuje tysiące punktów pomiarowych, wyciągając średnie i maksymalne tętno, przewyższenia i spalone kalorie.
- Zysk z Prompt Caching: Analiza gęstego pliku treningowego w zestawieniu z 30-dniową historią kosztuje 0.0013 $ zamiast kilkunastu centów, a bot od razu zadaje precyzyjne pytania trenerskie o ciężary i czucie stawów.
2. Nauczyciel hiszpańskiego podczas szybkiej konwersacji
W bocie językowym @PabloSpanishHQBot konwersacja polega na wymianie kilkudziesięciu krótkich wiadomości w ciągu 20 minut:
- Przed optymalizacją: Każda krótka wypowiedź (np. 15 tokenów od ucznia) wymuszała ponowne przetworzenie 20 000 tokenów zasad gramatycznych, słownika slangu i historii błędów.
- Po optymalizacji: Pierwsza wiadomość ładuje pamięć podręczną, a kolejne 30 odpowiedzi korzysta w ponad 90% z taniego odczytu z pamięci (Cache Read).
- Wynik: Płynny dialog w czasie rzeczywistym (< 1 sekundy opóźnienia) przy znikomym narzucie kosztowym na całą lekcję.
3. Twarde liczby i metryki produkcyjne
Weryfikacja w logach produkcyjnych (transcript_events w SQLite) po wdrożeniu zmian:
- Wskaźnik trafień pamięci (Cache Hit Ratio): Wzrost z ~15% do > 90% w aktywnych sesjach.
- Średni koszt zapytania: Spadek z ~0.0095 $ do ~0.0012 $ na pojedynczy krok.
- Czas reakcji (Latency): Skrócenie czasu oczekiwania na pierwszy token z 3.8s do 0.7s.
Czego się nauczyłem
- Prompt Caching wymaga żelaznej dyscypliny w kodzie: Nawet drobny błąd w formacie daty na początku promptu niszczy wydajność całego systemu agentowego. Elementy dynamiczne muszą zawsze trafiać na sam koniec.
- Hierarchia od statycznej do dynamicznej to standard produkcyjny: Podział kontekstu na warstwy o różnym cyklu życia (lifecycle) to najtańszy i najskuteczniejszy sposób na skalowanie aplikacji LLM.
- Prawdziwy agent to dialog, a nie tylko wyszukiwarka: Samo wstrzyknięcie pliku to za mało - kluczem jest domknięcie pętli zwrotnej (aktywny follow-up o subiektywne zmęczenie i ciężary) i utrwalenie wniosków w trwałym rejestrze stanu.