Skip to content
← Wszystkie wpisy
7 min czytania Dawid Skłodowski

Budowanie niezawodnych narzędzi dla LLM

Function calling, agenci i MCP wszystkie opierają się na tym samym: narzędziach, które LLM może wywołać. Ale narzędzie zbudowane dla niedeterministycznego, omylnego wołającego potrzebuje innego projektu niż zbudowane dla kodu. Deep-dive o tym, co czyni narzędzie niezawodnym dla LLM — nazewnictwo, zakres, walidacja, komunikaty błędów, z których może się odzyskać, i bezpieczeństwo, które nie może żyć w modelu.

Przez cały ten łuk pisania o LLM — function calling, agenci, MCP — jedna rzecz wciąż siedzi pod spodem: narzędzia. Funkcja, którą model może wywołać, by coś sprawdzić albo coś zrobić, to jednostka, która zamienia model umiejący tylko mówić w taki, który umie działać, a każdy wyższy wzorzec (agenci, systemy wieloagentowe, serwery MCP) jest ostatecznie zbudowany z narzędzi. Co podnosi pytanie, którym chcę zamknąć, bo to fundament, na którym stoi wszystko inne: co czyni narzędzie niezawodnym dla LLM do użycia? Odpowiedź nie jest ta sama co dla narzędzia zbudowanego dla kodu, bo wołający jest inny — narzędzie dla LLM to interfejs zaprojektowany dla niedeterministycznego, omylnego, dobrze-intencjonowanego-ale-niedoskonałego konsumenta, a to zmienia, jak je projektujesz. To deep-dive o budowaniu narzędzi, których twój model może faktycznie dobrze używać.

Wołający to różnica

Zacznij od rzeczy, która czyni to własną dyscypliną. Gdy piszesz funkcję, by kod ją wołał, wołający jest deterministyczny: przekazuje dokładnie argumenty, które określiłeś, we właściwych typach, i obsługuje twoją wartość zwrotną jak zaprogramowano. Gdy piszesz narzędzie, by LLM je wołał, wołający jest probabilistycznym modelem, który interpretuje twoje narzędzie z jego opisu, decyduje, kiedy go użyć, i dostarcza argumenty, które wyłuskał z języka naturalnego. Ten wołający może źle zrozumieć, co narzędzie robi, zawołać je w złym czasie, przekazać złe albo źle uformowane argumenty i źle odczytać twoją odpowiedź. Projektowanie dla tego wołającego — omylnego, niedeterministycznego, rozumującego z twojego opisu — to to, co oddziela narzędzie, którego LLM używa dobrze, od takiego, które ciągle nadużywa. Każda zasada poniżej wynika z potraktowania tego wołającego poważnie.

Uczyń je zrozumiałym: nazewnictwo i opis

Ponieważ model decyduje, czy i jak użyć narzędzia, z jego opisu, opis nie jest dokumentacją — to interfejs, i to rzecz o najwyższej dźwigni, którą kontrolujesz. To prompt engineering wycelowany w twoje definicje narzędzi:

  • Nazwij narzędzie jasno i konkretnie. Precyzyjna, opisowa nazwa (get_order_status, nie lookup) mówi modelowi, do czego narzędzie służy i kiedy po nie sięgnąć. Mgliste nazwy są sięgane mgliście.
  • Opisz, co robi, kiedy go użyć i co zwraca. Opis powinien uczynić wyraźnym dla modelu cel narzędzia, sytuacje, do których służy, i co wraca. Model, który nadużywa narzędzia, to bardzo często model, któremu dano kiepski opis go — napraw opis, zanim obwinisz model.
  • Opisz każdy parametr precyzyjnie. Czym jest, jego typ, jego ograniczenia, co jest wymagane. Model wyłuskuje je z języka naturalnego, więc im jaśniej je określisz, tym niezawodniej je dobrze dostaje.

Pojedynczo najbardziej wpływowym usprawnieniem, które zwykle możesz zrobić dla użycia narzędzi przez LLM, jest poprawienie jasności opisów narzędzi. Model rozumuje ze słów; daj mu dobre.

Uczyń je dobrze-zakresowym: wąskie i jednocelowe

Niezawodne narzędzie robi jedną dobrze-zdefiniowaną rzecz. To ma większe znaczenie dla wołającego LLM niż wołającego kodu, bo model wybierający spośród narzędzi wybiera znacznie dokładniej, gdy każde narzędzie ma jasny, odrębny cel. Wskazówka projektowa:

  • Wolej wąskie, jednocelowe narzędzia nad szerokie, wielotrybowe. Narzędzie, które robi jedną rzecz, jest łatwiejsze dla modelu do zrozumienia, do poprawnego wyboru i do zawołania z właściwymi argumentami. Narzędzie scyzoryk z tuzinem trybów to coś, czego model użyje w złym trybie.
  • Unikaj nakładających się narzędzi. Jeśli dwa narzędzia mogłyby wiarygodnie zrobić tę samą pracę, model musi zgadnąć, które miałeś na myśli, i czasem zgadnie źle. Odrębne, nienakładające się cele czynią wybór jednoznacznym.
  • Trzymaj powierzchnię argumentów małą i jasną. Mniej parametrów, każdy z jasnym znaczeniem, znaczy mniej szans, by model dostarczył coś złego. Złożoność w sygnaturze to złożoność, którą model musi dobrze zrobić za każdym razem.

Wąskie, jednocelowe, nienakładające się narzędzia są dla LLM tym, czym czyste API dla dewelopera — ale bardziej, bo model nie może przeczytać twojego źródła, by ujednoznacznić. Jasność musi być w kształcie samego narzędzia.

Uczyń je bezpiecznym: waliduj, bo argumenty są niezaufane

Oto zasada, której nie można skompromitować, i to ta, do której cała ta seria wciąż wraca: argumenty, które model dostarcza, to niezaufane wejście, a samo narzędzie musi egzekwować bezpieczeństwo. Model wyłuskał te argumenty z języka naturalnego; może je pomylić, a złośliwy użytkownik może próbować manipulować tym, co wysyła. Więc narzędzie — nie model — to gdzie żyje bezpieczeństwo:

  • Waliduj każdy argument przed działaniem na nim: typy, zakresy, formaty, dopuszczalne wartości. Traktuj argument dostarczony przez model dokładnie tak, jak traktowałbyś wejście od dowolnego niezaufanego klienta, bo tym jest.
  • Egzekwuj autoryzację w narzędziu. Czy to żądanie jest dozwolone, to zadanie narzędzia do sprawdzenia, nie modelu do zaufania. Model proszący o akcję nigdy nie znaczy, że akcja jest dozwolona; twoje narzędzie decyduje.
  • Strzeż operacji destrukcyjnych. Narzędzie, które zmienia albo usuwa dane, wywołane z prośby modelu, potrzebuje prawdziwej ochrony — potwierdzenia, ograniczenia zakresu, limitów — bo model może zawołać je źle. Narzędzia tylko-do-odczytu mogą być pozwalające; brzemienne nie mogą.

Nie możesz włożyć bezpieczeństwa w model i ufać, że się utrzyma — model jest niedeterministyczny i manipulowalny, więc bezpieczeństwo musi żyć w narzędziu, egzekwowane co wywołanie bez względu na to, co model zamierzał. To zasada kontroli function calling, uczyniona konkretną na poziomie budowania narzędzia: model proponuje, wołając; narzędzie dysponuje, walidując, autoryzując i strzegąc.

Uczyń je odzyskiwalnym: komunikaty błędów, na których model może działać

Subtelna, często-mijana zasada: niezawodne narzędzie daje błędy, które model może zrozumieć i z których może się odzyskać. Ponieważ model jest w pętli i czyta twoją odpowiedź, komunikat błędu to nie tylko linia logu — to instrukcja dla modelu o tym, co robić następnie. Projektuj błędy dla tego czytelnika:

  • Zwracaj jasne, wykonalne komunikaty błędów. „Nieprawidłowe order_id: musi być dodatnią liczbą całkowitą, dostano ‘abc’” mówi modelowi dokładnie, co poszło źle i jak naprawić następną próbę. Goły „błąd” albo nieprzejrzysty ślad stosu nie mówi mu nic, i będzie się miotał.
  • Czyń awarie odzyskiwalnymi, gdzie możliwe. Jeśli model zawołał narzędzie nieco źle, dobry błąd pozwala mu się poprawić i ponowić pomyślnie. Dobry komunikat błędu zamienia nieudane wywołanie w udane następne wywołanie — model je czyta, dostosowuje i robi dobrze, co jest dokładnie pętlą, której chcesz.
  • Odróżniaj „zawołałeś to źle” od „to naprawdę zawiodło”. Model powinien móc odróżnić odzyskiwalny błąd (napraw argumenty) od prawdziwej awarii (poddaj się albo eskaluj), bo właściwa następna akcja się różni.

To realna zmiana od projektowania błędów dla kodu, który potrzebuje tylko statusu do rozgałęzienia. Model potrzebuje błędu, który może przeczytać i z niego rozumować, bo jego następna akcja zależy od zrozumienia, co poszło źle. Narzędzie, z którego błędów model może się odzyskać, jest dramatycznie niezawodniejsze w pętli niż takie, które zawodzi nieprzejrzyście.

Uczyń je obserwowalnym

Ostatnia, krótsza zasada, przeniesiona ze wszystkiego o uruchamianiu tych systemów: loguj wywołania narzędzi i ich wyniki. Gdy agent albo interakcja function calling idzie źle, log wywołań narzędzi — co zostało zawołane, z jakimi argumentami, zwracając co — to jak to diagnozujesz. Narzędzie, które jest czarną skrzynką, to takie, którego nie możesz zdebugować, gdy model używa go w sposób, którego nie spodziewałeś, co ostatecznie zrobi. Obserwowalność na granicy narzędzia to część tego, co czyni cały system utrzymywalnym.

Werdykt

Narzędzia to fundament, na którym zbudowane jest wszystko inne o LLM — function calling, agenci, MCP wszystkie sprowadzają się do narzędzi, które model może wywołać — więc budowanie niezawodnych to bazowa dyscyplina, i jest odrębna, bo wołający jest odrębny: niedeterministyczny, omylny model, który interpretuje twoje narzędzie z jego opisu, decyduje, kiedy go użyć, i dostarcza argumenty wyłuskane z języka naturalnego. Z potraktowania tego wołającego poważnie zasady wynikają. Uczyń narzędzie zrozumiałym — jasne, konkretne nazwy i opisy, bo opis jest interfejsem, z którego model rozumuje, a poprawienie go to zwykle najbardziej wpływowa poprawka. Uczyń je dobrze-zakresowym — wąskie, jednocelowe, nienakładające się, z małą powierzchnią argumentów — bo model wybiera i woła skupione narzędzie znacznie dokładniej. Uczyń je bezpiecznym — waliduj każdy argument dostarczony przez model jako niezaufane wejście, egzekwuj autoryzację w narzędziu i strzeż operacji destrukcyjnych — bo bezpieczeństwo nie może żyć w niedeterministycznym modelu i musi być egzekwowane przez narzędzie co wywołanie. Uczyń je odzyskiwalnym — jasne, wykonalne komunikaty błędów, które model może przeczytać i z nich się poprawić, zamieniając nieudane wywołanie w udane ponowienie — bo w pętli błąd to instrukcja dla modelu. I uczyń je obserwowalnym, byś mógł zdebugować nadużycie, które ostatecznie się zdarzy. Projektuj narzędzia dla omylnego, dobrze-intencjonowanego, niedeterministycznego wołającego, którego faktycznie mają, a modele, agenci i systemy zbudowane na ich wierzchu stają się niezawodne — bo wszystko powyżej działa tylko tak dobrze jak narzędzia pod spodem. To gdzie budowanie z LLM ostatecznie spoczywa: na narzędziach, zbudowanych dobrze.