Integrace AI API: OpenAI, Anthropic a lokální modely
Přejít na sekci
Tři ekosystémy, jedna architektura
V produkci budete pracovat minimálně se dvěma providery. OpenAI má nejširší ekosystém, Anthropic nejlepší výkon na složitých úlohách, lokální modely (Ollama, vLLM) vám dají kontrolu nad daty a latencí. Klíč je abstrakce — váš kód by neměl být svázaný s jedním providerem.
Základní vzor je jednoduchý: definujte interface pro LLM volání, implementujte ho pro každého providera a přepínejte přes konfiguraci. V TypeScriptu to znamená společný typ ChatMessage a funkci complete(), která vrací AsyncIterable<string> pro streaming.
// Provider-agnostic interface
interface LLMProvider {
complete(messages: ChatMessage[], opts: CompletionOpts): AsyncIterable<string>;
countTokens(text: string): Promise<number>;
}
// OpenAI implementation
const openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });
const stream = await openai.chat.completions.create({
model: 'gpt-4o',
messages,
stream: true,
});
for await (const chunk of stream) {
yield chunk.choices[0]?.delta?.content ?? '';
}Streaming: proč a jak
Bez streamingu uživatel čeká 5-30 sekund na odpověď. Se streamingem vidí první token za 200-500 ms. Pro UX je to zásadní rozdíl. Ale streaming přidává komplexitu — musíte řešit partial JSON parsing, backpressure a reconnection.
Anthropic SDK používá Server-Sent Events (SSE) nativně. Pro klienta vystavíte vlastní SSE endpoint nebo použijete ReadableStream v Next.js Route Handleru. Důležité: nikdy neposílejte raw provider stream přímo klientovi — vždy transformujte do vlastního formátu, abyste mohli přepnout providera bez změny frontendu.
// Anthropic streaming with proper error handling
import Anthropic from '@anthropic-ai/sdk';
const client = new Anthropic();
const stream = client.messages.stream({
model: 'claude-sonnet-4-20250514',
max_tokens: 4096,
messages,
});
stream.on('text', (text) => {
// Transform to your own event format
res.write(`data: ${JSON.stringify({ type: 'delta', text })}\n\n`);
});
stream.on('error', (err) => {
res.write(`data: ${JSON.stringify({ type: 'error', code: err.status })}\n\n`);
});Error handling a retry strategie
Produkční API volání selhávají. Rate limity (429), server errors (500, 503), timeout, network errors. Bez retry logiky váš systém padá při prvním výpadku. S naivním retry (okamžitý opakovaný pokus) DDoSujete providera a dostanete delší ban.
Exponential backoff s jitterem je standard: první retry za 1s, druhý za 2s, třetí za 4s — plus náhodný jitter 0-500ms, aby se requesty od různých klientů nepotkávaly. Pro 429 respektujte header Retry-After. Pro 500/503 retryujte. Pro 400 (bad request) neretryujte — je to bug ve vašem kódu.
async function withRetry<T>(
fn: () => Promise<T>,
maxRetries = 3
): Promise<T> {
for (let attempt = 0; attempt <= maxRetries; attempt++) {
try {
return await fn();
} catch (err: any) {
if (attempt === maxRetries) throw err;
if (err.status === 400 || err.status === 401) throw err;
const base = Math.pow(2, attempt) * 1000;
const jitter = Math.random() * 500;
await new Promise(r => setTimeout(r, base + jitter));
}
}
throw new Error('Unreachable');
}Lokální modely: kdy a jak
Lokální modely (přes Ollama, vLLM nebo TGI) dávají smysl ve třech scénářích: data nesmí opustit vaši infrastrukturu, potřebujete předvídatelnou latenci bez rate limitů, nebo běžíte high-volume inference kde API náklady přesáhnou cenu GPU. Pro prototyp stačí Ollama s OpenAI-kompatibilním endpointem — váš stávající kód funguje beze změny.
// Ollama with OpenAI-compatible endpoint
const localLLM = new OpenAI({
baseURL: 'http://localhost:11434/v1',
apiKey: 'ollama', // required but unused
});
const response = await localLLM.chat.completions.create({
model: 'llama3.1:8b',
messages,
stream: true,
});Strukturované výstupy a function calling
V produkci potřebujete z modelu dostat strukturovaná data — JSON, ne volný text. OpenAI nabízí Structured Outputs s JSON Schema validací. Anthropic má tool_use, kde definujete input schema. Obojí funguje, ale implementace se liší. Váš wrapper by měl tuto diferenci schovat za jednotný interface.
Kritické pravidlo: nikdy neparsujte raw output modelu přímo. Vždy validujte přes Zod nebo podobnou knihovnu. Model může vrátit syntakticky validní JSON, který nesplňuje vaše business pravidla — chybějící pole, špatný typ, hodnota mimo rozsah. Validace na hranici systému je levnější než debug v produkci.
Nikdy nesvazujte aplikační kód s konkrétním providerem. Abstrakce přes interface vám ušetří týdny práce, až budete potřebovat přepnout model — a ten moment přijde dřív, než čekáte.
Začněte s jedním providerem a abstrakčním layerem. Druhého přidejte, až ho skutečně potřebujete. Over-engineering na začátku bolí víc než refactoring později.
Build & Deploy AI Apps
Nikdy neposílejte raw provider stream přímo klientovi. Vždy transformujte do vlastního event formátu — budete moci přepnout providera bez změny frontendu.
Implementujte LLMProvider interface pro OpenAI a Anthropic. Napište funkci complete(), která přijímá pole zpráv a vrací AsyncIterable<string>. Přidejte retry logiku s exponential backoff. Otestujte přepnutí providera přes environment proměnnou bez změny volajícího kódu.
Nápověda
Použijte factory pattern: createProvider(name: string) vrací správnou implementaci. Pro testy mockujte provider, který vrací předem definované odpovědi.
Implementujte jednoduchou AI-powered funkci s API: 1) Vyberte si providera (OpenAI, Anthropic, nebo open-source), 2) Nastavte API klíč, 3) Napište funkci, která přijme text a vrátí shrnutí (max 3 věty), 4) Přidejte error handling, 5) Změřte latenci a náklady na 100 requestů. Celé by to mělo trvat max 1 hodinu.
Nápověda
Zdokumentujte svůj postup a výsledky — poslouží jako reference pro budoucí podobné úkoly.
Implementujte AI funkci, která z textu extrahuje strukturovaná data. 1) Definujte Zod schéma pro výstup (např. ExtractedContact s polemi name, email, phone, company). 2) Napište prompt, který instruuje model vrátit JSON odpovídající schématu. 3) Validujte odpověď modelu přes Zod. 4) Ošetřete případ, kdy model vrátí nevalidní JSON — retry s upřesněným promptem. Otestujte na 10 různých vstupech.
Nápověda
Zod schema funguje jako kontrakt mezi vaším kódem a modelem. Definujte ho nejdřív a z něho odvozujte TypeScript typy — ne naopak.
- Abstrahujte LLM volání přes interface — provider lock-in je reálné riziko
- Streaming zlepšuje UX o řád, ale vyžaduje vlastní event formát
- Exponential backoff s jitterem je povinný pro produkční retry logiku
- Vždy validujte strukturované výstupy přes schema — model není databáze