Rachunek eksplodował, a nic się nie zmieniło

przez Łukasz | sie 24, 2026

Objawy

Rachunek za bieżący miesiąc jest wielokrotnie wyższy niż za poprzedni. Liczba obsłużonych zadań się nie zmieniła. Nikt niczego nie wdrażał — albo wdrożył coś, co z kosztem nie miało nic wspólnego.

Sygnały, które warto sprawdzić od razu, bo od nich zaczyna się rozpoznanie:

Mediana kosztu na zadanie kontra percentyl dziewięćdziesiąty piąty. Jeśli mediana stoi w miejscu, a ogon odjechał — winne są pojedyncze przebiegi. Jeśli mediana wzrosła — winne są wszystkie.

Tokeny wejściowe na pojedynczy obrót. Rosną z krokami zawsze, ale ich poziom startowy jest stały. Jeśli podskoczył, coś doszło do stałego prefiksu.

Udział tokenów odczytanych z pamięci podręcznej. Spadek do zera przy niezmienionym kodzie promptu to jeden z najczęstszych i najbardziej cichych powodów.

Mechanizm w trzech zdaniach

Przy każdym obrocie pętli cały dotychczasowy kontekst jedzie do modelu od nowa — instrukcje, definicje narzędzi, historia kroków i wszystkie zebrane wyniki. Koszt zadania nie jest więc iloczynem liczby kroków i ceny wywołania, tylko sumą rosnącego ciągu.

Szczegóły tej arytmetyki opisuje artykuł o pętli od środka. Tutaj interesuje nas wyłącznie to, co się w niej zmieniło.

Diagnostyka różnicowa

Siedem przyczyn, w kolejności od najczęstszej. Pierwszy podział jest prosty: czy wzrosła mediana, czy tylko ogon.

1. Zapętlenia — rośnie tylko ogon

Jak rozpoznać: mediana liczby kroków bez zmian, maksimum i percentyl dziewięćdziesiąty piąty wielokrotnie wyższe. Kilka procent przebiegów robi większość rachunku.

Test: policz rozkład liczby kroków. Jeśli maksimum jest trzykrotnie wyższe od mediany, przejdź do poprzedniego syndromu — to ta sama choroba widziana od strony faktury.

2. Rozdęte wyniki narzędzi — rośnie mediana

Jak rozpoznać: liczba kroków bez zmian, ale tokeny wejściowe na krok rosną szybciej niż wcześniej. Zwykle po zmianie w API albo po dodaniu pola do odpowiedzi.

Dlaczego to boli nieproporcjonalnie: jednorazowo duży wynik podnosi koszt wszystkich kolejnych obrotów, bo od tego momentu jest wysyłany ponownie za każdym razem.

Test: znajdź w śladzie krok, po którym przyrost tokenów między obrotami wyraźnie się zwiększa. Zobacz, co zwróciło narzędzie.

3. Zbity cache promptu — rośnie mediana

Jak rozpoznać: udział tokenów z pamięci podręcznej spadł do zera albo blisko zera. Koszt wzrósł o kilkadziesiąt procent równomiernie na wszystkich przebiegach.

Dlaczego się dzieje: cache działa od początku sekwencji do pierwszej różnicy. Wystarczy znacznik czasu, identyfikator sesji albo niestabilna kolejność definicji narzędzi na początku promptu, żeby unieważnić wszystko dalej.

Test: odczytaj z odpowiedzi API liczbę tokenów odczytanych z cache przy dwóch kolejnych wywołaniach z tym samym prefiksem. Zero oznacza rozpoznanie.

To jest przyczyna, którą najłatwiej wprowadzić przypadkiem — jedna linia dopisana do logów potrafi podnieść rachunek, nie powodując żadnej widocznej awarii.

4. Ciche ponowienia — rośnie mediana

Jak rozpoznać: liczba wywołań modelu w rozliczeniu dostawcy jest wyraźnie wyższa niż w twoim logu aplikacji.

Dlaczego się dzieje: większość frameworków ponawia po cichu — przy niepoprawnej strukturze argumentów, przy timeoucie, przy przekroczonym limicie zapytań. Nieudane wywołania bywają rozliczane i nie zostawiają śladu.

Test: porównaj obie liczby za ten sam okres. Rozjazd jest rozpoznaniem.

5. Rozrost katalogu narzędzi — rośnie mediana

Jak rozpoznać: tokeny wejściowe pierwszego obrotu wzrosły. To odróżnia tę przyczynę od wszystkich pozostałych — koszt rośnie, zanim cokolwiek się wydarzy.

Dlaczego się dzieje: ktoś podłączył serwer MCP albo dodał kilka narzędzi. Katalog jedzie do modelu przy każdym obrocie, więc mnoży się przez liczbę kroków.

Test: policz długość samych definicji narzędzi i porównaj z poprzednim wdrożeniem.

6. Model rozumujący — rośnie mediana kosztu wyjścia

Jak rozpoznać: tokeny wyjściowe są wielokrotnie liczniejsze niż widoczna odpowiedź.

Dlaczego się dzieje: tokeny rozumowania są rozliczane po stawce wyjściowej, a w interfejsie zwykle ich nie widać. Jeśli ktoś przełączył model na rozumujący albo podniósł nakład rozumowania, koszt wzrośnie bez żadnej zmiany w kodzie.

Test: odczytaj z odpowiedzi API liczbę tokenów rozumowania i porównaj z długością odpowiedzi.

7. Zmiana po stronie dostawcy

Jak rozpoznać: nic z powyższego nie pasuje, a data wzrostu jest ostra.

Dlaczego się dzieje: aktualizacja modelu potrafi zmienić jego gadatliwość albo skłonność do dłuższego rozumowania. Zmienić mógł się też cennik.

Test: sprawdź, czy zapisujesz wersję modelu w śladzie przebiegu. Jeśli nie — to jest pierwsza rzecz do naprawienia, bo bez tego ta przyczyna jest niediagnozowalna.

Postępowanie

Natychmiast: limit budżetu na przebieg jako warunek zakończenia oraz alert przy przekroczeniu progu dziennego. To zatrzymuje krwawienie w godzinę.

Potem: przejdź podział mediana kontra ogon. Ogon prowadzi do zapętleń, mediana do przyczyn od drugiej do siódmej. Sprawdzenie cache i porównanie liczby wywołań z rozliczeniem zajmuje kilkanaście minut i zamyka trzy z siedmiu możliwości.

Nie zaczynaj od skracania promptu systemowego. To najczęstszy odruch i zwykle najmniejsza pozycja — do tego akurat prompt systemowy jest kandydatem idealnym do cache, więc skracanie go bywa optymalizacją odwrotną do potrzebnej. Najwięcej daje przycięcie wyników narzędzi.

Profilaktyka

Loguj per przebieg: tokeny wejściowe i wyjściowe osobno, tokeny odczytane z cache, tokeny rozumowania, liczbę kroków, liczbę ponowień i wersję modelu. Wszystko to jest w odpowiedzi API — wystarczy zapisać.

Monitoruj rozkłady, nie średnie. Alert warto wieszać na percentylu, bo średnia wygładza dokładnie to, co powinno zapalić lampkę.

Zwracaj z narzędzi uchwyt zamiast danych tam, gdzie wynik bywa obszerny. I trzymaj się zasady: stałe na początku promptu, zmienne na końcu.

Jak zdiagnozować u siebie

Analizator śladów wskazujący, w którą stronę iść.

python
import statistics as st

def diagnoza(przebiegi):
    """przebiegi: lista {'kroki':int,'tokeny_we':int,'tokeny_wy':int,
                         'z_cache':int,'rozumowanie':int,'model':str,
                         'we_pierwszy_obrot':int}"""
    kroki = [p["kroki"] for p in przebiegi]
    koszt = [p["tokeny_we"] + p["tokeny_wy"] for p in przebiegi]

    med_k, max_k = st.median(kroki), max(kroki)
    med_c = st.median(koszt)
    p95 = sorted(koszt)[int(len(koszt) * 0.95) - 1]

    print(f"kroki   mediana {med_k}  maks {max_k}")
    print(f"koszt   mediana {med_c}  p95 {p95}")

    if max_k > 3 * med_k:
        print("→ OGON: podejrzenie zapętleń (przyczyna 1)")
    if p95 > 3 * med_c and max_k <= 3 * med_k:
        print("→ OGON bez zapętleń: sprawdź pojedyncze duże wyniki narzędzi")

    cache = sum(p["z_cache"] for p in przebiegi)
    we = sum(p["tokeny_we"] for p in przebiegi)
    udzial = cache / we if we else 0
    print(f"cache   {udzial:.0%} tokenów wejściowych")
    if udzial < 0.05:
        print("→ MEDIANA: cache promptu nie działa (przyczyna 3)")

    roz = sum(p["rozumowanie"] for p in przebiegi)
    wy = sum(p["tokeny_wy"] for p in przebiegi)
    if wy and roz / wy > 0.5:
        print(f"→ MEDIANA: {roz/wy:.0%} wyjścia to rozumowanie (przyczyna 6)")

    pierwsze = [p["we_pierwszy_obrot"] for p in przebiegi]
    print(f"prefiks {st.median(pierwsze)} tokenów w pierwszym obrocie")
    print("   porównaj z poprzednim wdrożeniem — wzrost = katalog narzędzi (5)")

    modele = {p["model"] for p in przebiegi}
    if len(modele) > 1:
        print(f"→ UWAGA: różne wersje modelu w próbce: {modele} (przyczyna 7)")

przebiegi = [
    {"kroki": 4, "tokeny_we": 12000, "tokeny_wy": 400, "z_cache": 0,
     "rozumowanie": 0, "model": "gpt-4o-mini@2026-05", "we_pierwszy_obrot": 2400},
    {"kroki": 5, "tokeny_we": 15000, "tokeny_wy": 500, "z_cache": 0,
     "rozumowanie": 0, "model": "gpt-4o-mini@2026-05", "we_pierwszy_obrot": 2400},
    {"kroki": 22, "tokeny_we": 98000, "tokeny_wy": 900, "z_cache": 0,
     "rozumowanie": 0, "model": "gpt-4o-mini@2026-05", "we_pierwszy_obrot": 2400},
]
diagnoza(przebiegi)

Powyższa próbka pokazuje dwie przyczyny naraz: jeden przebieg z dwudziestoma dwoma krokami przy medianie cztery oraz zerowy udział cache. Pierwsze to zapętlenie, drugie to zbity prefiks — i akurat te dwie lubią występować razem, bo zapętlony przebieg dokłada do historii coraz to nowe wyniki, przez co żaden fragment sekwencji nie powtarza się między wywołaniami.

Ćwiczenie: puść to na trzydziestu realnych przebiegach z tygodnia przed wzrostem i trzydziestu po. Różnica w tych pięciu liczbach niemal zawsze wskazuje przyczynę bez zaglądania w kod.

Powiązane

Arytmetyka kosztu i cache promptu: Maszyneria agenta. Skąd bierze się koszt długiego kontekstu na poziomie modelu: Anatomia LLM.

Następny syndrom: Agent wybiera złe narzędzie.

Model nie pamięta — pamięta produkt

Model nie pamięta — pamięta produkt

Model nie pamięta ani jednego słowa z waszej poprzedniej wymiany. Wagi są zamrożone, między turami nie zapisuje się nic, a wrażenie ciągłości powstaje przez mechanizm tak prosty, że po jego poznaniu przestaje dziwić połowa rzeczy z tej serii — łącznie z rachunkiem za...

Czego nie widzisz w oknie czatu

Czego nie widzisz w oknie czatu

Okno czatu wygląda jak najkrótsza możliwa droga do modelu, a jest najdłuższą. Między twoim zdaniem a wagami stoi więcej warstw niż w wywołaniu API — i żadnej z nich nie widzisz, nie ustawiasz i nie możesz wyłączyć. W poprzednim torze rozkładaliśmy to, co dzieje się na...