Klient, dla którego zbudowałem chatbota RAG do obsługi dokumentacji wewnętrznej, dostał w pierwszym pełnym miesiącu produkcyjnym fakturę z Anthropica na 1247 dolarów. Rok temu, przy podobnym ruchu, byłoby to akceptowalne. W 2026 to po prostu marnowanie pieniędzy. Po dwóch tygodniach optymalizacji ten sam projekt kosztuje go 358 dolarów miesięcznie i działa szybciej niż wcześniej.
Ten artykuł to lista pięciu dźwigni, których użyłem, z konkretnym kodem, liczbami i sytuacjami, w których optymalizacja nie ma sensu. Piszę z perspektywy developera, który bierze odpowiedzialność za faktury klienta, a nie z perspektywy autora bloga, któremu zapłaci Anthropic za hype.
Skąd biorą się koszty API w 2026
Przed optymalizacją trzeba zrozumieć, za co płacisz. Anthropic w połowie 2026 ma trzy aktywne modele i każdy z nich ma osobny cennik za input tokens i output tokens. Output jest standardowo 4-5 razy droższy od inputu, więc to dwa różne problemy.
Aktualne ceny per milion tokenów (stan na czerwiec 2026):
| Model | Input | Output | Cached input (read) |
|---|---|---|---|
| Claude Opus 4.7 | 15$ | 75$ | 1.50$ |
| Claude Sonnet 4.6 | 3$ | 15$ | 0.30$ |
| Claude Haiku 4.5 | 0.80$ | 4$ | 0.08$ |
Pierwsza obserwacja: cached input jest dziesięć razy tańszy od standardowego inputu. To nie jest mała optymalizacja, to fundamentalna zmiana ekonomii. Druga obserwacja: różnica między Opusem a Haiku to czynnik 18x na inpucie i 18x na outpucie. Jeśli używasz Opusa do klasyfikacji intencji, palisz pieniądze.
U mojego klienta przed optymalizacją 78% kosztów szło na input tokens. System prompta z polityką firmy, szablonami odpowiedzi i przykładami miał 14 tysięcy tokenów i był wysyłany w każdym zapytaniu. 4500 zapytań dziennie razy 14k tokenów razy 3$ za milion - 189 dolarów dziennie tylko na powtarzanie tego samego promptu.
Dźwignia pierwsza: prompt caching
Prompt caching pozwala oznaczyć fragment promptu jako cachowalny. Anthropic zapisuje go po stronie swoich serwerów na 5 minut (standard) albo 1 godzinę (extended cache). Każde kolejne zapytanie, które trafia w ten sam cache, płaci za ten fragment dziesięć razy mniej.
Co cachować:
- System prompt (zawsze taki sam)
- Dokumenty kontekstowe RAG, jeśli są reużywane między userami
- Few-shot examples
- Schematy tools/function calling
Czego nie cachować:
- User input (zmienia się za każdym razem)
- Dynamiczny kontekst krótszy niż 1024 tokeny (Anthropic ma minimum)
- Cokolwiek, co zmienia się częściej niż co 5 minut, bo cache się odbuduje
Praktyczne ułożenie wiadomości w Anthropic SDK:
from anthropic import Anthropic
client = Anthropic()
response = client.messages.create(
model="claude-sonnet-4-6",
max_tokens=1024,
system=[
{
"type": "text",
"text": "Jesteś asystentem firmy X. Stosujesz politykę..."
"[14k tokenów polityki, szablonów, przykładów]",
"cache_control": {"type": "ephemeral"}
}
],
messages=[
{"role": "user", "content": user_question}
]
)
Pierwszy request płaci 25% więcej za input (write to cache). Każdy kolejny w ciągu 5 minut płaci 10% standardowej ceny (read from cache). Break-even pojawia się przy drugim zapytaniu w oknie czasowym.
U mojego klienta z chatbotem to była największa pojedyncza zmiana. Średnio 3.2 zapytania na sesję usera mieściło się w 5-minutowym oknie. Przy 4500 zapytań dziennie i 14k tokenów cachowanego promptu zaoszczędziłem mu 142$ dziennie. To jedna dźwignia, jedna noc pracy.
Kiedy nie pomaga: jeśli masz 100 userów na dzień i każdy odpala jedno zapytanie, większość zapytań nie trafi w cache. Wtedy włącz extended cache (1h) - jest droższy w write, ale dla low-traffic systemów ratuje budżet.
Dźwignia druga: model routing
Model routing to po prostu decyzja, który model dostanie którą prośbę. W moich projektach 60-70% zapytań to rzeczy, z którymi Haiku 4.5 radzi sobie świetnie. Klasyfikacja, ekstrakcja, krótkie odpowiedzi z RAG, formatowanie. Sonnet 4.6 dostaje analizę, multi-step reasoning. Opus 4.7 włączam tylko do trudnych przypadków - długi kontekst, niejasne wymagania, gdy stawka błędu jest wysoka.
Najprostsza implementacja routingu to dwustopniowy proces. Najpierw Haiku klasyfikuje trudność, potem właściwy model wykonuje zadanie:
def classify_complexity(question: str) -> str:
response = client.messages.create(
model="claude-haiku-4-5",
max_tokens=10,
system="Sklasyfikuj zapytanie jako: SIMPLE, MEDIUM, HARD. "
"SIMPLE = lookup faktów, MEDIUM = analiza/synteza, "
"HARD = wieloetapowe rozumowanie, niejasny kontekst.",
messages=[{"role": "user", "content": question}]
)
return response.content[0].text.strip()
MODEL_MAP = {
"SIMPLE": "claude-haiku-4-5",
"MEDIUM": "claude-sonnet-4-6",
"HARD": "claude-opus-4-7",
}
def route_and_answer(question: str):
complexity = classify_complexity(question)
model = MODEL_MAP[complexity]
return client.messages.create(
model=model,
max_tokens=1024,
messages=[{"role": "user", "content": question}]
)
Krok klasyfikacji kosztuje grosze - Haiku, max_tokens=10, prawdopodobnie 50-100 tokenów inputu. Dla 4500 zapytań to dodatkowe 1.5$ dziennie, a oszczędności sięgają 40-60% kosztów modelu właściwego.
Dla bardziej zaawansowanego routingu używam pre-klasyfikatora opartego na regexach i prostych heurystykach (długość zapytania, słowa kluczowe), żeby nawet Haiku nie był wołany niepotrzebnie. To standardowy pattern - nie każda decyzja wymaga LLM.
Pułapka: nie ufaj klasyfikatorowi w trybie produkcyjnym bez fallbacku. Co jakiś czas Haiku zaklasyfikuje "podsumuj 200-stronicowy kontrakt" jako SIMPLE. Dla krytycznych zastosowań dodaj walidację długości kontekstu i forsuj większy model, gdy input przekracza próg.
Dźwignia trzecia: batching
Anthropic Batch API daje 50% rabatu na input i output. Limit się ostro rozwinął - w 2026 max 300 tysięcy tokenów per request w becie i do 100 tysięcy requestów per batch. Czas wykonania to do 24 godzin, ale w praktyce większość batchy kończy się w 1-2 godziny.
Co pasuje do batcha:
- Generowanie raportów nocnych
- Indexing dokumentów do RAG (embeddings + summarization)
- Reklasyfikacja historycznych danych
- Async enrichment (np. wzbogacanie leadów)
- Testy A/B promptów na dużym zbiorze
Co nie pasuje: cokolwiek, co user czeka w synchronicznym requestcie. Chatbot, asystent w aplikacji, real-time klasyfikacja.
Kod w Anthropic SDK:
from anthropic import Anthropic
from anthropic.types.messages.batch_create_params import Request
client = Anthropic()
requests = [
Request(
custom_id=f"doc-{i}",
params={
"model": "claude-sonnet-4-6",
"max_tokens": 512,
"messages": [{"role": "user", "content": f"Streść: {doc}"}]
}
)
for i, doc in enumerate(documents)
]
batch = client.messages.batches.create(requests=requests)
# Polluj batch.id albo subskrybuj webhooka
U klienta z e-commerce zbatchowałem nocne generowanie opisów produktów - 12 tysięcy SKU, każdy potrzebował opisu dla SEO. Synchroniczne wykonanie kosztowałoby 184$ jednorazowo. Batchem - 92$. Czas wykonania całości: 2 godziny 18 minut.
Dla PHP / Laravel używam analogicznego patternu przez bezpośrednie wywołanie endpointu /v1/messages/batches:
$response = Http::withToken(config('services.anthropic.key'))
->withHeaders(['anthropic-version' => '2023-06-01'])
->post('https://api.anthropic.com/v1/messages/batches', [
'requests' => collect($products)->map(fn($p) => [
'custom_id' => "product-{$p->id}",
'params' => [
'model' => 'claude-sonnet-4-6',
'max_tokens' => 512,
'messages' => [[
'role' => 'user',
'content' => "Napisz opis SEO dla: {$p->name}"
]],
],
])->toArray(),
]);
Dźwignia czwarta: kontrola output tokens
Output jest 4-5 razy droższy od inputu. Każdy zbędny token w odpowiedzi to ekspresowy bilet do większej faktury. Trzy techniki, których używam.
Pierwsza: agresywny max_tokens. Dla klasyfikacji - 10. Dla krótkiej odpowiedzi - 256. Dla pełnej odpowiedzi w chatbocie - 1024. Domyślne 4096 to zaproszenie do gadulstwa.
Druga: structured output przez tool use. Zamiast prosić "odpowiedz w JSON", definiujesz schema i Claude generuje tylko wymagane pola. Zero owijania w "Oczywiście, oto JSON, którego oczekiwałeś:".
tools = [{
"name": "extract_invoice",
"description": "Wyciągnij dane z faktury",
"input_schema": {
"type": "object",
"properties": {
"invoice_number": {"type": "string"},
"total": {"type": "number"},
"vat": {"type": "number"},
"issue_date": {"type": "string"},
},
"required": ["invoice_number", "total"]
}
}]
response = client.messages.create(
model="claude-sonnet-4-6",
max_tokens=300,
tools=tools,
tool_choice={"type": "tool", "name": "extract_invoice"},
messages=[{"role": "user", "content": invoice_text}]
)
Wymuszony tool_choice gwarantuje, że output to czysty JSON, bez tekstu wokół. U mnie dla pipeline'u ekstrakcji faktur to obniżyło średni output z 380 tokenów do 95.
Trzecia: instrukcja "bądź zwięzły" w system prompt działa, ale słabo. Strukturalna kontrola przez schemat działa zawsze.
Dźwignia piąta: kompresja kontekstu
Najgorszy antypattern, który widzę u klientów: wrzucanie do każdego zapytania całej historii rozmowy plus pełnej dokumentacji "na wszelki wypadek". 50 tysięcy tokenów inputu na pytanie, na które wystarczyłoby 800.
RAG zamiast wrzucania całości. Embeddings dokumentów, vector search, wrzucanie tylko top 3-5 chunków, które trafiają w pytanie. To standardowy ruch, ale w 2026 widzę go zaimplementowanego źle wystarczająco często, że nie zaszkodzi przypomnieć.
Konkretna konfiguracja, którą stosuję:
- Chunk size 500-800 tokenów (nie 2000)
- Top-K = 4 dla większości przypadków
- Re-ranking modelem cross-encoder, jeśli precision matters
- Hybrid search (BM25 + dense) dla domen z dużą ilością nazw własnych
Drugi temat: kompresja długich rozmów. Po N turach rozmowy zamiast wrzucać całą historię, generuję podsumowanie wcześniejszych etapów i wysyłam podsumowanie + ostatnie 4-6 wiadomości. Sumaryzator używa Haiku, koszt jest pomijalny, redukcja inputu - 60-80% w długich sesjach.
Implementacja w pseudokodzie:
def build_context(history: list, max_recent: int = 6):
if len(history) <= max_recent:
return history
older = history[:-max_recent]
recent = history[-max_recent:]
summary = client.messages.create(
model="claude-haiku-4-5",
max_tokens=400,
system="Streszcz rozmowę zachowując kluczowe fakty i decyzje.",
messages=[{"role": "user", "content": serialize(older)}]
)
return [
{"role": "system", "content": f"Podsumowanie: {summary.content[0].text}"},
*recent
]
Bonus: monitoring i alerty na koszty
Optymalizacja, której nie monitorujesz, regresuje. Po wdrożeniu pięciu dźwigni założyłem prosty pipeline monitoringu, bo zmiany w kodzie, które dorzucają nowe prompt templates, łatwo psują cache hit rate.
Co śledzę u każdego klienta z LLM w produkcji:
- Cache hit rate - procent zapytań, które trafiły w cache (cel: powyżej 70% dla high-traffic)
- Średnia liczba input tokens per request (rośnie - znaczy, że context puchnie niekontrolowanie)
- Średnia liczba output tokens per request (rośnie - max_tokens jest za luźne albo nowy prompt zachęca do gadulstwa)
- Rozkład requestów per model (jeśli Opus rośnie kosztem Sonneta - klasyfikator dryftuje)
- Koszt per user / per session - to metryka, którą pokazuję klientowi, bo łączy techniczne z biznesowym
W Anthropic Console od końca 2025 są wbudowane dashboardy z tymi metrykami, ale zwykle eksportuję dane przez Usage API i wrzucam do Metabase / Grafana, żeby skleić z metrykami biznesowymi (liczba aktywnych userów, konwersje). To pozwala odpowiedzieć na pytanie "ile kosztuje obsługa jednego klienta", a nie tylko "ile zapłaciliśmy Anthropicowi".
Alert, który zawsze ustawiam: powiadomienie, jeśli dzienny koszt przekroczył 130% średniej z ostatnich 7 dni. Trzy razy w karierze ten alert złapał regresję, której nikt nie widział - raz buga w kodzie, który puszczał infinite loop wywołań do API, raz nową integrację, która zapomniała o cachingu, raz nowego klienta, który robił testy obciążeniowe i zapomniał uprzedzić.
Realny case: chatbot RAG dla klienta
Klient z branży B2B, dokumentacja techniczna ich produktu, około 2400 stron. Chatbot na stronie, średnio 4500 zapytań dziennie, peak 9000. Stack: Python, FastAPI, Anthropic SDK, Pinecone.
Stan przed optymalizacją (luty 2026):
- System prompt 14k tokenów wysyłany w każdym requeście
- Każde zapytanie do Sonneta 4.6, niezależnie od trudności
- RAG z chunkami po 2000 tokenów, top-K = 8
- Brak limitu output tokens, średnia odpowiedź 900 tokenów
- Koszt miesięczny: 1247$
Stan po (kwiecień 2026):
- Prompt caching na system prompt + szablon RAG (oszczędność 73% input cost)
- Routing: Haiku dla 64% zapytań (proste lookup), Sonnet dla 34%, Opus dla 2%
- Chunki po 700 tokenów, top-K = 4, hybrid search
- max_tokens = 600, structured output dla intencji
- Nightly batch dla refresh embeddings + analytics
- Koszt miesięczny: 358$
Redukcja: 71%. Czas implementacji: 18 godzin pracy rozłożonych na 2 tygodnie. Klient odzyskuje koszt mojej pracy w ciągu trzech miesięcy i ma stałe oszczędności na resztę życia projektu.
Co istotne - latency też się poprawiła. Routing do Haiku dla prostych pytań skrócił średni czas odpowiedzi z 3.4s do 1.1s. Mniejszy kontekst RAG zmniejszył time-to-first-token. User nie wie nic o optymalizacji kosztów, czuje, że bot jest szybszy.
Kiedy nie optymalizować
Optymalizacja kosztów ma sens, gdy faktura jest realna. Jeśli prototypujesz, masz 50 zapytań dziennie i rachunek 12$ miesięcznie - zostaw to. Czas, który spędzisz na cachingu, kosztuje więcej niż roczny rachunek.
Druga sytuacja: latency-critical produkty. Routing przez klasyfikator dodaje 200-400ms. Jeśli masz SLA "odpowiedź w 800ms" i Sonnet wyrabia, nie wprowadzaj routingu. Czyste Sonnet plus prompt caching wystarczy.
Trzecia: low-traffic z high-stakes. System wsparcia decyzji medycznych z 30 zapytaniami dziennie. Tu nie pchasz Haiku ani batcha - płacisz za Opusa, walidujesz, śpisz spokojnie. Optymalizacja kosztów to nie cel sam w sobie.
Generalna zasada, którą stosuję u klientów: jeśli koszt API stanowi mniej niż 5% wartości generowanej przez system, nie warto się męczyć. Powyżej 15% - obowiązkowo. Między - decyzja zależy od tego, jak szybko skaluje się ruch.
Kolejność, w której optymalizuję
Jeśli dostaję projekt do audytu i mam 8 godzin, idę dokładnie tą kolejnością i zwykle to wystarczy:
- Prompt caching - największy ROI na godzinę pracy. 30 minut implementacji, redukcja kosztów input o 70-90%. Zawsze zaczynam od tego.
- max_tokens i structured output - kolejne 30 minut. Dla ekstrakcji / klasyfikacji wprowadzam tool use ze schemą. Łatwy win.
- Model routing - 2-3 godziny. Najpierw analiza dystrybucji zapytań (jakiej trudności zadania dostaję), potem klasyfikator i mapowanie. Wymaga testów, żeby nie zdegradować jakości.
- Kompresja kontekstu RAG - 2-3 godziny. Konfiguracja chunków, top-K, eventualnie hybrid search. Tu trzeba mieć eval set, żeby nie pogorszyć recall.
- Batching - tyle, ile zajmie zidentyfikowanie async workloadów i przeniesienie ich. Dla niektórych projektów zero, dla innych połowa kosztu.
To kolejność z ostatnich pięciu audytów. Pierwsze dwie pozycje są zawsze, kolejne zależą od architektury. U klienta z chatbotem szóstą pozycją było wdrożenie monitoringu, o którym pisałem wyżej - i to się szybko zwróciło, bo dwa miesiące po optymalizacji koszt zaczął rosnąć i monitoring pokazał, że nowa wersja system promptu rozwaliła cache structure.
Materiały, które uzupełniają temat: ile kosztuje chatbot AI - jak liczyć ROI projektu z LLM, RAG chatbot firmowy - architektura, którą używałem w opisanym case, zero hallucination protocol - struktura promptu wpływa nie tylko na jakość, ale i na koszt.
Twój projekt z LLM kosztuje więcej niż się spodziewałeś? Napisz do mnie - zrobię audyt kosztów i pokażę, gdzie tracisz pieniądze.