SSD Nodes Learn 🎉 VPS od $5.50/mies.
Przewodniki Matt ConnorAutor: Matt Connor · Zaktualizowano 2026-08-13

Jak samodzielnie hostować bramkę LiteLLM na VPS

Uruchom własny punkt końcowy zgodny z API OpenAI dla wielu dostawców LLM. Zarządzaj wirtualnymi kluczami, budżetami oraz mechanizmami fallback w jednym centralnym miejscu na serwerze.

Czym jest samodzielnie hostowana bramka LLM

LiteLLM to otwartoźródłowa bramka LLM hostowana we własnym zakresie: jeden punkt końcowy HTTP, z którym łączą się wszystkie aplikacje, a który następnie przekazuje każde żądanie do odpowiedniego dostawcy. LLM oznacza duży model językowy (ang. large language model). Bramka obsługuje API (ang. application programming interface) czatu OpenAI, dzięki czemu każda biblioteka kliencka współpracująca z OpenAI zadziała po wprowadzeniu dwóch zmian: adresu bazowego URL oraz klucza.

Ta jedna warstwa pośrednicząca jest kluczowa. Aplikacje przestają przechowywać dane uwierzytelniające dostawców. Zmiana modelu sprowadza się do jednej linii w pliku konfiguracyjnym na serwerze, zamiast modyfikacji kodu w pięciu usługach. Ponieważ każde wywołanie przechodzi przez jeden proces, istnieje centralne miejsce do zarządzania budżetem oraz prowadzenia rejestru wydatków.

Oto co otrzymujesz po uruchomieniu rozwiązania:

  • Jeden punkt końcowy. Aplikacje kierują żądania do https://gateway.example.com/v1 i wskazują zdefiniowaną przez Ciebie nazwę modelu, taką jak bulk lub strong.
  • Wirtualne klucze. Każda aplikacja otrzymuje własny klucz z przypisaną listą dozwolonych modeli oraz limitem wydatków. Możesz unieważnić jeden klucz bez wpływu na pozostałe.
  • Mechanizmy awaryjne (fallbacks). Nieudane wywołanie lub zbyt długi prompt są automatycznie ponawiane z użyciem innego modelu.
  • Rejestr logów. Każde żądanie zapisuje wiersz z informacją o koszcie, co pozwala jednoznacznie określić, która aplikacja wygenerowała dany wydatek.

Dlaczego warto samodzielnie uruchomić bramę

Zarządzany router ma taką samą formę, lecz w każdym żądaniu pośredniczy proces zewnętrznego dostawcy. Samodzielne uruchomienie usługi pozwala zachować klucze dostawcy oraz tekst promptów na serwerze, nad którym sprawuje się pełną kontrolę. Wiąże się to z realnymi kosztami: użytkownik staje się administratorem komponentu, od którego zależą wszystkie aplikacje. Ostatnia sekcja tego przewodnika poświęcona jest właśnie tym kosztom, ponieważ jest to aspekt pomijany w większości opracowań.

Wymagania

  • VPS (virtual private server) z systemem Ubuntu 24.04 oraz zainstalowanym Docker i wtyczką Compose.
  • Nazwa domeny wskazująca na serwer, jeśli maszyny zewnętrzne mają łączyć się z bramą przez TLS (transport layer security).
  • Przynajmniej jeden klucz API dostawcy.

Brama nie przeprowadza wnioskowania. Przekazuje żądania i przesyła odpowiedzi, więc obciążenie procesora zależy od liczby żądań, a nie od rozmiaru modelu. Serwer z 1 vCPU bez problemu obsłuży kilka wewnętrznych aplikacji. Zasobem, który rośnie, jest baza danych, ponieważ brama zapisuje wiersz z informacją o koszcie dla każdego żądania.

Przygotowanie pliku config.yaml

Plik konfiguracyjny określa, o jakie modele może wnioskować klient. Istotne są cztery sekcje najwyższego poziomu: model_list, litellm_settings, router_settings oraz general_settings.

model_list:
  - model_name: bulk
    litellm_params:
      model: anthropic/claude-haiku-4-5
      api_key: os.environ/ANTHROPIC_API_KEY
  - model_name: strong
    litellm_params:
      model: anthropic/claude-sonnet-5
      api_key: os.environ/ANTHROPIC_API_KEY
  - model_name: strong
    litellm_params:
      model: openai/gpt-5.5
      api_key: os.environ/OPENAI_API_KEY

litellm_settings:
  num_retries: 2
  request_timeout: 120
  allowed_fails: 3
  cooldown_time: 30
  json_logs: true
  set_verbose: false

router_settings:
  fallbacks: [{"bulk": ["strong"]}]
  context_window_fallbacks: [{"bulk": ["strong"]}]

general_settings:
  background_health_checks: true
  health_check_interval: 300

model_name to nazwa przesyłana przez klientów. litellm_params.model to właściwy model, zapisany jako provider/model. Modele należy nazywać zgodnie z ich przeznaczeniem, a nie nazwą dostawcy. Aplikacja żądająca modelu bulk będzie działać poprawnie, nawet jeśli w przyszłym miesiącu podjęta zostanie decyzja o zmianie bulk na inny model.

api_key: os.environ/ANTHROPIC_API_KEY instruuje LiteLLM, aby odczytał tę zmienną w czasie wykonywania. Klucz w postaci jawnej nigdy nie pojawia się w pliku, co jest istotne, ponieważ config.yaml jest plikiem podlegającym commitowaniu.

Dwa wpisy współdzielą nazwę strong celowo. Gdy więcej niż jedno wdrożenie posiada ten sam model_name, router traktuje je jako zamienne i podejmuje próbę użycia drugiego w przypadku awarii pierwszego. W ten sposób strong zachowuje ciągłość działania, gdy jeden z dostawców przechodzi chwilowe problemy.

num_retries: 2 ponawia próbę użycia tego samego wdrożenia w przypadku błędu kwalifikującego się do ponowienia. Mechanizm fallback uruchamia się dopiero po wyczerpaniu tych prób. allowed_fails: 3 wraz z cooldown_time: 30 wyłącza wdrożenie z rotacji na 30 sekund po wystąpieniu 3 błędów, dzięki czemu dostawca zwracający błędy 500 przestaje być odpytywany przy każdym żądaniu.

fallbacks oraz context_window_fallbacks posiadają różne wyzwalacze, przy czym drugi z nich jest przydatny, choć często pomijany.

  • fallbacks uruchamia się, gdy zawiedzie wywołanie podstawowe.
  • context_window_fallbacks uruchamia się, gdy dostawca odrzuci żądanie z powodu przekroczenia okna kontekstowego modelu, dzięki czemu zbyt długi prompt trafia do modelu dysponującego odpowiednią przestrzenią, zamiast zwracać błąd do wywołującego.

Dostępne jest również content_policy_fallbacks, stosowane w przypadku odmowy dostawcy ze względu na politykę treści. Należy je konfigurować tylko wtedy, gdy istnieje sensowne miejsce do przekierowania takich wywołań.

Wdrożenie LiteLLM na serwerze VPS przy użyciu Docker Compose

Utwórz katalog zawierający trzy pliki: config.yaml, docker-compose.yml oraz .env. Oficjalny przewodnik szybkiego startu pobiera obraz z tagiem latest. Przypnij konkretny tag wydania, aby w miesiącu docker compose up -d uzyskać tę samą wersję bramy, co obecnie. Dzięki temu wycofanie zmian sprowadza się do jednej linii.

services:
  litellm:
    image: ghcr.io/berriai/litellm:v1.95.0
    restart: unless-stopped
    command: ["--config", "/app/config.yaml", "--num_workers", "1"]
    ports:
      - "127.0.0.1:4000:4000"
    volumes:
      - ./config.yaml:/app/config.yaml:ro
    env_file: .env
    depends_on:
      db:
        condition: service_healthy

  db:
    image: postgres:16
    restart: unless-stopped
    environment:
      POSTGRES_USER: litellm
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?set POSTGRES_PASSWORD in .env}
      POSTGRES_DB: litellm
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U litellm"]
      interval: 5s
      timeout: 5s
      retries: 10
    volumes:
      - postgres_data:/var/lib/postgresql/data

volumes:
  postgres_data:

Compose odczytuje plik .env w tym miejscu dwukrotnie. Raz w celu podstawienia ${POSTGRES_PASSWORD} wewnątrz samego pliku compose, a drugi raz poprzez env_file, aby przekazać wszystkie zmienne do kontenera.

Wersja v1.95.0 była aktualnym wydaniem w sierpniu 2026 roku. Sprawdź stronę wydań projektu i przypnij wersję aktualną w momencie wdrażania. Każde wydanie posiada podpis, co pozwala zweryfikować obraz przed jego użyciem:

cosign verify --key https://raw.githubusercontent.com/BerriAI/litellm/v1.95.0/cosign.pub ghcr.io/berriai/litellm:v1.95.0

Linia portu to 127.0.0.1:4000:4000, co publikuje port wyłącznie na interfejsie loopback. Zapisanie 4000:4000 sprawi, że brama będzie dostępna z całego Internetu, ponieważ Docker dodaje własne reguły w łańcuchu FORWARD w iptables, które są przetwarzane przed regułami ufw, więc ufw deny 4000 nie zablokuje dostępu. Jest to najczęstszy sposób, w jaki samodzielnie hostowana brama staje się publicznie dostępna: zobacz jak Docker publikuje port kontenera z pominięciem ufw. Ruch z zewnątrz powinien docierać przez reverse proxy.

Przechowywanie kluczy dostawców poza obrazem

Plik .env zawiera wszystkie sekrety. Jest on przekazywany jako zmienne środowiskowe w czasie uruchomienia, dzięki czemu nigdy nie jest trwale zapisywany w obrazie ani przesyłany do repozytorium.

LITELLM_MASTER_KEY=sk-REPLACE_ME
LITELLM_SALT_KEY=sk-REPLACE_ME_TOO
POSTGRES_PASSWORD=REPLACE_ME_AS_WELL
DATABASE_URL=postgresql://litellm:REPLACE_ME_AS_WELL@db:5432/litellm
STORE_MODEL_IN_DB=True
LITELLM_MODE=PRODUCTION
LITELLM_LOG=ERROR
ANTHROPIC_API_KEY=sk-ant-...
OPENAI_API_KEY=sk-proj-...

Wygeneruj dwa klucze LiteLLM przy użyciu losowych wartości, a następnie zabezpiecz plik:

printf 'sk-%s\n' "$(openssl rand -hex 32)"
chmod 600 .env

LITELLM_MASTER_KEY to poświadczenie administratora. Uwierzytelnia ono API zarządzania i stanowi hasło do interfejsu Admin UI pod adresem /ui. Żadna aplikacja nie powinna go przechowywać.

LITELLM_SALT_KEY szyfruje poświadczenia dostawców przechowywane w bazie danych. Ustaw tę wartość raz i nie zmieniaj jej. Późniejsza zmiana spowoduje, że zapisane poświadczenia staną się niemożliwe do odszyfrowania, co sprawi, że bramka uruchomi się poprawnie, ale każde wywołanie dostawcy zakończy się błędem uwierzytelnienia.

STORE_MODEL_IN_DB=True umożliwia dodawanie i edycję modeli z poziomu interfejsu Admin UI bez modyfikowania pliku config.yaml. Jest to wygodne, ale dzieli źródło prawdy na dwie części. Zdecyduj, które z nich jest nadrzędne i zapisz tę decyzję obok konfiguracji.

Logika, która nakazuje trzymać klucze poza plikiem konfiguracyjnym, jest taka sama, jak w przypadku narzędzi przekazywanych agentowi. Przechowywanie sekretów dostawców poza agentami AI omawia ten wzorzec, a pliki env i sekrety w Docker Compose wyjaśnia kwestie techniczne.

Uruchom usługę i monitoruj pierwsze uruchomienie:

docker compose up -d
docker compose logs -f litellm

Weryfikacja poprawności działania

Istnieją dwie sondy niewymagające uwierzytelnienia oraz jedna wymagająca uwierzytelnienia; każda z nich kończy się niepowodzeniem z innego powodu.

curl -s http://127.0.0.1:4000/health/liveliness
curl -s http://127.0.0.1:4000/health/readiness

/health/liveliness nie wymaga uwierzytelnienia i odpowiada "I'm alive!", gdy proces jest uruchomiony. /health/readiness również nie wymaga uwierzytelnienia. Zwraca obiekt JSON z polami "status": "healthy" oraz db lub kod 503, gdy baza danych jest nieosiągalna. Należy skierować monitoring na readiness, ponieważ liveliness pozostaje zielony w bramie, która nie jest w stanie odnaleźć pojedynczego klucza wirtualnego.

Uwierzytelniona kontrola to ta, która komunikuje się z dostawcami:

curl -s http://127.0.0.1:4000/health \
  -H "Authorization: Bearer $LITELLM_MASTER_KEY"

Odpowiada ona tablicami healthy_endpoints oraz unhealthy_endpoints. Model znajdujący się w unhealthy_endpoints z błędem uwierzytelnienia oznacza, że klucz dostawcy w .env jest błędny lub go brakuje; jest to awaria, którą należy teraz zidentyfikować. Ponieważ background_health_checks: true jest ustawione, proxy wykonuje te sondy samodzielnie co health_check_interval sekund, a /health zwraca ostatni wynik, więc odpytywanie go nie wysyła za każdym razem żądania testowego do dostawców.

Wirtualne klucze i budżety dla poszczególnych kluczy

Każda aplikacja otrzymuje własny klucz, wygenerowany na podstawie klucza głównego.

curl -s http://127.0.0.1:4000/key/generate \
  -H "Authorization: Bearer $LITELLM_MASTER_KEY" \
  -H 'Content-Type: application/json' \
  -d '{
    "key_alias": "nightly-summariser",
    "models": ["bulk"],
    "max_budget": 5,
    "budget_duration": "30d",
    "rpm_limit": 60,
    "tpm_limit": 200000
  }'

Odpowiedź zawiera pole key rozpoczynające się od sk-. Ten ciąg znaków jest tym, co otrzymuje aplikacja i jest to jedyna informacja, do której aplikacja ma dostęp.

  • models to lista dozwolonych operacji, o które może wnioskować dany klucz. Powyższy klucz może żądać wyłącznie bulk i niczego więcej.
  • max_budget: 5 z wartością budget_duration: "30d" oznacza limit pięciu dolarów amerykańskich w okresie 30 dni, po przekroczeniu którego klucz przestaje działać.
  • rpm_limit oraz tpm_limit ograniczają liczbę żądań na minutę oraz liczbę tokenów na minutę wyłącznie dla tego klucza.
  • key_alias to identyfikator, który pozwoli rozpoznać użycie w dzienniku wydatków sześć tygodni później. Należy go zawsze ustawiać.

Po wyczerpaniu budżetu wywołanie kończy się błędem HTTP 401 z treścią o następującej strukturze:

ExceededBudget: Current spend for token: 7.2e-05; Max Budget for Token: 2e-07

Kod stanu jest źródłem nieporozumień. Biblioteka kliencka zgłasza 401 jako problem z uwierzytelnianiem, więc programista analizujący ślad stosu (stack trace) zaczyna sprawdzać poprawność klucza. Należy logować treść odpowiedzi obok kodu stanu, w przeciwnym razie wyczerpanie budżetu będzie za każdym razem wyglądać jak uszkodzone poświadczenie.

Klucze można sprawdzać i modyfikować za pomocą tego samego API zarządzania:

curl -s "http://127.0.0.1:4000/key/info?key=sk-..." \
  -H "Authorization: Bearer $LITELLM_MASTER_KEY"

curl -s -X POST http://127.0.0.1:4000/key/update \
  -H "Authorization: Bearer $LITELLM_MASTER_KEY" \
  -H 'Content-Type: application/json' \
  -d '{"key": "sk-...", "max_budget": 25}'

Budżet egzekwowany na poziomie bramy (gateway) działa nawet wtedy, gdy awarii ulegnie sam agent, dlatego stanowi on fundament kontroli kosztów agentów AI na serwerze VPS.

Przekazywanie zadań masowych do taniego modelu

Skieruj klienta na bramkę. Podaj Base URL, klucz oraz nazwę modelu:

curl -s http://127.0.0.1:4000/v1/chat/completions \
  -H "Authorization: Bearer sk-<the virtual key>" \
  -H 'Content-Type: application/json' \
  -d '{
    "model": "bulk",
    "messages": [{"role": "user", "content": "Say hello in five words."}]
  }'

Każda biblioteka kliencka OpenAI zachowuje się w ten sam sposób: ustaw base_url na https://gateway.example.com/v1, a api_key na klucz wirtualny.

Polityka routingu zdefiniowana w config.yaml jest teraz stosowana bez wiedzy klienta. Żądanie dla bulk trafia do taniego modelu. Jeśli to wywołanie zakończy się niepowodzeniem po wykonaniu ponownych prób, żądanie zostanie ponowione względem strong. Jeśli prompt jest zbyt długi dla bulk, context_window_fallbacks prześle go do strong zamiast zwracać błąd. Zadania masowe, takie jak klasyfikacja czy podsumowywanie zaległości, są domyślnie realizowane tanio, a wyższe koszty generują tylko trudne zapytania.

W tym miejscu bramka zyskuje również na znaczeniu w przypadku agentów korzystających z narzędzi. Serwer MCP (model context protocol) na tym samym VPS oraz sterujący nim agent mogą wskazywać na ten sam punkt końcowy, dzięki czemu model obsługujący oba te elementy można zmienić bez konieczności ponownego wdrażania któregokolwiek z nich.

Jak rozpoznać, że nastąpiło przełączenie na tryb awaryjny (fallback)?

Jest to tryb awarii generujący koszty, ponieważ z zewnątrz wszystko wydaje się sprawne. Udany fallback zwraca kod HTTP 200 wraz ze standardową treścią odpowiedzi. Tani model może być niedostępny przez cały dzień, każde wywołanie jest cicho obsługiwane przez model droższy, a pierwszym dowodem na ten stan rzeczy jest faktura.

Dowody istnieją w nagłówkach odpowiedzi. Należy o nie zapytać:

curl -s -D - -o /dev/null http://127.0.0.1:4000/v1/chat/completions \
  -H "Authorization: Bearer sk-<the virtual key>" \
  -H 'Content-Type: application/json' \
  -d '{"model":"bulk","messages":[{"role":"user","content":"ping"}]}' \
  | grep -i '^x-litellm'
  • x-litellm-model-group to model, o który poprosił klient. x-litellm-model-id to wdrożenie, które udzieliło odpowiedzi. Gdy te dwie wartości są różne, oznacza to, że nastąpił fallback.
  • x-litellm-attempted-fallbacks oraz x-litellm-attempted-retries zliczają takie zdarzenia. W przypadku poprawnego wywołania obie wartości wynoszą 0.
  • x-litellm-response-cost to koszt pojedynczego wywołania w dolarach amerykańskich.
  • x-litellm-call-id to identyfikator służący do odnalezienia tego samego wywołania w logach.

Należy rejestrować x-litellm-attempted-fallbacks przy każdym żądaniu i ustawić alert, gdy wartość ta przestanie wynosić 0. Ta jedna liczba stanowi różnicę między działającą polityką routingu a polityką, która po cichu zmieniła się w „zawsze używaj drogiego modelu”.

Pełną wersją tego rozwiązania jest tracing, który wymaga osobnej konfiguracji: self-hosted Langfuse do śledzenia wywołań agenta. LiteLLM dostarcza odpowiedni callback, więc jego podłączenie to dwie linie kodu oraz dane uwierzytelniające.

litellm_settings:
  success_callback: ["langfuse"]
  failure_callback: ["langfuse"]
LANGFUSE_PUBLIC_KEY=pk-lf-...
LANGFUSE_SECRET_KEY=sk-lf-...
LANGFUSE_HOST=https://langfuse.example.com

Należy ustawić zarówno failure_callback, jak i success_callback. W przeciwnym razie zachowane zostaną jedynie ślady wywołań, w których nie wystąpiły żadne błędy. Niezależnie od powyższego, LiteLLM zapisuje wiersz z informacją o koszcie każdego żądania w bazie Postgres, a interfejs Admin UI pod adresem /ui odczytuje tę tabelę. Rozmiar tabeli rośnie wraz z ruchem, dlatego należy monitorować zajętość miejsca na dysku.

Umieszczenie bramy za odwrotnym proxy

Żaden ruch z zewnątrz nie powinien docierać do portu 4000. Należy dokonać terminacji TLS w nginx lub Caddy i przekierować ruch na adres loopback.

location / {
    proxy_pass http://127.0.0.1:4000;
    proxy_http_version 1.1;
    proxy_set_header Host $host;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
    proxy_buffering off;
    proxy_read_timeout 600s;
}

Dwie z poniższych linii są często pomijane przez użytkowników. proxy_buffering off jest istotne, ponieważ strumieniowe generowanie odpowiedzi to seria zdarzeń typu server-sent events. Przy włączonym buforowaniu nginx przetrzymuje fragmenty danych do momentu zakończenia odpowiedzi, przez co klient czeka w bezczynności, a następnie otrzymuje całość naraz. proxy_read_timeout 600s jest kluczowe, ponieważ długotrwałe generowanie odpowiedzi przekracza domyślny limit 60 sekund w nginx. W takim przypadku klient otrzymuje błąd 504, a w dzienniku błędów zapisywany jest komunikat upstream timed out (110: Connection timed out) while reading response header from upstream.

W kwestii certyfikatu, Certbot z Let's Encrypt na nginx stanowi najkrótszą drogę. Jeśli serwer obsługuje już kilka kontenerów, Traefik przed wieloma aplikacjami Compose pozwala zarządzać routingiem i certyfikatami w jednym miejscu.

Brama sieciowa stanowi teraz pojedynczy punkt awarii

Należy zachować realizm w ocenie wdrożonego rozwiązania. Każda posiadana aplikacja zależy teraz od jednego kontenera na jednym VPS. Gdy jest on niedostępny, żadne wywołanie modelu nie zostanie obsłużone, nawet jeśli dostawcy usług działają poprawnie. Wynikają z tego cztery kwestie.

  • Błędna konfiguracja powoduje awarię całości. restart: unless-stopped restartuje proces po awarii, co oznacza ciągłe restartowanie kontenera, który nie może przetworzyć pliku config.yaml. Po każdej zmianie konfiguracji należy wykonać docker compose logs litellm, a zmiany wprowadzać w czasie pozwalającym na ich monitorowanie.
  • Postgres znajduje się na ścieżce żądania. Wyszukiwanie kluczy wirtualnych oraz rejestrowanie zużycia korzystają z tej bazy. Kod /health/readiness zwracający 503 jest ostrzeżeniem, że brama działa, ale nie może wykonać żadnej z tych operacji.
  • Skalowanie odbywa się przez dodawanie instancji, a nie zwiększanie zasobów jednej. Zalecenia projektu wskazują na jeden proces roboczy na instancję (--num_workers 1) przy współdzieleniu bazy danych przez kilka instancji. Dwie małe bramy za load balancerem eliminują problem pojedynczego kontenera, jednak nie eliminują zależności od bazy danych.
  • Należy tworzyć kopie zapasowe danych, których nie można odtworzyć. Dotyczy to plików config.yaml oraz .env, wraz z pg_dump bazy danych. Utrata LITELLM_SALT_KEY sprawia, że zaszyfrowane poświadczenia dostawców wewnątrz zrzutu stają się bezużyteczne, dlatego plik env oraz zrzut bazy powinny być częścią tego samego zadania kopii zapasowej: kopie zapasowe restic do zewnętrznej pamięci masowej.

Aktualizacja polega na edycji tagu obrazu i uruchomieniu docker compose up -d. LiteLLM domyślnie wykonuje prisma migrate deploy przy starcie, więc nowy kontener przeprowadza migrację schematu bazy danych podczas pierwszego uruchomienia. Przed zmianą tagu należy wykonać zrzut bazy, ponieważ przywrócenie starego obrazu nie cofa migracji, która została już wykonana.

FAQ

Czy LiteLLM wprowadza zauważalne opóźnienie dla każdego wywołania?

Projekt deklaruje 8 ms na 95. percentylu przy 1000 żądań na sekundę, zgodnie z informacjami w pliku README z sierpnia 2026 roku. Należy traktować to jako wartość dostarczoną przez producenta. Wartością, która faktycznie wpływa na opóźnienie, jest odległość sieciowa między aplikacjami a bramą, ponieważ każde wywołanie wymaga dodatkowego pełnego cyklu komunikacji (round trip). Uruchom bramę w tym samym regionie co aplikacje, które z niej korzystają, a następnie zmierz narzut za pomocą nagłówka x-litellm-overhead-duration-ms na rzeczywistej odpowiedzi.

Dlaczego streaming przestał działać po umieszczeniu nginx przed usługą?

Ponieważ nginx domyślnie buforuje odpowiedzi z backendu, a strumieniowe generowanie odpowiedzi to seria zdarzeń typu server-sent events. Przy włączonym proxy_buffering, nginx gromadzi fragmenty i wysyła je dopiero po zakończeniu odpowiedzi, przez co klient czeka w bezczynności, a następnie otrzymuje całą odpowiedź naraz. Ustaw proxy_buffering off; w bloku location. Zwiększ proxy_read_timeout w tym samym bloku, ponieważ w przeciwnym razie długie generowanie przekroczy domyślny limit 60 sekund w nginx, co spowoduje błąd 504 u klienta.

Co się dzieje, gdy wirtualny klucz wyczerpie budżet?

Wywołanie kończy się błędem HTTP 401 z treścią w formacie ExceededBudget: Current spend for token: 7.2e-05; Max Budget for Token: 2e-07. Błąd 401 jest pułapką: biblioteka kliencka zgłasza go jako błąd uwierzytelniania, więc użytkownicy sprawdzają poprawność klucza zamiast czytać treść komunikatu. Rejestruj treść odpowiedzi wraz z kodem statusu. Potwierdź rzeczywisty stan klucza za pomocą /key/info?key=sk-... względem klucza głównego i zwiększ limit za pomocą /key/update, jeśli budżet został ustawiony zbyt nisko.

Czy brama może kierować ruch zarówno do modelu lokalnego, jak i hostowanego?

Tak, jest to kolejny wpis w model_list. Użyj prefiksu ollama_chat/ wraz z api_base, na przykład model: ollama_chat/llama3.1 obok api_base: http://ollama:11434. Z wnętrza kontenera localhost oznacza ten sam kontener, więc użyj nazwy usługi z pliku Compose lub adresu hosta w sieci Docker, nigdy 127.0.0.1. Uruchomienie modelu lokalnego to osobne zadanie: zobacz samodzielne hostowanie LLM za pomocą Ollama na VPS.