Claude API tutorial: aplikacja na VPS Ubuntu
Dowiedz się, jak zbudować narzędzie do analizy logów w Python na Ubuntu 24.04. Dowiedz się, jak obsługiwać streaming oraz błąd %%C13%% przy użyciu Claude API.
Cel projektu
Narzędzie wiersza poleceń na nowym VPS z systemem Ubuntu 24.04. Do narzędzia przesyła się komunikat o błędzie lub fragment logów, a otrzymuje się diagnozę w języku naturalnym: journalctl -u nginx -n 50 | explain. Kod liczy około sześćdziesięciu linii w Pythonie. Projekt obejmuje wszystkie kluczowe elementy aplikacji korzystającej z Claude API: poprawne przechowywanie klucza, środowisko virtualenv, struktury odpowiedzi SDK, streaming, typowane łańcuchy wyjątków oraz jednostkę systemd do automatycznego uruchamiania.
Projekt został wybrany celowo. Większość poradników typu „pierwsza aplikacja API” wymaga budowy czatu, z którego nigdy więcej nie skorzysta się. Narzędzie do analizy logów jest użyteczne na serwerze od pierwszego dnia. Projekt wymusza opanowanie dwóch aspektów, w których początkujący najczęściej popełniają błędy: poprawne odczytywanie obiektu odpowiedzi oraz kontrolę kosztów. API rozlicza za liczbę tokenów i nie posiada limitów innych niż te ustawione przez użytkownika, dlatego kontrola kosztów jest tutaj elementem projektowym, a nie dodatkiem — jest to ta sama dyscyplina, która jest wymagana przy uruchamianiu Claude Code na tym samym VPS w tmux.
Pobierz klucz API z konsoli
Dostęp przez API jest zarządzany w Anthropic Console pod adresem platform.claude.com — należy założyć konto, a następnie utworzyć klucz w sekcji Settings → API Keys (link w dokumentacji prowadzi bezpośrednio do platform.claude.com/settings/keys). Klucz jest wyświetlany tylko raz, zaczyna się od sk-ant- i nie można go odzyskać — należy go natychmiast skopiować lub usunąć i wygenerować ponownie.
Kwestie płatności: w lipcu 2026 roku nie obowiązuje stały darmowy plan dla API. Dokumentacja cenowa Anthropic wskazuje, że nowi użytkownicy otrzymują niewielką kwotę darmowych kredytów do testów; dokładna kwota zależy od informacji wyświetlanych w Console podczas rejestracji. Po wyczerpaniu kredytów należy doładować konto, aby żądania mogły zostać przetworzone. Jest to niezależne od subskrypcji claude.ai — plany Pro lub Max nie obejmują kredytów API, a klucz API nie zapewnia dostępu do aplikacji czatowej. Wybór między subskrypcją a API to odrębny temat: który plan Claude jest faktycznie wymagany.
Klucz należy utworzyć z ograniczonym zakresem dostępu (scope) tylko dla jednego projektu lub serwera. W przypadku wycieku klucza — co nastąpi przy odpowiednio długim okresie czasu — można go unieważnić bez przerywania działania pozostałych usług.
Nie umieszczaj klucza w .bashrc
Metoda odniesienia zwrotnego jest export ANTHROPIC_API_KEY=sk-ant-... w ~/.bashrc. Nie należy jej stosować. Występują trzy odrębne problemy:
- Każdy proces dziedziczy tę zmienną. Zmienna środowiskowa wyeksportowana w shellu logowania jest propagowana do wszystkich uruchamianych procesów — aplikacji webowej, reportera błędów przesyłającego zmienne środowiskowe w raportach, czy strony
phpinfo()pozostawionej włączonej. Powierzchnia ekspozycji klucza obejmuje wszystkie procesy uruchomione przez tego użytkownika. - Wpisanie klucza zapisuje go w
~/.bash_history. Ręczne wykonanie polecenia export powoduje zapisanie klucza w pliku tekstowym na stałe, co skutkuje jego synchronizacją w każdym backupie katalogu home. - Zmienna jest niedostępna dla systemd. Usługi nie odczytują pliku
.bashrc, co powoduje błędy w momencie przekształcenia skryptu w jednostkę (unit) — zazwyczaj objawia się to błędem 401 o godzinie 6:00 rano.
Poprawny wzorzec na serwerze to dedykowany plik środowiskowy z uprawnieniami 600, ładowany wyłącznie przez proces, który go wymaga:
sudo mkdir -p /opt/explain
sudo install -m 600 -o root -g root /dev/null /etc/claude-explain.env
printf 'ANTHROPIC_API_KEY=sk-ant-YOUR-KEY-HERE\n' | sudo tee /etc/claude-explain.env >/dev/nullAby uniknąć zapisania klucza w plikach swap edytora, należy użyć tee z polecenia printf zamiast edytora tekstowego. W każdym przypadku należy zweryfikować za pomocą ls -l /etc/claude-explain.env, czy plik odczytuje -rw------- i czy należy do użytkownika root. Shell interaktywny otrzymuje klucz przy każdym wywołaniu poprzez wrapper (poniżej), natomiast systemd otrzymuje go przez EnvironmentFile= — root odczytuje plik przed obniżeniem uprawnień, więc użytkownik usługi nie musi mieć do niego dostępu. Klucz nie pojawia się w kodzie, w git, w wyjściu ps ani w historii shella.
Instalacja SDK w venv
W systemie Ubuntu 24.04 domyślnie występuje Python 3.12 z wymuszonym mechanizmem PEP 668. Próba instalacji pip install anthropic bezpośrednio w interpreterze systemowym kończy się błędem error: externally-managed-environment. Błąd ten wynika z poprawnego działania systemu operacyjnego — należy użyć środowiska virtualenv:
sudo apt update && sudo apt install -y python3-venv
sudo python3 -m venv /opt/explain/venv
sudo /opt/explain/venv/bin/pip install anthropicNa serwerze nie ma potrzeby aktywacji środowiska: bezpośrednie wywołanie /opt/explain/venv/bin/python zawsze wykorzystuje pakiety z venv.
Pierwsze wywołanie i poprawne odczytywanie odpowiedzi
import anthropic
client = anthropic.Anthropic() # reads ANTHROPIC_API_KEY from the environment
response = client.messages.create(
model="claude-opus-4-8",
max_tokens=1000,
messages=[{"role": "user", "content": "Explain what a systemd unit file is in three sentences."}],
)
for block in response.content:
if block.type == "text":
print(block.text)Dwie kwestie w tych dwunastu liniach stanowią podstawę modelu logicznego API. Po pierwsze, wywołanie anthropic.Anthropic() bez argumentów odczytuje klucz z zmiennych środowiskowych — nie należy przekazywać go jako ciąg znaków (string literal). Po drugie, response.content to lista bloków zawartości, a nie ciąg znaków. Bezpośrednie wypisanie obiektu skutkuje typowym błędem początkujących:
[TextBlock(citations=None, text='A systemd unit file is...', type='text')]Nie jest to błąd, lecz reprezentacja obiektu (repr). Odpowiedzi mogą zawierać wiele typów bloków (text, tool calls, thinking), dlatego należy iterować po liście i sprawdzać block.type == "text" przed uzyskaniem dostępu do .text. Implementacja tej pętli na początku pracy eliminuje błędy wynikające z błędnego wyświetlania danych.
Należy używać dokładnego identyfikatora modelu claude-opus-4-8. Identyfikatory obecnej generacji nie zawierają daty — nie należy dodawać przyrostków z datą, mimo sugestii w starszych artykułach; powoduje to błąd 404, opisany poniżej.
Działanie narzędzia: wyjaśnienie
Oto pełny program — dane wejściowe ze stdin, strumieniowa diagnostyka wyjściowa, obsługa błędów:
#!/usr/bin/env python3
"""explain: pipe an error or log excerpt in, get a diagnosis out."""
import sys
import anthropic
MODEL = "claude-opus-4-8"
def main() -> int:
text = sys.stdin.read().strip()
if not text:
print("usage: journalctl -u nginx -n 50 | explain", file=sys.stderr)
return 1
client = anthropic.Anthropic()
try:
with client.messages.stream(
model=MODEL,
max_tokens=1500,
system=(
"You are a senior Linux sysadmin. The user pipes you server "
"logs or error output. Name the most likely cause outright, "
"then give the commands to confirm and fix it. Be terse."
),
messages=[{"role": "user", "content": text}],
) as stream:
for chunk in stream.text_stream:
print(chunk, end="", flush=True)
print()
except anthropic.RateLimitError as e:
retry_after = e.response.headers.get("retry-after", "60")
print(f"rate limited; retry in {retry_after}s", file=sys.stderr)
return 2
except anthropic.APIStatusError as e:
print(f"API error {e.status_code}: {e.message}", file=sys.stderr)
return 2
except anthropic.APIConnectionError:
print("network error reaching the API", file=sys.stderr)
return 2
return 0
if __name__ == "__main__":
sys.exit(main())Zapisz plik jako /opt/explain/explain.py, a następnie dodaj wrapper ładujący klucz do użytku interaktywnego:
sudo tee /usr/local/bin/explain >/dev/null <<'EOF'
#!/bin/sh
set -a; . /etc/claude-explain.env; set +a
exec /opt/explain/venv/bin/python /opt/explain/explain.py "$@"
EOF
sudo chmod 755 /usr/local/bin/explain(Wrapper musi być uruchamiany za pomocą sudo lub plik środowiskowy musi należeć do grupy, w której znajduje się użytkownik administrator — należy wybrać jedną z tych metod zamiast zmieniania uprawnień pliku na 644.)
Dlaczego streaming. client.messages.stream wypisuje tokeny w momencie ich otrzymania, zamiast czekać na zakończenie pełnej generacji. Zapobiega to przekroczeniu limitu czasu połączenia HTTP (timeout) przy długich odpowiedziach — SDK odmawia obsługi bardzo dużych wartości max_tokens przy wywołaniach bez streamingu właśnie z tego powodu. Jeśli wymagany jest kompletny obiekt, należy wywołać stream.get_final_message() wewnątrz bloku with.
Dlaczego taka kolejność wyjątków. SDK zgłasza typowane wyjątki, zaczynając od najbardziej szczegółowych: RateLimitError oznacza błąd 429 i zawiera nagłówek retry-after informujący o czasie oczekiwania; APIStatusError obejmuje inne odpowiedzi spoza zakresu 2xx (sprawdź e.status_code >= 500 w przypadku problemów po stronie serwera); APIConnectionError oznacza brak odpowiedzi na zapytanie. Przed zaimplementowaniem pętli ponowień należy pamiętać: SDK automatycznie ponawia próby dla błędów 429 oraz 5xx, domyślnie dwukrotnie z wykorzystaniem wykładniczego czasu oczekiwania (max_retries po stronie klienta). W momencie uruchomienia except wszystkie próby ponowienia zostaną wyczerpane — dlatego w narzędziach CLI właściwym działaniem jest raportowanie błędu i zakończenie pracy, a nie oczekiwanie i ponowne wysyłanie żądań.
Kontrola kosztów
Ten temat wymaga osobnej sekcji, ponieważ API nie posiada wbudowanego miesięcznego limitu poza tym, co zostanie skonfigurowane, a każdy błąd generuje narastające koszty.
max_tokens to limit wydatków na pojedyncze wywołanie. Tokeny wyjściowe (output) są najdroższe — w modelu Opus 4.8 koszt są pięciokrotnie wyższy niż w przypadku tokenów wejściowych (input) — a max_tokens to sztywny limit liczby tokenów, które model może wygenerować. Nieprawidłowy prompt nie wygeneruje kosztów wyjściowych większych niż założono. Dobierz wartość do zadania: 1,500 jest wystarczające do diagnozy logów; zadanie klasyfikacji wymaga 100. Jeśli odpowiedzi przerywają się w połowie zdania z błędem stop_reason: "max_tokens", limit jest zbyt niski — należy go świadomie zwiększyć, zamiast ustawiać bardzo wysokie wartości domyślne.
Liczy się przed wysłaniem. Dane wejściowe również generują koszty, a logi są objętościowe. API posiada bezpłatny endpoint do liczenia tokenów (posiada własne limity zapytań, oddzielne od tworzenia wiadomości):
count = client.messages.count_tokens(
model="claude-opus-4-8",
messages=[{"role": "user", "content": big_log_text}],
)
print(count.input_tokens)Należy go używać, aby uniknąć przypadkowego przesyłania 2 GB logów przez narzędzie. Nie należy używać tiktoken do tego celu — jest to tokenizer OpenAI, który zaniża liczbę tokenów Claude o około 15–20% dla typowego tekstu, a jeszcze więcej dla kodu.
Wybieraj model pod zadanie, a nie pod lojalność. Według stanu na lipiec 2026, Opus 4.8 (claude-opus-4-8) kosztuje 5 USD za milion tokenów wejściowych i 25 USD za milion tokenów wyjściowych; Haiku 4.5 (claude-haiku-4-5) kosztuje 1 USD/5 USD przy oknie kontekstowym 200K; Sonnet 5 (claude-sonnet-5) kosztuje 3 USD/15 USD, z ceną promocyjną 2 USD/10 USD do 31 sierpnia 2026. Przykładowo: fragment logów o długości 2,000 tokenów z odpowiedzią o długości 500 tokenów kosztuje około 0.0225 USD na modelu Opus i 0.0045 USD na modelu Haiku. Należy zacząć od modelu Opus w celu oceny jakości odpowiedzi, a następnie przetestować te same prompty na modelu Haiku — przy prostych transformacjach o dużej objętości wyniki są często nieodróżnialne, a koszt jest pięciokrotnie niższy. Przed uwzględnieniem tych danych w budżecie należy zweryfikować aktualne stawki na stronie cennika.
Używaj Batch API dla zadań odroczonych w czasie. API Batches przetwarza żądania asynchronicznie w 50% standardowych cen, a większość zadań typu batch kończy się w ciągu godziny. Raporty nocne, uzupełnianie danych, masowa klasyfikacja — wszystko, co nie wymaga natychmiastowej reakcji człowieka, powinno być realizowane w ten sposób.
Cache'owanie promptów dla powtarzanego kontekstu. Jeśli każde wywołanie przesyła ten sam duży prompt systemowy lub instrukcję, należy oznaczyć go jako cache'owalny:
response = client.messages.create(
model="claude-opus-4-8",
max_tokens=1000,
system=[{
"type": "text",
"text": RUNBOOK_TEXT, # the same 30K tokens on every call
"cache_control": {"type": "ephemeral"},
}],
messages=[{"role": "user", "content": question}],
)
print(response.usage.cache_read_input_tokens) # non-zero from the second call onKoszt zapisu do cache wynosi około 1.25x ceny wejściowej, koszt odczytu z cache wynosi około 0.1x przy TTL wynoszącym 5 minut — oznacza to, że drugie wywołanie w tym oknie czasowym pokrywa koszt pierwszego. Istnieją dwa zastrzeżenia. Prefiks w cache musi spełniać minimalny wymóg modelu — kilka tysięcy tokenów dla Opus — w przeciwnym razie krótki prompt systemowy nie zostanie zachowany w pamięci podręcznej. Jeśli cache_read_input_tokens wynosi zero przy identycznych wywołaniach, oznacza to, że coś w prefiksie zmienia się przy każdym zapytaniu (zazwyczaj jest to znacznik czasu).
Należy pamiętać, co jest liczone jako input. Prompty systemowe, definicje narzędzi oraz — w rozmowach wieloturowych — cała historia przesyłana w każdym kroku, są rozliczane jako tokeny wejściowe. Pętla czatu, która nie skraca historii, generuje koszty rosnące kwadratowo. Pełny mechanizm rozliczeń należy zrozumieć przed budową jakichkolwiek rozwiązań konwersacyjnych: jak faktycznie sumują się zużycie tokenów i opłaty Claude.
Uruchomienie w systemd
Zaletą stosowania plików konfiguracyjnych typu environment-file jest timer, który każdego ranka podsumowuje błędy z dnia poprzedniego.
# /etc/systemd/system/log-digest.service
[Unit]
Description=Daily error-log digest via the Claude API
[Service]
Type=oneshot
User=explain
Group=systemd-journal
EnvironmentFile=/etc/claude-explain.env
ExecStart=/bin/sh -c 'journalctl -p err --since yesterday | /opt/explain/venv/bin/python /opt/explain/explain.py >> /var/log/log-digest.txt'# /etc/systemd/system/log-digest.timer
[Unit]
Description=Run the log digest every morning
[Timer]
OnCalendar=06:15
Persistent=true
[Install]
WantedBy=timers.targetsudo useradd -r -s /usr/sbin/nologin explain
sudo touch /var/log/log-digest.txt && sudo chown explain /var/log/log-digest.txt
sudo systemctl daemon-reload
sudo systemctl enable --now log-digest.timer
sudo systemctl start log-digest.service # test it once, right nowNależy zwrócić uwagę na korzyści wynikające z użycia EnvironmentFile=: systemd odczytuje plik należący do użytkownika root z uprawnieniami mode-600 przed przejściem na nieuprzywilejowanego użytkownika explain. Dzięki temu proces otrzymuje zmienną, podczas gdy użytkownik nie ma możliwości odczytania pliku z kluczem. Grupa systemd-journal zapewnia dostęp do logów. Należy przeprowadzić test za pomocą ręcznego systemctl start i sprawdzić journalctl -u log-digest.service — nie należy czekać do godziny 06:15, aby wykryć błąd w składni. Gdy ten wzorzec staje się zbyt złożony dla potoku shell, ta sama metoda przechowywania kluczy w pliku environment-file może być stosowana w workflowach n8n napędzanych przez Claude na tym samym serwerze.
Tryby awarii i napotkane komunikaty
401 przy poprawnym kluczu. Wyjątek brzmi:
anthropic.AuthenticationError: Error code: 401 - {'type': 'error', 'error': {'type': 'authentication_error', 'message': 'invalid x-api-key'}, 'request_id': 'req_011CSHoEeqs5C35K2UUqR7Fy'}Jeśli klucz działa w powłoce (shell), ale usługa zwraca błąd 401, usługa nie otrzymała klucza — należy pamiętać, że systemd nie odczytuje .bashrc; należy sprawdzić, czy EnvironmentFile= wskazuje na właściwą ścieżkę. Inne przyczyny: cudzysłowy wklejone do pliku env (ANTHROPIC_API_KEY="sk-ant-..." — systemd usuwa cudzysłowy, ale . file w wrapperze powłoki zachowuje je w wartości, jeśli użyto błędnego formatowania), spacje na końcu linii lub klucz unieważniony w Console w poprzednim tygodniu.
404 z powodu literówki w nazwie modelu. Najczęstszym przypadkiem jest dodanie przyrostka daty do aktualnego ID modelu:
anthropic.NotFoundError: Error code: 404 - {'type': 'error', 'error': {'type': 'not_found_error', 'message': 'model: claude-opus-4-8-20260115'}, 'request_id': 'req_011CSJqymAvNw4bT3qmDdMbA'}Identyfikatory bieżącej generacji są dokładnie takie, jak zostały zapisane — claude-opus-4-8, claude-haiku-4-5, claude-sonnet-5. Należy kopiować je z dokumentacji modeli, a nie z pamięci lub starych poradników.
429 rate_limit_error. Typ błędu to rate_limit_error, a odpowiedź zawiera nagłówek retry-after z liczbą sekund oczekiwania. SDK wykonało już dwie próby ponownego połączenia z opóźnieniem (backoff) przed wystąpieniem wyjątku, więc ciągłe błędy 429 oznaczają, że utrzymywane tempo przekracza limit przypisany do danego poziomu (tier) — należy grupować zadania (batching) lub rozłożyć je w czasie, zamiast skracać pętlę ponowień.
Wyświetlanie obiektu zamiast tekstu. Wynik wygląda jak [TextBlock(citations=None, text='...', type='text')]. Zamiast iteracji po blokach i odczytu .text z tych, w których występuje block.type == "text", wypisano response.content. Wszystkie powyższe przykłady SDK realizują to poprawnie; należy skopiować pętlę.
error: externally-managed-environment. Uruchomiono pip install w systemowym Pythonie w Ubuntu 24.04. Należy używać venv — nigdy nie należy używać --break-system-packages na serwerach produkcyjnych.
Ucięte odpowiedzi. response.stop_reason == "max_tokens" oznacza, że model osiągnął limit wyjściowy (output cap) w trakcie generowania. Jest to zachowanie zgodne z projektem; należy celowo zwiększyć limit.
Po uruchomieniu pierwszej aplikacji, budowa agenta AI przy użyciu Claude przekształca te same wywołania API w agenta wykorzystującego narzędzia (tools).
FAQ
Jaki jest koszt przetestowania Claude API?
Koszt jest bardzo niski. W lipcu 2026 roku model Opus 4.8 kosztuje 5 USD za milion tokenów wejściowych i 25 USD za milion tokenów wyjściowych. Typowa diagnostyka logów (kilka tysięcy tokenów wejściowych, kilkaset wyjściowych) kosztuje około dwóch centów. W przypadku modelu Haiku 4.5 (1 USD/5 USD) koszt wynosi mniej niż pół centa. Miesięczny koszt generowania codziennych podsumowań jest niższy niż cena kawy. Ryzykiem nie jest cena pojedynczego wywołania, lecz nieograniczone pętle oraz nieograniczony max_tokens. Z tego powodu oba parametry są wyraźnie definiowane w tym poradniku.
Czy Claude API posiada darmowy plan?
Według stanu na lipiec 2026 roku nie ma stałego darmowego planu. Dokumentacja cenowa Anthropic określa, że nowi użytkownicy otrzymują niewielką kwotę darmowych kredytów na przetestowanie API. Jest to jednorazowa próba, a dokładna kwota jest wyświetlana w Console podczas rejestracji. Po wykorzystaniu kredytów wymagane jest doładowanie konta. Jeśli priorytetem jest zerowy koszt krańcowy zamiast najwyższej jakości, alternatywą jest self-host an open-weight model with Ollama, gdzie płatność następuje w zasobach RAM zamiast tokenów.
Jak bezpiecznie przechowywać klucz API na serwerze?
Nigdy nie należy umieszczać klucza w kodzie, w repozytorium git, nie należy go eksportować z .bashrc ani wpisywać w powłokach (shell) z aktywną historią. Klucz należy umieścić w pliku należącym do użytkownika root z uprawnieniami 600. Należy ładować klucz na poziomie procesu: za pomocą skryptu opakowującego (wrapper script) w trybie interaktywnym lub za pomocą EnvironmentFile= w przypadku systemd. Należy przypisywać jeden klucz do jednego serwera lub projektu, aby unieważnienie wyciekłego klucza było procesem punktowym, a nie wymagało całkowitej wymiany systemu. Jeśli klucz zostanie opublikowany w serwisie typu paste lub w commicie git, należy go natychmiast unieważnić w Console; usunięcie commitu nie usuwa wycieku danych.
Od którego modelu Claude należy zacząć?
Należy zacząć od claude-opus-4-8 w celu oceny, czy generowane odpowiedzi są wystarczająco dobre do dalszej budowy systemu. Należy oceniać pomysł przy pełnej jakości, ponieważ przy małej skali koszt różnicy wynosi zaledwie centy. Po ustaleniu promptu należy uruchomić rzeczywiste dane wejściowe na claude-haiku-4-5. W zadaniach takich jak streszczanie, klasyfikacja i analiza logów model ten często oferuje taką samą jakość przy 1/5 kosztów. Przejście na modele Haiku lub Sonnet powinno wynikać z pomiarów wydajności, a nie z domyślnych ustawień.