Claude for Startups i 100+ programów z darmowymi kredytami AI dla startupów i JDG Darmowe kredyty AI dla Twojej firmy Sprawdź, co dostaniesz
✨ Świeża dostawa•nowe kody na kinetyka.pl: Gemini Pro 18 mies. 170 zł•Cursor, ElevenLabs, Lovable i więcej, roczne plany AI w ułamku ceny Zobacz →
Przejdź do treści
Artykuły

Routing zgłoszeń z AI: model decyzyjny w Pythonie krok po kroku

Skrypt w Pythonie, który przypisuje zgłoszenia do działów, ocenia pilność i odsyła niepewne sprawy do człowieka. Przetestowany lokalnie, z wynikami i pułapkami.

6 min czytania
Logo Cloudflare i AWS - routing zgłoszeń modelem decyzyjnym

👁 114 przeczytań

// w skrócie
  • Model decyzyjny Strands Decider 2B nadaje się do routingu zgłoszeń, bo zwraca wybrany dział z 6 opcji oraz pewność od 0 do 1, a niepewne sprawy kieruje do człowieka.
  • Jedno zapytanie na karcie RTX 4090 trwało ok. 135 ms, a załadowanie modelu 16 s; model bazowy Qwen3.5-2B-Base z adapterem zajmuje ok. 4,4 GB po pobraniu.
  • Bez opcji "inne" model przypisał puste zgłoszenie do awarii z pewnością 0,80, bo prawdopodobieństwa zawsze sumują się do 1 i model nie może odpowiedzieć "nie wiem".

Model decyzyjny nadaje się do routingu zgłoszeń lepiej niż czat: wybiera dział z Twojej listy, podaje pewność, a niepewne sprawy możesz automatycznie odesłać do człowieka. Poniżej pokazuję działający skrypt w Pythonie na otwartym modelu Strands Decider 2B, który sprawdziłem na swoim komputerze, razem z wynikami i dwoma pułapkami, na które trafiłem. Czym są modele decyzyjne i jak wypadły w teście na 30 polskich zgłoszeniach, opisałem w przewodniku Modele decyzyjne AI.

Co zbudujemy

Skrypt, który dla każdego zgłoszenia zwraca:

  • dział - jeden z 6 (płatności, dostawa, zwroty, konto, awarie, sprzedaż) albo „inne”,
  • pewność wyboru od 0 do 1,
  • kolejkę - dział, jeśli pewność jest wystarczająca, albo do_czlowieka,
  • pilność - czy sprawa wymaga reakcji jeszcze dziś.

Wynik to jedna linia JSON na zgłoszenie, którą łatwo podpiąć pod helpdesk, arkusz albo automatyzację.

Czego potrzebujesz

  • Python 3.10 lub nowszy.
  • PyTorch - z CUDA, jeśli masz kartę NVIDIA (model zajmuje niecałe 4 GB pamięci karty). Bez karty działa na procesorze, tylko wolniej.
  • Pakiet strands-decider (Apache-2.0). Przy pierwszym uruchomieniu pobierze ok. 4,4 GB: model bazowy Qwen3.5-2B-Base i adapter od AWS.
pip install strands-decider

Na Windowsie instalator pip potrafi podmienić PyTorch na wersję bez CUDA. Sprawdź to poleceniem python -c "import torch; print(torch.cuda.is_available())" - jeśli zwraca False, doinstaluj PyTorch z indeksu pytorch.org dla swojej wersji CUDA. U mnie tak właśnie się stało.

Skrypt routing.py

# Routing zgłoszeń modelem decyzyjnym Strands Decider 2B (lokalnie, Apache-2.0).
# pip install strands-decider   (do tego PyTorch z CUDA, jeśli masz kartę NVIDIA)
import json, sys
from strands_decider.infer import load_engine
from strands_decider.schema import SystemOneRequest

MODEL = "StrandsAgents/strands-decider-2B-hobson-v19"
DZIALY = {
    "platnosci": "Płatności, faktury, obciążenia karty, abonament",
    "dostawa": "Wysyłka, kurier, śledzenie paczki, adres dostawy",
    "zwrot": "Zwroty towaru, reklamacje, wymiana, uszkodzony produkt",
    "konto": "Logowanie, hasło, dane konta, bezpieczeństwo konta, RODO",
    "awaria": "Błędy aplikacji lub strony, awarie, integracje, wydajność",
    "sprzedaz": "Pytania o ofertę, plany, rabaty, wdrożenia dla firm",
    "inne": "Z treści nie da się ustalić, czego dotyczy zgłoszenie",
}
PROG_DZIAL = 0.60   # poniżej tej pewności zgłoszenie idzie do człowieka
PROG_PILNE = 0.40   # próg dobrany na moich 30 zgłoszeniach - sprawdź na swoich danych

silnik = load_engine(MODEL, device="cuda")  # "cpu" też działa, tylko wolniej

def rozdziel(tekst: str) -> dict:
    zapytanie = SystemOneRequest(state=tekst, questions={
        "dzial": {"type": "choice", "instructions": "Do którego działu trafia to zgłoszenie?", "criteria": DZIALY},
        "pilne": {"type": "noul", "instructions": "Czy zgłoszenie wymaga reakcji jeszcze dziś (awaria, utrata pieniędzy, włamanie, nieprzekraczalny termin)?"},
    })
    odp = silnik.evaluate(zapytanie).answers
    dzial, pewnosc, p_pilne = odp["dzial"].choice, odp["dzial"].confidence, odp["pilne"].noul
    kolejka = dzial if pewnosc >= PROG_DZIAL and dzial != "inne" else "do_czlowieka"
    return {"kolejka": kolejka, "dzial": dzial, "pewnosc": round(pewnosc, 2),
            "pilne": p_pilne >= PROG_PILNE, "p_pilne": round(p_pilne, 2)}

if __name__ == "__main__":
    for linia in sys.stdin:
        if linia.strip():
            print(json.dumps({"tekst": linia.strip()[:60], **rozdziel(linia)}, ensure_ascii=False))

Kilka decyzji projektowych:

  • Opisy działów są po polsku. W moim teście polskie opisy działały tak samo dobrze jak angielskie, a łatwiej je utrzymać zespołowi.
  • Dwa pytania w jednym zapytaniu. Model liczy zgłoszenie raz, a pytania doliczane są prawie bez kosztu, więc dodanie trzeciego pytania (np. „czy klient grozi odejściem?”) nie wydłuży czasu.
  • Decyzję podejmuje kod, nie model. Model zwraca liczby, a progi i kolejki są w Pythonie - łatwo je zmienić bez ruszania modelu.

Uruchomienie i wyniki

Zgłoszenia trafiają na standardowe wejście, po jednym w linii:

python routing.py < zgloszenia.txt

Wyniki z mojego RTX 4090 dla sześciu zgłoszeń, których model wcześniej nie widział:

ZgłoszenieBez opcji „inne”Z opcją „inne”
Od rana nie działa logowanie przez Google, cały zespół stoi.konto 0,54 - do człowieka, pilnekonto 0,46 - do człowieka, pilne
Proszę o fakturę VAT na firmę do zamówienia 77120.płatności 0,46 - do człowiekainne 0,63 - do człowieka
Paczka wróciła do nadawcy, bo kurier nie mógł znaleźć adresu.dostawa 0,96dostawa 0,90
Czy macie zniżki dla szkół i uczelni?sprzedaż 0,58 - do człowiekasprzedaż 0,31 - do człowieka
Hej, mam pytanie. Coś mi nie gra.awaria 0,80awaria 0,41 - do człowieka
Zwracam kurtkę, ale nie wiem, czy zwrot pójdzie na kartę czy na konto.zwrot 0,94zwrot 0,84

Jedno zapytanie trwało na karcie ok. 135 ms, a załadowanie modelu 16 s. Wszystkie zgłoszenia, które model rozdzielił automatycznie, trafiły do właściwego działu. Pilność oznaczył tylko przy awarii logowania całego zespołu - i o to chodziło.

Pułapka 1: pewny siebie przy pustym zgłoszeniu

Nie przepłacaj za te subskrypcje

Prowadzę sklep z rocznymi dostępami do narzędzi AI - te same konta, o których piszę wyżej, tylko taniej niż w cenniku producenta.

Zobacz, co jest dostępne

W pierwszej wersji nie miałem opcji „inne”. Zgłoszenie „Hej, mam pytanie. Coś mi nie gra.” model przypisał do awarii z pewnością 0,80 - wyżej niż mój próg, więc trafiłoby do zespołu technicznego. To nie błąd modelu: kazałem mu wybrać jeden z sześciu działów, a prawdopodobieństwa zawsze sumują się do 1. Model decyzyjny nie powie „nie wiem”, jeśli nie dasz mu takiej opcji.

Rozwiązanie: opcja inne z opisem „Z treści nie da się ustalić, czego dotyczy zgłoszenie” i reguła w kodzie, że inne zawsze idzie do człowieka. Po zmianie puste zgłoszenie spadło do 0,41 i poszło do człowieka.

Pułapka 2: opcja „inne” zabiera pewność

Dodatkowa opcja ma koszt: pewność spadła także przy oczywistych zgłoszeniach (paczka z 0,96 do 0,90, kurtka z 0,94 do 0,84), a prośba o fakturę VAT trafiła do „inne” zamiast do płatności. Co z tym zrobić:

  • Dopisz do opisów słowa, których używają Twoi klienci. Jeśli „faktura VAT” trafia do „inne”, w opisie płatności powinno być „faktura VAT, korekta faktury”.
  • Ustal próg na własnych danych. Weź 50-100 oznaczonych zgłoszeń z historii, puść przez skrypt i sprawdź, przy jakim progu automat się nie myli. Mój próg 0,60 to punkt startu, nie reguła.
  • Licz odsetek „do człowieka”. Jeśli przekracza 30-40%, opisy działów są za ogólne albo działy na siebie zachodzą.

Pilność: próg też trzeba ustawić

W teście na 30 polskich zgłoszeniach Strands Decider przypisywał pilnym sprawom prawdopodobieństwo od 0,22 do 0,47. Przy domyślnym progu 0,5 nie oznaczyłby jako pilnego niczego - stąd w skrypcie próg 0,40. Większy model Clef-flash od Cloudflare miał odwrotną skłonność i najlepiej działał przy progu 0,95. Zasada jest jedna: liczby z modelu traktuj jako ranking, a granicę wyznacz na swoich przykładach.

Jak przełączyć się na inny model

Strands Decider, Cloudflare Clef, Kev i Jev od TypeSafe używają tego samego formatu zapytań (System One: state + questions). Skrypt da się przenieść na każdy z nich, zmieniając tylko sposób wywołania:

  • Clef-flash lokalnie (karta 24 GB): funkcja systemone() z pliku joint_schema_model.py w repozytorium Cloudflare/clef-flash. Na moim teście był nieco szybszy (112 ms) i pewniejszy, ale potrzebuje 18 GB pamięci karty. O premierze piszę w tekście Cloudflare Clef.
  • Workers AI: POST na /ai/run/@cf/cloudflare/clef-flash z tym samym JSON-em, 0,09 USD za milion tokenów wejścia i darmowy przydział dzienny. Wymaga konta Cloudflare.
  • Serwer HTTP Strands: strands-decider serve ... --port 8000 i żądania na /v1/systemone - wtedy skrypt nie musi ładować modelu sam.

Więcej o samym Strands Decider, jego licencji i wynikach w tekście Strands Decider 2B.

Kiedy nie używać modelu decyzyjnego do routingu

  • Gdy chcesz od razu wygenerować odpowiedź dla klienta - do tego potrzebny jest LLM. Model decyzyjny może za to wybrać, który szablon odpowiedzi zastosować.
  • Gdy zgłoszeń jest kilka dziennie - prościej poprosić duży model o JSON (opisuję to w tekście Structured output).
  • Gdy działy zmieniają się co tydzień i nie masz historii z etykietami - nie ustawisz wtedy sensownych progów.
Google · Twoje źródłaPromptowy wyżej w Twoim Google - jednym kliknięciemDodaj do preferowanych źródeł →

Źródła

// Newsletter

Cały tydzień w AI, w jednym mailu

Wybrane premiery, narzędzia i analizy. Raz w tygodniu, prosto do skrzynki.

Zapisz się za darmo →
Za darmo. Wypisujesz się jednym kliknięciem.

Najczęstsze pytania

Jak zainstalować Strands Decider 2B do routingu zgłoszeń w Pythonie?

Wystarczy uruchomić pip install strands-decider, a przy pierwszym uruchomieniu pobrane zostanie ok. 4,4 GB danych. Na Windowsie instalator pip może podmienić PyTorch na wersję bez CUDA, co sprawdzisz poleceniem python -c "import torch; print(torch.cuda.is_available())".

Jaki próg pewności ustawić, żeby zgłoszenie trafiło do człowieka, a nie do działu?

W skrypcie domyślny próg to 0,60 - zgłoszenia z pewnością poniżej tej wartości trafiają do kolejki do_czlowieka. Autor zaznacza, że to punkt startu, a właściwy próg należy wyznaczyć na 50-100 własnych oznaczonych zgłoszeniach.

Czy model decyzyjny działa bez karty graficznej NVIDIA?

Tak, model działa na procesorze, tylko wolniej - w kodzie wystarczy zmienić parametr device z "cuda" na "cpu". Model zajmuje niecałe 4 GB pamięci karty, gdy działa na GPU.

Ile kosztuje użycie Cloudflare Clef zamiast lokalnego Strands Decider?

Workers AI pobiera 0,09 USD za milion tokenów wejścia i oferuje darmowy przydział dzienny, ale wymaga konta Cloudflare. Lokalnie Clef-flash był nieco szybszy niż Strands Decider (112 ms vs 135 ms), lecz potrzebuje 18 GB pamięci karty.

// czytaj też

Podobne tematy na Promptowym

Piotr Olszewski

Piotr Olszewski

AUTOR I WYDAWCA

Piotr Olszewski - twórca i autor Promptowego, polskiego serwisu o sztucznej inteligencji. Codziennie śledzi premiery modeli, narzędzia i regulacje AI, i tłumaczy je prostym, konkretnym językiem.

// mapa strony

🛒 Sklep Kinetyka Google AI Gemini Pro 170 zł CapCut Pro 460 zł/rok Lovable Pro 400 zł/rok Zobacz wszystko →
promptowy w liczbach 0tekstów w archiwum0newsów z ostatnich 7 dni0modeli wideo w obserwatorium0zagadek w grach
× ‹ powiększenie ›