Локальные LLM на Mac

совет
У десктопа есть путь в один клик

В десктопном приложении Hermes раздел Settings → Providers → Local Models сам поставит и настроит локальный сервер llama.cpp: скачает модели, подберёт раскладку по памяти и размер контекста. Подробности — в разделе Локальные модели. Это же руководство — про ручную настройку: MLX, самосборные бинарники или сервер, который вы хотите держать под своим контролем.

Это руководство проведёт вас через настройку локального LLM-сервера на macOS с OpenAI-совместимым API. Полная приватность, нулевые расходы на API и неожиданно хорошая скорость на Apple Silicon.

Разберём два бэкенда:

БэкендУстановкаСильные стороныФормат
llama.cppbrew install llama.cppМинимальное время до первого токена, квантованный KV-кэш для экономии памятиGGUF
omlxomlx.aiМаксимальная скорость генерации, нативная оптимизация MetalMLX (safetensors)

Оба бэкенда предоставляют OpenAI-совместимый эндпоинт /v1/chat/completions. Hermes работает с любым из них — достаточно указать http://localhost:8080 или http://localhost:8000.

инфо
Только Apple Silicon

Руководство рассчитано на Mac с Apple Silicon (M1 и новее). На Intel Mac llama.cpp работает, но без GPU-ускорения — скорость будет заметно ниже.


Выбор модели

Для старта рекомендуем Qwen3.5-9B — сильная модель с хорошим умозаключением, которая комфортно помещается в 8+ ГБ общей памяти при использовании квантования.

ВариантРазмер на дискеНужно RAM (контекст 128K)Бэкенд
Qwen3.5-9B-Q4_K_M (GGUF)5,3 ГБ~10 ГБ с квантованным KV-кэшемllama.cpp
Qwen3.5-9B-mlx-lm-mxfp4 (MLX)~5 ГБ~12 ГБomlx

Приблизительный расчёт памяти: размер модели + KV-кэш. Модель 9B Q4 весит ~5 ГБ. KV-кэш при контексте 128K с квантованием Q4 добавляет ещё ~4–5 ГБ. При KV-кэше по умолчанию (f16) это разрастается до ~16 ГБ. Флаги квантованного KV-кэша в llama.cpp — главный способ экономить память на ограниченных машинах.

Для больших моделей (27B, 35B) нужно 32+ ГБ общей памяти. Модель 9B — оптимальный выбор для машин с 8–16 ГБ.


Вариант A: llama.cpp

llama.cpp — самый переносимый локальный LLM-рантайм. На macOS он использует Metal для GPU-ускорения прямо из коробки.

Установка

brew install llama.cpp

После этого команда llama-server доступна глобально.

Загрузка модели

Нужна модель в формате GGUF. Удобнее всего скачивать с Hugging Face через huggingface-cli:

brew install huggingface-cli

Затем скачиваем:

huggingface-cli download unsloth/Qwen3.5-9B-GGUF Qwen3.5-9B-Q4_K_M.gguf --local-dir ~/models
совет
Защищённые модели

Некоторые модели на Hugging Face требуют аутентификации. Если получаете ошибку 401 или 404 — сначала выполните huggingface-cli login.

Запуск сервера

llama-server -m ~/models/Qwen3.5-9B-Q4_K_M.gguf \
  -ngl 99 \
  -c 131072 \
  -np 1 \
  -fa on \
  --cache-type-k q4_0 \
  --cache-type-v q4_0 \
  --host 0.0.0.0

Что делает каждый флаг:

ФлагНазначение
-ngl 99Выгрузить все слои на GPU (Metal). Большое значение гарантирует, что ничего не останется на CPU.
-c 131072Размер контекстного окна (128K токенов). Уменьшите, если не хватает памяти.
-np 1Количество параллельных слотов. Для одного пользователя — 1: больше слотов делят бюджет памяти.
-fa onFlash attention. Снижает расход памяти и ускоряет вывод на длинных контекстах.
--cache-type-k q4_0Квантование ключевого кэша до 4 бит. Главный способ экономить память.
--cache-type-v q4_0Квантование кэша значений до 4 бит. Вместе с предыдущим флагом сокращает KV-кэш на ~75% по сравнению с f16.
--host 0.0.0.0Слушать на всех интерфейсах. Используйте 127.0.0.1, если сетевой доступ не нужен.

Сервер готов, когда вы увидите:

main: server is listening on http://0.0.0.0:8080
srv  update_slots: all slots are idle

Оптимизация памяти на ограниченных системах

Флаги --cache-type-k q4_0 --cache-type-v q4_0 — самая важная оптимизация для машин с ограниченной памятью. Вот разница на контексте 128K:

Тип KV-кэшаРасход памяти (128K контекст, модель 9B)
f16 (по умолчанию)~16 ГБ
q8_0~8 ГБ
q4_0~4 ГБ

На Mac с 8 ГБ используйте KV-кэш q4_0 и выбирайте модель поменьше, которая всё ещё укладывается в минимальный контекст Hermes (64K). На 16 ГБ контекст 128K работает комфортно. На 32+ ГБ можно запускать большие модели или несколько параллельных слотов.

Если память всё равно заканчивается — уменьшайте контекст, но не ниже минимума Hermes в 64K. Либо переходите на модель меньшего размера или меньшее квантование (Q3_K_M вместо Q4_K_M).

Проверка

curl -s http://localhost:8080/v1/chat/completions \
  -H "Content-Type: application/json" \
  -d '{
    "model": "Qwen3.5-9B-Q4_K_M.gguf",
    "messages": [{"role": "user", "content": "Hello!"}],
    "max_tokens": 50
  }' | jq .choices[0].message.content

Узнать имя модели

Если забыли имя модели — запросите эндпоинт моделей:

curl -s http://localhost:8080/v1/models | jq '.data[].id'

Вариант B: MLX через omlx

omlx — нативное macOS-приложение для управления и раздачи MLX-моделей. MLX — собственный фреймворк Apple для машинного обучения, оптимизированный под архитектуру общей памяти Apple Silicon.

Установка

Скачайте и установите с omlx.ai. Приложение предоставляет GUI для управления моделями и встроенный сервер.

Загрузка модели

Просматривайте и скачивайте модели прямо в интерфейсе omlx. Найдите Qwen3.5-9B-mlx-lm-mxfp4 и загрузите её. Модели хранятся локально (обычно в ~/.omlx/models/).

Запуск сервера

По умолчанию omlx раздаёт модели на http://127.0.0.1:8000. Запустите раздачу через UI приложения или через CLI, если он доступен.

Проверка

curl -s http://127.0.0.1:8000/v1/chat/completions \
  -H "Content-Type: application/json" \
  -d '{
    "model": "Qwen3.5-9B-mlx-lm-mxfp4",
    "messages": [{"role": "user", "content": "Hello!"}],
    "max_tokens": 50
  }' | jq .choices[0].message.content

Список доступных моделей

omlx может раздавать несколько моделей одновременно:

curl -s http://127.0.0.1:8000/v1/models | jq '.data[].id'

Бенчмарки: llama.cpp против MLX

Оба бэкенда тестировались на одной машине (Apple M5 Max, 128 ГБ общей памяти) с одинаковой моделью (Qwen3.5-9B) при сопоставимом квантовании (Q4_K_M для GGUF, mxfp4 для MLX). Пять разнообразных промптов, по три запуска каждый, бэкенды тестировались поочерёдно во избежание конкуренции за ресурсы.

Результаты

Метрикаllama.cpp (Q4_K_M)MLX (mxfp4)Победитель
TTFT (среднее)67 мс289 мсllama.cpp (в 4,3 раза быстрее)
TTFT (p50)66 мс286 мсllama.cpp (в 4,3 раза быстрее)
Генерация (среднее)70 tok/s96 tok/sMLX (на 37% быстрее)
Генерация (p50)70 tok/s96 tok/sMLX (на 37% быстрее)
Полное время (512 токенов)7,3 с5,5 сMLX (на 25% быстрее)

Что это означает на практике

  • llama.cpp выигрывает при обработке промпта — связка flash attention + квантованный KV-кэш даёт первый токен уже через ~66 мс. Для интерактивных приложений, где важна воспринимаемая отзывчивость (чат-боты, автодополнение), это ощутимое преимущество.

  • MLX генерирует токены на ~37% быстрее после первого. Для пакетных задач, длинного контента или любой работы, где важно общее время выполнения, MLX финиширует раньше.

  • Оба бэкенда показывают очень стабильные результаты — разброс между запусками минимален. На эти цифры можно опираться.

Что выбрать?

СценарийРекомендация
Интерактивный чат, низкая задержкаllama.cpp
Длинная генерация, пакетная обработкаMLX (omlx)
Ограниченная память (8–16 ГБ)llama.cpp (квантованный KV-кэш вне конкуренции)
Несколько моделей одновременноomlx (встроенная поддержка)
Максимальная совместимость (в том числе Linux)llama.cpp

Подключение к Hermes

После запуска локального сервера:

hermes model

Выберите Custom endpoint и следуйте подсказкам. Нужно будет указать базовый URL и имя модели — возьмите значения из настроенного бэкенда.


Таймауты

Hermes автоматически распознаёт локальные эндпоинты (localhost, LAN-адреса) и увеличивает таймауты стриминга. Для большинства сетапов никакой настройки не нужно.

Если таймауты всё равно срабатывают (например, очень большие контексты на медленном железе), можно переопределить таймаут чтения стрима:

# В .env — увеличиваем с 120 с по умолчанию до 30 минут
HERMES_STREAM_READ_TIMEOUT=1800
ТаймаутПо умолчаниюАвто-корректировка для локальныхПеременная окружения
Чтение стрима (на уровне сокета)120 сПовышается до 1800 сHERMES_STREAM_READ_TIMEOUT
Обнаружение зависшего стрима180 сПолностью отключаетсяHERMES_STREAM_STALE_TIMEOUT
API-вызов (без стриминга)1800 сИзменений не нужноHERMES_API_TIMEOUT

Чаще всего проблемы возникают именно с таймаутом чтения стрима — это дедлайн на уровне сокета для получения очередного чанка данных. При prefill на больших контекстах локальная модель может не выдавать ни байта несколько минут, пока обрабатывает промпт. Авто-определение локальных эндпоинтов решает это прозрачно.

совет
Тишина на первом ходу — это обычно prefill, а не зависание

Hermes при каждом вызове отправляет системный промпт и схемы инструментов, поэтому на медленном железе первый ход может сопровождаться несколькими минутами тишины, пока модель обрабатывает этот промпт, прежде чем начать генерацию. Это работа prefill, а не зависшая сессия. Способы смягчить эффект — держать модель загруженной и сократить фиксированный промпт через hermes prompt-size — см. в разделе Медленный первый ответ (prefill) руководства по Ollama.

перевод и редактура:

обновлено

⌕ ESC