Model językowy sam z siebie nie zna dzisiejszej pogody w Serengeti ani aktualnej populacji lwów - potrafi tylko układać słowa. Function calling daje mu narzędzia: Twoje funkcje Pythona, po które może sięgnąć, gdy pytanie wykracza poza to, co wie.
Zanim napiszesz linijkę kodu, zrozum jedno - to źródło wszystkich nieporozumień z function calling. Model nigdy sam nie uruchamia Twojej funkcji. On tylko mówi: "chcę wywołać
get_weather z argumentem Serengeti". Wykonanie należy do Ciebie. To rozmowa w turach:Przejdziemy przez te tury po kolei. Zaczynamy od tego, jak opisać modelowi narzędzie - bo model nie widzi Twojego kodu, tylko jego opis.
1from openai import OpenAI
2import json
3
4client = OpenAI()
5
6tools = [
7 {
8 "type": "function",
9 "function": {
10 "name": "get_weather",
11 "description": "Pobiera pogodę dla lokalizacji Safari",
12 "parameters": {
13 "type": "object",
14 "properties": {
15 "location": {
16 "type": "string",
17 "description": "Nazwa lokalizacji, np. 'Serengeti'"
18 }
19 },
20 "required": ["location"]
21 }
22 }
23 }
24]Zwróć uwagę: to jest sam opis narzędzia, nie jego działanie. Pola
description są tu najważniejsze - to po nich model decyduje, kiedy sięgnąć po funkcję i co wpisać w argument. Im lepszy opis, tym trafniej model wybiera narzędzie. Sekcja required mówi, które argumenty są obowiązkowe.Teraz wysyłamy pytanie razem z listą narzędzi. Uważnie przeczytaj, co model zwraca - to nie odpowiedź dla użytkownika.
1response = client.chat.completions.create(
2 model="gpt-5",
3 messages=[{"role": "user", "content": "Jaka jest pogoda w Serengeti?"}],
4 tools=tools,
5 tool_choice="auto"
6)
7
8message = response.choices[0].message
9if message.tool_calls:
10 for tool_call in message.tool_calls:
11 function_name = tool_call.function.name
12 arguments = json.loads(tool_call.function.arguments)
13 print(f"Model chce wywołać: {function_name}({arguments})")Zamiast gotowej odpowiedzi model zwrócił
tool_calls - listę próśb o wywołanie. Argumenty przychodzą jako tekst JSON, dlatego json.loads zamienia je na słownik Pythona. Na tym etapie nic się jeszcze nie wydarzyło - model tylko powiedział, czego chce. Wykonanie jest naszym kolejnym krokiem.Model zna nazwę funkcji tylko jako napis. Musimy więc mieć jej prawdziwą implementację i sposób, by z nazwy trafić do właściwego kodu.
1def get_weather(location: str) -> dict:
2 # W prawdziwej aplikacji - wywołanie API pogodowego
3 return {"location": location, "temperature": 28, "conditions": "Słonecznie"}
4
5available_functions = {
6 "get_weather": get_weather
7}Słownik
available_functions to most między światem modelu a Twoim kodem: model podaje napis "get_weather", a Ty po tym napisie wyciągasz prawdziwą funkcję. To Ty decydujesz, co naprawdę się wykona - model nie ma dostępu do niczego, czego mu tu nie udostępnisz. Zauważ, że to naturalne miejsce na kontrolę bezpieczeństwa.Zobacz teraz pełną rozmowę: pytanie, prośba modelu, wykonanie, oddanie wyniku i finalna odpowiedź. Prześledź, jak lista
messages rośnie o kolejne tury.1def chat_with_tools(user_message: str):
2 messages = [{"role": "user", "content": user_message}]
3
4 response = client.chat.completions.create(
5 model="gpt-5", messages=messages, tools=tools
6 )
7 message = response.choices[0].message
8
9 if message.tool_calls:
10 messages.append(message) # zapisz prośbę modelu
11
12 for tool_call in message.tool_calls:
13 name = tool_call.function.name
14 args = json.loads(tool_call.function.arguments)
15 result = available_functions[name](**args) # Ty wykonujesz
16
17 messages.append({
18 "role": "tool",
19 "tool_call_id": tool_call.id,
20 "content": json.dumps(result) # oddaj wynik modelowi
21 })
22
23 final = client.chat.completions.create(model="gpt-5", messages=messages)
24 return final.choices[0].message.content
25
26 return message.contentKluczowe są dwa wywołania
create: pierwsze, w którym model prosi o narzędzie, i drugie - po dopisaniu wyniku - w którym model układa odpowiedź dla człowieka. Rola "tool" i tool_call_id to sposób, w jaki mówisz modelowi "oto wynik dokładnie tej funkcji, o którą prosiłeś". Bez tej drugiej tury użytkownik dostałby surowy JSON zamiast zdania.Ręczne prowadzenie tur bywa żmudne. LangChain pakuje całą tę pętlę w agenta - Ty tylko oznaczasz funkcje jako narzędzia, a on sam prowadzi rozmowę.
1from langchain.tools import tool
2from langchain.agents import create_tool_calling_agent, AgentExecutor
3from langchain_openai import ChatOpenAI
4from langchain_core.prompts import ChatPromptTemplate
5
6@tool
7def calculate_distance(from_loc: str, to_loc: str) -> str:
8 """Oblicza odległość między lokalizacjami Safari."""
9 return f"Odległość z {from_loc} do {to_loc}: 150 km"
10
11llm = ChatOpenAI(model="gpt-5")
12prompt = ChatPromptTemplate.from_messages([
13 ("system", "Jesteś pomocnym asystentem Safari."),
14 ("human", "{input}"),
15 ("placeholder", "{agent_scratchpad}")
16])
17
18agent = create_tool_calling_agent(llm, [calculate_distance], prompt)
19executor = AgentExecutor(agent=agent, tools=[calculate_distance], verbose=True)
20executor.invoke({"input": "Jak daleko jest z Nairobi do Serengeti?"})Dekorator
@tool robi to, co wcześniej pisaliśmy ręcznie: z docstringa buduje opis dla modelu, a z sygnatury - listę argumentów. AgentExecutor prowadzi za Ciebie wszystkie tury z poprzedniego przykładu, w kółko, aż model uzna, że ma komplet informacji.Pamiętaj z tej lekcji jedno zdanie: function calling to nie "AI uruchamia kod", tylko model prosi, a Ty wykonujesz i oddajesz wynik. Cała reszta - OpenAI czy LangChain - to tylko wygodniejszy zapis tej samej rozmowy w turach.