LiteLLM proxy to lokalny serwer, który przyjmuje zapytania w formacie OpenAI i przekazuje je do wybranego modelu: lokalnego (Ollama) albo chmurowego (OpenAI). Definiujesz aliasy modeli w pliku YAML, a aplikacja zna tylko alias i adres bramki, więc przełączenie z chmury na model lokalny to zmiana jednej wartości w konfiguracji. Bez dodatkowych ustawień LiteLLM nie przechodzi sam na chmurę, gdy model lokalny zawiedzie.
To druga część cyklu o modelach lokalnych. W pierwszej części wywołałeś model w Ollamie klientem openai. Zostały w kodzie „na sztywno” adres, klucz i nazwa modelu. Tu to uporządkujemy.
Czym jest gateway do modeli i po co mi on?
Gateway (bramka) to pośrednik między aplikacją a dostawcami modeli. Aplikacja rozmawia z jednym adresem, a bramka decyduje, dokąd zapytanie trafi.
| Bez bramki | Z bramką |
|---|---|
| Aplikacja zna adres i format każdego dostawcy | Aplikacja zna jeden adres i jeden format (OpenAI) |
| Zmiana modelu = zmiana kodu | Zmiana modelu = zmiana konfiguracji |
| Klucze do chmury są w aplikacji | Klucze są tylko w bramce |
| Każdy dostawca ma własne różnice w API | LiteLLM wyrównuje te różnice |
Dla małego projektu to na początku dodatkowy proces, ale zyskujesz miejsce, w którym możesz porównać modele na tym samym kodzie, co zrobimy w trzeciej części.
Jak zainstalować LiteLLM proxy?
Wszystko robimy w jednym środowisku wirtualnym, bez kontenerów:
python -m venv .venv
source .venv/bin/activate
pip install "litellm[proxy]" openai pydantic-settings pyyaml
Nawias kwadratowy [proxy] doinstalowuje serwer. Sama biblioteka litellm pozwala wywoływać modele z kodu, ale bramka wymaga dodatku.
Jak skonfigurować aliasy modeli w YAML?
Bramkę konfiguruje plik litellm.yaml:
;yaml
model_list:
- model_name: local
litellm_params:
model: ollama_chat/llama3.2
api_base: http://localhost:11434
- model_name: cloud
litellm_params:
model: openai/gpt-4o-mini
api_key: os.environ/OPENAI_API_KEY
general_settings:
master_key: sk-lokalny-klucz
Co tu jest ważne:
model_nameto alias, który zobaczy aplikacja (local,cloud). Nazwa dostawcy nie wycieka do kodu.ollama_chat/w nazwie modelu mówi LiteLLM, że ma użyć wariantu czatu Ollamy. Po ukośniku wpisujesz nazwę zollama list.os.environ/OPENAI_API_KEYto składnia LiteLLM do odczytania klucza ze zmiennej środowiskowej, dzięki czemu klucz nie trafia do pliku.master_keyto klucz, którym aplikacja uwierzytelnia się w bramce. To nie jest klucz OpenAI. Na lokalnym komputerze możesz wpisać cokolwiek, ale nie używaj tu prawdziwych kluczy dostawców.
Uruchom bramkę w osobnym terminalu:
litellm --config litellm.yaml --port 4000
Sprawdź, jakie aliasy widzi aplikacja:
curl http://localhost:4000/v1/models -H "Authorization: Bearer sk-lokalny-klucz"
W odpowiedzi powinny być identyfikatory local i cloud. Zwróć uwagę, że alias cloud pojawia się na liście nawet bez klucza OpenAI, bo bramka sprawdza go dopiero przy użyciu.
Jak przełączać między chmurą a modelem lokalnym?
Aplikacja używa tego samego klienta openai, ale kieruje go na bramkę:
from openai import OpenAI
client = OpenAI(base_url="http://localhost:4000", api_key="sk-lokalny-klucz")
response = client.chat.completions.create(
model="local", # albo "cloud"
messages=[{"role": "user", "content": "Wyjaśnij, czym jest token."}],
)
print(response.choices[0].message.content)
Jedyna różnica względem chmury to wartość model. Żeby nie wpisywać jej w kodzie, wyciągniemy ustawienia do konfiguracji.
Jak trzymać ustawienia w pliku YAML i zmiennych środowiskowych?
Do tego służy pydantic-settings, rozszerzenie Pydantic do wczytywania konfiguracji (podstawy biblioteki Pydantic opisaliśmy w osobnym artykule). Plik config.yaml:
;yaml
gateway_url: http://localhost:4000
api_key: sk-lokalny-klucz
model: local
timeout_seconds: 300
max_chunk_tokens: 1500
I klasa ustawień w settings.py:
from pydantic_settings import (
BaseSettings,
PydanticBaseSettingsSource,
SettingsConfigDict,
YamlConfigSettingsSource,
)
class Settings(BaseSettings):
model_config = SettingsConfigDict(yaml_file="config.yaml", env_prefix="LLM_")
gateway_url: str
api_key: str
model: str = "local"
timeout_seconds: int = 300
max_chunk_tokens: int = 1500
@classmethod
def settings_customise_sources(
cls,
settings_cls: type[BaseSettings],
init_settings: PydanticBaseSettingsSource,
env_settings: PydanticBaseSettingsSource,
dotenv_settings: PydanticBaseSettingsSource,
file_secret_settings: PydanticBaseSettingsSource,
):
# kolejność = priorytet: argumenty > zmienne środowiskowe > config.yaml
return (init_settings, env_settings, YamlConfigSettingsSource(settings_cls))
Metoda settings_customise_sources decyduje, skąd i w jakiej kolejności są czytane wartości. U nas: argumenty przekazane do klasy, potem zmienne środowiskowe z prefiksem LLM_, na końcu plik YAML. Efekt:
python analyzer.py raport.txt # model z config.yaml (local)
LLM_MODEL=cloud python analyzer.py raport.txt # tylko ten jeden raz chmura
Typy w klasie (int, str) są walidowane, więc literówka w konfiguracji (np. timeout_seconds: abc) kończy się czytelnym błędem już przy starcie programu, a nie w środku zapytania.
Czy mogę podłączyć kilka płatnych modeli naraz?
Tak. LiteLLM obsługuje ponad sto dostawców, a w model_list możesz mieć dowolnie wiele wpisów od różnych firm. Każdy ma własny alias i własny klucz:
;yaml
model_list:
- model_name: local
litellm_params:
model: ollama_chat/llama3.2
api_base: http://localhost:11434
- model_name: tani
litellm_params:
model: openai/gpt-4o-mini
api_key: os.environ/OPENAI_API_KEY
- model_name: mocny
litellm_params:
model: anthropic/claude-sonnet-5-5
api_key: os.environ/ANTHROPIC_API_KEY
- model_name: gemini
litellm_params:
model: gemini/gemini-2.5-flash
api_key: os.environ/GEMINI_API_KEY
Prefiks przed ukośnikiem (openai/, anthropic/, gemini/) mówi bramce, do którego dostawcy ma wysłać zapytanie. Aplikacja nadal ma jednego klienta openai i jeden adres, a o modelu decyduje wartość model w konfiguracji. Nazwy modeli dostawcy zmieniają, więc przed użyciem sprawdź aktualne identyfikatory w ich dokumentacji.
Uwaga: jeśli kilka wpisów dostanie ten sam model_name, LiteLLM zacznie rozdzielać ruch między nimi (równoważenie obciążenia). To osobna funkcja. Gdy chcesz wybierać model świadomie albo porównywać modele, każdy wpis musi mieć własny alias.
Czy do każdego zadania potrzebuję najmocniejszego modelu?
Nie, i to jest jeden z najlepszych powodów, żeby mieć kilka aliasów. Najmocniejszy model jest najdroższy i zwykle najwolniejszy, a wiele zadań jest prostych. To jak koszenie trawnika przed domem kombajnem: da się, ale nie ma po co. Podziel zadania według trudności:
| Rodzaj zadania | Przykład | Jaki model |
|---|---|---|
| Proste, powtarzalne | Streszczenie jednego fragmentu, klasyfikacja, wyciągnięcie pola z tekstu | Tani lub lokalny |
| Średnie | Odpowiedź na pytanie z kontekstu, przeredagowanie tekstu | Średni |
| Złożone | Łączenie wielu źródeł, rozumowanie krok po kroku, ważna decyzja | Mocny |
W analizatorze dokumentów widać to wyraźnie. Streszczanie każdego fragmentu osobno to praca, którą wykona tani model, a to ona generuje większość wywołań. Tylko ostatni krok, czyli połączenie streszczeń w jedno, wymaga staranności. Wystarczy dodać do ustawień drugi alias:
class Settings(BaseSettings):
...
model: str = "tani" # fragmenty: dużo prostych wywołań
model_final: str = "mocny" # scalanie: jedno wywołanie, które liczy się najbardziej
W kodzie przekazujesz do ask(...) odpowiedni alias zależnie od kroku. Koszt spada, bo większość tokenów przechodzi przez tańszy model, a jakość tam, gdzie ma znaczenie, zostaje wysoka. Zasada jest prosta: zacznij od najtańszego modelu, który daje akceptowalny wynik na kilku Twoich prawdziwych przykładach, i podnoś poziom tylko tam, gdzie ten wynik jest za słaby. Ile dokładnie zaoszczędzisz, zależy od cenników i od Twoich danych, więc zmierz to w raporcie zużycia tokenów, a nie zakładaj z góry.
Dlaczego nie robię automatycznego przejścia na chmurę (fallbacku)?
LiteLLM potrafi skonfigurować automatyczne przejście na inny model, gdy pierwszy zawiedzie. W tym cyklu świadomie tego nie włączamy, z trzech powodów:
- Prywatność. Jeśli wybrałeś model lokalny, bo dane nie mogą opuścić komputera, ciche przekierowanie do chmury łamie tę decyzję dokładnie wtedy, gdy nikt nie patrzy.
- Koszt. Awaria lokalnego modelu zamieniłaby się w płatne wywołania, o których dowiesz się z faktury.
- Porównywalność. Wyniki testów mają mówić o jednym modelu. Mieszanie ich ukryłoby, który faktycznie odpowiedział.
Zamiast tego aplikacja ma zawieść głośno. Gdy nie ma połączenia z bramką, klient openai zgłasza APIConnectionError, a my zamieniamy go na zrozumiały komunikat:
from openai import APIConnectionError
try:
response = client.chat.completions.create(model=settings.model, messages=messages)
except APIConnectionError as error:
raise SystemExit(f"Brak połączenia z bramką {settings.gateway_url}: {error}")
Przełączenie na chmurę ma być Twoją decyzją (LLM_MODEL=cloud), a nie automatem. Dobrze jest też sprawdzić u siebie, co dokładnie zwraca bramka, gdy sama działa, a Ollama jest wyłączona: zobaczysz błąd serwera zamiast odpowiedzi, a o to nam chodzi.
Co robić, gdy bramka zwraca błąd zgodności z modelem?
Zdarza się, że zapytanie, które działa w OpenAI, kończy się błędem po stronie modelu lokalnego, np. o niedozwolonej roli wiadomości (Invalid role). Bramka tłumaczy format, ale nie zmieni możliwości samego modelu. Diagnozuj w tej kolejności:
- Zapytaj ten sam model bezpośrednio przez Ollamę (
/v1z poprzedniej części). Jeśli tam też jest błąd, problem leży w modelu lub w treści zapytania, nie w bramce. - Uprość zapytanie do jednej wiadomości
useri dodawaj kolejne elementy (rolasystem, historia), aż błąd wróci. - Zajrzyj do logów bramki, w terminalu, w którym ją uruchomiłeś. Tam jest pełny komunikat dostawcy.
Jak sprawdzić, że wszystko jest podłączone dobrze?
Przed kodem sprawdź trzy rzeczy po kolei: czy Ollama odpowiada (curl localhost:11434/api/version), czy bramka widzi aliasy (/v1/models) i czy Settings() wczytuje to, czego oczekujesz (python -c "from settings import Settings; print(Settings())"). Jeśli któryś krok zawodzi, wiesz dokładnie, w której warstwie jest problem.
Co dalej?
W trzeciej części podepniemy do tej bramki analizator dokumentów z OpenAI i zrobimy porównanie modelu lokalnego z chmurowym: jakości streszczeń i czasu odpowiedzi na tych samych dokumentach.
Jeśli chcesz, żeby ktoś przejrzał Twoją konfigurację i architekturę projektu, zobacz mentoring w ścieżce Python.


