Masz klucz API od Anthropic i chcesz zbudować coś realnego. Nie kolejne "Hello, World!" z LLM — prawdziwy agent, który potrafi używać narzędzi, streamować odpowiedzi i nie wywracać się na produkcji. Ten artykuł to droga od zera do działającego agenta w TypeScript.
Klucz API i setup
Klucz generujesz na console.anthropic.com. Po zalogowaniu: **API Keys → Create Key**. Zapisz go od razu — nie zobaczysz go drugi raz.
Instalacja SDK:
```bash npm install @anthropic-ai/sdk ```
Podstawowa konfiguracja klienta:
```typescript import Anthropic from '@anthropic-ai/sdk';
const anthropic = new Anthropic({ apiKey: process.env.ANTHROPIC_API_KEY, }); ```
Klucz trzymaj w zmiennej środowiskowej. Nigdy nie hardkoduj w kodzie. SDK domyślnie szuka `ANTHROPIC_API_KEY` w env — jeśli zmienna jest ustawiona, możesz pominąć parametr `apiKey` w konstruktorze.
Pierwsze zapytanie: messages.create()
Podstawowe wywołanie:
```typescript const message = await anthropic.messages.create({ model: 'claude-sonnet-4-6', max_tokens: 1024, system: 'Jesteś pomocnym asystentem technicznym. Odpowiadasz zwięźle i konkretnie.', messages: [ { role: 'user', content: 'Wyjaśnij czym jest connection pooling w kontekście PostgreSQL.', }, ], });
console.log(message.content[0].type); // 'text' console.log((message.content[0] as Anthropic.TextBlock).text); ```
Kilka rzeczy wartych uwagi:
- `model` — wybierasz konkretną wersję modelu. Nie "claude" jako ogólne pojęcie. - `max_tokens` — maksymalna liczba tokenów w odpowiedzi. Zawsze ustaw. Brak limitu może kosztować. - `system` — prompt systemowy. Określa zachowanie modelu dla całej konwersacji. - `message.content` to tablica bloków — może zawierać text lub tool_use. Sprawdzaj `type` przed castowaniem.
Typy są kluczowe. Anthropic SDK jest w pełni otypowany, korzystaj z tego — `Anthropic.TextBlock`, `Anthropic.ToolUseBlock`, `Anthropic.Message` etc.
Streaming: dlaczego ma znaczenie dla UX
Bez streamingu użytkownik czeka na cały response zanim zobaczy cokolwiek. Przy dłuższych odpowiedziach to 3-10 sekund pustego ekranu. Streaming daje natychmiastowy feedback:
```typescript const stream = anthropic.messages.stream({ model: 'claude-sonnet-4-6', max_tokens: 1024, messages: [{ role: 'user', content: 'Opisz architekturę mikroserwisów.' }], });
stream.on('text', (text) => { process.stdout.write(text); });
const finalMessage = await stream.finalMessage(); console.log('\nStop reason:', finalMessage.stop_reason); ```
`stream.on('text', ...)` odpala się dla każdego kawałka tekstu. W aplikacji webowej wpisujesz te kawałki do stanu React lub serwujesz przez SSE do frontendu.
Alternatywny styl z `for await`:
```typescript const stream = await anthropic.messages.create({ model: 'claude-sonnet-4-6', max_tokens: 1024, messages: [{ role: 'user', content: 'Twoje pytanie' }], stream: true, });
for await (const event of stream) { if (event.type === 'content_block_delta' && event.delta.type === 'text_delta') { process.stdout.write(event.delta.text); } } ```
Tool use (function calling)
Tool use to mechanizm, który pozwala modelowi "wywoływać funkcje" — w praktyce model zwraca strukturyzowany JSON opisujący jakie narzędzie chce wywołać i z jakimi argumentami. Twój kod faktycznie wykonuje tę funkcję i zwraca wynik.
Definicja narzędzia:
```typescript const tools: Anthropic.Tool[] = [ { name: 'get_weather', description: 'Pobiera aktualną pogodę dla podanego miasta.', input_schema: { type: 'object' as const, properties: { city: { type: 'string', description: 'Nazwa miasta, np. "Warszawa"', }, unit: { type: 'string', enum: ['celsius', 'fahrenheit'], description: 'Jednostka temperatury', }, }, required: ['city'], }, }, ]; ```
Wywołanie z narzędziami:
```typescript const response = await anthropic.messages.create({ model: 'claude-sonnet-4-6', max_tokens: 1024, tools, messages: [{ role: 'user', content: 'Jaka jest pogoda w Krakowie?' }], });
if (response.stop_reason === 'tool_use') { const toolUse = response.content.find( (block): block is Anthropic.ToolUseBlock => block.type === 'tool_use' ); console.log('Model chce wywołać:', toolUse?.name); console.log('Z argumentami:', toolUse?.input); } ```
Pełny agent loop w TypeScript
Agent loop to wzorzec, w którym model może wielokrotnie wywoływać narzędzia aż do osiągnięcia celu. Pełna, działająca implementacja:
```typescript import Anthropic from '@anthropic-ai/sdk';
const anthropic = new Anthropic();
async function runAgent(userQuery: string): Promise
const messages: Anthropic.MessageParam[] = [ { role: 'user', content: userQuery }, ];
while (true) { const response = await anthropic.messages.create({ model: 'claude-sonnet-4-6', max_tokens: 4096, tools, messages, });
// Dodaj odpowiedź asystenta do historii messages.push({ role: 'assistant', content: response.content });
if (response.stop_reason === 'end_turn') { // Model skończył — zwróć ostatni blok tekstowy const textBlock = response.content.find( (b): b is Anthropic.TextBlock => b.type === 'text' ); return textBlock?.text ?? ''; }
if (response.stop_reason === 'tool_use') { const toolResults: Anthropic.ToolResultBlockParam[] = [];
for (const block of response.content) { if (block.type !== 'tool_use') continue;
let result: string; if (block.name === 'calculate') { const input = block.input as { expression: string }; try { result = String(eval(input.expression)); } catch { result = 'Błąd obliczenia'; } } else if (block.name === 'search_docs') { result = 'Wyniki wyszukiwania: [przykładowe dane]'; } else { result = 'Nieznane narzędzie'; }
toolResults.push({ type: 'tool_result', tool_use_id: block.id, content: result, }); }
messages.push({ role: 'user', content: toolResults }); } } }
// Użycie runAgent('Ile to jest 847 * 293?').then(console.log); ```
Ten kod jest kompletny i działa. Kluczowe elementy: pętla `while (true)`, sprawdzanie `stop_reason`, dodawanie wyników narzędzi jako `tool_result` w wiadomości od użytkownika.
Modele: który kiedy
Anthropic oferuje trzy poziomy modeli (stan na 2026):
**claude-opus-4-7** — najsilniejszy model. Najlepszy do złożonych zadań analitycznych, długich dokumentów, trudnych zadań wymagających rozumowania. Najdroższy. Używaj gdy jakość jest krytyczna i masz budżet.
**claude-sonnet-4-6** — złoty środek. Doskonały stosunek jakości do ceny dla większości zastosowań produkcyjnych: chatboty, agencie pętle, generowanie kodu, analiza danych. To domyślny wybór dla 80% przypadków.
**claude-haiku-4-5** — najszybszy i najtańszy. Świetny do prostych zadań: klasyfikacja, ekstrakcja danych, krótkie odpowiedzi, scenariusze gdzie latencja jest krytyczna. Przy dużych wolumenach różnica kosztowa jest znacząca.
Cennik orientacyjny (input/output per MTok): Opus jest ok. 15x droższy od Haiku. Sonnet jest gdzieś pośrodku — ok. 3-4x droższy od Haiku. Zawsze sprawdzaj aktualny cennik na anthropic.com/pricing przed planowaniem budżetu.
Caching systemu promptów
Jeśli masz długi system prompt (np. dokumentację, kontekst biznesowy), możesz go zcachować. Przy pierwszym wywołaniu jest przetwarzany i cachowany — kolejne wywołania używają cache, co redukuje koszty o ~90% dla zcachowanych tokenów:
```typescript const response = await anthropic.messages.create({ model: 'claude-sonnet-4-6', max_tokens: 1024, system: [ { type: 'text', text: 'Bardzo długa dokumentacja produktu... [10,000 tokenów]', cache_control: { type: 'ephemeral' }, }, ], messages: [{ role: 'user', content: 'Pytanie o produkt' }], }); ```
Cache działa przez 5 minut od ostatniego użycia. Przy chatbocie, gdzie każda wiadomość użytkownika korzysta z tego samego system promptu — oszczędności są bardzo realne.
Produkcja: rate limity, retry i śledzenie kosztów
**Rate limity** są zależne od poziomu konta (Tier 1–4). Domyślnie: kilkaset RPM i kilkadziesiąt tysięcy TPM. SDK rzuca `RateLimitError` gdy przekroczysz limit.
**Exponential backoff** — standardowy wzorzec retry:
```typescript import Anthropic from '@anthropic-ai/sdk';
async function withRetry
**Śledzenie kosztów** — każda odpowiedź zawiera `usage`:
```typescript console.log(response.usage.input_tokens); console.log(response.usage.output_tokens); console.log(response.usage.cache_read_input_tokens); // tokeny z cache ```
Loguj to do swojej bazy danych lub systemu monitoringu. Bez śledzenia koszty mogą zaskoczyć pod koniec miesiąca.
**Typy błędów** w SDK:
```typescript try { const response = await anthropic.messages.create({ ... }); } catch (error) { if (error instanceof Anthropic.APIError) { console.error('Status:', error.status); console.error('Message:', error.message); } if (error instanceof Anthropic.AuthenticationError) { console.error('Zły klucz API'); } if (error instanceof Anthropic.RateLimitError) { console.error('Przekroczono rate limit'); } if (error instanceof Anthropic.InternalServerError) { console.error('Problem po stronie Anthropic'); } } ```
Podsumowanie
Claude API to jedno z najbardziej dopracowanych API w ekosystemie LLM. TypeScript SDK jest dobrze otypowany, streaming działa bez zaskoczek, a tool use jest na tyle elastyczny, że da się na nim zbudować złożone agenty.
Droga produkcyjna: zacznij od `claude-sonnet-4-6`, włącz caching system promptów od razu, loguj `usage` od pierwszego dnia, zaimplementuj retry z exponential backoff zanim trafisz na rate limit (nie po). Agent loop z powyższego przykładu jest dobrą bazą — rozbuduj go o persistence historii konwersacji i bardziej rozbudowane narzędzia.
Potrzebujesz podobnego rozwiązania?
Porozmawiajmy o Twoim projekcie
Pierwsza rozmowa jest bezpłatna. Opisz nam swój pomysł — odpowiemy w ciągu jednego dnia roboczego.
Umów bezpłatną rozmowę