{<Z Kordian Zadrożny

AI, Strony WWW, Programowanie, Bazy danych

Lokalny agent AI i „Function Calling”: Czy modele poniżej 10B parametrów zawsze zawodzą? Studium przypadku

utworzone przez | lis 1, 2025 | różne, AI | 0 komentarzy

Ekscytują mnie małe modele, SLM, czyli AI na twoim komputerze czy w infrastrukturze.

Obietnica jest kusząca: pełna prywatność danych, zerowe koszty API i działanie na własnym sprzęcie. Ale jak ta obietnica sprawdza się, gdy przechodzimy do budowy chatbotów z podstawowym RAG?

Celem zabawy było stworzenie asystentaAI dla wewnętrznego użytku hipotetycznej firmy, który potrafi autonomicznie korzystać z narzędzi – w tym przypadku przeszukiwać bazę dokumentów firmowych (RAG).

Podczas testów odkryłem twardą ścianę: małe modele (poniżej 10-12 miliardów parametrów) nie potrafią poprawnie obsłużyć standardowego API Function Calling od OpenAI, a przynajmniej nie w moim stacku, czyli pydentic AI + ollama

To studium przypadku dokumentuje moją drogę przez serię niepowodzeń aż do znalezienia pół rozwiązania – bo docelowym nie jest.

Cel i stos technologiczny

Zanim przejdziemy do testów, zdefiniujmy problem. Chcieliśmy, aby agent AI uruchomiony na lokalnej maszynie (przez Ollama) i zarządzany przez framework Pydantic AI, sam decydował, kiedy potrzebuje informacji z naszej bazy wektorowej ChromaDB.

Stack projektu:

  • Backend: Django 5
  • Framework agenta: Pydantic AI 1.9.1
  • Hostowanie LLM: Ollama
  • Baza wektorowa: ChromaDB 1.3.0
  • Sprzęt: Intel i7-13700F (16 rdzeni), 64GB RAM, RTX 4060 Ti 16GB VRAM

Kluczowym elementem było narzędzie search_documents_tool, które agent miał samodzielnie wywoływać.

Aby to zadziałało, model musi nie tylko rozumieć język polski (to znaczy takie było moje założenie), ale także format JSON, w jakim Pydantic AI (korzystający ze schematu OpenAI) przekazuje definicje narzędzi i oczekuje odpowiedzi.

Chronologia testów: metodyczne poszukiwanie granic

Zacząłem od modeli najmniejszych, systematycznie zwiększając ich rozmiar.

Test 1: Gemma 3 Tools 2B ❌ (Porażka formatu)

  • Model: gemma:3-tools-2b (2 mld parametrów)
  • Oczekiwanie: Nazwa „Tools” sugeruje wsparcie dla function calling.
  • Wynik: Natychmiastowy błąd aplikacji: Error: invalid message content type: <nil>
  • Diagnoza: Model jest zbyt mały. Zamiast zwrócić poprawną strukturę JSON lub pustą odpowiedź, zwrócił nil. Format OpenAI API jest dla niego całkowicie niezrozumiały.

Test 2: Mistral 7B ❌ (Porażka logiki)

  • Model: mistral:7b (7 mld parametrów)
  • Oczekiwanie: Jeden z najpopularniejszych modeli, świetny w zadaniach ogólnych.
  • Wynik:User: Jaki jest adres firmy? AI: ```yaml search_documents_tool("adres firmy")
  • Diagnoza: Mistral zrozumiał intencję – wiedział, że powinien użyć narzędzia. Jednak zamiast wywołać je przez API (generując specjalny JSON), „napisał” kod w odpowiedzi. Traktuje definicję narzędzia jak zwykły tekst do naśladowania, a nie meta-instrukcję do wykonania.

Test 3: Qwen3-VL 8B ⚠️ (Porażka wydajności)

  • Model: qwen3-vl:8b (8 mld parametrów)
  • Oczekiwanie: Model z dodatkową warstwą „thinking” sądziłem, że sobie poradzi, i w sumie poradził.
  • Wynik: Sukces! Logi pokazały: 🔍 TOOL CALLED: search_documents('firma adres')
  • Problem: Czas odpowiedzi: 11 minut i 23 sekundy!
  • Diagnoza: Model poprawnie obsłużył function calling, jednak jego dodatkowa warstwa „rozumowania” (thinking) sprawia, że „zastanawia się” przez ponad 11 minut przed wywołaniem narzędzia. W aplikacji czatowej jest to absolutnie nieakceptowalne.

Test 4: Mistral Nemo 12B ✅ (Częściowy sukces)

  • Model: mistral-nemo:12b (12 mld parametrów)
  • Oczekiwanie: Przekroczenie progu 10B powinno pomóc.
  • Wynik: 🔍 TOOL CALLED: search_documents('firma') – działa! Czas odpowiedzi: ok. 30 sekund.
  • Problem: Słaba obsługa języka polskiego. Mimo polskich promptów, model odpowiadał po angielsku lub w niestrawnej mieszance „ponglish”.
  • Diagnoza: Function calling działa, ale model jest bezużyteczny dla polskiego użytkownika.

Test 5: GPT-OSS 14B ✅ (Zwycięzca na dziś)

  • Model: gpt-oss:14b (14 mld parametrów)
  • Oczekiwanie: Ostatnia nadzieja w modelach średniego rozmiaru.
  • Wynik: Pełen sukces.💬 NEW CHAT REQUEST Question: 'Do jakiej gwiazdy lecą w opowiadaniu?' 🔍 TOOL CALLED: search_documents_tool Query: 'gwiazda opowiadanie cel podróży' Documents: WEGA.pdf Chunks: 3 🤖 AI Response: "W opowiadaniu statek jest w tunelu czasoprzestrzennym od 90 AU od Słońca do „Wegii" – czyli do tej właśnie gwiazdy, do której jest skierowana misja."
  • Diagnoza:
    • Function calling działa perfekcyjnie.
    • Model autonomicznie decyduje, kiedy użyć narzędzia.
    • Język polski jest na bardzo dobrym poziomie.
    • Czas odpowiedzi (ok. 45 sekund na RTX 4060 Ti 16GB) jest akceptowalny dla aplikacji wewnętrznej.

Analiza: Dlaczego małe modele nie radzą sobie z Function Calling?

Problem leży w złożoności zadania. Standard OpenAI Function/tool Calling wymaga od modelu wykonania trzech kroków:

  1. Analiza definicji narzędzia: Zrozumienie skomplikowanego formatu JSON podanego w promcie systemowym.
  2. Decyzja: Zdecydowanie, czy odpowiedzieć bezpośrednio, czy użyć jednego z dostępnych narzędzi.
  3. Wygenerowanie odpowiedzi: Stworzenie precyzyjnej, kolejnej struktury JSON (zamiast zwykłego tekstu), która instruuje system, jakie narzędzie wywołać i z jakimi argumentami.

Modele poniżej 10B parametrów zdaje się po prostu nie mają wystarczającej „głębi” obliczeniowej, aby zrozumieć tę meta-instrukcję. Widzą definicję narzędzia jako przykład tekstu, który mają naśladować (stąd search_documents_tool("adres firmy") w odpowiedzi), a nie jako polecenie do wykonania akcji.

Ale jeszcze się nie poddaję, mój test, spróbuje wykonać na kolejnych modelach, unikałem na razie llama 3.1. Ma słaby polski, ale sądzę, że da radę.

Poniższa tabela podsumowuje wyniki testów:

Wielkość modeluFunction CallingCzas odpowiedziJęzyk Polski
2B (Gemma)❌ Błąd formatuN/AN/A
7B (Mistral)❌ Pisze kod zamiast wywołać~30s
8B (Qwen3-VL)✅ Działa❌ 11 minut
12B (Mistral Nemo)✅ Działa✅ 30s
14B (GPT-OSS)✅ Działa✅ 45s

Wnioski i rekomendacje

Budowanie systemów AI to sztuka kompromisu – w tym przypadku między rozmiarem modelu, jego „inteligencją” a dostępną mocą obliczeniową.

Moje testy pokazują, że jeśli chcesz budować lokalnych agentów AI zdolnych do autonomicznego korzystania z narzędzi (przez API w stylu OpenAI), minimalny próg wejścia to model o wielkości 12-14 miliardów parametrów – przynajmniej na tę chwile testów.

Zejście poniżej tej granicy prowadzi do frustracji i błędów logicznych, których nie da się naprawić prostym prompt engineeringiem.

Co jeśli musisz użyć małego modelu (<10B)?

Trzeba porzucić eleganckie API function calling i wrócić do starszych technik:

  1. Parsowanie Regex: Uczenie modelu, by zwracał specjalny token (np. AKCJA: WYSZUKAJ[zapytanie]), który następnie wycinamy z odpowiedzi.
  2. Podejście ReAct: Implementacja pętli, w której model „myśli”, „działa”, a my ręcznie parsujemy jego odpowiedzi.
  3. Podejście „zawsze szukaj”: Mniej inteligentne, ale proste – każde zapytanie użytkownika najpierw przepuszczamy przez wyszukiwarkę RAG i dodajemy wyniki do kontekstu.
  4. Lub czysty JSON i obsługa w klasycznych if i funkcjach

Wszystkie te obejścia działają, ale są mniej eleganckie i dokładne niż natywne function calling.

Dla mnie wybór padł na model gpt-oss:14b. Te metodyczne testy pozwoliły precyzyjnie określić ograniczenia dostępnych narzędzi, zanim zbudowaliśmy na nich cały system.

Jakie są Wasze doświadczenia z function calling na małych modelach?

0 komentarzy

Wyślij komentarz

Twój adres e-mail nie zostanie opublikowany. Wymagane pola są oznaczone *

Share This

Share this post with your friends!