Evaluace function calling: jak testovat spolehlivost tool use u LLM (2026)

Praktický průvodce evaluací function calling u LLM v roce 2026: metriky, datasety, deterministické asserty, LLM-as-judge a CI/CD pipeline s DeepEvalem, Promptfoo a Langfuse.

Function Calling Eval: Testy LLM (2026)

Aktualizováno: 13. září 2026

Evaluace function calling znamená měřit, jak spolehlivě LLM vybírá správný nástroj, generuje validní argumenty podle JSON Schema a dochází ke správnému výsledku napříč více kroky. V praxi to není jedna metrika, ale kombinace deterministických testů (schema, název nástroje, hodnoty argumentů) a hodnocení celé trajektorie. U multi-tool úloh se hodí LLM-as-judge nebo end-state kontrola stavu prostředí. Tenhle průvodce ukazuje sadu metrik, datasetů a frameworků, které v roce 2026 dávají smysl pro produkční nasazení, a jak je zapojit do CI/CD.

  • Function calling má tři úrovně chyb: špatný nástroj, špatné argumenty a špatná trajektorie. Každou z nich se dá měřit jinou metrikou.
  • Deterministické testy (schema validation, exact-match argumentů, tool selection accuracy) běží rychle a chytí zhruba 70 % regresí; LLM-as-judge doplňuje zbytek.
  • Berkeley Function Calling Leaderboard (BFCL v3) a τ-bench jsou v roce 2026 referenční benchmarky, ale vlastní doménový dataset (100–300 příkladů) je pro produkci nezbytný.
  • Frameworky jako DeepEval, Promptfoo, Braintrust a Ragas mají odlišné silné stránky: pro CI/CD volte Promptfoo, pro assertion-heavy testy DeepEval.
  • Produkční monitoring přes Langfuse nebo OpenTelemetry umožňuje samplovat živý provoz a průběžně detekovat drift v tool selection.

Proč evaluovat function calling samostatně

Ve své praxi jsem si zvykl dělit LLM úlohy na tři skupiny: generativní text, strukturované výstupy a tool use. U tool use platí, že standardní eval na kvalitu textu (BLEU, ROUGE, LLM-judge nad odpovědí) je téměř bezcenný. Model může vrátit krásně formulovanou větu a přesto zavolat špatný nástroj, poslat mu argument v nesprávné jednotce nebo úplně přeskočit vyžadovaný krok. Právě proto potřebuje function calling vlastní testovací pyramidu.

Druhý důvod je regresní citlivost. Když v roce 2026 povýšíte z Claude Sonnet 4.5 na Sonnet 5, upgradujete z gpt-4o-2024-08-06 na novější snapshot nebo změníte popis nástroje v tools[], dopad se projeví právě v tool selection accuracy a v distribuci argumentů. Bez záchytné sítě testů si toho všimnete až v produkci (typicky když se v Langfuse trace objeví neplatné argumenty a downstream API vrátí 4xx). Přesně tohle mě v jednom projektu zabolelo, když jsme přepnuli model o víkendu a v pondělí nám support hlásil zvýšenou chybovost.

Třetí důvod je čistě ekonomický. Podle mých benchmarků chyby v tool selection způsobují 3–8× více vstupních tokenů, protože agent musí volání opakovat s korekcí. U vysokoprovozových agentů to reálně znamená tisíce dolarů měsíčně a několik dodatečných sekund latence na uživatele.

Jaké metriky používat pro function calling?

Metriky rozděluji do čtyř hladin, které odpovídají tomu, kde v pipeline model selže. Bez tohoto rozdělení skončíte s jednou „pass rate“ hodnotou, která vám neřekne, kde investovat čas.

  1. Tool Selection Accuracy zavolal model vůbec ten správný nástroj? Klasifikační metrika (accuracy, macro-F1 přes seznam nástrojů). Pro agenty s ≥ 10 nástroji sleduji navíc confusion matrix, typicky se objeví záměny mezi sémanticky podobnými nástroji (search_orders vs. list_orders).
  2. Argument Validity prošel payload validací proti JSON Schema? Binární metrika. Většina moderních SDK (OpenAI structured outputs, Anthropic tool_choice, Gemini function_calling) garantuje syntaktickou validitu, ale sémantická validita (např. datum ve správném formátu, ID existuje) zůstává na vás.
  3. Argument Correctness jsou hodnoty argumentů správné vůči očekávané ground truth? Zde rozlišuji exact-match (pro enumy, ID, boolean flagy) a soft-match (pro volnější stringy, např. rewritten search query se hodnotí přes cosine similarity nebo LLM-judge).
  4. Trajectory Success u multi-step agentů: dosáhl model požadovaného koncového stavu? Buď měříte end-state (změny v mock DB, výsledek API), nebo hodnotíte sekvenci volání LLM-judgem.

Doplňkově sleduji latenci per tool call, počet iterací do dokončení a token cost per successful trajectory. Ty samy o sobě nejsou kvalitativní metrika, ale odhalují regrese, kterých si accuracy nevšimne. Typický scénář: nová verze modelu volá „správný“ nástroj, jenže s dvojnásobkem kroků.

Jak sestavit dataset pro evaluaci tool use

Nemá cenu evaluovat proti Berkeley Function Calling Leaderboardu (BFCL v3) a považovat to za hotovo. BFCL je skvělá referenční půda pro srovnání modelů, ale nepokrývá vaše vlastní schema, doménovou terminologii ani reálné edge case, které dostanete od zákazníků. Doporučuji hybridní přístup ve třech vrstvách:

  • Golden set (50–150 příkladů, ručně kurátorský): reprezentativní scénáře, které musí vždycky projít. Sem patří regrese, které vás v minulosti bolely, ukázkové happy-path a klíčové edge case (chybějící argument, ambiguity, out-of-scope dotaz). Golden set se aktualizuje pomalu.
  • Auto-generated set (500–2000 příkladů): generováno silnějším modelem (např. Claude Opus 5) z popisů nástrojů a schema. Chytá pokrytí, rychle odhalí, na kterých kombinacích nástrojů model selhává.
  • Production replay (samplované z Langfuse/tracing): reálné konverzace, anonymizované a označkované. Neocenitelné pro drift monitoring, ale bez ground truth. Zde funguje jen LLM-as-judge nebo human review.
from pydantic import BaseModel
from typing import Literal, Any

class ToolCallExample(BaseModel):
    id: str
    user_message: str
    context: list[dict] = []  # předchozí konverzace, pokud existuje
    expected_tool: str | None  # None = model NEMÁ volat nástroj
    expected_args: dict[str, Any] | None
    match_mode: dict[str, Literal["exact", "soft", "ignore"]]
    tags: list[str] = []  # ["happy-path", "ambiguous", "regression-2026-08"]

# Příklad z golden setu
example = ToolCallExample(
    id="orders-001",
    user_message="Ukaž objednávky Nováka z poslední dva týdny",
    expected_tool="search_orders",
    expected_args={
        "customer_query": "Novák",
        "date_from": "2026-08-30",
        "date_to": "2026-09-13",
    },
    match_mode={
        "customer_query": "soft",     # akceptuj "Novak" i "novák"
        "date_from": "exact",
        "date_to": "exact",
    },
    tags=["happy-path", "cs-diacritics"],
)

V týmu tenhle seznam držíme jako YAML/JSONL v repu vedle kódu agenta. Testovací data mají být code-reviewed stejně jako produkční kód. Pro doménová schema, kde je hodně strukturovaných argumentů, doporučuji podívat se na strukturované výstupy v Pythonu s Instructorem a Pydanticem. Stejné validační vzory přenesete i sem.

Deterministické testy: schema, argumenty, tool selection

Deterministické asserty jsou levné, rychlé a chytí drtivou většinu regresí. Následující runner je záměrně minimalistický. V produkci ho pak zabalíte do DeepEvalu nebo pytestu, ale logika zůstává.

import json
from anthropic import Anthropic

client = Anthropic()

TOOLS = [
    {
        "name": "search_orders",
        "description": "Vyhledá objednávky podle jména zákazníka a časového rozsahu.",
        "input_schema": {
            "type": "object",
            "properties": {
                "customer_query": {"type": "string"},
                "date_from": {"type": "string", "format": "date"},
                "date_to": {"type": "string", "format": "date"},
            },
            "required": ["customer_query", "date_from", "date_to"],
        },
    },
]

def run_case(example: ToolCallExample) -> dict:
    resp = client.messages.create(
        model="claude-sonnet-5",
        max_tokens=512,
        tools=TOOLS,
        messages=[{"role": "user", "content": example.user_message}],
    )
    tool_use = next(
        (b for b in resp.content if b.type == "tool_use"), None
    )

    result = {"id": example.id, "selected_tool": None, "checks": {}}
    if tool_use is None:
        result["selected_tool"] = None
        result["checks"]["tool_selection"] = example.expected_tool is None
        return result

    result["selected_tool"] = tool_use.name
    result["checks"]["tool_selection"] = tool_use.name == example.expected_tool
    if not result["checks"]["tool_selection"]:
        return result  # další asserty nemají smysl

    for key, mode in example.match_mode.items():
        expected = example.expected_args.get(key)
        actual = tool_use.input.get(key)
        if mode == "exact":
            result["checks"][f"arg::{key}"] = expected == actual
        elif mode == "soft":
            result["checks"][f"arg::{key}"] = _soft_equal(expected, actual)
        # "ignore" se přeskočí
    return result

def _soft_equal(a: str, b: str) -> bool:
    return a.strip().lower() == b.strip().lower()

Klíčové poučení z produkce: když selže tool_selection, zbytek assertů raději přeskočte. Jinak vám metriky opticky nadhodnotí chybovost argumentů, protože model počítal argumenty pro jiný nástroj. Já ještě navíc počítám tři agregáty: selection accuracy, strict pass rate (všechno musí projít) a args accuracy pouze na podmnožině, kde selection prošel.

LLM-as-judge pro trajektorie a multi-step úlohy

Deterministické asserty přestávají stačit ve chvíli, kdy má agent možnost jít k cíli více cestami. Klasický příklad je zákaznická podpora: model může nejdřív volat get_customer_id a pak list_orders, nebo rovnou search_orders s jménem. Obě trajektorie jsou správně, ale hard-coded golden trajectory by jednu z nich označila jako fail.

Pro tyhle případy funguje trajectory judge, LLM (typicky silnější než hodnocený model, u mě obvykle Claude Opus 5) dostane popis úlohy, popis nástrojů, sekvenci volání a koncový stav, a vrátí strukturované hodnocení. Klíčové je, aby judge vracel rubric-based ohodnocení, ne jen „pass/fail“.

from pydantic import BaseModel, Field

class TrajectoryVerdict(BaseModel):
    reached_goal: bool
    unnecessary_calls: int = Field(ge=0, description="Počet redundantních tool calls")
    dangerous_calls: int = Field(ge=0, description="Volání s vedlejšími efekty, která neměla být provedena")
    reasoning: str

JUDGE_PROMPT = """Jsi evaluátor AI agentů. Posuď následující trajektorii.

ÚLOHA:
{task}

DOSTUPNÉ NÁSTROJE:
{tools}

TRAJEKTORIE (nástroj + argumenty + výsledek):
{trajectory}

KONCOVÝ STAV PROSTŘEDÍ:
{end_state}

Vrať JSON podle schema TrajectoryVerdict. Buď přísný, ale spravedlivý.
Alternativní cesty ke stejnému cíli jsou v pořádku, pokud jsou efektivní."""

Dvě věci, které jsem se naučil tvrdě: (1) judge musí dostat i popis nástrojů, jinak halucinuje, co je „správné“ volání; (2) judge sám musí projít kalibrací. Ručně označte zhruba 50 trajektorií a spočítejte Cohenovu κ mezi judgem a člověkem. Pokud κ < 0.6, judge není důvěryhodný a musíte upravit prompt nebo model. Detailně jsem tuhle metodiku rozebral v článku o testování a evaluaci LLM aplikací s DeepEvalem, kde jsou i konkrétní kalibrační kroky.

Pro pokročilejší multi-turn agenty s uživatelskou interakcí je referenčním benchmarkem τ-bench od Sierra Research, který simuluje real-world konverzace v maloobchodu a leteckých rezervacích. Stojí za to inspirovat se jeho evaluačním protokolem, i když ho nespouštíte přímo.

Frameworky: DeepEval, Promptfoo, Braintrust, Ragas

V roce 2026 se ekosystém trochu ustálil. Následující tabulka shrnuje, na co který framework používám:

FrameworkSilná stránkaCI/CD integraceVhodné proSlabá stránka
DeepEvalPytest-native, bohatá knihovna assertůVýborná (pytest --deepeval)Assertion-heavy suity, unit-style evalTrajectory eval je manuálnější
PromptfooYAML konfigurace, matrix testing přes modelyVýborná (CLI, GH Action)Model comparison, red team, prompt regreseCustom Python asserty jsou přes JS bridge nešikovné
BraintrustUI pro diff mezi runy, hosted experiment trackingDobrá (SDK)Tým s data-scientist rolí, iterace promptůPlacené, méně kontroly nad daty
RagasBohatá knihovna RAG metrik, teď i agent metrikyPrůměrnáRAG-first agenty, kombinace retrieval + tool useAgent část je mladší, méně cookbooků

Moje default kombinace pro nový projekt: DeepEval pro deterministické asserty + Promptfoo pro matrix comparison napříč modely + Langfuse pro produkční tracing. Braintrust přidávám, když v týmu potřebujeme sdílené experiment tracking UI.

Regresní testování function calling v CI/CD

Ideální stav: každý PR, který se dotkne promptů, tool definic nebo verze modelu, spustí evaluaci a v GitHub checku ukáže rozdíl proti mainu. Toho dosáhnete kombinací pytestu (DeepEval) a Promptfoo přes GitHub Actions.

# .github/workflows/eval-function-calling.yml
name: Eval function calling
on:
  pull_request:
    paths:
      - "src/agents/**"
      - "src/tools/**"
      - "prompts/**"
      - "eval/**"

jobs:
  eval:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-python@v5
        with:
          python-version: "3.12"
      - run: pip install -r requirements-eval.txt
      - name: Run deterministic asserts
        env:
          ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
          OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
        run: pytest eval/ -q --junitxml=results.xml
      - name: Run Promptfoo matrix
        run: npx [email protected] eval -c eval/promptfoo.yaml --output eval/out.json
      - name: Comment diff on PR
        uses: actions/github-script@v7
        with:
          script: |
            const fs = require("fs");
            const summary = fs.readFileSync("eval/out.json", "utf8");
            // ...vytvoří PR comment s tool_selection_accuracy delta

Doporučené prahové hodnoty (na golden setu): tool_selection_accuracy ≥ 0.95, strict_pass_rate ≥ 0.85, args_accuracy ≥ 0.90. Pokud PR některou hodnotu shodí o víc než 2 procentní body, check padá a v komentáři je konkrétní seznam příkladů, které selhaly. Tenhle setup mi ušetřil několik regresí, které by jinak prošly do produkce. Typicky když někdo změnil popis nástroje „na lepší“ a rozbil tool selection na 6 % dotazů.

Jak monitorovat function calling v produkci?

CI/CD chytí regrese před merge, ale produkce má vždy odlišnou distribuci vstupů. Proto potřebujete druhou obrannou linii, online evaluaci. V produkci nemáte ground truth, takže se opíráte o tři signály:

  1. Schema/API errors: každé zamítnutí downstream API (400/422) je proxy pro sémantickou nevalidnost. Tagujte trace v Langfuse a alertujte, když tool_call_error_rate překročí 1 % za 5 min.
  2. LLM-as-judge sampling: náhodných 1–5 % trajektorií hodnoťte judgem každou hodinu. Sledujte trend reached_goal a unnecessary_calls.
  3. User feedback: negativní 👎 nebo eskalace na člověka je nejsilnější signál. Automaticky replayujte tyhle trace do golden setu (po anonymizaci).

Pro implementaci tracing vrstvy doporučuji projít průvodce observabilitou LLM aplikací s Langfuse. Langfuse od verze 3.x nativně podporuje tool_calls jako first-class span, takže judge job může spouštět SQL query nad trace daty a nemusí parsovat logy.

Časté chyby a antipatterny

  • Jedna „pass rate“ metrika: Rozbíjejte metriky podle tool selection / argumentů / trajektorie. Agregátní číslo skrývá, co je špatně.
  • Judge stejného modelu jako testovaný agent: Judge trpí self-preference bias. Používejte silnější a ideálně jiný model (např. Claude Opus 5 hodnotí GPT-5-mini agenta).
  • Nedeterministický judge bez kalibrace: Vždy měřte κ vůči lidským anotacím na 50+ příkladech. Kalibraci opakujte při každé změně judge promptu.
  • Chybějící negativní příklady: Model, který volá nástroj i na „kolik je hodin?“, projde vaším testem, pokud tam není nic, co má vrátit „žádný nástroj“.
  • Evaluace pouze na happy path: Do golden setu patří i unicode, dlouhé stringy, chybějící pole, ambiguity. Podle oficiálních doporučení OpenAI je 30–40 % edge case v datasetu zdravé minimum.
  • Ignorování cost/latency metrik: Model, který dosáhne cíle za 12 tool call místo 3, je regres, i když „projde“. Sledujte tokens_per_success.

Často kladené otázky

Jak často mám spouštět eval suitu?

Deterministické asserty na golden setu (50–150 příkladů) na každém PR, trvá to typicky 2–5 minut. Full matrix eval napříč modely 1× denně nebo při každém release candidate. Judge-based trajectory eval na sample 1–5 % produkčního trafficu kontinuálně.

Jaký model použít jako LLM judge?

Silnější a ideálně z jiné rodiny než hodnocený model, abyste minimalizovali self-preference bias. V roce 2026 se osvědčila kombinace Claude Opus 5 nebo GPT-5 jako judge nad menšími produkčními modely. Vždy ho ale kalibrujte proti 50+ lidským anotacím.

Stačí Berkeley Function Calling Leaderboard místo vlastního datasetu?

Ne. BFCL v3 je skvělý referenční benchmark pro srovnání modelů, ale používá generická schema, která nepokrývají vaši doménu. Kombinujte BFCL pro selection modelu s doménovým golden setem 100–300 příkladů pro produkční eval.

Jak testovat, když se agent má rozhodnout nevolat žádný nástroj?

Přidejte do datasetu explicitní negativní příklady s expected_tool = None (small talk, out-of-scope dotazy, uzavírací fráze). U nich testujte, že model buď odpoví textově, nebo požádá o upřesnění. Podíl negativních příkladů by měl odpovídat produkční distribuci, u zákaznických botů typicky 15–25 %.

Co dělat, když model volá správný nástroj, ale s nepatrně jinými argumenty?

Rozdělte argumenty do tří match módů: exact pro enumy, ID a boolean flagy, soft pro volné stringy (case-insensitive porovnání nebo cosine similarity) a ignore pro pole, která pipeline stejně přepíše. Slepá exact-match kontrola nad všemi argumenty produkuje falešná selhání a šum v metrikách.

Daichi Watanabe
O Autorovi Daichi Watanabe

LLM integration specialist with a strong opinion about function calling and an even stronger one about evaluations.