Usuń wszystkie modele routera llama.cpp bez restartowania

Darmowa pamięć VRAM bez zabijania llama-server.

Page content

Tryb routera w llama.cpp to jedna z najbardziej przydatnych zmian wprowadzonych do llama-server w ciągu ostatnich lat. Wreszcie daje lokalnym operatorom modeli LLM coś w rodzaju zarządzania modelami, do którego są przyzwyczajeni z Ollama, jednocześnie zachowując surową wydajność i kontrolę na niskim poziomie, która sprawia, że warto korzystać z llama.cpp w pierwszej kolejności.

Jest jednak jeden ostry problem: odciążenie wszystkiego nie jest pojedynczą magiczną przyciskiem w interfejsie HTTP API.

Router może wyświetlić listę modeli. Może załadować model. Może odładować model. Może usunąć najmniej ostatnio używany model po osiągnięciu limitu --models-max. Tym, czego obecnie nie dokumentuje jako pierwszoklasowego punktu końcowego, jest uniwersalne wywołanie „odładuj wszystkie modele teraz”.

laptop and desktop with llms

To nie jest prawdziwą przeszkodą. Poprawny wzorzec jest prosty, jawny i skryptowalny:

  1. Zapytaj router, które modele istnieją.
  2. Odfiltruj modele, których status to loaded (załadowany).
  3. Wywołaj /models/unload dla każdego załadowanego modelu.

To podejście polecam w poważnych przepływach pracy z lokalnymi LLM. Jest nudne, widoczne i łatwe do debugowania. To dokładnie to, czego potrzebujesz, gdy twoim celem jest zwolnienie pamięci VRAM bez restartowania całej usługi wnioskowania.

Co tak naprawdę robi tryb routera w llama.cpp

W klasycznym użyciu llama-server uruchamiasz jeden serwer z jednym modelem:

llama-server \
  --model ./models/qwen3-8b.gguf \
  --port 8080

Tryb routera zmienia tę logikę. Zamiast wiązać serwer z jednym plikiem GGUF, router staje się koordynatorem dla wielu modeli. Może odkrywać modele z pamięci podręcznej lub z katalogu modeli, ładować je na żądanie, kierować żądania do odpowiedniego modelu oraz odładowywać modele, gdy to konieczne.

Typowy start w trybie routera wygląda następująco:

llama-server \
  --models-dir ./models \
  --models-max 4 \
  --port 8080

Kluczową opcją tutaj jest --models-max. Kontroluje ona, ile modeli może być jednocześnie załadowanych. Jeśli limit zostanie osiągnięty, llama.cpp może usunąć najmniej ostatnio używany model. Jest to przydatne, ale nie zastępuje celowej operacji odładowania. Usunięcie LRU (Least Recently Used) jest reaktywne. Skrypt odładowania to operacyjna kontrola.

Moja zdeterminowana opinia: jeśli uruchamiasz lokalne modele do prawdziwej pracy, powinieneś traktować tryb routera jak menedżer procesów wnioskowania, a nie jak zabawkowy serwer czatu. Jawne operacje cyklu życia mają znaczenie.

Punkty końcowe zarządzania modelami, których potrzebujesz

Głównym punktem końcowym do odkrywania modeli jest:

curl -s http://localhost:8080/models | jq

Ten punkt końcowy zwraca modele znane routerowi oraz ich aktualny status cyklu życia. Dokładna struktura JSON może nieznacznie różnić się między wersjami, więc zbadaj własną odpowiedź przed napisaniem automatyzacji.

Typowa odpowiedź wygląda następująco:

{
  "data": [
    {
      "id": "qwen3-8b",
      "status": "loaded"
    },
    {
      "id": "llama-3.2-3b",
      "status": "unloaded"
    }
  ]
}

Aby odładować jeden model, wywołaj:

curl -s -X POST http://localhost:8080/models/unload \
  -H "Content-Type: application/json" \
  -d '{"model":"qwen3-8b"}' \
  | jq

To jest operacja pierwotna. Wszystko inne w tym artykule buduje się na niej.

Nie ma udokumentowanego punktu końcowego „odładuj wszystko”

To jest część, która sprawia problemy.

Możesz oczekiwać czegoś takiego:

curl -X POST http://localhost:8080/models/unload-all

Nie buduj na tym założeniu. Udokumentowana operacja dotyczy pojedynczego modelu. Przekazujesz identyfikator modelu do /models/unload, a llama.cpp odładowuje ten jeden model.

To niekoniecznie jest złym designem API. Operacja na pojedynczym modelu jest bezpieczniejsza. Zmusza wywołującego do decyzji, co powinno zostać odładowane. Unikaja również niespodziewanego zachowania w środowisku produkcyjnym, gdzie jedno żądanie administratora przypadkowo zabija wszystkie „ciepłe” modele używane przez innych klientów.

Dla stacji roboczej skrót „odładuj wszystko” byłby wygodny. Dla wieloużytkownikowej maszyny wnioskowania jawne pętle są lepsze.

Najpierw odładuj jeden model

Przed automatyzowaniem czegokolwiek, przetestuj dokładny identyfikator modelu, którego oczekuje router.

Najpierw wyświetl listę modeli:

curl -s http://localhost:8080/models | jq

Wybierz jeden załadowany model z wyniku, a następnie go odładuj:

curl -s -X POST http://localhost:8080/models/unload \
  -H "Content-Type: application/json" \
  -d '{"model":"qwen3-8b"}' \
  | jq

Sprawdź ponownie listę modeli:

curl -s http://localhost:8080/models | jq

Jeśli status modelu zmienił się na unloaded (odładowany), twój punkt końcowy, port i identyfikator modelu są poprawne.

Jeśli to nie działa, nie zgaduj. Zbadaj JSON. Aliasy routera, nazwy plików GGUF i identyfikatory modeli często nie są tym samym ciągiem znaków.

Odładuj wszystkie załadowane modele za pomocą curl i jq

Gdy odładowanie pojedynczego modelu działa, wzorzec „odładuj wszystko” to po prostu pętla powłoki.

Użyj tego, gdy odpowiedź z /models ma .data[].id i .data[].status:

curl -s http://localhost:8080/models \
| jq -r '.data[] | select(.status == "loaded") | .id' \
| while IFS= read -r model; do
    echo "Unloading: $model"
    curl -s -X POST http://localhost:8080/models/unload \
      -H "Content-Type: application/json" \
      -d "{\"model\":\"$model\"}" \
      | jq
  done

To jest cała sztuczka. Nie jest glamour, ale ma odpowiednią strukturę dla operacji administracyjnej:

  • Odładowuje tylko modele, które są faktycznie załadowane.
  • Drukuje, co robi.
  • Nieudaje się model po modelu, zamiast ukrywać wszystko pod jedną nieprzejrzystą akcją.
  • Działa z cron, hakami systemd, SSH lub zadaniami CI.

Skrypt wielokrotnego użytku do zastosowań produkcyjnych

Dla wszystkiego, co uruchamiasz więcej niż dwa razy, przestań wklewać jednolinijkowe komendy. Zapisz skrypt.

Stwórz llama-router-unload-all.sh:

#!/usr/bin/env bash
set -euo pipefail

LLAMA_SERVER_URL="${LLAMA_SERVER_URL:-http://localhost:8080}"

models_json="$(curl -fsS "$LLAMA_SERVER_URL/models")"

loaded_models="$(printf '%s' "$models_json" \
  | jq -r '.data[] | select(.status == "loaded") | .id')"

if [ -z "$loaded_models" ]; then
  echo "No loaded models found."
  exit 0
fi

printf '%s\n' "$loaded_models" | while IFS= read -r model; do
  [ -z "$model" ] && continue

  echo "Unloading: $model"

  curl -fsS -X POST "$LLAMA_SERVER_URL/models/unload" \
    -H "Content-Type: application/json" \
    -d "{\"model\":\"$model\"}" \
    | jq

done

echo "Done. Current model state:"
curl -fsS "$LLAMA_SERVER_URL/models" | jq

Spraw go wykonywalnym:

chmod +x llama-router-unload-all.sh

Uruchom go przeciwko domyślnemu lokalnemu serwerowi:

./llama-router-unload-all.sh

Uruchom go przeciwko innemu hostowi:

LLAMA_SERVER_URL=http://192.168.1.50:8080 ./llama-router-unload-all.sh

To jest wersja, którą faktycznie zachowałbym w katalogu narzędzi. Używa curl -f, więc błędy HTTP kończą skrypt, a także pozwala na nadpisanie adresu URL serwera bez edycji pliku.

Dostosowanie skryptu do twojej struktury JSON

Nie zakładaj oślepnie, że każda kompilacja llama.cpp zwraca dokładnie te same pola na zawsze. Tryb routera wciąż się rozwija, a twoja kompilacja może eksponować nieco inną strukturę JSON.

Zacznij od zbadania odpowiedzi:

curl -s http://localhost:8080/models | jq

Skrypt używa tego filtra:

jq -r '.data[] | select(.status == "loaded") | .id'

Jeśli twój identyfikator modelu znajduje się w .name, zmień go na:

jq -r '.data[] | select(.status == "loaded") | .name'

Jeśli pole statusu używa innej wartości, dostosuj filtr odpowiednio. Liczy się zasada: wybierz załadowane modele, wyodrębnij identyfikator akceptowany przez /models/unload, a następnie wywołaj odładowanie dla każdego z nich.

Dlaczego modele mogą ładować się ponownie po ich odładowaniu

To jest najczęstsze źródło nieporozumień.

Tryb routera obsługuje ładowanie na żądanie. Jeśli klient wyśle żądanie uzupełnienia czatu dla modelu, który jest obecnie odładowany, router może załadować go ponownie automatycznie.

Oznacza to, że możliwa jest następująca sekwencja:

  1. Odładowujesz każdy model.
  2. Open WebUI, skrypt testowy lub agent wysyła żądanie.
  3. llama.cpp ponownie ładuje żądany model.
  4. Myślisz, że odładowanie nie powiodło się, ale tak nie jest.

Rozwiązanie jest operacyjne, a nie techniczne. Najpierw zatrzymaj ruch klientów, jeśli twoim celem jest utrzymanie swobodnej pamięci VRAM.

Na przykład:

  • Zatrzymaj skrypty benchmarkowe.
  • Wstrzymaj agentów i zadania cron.
  • Zamknij lub odłącz sesje Open WebUI.
  • Wyłącz checki zdrowia, które przypadkowo wykonują rzeczywiste żądania modeli.

Odładowanie nie jest zaporem sieciowym. Jeśli klienci wciąż proszą o modele, tryb routera wykonuje swoją pracę, obsługując je.

Open WebUI i przycisk Eject

Open WebUI może integrować się z obsługą odładowywania modeli w llama.cpp. Gdy dostawca jest skonfigurowany jako llama.cpp, Open WebUI może wyświetlać stan załadowanych modeli i udostępniać akcję Eject (Wysunięcie) dla administratorów.

W tle ta akcja wywołuje własne API odładowywania Open WebUI, które następnie wywołuje punkt końcowy /models/unload llama.cpp na skonfigurowanym połączeniu.

To jest miłe do ręcznej operacji, ale nadal zachowałbym skrypt powłoki. Przycisk w interfejsie jest wygodny. Skrypt jest audytowalny, powtarzalny i przydatny na maszynie bez interfejsu graficznego o 2 rano.

Kiedy używać odładowania wszystkiego

Odładowanie każdego załadowanego modelu jest przydatne, gdy chcesz:

  • Zwolnić pamięć GPU przed uruchomieniem większego modelu.
  • Zresetować maszynę deweloperską bez restartowania llama-server.
  • Przygotować się do uruchomienia benchmarku z czystym stanem pamięci.
  • Odstawić lokalne obciążenia wnioskowania przed konserwacją.
  • Odzyskać kontrolę po bałaganistej sesji, w której zbyt wiele modeli zostało „rozgrzanych”.

Nie jest to odpowiednie narzędzie, gdy aktywni użytkownicy polegają na „ciepłych” modelach. W takim przypadku dostosuj --models-max, używaj celowego routingu i pozwól, by usunięcie LRU wykonało część pracy. Jeśli potrzebujesz smarter timeout-based unloading with per-model lifecycle control, llama-swap to proxy stworzone specjalnie do tego celu, które nakłada dokładnie te funkcje na dowolną konfigurację llama-server.

Moja zasada jest prosta: używaj LRU przy normalnym obciążeniu, a jawne odładowanie przy intencji operatora.

Rozwiązywanie problemów

Punkt końcowy modeli zwraca 404

Może nie uruchamiasz kompilacji obsługującej routera, lub wywołujesz błędny port.

Sprawdź proces serwera i dostępne opcje:

llama-server --help | grep -i models

Następnie przetestuj oba punkty końcowe:

curl -s http://localhost:8080/models | jq
curl -s http://localhost:8080/v1/models | jq

Punkt końcowy /v1/models to lista modeli zgodna z OpenAI. Punkt końcowy /models to punkt końcowy zarządzania modelami routera. Są ze sobą powiązane, ale nie są tym samym.

jq nie jest zainstalowany

Zainstaluj go przed skryptowaniem parsowania JSON.

Na Ubuntu lub Debian:

sudo apt-get update
sudo apt-get install jq

Na macOS z Homebrew:

brew install jq

Wywołanie odładowania zwraca błąd

Większość niepowodzeń wynika z przekazywania błędnego identyfikatora modelu. Użyj dokładnie identyfikatora zwróconego przez /models, a nie nazwy pliku, która według ciebie powinna działać.

Sprawdź również, czy nazwa twojego modelu zawiera cudzysłowy, ukośniki lub spacje. Powyższy skrypt dobrze radzi sobie ze zwykłymi ciągami, ale nietypowe nazwy mogą wymagać bardziej ostrożnej konstrukcji JSON.

Dla maksymalnego bezpieczeństwa możesz zbudować ciało POST za pomocą jq:

jq -n --arg model "$model" '{model: $model}'

Bardziej defensywna pętla odładowania użyłaby tego ciała zamiast ręcznie uciekanego JSON.

VRAM nie jest zwalniana natychmiast

Najpierw potwierdź zmianę statusu modelu. Następnie sprawdź, czy inne żądanie go ponownie załadowało. Pamiętaj również, że narzędzia do monitorowania pamięci GPU mogą opóźniać się lub raportować zachowanie alokatora, a nie natychmiastową intencję na poziomie aplikacji.

Praktyczny test jest prosty: zatrzymaj ruch, odładuj modele, wypisz status modeli, a następnie zbadaj pamięć GPU. W przypadku zmierzonych użycia VRAM dla różnych rozmiarów modeli i okien kontekstowych w llama.cpp, testy wydajności llama.cpp dla 16 GB VRAM dają konkretne liczby do weryfikacji.

Bezpieczniejsza wersja ciała JSON

Jeśli twoje identyfikatory modeli zawierają nietypowe znaki, użyj jq do wygenerowania ciała żądania JSON:

curl -s http://localhost:8080/models \
| jq -r '.data[] | select(.status == "loaded") | .id' \
| while IFS= read -r model; do
    echo "Unloading: $model"
    body="$(jq -n --arg model "$model" '{model: $model}')"
    curl -s -X POST http://localhost:8080/models/unload \
      -H "Content-Type: application/json" \
      -d "$body" \
      | jq
  done

To jest wersja, której należy użyć, jeśli twoje modele są nazwane identyfikatorami w stylu repozytorium, niestandardowymi aliasami lub ścieżkami.

Podsumowanie

Tryb routera w llama.cpp to duży krok naprzód w lokalnych operacjach LLM. Daje ci dynamiczne ładowanie, przełączanie modeli i świadomą pamięci ewikację, bez rezygnacji z bezpośredności llama-server.

Ale nie czekaj na idealny punkt końcowy „odładuj wszystko”. Czyste rozwiązanie już istnieje: wypisz załadowane modele i odładuj je po jednym.

Ten wzorzec jest jawny. Jest skryptowalny. Działa przez SSH. Dobrze współgra z Open WebUI. I co najważniejsze, zwalnia VRAM bez restartowania routera.

Dla lokalnej infrastruktury AI to dokładnie ten rodzaj nudnej powierzchni kontrolnej, której potrzebujesz.

Subskrybuj

Otrzymuj nowe wpisy o systemach, infrastrukturze i inżynierii AI.