Function calling (tool use) po polsku: jak AI wywołuje Twoje funkcje
Jak model prosi o wywołanie funkcji, a Twój kod ją wykonuje. Działający przykład w Pythonie z trzema narzędziami i prawdziwymi przebiegami.

👁 115 przeczytań
- Function calling to mechanizm, w którym model językowy nie wykonuje kodu, lecz zwraca ustrukturyzowaną prośbę - np. pogoda(miasto='Gdańsk') - a Twój program wywołuje funkcję i odsyła wynik.
- Mechanizm obsługują wszystkie duże API: OpenAI, Anthropic (Claude), Google (Gemini), Mistral, DeepSeek i Qwen, a format opisu funkcji oparty jest na JSON Schema.
- Przykład z trzema funkcjami (pogoda, kalkulator, Wikipedia) uruchomiony na modelu openai/gpt-5.6-luna przez OpenRouter kosztował 0,001 USD łącznie za trzy testowe pytania.
Function calling (w Claude nazywa się to tool use) to mechanizm, w którym model językowy nie wykonuje kodu sam, tylko odpisuje: „wywołaj funkcję X z takimi argumentami”. Twój program wykonuje funkcję, odsyła wynik, a model składa z niego odpowiedź. Poniżej pokazuję cały obieg na działającym przykładzie w Pythonie (pogoda, kalkulator, Wikipedia), z prawdziwymi przebiegami z 4 października 2026. To drugi krok ścieżki Agenci AI od zera.
Stan na 4 października 2026
- Function calling obsługują wszystkie duże API: OpenAI, Anthropic (Claude), Google (Gemini), Mistral, DeepSeek, Qwen. Format jest prawie taki sam: opis funkcji w JSON Schema.
- Przykład uruchomiłem na Windows 11, Python 3.13, biblioteka
openai3.24.0, modelopenai/gpt-5.6-lunaprzez OpenRouter. - Koszt trzech testowych pytań: 0,001 USD łącznie.
- Kod jest w paczce promptowy-agenci-starter (plik
01_function_calling.py).
Co to jest function calling, prostymi słowami
Model językowy umie tylko jedno: przyjąć tekst i oddać tekst. Nie ma internetu, zegarka ani kalkulatora. Function calling to umowa: w zapytaniu do API dokładasz listę funkcji, które masz u siebie, z opisem i listą parametrów. Model, zamiast zgadywać, może odpowiedzieć ustrukturyzowaną prośbą: „chcę pogoda(miasto="Gdańsk")”.
Najważniejsza rzecz, którą warto zrozumieć na starcie: model niczego nie uruchamia. To Twój kod czyta tę prośbę, woła prawdziwą funkcję, a wynik dokleja do rozmowy. Dzięki temu Ty decydujesz, co model może zrobić. Jeśli nie dasz mu funkcji „usuń plik”, nie ma jak czegoś usunąć.
Jeśli nie wiesz jeszcze, czym jest zapytanie do API i token, zacznij od przewodnika po API modeli AI i tekstu o tokenach.
Cały obieg w 5 krokach
- Opisujesz funkcje. Nazwa, opis po ludzku i parametry w JSON Schema. Opis jest dla modelu instrukcją, kiedy ma po funkcję sięgnąć.
- Wysyłasz pytanie razem z listą funkcji (parametr
tools). - Model odpowiada prośbą o wywołanie (
tool_calls) albo zwykłym tekstem, jeśli funkcje nie są potrzebne. - Twój kod wykonuje funkcję i dokleja wynik do rozmowy jako wiadomość z rolą
tool, z tym samymtool_call_id. - Wysyłasz rozmowę jeszcze raz. Model albo prosi o kolejne funkcje, albo daje odpowiedź końcową.
Kroki 3-5 powtarzają się w pętli. Jeśli tę pętlę oddasz modelowi i pozwolisz mu samemu decydować, ile razy i po co sięga po narzędzia, masz już agenta. O tym jest tekst Jak zbudować agenta AI w Pythonie.
Jak wygląda prośba modelu o wywołanie funkcji
Zapytałem „Ile to 15% z 2340 zł?”, dając modelowi kalkulator. Oto surowa odpowiedź API, bez żadnej obróbki:
# surowa wiadomość modelu (message) na pytanie „Ile to 15% z 2340 zł?”
{
"role": "assistant",
"tool_calls": [
{
"id": "call_m25uxE3pxeXOqgLd33CWNInn",
"function": {
"arguments": "{\"wyrazenie\":\"2340*0.15\"}",
"name": "kalkulator"
},
"type": "function",
"index": 0
}
]
}
finish_reason: tool_calls koszt 6.58e-05Zwróć uwagę na trzy rzeczy:
- pole
contentjest puste: model nic nie napisał, tylko poprosił o funkcję; argumentsto tekst z JSON-em, nie gotowy obiekt. Trzeba go sparsować (json.loads), a w razie błędu odesłać modelowi komunikat;finish_reasonma wartośćtool_calls. Po tym poznajesz, że rozmowa się nie skończyła.
Działający przykład: pogoda, kalkulator i Wikipedia
Napisałem trzy zwykłe funkcje w Pythonie. Żadna nie wymaga klucza API: pogodę daje darmowe Open-Meteo, wyszukiwanie daje API polskiej Wikipedii, a kalkulator liczy lokalnie. Kalkulator celowo nie używa eval(), tylko rozbiera wyrażenie przez moduł ast. Gdyby model (albo ktoś, kto mu podsunie złośliwy tekst) wpisał tam polecenie systemowe, eval by je wykonał.
"""01 - Function calling (tool use): model prosi o wywołanie Twojej funkcji, Ty ją wykonujesz.
Uruchom: python 01_function_calling.py "Jaka jest pogoda w Gdańsku i ile to 15% z 2340 zł?"
"""
import ast, json, operator, os, sys
import requests
from dotenv import load_dotenv
from openai import OpenAI
load_dotenv()
MODEL = os.getenv("MODEL", "openai/gpt-5.6-luna")
client = OpenAI(base_url="https://openrouter.ai/api/v1", api_key=os.environ["OPENROUTER_API_KEY"])
UA = {"User-Agent": "promptowy-agenci-starter/1.0"}
# --- 1. Zwykłe funkcje w Pythonie ---------------------------------------------
def pogoda(miasto: str) -> dict:
geo = requests.get("https://geocoding-api.open-meteo.com/v1/search",
params={"name": miasto, "count": 1, "language": "pl"}, headers=UA, timeout=15).json()
if not geo.get("results"):
return {"blad": f"nie znam miasta {miasto}"}
m = geo["results"][0]
w = requests.get("https://api.open-meteo.com/v1/forecast", params={
"latitude": m["latitude"], "longitude": m["longitude"],
"current": "temperature_2m,wind_speed_10m,precipitation", "timezone": "Europe/Warsaw"},
headers=UA, timeout=15).json()
return {"miasto": m["name"], **w["current"]}
OPS = {ast.Add: operator.add, ast.Sub: operator.sub, ast.Mult: operator.mul,
ast.Div: operator.truediv, ast.Pow: operator.pow, ast.USub: operator.neg}
def kalkulator(wyrazenie: str) -> dict:
def licz(n): # bezpieczne liczenie: tylko liczby i + - * / **
if isinstance(n, ast.Constant) and isinstance(n.value, (int, float)): return n.value
if isinstance(n, ast.BinOp): return OPS[type(n.op)](licz(n.left), licz(n.right))
if isinstance(n, ast.UnaryOp): return OPS[type(n.op)](licz(n.operand))
raise ValueError("niedozwolone wyrażenie")
return {"wyrazenie": wyrazenie, "wynik": licz(ast.parse(wyrazenie, mode="eval").body)}
def szukaj_wikipedia(fraza: str) -> dict:
r = requests.get("https://pl.wikipedia.org/w/api.php", params={
"action": "query", "list": "search", "srsearch": fraza, "srlimit": 3, "format": "json"},
headers=UA, timeout=15).json()
return {"wyniki": [{"tytul": h["title"], "fragment": h["snippet"].replace('<span class="searchmatch">', "").replace("</span>", "")}
for h in r["query"]["search"]]}
FUNKCJE = {"pogoda": pogoda, "kalkulator": kalkulator, "szukaj_wikipedia": szukaj_wikipedia}
# --- 2. Opis funkcji dla modelu (JSON Schema) ----------------------------------
def narzedzie(nazwa, opis, param, opis_param):
return {"type": "function", "function": {"name": nazwa, "description": opis, "parameters": {
"type": "object", "properties": {param: {"type": "string", "description": opis_param}}, "required": [param]}}}
TOOLS = [
narzedzie("pogoda", "Aktualna pogoda w podanym mieście (temperatura, wiatr, opad).", "miasto", "nazwa miasta, np. Kraków"),
narzedzie("kalkulator", "Liczy wyrażenie arytmetyczne. Używaj zamiast liczenia w pamięci.", "wyrazenie", "np. 2340*0.15"),
narzedzie("szukaj_wikipedia", "Szuka w polskiej Wikipedii, zwraca 3 tytuły z fragmentami.", "fraza", "czego szukać"),
]
# --- 3. Rozmowa: model -> wywołania funkcji -> wyniki -> odpowiedź --------------
pytanie = sys.argv[1] if len(sys.argv) > 1 else "Jaka jest teraz pogoda w Gdańsku i ile to 15% z 2340 zł?"
messages = [{"role": "system", "content": "Odpowiadaj po polsku, krótko. Do faktów i liczenia używaj narzędzi."},
{"role": "user", "content": pytanie}]
koszt = 0.0
for runda in range(1, 6):
r = client.chat.completions.create(model=MODEL, messages=messages, tools=TOOLS)
koszt += getattr(r.usage, "cost", 0) or 0
msg = r.choices[0].message
if not msg.tool_calls: # model nie chce już narzędzi = gotowa odpowiedź
print(f"\n[runda {runda}] ODPOWIEDŹ:\n{msg.content}")
break
messages.append(msg.model_dump(exclude_none=True))
for call in msg.tool_calls:
args = json.loads(call.function.arguments)
print(f"[runda {runda}] model prosi: {call.function.name}({json.dumps(args, ensure_ascii=False)})")
try:
wynik = FUNKCJE[call.function.name](**args)
except Exception as e: # błąd też oddajemy modelowi, niech sam zdecyduje
wynik = {"blad": str(e)}
print(f" wynik: {json.dumps(wynik, ensure_ascii=False)[:300]}")
messages.append({"role": "tool", "tool_call_id": call.id, "content": json.dumps(wynik, ensure_ascii=False)})
print(f"\nmodel: {MODEL}, rund: {runda}, koszt: {koszt:.6f} USD")Instalacja to pip install openai requests python-dotenv i plik .env z kluczem OPENROUTER_API_KEY. Przez OpenRouter ten sam kod działa z modelami OpenAI, Google, Anthropic czy DeepSeek: zmieniasz tylko nazwę modelu w zmiennej MODEL.
Przebieg 1: dwie funkcje naraz
> python 01_function_calling.py "Jaka jest teraz pogoda w Gdańsku i ile to 15% z 2340 zł?"
[runda 1] model prosi: pogoda({"miasto": "Gdańsk"})
wynik: {"miasto": "Gdańsk", "time": "2026-10-04T13:45", "interval": 900, "temperature_2m": 19.0, "wind_speed_10m": 14.0, "precipitation": 0.0}
[runda 1] model prosi: kalkulator({"wyrazenie": "2340*0.15"})
wynik: {"wyrazenie": "2340*0.15", "wynik": 351.0}
[runda 2] ODPOWIEDŹ:
W Gdańsku jest teraz **19°C**, wiatr **14 km/h**, bez opadów.
**15% z 2340 zł = 351 zł.**
model: openai/gpt-5.6-luna, rund: 2, koszt: 0.000260 USDModel w pierwszej rundzie poprosił o dwie funkcje jednocześnie (to się nazywa parallel tool calls). Mój kod wykonał obie, odesłał wyniki i w drugiej rundzie dostałem gotową odpowiedź. Temperatura 19 stopni to faktyczny odczyt z Open-Meteo z 13:45, a nie zgadywanie modelu.
Przebieg 2: narzędzie zwraca śmieci
Mam to spisane w całości
Zebrałem te rzeczy w ebookach, które możesz pobrać za darmo - bez zapisu na listę, bez haczyka.
Ten przebieg jest ciekawszy, bo pokazuje typowy problem:
> python 01_function_calling.py "Kto zbudował Pałac Kultury i Nauki i ile lat ma ten budynek w 2026 roku?"
[runda 1] model prosi: szukaj_wikipedia({"fraza": "Pałac Kultury i Nauki kto zbudował ukończony 1955"})
wynik: {"wyniki": [{"tytul": "Siedem Sióstr (Moskwa)", "fragment": "2019-09-05] (ros.). Jarosław Zieliński: Pałac Kultury i Nauki, Księży Młyn 2012, s.16. Pałac Kultury i Nauki w Warszawie [online], www.pkin.pl [dostęp"}, {"tytul": "Drezno", "fragment": " kościół Krzyża (do 1955), Zwinger (do 1963), kated
[runda 2] model prosi: kalkulator({"wyrazenie": "2026-1955"})
wynik: {"wyrazenie": "2026-1955", "wynik": 71}
[runda 3] ODPOWIEDŹ:
Pałac Kultury i Nauki zbudował **Związek Radziecki** jako dar dla Polski. Projekt opracował **Lew Rudniew**, a budowę prowadzili radzieccy robotnicy.
Oddano go do użytku **22 lipca 1955 roku**, więc w 2026 roku będzie miał **71 lat** - dokładnie od 22 lipca; wcześniej w tym roku będzie miał jeszcze 70 lat.
model: openai/gpt-5.6-luna, rund: 3, koszt: 0.000662 USDModel wpisał do wyszukiwarki zbyt długie zapytanie („Pałac Kultury i Nauki kto zbudował ukończony 1955”) i Wikipedia zwróciła artykuły o moskiewskich wieżowcach i o Dreźnie. Model tego nie zauważył głośno, tylko odpowiedział z własnej wiedzy. Tym razem fakty się zgadzają (oddanie do użytku 22 lipca 1955, projekt Lwa Rudniewa), ale gdyby chodziło o świeże dane, mielibyśmy halucynację z pozorem źródła. Wnioski, które z tego wyciągnąłem:
- w opisie funkcji wyszukiwania dopisz: „używaj krótkich zapytań, 2-4 słowa”;
- w poleceniu systemowym każ modelowi mówić, kiedy narzędzie nie dało odpowiedzi;
- loguj wszystkie wywołania i wyniki. Bez logu nie zauważyłbym, że odpowiedź nie pochodzi z Wikipedii.
Przebieg 3: model wie, kiedy funkcje są zbędne
> python 01_function_calling.py "Napisz jedno zdanie zachęty do nauki programowania."
[runda 1] ODPOWIEDŹ:
Zacznij naukę programowania już dziś i twórz własne pomysły, aplikacje oraz nowe możliwości kariery!
model: openai/gpt-5.6-luna, rund: 1, koszt: 0.000080 USDProśba o zdanie zachęty nie wymagała żadnej funkcji, więc model odpowiedział od razu, w jednej rundzie, za 0,00008 USD. Nie musisz sam decydować, kiedy dać modelowi narzędzia, a kiedy nie. Dajesz zawsze, a model wybiera.
Jak pisać opisy funkcji, żeby model ich dobrze używał
- Opis to instrukcja. „Liczy wyrażenie arytmetyczne. Używaj zamiast liczenia w pamięci” działa lepiej niż „kalkulator”.
- Parametry z przykładami.
"np. 2340*0.15"w opisie parametru ucina większość błędów formatu. - Mało funkcji naraz. 3-10 dobrze opisanych funkcji to bezpieczny zakres. Przy kilkudziesięciu model zaczyna mylić podobne narzędzia.
- Wynik w JSON, krótki. Każdy znak wyniku wraca do modelu jako tokeny wejściowe i kosztuje. Przycinaj długie odpowiedzi.
- Błąd też jest wynikiem. Zamiast przerywać program, odeślij
{"blad": "nie znam miasta"}. Model zwykle sam poprawi argument.
Function calling w OpenAI, Claude i Gemini: różnice
| API | Nazwa | Gdzie opis funkcji | Jak przychodzi prośba | Jak odsyłasz wynik |
|---|---|---|---|---|
| OpenAI (Chat Completions, też OpenRouter) | function calling / tools | tools=[{"type":"function","function":{...}}] | message.tool_calls | wiadomość role: "tool" z tool_call_id |
| Anthropic (Claude) | tool use | tools=[{"name","description","input_schema"}] | blok tool_use w treści, stop_reason: "tool_use" | blok tool_result w wiadomości user |
| Google (Gemini) | function calling | function_declarations | functionCall w odpowiedzi | functionResponse |
Pod spodem to ten sam pomysł. Dlatego biblioteki takie jak LangGraph czy OpenAI Agents SDK potrafią przełączać się między dostawcami, a protokół MCP pozwala opisać narzędzia raz dla wszystkich aplikacji.
Bezpieczeństwo: o czym pamiętać
- Model może wywołać funkcję z argumentami, których się nie spodziewasz. Waliduj je w kodzie (zakresy, ścieżki, typy).
- Funkcje, które coś zmieniają (wysyłają maila, płacą, kasują), powinny prosić człowieka o potwierdzenie.
- Tekst ze stron WWW i dokumentów, który trafia do modelu przez narzędzie, może zawierać ukryte polecenia (prompt injection). Traktuj go jako dane, nie jako rozkazy.
Najczęstsze pytania
Czym się różni function calling od tool use?
Niczym istotnym. OpenAI i Google mówią „function calling”, Anthropic „tool use”. W obu przypadkach model zwraca nazwę narzędzia i argumenty, a kod je wykonuje.
Czy function calling działa po polsku?
Tak. Opisy funkcji i parametrów pisałem po polsku, model rozumiał je bez problemu i sam wpisywał polskie nazwy miast. Nazwy funkcji lepiej trzymać bez polskich znaków (szukaj_wikipedia), bo niektóre API dopuszczają w nazwach tylko litery łacińskie, cyfry i podkreślenia.
Czy każdy model obsługuje function calling?
Nie. Duże modele komercyjne tak. Lokalne modele w Ollamie obsługują narzędzia, jeśli mają to w szablonie (np. Llama 3.1, Qwen, Mistral), ale mniejsze częściej mylą argumenty. Na OpenRouterze lista modeli ma filtr „tools”.
Ile kosztuje function calling?
Tyle, co zwykłe tokeny, ale każda runda to osobne zapytanie, a opisy funkcji doliczają się do wejścia przy każdym zapytaniu. U mnie pytanie z dwiema funkcjami kosztowało 0,00026 USD, a z trzema rundami 0,00066 USD.
Czy model może sam uruchomić kod na moim komputerze?
Nie. Model tylko prosi. Uruchamia Twój program i tylko te funkcje, które sam podłączysz.
Function calling a agent AI: czym się różnią?
Function calling to mechanizm: jedno zapytanie, jedna prośba o funkcję. Agent to pętla, w której model wiele razy wybiera narzędzia, ogląda wyniki i decyduje, co dalej, aż skończy zadanie. Przykłady agentów są w tekście Agent AI: przykłady.
Google · Twoje źródłaPromptowy wyżej w Twoim Google - jednym kliknięciemDodaj do preferowanych źródeł →Źródła i data sprawdzenia
- OpenAI: Function calling guide (sprawdzone 4.10.2026).
- Anthropic: Tool use with Claude (sprawdzone 4.10.2026).
- Google: Function calling with the Gemini API (sprawdzone 4.10.2026).
- Dane pogodowe: Open-Meteo, wyszukiwanie: API polskiej Wikipedii.
- Wszystkie przebiegi: mój komputer, 4 października 2026, 13:49-13:50.
Cały tydzień w AI, w jednym mailu
Wybrane premiery, narzędzia i analizy. Raz w tygodniu, prosto do skrzynki.
Zapisz się za darmo →
