Volání LLM je obyčejný HTTP požadavek, ale chová se jinak než API, na která jsi zvyklý: odpovídá v řádu sekund, počítá si tě podle tokenů, umí posílat odpověď po kouscích a občas ti prostě řekne „teď ne, zkus později“. Tahle kapitola je o tom, jak takové volání napsat, aby ti nespadlo v produkci.
Jak vypadá požadavek
POST /v1/messages
Authorization: Bearer sk-...
Content-Type: application/json
{
"model": "nazev-modelu",
"max_tokens": 1000,
"temperature": 0.2,
"messages": [
{ "role": "user", "content": "Shrň tenhle text do tří bodů: ..." }
]
}
Odpověď obsahuje vygenerovaný text, důvod ukončení a spotřebu tokenů:
{
"content": [{ "type": "text", "text": "• ...\n• ...\n• ..." }],
"stop_reason": "end_turn",
"usage": { "input_tokens": 412, "output_tokens": 88 }
}
💡
usagesi loguj vždycky. Bez něj nezjistíš, která část aplikace ti utrácí peníze, a nemáš jak hlídat trend, když se prompty postupně nafukují.
Streaming
Bez streamingu čeká uživatel na celou odpověď (klidně 10 sekund) a kouká na prázdno. Se streamingem dostáváš tokeny průběžně a rovnou je vypisuješ.
klient ──▶ server ──▶ LLM
◀── "Ob" "jed" "návka" " je" ...
klient ◀── SSE / websocket
Na co dát pozor:
- Chyba může přijít i uprostřed streamu, když už jsi něco zobrazil. Musíš to umět ošetřit.
- JSON se streamovat nedá rozumně: dokud není celý, nedá se parsovat. Struktura = bez streamu.
- Zrušení požadavku ukončí generování a tím i placení. Když uživatel odejde ze stránky, zruš to.
Chyby, které tě potkají
| Stav | Co znamená | Co dělat |
|---|---|---|
429 | Překročil jsi limit (rate limit) | Počkat a zkusit znovu s exponenciálním backoffem |
529 / 503 | Model je přetížený | Totéž, případně přepnout na jiný model |
400 | Špatný požadavek (moc tokenů, špatný formát) | Neopakovat, je to chyba v kódu |
401 | Špatný klíč | Neopakovat |
| timeout | Odpověď nedorazila včas | Opakovat, ale s nižším limitem tokenů |
Retry piš jen na dočasné chyby (429, 5xx, timeout), s náhodným rozptylem (jitter), aby ti všechny instance nezkoušely znovu ve stejnou milisekundu. Trvalé chyby opakovat nemá smysl a jen platíš.
Timeouty a latence
LLM je pomalé API. Počítej s tím:
- Nastav timeout vědomě (např. 60 s pro dlouhé odpovědi), ne default knihovny.
- Nevolej to synchronně v HTTP requestu, pokud odpověď trvá dlouho, použij frontu a job.
- Latence roste s délkou výstupu, ne vstupu. Když potřebuješ rychlost, zkrať odpověď.
Klíče a bezpečnost
- Klíč nikdy nedávej do frontendu. Ani „dočasně“, vytáhne ho kdokoli z prohlížeče a bude utrácet tvoje peníze. Volání jde vždy přes tvůj backend.
- Klíč patří do proměnných prostředí, ne do gitu.
- Nastav si limit útraty u poskytovatele. Chyba ve smyčce umí za noc utratit měsíční rozpočet.
- Loguj prompty opatrně. Můžou obsahovat osobní údaje zákazníků, viz bezpečnost dat.
Kostra, která obstojí
1. sestav prompt (a spočítej tokeny, ať víš, že se vejde)
2. zavolej API s timeoutem
3. retry na 429/5xx s backoffem, max 3 pokusy
4. zkontroluj stop_reason (usekla se odpověď?)
5. zvaliduj obsah (schéma, rozsahy)
6. zaloguj usage + latenci + verzi promptu
7. při selhání fallback: jiný model / cache / hláška uživateli
Cvičení
- Volání občas vrátí 429 a občas se zasekne na dvě minuty. Napiš, jak se má chovat retry v obou případech a proč se liší.
- Streamuješ odpověď a uživatel zavře záložku. Co se stane s účtem?
Náčrt řešení: rozbal, až si cvičení zkusíš sám
- Na 429 opakuj s exponenciálním odstupem a jitterem, na timeout opakuj nejvýš jednou. 429 znamená „teď ne, zkus později“, takže má smysl počkat a zopakovat, ale s náhodným rozptylem, jinak se ti všechny paralelní požadavky sejdou v jedné sekundě a poskytovatele dorazíš sám. Timeout je jiný případ: požadavek se možná zpracovává dál, takže opakováním platíš dvakrát. U timeoutu proto raději jeden pokus a pak fallback nebo poctivá chyba.
- Zaplatíš za tokeny, které se do zavření stihly vygenerovat. Streamování je jen způsob doručení, ne účtování. Pokud tvůj kód spojení opravdu ukončí, generování se zastaví a dál neplatíš; pokud ho drží nějaká fronta nebo proxy, doběhne celé. Proto se u dlouhých odpovědí hlídá zrušení požadavku, ne jen zavření prohlížeče.
Shrnutí
- Volání je běžné HTTP, ale pomalé, placené podle tokenů a občas přetížené.
- Streaming zlepší vnímanou rychlost, ale komplikuje chyby a nejde s ním parsovat JSON.
- Opakuj jen dočasné chyby, s backoffem a jitterem; klíč nikdy nepatří do frontendu.
- Vždy loguj spotřebu tokenů, latenci a důvod ukončení.
Dostáváš `429`. Co uděláš?
Počkáš a zkusíš znovu s exponenciálním backoffem a náhodným rozptylem, s omezeným počtem pokusů. Zároveň se podíváš, jestli nejde snížit zátěž, kratší prompty, cache, dávkování nebo rozložení požadavků v čase.
Proč nedávat API klíč do frontendové aplikace, i když je to „jen interní nástroj“?
Protože všechno, co je v prohlížeči, si může kdokoli přečíst, včetně klíče, kterým pak bude utrácet na tvůj účet. Volání LLM patří na backend, který drží klíč a řídí autorizaci i limity.
Aplikace vrací uživatelům občas useknuté odpovědi a nikdo neví proč. Kde hledat?
U stop_reason. Když je length, došel limit max_tokens nebo se vstup s výstupem nevešly do
kontextového okna. Proto se důvod ukončení kontroluje a loguje, jinak je to tichá chyba.
