SSD Nodes Learn Hosting plans →
Przewodniki Matt ConnorAutor: Matt Connor · Zaktualizowano 2026-08-08

Claude API: pierwsza aplikacja w Python na Ubuntu VPS

Stwórz narzędzie do analizy logów z Claude API na Ubuntu 24.04. Poradnik obejmuje konfigurację virtualenv, bezpieczne zarządzanie kluczami, obsługę strumieniowania i limitów.

Co budujesz

Narzędzie wiersza poleceń na świeżym systemie Ubuntu 24.04 VPS, do którego przesyłasz komunikat o błędzie lub fragment dziennika, aby otrzymać diagnozę w zrozumiałym języku: journalctl -u nginx -n 50 | explain. Skrypt składa się z około 60 linii kodu w języku Python i wykorzystuje wszystkie elementy niezbędne w rzeczywistej aplikacji korzystającej z Claude API: poprawnie przechowywany klucz, środowisko virtualenv, struktury odpowiedzi SDK, strumieniowanie, typowany łańcuch wyjątków oraz jednostkę systemd, dzięki której narzędzie działa w tle.

Projekt został wybrany celowo. Większość samouczków dotyczących "pierwszej aplikacji API" prowadzi do stworzenia chatbota, którego użytkownik nigdy więcej nie otworzy. Wyjaśniacz dzienników jest przydatny na serwerze od pierwszego dnia i wymusza opanowanie dwóch kwestii, w których początkujący najczęściej popełniają błędy: poprawnego odczytywania obiektu odpowiedzi oraz kontrolowania kosztów. API rozlicza się za tokeny bez górnego limitu poza tymi, które sam ustawisz, więc kontrola kosztów jest tutaj założeniem projektowym, a nie dodatkiem. To ta sama dyscyplina, która jest wymagana, gdy przejdziesz do uruchamiania Claude Code na tym samym VPS wewnątrz tmux.

Uzyskanie klucza API z konsoli

Dostęp do 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 (dokumentacja 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 ponownie odczytać. Należy go natychmiast skopiować lub usunąć i wygenerować ponownie.

Kwestie finansowe: na lipiec 2026 r. nie istnieje stały darmowy plan dla API. Dokumentacja cenowa Anthropic wskazuje, że nowi użytkownicy otrzymują niewielką ilość darmowych kredytów na testy. Dokładna kwota jest widoczna w Console podczas rejestracji. Po ich wyczerpaniu należy zasilić konto, aby żądania były realizowane. Jest to usługa oddzielna od subskrypcji claude.ai; plan Pro lub Max nie obejmuje kredytów API, a klucz API nie zapewnia dostępu do aplikacji czatowej. Jeśli rozważasz wybór między subskrypcją a API, ten kompromis jest osobnym zagadnieniem: którego planu Claude faktycznie potrzebujesz.

Klucz należy utworzyć z ograniczeniem do jednego projektu lub serwera. W przypadku wycieku klucza – co w dłuższej perspektywie jest nieuniknione – możliwe będzie jego unieważnienie bez wpływu na pozostałe zasoby.

Przechowywanie klucza poza plikiem .bashrc

Odruchowym działaniem jest export ANTHROPIC_API_KEY=sk-ant-... w ~/.bashrc. Należy tego unikać. Istnieją trzy odrębne problemy:

  • Każdy proces dziedziczy klucz. Zmienna środowiskowa wyeksportowana w powłoce logowania jest przekazywana do wszystkiego, co uruchomisz: aplikacji webowej, narzędzia do raportowania błędów, które może zrzucić środowisko do pliku, czy strony phpinfo(), którą ktoś pozostawił aktywną. Powierzchnia ataku na klucz obejmuje wtedy wszystko, co uruchamia dany użytkownik.
  • Wpisanie klucza trafia do ~/.bash_history. Ręczne wykonanie polecenia export sprawia, że klucz zostaje zapisany w pliku tekstowym na zawsze i jest synchronizowany z każdą kopią zapasową katalogu domowego.
  • Klucz jest niedostępny dla systemd. Usługi nie odczytują pliku .bashrc, więc ten schemat zawodzi w momencie przeniesienia skryptu do jednostki systemd, co zazwyczaj objawia się tajemniczym błędem 401 o godzinie 6 rano.

Właściwym wzorcem na serwerze jest 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/null

Użyj tee z polecenia printf zamiast edytora, jeśli chcesz uniknąć zapisania klucza w plikach tymczasowych edytora. Niezależnie od metody, zweryfikuj za pomocą ls -l /etc/claude-explain.env, czy plik ma uprawnienia -rw------- i czy jego właścicielem jest root. Powłoki interaktywne otrzymują klucz przy każdym wywołaniu za pomocą poniższego wrappera, a systemd otrzymuje go przez EnvironmentFile=. Proces root odczytuje plik przed porzuceniem uprawnień, dzięki czemu użytkownik usługi nie potrzebuje do niego dostępu. Klucz nigdy nie pojawia się w kodzie, w git, w wynikach ps ani w historii powłoki.

Instalacja SDK w venv

Ubuntu 24.04 dostarcza Python 3.12 z wymuszaniem PEP 668, dlatego zwykłe polecenie pip install anthropic uruchomione dla interpretera systemowego kończy się błędem error: externally-managed-environment. Ten błąd oznacza, że system operacyjny działa zgodnie z założeniami; należy użyć virtualenv:

sudo apt update && sudo apt install -y python3-venv
sudo python3 -m venv /opt/explain/venv
sudo /opt/explain/venv/bin/pip install anthropic

Na serwerze nie jest wymagana aktywacja środowiska: bezpośrednie wywołanie /opt/explain/venv/bin/python zawsze korzysta z pakietów zainstalowanych w venv.

Pierwsze wywołanie i poprawne odczytanie 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ą fundament modelu koncepcyjnego tego API. Po pierwsze, anthropic.Anthropic() bez argumentów odczytuje klucz ze zmiennych środowiskowych; nigdy nie należy przekazywać go jako literału ciągu znaków. Po drugie, response.content to lista bloków zawartości, a nie zwykły ciąg znaków. Bezpośrednie wypisanie tego obiektu skutkuje typowym dla początkujących błędem:

[TextBlock(citations=None, text='A systemd unit file is...', type='text')]

Nie jest to błąd w działaniu programu, lecz reprezentacja obiektu (repr). Odpowiedzi mogą zawierać wiele typów bloków (tekst, wywołania narzędzi, proces myślowy), dlatego należy iterować po nich i sprawdzać block.type == "text" przed odwołaniem się do .text. Zaimplementowanie tej pętli już na początku eliminuje całą klasę problemów związanych z „wyświetlaniem śmieci”.

Należy używać dokładnego identyfikatora modelu claude-opus-4-8. Identyfikatory obecnej generacji nie zawierają dat; należy wystrzegać się nawyku (lub starych wpisów na blogach) dodawania przyrostka z datą, gdyż skutkuje to błędem 404, opisanym poniżej.

Właściwe narzędzie: wyjaśnienie

Oto pełny program, przyjmujący dane ze standardowego wejścia (stdin) i przesyłający diagnostykę na wyjście strumieniowe, z obsługą 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 go jako /opt/explain/explain.py, a następnie dodaj wrapper, który wczytuje 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 przez sudo lub plik środowiskowy musi należeć do grupy, której członkiem jest użytkownik administrator; należy wybrać jedno z tych rozwiązań zamiast nadawania uprawnień 644).

Dlaczego strumieniowanie. client.messages.stream wypisuje tokeny w miarę ich napływania, zamiast oczekiwać w bezczynności na zakończenie pełnej generacji. Pozwala to uniknąć przekroczenia limitów czasu HTTP przy długich odpowiedziach; SDK odrzuci bardzo duże wartości max_tokens w wywołaniach niestrumieniowych właśnie z tego powodu. Jeśli po zakończeniu operacji potrzebujesz kompletnego obiektu, wywołaj stream.get_final_message() wewnątrz bloku with.

Dlaczego taka kolejność wyjątków. SDK zgłasza wyjątki typowane, od najbardziej szczegółowych: RateLimitError to błąd 429, który zawiera nagłówek retry-after informujący o wymaganym czasie oczekiwania; APIStatusError obejmuje pozostałe odpowiedzi spoza zakresu 2xx (sprawdź e.status_code >= 500 w poszukiwaniu problemów po stronie serwera); APIConnectionError oznacza, że żądanie w ogóle nie otrzymało odpowiedzi. Zanim zbudujesz pętlę ponawiania: SDK automatycznie ponawia żądania przy błędach 429 oraz 5xx, domyślnie dwukrotnie z wykładniczym wycofaniem (max_retries po stronie klienta). W momencie, gdy uruchamia się Twój blok except, próby ponowienia zostały już wyczerpane, więc właściwym działaniem w CLI jest zgłoszenie błędu i zakończenie pracy, a nie usypianie procesu i ponowne wysyłanie żądań.

Kontrola kosztów

Kwestia ta wymaga osobnej sekcji, ponieważ API nie posiada wbudowanego limitu miesięcznego poza tym, który zostanie skonfigurowany, a każdy błąd w tym zakresie generuje narastające koszty w sposób niezauważalny.

max_tokens stanowi górny limit wydatków na pojedyncze wywołanie. Tokeny wyjściowe są droższe; w modelu Opus 4.8 kosztują pięciokrotnie więcej niż wejściowe, a max_tokens stanowi twardy limit liczby tokenów, które model może wygenerować. Błędny prompt nie wygeneruje kosztów wyjściowych wyższych niż zdefiniowany limit. Należy dobrać go do zadania: 1500 to wartość wystarczająca do analizy logów; zadanie klasyfikacji wymaga 100. Jeśli odpowiedzi są przerywane w połowie zdania z powodu stop_reason: "max_tokens", limit jest zbyt niski – należy go świadomie zwiększyć, zamiast domyślnie ustawiać bardzo wysokie wartości.

Zliczaj przed wysłaniem. Dane wejściowe również kosztują, a logi bywają obszerne. API udostępnia darmowy punkt końcowy do zliczania tokenów (posiada on własne limity szybkości, niezależne 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 przesłania 2 GB logów przez narzędzie. Nie należy używać do tego celu tiktoken, ponieważ jest to tokenizator OpenAI, który zaniża liczbę tokenów Claude o około 15–20% w przypadku typowego tekstu, a w przypadku kodu o jeszcze więcej.

Dobieraj model do zadania, nie kieruj się przywiązaniem. Według stanu na lipiec 2026 r. Opus 4.8 (claude-opus-4-8) kosztuje 5 USD za milion tokenów wejściowych i 25 USD za milion wyjściowych; Haiku 4.5 (claude-haiku-4-5) kosztuje 1 USD/5 USD przy kontekście 200K; Sonnet 5 (claude-sonnet-5) plasuje się pomiędzy nimi z cenami 3 USD/15 USD (z cenami promocyjnymi 2 USD/10 USD obowiązującymi do 31 sierpnia 2026 r.). W praktyce: fragment logu o długości 2000 tokenów z odpowiedzią 500 tokenów kosztuje około 0,0225 USD w modelu Opus i 0,0045 USD w modelu Haiku. Należy rozpocząć od modelu Opus w celu oceny jakości odpowiedzi, a następnie przetestować te same prompty na modelu Haiku – w przypadku prostych transformacji o dużej skali różnica w jakości jest często niezauważalna, przy pięciokrotnie niższej cenie. Przed zakodowaniem tych wartości w budżecie należy zweryfikować aktualne stawki na stronie z cennikiem.

Przetwarzanie wsadowe dla zadań niepilnych. API Batches przetwarza żądania asynchronicznie za 50% standardowej ceny, a większość partii kończy się w ciągu godziny. Nocne zestawienia, uzupełnianie danych, masowa klasyfikacja – każde zadanie, na które nie czeka człowiek, powinno być realizowane w ten sposób.

Buforowanie promptów dla powtarzalnego kontekstu. Jeśli każde wywołanie ponownie przesyła ten sam duży prompt systemowy lub instrukcję obsługi, należy oznaczyć go jako możliwy do buforowania:

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 on

Zapis w pamięci podręcznej kosztuje około 1,25x ceny wejściowej, a odczyt około 0,1x, przy czasie życia (TTL) wynoszącym 5 minut. Dzięki temu drugie wywołanie w tym oknie czasowym zwraca koszt pierwszego. Istnieją dwa ograniczenia. Buforowany prefiks musi przekraczać minimalną liczbę tokenów dla danego modelu (kilka tysięcy w przypadku Opus), więc krótki prompt systemowy nie zostanie zbuforowany. Jeśli cache_read_input_tokens pozostaje zerowe przy identycznych wywołaniach, oznacza to, że coś w prefiksie zmienia się przy każdym żądaniu (zazwyczaj przyczyną jest znacznik czasu).

Pamiętaj, co wlicza się do danych wejściowych. Prompty systemowe, definicje narzędzi oraz – w konwersacjach wieloetapowych – cała historia przesyłana przy każdym kroku, są rozliczane jako tokeny wejściowe. Pętla czatu, która nie przycina historii, generuje koszty rosnące kwadratowo. Przed rozpoczęciem budowy jakiegokolwiek systemu konwersacyjnego warto zrozumieć pełny mechanizm rozliczeń: jak faktycznie sumuje się użycie tokenów i rozliczenia w Claude.

Uruchamianie w systemd

Dyscyplina w stosowaniu plików środowiskowych przynosi korzyść: timer, który każdego ranka podsumowuje błędy z poprzedniego dnia.

# /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.target
sudo 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 now

Należy zwrócić uwagę na korzyści płynące z EnvironmentFile=: systemd odczytuje plik należący do root z uprawnieniami 600 przed zmianą uprawnień na użytkownika explain, dzięki czemu proces otrzymuje zmienną, podczas gdy użytkownik nie ma możliwości odczytu pliku z kluczem. Grupa systemd-journal zapewnia dostęp do logów. Należy przeprowadzić test za pomocą ręcznego wywołania systemctl start i sprawdzić journalctl -u log-digest.service; nie należy czekać do godziny 06:15, aby wykryć literówkę. Gdy ten schemat przestaje wystarczać dla potoku powłoki, to samo podejście z kluczem w pliku środowiskowym można bezpośrednio przenieść do przepływów pracy n8n zintegrowanych z Claude na tej samej maszynie.

Tryby awaryjne i komunikaty błędów

Błąd 401 przy działającym kluczu. Wyjątek zawiera treść:

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, a usługa zwraca 401, oznacza to, że usługa go nie otrzymała. Należy pamiętać, że systemd nie odczytuje .bashrc; należy sprawdzić, czy EnvironmentFile= wskazuje na poprawną ścieżkę. Inne przyczyny: cudzysłowy wklejone do pliku env (ANTHROPIC_API_KEY="sk-ant-...", systemd usuwa cudzysłowy, ale . file w skrypcie powłoki może je zachować w wartości, jeśli użyto błędnego cytowania), znaki spacji na końcu linii lub klucz unieważniony w konsoli w poprzednim tygodniu.

Błąd 404 spowodowany literówką w nazwie modelu. Najczęstszym przypadkiem jest dodanie przyrostka daty do bieżącego identyfikatora 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 modeli bieżącej generacji są dokładne, tak jak zapisano: claude-opus-4-8, claude-haiku-4-5, claude-sonnet-5. Należy kopiować je z dokumentacji modeli, nigdy z pamięci lub starych poradników.

Błąd 429 rate_limit_error. Ciąg znaków typu błędu to rate_limit_error, a odpowiedź zawiera nagłówek retry-after z liczbą sekund oczekiwania. Zestaw SDK wykonał już dwie próby ponowienia z mechanizmem backoff przed wyświetleniem wyjątku. Utrzymujące się błędy 429 oznaczają, że stałe tempo zapytań przekracza przydzielony limit. Należy grupować zadania lub rozłożyć je w czasie, zamiast zwiększać częstotliwość ponowień.

Wyświetlany jest obiekt, a nie tekst. Dane wyjściowe wyglądają jak [TextBlock(citations=None, text='...', type='text')]. Wydrukowano response.content zamiast iterować po blokach i odczytywać .text z tych, w których block.type == "text". Każdy przykład SDK powyżej wykonuje to poprawnie; należy skopiować pętlę.

error: externally-managed-environment. Uruchomiono pip install w systemowym środowisku Python w systemie Ubuntu 24.04. Należy używać venv i nigdy nie uruchamiać --break-system-packages na serwerze produkcyjnym.

Ucięte odpowiedzi. response.stop_reason == "max_tokens" oznacza, że model osiągnął limit wyjściowy w trakcie generowania odpowiedzi. Jest to zachowanie zgodne z projektem; należy celowo zwiększyć limit.

Gdy pierwsza aplikacja zacznie działać, budowanie agenta AI z Claude pozwoli przekształcić te same wywołania API w agenta korzystającego z narzędzi.

FAQ

Ile kosztuje testowanie API Claude?

Niewiele w stosunku do możliwości narzędzia. Według stanu na lipiec 2026 r. model Opus 4.8 kosztuje 5 USD za milion tokenów wejściowych i 25 USD za milion wyjściowych. Typowa analiza logów, obejmująca kilka tysięcy tokenów wejściowych i kilkaset wyjściowych, kosztuje około dwóch centów, a w przypadku modelu Haiku 4.5 (1 USD / 5 USD) poniżej pół centa. Miesięczny koszt codziennych podsumowań jest niższy niż cena kawy. Ryzykiem nie jest koszt pojedynczego zapytania, lecz nieograniczone pętle i nieograniczone max_tokens, dlatego w tym przewodniku oba parametry są ustawiane jawnie.

Czy istnieje darmowy plan dla API Claude?

Według stanu na lipiec 2026 r. nie ma stałego darmowego planu. Dokumentacja cenowa Anthropic wskazuje, że nowi użytkownicy otrzymują jednorazowy pakiet darmowych kredytów na testy API. Dokładna kwota jest widoczna w konsoli po rejestracji, po czym należy zasilić konto. Jeśli celem jest zerowy koszt krańcowy za zapytanie, a nie najwyższa jakość, alternatywą jest samodzielne uruchomienie modelu typu open-weight za pomocą Ollama, gdzie płaci się zasobami RAM zamiast tokenami.

Jak bezpiecznie przechowywać klucz API na serwerze?

Nigdy w kodzie, nigdy w repozytorium git, nigdy w zmiennych wyeksportowanych z .bashrc i nigdy nie wpisywany bezpośrednio w powłoce, gdzie historia poleceń go zachowa. Klucz należy umieścić w pliku należącym do użytkownika root z uprawnieniami 600. Należy ładować go dla każdego procesu osobno, używać skryptu opakowującego do pracy interaktywnej, EnvironmentFile= dla systemd oraz przypisywać jeden klucz do jednego serwera lub projektu. Dzięki temu unieważnienie wyciekłego klucza jest operacją punktową, a nie globalną. Jeśli klucz trafi do serwisu typu pastebin lub zostanie zatwierdzony w commicie git, należy go natychmiast unieważnić w konsoli; usunięcie commita nie usuwa wycieku.

Od którego modelu Claude zacząć?

Należy zacząć od claude-opus-4-8 podczas oceny, czy generowane wyniki są wystarczające do dalszych prac. Pozwala to ocenić koncepcję przy pełnej jakości, a przy hobbystycznym wolumenie różnica w kosztach wynosi zaledwie centy. Gdy prompt zostanie dopracowany, należy przetestować rzeczywiste dane wejściowe na modelu claude-haiku-4-5. W przypadku podsumowań, klasyfikacji i analizy logów jest on często równie skuteczny przy jednej piątej ceny. Przejście na Haiku lub Sonnet powinno wynikać z pomiarów, a nie z domyślnego wyboru.