Wszystkie wpisy
Nowe wydanie10 min czytania

Silnik basal 2.5: wiele GPU, kaskada modeli i te same odpowiedzi

Domyślne odpowiedzi są identyczne bajt w bajt z 2.4.1, więc aktualizacja jest bezpieczna, a nowości włączasz sam. basal-router łączy kilka GPU pod jednym portem, kaskada pico → mini → ultra kieruje trudniejsze pytania do większego modelu, a ultra działa w FP8 na jednej karcie 80 GB. Opisujemy też granice tych zmian i plan na 2.5.1.

Remigiusz Kinas · ai5
Silnik basal 2.5: basal-router 1,58× żądań na dwóch H100, kaskada mini → ultra około 1,5× żądań przy tej samej trafności; domyślne odpowiedzi bajt w bajt jak w 2.4.1.

Gdy system decyzyjny zaczyna obsługiwać prawdziwy ruch, pojawiają się nowe pytania. Co zrobić, gdy jedno GPU nie wystarcza? Jak przekazać trudniejsze przypadki większemu modelowi? Kiedy lepiej nie automatyzować odpowiedzi? Wydany 2 listopada silnik basal 2.5 daje na te pytania nowe narzędzia i nie zmienia odpowiedzi, które już znasz.

Domyślne odpowiedzi są identyczne bajt w bajt z 2.4.1. Sprawdziliśmy to na H100 dla basal-omni-1.0-mini, basal-omni-1.0-max i basal-omni-pico w --mode fast oraz dla mini i pico w --mode eager. Aktualizacja jest więc bezpieczna. Prawie wszystko, co nowe, włączasz sam: flagą serwera albo polem w żądaniu.

1,58×basal-router: żądania na dwóch H100 względem jednego GPU
~1,5×kaskada mini → ultra: żądania względem samego ultra, ta sama trafność
1,35×ultra w blokowym FP8 na jednym GPU 80 GB: decyzje/s względem bf16

Źródło liczb i konfiguracji: wydanie v2.5.0 i dokumentacja w repozytorium. Kaskadę mierzyliśmy na odłożonej połowie zbioru, na jednym H100.

Bezpieczna aktualizacja

Zasada tego wydania jest prosta: jeśli nie zmieniasz konfiguracji, dostajesz te same prawdopodobieństwa co w 2.4.1. Notatki do wydania wymieniają cztery zmiany na domyślnej ścieżce. Dotyczą obsługi żądań, a nie samych decyzji:

  • Nazwy pytań z ::. W żądaniu z pytaniami typu zadanie pytanie o nazwie <zadanie>::<część> jest odrzucane (HTTP 422). Wcześniej zadanie mogło odczytać jego odpowiedź jako własną (#59).
  • Czytelniejsze błędy. Treść błędu ma pole code (INVALID_REQUEST, OUT_OF_MEMORY, INTERNAL_ERROR), a odpowiedź 422 także detail. Pole error zachowuje dotychczasowy tekst (#62).
  • Więcej w GET /v1/models. Lista modeli podaje certyfikowane progi dla każdego celu max_error i typu pytania oraz parametry kontekstu (#64).
  • Identyfikator żądania. Każda odpowiedź ma nagłówek x-request-id (#68).

Jedna domyślna zmiana przyspiesza pracę bez zmiany wyników. Serwer tokenizuje wspólną część promptów, czyli szablon i stan, tylko raz. Silnik dostaje te same identyfikatory tokenów co wcześniej. Żądanie rank z 300 kandydatami i stanem 12,5 tys. tokenów zajmuje 0,14 s CPU zamiast 5,8 s.

Są też poprawki. Puste "questions" daje teraz HTTP 422 zamiast 500. Edytowane stany czatu z cache stanu nie kończą się już błędem 500 z wartościami NaN. Na basal-omni-1.0-ultra błąd dotyczył 27 z 1 248 takich żądań, po poprawce żadnego. Wersja 2.4.1 miała ten sam problem.

Wiele GPU i kaskada modeli

basal-router łączy kilka replik basal-serve pod jednym portem. Opcja --gpus 0,1 uruchamia po jednej replice na GPU i zatrzymuje je razem z routerem, także gdy router zostanie zabity. Żądania o ten sam stan trafiają do tej samej repliki (routing po skrócie stanu z ograniczeniem obciążenia), więc jej cache stanu dalej działa. Replika, która odpowiada 503 albo milczy, nie dostaje żądań, dopóki nie będzie gotowa.

Na dwóch H100 router obsłużył 1,58× tyle żądań co jedno GPU, przy dwukrotnie większej liczbie klientów. Skalowanie nie jest jeszcze liniowe: dwa GPU to na razie nie dwa razy więcej pracy.

Kaskada (--escalate, --escalate-below) zadaje pytanie ponownie większemu modelowi, gdy najwyższe prawdopodobieństwo jest poniżej progu. Poziomy można łączyć, na przykład basal-omni-pico → mini → ultra, każdy z własnym progiem:

TERMINAL
basal-serve --model Remek/basal-omni-pico --port 8001 &
basal-serve --model Remek/basal-omni-1.0-mini --port 8002 &
basal-serve --model Remek/basal-omni-1.0-ultra --port 8003 &
basal-router --replicas http://127.0.0.1:8001 \
    --escalate http://127.0.0.1:8002 --escalate-below 0.8 \
    --escalate http://127.0.0.1:8003 --escalate-below 0.9 --port 8000

Każda odpowiedź mówi, który model jej udzielił (answered_by), a przy dwóch i więcej poziomach podaje też poziom. /metrics liczy odpowiedzi na każdym poziomie. Gdy któryś poziom nie działa, zostaje odpowiedź z poprzedniego i żądanie nie kończy się błędem. Opcja --route kieruje stany w jednym alfabecie, na przykład cyrylicy, do osobnych replik.

Na odłożonej połowie zbioru kaskada mini → ultra z progiem 0,95 obsłużyła około 1,5× tyle żądań co sam ultra na jednym H100, przy tej samej trafności (w granicach 0,5 pkt). Ultra dostaje tylko pytania, przy których mini nie jest pewny.

Blend (--blend) działa odwrotnie. Każde pytanie trafia do kilku modeli, a router zwraca ważoną średnią ich prawdopodobieństw. Mieszanka 0,6 ultra + 0,2 mini + 0,2 pico dała +0,45 pkt względem samego ultra (95% przedział: od +0,09 do +0,81). To opcja dla jakości, nie dla szybkości.

Rezerwa LLM (--fallback) działa po ostatnim poziomie. Pytanie wyboru, którego najwyższe prawdopodobieństwo wciąż jest poniżej progu T, trafia do modelu czatu, który serwujesz za endpointem zgodnym z OpenAI. Jego odpowiedź ("answered_by": "fallback") zastępuje odpowiedź basal. W przykładzie z basal-omni-1.0-ultra i Gemma 4 31B-it z myśleniem przy T = 0,8 wynik wzrósł o 1,40 pkt (95% przedział: od +0,90 do +1,89), a do modelu czatu trafiło 8,4% pytań. Ten wynik zakłada, że limit czasu nie przerwał żadnego wywołania. Jedno wywołanie trwało 54 s w medianie i 308 s w p95. Przy domyślnym --fallback-timeout 60 w 46 na 100 wywołań została odpowiedź basal, co dało około trzech czwartych zysku na tej próbie.

basal-router · tryby opcjonalne
TrybKiedy pomagaPomiarNa co uważać
Repliki na wielu GPUruch większy
niż jedno GPU
1,58× żądań
na 2 × H100
skalowanie
nie jest liniowe
Kaskada mini → ultraduży model tylko
dla trudnych pytań
~1,5× żądań,
ta sama trafność
wyższe p95
przy 16 klientach
Blendliczy się jakość+0,45 pkt
względem ultra
każde pytanie
do każdego modelu;
bez kalibracji
Rezerwa LLMnajtrudniejsze,
rzadkie pytania
+1,40 pkt;
8,4% pytań wysłanych
mediana wywołania 54 s;
bez kalibracji

Pomiary kaskady, blendu i rezerwy pochodzą z odłożonej połowy zbioru. Szczegóły: RESULTS.md i SERVING.md.

Odpowiedzi, na których można polegać

basal zwraca skalibrowane prawdopodobieństwa. Wersja 2.5 pozwala aplikacji korzystać z nich bezpośrednio, bez własnej logiki progów:

  • Zbiory odpowiedzi. "coverage": 0.9 zwraca zbiór opcji, który z tym prawdopodobieństwem zawiera właściwą. Gdy model waha się między dwiema kategoriami, zbiór zawiera obie, a człowiek wybiera już tylko spośród nich.
  • Wstrzymanie się od odpowiedzi. Gdy podasz "max_error", odpowiedź wyboru, tak/nie lub skali dostaje "abstain": true wtedy, gdy pewność jest poniżej certyfikowanego progu dla jej typu. Sama odpowiedź się nie zmienia, ale aplikacja wie, że tę sprawę lepiej przekazać człowiekowi.
  • Drugi odczyt (--reread T). Odpowiedzi poniżej progu T są czytane ponownie, ze stanem podanym dwa razy, a oba odczyty są łączone. --reread-image czyta obraz ponownie przy większej liczbie tokenów.
  • Kolejność opcji. basal-bench --order-check mierzy, jak często najwyżej oceniona odpowiedź zmienia się po odwróceniu lub przesunięciu opcji. --order-average K zwraca średnią z K kolejności i czyta stan tylko raz. Pomaga wyłącznie basal-omni-1.0-mini: +0,6 pkt top-1 na naszym zbiorze developerskim przy K = 2, kosztem mniej więcej dwukrotnie dłuższego czasu na pytanie. Na max i pico nie pomaga, dlatego jest domyślnie wyłączone.

Gotowe zestawy pytań guard_input, guard_output i route_intent mają na razie progi dobrane ręcznie, a nie certyfikowane. Przy max_error korzystają z kalibracji serwowanego modelu dla każdego typu pytania.

Więcej sprzętu i szybszy start

basal-omni-1.0-ultra, największy model rodziny, dostaje trzy tryby kwantyzacji przy ładowaniu i mieści się na mniejszych kartach. Działają one tylko dla ultra. Pozostałe modele basal-omni nie przeszły kontroli dokładności, więc silnik je w tych trybach odrzuca.

basal-omni-1.0-ultra · tryby opcjonalne
TrybSprzętWynikUwagi
--mode fp8jeden GPU 80 GB1,35× decyzji/s względem bf16;
53 zamiast 74 GB
przy 16 klientach
cache stanu
wyłączony do 2.5.1
--mode nvfp4Blackwell2,3× szybsze ocenianie
niż bf16
2,4% odpowiedzi top-1:
inne niż w bf16
--mode int8Ampere i nowsze,
bez FP8
99,4% odpowiedzi top-1
jak w bf16
własna kalibracja
--quant-load cpukarta 48 GBultra w fp8 lub int8
mieści się na 48 GB
kwantyzacja
po jednej warstwie

Checkpointy w repozytoriach -FP8 ładują się teraz bez kopii bf16 na GPU. Dzięki temu basal-omni-1.0-ultra-FP8 działa na jednym H100 80 GB; wcześniej przy ładowaniu brakowało pamięci. --state-cache-bits 8 przechowuje stany w cache jako 8-bitowe liczby całkowite: mieści się ich około 1,7× więcej na GB, a na basal-omni-1.0-max 99,45% najwyżej ocenionych odpowiedzi jest takich samych jak z cache 16-bitowym.

Szybszy start. basal-prepare raz zapisuje jądra dostrojone do danego GPU i wersji silnika, a --prepared auto wczytuje je przy starcie. Odpowiedzi są identyczne bajt w bajt z zimnym startem. basal-omni-pico startuje w 72 s zamiast 287 s. Repozytoria modeli mają już katalog prepared/ dla H100, a --prepared auto pobiera tylko część dla Twojego GPU i wersji. Pierwszy start na H100 z pustymi cache: basal-omni-1.0-mini 290 → 172 s, basal-omni-1.0-max 259 → 164 s. Pico na H100 daje teraz te same odpowiedzi po każdym starcie; w 2.4.1 jego odpowiedzi w trybie fast mogły się różnić między startami.

Apple Silicon. Cache stanu działa teraz w --mode mlx, z odpowiedziami identycznymi bit w bit jak bez cache. Na MacBooku Air M4 polityka o długości 2,1 tys. tokenów, zapisana w cache, skróciła czas żądania z 13,7 do 0,9 s. Opcjonalne --mlx-forward basal liczy ostatnie warstwy tylko w wierszach odpowiedzi: basal-omni-1.0-mini-MLX odpowiada na jeden prompt w 131 ms zamiast 225 ms (M4 Pro). basal-omni-pico działa na Macu w --mode mlx. Na 1 700 odłożonych promptach jego odpowiedzi zgadzają się z CUDA eager float32 (top-1 100%).

Starsze GPU. Przewodnik OLD_GPUS.md uruchamia basal-omni-pico na kartach GTX 10xx, RX 480 / 580 i Intel Iris Xe przez backend Vulkan w llama.cpp. basal-selfcheck porównuje Twoją konfigurację z odpowiedziami referencyjnymi dołączonymi do wydania.

Na produkcji: podgląd na żywo, metryki, klucze API

Strona na żywo dla basal-serve (--ui on, domyślnie wyłączona) pokazuje stan serwera, ruch od startu, cache stanu, telemetrię GPU i hosta, konfigurację oraz metadane ostatnich 200 żądań, nigdy ich treść. --ui full pokazuje także każde z tych żądań i odpowiedź. Przy jednej otwartej stronie koszt jest mały. Na H100 z basal-omni-1.0-mini przepustowość mieściła się w 1% wyniku z --ui off. Mediana opóźnienia przy 8 klientach rosła o 0,4–2,2% z 1–2 stronami i o 5,2% z --ui full. Przy jednym kliencie odpowiedzi są identyczne bajt w bajt.

Jedna strona dla routera i wszystkich replik (basal-router --ui on) pokazuje routing, gotowość, żądania w toku, błędy i ostatnie 200 żądań routera. Obok są karty replik z ich stanem, ruchem, cache stanu i telemetrią GPU. Na 2 × H100 mediana przy 8 klientach wzrosła o 1,3%, w granicach rozrzutu samego --ui off.

  • GET /metrics w formacie Prometheus dla basal-serve i basal-router, z wersją silnika, usunięciami z cache stanu i trafieniami prefiksu (#48). Podczas ładowania modelu serwer odpowiada HTTP 503, a nieudany start kończy proces, zamiast go zawieszać.
  • Klucze API (--api-keys PATH, opcjonalnie). Plik JSON przechowuje skróty SHA-256 kluczy. Każda ścieżka poza GET /health wymaga klucza, a administrator widzi użycie kluczy w GET /v1/keys. --cors-origins otwiera dostęp z przeglądarki dla wskazanych domen (#68). basal-router przekazuje klucz klienta replikom, ale nigdy do --fallback.
  • Repliki tylko lokalnie. Repliki uruchamiane przez router z --gpus nasłuchują na 127.0.0.1. Wcześniej nasłuchiwały na 0.0.0.0, więc były osiągalne z sieci z pominięciem routera.

Integracje: od API OpenAI po LM Studio

  • OpenAI Decisions API. basal-serve odpowiada na /v1/decisions, więc client.decisions.create z SDK OpenAI działa z lokalnym modelem. Prawdopodobieństwa są te same co w /v1/systemone.
  • LM Studio (opcjonalnie). basal-export-gguf --systemone zamienia plik basal w formacie GGUF w model decyzyjny LM Studio: wybór, skala i tak/nie, do 26 opcji, ze skalibrowanymi temperaturami i bez basal-serve. Potrzebne jest LM Studio 0.4.26 lub nowsze (kanał beta) z silnikiem llama.cpp 2.65.1 lub nowszym. Na wszystkich 607 pytaniach, na które LM Studio odpowiedziało, najwyżej oceniona odpowiedź była taka sama jak w basal-serve. LM Studio przerywa żądanie po 30 s.
  • Klient TypeScript (clients/typescript, bez zależności w czasie działania): typowane pytania i odpowiedzi, zbiory odpowiedzi, abstain, ask_if / depends_on, obrazy i błędy. Na razie budujesz go z repozytorium; nie ma go jeszcze w npm.
  • Frameworki agentowe. Lekkie narzędzia dla LangChain, LlamaIndex i CrewAI korzystają z API HTTP (basal.adapters). Gotowe zestawy pytań (triage, moderacja, compliance) są w basal-run --preset.
  • MCP. Serwer z kilkoma modelami udostępnia list_models oraz argument model dla narzędzi decide i decide_many (#66).
  • Klient Python ma metodę dla każdego typu pytania, AsyncBasal dla asyncio i błędy według kodu statusu (#58).
  • Własne modele. basal-attach serwuje jako model decyzyjny model czatu, który nigdy nie był trenowany na basal, na przykład Gemma 4 lub Qwen. Startuje bez kalibracji; basal-calibrate dopasowuje go do Twoich oznaczonych żądań. basal-finetune trenuje LoRA na Twoich oznaczonych decyzjach. Sprawdziliśmy go na Apple Silicon z basal-omni-pico, na innych GPU jeszcze nie.
  • Adaptery LoRA przy ładowaniu (--adapter DIR w basal-serve, basal-calibrate i basal-bench). Odpowiedzi są bit w bit takie same jak z checkpointu po scaleniu, który zapisuje basal-fuse-adapter (#52).

Eksperymentalnie: rank i basal-navigate

rank ocenia tysiące kandydatów w jednym żądaniu. Najpierw przesiewa je pytaniami tak/nie, potem wybiera jednego spośród 20 najlepszych. Jedno żądanie mieści do 10 000 kandydatów. basal-navigate wyszukuje, przechodząc drzewo dokumentów poziom po poziomie: tematy, strony, fragmenty. Nie potrzebuje indeksu.

Z basal-omni-1.0-max na jednym H100, w porównaniu z gęstym modelem embeddingów 8B: MultiDoc2Dial R@1 44,3 wobec 34,7 (+9,6), ConditionalQA Hit@1 63,5 wobec 44,3 (+19,2).

Ta trafność ma swoją cenę: czas. Przy jednym zapytaniu naraz wyszukiwanie trwa p50 3,0 s i p95 3,5 s na turę MultiDoc2Dial oraz p50 9,2 s i p95 18,4 s na pytanie ConditionalQA. Przy 8 równoległych klientach mediana wynosi 24 s i 64 s. Dlatego oba narzędzia są eksperymentalne. --split-at N pozwala przeszukać drzewa większe niż limit 10 000 kandydatów, ale nie przyspiesza: na Natural Questions (2,68 mln akapitów) to 51,6 s na pytanie. Szczegóły: NAVIGATE.md.

Uczciwie o ograniczeniach

  • Kaskada a najwolniejsze odpowiedzi. Kaskada mini → ultra obsługuje więcej żądań, ale przy 16 klientach jej p95 jest wyższe niż samego ultra.
  • basal-navigate jest wolny: sekundy na zapytanie przy jednym kliencie i dziesiątki sekund przy ośmiu.
  • Strix Halo i Apple M5 nie są sprawdzone na naszym sprzęcie. --mode strix utrzymuje społeczność. Na karcie Radeon 8060S jedno krótkie pytanie skraca się teraz z 390 do 161 ms, ale nie sprawdziliśmy tego na własnym sprzęcie. Ustawienia float32 dla pico na Apple M5 nie sprawdziliśmy jeszcze na tym układzie; na M4 Pro odpowiedzi są identyczne jak wcześniej.
  • Ultra w blokowym FP8 działa bez cache stanu do 2.5.1. --state-cache-gb jest wtedy pomijane z ostrzeżeniem przy starcie, a żądania są obsługiwane jak bez cache.
  • Blend i rezerwa LLM nie są skalibrowane. Przed automatyzacją dopasuj progi przez basal-calibrate. Odpowiedzi rezerwy są powtarzalne tylko z ustawieniami endpointu opisanymi w SERVING.md.
  • Rzadki HTTP 500 z cache stanu. Przy --mode fast i --state-cache-gb rzadki wyścig może zakończyć jedno żądanie błędem 500: w naszych testach około 1 na 8 000. Serwer działa dalej, a żądanie można powtórzyć. Problem istnieje od 2.3.1. --state-cache-lock on go usuwa, ale kosztuje: na H100 z basal-omni-1.0-max, przy 8 klientach i 8 740 żądaniach, 0 błędów zamiast 1, lecz 8,19 zamiast 8,88 żądań/s (-8%) i mediana 630 zamiast 577 ms.
  • Długie stany w ultra. To samo żądanie o długości 24–32 tys. tokenów, wysłane ponownie, dało bitowo identyczne prawdopodobieństwa w 95 na 100 przypadków, tak jak w 2.4.1. Mini: w 100 na 100.

Dziękujemy społeczności

Duża część tego wydania przyszła w pull requestach. Dziękujemy:

  • Miłosz Kucharski (@Butterski): mniej pracy CPU w kaskadzie CUDA (#44), cache torch.compile, który przetrwa restart (#47; pierwszy start basal-2.0-mini po restarcie 417 → 238 s na RTX 4060 Ti) i --compile-scope layer (#49).
  • Paweł Kiszczak (@pawelkiszczak): polecenia deweloperskie i kontrole przed commitem (#46), adaptery LoRA (#52), kody błędów (#62), progi i kontekst w /v1/models (#64), wybór modelu w MCP (#66), klucze API (#68), kalibracja na snapshotach Hugging Face (#71) oraz basal-viz z facts i dla Gemma 4 (#73, #75).
  • Marcin Brzeżański (@marcinbrzezanski): nowe metryki (#48) i cache stanu, który zachowuje rozgałęzienia historii (#56). W historiach agentów ponownie wykorzystuje 1,6–1,7× więcej tokenów prefiksu.
  • Dariusz Koryto (@dkoryto): CI na Linuksie i macOS (#53), klient Python (#58), poprawka nazw z :: (#59) i basal-interpolate (#60).
  • Grzegorz Stark (@gstark0): cache stanu na Apple Silicon (#50).
  • Michał Kardyś (@kardysm): --mode strix bez czekania na kompilację jąder przy pierwszych żądaniach (#51).
  • Grzegorz Brzezinka (@agentGreg): testy na Apple M5 (#76) i basal-viz na silniku MLX (#78).

Miłoszowi dziękujemy też za wcześniejszy #17: dzięki niemu język stanu jest wykrywany raz na żądanie, a nie przy każdym pytaniu.

Co przyniesie 2.5.1

  • Certyfikowane progi dla gotowych zestawów pytań; dziś są dobrane ręcznie.
  • int8 na Apple M5.
  • Cache stanu w blokowym FP8 (--mode fp8).
  • Poprawkę rzadkiego HTTP 500 z --mode fast i cache stanu, bez spadku przepustowości, który daje --state-cache-lock.

Jak zacząć

GPU NVIDIA, z uv w nowym środowisku:

TERMINAL
uv venv --python 3.12 --allow-existing ~/basal-env && source ~/basal-env/bin/activate
uv pip install torch==2.11.0 --index-url https://download.pytorch.org/whl/cu128
uv pip install "basal[fp8] @ https://github.com/rkinas/basal/archive/refs/tags/v2.5.0.tar.gz"
basal-serve --model Remek/basal-2.0-4.5B --mode fast --template-prefix off --port 8000

Apple Silicon:

TERMINAL
uv venv --python 3.12 --allow-existing ~/basal-mlx && source ~/basal-mlx/bin/activate
uv pip install "basal[mlx] @ https://github.com/rkinas/basal/archive/refs/tags/v2.5.0.tar.gz"
basal-serve --model Remek/basal-2.0-4.5B-MLX-8bit --mode mlx --port 8000

Dwa GPU pod jednym portem, ze stroną na żywo routera i replik:

TERMINAL
basal-router --gpus 0,1 --port 8000 --ui on -- --model Remek/basal-omni-1.0-mini --mode fast --ui on

Aby dostać zbiór odpowiedzi i sygnał wstrzymania, dodaj do pytania pola coverage i max_error:

POST /v1/systemone · application/json
{
  "state": "Zgłoszenie klienta: zamówiona szafa dotarła z pękniętymi drzwiami. Proszę o wymianę drzwi do piątku.",
  "questions": {
    "zespol": {
      "type": "choice",
      "instructions": "Który zespół powinien obsłużyć sprawę?",
      "criteria": {
        "serwis": "Naprawy i wymiana elementów",
        "sprzedaz": "Nowe zamówienia",
        "ksiegowosc": "Rozliczenia"
      },
      "coverage": 0.9,
      "max_error": 0.01
    }
  }
}

To przykładowe żądanie; jego wyników nie symulujemy. Poziomy pokrycia pochodzą z kalibracji dopasowanej przez basal-calibrate --conformal. Gdy plik kalibracji nie ma danego poziomu, zbiór obejmuje wszystkie opcje, a pole coverage_note wyjaśnia dlaczego.

Po aktualizacji domyślne odpowiedzi się nie zmienią. Każdą nową opcję warto jednak najpierw zmierzyć na własnych żądaniach, zanim trafi na produkcję.

Pełny opis wydania i pomiary → · Serwer inferencji, REST i MCP →