Proč by vás function calling měl zajímat
Jazykové modely umí generovat text. To je skvělé — ale upřímně, k ničemu, pokud potřebujete, aby váš AI agent skutečně udělal něco v reálném světě. Odeslal e-mail. Načetl data z databáze. Zavolal API třetí strany. Vytvořil objednávku.
Přesně tady vstupuje na scénu function calling (volání funkcí), nebo jak to Anthropic nazývá — tool use.
Function calling je schopnost LLM rozpoznat, že pro splnění uživatelského požadavku potřebuje zavolat externí funkci, a vygenerovat strukturovaný výstup (typicky JSON) s názvem funkce a jejími parametry. Model funkci sám nevolá — pouze řekne „zavolej tohle s těmito argumenty" a vaše aplikace to provede. Takový prostředník, který ví co chce, ale nemůže si to udělat sám.
V roce 2026 je function calling naprosto zásadní stavební blok. Bez něj nepostavíte nic z toho, o čem se dnes mluví — AI agenty, automatizované workflow, inteligentní asistenty. Je to most mezi jazykovým modelem a zbytkem vašeho tech stacku. A pokud jste četli náš průvodce Model Context Protocol (MCP), tak vězte — i MCP pod kapotou využívá právě function calling pro invokaci nástrojů.
V tomhle článku si projdeme kompletní implementaci function callingu v Pythonu. Od základního workflow přes praktické příklady s OpenAI Responses API i Anthropic Claude API, paralelní a sekvenční volání, structured outputs, error handling až po best practices pro produkční nasazení. Všechno s funkčním kódem, který si můžete rovnou vyzkoušet.
Jak function calling funguje — krok za krokem
Než se vrhneme na kód, pojďme si rozebrat celý životní cyklus function callu. Proces probíhá ve čtyřech fázích a je to vlastně taková konverzační smyčka mezi vaší aplikací a modelem.
Fáze 1: Definice dostupných nástrojů
Vývojář při volání API předá modelu seznam dostupných funkcí — jejich názvy, popisy a JSON Schema popisující vstupní parametry. Model ze schématu pochopí, jaké nástroje má k dispozici a kdy je použít.
Fáze 2: Model rozhodne o volání
Na základě uživatelského promptu model vyhodnotí, jestli je potřeba zavolat funkci. Pokud ano, místo textové odpovědi vrátí strukturovaný JSON s názvem funkce a argumenty. Pokud ne, odpoví normálním textem. Tady je to klíčové — model funkci sám nespouští. Pouze generuje instrukce.
Fáze 3: Aplikace provede funkci
Vaše aplikace přijme JSON od modelu, naparsuje ho, zavolá příslušnou funkci s danými argumenty a získá výsledek. Tady probíhá ta skutečná akce — dotaz do databáze, HTTP request, výpočet, cokoliv.
Fáze 4: Výsledek zpět modelu
Výsledek funkce pošlete zpět modelu jako součást konverzace. Model ho zpracuje a buď vygeneruje finální odpověď pro uživatele, nebo se rozhodne zavolat další funkci (sekvenční volání).
Celý cyklus se může opakovat — model může zavolat více funkcí za sebou, než dospěje ke konečné odpovědi. A přesně tohle je základ toho, jak fungují AI agenti.
Implementace s OpenAI Responses API
OpenAI v březnu 2025 představilo Responses API, které nahrazuje starší Chat Completions i Assistants API (to bude ukončeno v roce 2026). Responses API je nyní doporučený způsob pro práci s function callingem u modelů GPT.
Základní příklad — získání počasí
Začneme klasickým příkladem, na kterém si ukážeme celý flow. Definujeme funkci pro získání aktuálního počasí a necháme model, aby ji zavolal:
import json
from openai import OpenAI
client = OpenAI()
# Definice nástroje
tools = [
{
"type": "function",
"name": "get_weather",
"description": "Vrátí aktuální počasí pro zadané město.",
"parameters": {
"type": "object",
"properties": {
"city": {
"type": "string",
"description": "Název města, např. Praha, Brno"
},
"units": {
"type": "string",
"enum": ["celsius", "fahrenheit"],
"description": "Jednotky teploty"
}
},
"required": ["city"]
},
"strict": True
}
]
# Simulace skutečné funkce
def get_weather(city: str, units: str = "celsius") -> dict:
# V produkci by zde byl HTTP request na weather API
return {"city": city, "temperature": 18, "units": units, "condition": "oblačno"}
# Krok 1: Odeslání promptu s nástroji
response = client.responses.create(
model="gpt-4o",
input="Jaké je dnes počasí v Praze?",
tools=tools
)
# Krok 2: Zpracování odpovědi modelu
for item in response.output:
if item.type == "function_call":
# Model chce zavolat funkci
args = json.loads(item.arguments)
result = get_weather(**args)
# Krok 3: Poslat výsledek zpět modelu
final_response = client.responses.create(
model="gpt-4o",
input=[
{"role": "user", "content": "Jaké je dnes počasí v Praze?"},
item, # function_call od modelu
{
"type": "function_call_output",
"call_id": item.call_id,
"output": json.dumps(result)
}
],
tools=tools
)
print(final_response.output_text)
Všimněte si parametru strict: True v definici nástroje. To aktivuje Structured Outputs — OpenAI garantuje, že vygenerovaný JSON bude přesně odpovídat vašemu schématu. Žádné chybějící povinné parametry, žádné špatné datové typy. Pro produkci je to naprosto klíčové (a upřímně, bez toho bych to do produkce nepouštěl).
Paralelní volání funkcí
Moderní modely dokážou v jedné odpovědi zavolat více funkcí najednou. To je fakt šikovná věc. Pokud se uživatel zeptá na počasí ve třech městech, model vrátí tři function cally současně místo jednoho po druhém:
response = client.responses.create(
model="gpt-4o",
input="Porovnej počasí v Praze, Brně a Ostravě.",
tools=tools
)
# response.output může obsahovat 3 function_call položky
results = []
for item in response.output:
if item.type == "function_call":
args = json.loads(item.arguments)
result = get_weather(**args)
results.append({
"type": "function_call_output",
"call_id": item.call_id,
"output": json.dumps(result)
})
# Odeslat všechny výsledky najednou
final = client.responses.create(
model="gpt-4o",
input=[{"role": "user", "content": "Porovnej počasí v Praze, Brně a Ostravě."}]
+ [item for item in response.output]
+ results,
tools=tools
)
print(final.output_text)
Paralelní volání dramaticky snižuje latenci. Místo tří sekvenčních roundtripů model-aplikace-model provedete všechno v jednom cyklu. U real-time aplikací je to rozdíl, který vaši uživatelé opravdu poznají.
Implementace s Anthropic Claude API
Anthropic pro function calling používá termín tool use. Koncept je stejný, syntaxe se mírně liší. V roce 2026 jsou k dispozici modely Claude Opus 4.6, Claude Sonnet 4.5 a Claude Haiku 4.5 — všechny s plnou podporou tool use včetně structured outputs.
Základní příklad s Claude
import anthropic
import json
client = anthropic.Anthropic()
# Definice nástroje pro Claude
tools = [
{
"name": "get_stock_price",
"description": "Vrátí aktuální cenu akcie pro daný ticker symbol.",
"input_schema": {
"type": "object",
"properties": {
"ticker": {
"type": "string",
"description": "Ticker symbol akcie, např. AAPL, MSFT, GOOGL"
}
},
"required": ["ticker"]
}
}
]
def get_stock_price(ticker: str) -> dict:
# Simulace API volání
prices = {"AAPL": 245.30, "MSFT": 498.15, "GOOGL": 195.80}
return {"ticker": ticker, "price": prices.get(ticker, 0), "currency": "USD"}
# Krok 1: Odeslání promptu s nástroji
response = client.messages.create(
model="claude-sonnet-4-5-20250514",
max_tokens=1024,
tools=tools,
messages=[
{"role": "user", "content": "Jaká je aktuální cena akcií Apple?"}
]
)
# Krok 2: Zpracování — Claude vrací content blocks
tool_results = []
for block in response.content:
if block.type == "tool_use":
result = get_stock_price(**block.input)
tool_results.append({
"type": "tool_result",
"tool_use_id": block.id,
"content": json.dumps(result)
})
# Krok 3: Odeslání výsledků zpět
if tool_results:
final = client.messages.create(
model="claude-sonnet-4-5-20250514",
max_tokens=1024,
tools=tools,
messages=[
{"role": "user", "content": "Jaká je aktuální cena akcií Apple?"},
{"role": "assistant", "content": response.content},
{"role": "user", "content": tool_results}
]
)
# Extrakce textové odpovědi
for block in final.content:
if hasattr(block, "text"):
print(block.text)
Strict mode u Claude
Od roku 2026 Claude podporuje Structured Outputs s parametrem strict: true v definici nástrojů — stejně jako OpenAI. Garantuje to, že vstupy nástrojů budou přesně odpovídat vašemu JSON Schema:
tools = [
{
"name": "create_order",
"description": "Vytvoří novou objednávku v systému.",
"strict": True,
"input_schema": {
"type": "object",
"properties": {
"product_id": {"type": "integer", "description": "ID produktu"},
"quantity": {"type": "integer", "description": "Počet kusů"},
"shipping_address": {"type": "string", "description": "Doručovací adresa"}
},
"required": ["product_id", "quantity", "shipping_address"]
}
}
]
Se strict: true máte jistotu, že product_id bude vždy integer, nikdy string. V produkčních systémech tohle eliminuje celou kategorii bugů — a věřte mi, debugovat špatně typované parametry z LLM výstupu není žádná legrace.
Sekvenční volání a agentní smyčka
Tak, a teď to začne být opravdu zajímavé.
Skutečná síla function callingu se projeví v sekvenčním volání, kdy model volá funkce jednu po druhé a každý výsledek ovlivňuje další rozhodnutí. Tohle je jádro toho, jak fungují AI agenti — a pokud vás téma zajímá hlouběji, mrkněte na náš průvodce multi-agentními systémy.
Příklad: Agent pro analýzu portfolia
import anthropic
import json
client = anthropic.Anthropic()
# Definujeme více nástrojů
tools = [
{
"name": "get_portfolio",
"description": "Vrátí seznam akcií v portfoliu uživatele.",
"input_schema": {
"type": "object",
"properties": {
"user_id": {"type": "string", "description": "ID uživatele"}
},
"required": ["user_id"]
}
},
{
"name": "get_stock_price",
"description": "Vrátí aktuální cenu akcie.",
"input_schema": {
"type": "object",
"properties": {
"ticker": {"type": "string"}
},
"required": ["ticker"]
}
},
{
"name": "calculate_total_value",
"description": "Vypočítá celkovou hodnotu portfolia.",
"input_schema": {
"type": "object",
"properties": {
"holdings": {
"type": "array",
"items": {
"type": "object",
"properties": {
"ticker": {"type": "string"},
"shares": {"type": "number"},
"price": {"type": "number"}
}
}
}
},
"required": ["holdings"]
}
}
]
def run_agent(user_message: str):
"""Agentní smyčka — model volá funkce, dokud nemá finální odpověď."""
messages = [{"role": "user", "content": user_message}]
while True:
response = client.messages.create(
model="claude-sonnet-4-5-20250514",
max_tokens=4096,
tools=tools,
messages=messages
)
# Pokud model skončil (end_turn), vrátíme odpověď
if response.stop_reason == "end_turn":
for block in response.content:
if hasattr(block, "text"):
return block.text
# Pokud model chce volat nástroje
if response.stop_reason == "tool_use":
messages.append({"role": "assistant", "content": response.content})
tool_results = []
for block in response.content:
if block.type == "tool_use":
result = execute_tool(block.name, block.input)
tool_results.append({
"type": "tool_result",
"tool_use_id": block.id,
"content": json.dumps(result)
})
messages.append({"role": "user", "content": tool_results})
def execute_tool(name: str, args: dict) -> dict:
"""Dispatcher — směruje volání na správnou funkci."""
if name == "get_portfolio":
return {"holdings": [
{"ticker": "AAPL", "shares": 50},
{"ticker": "MSFT", "shares": 30}
]}
elif name == "get_stock_price":
prices = {"AAPL": 245.30, "MSFT": 498.15}
return {"ticker": args["ticker"], "price": prices.get(args["ticker"], 0)}
elif name == "calculate_total_value":
total = sum(h["shares"] * h["price"] for h in args["holdings"])
return {"total_value": total, "currency": "USD"}
return {"error": "Neznámý nástroj"}
# Spuštění
answer = run_agent("Jaká je celková hodnota mého portfolia? Moje ID je user_123.")
print(answer)
V tomhle příkladu model nejdřív zavolá get_portfolio, aby zjistil, jaké akcie uživatel drží. Pak pro každou akcii zavolá get_stock_price. Nakonec zavolá calculate_total_value se všemi daty. Tři kroky, automaticky orchestrované modelem — žádné manuální řízení workflow. Model sám ví, co má dělat dál, a to je na tom to krásné.
Function calling vs. MCP — kdy co použít
Pokud jste četli náš článek o Model Context Protocol (MCP), možná se ptáte — jaký je vlastně rozdíl? Jednoduše řečeno: function calling je mechanismus, MCP je protokol. MCP pod kapotou function calling používá.
Použijte function calling, když:
- Stavíte jednoduchou aplikaci s 2–5 vlastními funkcemi
- Nástroje se nemění za běhu — jsou pevně dané
- Nepotřebujete sdílet nástroje mezi různými AI klienty
- Chcete rychlý prototyp bez další infrastruktury
Použijte MCP, když:
- Potřebujete dynamické objevování nástrojů za běhu
- Chcete sdílet stejné nástroje mezi různými AI aplikacemi
- Stavíte enterprise řešení s desítkami až stovkami nástrojů
- Potřebujete standardizovanou autentizaci a autorizaci (OAuth 2.1)
V praxi se oba přístupy často kombinují. Vaše aplikace používá MCP pro připojení k externím serverům s nástroji — a tyto nástroje jsou modelu prezentovány jako function cally. Není to buď-anebo, je to vrstvená architektura. Tenhle bod je důležitý, protože se na to lidé často ptají.
Error handling a samoopravná smyčka
V produkci věci selhávají. API neodpovídá, model vygeneruje špatné parametry, funkce vyhodí výjimku. A tady se ukáže, jestli máte prototyp, nebo produkční systém.
Klasifikace chyb
- Přechodné chyby (timeout, rate limit) — řešíte automatickým retryem s exponenciálním backoffem
- Chyby parametrů (model poslal špatný typ, chybějící pole) — pošlete chybovou zprávu zpět modelu, ať se opraví
- Trvalé chyby (špatné credentials, neexistující resource) — vyžadují jiný přístup nebo eskalaci na uživatele
Samoopravná smyčka v praxi
def execute_tool_with_retry(name: str, args: dict, max_retries: int = 3) -> dict:
"""Provede nástroj s ošetřením chyb a retry logikou."""
for attempt in range(max_retries):
try:
result = execute_tool(name, args)
if "error" in result:
return result # Aplikační chyba — vrátíme modelu
return result
except TimeoutError:
if attempt < max_retries - 1:
import time
time.sleep(2 ** attempt) # Exponenciální backoff
continue
return {"error": f"Nástroj {name} neodpovídá po {max_retries} pokusech."}
except ValueError as e:
return {"error": f"Neplatné parametry: {str(e)}"}
except Exception as e:
return {"error": f"Neočekávaná chyba: {str(e)}"}
return {"error": "Maximální počet pokusů vyčerpán."}
Klíčový princip: vždy pošlete chybovou zprávu zpět modelu. Nepřerušujte smyčku. Model dokáže zpracovat chybu a opravit svůj přístup — třeba přeformulovat parametry nebo zvolit alternativní nástroj. Výzkumy ukazují, že tenhle samoopravný mechanismus zvyšuje úspěšnost agentních systémů o desítky procent. Je to vlastně podobné tomu, jak ladíte kód vy sami — zkusíte něco, nefunguje to, tak zkusíte jinak.
Best practices pro produkční nasazení
1. Pište kvalitní popisy nástrojů
Popis nástroje je vlastně prompt pro model. Čím přesnější a konkrétnější popis, tím spolehlivější volání. Místo „Získá data" napište „Vrátí historické ceny akcie za zadané období ve formátu pole objektů s poli date a price." Uvádějte příklady hodnot, očekávané formáty, omezení. Tahle investice do pár vět se vám mnohonásobně vrátí.
2. Používejte strict mode
U OpenAI i Claude nastavte strict: true. Structured Outputs garantují shodu se schématem a eliminují celou kategorii runtime chyb způsobených nevalidním JSON výstupem. Není důvod to nepoužívat.
3. Minimalizujte počet nástrojů
Začněte s 2–3 nástroji, které pokrývají hlavní use case. Každý přidaný nástroj zvyšuje spotřebu tokenů (definice se posílá s každým requestem) a zhoršuje přesnost výběru. Pokud potřebujete desítky nástrojů, zvažte MCP nebo techniku tool search.
4. Validujte vstupy na straně aplikace
I se strict mode validujte argumenty před provedením funkce. Kontrolujte rozsahy, oprávnění, existenci zdrojů. Model může vygenerovat syntakticky validní, ale sémanticky nesmyslné parametry — třeba korektní integer, ale záporný počet kusů. To se stává častěji, než byste čekali.
5. Logujte všechno
Zaznamenávejte každý function call — vstupní argumenty, výstupy, latenci, chyby. Tato data jsou neocenitelná pro ladění a optimalizaci. Nástroje jako Langfuse (open-source) nebo LangSmith nabízejí specializované tracing pro LLM aplikace a doporučuju je vyzkoušet hned od začátku projektu.
6. Implementujte lidský dohled
Pro akce s reálnými důsledky (odeslání e-mailu, vytvoření objednávky, smazání dat) vždy požadujte potvrzení od uživatele. OpenAI i Anthropic to explicitně doporučují ve své dokumentaci. Přidejte do svého workflow krok „human-in-the-loop" před provedením destruktivních operací. Lepší jedno kliknutí navíc než neoprávněně smazaná databáze.
7. Nastavte timeouty a rate limity
Každý external call by měl mít timeout. Nastavte maximální počet function callů na konverzaci (třeba 10), abyste zabránili nekonečným smyčkám. Implementujte rate limiting pro jednotlivé nástroje, zvlášť pokud volají placená API. Váš provozní rozpočet vám poděkuje.
Pokročilé vzory: ReAct a plánování
Function calling sám o sobě je mocný, ale v kombinaci s pokročilými prompting technikami — jako je ReAct (Reasoning + Acting) — se stává ještě silnějším. ReAct vzor nutí model, aby před každým voláním funkce nejdřív vysvětlil svůj záměr:
system_prompt = """Jsi analytický asistent. Při řešení úkolů vždy postupuj takto:
1. MYŠLENKA: Nejdřív vysvětli, co potřebuješ zjistit a proč.
2. AKCE: Zavolej příslušný nástroj.
3. POZOROVÁNÍ: Analyzuj výsledek.
4. Opakuj, dokud nemáš dostatek informací pro odpověď."""
Výzkumy konzistentně ukazují, že ReAct vzor snižuje chybovost function callingu o 15–30 % ve srovnání s přímým voláním bez reasoning kroku. Model líp vybírá správné nástroje a generuje přesnější parametry, protože si nejdřív „rozmyslí" svůj přístup. Dává to smysl — i lidé dělají míň chyb, když si věci promyslí, než se do nich pustí.
Ještě pokročilejší je vzor Plan-and-Execute: silnější model (např. Claude Opus) vytvoří plán kroků a levnější model (např. Claude Haiku) jednotlivé kroky provádí. Tím se dají snížit náklady až o 90 % oproti použití frontier modelu pro všechno — a přitom si zachováte kvalitu plánování. Tohle je mimochodem přístup, který v praxi používá čím dál víc produkčních agentních systémů.
Často kladené otázky (FAQ)
Jaký je rozdíl mezi function calling a tool use?
Žádný zásadní — jde o různé názvy pro stejný koncept. OpenAI historicky používá termín „function calling", Anthropic preferuje „tool use". Oba popisují schopnost LLM generovat strukturované instrukce pro volání externích funkcí. V komunitě se oba termíny používají zaměnitelně, takže se tím netrapte.
Podporují function calling i open-source modely?
Ano, a docela dobře. V roce 2026 podporují function calling mnohé open-source modely — Llama 3, Mistral, Qwen a další. Některé používají speciální tokeny pro značení function callů, jiné spoléhají na fine-tuning. Výkon se liší, ale pro jednodušší use case jsou plně použitelné. Knihovny jako LiteLLM poskytují jednotné rozhraní pro function calling napříč poskytovateli, což hodně usnadňuje přechod mezi modely.
Kolik nástrojů může model zpracovat najednou?
Technicky je limit daný velikostí kontextového okna — definice nástrojů zabírají tokeny. Prakticky OpenAI i Claude zvládají desítky nástrojů, ale s rostoucím počtem klesá přesnost výběru. Doporučuje se nepřesáhnout 15–20 nástrojů na jeden request. Pro větší počet použijte techniky jako tool search (Anthropic) nebo dynamické filtrování nástrojů podle kontextu.
Jak zabránit tomu, aby model halucinoval názvy funkcí?
Používejte strict: true (Structured Outputs) — model pak může volat pouze funkce definované ve schématu. Bez strict mode se občas stane, že model vygeneruje volání neexistující funkce. Dále pomáhá jasný a specifický popis každého nástroje a parametr tool_choice pro omezení výběru.
Je function calling bezpečný pro produkční nasazení?
Ano, pokud dodržujete bezpečnostní best practices: validace vstupů, princip nejmenších oprávnění (read-only tokeny kde to jde), human-in-the-loop pro destruktivní akce, sandboxování execution prostředí a důsledné logování. Nikdy nepouštějte funkce s neomezenými právy — model by neměl mít přístup k ničemu, co nepotřebuje. Bezpečnost je tady víc o architektuře vaší aplikace než o samotném LLM.