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
descriptiondo 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 (
miastozamiastm,zapytaniezamiastq). - Enum dla ograniczonych opcji — jeśli parametr przyjmuje jedną z kilku wartości, użyj
enumzamiast samegostring. Dzięki temu Claude nie będzie zgadywał. - Opisuj każdy parametr — pole
descriptionw schemacie pomaga Claude zrozumieć, co dokładnie podać jako wartość. - Strict mode — w produkcji dodaj
"strict": truedo 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:
- Wysyłasz zapytanie użytkownika wraz z listą dostępnych narzędzi.
- Claude analizuje zapytanie i decyduje, czy potrzebuje narzędzia.
- Jeśli tak — zwraca blok
tool_usez nazwą narzędzia i parametrami (zamiast odpowiedzi tekstowej). - Twój kod wykonuje odpowiednią funkcję i odsyła wynik jako blok
tool_result. - 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 iteracji —
max_iteracjizapobiega 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_usew 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:
- Claude generuje kod Python w bloku
server_tool_usezamiast pojedynczego żądania narzędzia. - Kod wykonuje się w sandboxowanym środowisku Code Execution.
- Gdy skrypt wywołuje narzędzie (np.
await pobierz_dane("dział IT")), wykonanie się wstrzymuje. - API zwraca blok
tool_use— Twój kod wykonuje narzędzie i odsyła wynik. - 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:
| Aspekt | Claude Tool Use | OpenAI Function Calling |
|---|---|---|
| Okno kontekstu | 1M tokenów | 128K tokenów |
| Strict mode | Structured Outputs (natywne) | Strict mode (natywne) |
| Orkiestracja kodem | PTC (Programmatic Tool Calling) | Brak odpowiednika |
| Integracja z agentami | Claude Agent SDK + MCP | OpenAI Agents SDK |
| Kompatybilność | SDK OpenAI (warstwa zgodności) | Natywne |
| Wbudowane narzędzia | Web Search, Text Editor, Code Execution | Web 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.