Tool Use w Claude API — praktyczny poradnik z Pythonem

Praktyczny poradnik Tool Use w Claude API z Pythonem. Pętla agentowa, Programmatic Tool Calling, Structured Outputs i wzorce produkcyjne — z działającymi przykładami kodu.

Czym jest Tool Use i dlaczego to zmienia reguły gry?

Duże modele językowe to imponujące maszyny rozumowania — ale bez dostępu do zewnętrznego świata zostają zamknięte w bańce swoich danych treningowych. Nie sprawdzą pogody, nie odpytają bazy danych, nie wywołają firmowego API. I tu właśnie wchodzi Tool Use (wywoływanie narzędzi) — mechanizm, który zmienia pasywny model językowy w aktywnego agenta zdolnego do działania.

Jak to działa w Claude API? Schemat jest prosty, ale zaskakująco skuteczny: definiujesz narzędzia jako schematy JSON, Claude decyduje kiedy i jak je wywołać, a Twój kod wykonuje faktyczną robotę i odsyła wyniki z powrotem do modelu. Co ważne — Claude nie wykonuje narzędzi samodzielnie. Model jedynie sygnalizuje intencję ich użycia, a pełna kontrola zostaje po stronie programisty.

W 2026 roku Anthropic poszedł jeszcze dalej, wprowadzając Programmatic Tool Calling (PTC) — mechanizm pozwalający Claude pisać kod Python orkiestrujący wiele narzędzi w jednym bloku wykonania, zamiast tradycyjnych rund API. Wewnętrzne testy Anthropic wykazały 37% redukcję zużycia tokenów i spore zmniejszenie opóźnień przy złożonych zadaniach agentowych. Szczerze mówiąc, to dość imponujące liczby.

W tym poradniku zbudujesz krok po kroku działającego agenta AI opartego na narzędziach — od podstawowej definicji narzędzia, przez pętlę agentową, aż po zaawansowane wzorce produkcyjne. Wszystko z działającymi przykładami kodu w Pythonie, które możesz skopiować i uruchomić od razu.

Przygotowanie środowiska

Instalacja SDK i konfiguracja klucza API

Anthropic udostępnia oficjalny Python SDK obsługujący zarówno synchroniczne, jak i asynchroniczne wywołania. Minimalna wymagana wersja Pythona to 3.9.

# Instalacja SDK Anthropic
pip install anthropic

# Opcjonalnie: wsparcie dla AWS Bedrock
pip install anthropic[bedrock]

# Opcjonalnie: wsparcie dla Google Vertex AI
pip install anthropic[vertex]

Ustaw klucz API jako zmienną środowiskową:

export ANTHROPIC_API_KEY="sk-ant-twoj-klucz-api"

Szybka weryfikacja, czy połączenie działa:

import anthropic

client = anthropic.Anthropic()
message = client.messages.create(
    model="claude-sonnet-4-6",
    max_tokens=256,
    messages=[{"role": "user", "content": "Powiedz cześć po polsku."}]
)
print(message.content[0].text)

Jeśli widzisz odpowiedź Claude w konsoli — świetnie, wszystko gra. Możemy przejść do definiowania narzędzi.

Definiowanie narzędzi — schemat JSON krok po kroku

Anatomia definicji narzędzia

Każde narzędzie to obiekt JSON z trzema kluczowymi polami: name (unikalna nazwa), description (opis funkcjonalności) i input_schema (schemat parametrów wejściowych w formacie JSON Schema). Claude wykorzystuje opisy, żeby zdecydować, kiedy dane narzędzie jest odpowiednie — i tu kryje się klucz do skutecznego Tool Use. Im lepszy opis, tym trafniejsze decyzje modelu.

tools = [
    {
        "name": "pobierz_pogode",
        "description": "Pobiera aktualną prognozę pogody dla podanej lokalizacji. "
                       "Użyj tego narzędzia, gdy użytkownik pyta o pogodę, "
                       "temperaturę lub warunki atmosferyczne.",
        "input_schema": {
            "type": "object",
            "properties": {
                "miasto": {
                    "type": "string",
                    "description": "Nazwa miasta, np. Warszawa, Kraków"
                },
                "jednostka": {
                    "type": "string",
                    "enum": ["celsius", "fahrenheit"],
                    "description": "Jednostka temperatury (domyślnie celsius)"
                }
            },
            "required": ["miasto"]
        }
    },
    {
        "name": "wyszukaj_w_bazie",
        "description": "Wyszukuje informacje w firmowej bazie wiedzy. "
                       "Użyj tego narzędzia, gdy użytkownik pyta o produkty, "
                       "cenniki, regulaminy lub procedury firmowe.",
        "input_schema": {
            "type": "object",
            "properties": {
                "zapytanie": {
                    "type": "string",
                    "description": "Treść zapytania do wyszukania"
                },
                "kategoria": {
                    "type": "string",
                    "enum": ["produkty", "cenniki", "regulaminy", "procedury"],
                    "description": "Kategoria wyszukiwania (opcjonalna)"
                },
                "limit": {
                    "type": "integer",
                    "description": "Maksymalna liczba wyników (domyślnie 5)"
                }
            },
            "required": ["zapytanie"]
        }
    }
]

Dobre praktyki przy definiowaniu narzędzi

Kilka zasad, które warto mieć na uwadze:

  • Opisowy opis — brzmi jak masło maślane, ale to naprawdę najważniejsza rzecz. Claude wykorzystuje pole description do decyzji o użyciu narzędzia. Im precyzyjniej opiszesz, kiedy narzędzie powinno być używane, tym lepsze wyniki dostaniesz.
  • Jasne nazwy parametrów — używaj zrozumiałych nazw zamiast skrótów (miasto zamiast m, zapytanie zamiast q).
  • Enum dla ograniczonych opcji — jeśli parametr przyjmuje jedną z kilku wartości, użyj enum zamiast samego string. Dzięki temu Claude nie będzie zgadywał.
  • Opisuj każdy parametr — pole description w schemacie pomaga Claude zrozumieć, co dokładnie podać jako wartość.
  • Strict mode — w produkcji dodaj "strict": true do definicji narzędzia, żeby włączyć gwarancję poprawności schematu (o tym więcej za chwilę).

Pętla agentowa — serce Tool Use

Jak działa obieg danych

No dobra, to jest najważniejsza część całego artykułu. Mechanizm Tool Use opiera się na wieloetapowej konwersacji między Twoim kodem a API Claude. Cykl wygląda tak:

  1. Wysyłasz zapytanie użytkownika wraz z listą dostępnych narzędzi.
  2. Claude analizuje zapytanie i decyduje, czy potrzebuje narzędzia.
  3. Jeśli tak — zwraca blok tool_use z nazwą narzędzia i parametrami (zamiast odpowiedzi tekstowej).
  4. Twój kod wykonuje odpowiednią funkcję i odsyła wynik jako blok tool_result.
  5. Claude przetwarza wynik i generuje finalną odpowiedź (albo wywołuje kolejne narzędzie, jeśli potrzebuje więcej danych).

Kluczowy szczegół, o którym warto pamiętać: Claude nigdy nie wykonuje narzędzia sam. Model jedynie generuje strukturalny opis żądanego wywołania — to Twój kod jest odpowiedzialny za faktyczną logikę biznesową. To świadomy wybór projektowy Anthropic i szczerze mówiąc, daje programiście pełną kontrolę nad tym, co się dzieje.

Kompletna implementacja pętli agentowej

Poniższy kod pokazuje pełną pętlę agentową z obsługą wielu narzędzi, walidacją błędów i limitem iteracji. Możesz go skopiować i od razu uruchomić:

import json
import anthropic

client = anthropic.Anthropic()

# Definicje narzędzi (schemat JSON)
tools = [
    {
        "name": "pobierz_pogode",
        "description": "Pobiera aktualną prognozę pogody dla podanego miasta.",
        "input_schema": {
            "type": "object",
            "properties": {
                "miasto": {
                    "type": "string",
                    "description": "Nazwa miasta, np. Warszawa"
                }
            },
            "required": ["miasto"]
        }
    },
    {
        "name": "przelicz_walute",
        "description": "Przelicza kwotę z jednej waluty na drugą "
                       "według aktualnego kursu.",
        "input_schema": {
            "type": "object",
            "properties": {
                "kwota": {"type": "number", "description": "Kwota do przeliczenia"},
                "z_waluty": {"type": "string", "description": "Waluta źródłowa, np. PLN"},
                "na_walute": {"type": "string", "description": "Waluta docelowa, np. EUR"}
            },
            "required": ["kwota", "z_waluty", "na_walute"]
        }
    }
]

# Implementacja narzędzi (Twoja logika biznesowa)
def pobierz_pogode(miasto: str) -> dict:
    """Symulacja API pogodowego."""
    dane = {
        "Warszawa": {"temp": 12, "opis": "Pochmurno", "wilgotnosc": 78},
        "Kraków": {"temp": 10, "opis": "Deszczowo", "wilgotnosc": 85},
    }
    wynik = dane.get(miasto, {"temp": 15, "opis": "Brak danych", "wilgotnosc": 60})
    wynik["miasto"] = miasto
    return wynik

def przelicz_walute(kwota: float, z_waluty: str, na_walute: str) -> dict:
    """Symulacja API walutowego."""
    kursy = {"PLN_EUR": 0.23, "PLN_USD": 0.25, "EUR_PLN": 4.35, "USD_PLN": 3.98}
    klucz = f"{z_waluty}_{na_walute}"
    kurs = kursy.get(klucz, 1.0)
    return {"kwota_zrodlowa": kwota, "kwota_docelowa": round(kwota * kurs, 2),
            "z": z_waluty, "na": na_walute, "kurs": kurs}

# Dispatcher — mapuje nazwy narzędzi na funkcje
def wykonaj_narzedzie(nazwa: str, parametry: dict) -> str:
    funkcje = {
        "pobierz_pogode": lambda p: pobierz_pogode(p["miasto"]),
        "przelicz_walute": lambda p: przelicz_walute(
            p["kwota"], p["z_waluty"], p["na_walute"]
        ),
    }
    if nazwa not in funkcje:
        return json.dumps({"error": f"Nieznane narzędzie: {nazwa}"})
    try:
        wynik = funkcje[nazwa](parametry)
        return json.dumps(wynik, ensure_ascii=False)
    except Exception as e:
        return json.dumps({"error": str(e)})

# Pętla agentowa
def zapytaj_agenta(zapytanie: str, max_iteracji: int = 10) -> str:
    messages = [{"role": "user", "content": zapytanie}]

    for i in range(max_iteracji):
        response = client.messages.create(
            model="claude-sonnet-4-6",
            max_tokens=4096,
            tools=tools,
            messages=messages
        )

        # Sprawdź, czy Claude chce użyć narzędzi
        if response.stop_reason == "end_turn":
            # Claude zakończył — zbierz odpowiedź tekstową
            return "".join(
                blok.text for blok in response.content
                if blok.type == "text"
            )

        # Dodaj odpowiedź Claude do historii
        messages.append({"role": "assistant", "content": response.content})

        # Przetwórz wywołania narzędzi
        wyniki_narzedzi = []
        for blok in response.content:
            if blok.type == "tool_use":
                print(f"  [Narzędzie] {blok.name}({blok.input})")
                wynik = wykonaj_narzedzie(blok.name, blok.input)
                wyniki_narzedzi.append({
                    "type": "tool_result",
                    "tool_use_id": blok.id,
                    "content": wynik
                })

        # Odesłanie wyników do Claude
        messages.append({"role": "user", "content": wyniki_narzedzi})

    return "Przekroczono limit iteracji agenta."

# Uruchomienie
odpowiedz = zapytaj_agenta(
    "Jaka jest pogoda w Warszawie? I ile to jest 500 PLN w euro?"
)
print(odpowiedz)

Zwróć uwagę na kilka kluczowych elementów:

  • Limit iteracjimax_iteracji zapobiega nieskończonym pętlom, gdyby model zaczął się zapętlać. Uwierz mi, chcesz mieć to zabezpieczenie.
  • Obsługa błędów — funkcja wykonaj_narzedzie łapie wyjątki i zwraca czytelny komunikat błędu, który Claude potrafi zinterpretować i odpowiednio zareagować.
  • Historia konwersacji — cała historia jest przekazywana w każdym wywołaniu API, co pozwala Claude śledzić kontekst wieloetapowej interakcji.
  • Wiele narzędzi naraz — Claude może zwrócić kilka bloków tool_use w jednej odpowiedzi (np. pogodę i walutę jednocześnie), a pętla przetwarza je wszystkie.

Automatyczny tool_runner z SDK

Jeśli nie masz ochoty ręcznie zarządzać pętlą agentową (co jest całkowicie zrozumiałe — to sporo boilerplate'u), Anthropic Python SDK oferuje wbudowany tool_runner, który automatyzuje cały cykl. Wystarczy oznaczyć funkcje dekoratorem @beta_tool:

import json
from anthropic import Anthropic, beta_tool

client = Anthropic()

@beta_tool
def pobierz_pogode(miasto: str) -> str:
    """Pobiera aktualną prognozę pogody dla podanego miasta.

    Args:
        miasto: Nazwa miasta, np. Warszawa, Kraków
    Returns:
        JSON z danymi pogodowymi
    """
    dane = {
        "Warszawa": {"temp": 12, "opis": "Pochmurno"},
        "Kraków": {"temp": 10, "opis": "Deszczowo"},
    }
    return json.dumps(
        dane.get(miasto, {"temp": 15, "opis": "Brak danych"}),
        ensure_ascii=False
    )

@beta_tool
def przelicz_walute(kwota: float, z_waluty: str, na_walute: str) -> str:
    """Przelicza kwotę z jednej waluty na drugą.

    Args:
        kwota: Kwota do przeliczenia
        z_waluty: Waluta źródłowa (np. PLN)
        na_walute: Waluta docelowa (np. EUR)
    Returns:
        JSON z wynikiem przeliczenia
    """
    kursy = {"PLN_EUR": 0.23, "EUR_PLN": 4.35}
    kurs = kursy.get(f"{z_waluty}_{na_walute}", 1.0)
    return json.dumps({"wynik": round(kwota * kurs, 2), "waluta": na_walute})

# Automatyczna pętla agentowa — SDK zarządza cyklem
runner = client.beta.messages.tool_runner(
    model="claude-sonnet-4-6",
    max_tokens=4096,
    tools=[pobierz_pogode, przelicz_walute],
    messages=[{
        "role": "user",
        "content": "Jaka jest pogoda w Krakowie? Przelicz też 1000 PLN na EUR."
    }],
)

for message in runner:
    # Każda iteracja to jedno wywołanie API
    # tool_runner automatycznie wywołuje narzędzia i zwraca wyniki
    if hasattr(message, "content"):
        for blok in message.content:
            if hasattr(blok, "text"):
                print(blok.text)

Dekorator @beta_tool automatycznie generuje schemat JSON z docstringa i type hints funkcji. A tool_runner zajmuje się całą resztą: wysyła zapytanie, wykrywa żądania narzędzi, wykonuje je i zwraca wyniki do Claude. Zero boilerplate'u po Twojej stronie — warto z tego korzystać, szczególnie przy prototypowaniu.

Programmatic Tool Calling — orkiestracja narzędzi kodem

Czym jest PTC i kiedy go używać

Programmatic Tool Calling (PTC) to zaawansowana funkcja, która zmienia sposób orkiestracji narzędzi przez Claude. Zamiast klasycznego wzorca „jedno narzędzie — jedna runda API", Claude pisze skrypt Python, który wywołuje wiele narzędzi w pętlach, z warunkami i przetwarzaniem pośrednim.

To brzmi abstrakcyjnie, więc spójrzmy na konkrety. Kluczowe zalety PTC według benchmarków Anthropic:

  • 37% mniej tokenów — pośrednie wyniki narzędzi nie trafiają do okna kontekstu Claude, tylko są przetwarzane przez skrypt.
  • Mniejsze opóźnienia — eliminacja powtarzanych rund inferencji modelu między wywołaniami narzędzi.
  • Lepsza dokładność — jawna logika orkiestracji w kodzie Python zmniejsza ryzyko błędów (w porównaniu z poleganiem na naturalnym języku).

PTC sprawdza się szczególnie gdy:

  • Agent musi wywołać wiele narzędzi sekwencyjnie (np. lista pracowników → wydatki każdego → analiza).
  • Wyniki narzędzi wymagają filtrowania lub agregacji przed dalszym przetwarzaniem.
  • Workflow zawiera pętle i logikę warunkową (np. ponów próbę, jeśli wynik jest niepełny).

Jak działa PTC pod maską

Mechanizm jest sprytny. Cały przepływ wygląda następująco:

  1. Claude generuje kod Python w bloku server_tool_use zamiast pojedynczego żądania narzędzia.
  2. Kod wykonuje się w sandboxowanym środowisku Code Execution.
  3. Gdy skrypt wywołuje narzędzie (np. await pobierz_dane("dział IT")), wykonanie się wstrzymuje.
  4. API zwraca blok tool_use — Twój kod wykonuje narzędzie i odsyła wynik.
  5. Skrypt kontynuuje wykonanie — Claude widzi tylko finalny output, nie pośrednie wyniki.

Narzędzia klienckie są konwertowane na asynchroniczne funkcje Python z await, co wspiera równoległe wywoływanie:

# Claude generuje taki kod wewnętrznie (uproszczony przykład):
async def analyze_team_expenses():
    members = await get_team_members("engineering")
    total = 0
    for member in members:
        expenses = await get_expenses(member["id"], "Q1-2026")
        member_total = sum(e["amount"] for e in expenses)
        total += member_total
        if member_total > 5000:
            budget = await get_custom_budget(member["id"])
            print(f"{member['name']}: {member_total} (budżet: {budget})")
    print(f"Suma wydatków zespołu: {total}")

Bez PTC każde z tych wywołań wymagałoby osobnej rundy API — dla zespołu 20 osób to ponad 40 rund. Z PTC? Jeden blok kodu z automatycznym wstrzymywaniem i wznawianiem. Różnica jest ogromna, zwłaszcza w kontekście kosztów i opóźnień.

Obsługa błędów i wzorce produkcyjne

Zwracanie błędów do Claude

Gdy narzędzie napotka błąd, najlepszą praktyką jest odesłanie czytelnego komunikatu z flagą is_error. Dzięki temu Claude wie, że coś poszło nie tak, i może odpowiednio zareagować — np. poprosić o inne parametry lub poinformować użytkownika.

# W bloku przetwarzania tool_use:
try:
    wynik = wykonaj_narzedzie(blok.name, blok.input)
    wyniki_narzedzi.append({
        "type": "tool_result",
        "tool_use_id": blok.id,
        "content": wynik
    })
except Exception as e:
    wyniki_narzedzi.append({
        "type": "tool_result",
        "tool_use_id": blok.id,
        "content": f"Błąd: {str(e)}",
        "is_error": True
    })

Flaga is_error: true to mały detal, ale robi dużą różnicę. Model może wtedy spróbować z innymi parametrami albo po prostu powiedzieć użytkownikowi, co się stało.

Walidacja danych wejściowych

Claude generuje parametry zgodne ze schematem, ale mimo to warto dodać walidację po stronie kodu — zwłaszcza w systemach produkcyjnych, gdzie „ufaj, ale weryfikuj" jest świętą zasadą:

from pydantic import BaseModel, validator

class PogodaParams(BaseModel):
    miasto: str

    @validator("miasto")
    def waliduj_miasto(cls, v):
        dozwolone = ["Warszawa", "Kraków", "Gdańsk", "Wrocław", "Poznań"]
        if v not in dozwolone:
            raise ValueError(f"Nieobsługiwane miasto: {v}. "
                           f"Dostępne: {', '.join(dozwolone)}")
        return v

def pobierz_pogode_bezpiecznie(parametry: dict) -> str:
    params = PogodaParams(**parametry)  # walidacja Pydantic
    return pobierz_pogode(params.miasto)

Kontrola dostępu — Human-in-the-Loop

Dla narzędzi wykonujących wrażliwe operacje (usuwanie danych, przelewy, wysyłka e-maili) warto zaimplementować wzorzec Human-in-the-Loop. To w zasadzie potwierdzenie od człowieka przed wykonaniem operacji, której nie da się cofnąć:

NARZEDZIA_WYMAGAJACE_AKCEPTACJI = {"usun_rekord", "wyslij_przelew", "wyslij_email"}

def wykonaj_z_akceptacja(nazwa: str, parametry: dict) -> str:
    if nazwa in NARZEDZIA_WYMAGAJACE_AKCEPTACJI:
        print(f"\n⚠ Narzędzie wymaga akceptacji: {nazwa}")
        print(f"  Parametry: {json.dumps(parametry, ensure_ascii=False, indent=2)}")
        decyzja = input("  Zaakceptować? (t/n): ").strip().lower()
        if decyzja != "t":
            return json.dumps({"error": "Operacja odrzucona przez użytkownika"})
    return wykonaj_narzedzie(nazwa, parametry)

Strict mode i Structured Outputs

W środowisku produkcyjnym niedopasowanie typów w parametrach narzędzi potrafi powodować subtelne i trudne do wyłapania błędy. Anthropic oferuje tryb Structured Outputs, który gwarantuje, że wywołania narzędzi Claude zawsze będą zgodne z podanym schematem JSON.

Włączenie jest proste — dodaj "strict": true do definicji narzędzia:

tools = [
    {
        "name": "pobierz_pogode",
        "description": "Pobiera prognozę pogody dla miasta.",
        "strict": True,
        "input_schema": {
            "type": "object",
            "properties": {
                "miasto": {"type": "string"},
                "jednostka": {
                    "type": "string",
                    "enum": ["celsius", "fahrenheit"]
                }
            },
            "required": ["miasto", "jednostka"],
            "additionalProperties": False
        }
    }
]

Jest pewien kompromis: w trybie strict wszystkie właściwości muszą mieć ustawione required, a additionalProperties musi być false. W zamian zyskujesz gwarancję, że Claude nigdy nie wygeneruje parametrów niezgodnych ze schematem. To eliminuje całą klasę błędów w runtime i jest zdecydowanie warte tego dodatkowego ograniczenia.

Wybór modelu — który Claude do Tool Use?

Nie każdy model Claude sprawdza się tak samo przy pracy z narzędziami. Oto co Anthropic rekomenduje (i co pokrywa się z moim doświadczeniem):

  • Claude Opus 4.6 — najlepsza dokładność przy złożonych scenariuszach z wieloma narzędziami. Pyta o wyjaśnienia, gdy zapytanie jest niejednoznaczne, zamiast zgadywać. Idealny do prototypowania i złożonych workflow.
  • Claude Sonnet 4.6 — doskonały balans szybkości i dokładności. Dla większości zastosowań Tool Use w produkcji to będzie najlepszy wybór.
  • Claude Haiku 4.5 — najszybszy i najtańszy. Sprawdza się przy prostych narzędziach z jednoznacznymi schematami. Uwaga: może domyślnie uzupełniać brakujące parametry zamiast pytać — dlatego w produkcji koniecznie stosuj strict: true.

Wszystkie modele Claude w 2026 obsługują okno kontekstu 1 miliona tokenów. Dla porównania — GPT-4o obsługuje 128K tokenów, więc różnica jest spora. W praktyce oznacza to, że możesz przekazać Claude całą bazę kodu, setki stron dokumentacji lub długą historię konwersacji bez obaw o przekroczenie limitu.

Tool Use a MCP — jak to się łączy

Jeśli czytałeś nasz poradnik o Model Context Protocol (MCP), pewnie zastanawiasz się, jaka jest relacja między MCP a Tool Use. Krótka odpowiedź: MCP to standard transportowy dla narzędzi, a Tool Use to mechanizm wywołania w API Claude.

W praktyce wygląda to tak:

  • Tool Use (natywne API) — definiujesz narzędzia bezpośrednio w kodzie jako schematy JSON. Idealne, gdy kontrolujesz cały stack i chcesz mieć wszystko w jednym miejscu.
  • MCP — standaryzowany protokół pozwalający Claude łączyć się z zewnętrznymi serwerami narzędzi. Idealne do ekosystemów, gdzie narzędzia dostarczają różni dostawcy.

Oba podejścia można łączyć — np. natywne Tool Use dla krytycznych funkcji wewnętrznych + MCP dla integracji z zewnętrznymi usługami. Claude Agent SDK obsługuje oba mechanizmy jednocześnie, więc nie musisz się ograniczać.

Porównanie z OpenAI Function Calling

Jeśli pracowałeś wcześniej z function calling w GPT-4, Claude Tool Use wyda Ci się znajomy — ale z kilkoma istotnymi różnicami:

AspektClaude Tool UseOpenAI Function Calling
Okno kontekstu1M tokenów128K tokenów
Strict modeStructured Outputs (natywne)Strict mode (natywne)
Orkiestracja kodemPTC (Programmatic Tool Calling)Brak odpowiednika
Integracja z agentamiClaude Agent SDK + MCPOpenAI Agents SDK
KompatybilnośćSDK OpenAI (warstwa zgodności)Natywne
Wbudowane narzędziaWeb Search, Text Editor, Code ExecutionWeb Search, File Search, Code Interpreter

Warto wiedzieć, że Anthropic udostępnia warstwę zgodności z SDK OpenAI — możesz przetestować Claude z minimalnymi zmianami w istniejącym kodzie. Natomiast do produkcji zdecydowanie lepiej użyć natywnego SDK Anthropic, które daje dostęp do pełnego zestawu funkcji (prompt caching, extended thinking, PTC).

Kompletny przykład — agent asystent biurowy

Na koniec zbudujmy bardziej realistycznego agenta, który łączy wszystkie omówione koncepty. Będzie to prosty asystent biurowy z trzema narzędziami: kalendarz, kontakty i zegar systemowy.

import json
import anthropic
from datetime import datetime

client = anthropic.Anthropic()

# Symulacja firmowych systemów
baza_kalendarza = {
    "2026-03-10": [
        {"godzina": "09:00", "tytul": "Standup zespołu", "sala": "Online"},
        {"godzina": "14:00", "tytul": "Review sprintu", "sala": "Sala A"},
    ],
    "2026-03-11": [
        {"godzina": "10:00", "tytul": "Spotkanie z klientem", "sala": "Sala B"},
    ]
}

baza_kontaktow = {
    "Jan Kowalski": {"email": "[email protected]", "dzial": "IT", "tel": "501-001-001"},
    "Anna Nowak": {"email": "[email protected]", "dzial": "Marketing", "tel": "501-002-002"},
}

# Narzędzia agenta
tools = [
    {
        "name": "sprawdz_kalendarz",
        "description": "Sprawdza spotkania w kalendarzu na podany dzień.",
        "input_schema": {
            "type": "object",
            "properties": {
                "data": {
                    "type": "string",
                    "description": "Data w formacie YYYY-MM-DD"
                }
            },
            "required": ["data"]
        }
    },
    {
        "name": "znajdz_kontakt",
        "description": "Wyszukuje dane kontaktowe pracownika po imieniu i nazwisku.",
        "input_schema": {
            "type": "object",
            "properties": {
                "imie_nazwisko": {
                    "type": "string",
                    "description": "Imię i nazwisko pracownika"
                }
            },
            "required": ["imie_nazwisko"]
        }
    },
    {
        "name": "pobierz_date",
        "description": "Zwraca aktualną datę i godzinę.",
        "input_schema": {
            "type": "object",
            "properties": {},
            "required": []
        }
    }
]

def wykonaj(nazwa: str, params: dict) -> str:
    if nazwa == "sprawdz_kalendarz":
        spotkania = baza_kalendarza.get(params["data"], [])
        if not spotkania:
            return json.dumps({"info": f"Brak spotkań na {params['data']}"}, ensure_ascii=False)
        return json.dumps(spotkania, ensure_ascii=False)
    elif nazwa == "znajdz_kontakt":
        kontakt = baza_kontaktow.get(params["imie_nazwisko"])
        if not kontakt:
            return json.dumps({"error": f"Nie znaleziono: {params['imie_nazwisko']}"}, ensure_ascii=False)
        return json.dumps(kontakt, ensure_ascii=False)
    elif nazwa == "pobierz_date":
        return json.dumps({"data": datetime.now().strftime("%Y-%m-%d %H:%M")})
    return json.dumps({"error": "Nieznane narzędzie"})

def agent_biurowy(zapytanie: str) -> str:
    messages = [{"role": "user", "content": zapytanie}]
    system = ("Jesteś asystentem biurowym. Odpowiadaj zwięźle po polsku. "
              "Używaj dostępnych narzędzi, aby odpowiedzieć na pytania "
              "o kalendarz, kontakty i bieżącą datę.")

    for _ in range(10):
        response = client.messages.create(
            model="claude-sonnet-4-6",
            max_tokens=2048,
            system=system,
            tools=tools,
            messages=messages
        )

        if response.stop_reason == "end_turn":
            return "".join(b.text for b in response.content if b.type == "text")

        messages.append({"role": "assistant", "content": response.content})
        wyniki = []
        for blok in response.content:
            if blok.type == "tool_use":
                wynik = wykonaj(blok.name, blok.input)
                wyniki.append({
                    "type": "tool_result",
                    "tool_use_id": blok.id,
                    "content": wynik
                })
        messages.append({"role": "user", "content": wyniki})

    return "Przekroczono limit iteracji."

# Przykładowe zapytania
print(agent_biurowy("Jakie mam spotkania 10 marca 2026?"))
print("---")
print(agent_biurowy("Jaki jest email Jana Kowalskiego i kiedy mam "
                     "następne spotkanie?"))

Ten agent łączy trzy narzędzia w spójny system. Claude samodzielnie decyduje, które narzędzia wywołać i w jakiej kolejności. Na pytanie „kiedy mam następne spotkanie?" najpierw pobierze aktualną datę, a potem sprawdzi kalendarz — dokładnie tak, jak zrobiłby to człowiek.

Najczęściej zadawane pytania (FAQ)

Czym różni się Tool Use od Function Calling?

To w zasadzie dwie nazwy tego samego mechanizmu. Anthropic używa terminu „Tool Use", a OpenAI „Function Calling". Koncept jest identyczny: model sygnalizuje chęć wywołania zewnętrznej funkcji, Twój kod ją wykonuje i odsyła wynik. Różnice leżą w szczegółach API (format schematów, nazwy pól), ale wzorzec architektoniczny jest ten sam.

Ile narzędzi mogę zdefiniować w jednym wywołaniu API?

Nie ma sztywnego limitu, ale każda definicja narzędzia zużywa tokeny z okna kontekstu. Przy dużych bibliotekach narzędzi (50+) Anthropic rekomenduje wzorzec Tool Search — definiujesz narzędzie-wyszukiwarkę, które dynamicznie ładuje definicje pozostałych narzędzi na żądanie. Sprytne rozwiązanie, które w praktyce działa bardzo dobrze.

Czy Claude może wywoływać narzędzia równolegle?

Tak. Claude może zwrócić wiele bloków tool_use w jednej odpowiedzi, gdy uzna, że narzędzia są od siebie niezależne. Twój kod powinien wtedy wykonać je wszystkie i odesłać wyniki w jednej wiadomości. W trybie PTC Claude może dodatkowo orkiestrować równoległe wywołania za pomocą asyncio w generowanym kodzie Python.

Jak zapobiec niepotrzebnym wywołaniom narzędzi?

Użyj parametru tool_choice w zapytaniu API. Wartość {"type": "auto"} (domyślna) pozwala Claude decydować samodzielnie. Wartość {"type": "none"} wyłącza narzędzia dla danego zapytania. Możesz też wymusić konkretne narzędzie: {"type": "tool", "name": "pobierz_pogode"}. No i oczywiście — precyzyjne opisy w polach description znacząco poprawiają trafność decyzji modelu.

Czy PTC działa z Claude Code?

Na dzień marca 2026 PTC nie jest jeszcze dostępny w Claude Code (terminal i aplikacja desktopowa). Na GitHubie jest otwarte zgłoszenie (issue #12836) z prośbą o dodanie tego wsparcia. Na razie jedyną drogą do korzystania z PTC pozostaje bezpośrednia integracja przez API Claude.

O Autorze Editorial Team

Our team of expert writers and editors.