Alex (AI asistent pro autoservisy na CoreSynthu) generuje při každém požadavku system prompt o stovkách tisíců toků — statické moduly, nástroje, historie konverzace. Při průměru ~259k input toků na run a ceně $5/1M necacheovaných vs. $0.5/1M cacheovaných je prompt caching rozdíl mezi tím, jestli se projekt finančně vyplatí, nebo ne.
OpenAI automatic prefix caching funguje jednoduše: když prefix requestu (tools → instructions → input) zůstane byte-identický, API ho cacheuje a opakované části se počítají za desetinovou cenu. Žádné speciální breakpointy, žádná konfigurace — jen stabilní prefix.
Měli jsme 0 % cache hit rate. Na všech 108 runech. Nulová.
Diagnostika
Začal jsem auditem celé pipeline: jak se system prompt staví, co se mění mezi requesty, jaké pořadí má OpenAI Responses API (tools → instructions → input).
Hlavní viník byl na první řádku:
buildSessionBanner() → "Now: 2026-06-16, 14:32"
Session banner s timestampem byl na úplném začátku instructions. A buildSystemPrompt() se volal znovu při každém kroku agenta s new Date(). Výsledek: jakmile se přečte minuta, prefix je jiný → cache bust → plná cena za celý input.
Dva sekundární viníci:
- Stav košíku — připojoval se do
instructionskdyž byl neprázdný. Měnil se mezi kroky. - customInstructions — per-user instrukce v prefixu. Sice stabilní v rámci jednoho uživatele, ale blokoval cross-user cache sharing.
Fix: zmraz prefix, dynamiku ven
Řešení je jednoduché na popis, složitější na implementaci: všechno, co se mění, musí jít za statický prefix do aktuálního user turnu.
PŘED (prefix se mění):
instructions[0] = "Now: 14:32, Cart: 3 items, custom: ..." ← BUST
instructions[1+] = statické .md moduly
tools = deterministické
input = historie
PO (prefix zmrazen):
instructions[0] = statické .md moduly ← STABILNÍ
tools = deterministické
input = historie + <current_time> + <cart_state> + <custom_instructions>
Volatilní kontext se nyní skládá přes buildVolatileContextXml() — XML bloky na konci aktuálního user message. Session banner, stav košíku, custom instrukce — všechno za prefixem. Prefix je byte-identický napříč requesty, kroky i uživateli.
Výsledky
PŘED: 108 runů, 28M input tokenů, 0 cacheovaných → hit rate 0 %
PO: Všech 5 testovaných scénářů ukázalo cache hit → 5–19 %
Proxy cache propaguje — baseline 0 % byla způsobená volatilním prefixem, ne proxy problémem. Po fixech se cached_tokens začaly objevovat ve všech testovaných konverzacích.
Co jsem se naučil
Prefix caching není magie. OpenAI ho dělá automaticky, ale jen když mu dáš stabilní prefix. Jedna timestamp sekunda na pozici 0 zničí cache pro celý request.
Měř před a po. Bez instrumentace cache_read_input_tokens bych nikdy nezjistil, jestli fix funguje. Přidal jsem tracking do databáze (schema migration + logování), což umožnilo srovnání baseline vs. post-fix.
Dynamika patří na konec. Generální pravidlo pro LLM caching: všechno statické na začátek, všechno dynamické na konec. Nejen timestamp — i stav košíku, per-user instrukce, kontext konverzace.
Testy musí projít. 322 testů, lint, TypeScript check — všechno čisté. Prompt caching je core feature, ne optimalizace „na rychlo”.
Tech detail
PATTERN → OpenAI automatic prefix caching (Responses API)
FIX → Volatilní kontext z instructions do user message tail
METRIKY → cache_read_input_tokens (schema migration + DB)
NÁSTROJE → TypeScript, Prisma, OpenAI Responses API
VÝSLEDEK → 0% → 5–19% hit rate, ~90% snížení input nákladů na cacheované tokeny
Projekt běží na OpenAI GPT-5.5 přes CoreSynth proxy. Cíl není jen ušetřit — je to o tom, aby AI agent byl finančně udržitelný i při masivním system promptu a vysokém throughputu.