Forensyka trafień w cache promptów i twarde limity wydatku: rachunek za LLM czytany jak faktura
Forensyka trafień w cache promptów polega na sprawdzeniu, wywołanie po wywołaniu, czy zapytanie trafiło w cache dostawcy, ile to kosztowało i dlaczego doszło do chybienia. W parze z limitem wydatku, który zatrzymuje ruch zamiast wysyłać maila, zamienia rachunek za LLM z comiesięcznej niespodzianki w fakturę z pozycjami.
dfzoo AI Engineering prowadzi forensykę trafień w cache promptów i wdraża twarde limity wydatku dla zespołów inżynierskich, których rachunek za LLM rośnie szybciej niż ruch. Praca polega na przypisaniu każdego wywołania do funkcji produktu i sesji, odczytaniu pól cache, które dostawca zwraca w każdej odpowiedzi, i nazwaniu przyczyny każdego chybienia: przepisany prefiks, proxy gubiące znacznik cache, równoległa seria zapytań, wygasły TTL. Klient zostaje z raportem forensycznym z tabelą per wywołanie, układem promptu, który cały zespół może zweryfikować hashem, oraz rejestrem limitów z zapisem, co dzieje się z każdą funkcją po wyczerpaniu budżetu. Oszczędność jest mierzona na raporcie zużycia dostawcy przed zmianą i po niej.
Dla kogo jest ten przewodnik.
- CTO i szefowie inżynierii, którzy dostali rachunek, którego nie da się wyjaśnić z panelu zużycia
- Zespoły platformowe utrzymujące gateway albo proxy do LLM (LiteLLM, własny router, Bedrock za warstwą zgodną z OpenAI)
- Zespoły budujące pętle agentowe, w których każda tura wysyła całą rozmowę od nowa
- Osoby od finansów, które potrzebują kosztu LLM w podziale na funkcje produktu i na klientów, a nie na klucze API
Rachunek czytany jak faktura
Raport zużycia u dostawcy odpowiada na jedno pytanie: ile tokenów każdego rodzaju przeszło przez każdy klucz API, per model, per dzień. Usage and Cost API Anthropica grupuje po kluczu, workspace, modelu i tierze usługi oraz dzieli wejście na tokeny niecache'owane, zapisane do cache i odczytane z cache; OpenAI i Google dają ten sam kształt. Żaden z nich nie wie, czym jest funkcja produktu ani sesja, bo ta informacja żyje w waszym kodzie.
Faktura wymaga trzech złączeń, których panel nie ma. Po pierwsze, każde zapytanie niesie nazwę funkcji i identyfikator sesji jako metadane, zapisane w gatewayu obok identyfikatora zapytania od dostawcy. Po drugie, blok usage z każdej odpowiedzi jest przechowywany per wywołanie, a nie sumowany per dzień: Anthropic zwraca cache_creation_input_tokens i cache_read_input_tokens, OpenAI zwraca input_tokens_details.cached_tokens i cache_write_tokens, Converse API w Bedrocku zwraca cacheReadInputTokens i cacheWriteInputTokens. Po trzecie, cennik z mnożnikami zapisu i odczytu jest stosowany per wywołanie, więc każde zapytanie ma własny koszt.
Z tymi złączeniami pytanie zmienia się z "dlaczego rachunek jest wysoki" na "która funkcja, w których sesjach, zapłaciła pełną cenę wejścia za prefiks, który powinien był siedzieć w cache". Na takie pytanie istnieje odpowiedź w postaci rankingu.
Jak trzej dostawcy rozliczają cache promptów
Mechanika różni się na tyle, że reguła kciuka z jednego dostawcy produkuje chybienia u drugiego. Tabela pochodzi z dokumentacji dostawców według stanu na wrzesień 2026; linki do źródeł są na końcu strony.
| Dostawca | Co jest cache'owane | Czas życia | Cena zapisu i odczytu | Gdzie widać trafienie |
|---|---|---|---|---|
| Anthropic (Claude API) | Prefiks kończący się na breakpoincie cache_control, w kolejności tools, system, messages; minimum od 512 do 4096 tokenów zależnie od modelu | Domyślnie 5 minut, opcjonalnie 1 godzina | Zapis 1,25x ceny wejścia (5 min) albo 2x (1 h); odczyt 0,1x, na części modeli 0,025x | cache_creation_input_tokens, cache_read_input_tokens |
| OpenAI | Automatyczne dopasowanie dokładnego prefiksu od 1024 widocznych tokenów; definicje narzędzi, schematy i ustawienia reasoning są częścią prefiksu | 30 minut od ostatniego zapisu lub użycia w GPT-5.6 i nowszych; 5 do 10 minut bezczynności, do godziny, we wcześniejszych | Odczyt 0,1x ceny wejścia w GPT-5.6 i nowszych; wcześniejsze modele mają własne stawki za cache | input_tokens_details.cached_tokens, cache_write_tokens |
| Google Gemini | Cache niejawny włączony domyślnie od Gemini 2.5, od 2048 albo 4096 tokenów; cache jawny tworzony przez wywołującego | TTL cache jawnego domyślnie 1 godzina, do ustawienia | Tokeny z cache po obniżonej stawce plus opłata za przechowywanie za milion tokenów na godzinę przy cache jawnym | tokeny z cache w metadanych zużycia |
| Amazon Bedrock (Claude) | Blok cachePoint w Converse albo cache_control w InvokeModel, ta sama kolejność sekcji; minimum liczone łącznie dla wszystkich sekcji | 5 minut, 1 godzina w obsługiwanych modelach; odnawia się przy każdym trafieniu | Odczyt po stawce cache danego modelu; zapis może być rozliczany powyżej ceny wejścia | cacheReadInputTokens, cacheWriteInputTokens; dokumentacja zaznacza, że obsługa cache nie gwarantuje trafienia |
Co po cichu psuje prefiks
Chybienie nigdy nie jest losowe. Między dwoma zapytaniami zmieniło się coś przed breakpointem albo drugie zapytanie przyszło, gdy wpis jeszcze nie istniał. Poniższy katalog to to, co diff forensyczny znajduje najczęściej, w przybliżonej kolejności kosztu.
- Warstwa oszczędzająca tokeny, która przepisuje historię. Kompresja albo przycinanie starszych wiadomości zmienia bajty przed breakpointem w każdej turze, więc każda tura zapisuje nowy wpis po 1,25x zamiast czytać po 0,1x. Praktycy na forach deweloperskich opisali dokładnie ten przypadek: wtyczka sprzedawana jako oszczędzająca tokeny podniosła rachunek u Anthropica. Mniej tokenów po pełnej cenie kosztuje więcej niż więcej tokenów po jednej dziesiątej.
- Definicje narzędzi, które się zmieniają albo zmieniają kolejność. Narzędzia stoją na początku prefiksu, więc dodane narzędzie, poprawiony opis albo lista renderowana ze zbioru bez ustalonej kolejności unieważnia wszystko za nimi.
- Treść dynamiczna na górze promptu systemowego: znacznik czasu, imię użytkownika, identyfikator zapytania. Jeden bajt przed breakpointem i to jest inny prefiks.
- Proxy, które nie przenosi znacznika. Warstwa zgodna z OpenAI przed Bedrockiem musi przetłumaczyć cache_control na blok cachePoint; jeśli go gubi, każda warstwa nadal "obsługuje cache" i nic nie jest cache'owane. W jednym publicznie opisanym przypadku łańcuch Droid przez LiteLLM do Bedrocka skończył się rachunkiem 37 901,73 USD przy około 6,47 mld niecache'owanych tokenów wejścia.
- Równoległe serie. Anthropic dokumentuje, że wpis staje się dostępny dopiero, gdy zacznie się pierwsza odpowiedź, więc dziesięć zapytań wysłanych naraz zapisuje ten sam prefiks dziesięć razy.
- Przełączniki parametrów. Thinking, effort, tool_choice, schematy wyjścia i ustawienia reasoning są częścią tożsamości prefiksu u Anthropica i OpenAI; flaga funkcji, która przełącza jedno z nich, przełącza też cache.
- Wygasły czas życia. Batch, który wysyła jedno zapytanie na dokument co dwadzieścia minut, nigdy nie użyje wpisu na 5 minut; Anthropic ma na taki kształt opcję 1 godziny, OpenAI w GPT-5.6 domyślne okno 30 minut.
- Arytmetyka pętli agentowych. Każda tura wysyła całą rozmowę od nowa, więc liczba tokenów wejścia w sesji rośnie z kwadratem liczby tur. Z trafieniami większość tego kwadratu jest rozliczana po 0,1x; bez nich całość po pełnej cenie. Dlatego rachunek za agenta skacze, a nie rośnie.
Gdzie kończy się alert, a zaczyna twarde zatrzymanie
Każdy publicznie opisany incydent kosztowy kończy się tym samym zdaniem: alerty były skonfigurowane. Alert to mail z opóźnieniem liczonym w godzinach; pętla bez warunku wyjścia liczy się w zapytaniach na sekundę. Mechanizmy dostawców, które naprawdę zatrzymują ruch, różnią się między sobą i te różnice przesądzają o projekcie.
- Limity per funkcja i per klient nie istnieją u żadnego dostawcy. Żyją w waszym gatewayu: licznik per funkcja i per klient, budżet i kontrolowany błąd po jego przekroczeniu.
- Limit jest decyzją produktową. Zanim zostanie włączony, każda funkcja dostaje pisemną odpowiedź na pytanie "co widzi użytkownik, gdy to stanie": tryb ograniczony, kolejka, mniejszy model albo czytelny komunikat.
- Limit, który nigdy nie zadziałał, jest zgadywaniem. Próba to syntetyczna pętla na projekcie testowym, która dochodzi do limitu, z zapisem, ile trwało egzekwowanie i kto dostał powiadomienie.
| Dostawca | Co zatrzymuje ruch | Co tylko powiadamia | Poziom |
|---|---|---|---|
| Anthropic | Miesięczny limit wydatku per workspace oraz limity zapytań i tokenów na minutę per workspace | Alerty wydatku na progach | Workspace; nie na domyślnym workspace i nie per klucz API |
| OpenAI | Twardy limit wydatku per organizacja albo projekt: zapytania dostają 429 z kodem organization_spend_limit_exceeded albo project_spend_limit_exceeded; egzekwowanie nie jest natychmiastowe | Alerty wydatku, ruch idzie dalej | Organizacja i projekt |
| Google Cloud (Gemini przez Cloud Billing) | Budżet z limitem wydatku (spend cap, w podglądzie) dla objętych usług albo powiadomienie budżetowe na Pub/Sub podpięte pod kod, który wyłącza rozliczanie projektu | Budżety alertowe, które według dokumentacji nie ograniczają użycia ani wydatku | Konto rozliczeniowe i projekt |
Jak wygląda raport forensyczny i co zespół z nim robi
Rdzeniem raportu jest jedna tabela z jednym wierszem na wywołanie. Wszystko inne w raporcie jest agregatem tej tabeli, więc każdą liczbę z podsumowania da się cofnąć do zapytań, które ją wyprodukowały.
- Przyczyny chybień układa się według pieniędzy, które za nimi stoją, nie według częstości. Zwykle jedna przyczyna niesie większość kosztu.
- Najdroższe przyczyny naprawia się u źródła: zmiana kolejności promptu, przypięcie listy narzędzi, przeniesienie treści dynamicznej pod breakpoint, serializacja pierwszego zapytania per prefiks, poprawka tłumaczenia w proxy.
- Tę samą tabelę liczy się ponownie na raporcie zużycia dostawcy po zmianie. Stan przed i po pochodzi z tego samego źródła, więc oszczędność jest pomiarem.
- Limity wchodzą na końcu, gdy baza jest uczciwa, żeby budżet był ustawiony względem realnego zużycia, a nie względem błędu.
| Kolumna | Co zawiera | Skąd pochodzi |
|---|---|---|
| Wywołanie | Identyfikator zapytania od dostawcy, czas, model | Nagłówki odpowiedzi i blok usage |
| Funkcja i sesja | Nazwa funkcji, identyfikator sesji, klient | Metadane, które wasz gateway dokleja do zapytania |
| Prefiks | Hash bajtów do ostatniego breakpointu i pozycja, w której po raz pierwszy różni się od poprzedniego wywołania w sesji | Log gatewaya, diff liczony offline |
| Trafienie lub chybienie | Tokeny odczytane, zapisane i niecache'owane | Blok usage, pola według dostawcy |
| Koszt | Cena per klasa tokenów z dnia wywołania | Cennik dostawcy, wasza faktura |
| Przyczyna chybienia | Jedna pozycja z katalogu powyżej albo "niewyjaśnione" | Diff, odstęp między wywołaniami, ślad z proxy |
Gdzie to się psuje i co z tym robimy.
- 1Warstwa oszczędzająca tokeny przepisuje prefiks w każdej turze
Wtyczka do kompresji albo przycinania historii siedzi między aplikacją a dostawcą i edytuje starsze wiadomości, żeby wysłać mniej tokenów. Edycja zmienia bajty przed breakpointem, więc każda tura tworzy nowy wpis po cenie zapisu i nie czyta nic. Panel pokazuje mniej tokenów wejścia i wyższy rachunek, a zespół czyta to tak, jakby model podrożał.
Co z tym robimyLogujemy kolejne zapytania per sesja w gatewayu i porównujemy je bajt po bajcie, żeby znaleźć pierwszą różnicę. Jeśli siedzi w rzekomo statycznym prefiksie, przenosimy dynamiczną edycję pod breakpoint albo wyłączamy warstwę. Potem porównujemy cache_read_input_tokens z całym wejściem na raporcie zużycia dostawcy dla tej samej funkcji, przed zmianą i po niej.
Co zostaje u was: raport diffów per sesja z pozycją pierwszej różnicy dla każdego wywołania oraz udział odczytów z cache przed zmianą i po niej - 2Proxy w łańcuchu gubi znacznik cache
Aplikacja oznacza breakpoint, proxy zgodne z OpenAI przekazuje zapytanie do Bedrocka, a znacznik nigdy nie staje się blokiem cachePoint. Każda warstwa łańcucha dokumentuje obsługę cache promptów i dokładnie to zespół sprawdził. Nic w łańcuchu nie raportuje, że znacznik zginął; jedynym dowodem są tokeny odczytu z cache stojące na zerze po stronie dostawcy.
Co z tym robimyOdtwarzamy jedno stałe zapytanie przez każdy przeskok łańcucha i czytamy pola cache zwracane na każdym z nich, więc warstwa gubiąca znacznik jest nazwana, a nie podejrzewana. Naprawiamy tłumaczenie albo konfigurację na tym przeskoku i powtarzamy odtworzenie, aż dostawca zaraportuje odczyty.
Co zostaje u was: tabela śladu przeskok po przeskoku dla stałego zapytania, z polami cache zwróconymi na każdej warstwie - 3Treść dynamiczna stoi nad breakpointem
Prompt systemowy zaczyna się od bieżącej daty, imienia użytkownika albo identyfikatora zapytania, albo lista narzędzi jest renderowana ze zbioru bez stałej kolejności. Prefiks różni się w każdym wywołaniu o kilka bajtów i dostawca poprawnie traktuje go jako nowy. To najczęstsza przyczyna i najtańsza w naprawie, a bez diffa pozostaje niewidoczna.
Co z tym robimySpisujemy układ promptu jako uporządkowaną listę bloków z częścią statyczną i dynamiczną, przypinamy kolejność narzędzi i przenosimy każdą wartość per zapytanie za ostatni breakpoint. Układ dostaje hash, który gateway sprawdza przy każdym zapytaniu, więc późniejsza edycja łamiąca kolejność wywala test, a nie rachunek.
Co zostaje u was: specyfikacja układu promptu z hashem prefiksu i sprawdzenie w gatewayu, które nie przechodzi, gdy zmieni się część statyczna - 4Alert się odzywa i nic się nie zatrzymuje
U dostawcy jest skonfigurowany alert budżetowy, a w nocy startuje pętla z błędem w warunku wyjścia. Mail przychodzi na progu, dyżurny czyta go rano, a wydatek między jednym a drugim to jest incydent. Alert dostawcy zrobił to, do czego został zaprojektowany; projekt zakładał człowieka w pętli działającego z prędkością zapytań.
Co z tym robimyUstawiamy u dostawcy mechanizm, który naprawdę zatrzymuje ruch: limit wydatku per workspace u Anthropica, twardy limit projektu w OpenAI, spend cap albo funkcję wyłączającą rozliczanie w Google Cloud. Nad tym dokładamy budżety per funkcja i per klient w waszym gatewayu z kontrolowanym błędem i przeprowadzamy próbę, która dochodzi do każdego limitu na projekcie testowym, żeby zapisać opóźnienie egzekwowania.
Co zostaje u was: rejestr limitów per dostawca i per funkcja, z zachowaniem widocznym dla użytkownika po osiągnięciu każdego limitu i wynikami próby - 5Równoległy rozrzut i ponowienia zapisują ten sam prefiks wiele razy
Funkcja wysyła naraz dziesięć zapytań z tym samym prefiksem albo polityka ponowień wysyła pełny prompt przy każdym 429. Anthropic dokumentuje, że wpis w cache staje się dostępny dopiero po rozpoczęciu pierwszej odpowiedzi, więc seria zapisuje wpis raz na zapytanie i nie czyta go nigdy. Wskaźnik trafień wygląda przyzwoicie w średniej i fatalnie dokładnie w tych funkcjach, które się rozrzucają.
Co z tym robimySerializujemy pierwsze zapytanie per prefiks i zwalniamy resztę, gdy wpis już istnieje, ograniczamy ponowienia i dodajemy losowy odstęp, a stosunek zapisów do odczytów mierzymy per funkcja, nie per klucz. Funkcje z rozrzutem dostają własny wiersz w raporcie, żeby średnia nie mogła ich ukryć.
Co zostaje u was: stosunek zapisów do odczytów per funkcja, przed zmianą i po niej, z raportu zużycia dostawcy
Co zostaje u was.
- Raport forensyczny z jednym wierszem na wywołanie: funkcja, sesja, hash prefiksu, trafienie lub chybienie, tokeny per klasa, koszt, przyczyna chybienia
- Przypisanie kosztu per funkcja, per sesja i per klient, uzgodnione z raportem kosztów dostawcy za ten sam okres
- Specyfikacja układu promptu z hashem prefiksu i sprawdzeniem w gatewayu, które nie przechodzi, gdy zmieni się część statyczna
- Rejestr limitów: per dostawca i per funkcja, co zatrzymuje, co powiadamia, co widzi użytkownik i kto dostaje powiadomienie
- Zapis próby: każdy limit osiągnięty raz na projekcie testowym, ze zmierzonym opóźnieniem egzekwowania
- Pomiar przed zmianą i po niej na raporcie zużycia dostawcy, z zapytaniem, którym finanse mogą go powtórzyć
Jak pracujemy.
- 11. Zbieranie
Doklejamy metadane funkcji, sesji i klienta w waszym gatewayu, zapisujemy blok usage z każdej odpowiedzi i pobieramy raporty zużycia i kosztów dostawcy za to samo okno. Tydzień do dwóch tygodni ruchu wystarczy, żeby diff był reprezentatywny.
Tydzień 1-2 - 22. Forensyka
Budujemy tabelę per wywołanie, porównujemy kolejne zapytania w sesji, odtwarzamy stałe zapytanie przez każdy przeskok łańcucha proxy i przypisujemy przyczynę każdemu chybieniu. Przyczyny układamy według pieniędzy, które za nimi stoją.
Tydzień 2-3 - 33. Poprawki i limity
Naprawiamy najdroższe przyczyny u źródła razem z waszym zespołem, spisujemy układ promptu z hashem, a potem ustawiamy limit u dostawcy i budżety per funkcja z ustalonym zachowaniem dla każdej. Każdy limit osiągamy raz na projekcie testowym.
Tydzień 3-5 - 44. Ponowny pomiar i przekazanie
Liczymy tę samą tabelę na raporcie zużycia dostawcy po zmianie i przekazujemy raport, specyfikację układu, sprawdzenie w gatewayu i rejestr limitów. Sprawdzenie zostaje w waszym pipeline, żeby kolejna edycja promptu nie mogła po cichu otworzyć chybień na nowo.
Tydzień 5-6
Pozostałe strony z tej serii.
Pomiar realnego efektu AI w zespole
Dlaczego szybsze zamykanie ticketów nie jest dowodem, co zmierzyły eksperymenty z lat 2025-2026, jak zaprojektować linię bazową, pary metryk i grupę porównawczą oraz których metryk per osoba unikać.
Porównanie narzędzi do AI code review
Dlaczego rankingi dostawców narzędzi do AI code review nie są porównaniami, czego nie publikują (precyzja per klasa, komentarze spoza diffu, zmyślone ustalenia, koszt na PR) i protokół na własne PR-y.
Polityka wkładu AI w kodzie
Polityka do wdrożenia w tydzień: kto podpisuje się pod kodem pisanym z AI, gdzie idzie deklaracja (trailer, pole w szablonie, etykieta), co reviewer zamyka bez czytania i jak to sprawdzać.
Gdzie to zamienia się w usługę.
Optymalizacja kosztów i modeli
Audyt i przebudowa wydatków na LLM - caching, routing, kompresja promptów. Zwykle 20-60% oszczędności.
LLM Observability
Tracing, automatyczna ewaluacja, wykrywanie dryfu - zobacz, co wasz stos agentowy faktycznie robi.
Skąd pochodzą daty i liczby.
- Anthropic, Prompt caching: breakpointy, kolejność, czas życia, mnożniki cen, pola usage, uwaga o równoległości
- Anthropic, Workspaces: miesięczne limity wydatku i limity zapytań per workspace
- Anthropic, Usage and Cost API: grupowanie po kluczu API, workspace i modelu; klasy tokenów z cache i bez
- OpenAI, Prompt caching: dopasowanie prefiksu, minimalna długość, czas przechowywania, rabat za tokeny z cache, pola usage
- OpenAI, Spend limits: twarde limity per organizacja i projekt, kody błędów 429, alerty
- Google, Gemini API context caching: domyślny cache niejawny i minimalne liczby tokenów
- Google, Gemini API cache jawny: domyślny TTL 1 godzina i składniki rozliczenia
- Google, cennik Gemini API: context caching i opłata za przechowywanie na godzinę
- Google Cloud, budżety Cloud Billing: budżety alertowe nie ograniczają wydatku
- AWS, Amazon Bedrock prompt caching: cachePoint, kolejność sekcji, TTL, brak gwarancji trafienia
- Przypadek opisany przez autora na Hacker News: rachunek 37 901,73 USD na Bedrocku po chybieniach cache w łańcuchu proxy
Pytania, które zadają zespoły.
Porozmawiaj z inżynierem.
Napiszcie, na jakim etapie jesteście z rachunkiem za LLM i limitami wydatku. Odpowiedź w jeden dzień roboczy.