Lokalne LLM na macOS: Ollama, LM Studio, MLX, llama.cpp
Tłumaczenie automatyczne
Ten artykuł został automatycznie przetłumaczony z angielskiego oryginału.
Inżynierowie uruchamiający lokalne modele na Macu z Apple Silicon muszą oszacować całe obciążenie, a nie tylko wybrać model, który da się pobrać. Apple Silicon obsłuży codzienne dane wejściowe typu prompt bez zdalnego API, bo CPU i GPU współdzielą jedną pulę pamięci ujednoliconej. Wagi modelu dzielą jednak tę pamięć z cache przechowującym kontekst, buforami roboczymi środowiska uruchomieniowego i samym macOS.
Gdy model, kontekst i zapas pamięci się mieszczą, wybierz narzędzie pasujące do twojego sposobu pracy: Ollama jako zarządzaną usługę lokalną, LM Studio do przeglądania modeli w aplikacji desktopowej, llama.cpp do bezpośredniej kontroli nad flagami uruchomieniowymi albo MLX-LM do pracy w Pythonie natywnym dla Apple. Dowiesz się, jak oszacować pamięć, do czego służy każde narzędzie, jak je uruchomić i jak porównać je na tym samym zadaniu.
Zwięzłą tabelę decyzyjną znajdziesz w Lokalne narzędzia LLM na macOS w 2026 roku.
Ile pamięci potrzebuje model lokalny
W tym artykule pojawiają się cztery pojęcia:
- Kwantyzacja zapisuje wagi modelu na mniejszej liczbie bitów, aby zmniejszyć zużycie pamięci. W nazwie pliku takiej jak
Llama-3.1-8B-Instruct-Q4_K_M.ggufQ4oznacza schemat 4-bitowy;Q4_K_Mdaje średnio nieco poniżej 5 bitów na wagę, gdy doliczyć skale i kilka tensorów o wyższej precyzji. - GGUF to jednoplikowy format modelu używany przez llama.cpp i narzędzia na nim zbudowane. MLX-LM ładuje modele przekonwertowane na MLX, więc ten sam model jest dostępny jako osobne pobrania w formacie GGUF i MLX.
- KV cache przechowuje klucze i wartości uwagi dla każdej jednostki token w kontekście. Rośnie wraz z długością kontekstu i liczbą równoległych żądań.
- Rodzina checkpoint oznacza jeden model bazowy w jednym rozmiarze, na przykład Llama 3.1 8B Instruct, niezależnie od formatu pliku i kwantyzacji.
Wykres sumuje wagi i KV cache dla Llama 3.1 8B w Q4_K_M przy kontekście 8K i 32K. Porównuje te sumy częściowe z zalecanym zestawem roboczym GPU zgłoszonym na jednym Macu M1 z 16 GB pamięci; obszar roboczy środowiska wykonawczego i macOS potrzebują dodatkowej pamięci.
Rozmiar skwantyzowanych wag to dopiero pierwszy składnik:
peak memory ≈ model weights
+ KV cache
+ runtime workspace
+ multimodal components
+ application and OS memory
Wynik zmieniają długość kontekstu, typ danych cache, liczba równoległych żądań i architektura modelu. Składnikiem, który kontrolujesz najbezpośrednio, jest KV cache, przez długość kontekstu:
KV bytes per token = 2 × layers × KV heads × head dimension × bytes per value
Dla Llama 3.1 8B (32 warstwy, 8 głowic KV, wymiar głowicy 128) z 16-bitowym cache daje to 2 × 32 × 8 × 128 × 2 = 131 072 bajtów, czyli 128 KiB na token. Kontekst 8K wymaga 1 GiB cache na żądanie, a kontekst 32K wymaga 4 GiB. Wagi Q4_K_M to plik o rozmiarze 4,9 GB (4,6 GiB). Przed doliczeniem obszaru roboczego model potrzebuje około 5,6 GiB przy kontekście 8K i około 8,6 GiB przy kontekście 32K.
Metal zgłasza zalecany rozmiar zestawu roboczego GPU, recommendedMaxWorkingSetSize, który szacuje, ile pamięci GPU można przydzielić bez wpływu na wydajność środowiska wykonawczego. To zalecenie wydajnościowe, a nie wymuszany limit przydziału. W raporcie z 2023 roku dotyczącym jednego M1 z 16 GB llama.cpp wypisał 10 923 MB, czyli około 10,7 GiB lub dwie trzecie RAM. Względem tego zalecenia suma dla 8K pozostawia 5,1 GiB na obszar roboczy i zapas, a dla 32K — 2,1 GiB. Sprawdź wartość na swoim Macu i zmierz pełne obciążenie, zanim uznasz, że mieści się w pamięci. Mac z 8 GB ma mniejszy zapas; same te wagi potrzebują już 4,6 GiB.
Zaplanuj też miejsce na dysku. Każdy 4-bitowy model 7B lub 8B to pobranie rzędu 4–5 GB, a mała biblioteka modeli w obu formatach zajmuje 10–20 GB.
Podczas testów korzystaj z Monitora aktywności albo z metryk samego środowiska uruchomieniowego. Apple określa obciążenie pamięci na podstawie wolnej pamięci, tempa swapowania, pamięci przypiętej (wired) i plików w cache. Zostaw dość zapasu, by uniknąć trwałego swapowania; model, który się ładuje, ale mocno obciąża pamięć systemu, nie nadaje się do pracy interaktywnej.
Każda z poniższych sekcji o narzędziach pokazuje, gdzie ustawić długość kontekstu, bo to ustawienie najbardziej zmienia KV cache.
Cztery narzędzia rozwiązują różne problemy w pracy
| Narzędzie | Główny interfejs | Format modelu | Licencja | Wybierz, gdy |
|---|---|---|---|---|
| Ollama | CLI i lokalne HTTP API | Zarządzane pakiety modeli, głównie GGUF | MIT | Aplikacja potrzebuje prostej, zarządzanej usługi lokalnej |
| LM Studio | Interfejs desktopowy, CLI, SDK, API | GGUF i MLX | Aplikacja własnościowa, bezpłatna w pracy; SDK MIT | Ktoś musi wizualnie wyszukiwać, porównywać, analizować i udostępniać modele |
| llama.cpp | CLI, biblioteka C/C++, serwer lokalny | GGUF | MIT | Potrzebujesz bezpośrednich flag, narzędzi do konwersji i kwantyzacji albo wbudowanej inferencji |
| MLX-LM | Python i CLI | Wagi w formacie MLX | MIT | Piszesz w Pythonie na Apple Silicon albo wykonujesz fine-tuning modelu na tym samym Macu |
To tabela zakresów odpowiedzialności, nie ranking szybkości. Trzy narzędzia korzystają ze wspólnej biblioteki tensorów. Ollama uruchamia modele GGUF we własnym silniku, zbudowanym na GGML, bibliotece używanej przez llama.cpp. Wersja testowa MLX w Ollama z marca 2026 uruchamia też wybrane modele na Macach z ponad 32 GB pamięci. LM Studio uruchamia modele GGUF przez llama.cpp, a modele MLX przez MLX. Wspólna biblioteka GGML nie oznacza identycznych silników inferencji. Nawet przy tym samym pliku GGUF na szybkość mogą wpływać ustawienia domyślne, takie jak długość kontekstu i rozmiar partii, implementacje silników oraz wersje zaplecza.
Diagram pokazuje formaty modeli i wspólne biblioteki używane przez poszczególne narzędzia, w tym warunkową ścieżkę MLX w Ollama.
Jeśli nie wiesz jeszcze, jaki masz sposób pracy, skorzystaj z ostatniej kolumny tabeli. Jeśli chcesz po prostu coś uruchomić dzisiaj, zacznij od Ollama.
Jedno zastosowanie wykracza poza te cztery narzędzia: KoboldCpp, fork llama.cpp z interfejsem webowym, jest przeznaczony do długiej fikcji i odgrywania ról. Ma licencję AGPL, co ma znaczenie, jeśli wbudowujesz go w produkt.
Jedno API dla wszystkich czterech narzędzi
Wszystkie cztery narzędzia mogą wystawiać na localhost API zgodne z OpenAI. Kod aplikacji używający klienta openai może zmienić narzędzie przez zmianę base_url, a tym samym klientem można zmierzyć wszystkie cztery.
| Narzędzie | Polecenie serwera | Domyślny base URL |
|---|---|---|
| Ollama | ollama serve (aplikacja macOS uruchamia je) | http://localhost:11434/v1 |
| LM Studio | lms server start | http://localhost:1234/v1 |
| llama.cpp | llama-server -hf <repo>:<quant> | http://localhost:8080/v1 |
| MLX-LM | mlx_lm.server --model <repo> | http://localhost:8080/v1 |
Klient wymaga wartości klucza API. Użyj local jako wartości zastępczej, gdy uwierzytelnianie serwera jest wyłączone; gdy jest włączone, użyj skonfigurowanej wartości token. llama-server i mlx_lm.server domyślnie używają portu 8080, więc uruchom jeden z nich z --port 8081, aby działały jednocześnie.
Przykłady w Pythonie, które importują openai, wymagają pakietu openai. Poniższy benchmark zawiera polecenie uruchamiające zapisany skrypt z tą zależnością.
from openai import OpenAI
# Swap base_url for :1234 (LM Studio) or :8080 (llama.cpp, MLX-LM).
client = OpenAI(base_url="http://localhost:11434/v1", api_key="local")
Sekcja o benchmarku poniżej używa tego klienta, by jednym skryptem zmierzyć czas wszystkich czterech narzędzi.
Ollama: zarządzana usługa lokalna
Ollama zarządza pobieraniem modeli, szablonami, cyklem życia procesu i API na localhost. Przydaje się, gdy kod aplikacji ma korzystać ze stabilnej usługi lokalnej, a nie sam odpowiadać za flagi inferencji.
MODEL=llama3.2
ollama pull "$MODEL"
ollama show "$MODEL" # architecture, parameters, context length, quantization
ollama run "$MODEL" "Explain unified memory."
curl http://localhost:11434/api/chat \
-H 'Content-Type: application/json' \
-d '{
"model": "'"$MODEL"'",
"messages": [{"role": "user", "content": "Explain unified memory."}],
"options": {"num_ctx": 8192},
"stream": false
}'
Opcja num_ctx ustawia długość kontekstu dla jednego żądania. Aby ustawić ją dla całego serwera, uruchom go z OLLAMA_CONTEXT_LENGTH=8192 ollama serve albo użyj suwaka w ustawieniach aplikacji. Bez żadnego z tych ustawień Ollama dobiera wartość domyślną na podstawie dostępnej pamięci: 4K jednostek token, gdy dostępnych jest mniej niż 24 GiB pamięci GPU. ollama ps pokazuje kontekst używany przez każdy załadowany model.
Krótka nazwa z biblioteki, taka jak llama3.2, nie wskazuje stałego pliku; po aktualizacji ta sama nazwa może prowadzić do innej wersji. Przy każdej ewaluacji zapisuj identyfikator, który wypisuje ollama list, czyli początek skrótu modelu.
Ollama poświęca część widoczności niskiego poziomu na rzecz wygody zarządzania cyklem życia. Przejdź na llama.cpp lub inne środowisko, gdy chcesz bezpośrednio kontrolować plik GGUF, szablon czatu, ustawienie cache albo nową funkcję backendu.
LM Studio: eksploracja na desktopie i lokalne API
LM Studio przydaje się, gdy wyszukiwanie modeli, konfiguracja ładowania, podgląd czatu i porównywanie modeli obok siebie przez człowieka mają należeć do jednego procesu pracy. Udostępnia natywne SDK dla Pythona i TypeScripta, punkty końcowe zgodne z OpenAI, ustrukturyzowane dane wyjściowe, korzystanie z narzędzi i demona działającego bez interfejsu, więc to coś więcej niż desktopowy interfejs graficzny. Jego widok czatu przyjmuje też załączone pliki .pdf, .docx i .txt jako kontekst, co wystarcza do prostych pytań o dokumenty bez pisania kodu.
Obecny SDK Pythona łączy się z wcześniej pobranym modelem. Uruchom lokalny serwer LM Studio, zapisz kod jako lmstudio_demo.py i wykonaj uv run --with lmstudio python lmstudio_demo.py. Jeśli uwierzytelnianie jest włączone, ustaw LM_API_TOKEN; SDK odczyta go automatycznie.
import lmstudio as lms
MODEL_KEY = "ibm/granite-4-micro"
with lms.Client() as client:
model = client.llm.model(MODEL_KEY)
response = model.respond("Write one sentence about local inference.")
print(response)
Uruchom lokalne API z zakładki Developer albo z CLI. lms load ustawia długość kontekstu podczas ładowania modelu, a --estimate-only wypisuje oszacowanie pamięci bez ładowania czegokolwiek:
lms server start
lms load ibm/granite-4-micro --context-length 8192 --estimate-only
lms load ibm/granite-4-micro --context-length 8192
LM Studio może działać na localhost albo w sieci lokalnej i obsługuje uwierzytelnianie przez token API w ustawieniach uwierzytelniania API. Zostaw go na interfejsie loopback, chyba że zdalny dostęp jest zamierzony. Jeśli wystawiasz go w sieci LAN, wymagaj udokumentowanej opcji uwierzytelniania przez token API, zastosuj regułę zapory hosta i sprawdź dostęp narzędzi oraz integracji.
llama.cpp: bezpośrednie uruchamianie GGUF
llama.cpp to wzorcowe narzędzie do plików GGUF, gdy chcesz samodzielnie ustawić flagi uruchomieniowe. Obsługuje Apple Metal oraz CPU i inne backendy sprzętowe.
brew install llama.cpp
# Download through the Hugging Face integration and select a quantization.
MODEL_REPO=ggml-org/gemma-3-1b-it-GGUF
QUANT=Q4_K_M
llama-cli -hf "$MODEL_REPO:$QUANT"
# The same model with explicit settings: one turn, then exit.
llama-cli -hf "$MODEL_REPO:$QUANT" \
-c 8192 -n 256 --temp 0.7 -st \
-p "Write a Python function that sorts a list."
# Or start an OpenAI-compatible local server with an 8K context.
llama-server -hf "$MODEL_REPO:$QUANT" -c 8192
-c (--ctx-size) ustawia długość kontekstu, a tym samym rozmiar KV cache. -n ogranicza liczbę generowanych jednostek token, --temp ustawia temperaturę próbkowania, -st wykonuje jedną turę i kończy, a -p podaje prompt.
Opcja -hf pobiera też pasujący projektor multimodalny, jeśli repozytorium go zawiera. Obsługa modeli, szablony i flagi CLI zmieniają się szybko, więc przypnij znaną wersję i trzymaj polecenie uruchomienia razem z zapisem ewaluacji.
Wybierz llama.cpp, gdy celem jest bezpośrednia kontrola, a nie dlatego, że „niższy poziom” automatycznie oznacza szybciej. Zarządzane narzędzie może dobrać dobre ustawienia domyślne; bezpośrednie flagi mogą też pogorszyć wydajność.
MLX-LM: praca w Pythonie natywnym dla Apple
MLX-LM bazuje na frameworku tablicowym Apple MLX. Obsługuje generowanie, czat, konwersję, kwantyzację i fine-tuning z oszczędnym wykorzystaniem parametrów dla zgodnych modeli.
MODEL=mlx-community/Llama-3.2-3B-Instruct-4bit
uv run --with mlx-lm mlx_lm.generate \
--model "$MODEL" \
--max-kv-size 8192 \
--prompt "Explain Metal acceleration in one paragraph."
MLX-LM nie ma stałego ustawienia kontekstu: cache rośnie wraz z tekstem typu prompt i wynikiem. --max-kv-size ogranicza cache do tylu jednostek token. Po przekroczeniu limitu MLX-LM odrzuca najstarsze jednostki token i zachowuje kilka pierwszych.
Python udostępnia model i tokenizer bezpośrednio. Zapisz poniższy kod jako mlx_demo.py i uruchom uv run --with mlx-lm python mlx_demo.py:
from mlx_lm import generate, load
MODEL = "mlx-community/Llama-3.2-3B-Instruct-4bit"
model, tokenizer = load(MODEL)
messages = [{"role": "user", "content": "Give one local-LLM benchmark rule."}]
prompt = tokenizer.apply_chat_template(
messages,
tokenize=False,
add_generation_prompt=True,
)
print(generate(model, tokenizer, prompt=prompt, max_tokens=80))
Serwer HTTP MLX-LM, mlx_lm.server, jest udokumentowany jako serwer deweloperski z podstawowymi kontrolami bezpieczeństwa, a nie usługa produkcyjna. Używaj go do lokalnych eksperymentów albo umieść przed nim proxy z uwierzytelnianiem lub własną usługę API.
Porównanie narzędzi na tym samym obciążeniu
Testuj tę samą rodzinę checkpoint i porównywalną kwantyzację tam, gdzie pozwalają na to formaty. Plik GGUF Q4_K_M i 4-bitowy model MLX pochodzący z tego samego zapisu checkpoint są porównywalne, ale nie identyczne.
Użyj małego zestawu danych wejściowych typu prompt zawierającego:
- jeden krótki prompt interaktywny
- jeden długi prompt zbliżony do docelowego kontekstu
- ustrukturyzowane dane wyjściowe lub wywołania narzędzi, jeśli aplikacja ich potrzebuje
- reprezentatywną długość generowanego tekstu
- jedno powtórzone żądanie, aby odróżnić zimne ładowanie od ciepłej inferencji
Zapisuj:
| Metryka | Dlaczego ma znaczenie |
|---|---|
| Wynik zadania | Szybki, ale błędny model jest bezużyteczny |
| Czas do pierwszej jednostki token | Responsywność interaktywna |
| Wyjściowe jednostki token na sekundę | Przepustowość generowania |
| Szczytowa pamięć i jej obciążenie | Czy maszyna nadal nadaje się do pracy |
| Czas zimnego ładowania | Wrażenia na desktopie i przy ładowaniu na żądanie |
| Zużycie energii i zachowanie termiczne | Długotrwała praca na laptopie |
| Zgodność API/schematu | Czy narzędzie pasuje do aplikacji |
Każde narzędzie samo wypisuje liczby dotyczące szybkości:
- Ollama:
ollama run "$MODEL" --verbosepo każdej odpowiedzi wypisuje czas ładowania, tempo przetwarzania danych typu prompt (prompt eval rate) i tempo generowania (eval rate, czyli wyjściowe jednostki token na sekundę). - llama.cpp:
llama-bench -hf "$MODEL_REPO:$QUANT" -p 512 -n 128mierzy przetwarzanie tekstu typu prompt o długości 512 jednostek token i generowanie 128 jednostek token, w jednostkach token na sekundę. - MLX-LM:
mlx_lm.generatepo każdym uruchomieniu wypisuje liczbę jednostek token na sekundę dla przetwarzania danych typu prompt i generowania oraz szczytową pamięć. - LM Studio: widok czatu pokazuje liczbę jednostek token na sekundę i czas do pierwszej jednostki token pod każdą odpowiedzią, a natywne REST API zwraca te same wartości w polu
stats.
Aby porównać opóźnienie obserwowane przez klienta, zmierz czas strumieniowego żądania do punktów końcowych zgodnych z OpenAI wymienionych w sekcji „Jedno API dla wszystkich czterech narzędzi”. Skrypt mierzy całkowity czas żądania i czas do pierwszego widocznego tekstu, a następnie wypisuje odpowiedź do sprawdzenia. Widoczny tekst może pojawić się po rozumowaniu lub wyniku narzędzia, więc ten czas nie musi odpowiadać czasowi środowiska wykonawczego do pierwszej wygenerowanej jednostki token.
Uruchom wybrany serwer z pobranym modelem, zapisz skrypt jako benchmark.py i wykonaj uv run --with openai python benchmark.py. Zmień BASE_URL i MODEL dla każdego narzędzia. Ustaw LOCAL_API_KEY na skonfigurowany token, jeśli uwierzytelnianie serwera jest włączone; w przeciwnym razie skrypt użyje local jako wartości zastępczej.
import os
import time
from openai import OpenAI
BASE_URL = "http://localhost:11434/v1"
MODEL = "llama3.2"
client = OpenAI(base_url=BASE_URL, api_key=os.environ.get("LOCAL_API_KEY", "local"))
start = time.perf_counter()
first = None
parts = []
stream = client.chat.completions.create(
model=MODEL,
messages=[{"role": "user", "content": "Explain unified memory in 200 words."}],
max_tokens=300,
stream=True,
)
for chunk in stream:
if chunk.choices and chunk.choices[0].delta.content:
if first is None:
first = time.perf_counter()
parts.append(chunk.choices[0].delta.content)
end = time.perf_counter()
print(f"request duration: {end - start:.2f} s")
if first is None:
print("time to first visible text: unavailable (no visible text)")
else:
print(f"time to first visible text: {first - start:.2f} s")
print("response:", "".join(parts))
Fragmenty tekstu przesyłane strumieniowo nie są liczbami jednostek token. Aby uzyskać liczbę wyjściowych jednostek token na sekundę, użyj wymienionych wyżej liczników środowiska wykonawczego i zapisz, czy obejmują rozumowanie lub wyniki narzędzi. Na przykład LM Studio osobno raportuje łączną liczbę wyjściowych jednostek token i jednostki token związane z rozumowaniem. Strumień bez widocznego tekstu zgłasza jego brak zamiast przypisywać opóźnienie tekstu; sprawdź rozumowanie, wywołania narzędzi lub stan zakończenia przez interfejs środowiska wykonawczego.
Wykonaj każde żądanie dwukrotnie: pierwsze może obejmować ładowanie modelu, a drugie może ponownie wykorzystać załadowane wagi i stan danych typu prompt w pamięci podręcznej. Zapisz wersje środowiska wykonawczego i ustawienia buforowania danych typu prompt, aby powtarzane żądania pozostały porównywalne.
Nie porównuj 4-bitowego modelu z jednego narzędzia z modelem o pełnej precyzji z drugiego i nie przypisuj różnicy środowisku uruchomieniowemu.
Lista kontrolna bezpieczeństwa i prywatności
Rozdziel lokalną inferencję od pracy offline. Dane typu prompt mogą zostawać na maszynie, a aplikacja nadal łączy się z siecią, by pobierać modele, aktualizacje lub korzystać z funkcji opcjonalnych. Ollama opisuje w FAQ osobno lokalne wykonywanie, pobieranie modeli i opcjonalne funkcje w chmurze. Traktuj pracę offline jako wymaganie do zweryfikowania w każdej aplikacji: najpierw pobierz artefakty, odłącz sieć i przetestuj cały proces.
Diagram pokazuje cztery ścieżki sieciowe do sprawdzenia podczas lokalnej inferencji: do jakiej części Maca docierają, w którą stronę płyną dane i co sprawdzić w każdej z nich. Funkcje chmurowe i dostęp przez LAN zależą od konfiguracji aplikacji.
- Wiąż API z loopback, chyba że dostęp sieciowy jest wymagany.
- Jeśli dostęp z LAN jest wymagany, użyj zapory hosta oraz udokumentowanego uwierzytelniania narzędzia albo odwrotnego proxy z uwierzytelnianiem przed usługą. Obsługa uwierzytelniania przez token API zależy od narzędzia: LM Studio dokumentuje uwierzytelnianie przez token API, a
llama-serverprzyjmuje--api-key. Ollama domyślnie nasłuchuje na127.0.0.1:11434, aOLLAMA_HOSTzmienia adres nasłuchiwania. Jej lokalne API nie wymaga uwierzytelniania, więc do użycia w sieci LAN postaw przed nią proxy z uwierzytelnianiem. - Traktuj pliki modeli jak artefakty stron trzecich; zapisuj źródło, rewizję, licencję i skrót.
- Pliki GGUF nie zawierają kodu wykonywalnego. Modele w formacie Hugging Face mogą dostarczać kod Pythona; nie przekazuj
--trust-remote-codedo MLX-LM ani Transformers dla repozytorium, którego nie przejrzałeś. - Sprawdź, czy opcjonalne funkcje dokumentów, narzędzi, aktualizacji lub analityki wykonują wywołania sieciowe.
- Nie zakładaj, że lokalne generowanie zabezpiecza pobrane dokumenty, logi ani skutki uboczne narzędzi.