Anatomia definicji narzędzia

przez Łukasz | sie 24, 2026

Kiedy agent wybiera niewłaściwe narzędzie, pierwszym odruchem jest poprawianie promptu systemowego. Drugim — zmiana modelu na mocniejszy.

Zwykle problem leży gdzie indziej i naprawia się go bez dotykania jednego i drugiego.

Model nie widzi kodu

To warto powiedzieć wprost, bo bywa zaskoczeniem. Model nie ma dostępu do implementacji narzędzia. Nie wie, co funkcja robi, jakie ma zabezpieczenia ani skąd bierze dane.

Widzi wyłącznie definicję: nazwę, opis słowny, schemat parametrów i informację, które pola są wymagane. Kilkadziesiąt słów. Na tej podstawie decyduje, czy sięgnąć po to narzędzie i co wpisać w argumenty.

Ten opis jest promptem. Podlega dokładnie tym samym prawom co prompt systemowy — wpływa na rozkład prawdopodobieństwa, konkuruje o uwagę z resztą kontekstu i wymaga testowania. Różnica polega na tym, że prawie nikt go tak nie traktuje.

Do tego jest promptem kosztownym: cały katalog narzędzi jedzie do modelu przy każdym obrocie pętli. Przy dziesięciu krokach dziesięć razy.

Nazwa

Nazwa niesie więcej informacji, niż się wydaje, bo jest tym, co model widzi jako pierwsze i najkrócej.

Zła nazwa odzwierciedla wewnętrzną strukturę systemu: get_data, api_call_v2, crm_query. Dobra mówi, co narzędzie robi z punktu widzenia zadania: sprawdz_status_zamowienia, wyslij_potwierdzenie_platnosci.

Zasada, która porządkuje resztę: nazwa powinna wystarczyć do wyboru w typowym przypadku. Opis służy do rozstrzygania przypadków granicznych, nie do tłumaczenia, co właściwie oznacza nazwa.

Opis: powiedz też, kiedy nie

Większość opisów mówi wyłącznie, co narzędzie robi. Przy jednym narzędziu to wystarcza. Przy kilku podobnych zaczyna się problem, bo model wybiera to, którego opis najbardziej przypomina zapytanie — a dwa zbliżone opisy realnie ze sobą konkurują.

Rozwiązaniem jest dopisanie granicy negatywnej: kiedy tego narzędzia nie używać i co użyć zamiast.

Zamiast „wyszukuje zamówienia klienta” lepiej: „wyszukuje zamówienia po numerze klienta. Nie używaj do sprawdzania statusu pojedynczego zamówienia — do tego służy status_zamowienia. Nie używaj do wyszukiwania po adresie e-mail.”

Trzy zdania zamiast jednego, ale rozstrzygają dokładnie te przypadki, w których model się myli. To najtańsza poprawa trafności, jaka istnieje w całym stosie.

Schemat parametrów

Opisy pól są pomijane najczęściej i to one generują najwięcej błędnych argumentów.

Format, jeśli nie jest oczywisty. Data w formacie RRRR-MM-DD. Numer zamówienia z prefiksem ZAM-. Kwota w groszach, nie w złotych. Model nie zgadnie konwencji twojego systemu — a zgadywanie kończy się argumentem, który wygląda poprawnie i nie działa.

Wyliczenie zamiast prozy. Jeśli pole przyjmuje jedną z pięciu wartości, wpisz je w schemat jako listę dopuszczalnych, a nie w opis. Model wtedy nie ma z czego wybrać źle, a walidacja dzieje się przed wykonaniem.

Pola wymagane naprawdę wymagane. Oznaczenie pola jako opcjonalnego, gdy bez niego narzędzie nie działa, to zaproszenie do wywołania bez niego.

Schemat jest przy tym pierwszą linią walidacji — wyłapuje halucynację strukturalną, zanim cokolwiek się wykona. Im więcej powiesz w schemacie, tym mniej trzeba sprawdzać ręcznie.

Wynik też jest promptem

Definicja opisuje wejście. Ale to, co narzędzie zwraca, wraca do kontekstu i staje się materiałem dla kolejnej decyzji modelu.

Widzieliśmy to w poprzednim artykule: puste [] zamiast komunikatu wystarczyło, żeby agent wpadł w zapętlenie. Ta sama zasada dotyczy wyników udanych — struktura i objętość odpowiedzi decydują o tym, czy model ją wykorzysta i ile zapłacisz za wysyłanie jej przy każdym kolejnym obrocie. Więcej o samym formatowaniu jest w artykule z serii o context engineeringu.

Definicja jako powierzchnia ataku

Skoro opis narzędzia jest instrukcją dla modelu, to opis napisany przez kogoś innego jest cudzą instrukcją w twoim kontekście.

Przy narzędziach własnych to nie problem. Przy serwerach MCP osób trzecich — jak najbardziej. Opis pobierany z zewnętrznego serwera trafia do kontekstu z tym samym statusem co twój prompt systemowy i może zawierać polecenia skierowane do modelu.

Dochodzi do tego wariant trudniejszy do wykrycia: opis zmieniony po zatwierdzeniu. Użytkownik przejrzał narzędzie przy podłączeniu, uznał za bezpieczne, a serwer podmienił opis przy kolejnym odpytaniu katalogu.

Wniosek praktyczny: katalog narzędzi z zewnętrznych źródeł warto przypinać do znanej wersji i porównywać przy każdym pobraniu, a nie zakładać, że skoro raz był w porządku, to jest nadal.

Testuj definicje jak prompty

Skoro to prompt, podlega tej samej dyscyplinie: zestaw realnych zapytań, zapisany oczekiwany wybór narzędzia, pomiar trafności przed zmianą i po niej.

Kilkanaście przypadków wystarczy, żeby zobaczyć, czy poprawka opisu pomogła, czy tylko wydawała się lepsza. To jest robota na godzinę, a zwraca się przy każdej kolejnej zmianie katalogu.

Jak to sprawdzić u siebie

Poniższy kod porównuje dwie wersje definicji tych samych narzędzi — najpierw ogólne, potem z granicami negatywnymi i opisami pól — i mierzy trafność wyboru na zestawie zapytań.

python
import json
from openai import OpenAI

client = OpenAI()

def narzedzia(precyzyjne):
    if not precyzyjne:
        return [
            _f("szukaj_zamowien", "Wyszukuje zamówienia klienta",
               {"numer_klienta": {"type": "string"}}),
            _f("status_zamowienia", "Zwraca informacje o zamówieniu",
               {"numer_zamowienia": {"type": "string"}}),
        ]
    return [
        _f("szukaj_zamowien",
           "Zwraca LISTĘ zamówień danego klienta. Używaj, gdy pytanie dotyczy "
           "klienta i wielu zamówień. NIE używaj, gdy znany jest numer "
           "konkretnego zamówienia — wtedy użyj status_zamowienia.",
           {"numer_klienta": {"type": "string",
                              "description": "Numer klienta, format KL-0000"}}),
        _f("status_zamowienia",
           "Zwraca status JEDNEGO zamówienia o znanym numerze. NIE używaj do "
           "wyszukiwania zamówień klienta.",
           {"numer_zamowienia": {"type": "string",
                                 "description": "Numer zamówienia, format ZAM-0000"}}),
    ]

def _f(nazwa, opis, pola):
    return {"type": "function", "function": {
        "name": nazwa, "description": opis,
        "parameters": {"type": "object", "properties": pola,
                       "required": list(pola)}}}

przypadki = [
    ("Gdzie jest zamówienie ZAM-4471?",                "status_zamowienia"),
    ("Co zamawiał klient KL-0012 w tym roku?",         "szukaj_zamowien"),
    ("Czy ZAM-9001 zostało już wysłane?",              "status_zamowienia"),
    ("Pokaż wszystkie zamówienia klienta KL-0300.",    "szukaj_zamowien"),
    ("Klient KL-0012 pyta o swoje zakupy.",            "szukaj_zamowien"),
    ("Sprawdź ZAM-1234.",                              "status_zamowienia"),
]

for precyzyjne in (False, True):
    trafienia = 0
    for pytanie, oczekiwane in przypadki:
        odp = client.chat.completions.create(
            model="gpt-4o-mini",
            messages=[{"role": "user", "content": pytanie}],
            tools=narzedzia(precyzyjne),
        ).choices[0].message
        wybrane = odp.tool_calls[0].function.name if odp.tool_calls else "brak"
        trafienia += (wybrane == oczekiwane)
    etykieta = "precyzyjne" if precyzyjne else "ogólne"
    print(f"{etykieta:11} {trafienia}/{len(przypadki)}")

Nie zmieniamy tu ani modelu, ani promptu, ani jednej linii kodu wykonawczego. Zmieniamy wyłącznie tekst opisu — i to zwykle wystarcza, żeby przypadki graniczne przestały być zgadywaniem.

Drugie ćwiczenie, jeśli chcesz zobaczyć drugą stronę: dopisz do wariantu precyzyjnego opis pola z formatem, a potem zadaj pytanie, w którym numer podany jest bez prefiksu — na przykład „sprawdź 4471″. Zobaczysz, czy model uzupełni format sam, czy dopyta.

Podstaw własne narzędzia i własne zapytania z produkcji. Sześć przypadków wystarczy na demonstrację, do decyzji weź kilkanaście.

Co dalej

Wiemy, jak opisać jedno narzędzie. Zostaje pytanie, ile ich może być naraz — bo każde kolejne konkuruje o uwagę modelu z wszystkimi pozostałymi i zajmuje miejsce w kontekście przy każdym obrocie pętli.

O tym jest artykuł piąty: Ile narzędzi to za dużo.

Stan poza kontekstem

Stan poza kontekstem

W pierwszym artykule padło zdanie, że agent nie kontynuuje pracy — zaczyna od zera z coraz dłuższą notatką. Notatką jest kontekst, a jego rosnąca długość to główny składnik rachunku. Pytanie, które z tego wynika, brzmi: co właściwie musi być w tej notatce, a co...

Kiedy narzędzie zawodzi

Kiedy narzędzie zawodzi

Wszystkie dotychczasowe przykłady zakładały, że narzędzie działa. W produkcji to założenie jest fałszywe częściej, niż się zakłada przy projektowaniu: API mają awarie, limity, przeciążenia i czasy oczekiwania. To, co pętla zrobi w takim momencie — i co powie o tym...

Ile narzędzi to za dużo

Ile narzędzi to za dużo

Dodanie narzędzia wygląda na czynność addytywną. Agent umie o jedną rzecz więcej, reszta zostaje jak była. Nie zostaje. Każde kolejne narzędzie pogarsza wybór wszystkich pozostałych i podnosi cenę każdego obrotu pętli — a oba efekty są niewidoczne, dopóki ktoś ich nie...

Agent wyzwalany kontra agent długo żyjący

Agent wyzwalany kontra agent długo żyjący

Dwa poprzednie artykuły opisywały pętlę, która dostaje zadanie i ma je skończyć. Warunek zakończenia dotyczył całego agenta, budżet liczyliśmy na zadanie, a na wynik ktoś czekał. Istnieje druga kategoria, w której żadne z tych założeń nie obowiązuje. Dwa rytmy Agent...