Większość ludzi używa AI jak wyszukiwarki. To błąd.
Wpisujesz pytanie, dostajesz odpowiedź, zamykasz kartę. Tymczasem ta sama technologia potrafi samodzielnie przeszukać internet, przeczytać wyniki, podjąć decyzję co zrobić dalej, zapisać raport — i poinformować cię dopiero gdy skończy. Różnica między chatbotem a agentem AI to różnica między kalkulatorem a arkuszem kalkulacyjnym: oba liczą, ale tylko jeden może sam zaplanować budżet.
W tym artykule budujesz prawdziwego agenta — takiego, który wykonuje narzędzia, zarządza pętlą konwersacji i nie wymaga od ciebie żadnej magii poza standardowym kluczem API. Czas: 30 minut. Wymagania wstępne: podstawy Pythona lub TypeScript i konto w Anthropic Console.
Czym właściwie jest agent AI (i czym nie jest)
Agent AI to program, który w pętli wywołuje model językowy, pozwala mu korzystać z narzędzi (funkcji), i powtarza cykl aż model stwierdzi, że zadanie jest skończone. Kluczowe słowo: pętla. Zwykłe wywołanie API to jedno zapytanie i jedna odpowiedź. Agent to wielokrotna wymiana, gdzie model sam decyduje kiedy i które narzędzie uruchomić.
Konkretnie: wysyłasz zapytanie, model odpowiada z blokiem tool_use zamiast tekstu. Ty wykonujesz to narzędzie lokalnie (np. faktycznie szukasz w internecie), odsyłasz wynik jako tool_result, model przetwarza wynik i albo wywołuje kolejne narzędzie, albo kończy z stop_reason == "end_turn".
To jest cały sekret. Nie ma tu żadnej czarnej magii — tylko dobrze zaprojektowana pętla while.
Wybór modelu: ile chcesz zapłacić za inteligencję
Anthropic oferuje trzy modele w różnych punktach cenowych (ceny za milion tokenów, stan na czerwiec 2026):
| Model | ID | Input $/1M | Output $/1M | Kontekst | Kiedy używać |
|---|---|---|---|---|---|
| Claude Opus 4.8 | claude-opus-4-8 | $5,00 | $25,00 | 1M tokenów | Złożone zadania wieloetapowe, rozumowanie |
| Claude Sonnet 4.6 | claude-sonnet-4-6 | $3,00 | $15,00 | 1M tokenów | Codzienna robota, dobry balans cena/jakość |
| Claude Haiku 4.5 | claude-haiku-4-5 | $1,00 | $5,00 | 200K tokenów | Proste zadania, duże wolumeny, testy |
Do nauki i prototypowania zacznij od Sonnet 4.6. Jest wystarczająco sprytny do niemal wszystkiego, a różnica w cenie względem Opusa jest pięciokrotna na wejściu i dziesięciokrotna na wyjściu — przy eksperymentowaniu to ma znaczenie. Opus zostawiasz na produkcję, gdzie naprawdę potrzebujesz głębokiego rozumowania.
Setup w 3 minuty
Zacznij od zainstalowania SDK i ustawienia klucza API. Klucz pobierasz z console.anthropic.com — nigdy nie hardkoduj go w kodzie.
Python:
pip install anthropicTypeScript/Node:
npm install @anthropic-ai/sdk zodKlucz ustawiasz jako zmienną środowiskową:
export ANTHROPIC_API_KEY=sk-ant-twój-kluczSDK automatycznie odczyta tę zmienną — nie musisz jej przekazywać jawnie w kodzie. To nie jest kwestia stylu, to kwestia bezpieczeństwa.
Budowa agenta: wersja manualna (żebyś rozumiał co się dzieje)
Zanim użyjesz skrótów, musisz zobaczyć jak pętla działa od środka. Poniższy agent badawczy szuka informacji w sieci i zapisuje wyniki do pliku. Narzędzia są mockami — w produkcji podłączysz prawdziwe API (Brave Search, Serper, Tavily).
import anthropic
import json
client = anthropic.Anthropic()
tools = [
{
"name": "web_search",
"description": "Przeszukaj internet w poszukiwaniu aktualnych informacji. Użyj gdy pytanie dotyczy bieżących danych, cen lub niedawnych wydarzeń.",
"input_schema": {
"type": "object",
"properties": {
"query": {"type": "string", "description": "Zapytanie do wyszukiwarki"}
},
"required": ["query"]
}
},
{
"name": "save_to_file",
"description": "Zapisz wyniki badań do pliku lokalnego.",
"input_schema": {
"type": "object",
"properties": {
"filename": {"type": "string"},
"content": {"type": "string"}
},
"required": ["filename", "content"]
}
}
]
def execute_tool(name: str, input_data: dict) -> str:
if name == "web_search":
# Tu podłączasz prawdziwe API wyszukiwarki
return f"[Wyniki dla '{input_data['query']}': przykładowe dane]"
elif name == "save_to_file":
with open(input_data["filename"], "w", encoding="utf-8") as f:
f.write(input_data["content"])
return f"Zapisano do {input_data['filename']}"
return "Nieznane narzędzie"
def run_agent(user_query: str) -> str:
messages = [{"role": "user", "content": user_query}]
while True:
response = client.messages.create(
model="claude-sonnet-4-6",
max_tokens=8000,
system="Jesteś asystentem badawczym. Szukaj informacji w sieci i zapisuj wyniki do plików gdy użytkownik o to prosi.",
tools=tools,
messages=messages
)
if response.stop_reason == "end_turn":
return next(b.text for b in response.content if b.type == "text")
if response.stop_reason == "tool_use":
# WAŻNE: appendujesz cały response.content, nie tylko tekst
messages.append({"role": "assistant", "content": response.content})
tool_results = []
for block in response.content:
if block.type == "tool_use":
result = execute_tool(block.name, block.input)
tool_results.append({
"type": "tool_result",
"tool_use_id": block.id,
"content": result
})
# WSZYSTKIE wyniki w jednej wiadomości — nie rozdzielaj
messages.append({"role": "user", "content": tool_results})
else:
break
return "Agent zakończył nieoczekiwanie"
result = run_agent("Zbadaj trendy AI z 2025 roku i zapisz podsumowanie do pliku ai_trendy.txt")
print(result)Trzy błędy, które popełnia każdy na początku
- Appendowanie tylko tekstu zamiast całego response.content. Bloki
tool_usemuszą trafić z powrotem do historii wiadomości — bez nich model dostanie orphaned tool_result i zwróci błąd 400. - Wysyłanie wyników narzędzi w osobnych wiadomościach. Wszystkie
tool_resultz jednej rundy muszą być w jednym obiekcieusermessage. - Używanie
budget_tokensna Opus 4.8. Ten parametr został usunięty na modelach 4.7 i 4.8 — dostaniesz błąd 400. Zamiast tego użyjthinking: {"type": "adaptive"}bez żadnego budżetu.
Wersja skrócona: Tool Runner automatyzuje pętlę
Jeśli nie potrzebujesz pełnej kontroli nad pętlą, SDK oferuje Tool Runner — wrapper, który obsługuje cały cykl automatycznie. Kod skraca się o połowę:
from anthropic import Anthropic, beta_tool
client = Anthropic()
@beta_tool
def search_web(query: str) -> str:
"""Przeszukaj internet w poszukiwaniu aktualnych informacji.
Args:
query: Zapytanie do wyszukiwarki.
"""
return f"Wyniki dla: {query}"
@beta_tool
def save_file(filename: str, content: str) -> str:
"""Zapisz treść do pliku lokalnego.
Args:
filename: Nazwa pliku do utworzenia.
content: Zawartość tekstowa.
"""
with open(filename, "w", encoding="utf-8") as f:
f.write(content)
return f"Zapisano {filename}"
runner = client.beta.messages.tool_runner(
model="claude-sonnet-4-6",
max_tokens=8000,
system="Jesteś asystentem badawczym.",
tools=[search_web, save_file],
messages=[{"role": "user", "content": "Zbadaj trendy AI i zapisz raport do report.txt"}]
)
for message in runner:
for block in message.content:
if block.type == "text":
print(block.text)Dekorator @beta_tool automatycznie generuje input_schema z docstringa i type hintów. Tool Runner sam obsługuje kolejne rundy wymiany z modelem aż do end_turn. Minusem: masz mniejszą kontrolę nad logiką — np. nie możesz przerwać pętli po określonej liczbie kroków bez własnego wrappera.
TypeScript? Też spokojnie
Ekwiwalent w TypeScript z użyciem betaZodTool i biblioteki Zod do walidacji schemy:
import Anthropic from "@anthropic-ai/sdk";
import { betaZodTool } from "@anthropic-ai/sdk/helpers/beta/zod";
import { z } from "zod";
const client = new Anthropic();
const searchWeb = betaZodTool({
name: "search_web",
description: "Przeszukaj internet. Użyj gdy pytanie dotyczy aktualnych danych.",
inputSchema: z.object({
query: z.string().describe("Zapytanie wyszukiwarki"),
}),
run: async ({ query }) => {
return `Wyniki wyszukiwania dla: ${query}`;
},
});
const saveFile = betaZodTool({
name: "save_file",
description: "Zapisz treść do pliku lokalnego.",
inputSchema: z.object({
filename: z.string(),
content: z.string(),
}),
run: async ({ filename, content }) => {
const fs = await import("fs/promises");
await fs.writeFile(filename, content, "utf-8");
return `Zapisano ${filename}`;
},
});
const finalMessage = await client.beta.messages.toolRunner({
model: "claude-sonnet-4-6",
max_tokens: 8000,
system: "Jesteś asystentem badawczym.",
tools: [searchWeb, saveFile],
messages: [{ role: "user", content: "Zbadaj trendy AI i zapisz do report.txt" }],
});
for (const block of finalMessage.content) {
if (block.type === "text") console.log(block.text);
}Ile to kosztuje w praktyce
Policzmy konkretnie. Założenie: 10 rozmów z agentem dziennie, każda to średnio 5 000 tokenów na wejściu i 1 000 tokenów na wyjściu (realistyczne dla prostego agenta badawczego).
Bez cache, model Sonnet 4.6:
- Input: 10 × 5 000 × $3/1M = $0,15/dzień = ~$4,50/miesiąc
- Output: 10 × 1 000 × $15/1M = $0,15/dzień = ~$4,50/miesiąc
- Łącznie: ~$9/miesiąc
Z prompt cachingiem (system prompt 4 000 tokenów, powtarzany w każdej rundzie):
- Pierwsze zapisy cache: 4 000 × $3,75/1M = $0,015 (jednorazowo co 5 minut TTL)
- Odczyty cache: 4 000 × $0,30/1M = $0,0012 za każde wywołanie
- Oszczędność na systemowym prompcie: ~90%
Żeby skorzystać z cachingu, dodajesz cache_control do bloku systemowego:
system=[{
"type": "text",
"text": "Twój długi system prompt...",
"cache_control": {"type": "ephemeral"}
}]Minimalna wielkość prefiksu do cachowania: 2 048 tokenów dla Sonnet 4.6, 4 096 dla Opus 4.8. Przy krótkich system promptach caching nie zadziała — warto o tym wiedzieć zanim zaczniesz się dziwić, że oszczędności nie ma.
Jeśli twój agent przetwarza dane wsadowo (analityka, bulk research, nie wymaga odpowiedzi w czasie rzeczywistym) — Batch API daje 50% zniżki na wszystkie tokeny. Zero zmian w logice, tylko inny endpoint.
Zasady, których złamanie zwróci błąd 400
API Claude'a jest dość restrykcyjne i kilka reguł egzekwuje twardo:
- Nie używaj jednocześnie
temperatureitop_p. Na modelach Claude 4.x to zwraca błąd 400 — wybierz jedno albo żadne. - Nie używaj
budget_tokensna Opus 4.8. Parametr usunięty na serii 4.7/4.8. Dla rozszerzonego myślenia użyjthinking: {"type": "adaptive"}. - Nagłówek
anthropic-version: 2023-06-01jest wymagany przy bezpośrednich wywołaniach HTTP. SDK dodaje go automatycznie — martwisz się o to tylko jeśli piszesz własne requestycurllubfetch. - Maksymalnie 128 narzędzi per definicja agenta. Spokojnie wystarczy — jeśli potrzebujesz więcej, to sygnał, że agent robi za dużo rzeczy naraz.
- Ustrukturyzowane wyjście JSON deklarujesz przez
output_config: {format: ...}— parametroutput_formatjest przestarzały i może być ignorowany.
Co dalej po "Hello World" agenta
Kiedy masz działającą pętlę, naturalne rozszerzenia to:
Prawdziwe narzędzia zamiast mocków
Podłącz Brave Search API (podstawowy plan od $3/miesiąc) albo Tavily (pierwsze 1 000 zapytań miesięcznie gratis). Wymiana mocka na prawdziwe API to podmiana jednej funkcji execute_tool.
Pamięć między sesjami
Domyślnie agent nie pamięta poprzednich rozmów. Najprostsza pamięć: zapisz historię messages do JSONa i ładuj przy starcie. Zaawansowana: wektorowa baza danych (Qdrant, Chroma) z semantic search po przeszłych wątkach.
Multi-agent z handoff
Jeden agent-koordynator wywołuje wyspecjalizowane pod-agenty przez narzędzia. Okno kontekstowe 1M tokenów na Sonnecie 4.6 i Opus 4.8 jest wystarczające na bardzo długie łańcuchy rozumowania — 200K tokenów Haiku 4.5 możesz poczuć przy złożonych zadaniach.
Podsumowanie praktyczne
- Zbudowanie podstawowego agenta zajmuje mniej czasu niż skonfigurowanie LangChain — natywne API Anthropic jest wystarczająco proste do bezpośredniego użycia.
- Pętla agenta to trzy kroki: wyślij zapytanie → sprawdź
stop_reason→ jeślitool_use, wykonaj narzędzie i wyślijtool_resultz powrotem; powtarzaj ażend_turn. - Do nauki używaj Sonnet 4.6 ($3/$15 za 1M tokenów) — Opus 4.8 zostawiasz na produkcję gdzie naprawdę potrzebujesz maksymalnej inteligencji.
- Prompt caching to łatwe 90% oszczędności na powtarzających się system promptach — jeden dodatkowy blok JSON w konfiguracji.
- Trzy pułapki do zapamiętania: nie mieszaj
temperatureztop_p, nie używajbudget_tokensna Opus 4.8, appenduj zawsze pełnyresponse.contenta nie tylko tekst. - Koszt prostego agenta w produkcji to realnie $9-20/miesiąc przy rozsądnym użyciu z cachingiem — nie $200, które straszy większość osób przy pierwszym spojrzeniu na cennik.
Agent AI to nie projekt na kwartał. To 30 minut do pierwszego działającego prototypu i kilka dni do czegoś, co możesz faktycznie używać. Klucz API masz, Python masz, czas startować.