Выгрузка всех моделей маршрутизатора llama.cpp без перезапуска
Свободная VRAM без остановки llama-server
Режим маршрутизации llama.cpp — одно из самых полезных изменений в llama-server за последние годы. Наконец-то локальным операторам LLM предоставляется опыт управления моделями, близкий к тому, к которому пользователи привыкли в Ollama, при этом сохраняются высокая производительность и низкоуровневый контроль, которые делают llama.cpp стоящими того, чтобы использовать их в первую очередь.
Но есть один неприятный нюанс: выгрузка всего сразу не осуществляется одной волшебной кнопкой в HTTP API.
Маршрутизатор может перечислять модели. Он может загрузить модель. Он может выгрузить модель. Он может отклонить наименее недавно использованную модель, когда достигнут лимит --models-max. То, что в настоящее время не задокументировано как первоклассный конечный пункт, — это универсальный вызов сгрузить все модели сейчас.

Это не является непреодолимым препятствием. Правильный подход прост, явен и пригоден для автоматизации скриптами:
- Спросите у маршрутизатора, какие модели существуют.
- Отфильтруйте модели, статус которых
loaded(загружены). - Вызовите
/models/unloadодин раз для каждой загруженной модели.
Это тот подход, который я рекомендую для серьезных локальных рабочих процессов с LLM. Это скучно, прозрачно и легко отлаживается. Именно то, что вам нужно, когда ваша цель — освободить VRAM, не перезапуская весь сервис инференса.
Что на самом деле делает режим маршрутизации llama.cpp
При классическом использовании llama-server вы запускаете один сервер с одной моделью:
llama-server \
--model ./models/qwen3-8b.gguf \
--port 8080
Режим маршрутизации меняет это. Вместо привязки сервера к одному файлу GGUF, маршрутизатор становится координатором для нескольких моделей. Он может обнаруживать модели из кэша или из директории моделей, загружать их по запросу, маршрутизировать запросы к правильной модели и выгружать модели при необходимости.
Типичный запуск в режиме маршрутизатора выглядит так:
llama-server \
--models-dir ./models \
--models-max 4 \
--port 8080
Важным параметром здесь является --models-max. Он контролирует, сколько моделей может быть загружено одновременно. Если лимит достигнут, llama.cpp может отклонить наименее недавно использованную модель. Это полезно, но не заменяет целенаправленную операцию выгрузки. Отклонение по LRU (Least Recently Used) является реактивным. Скрипт выгрузки — это операционный контроль.
Мое субъективное мнение: если вы запускаете локальные модели для реальной работы, вы должны относиться к режиму маршрутизации как к менеджеру процессов инференса, а не как к игрушечному чат-серверу. Явные операции жизненного цикла имеют значение.
Конечные пункты управления моделями, которые вам нужны
Основной конечный пункт для обнаружения:
curl -s http://localhost:8080/models | jq
Этот конечный пункт возвращает модели, известные маршрутизатору, и их текущий статус жизненного цикла. Точная структура JSON может немного различаться между сборками, поэтому проверьте свой собственный ответ перед написанием автоматизации.
Обычная структура ответа выглядит так:
{
"data": [
{
"id": "qwen3-8b",
"status": "loaded"
},
{
"id": "llama-3.2-3b",
"status": "unloaded"
}
]
}
Чтобы выгрузить одну модель, выполните вызов:
curl -s -X POST http://localhost:8080/models/unload \
-H "Content-Type: application/json" \
-d '{"model":"qwen3-8b"}' \
| jq
Это базовая операция. На ней строится все остальное в этой статье.
Не задокументированного конечного пункта “выгрузить все” не существует
Это та часть, которая сбивает людей с толку.
Вы можете ожидать чего-то вроде этого:
curl -X POST http://localhost:8080/models/unload-all
Не стройте архитектуру на этом предположении. Задокументированная операция выполняется для каждой модели отдельно. Вы передаете идентификатор модели в /models/unload, и llama.cpp выгружает эту одну модель.
Это не обязательно плохой дизайн API. Операция для каждой модели безопаснее. Она заставляет вызывающую сторону решать, что должно быть выгружено. Это также избегает неожиданного поведения в производстве, когда один запрос администратора случайно убивает все “теплые” модели, используемые другими клиентами.
Для рабочей станции шорткат “выгрузить все” был бы удобен. Для многосерверной системы инференса явные циклы лучше.
Сначала выгрузите одну модель
Прежде чем автоматизировать что-либо, протестируйте точный идентификатор модели, который ожидает ваш маршрутизатор.
Сначала перечислите модели:
curl -s http://localhost:8080/models | jq
Выберите одну загруженную модель из вывода, затем выгрузите ее:
curl -s -X POST http://localhost:8080/models/unload \
-H "Content-Type: application/json" \
-d '{"model":"qwen3-8b"}' \
| jq
Проверьте список моделей снова:
curl -s http://localhost:8080/models | jq
Если статус модели изменится на unloaded (выгружена), значит, ваш конечный пункт, порт и идентификатор модели верны.
Если это не сработает, не угадывайте. Изучите JSON. Псевдонимы маршрутизатора, имена файлов GGUF и идентификаторы моделей часто не являются одной и той же строкой.
Выгрузка всех загруженных моделей с помощью curl и jq
Как только выгрузка одной модели работает, паттерн “выгрузить все” — это просто цикл оболочки.
Используйте это, если ваш ответ /models имеет .data[].id и .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
В этом вся хитрость. Это не гламурно, но это правильная форма для операции администрирования:
- Он выгружает только модели, которые действительно загружены.
- Он выводит информацию о том, что делает.
- Он завершается с ошибкой модель за моделью, вместо того чтобы скрывать все за одним непрозрачным действием.
- Он работает из cron, хуков systemd, SSH или заданий CI.
Повторно используемый скрипт для производственного использования
Для всего, что вы запускаете более двух раз, перестаньте вставлять однострочники. Сохраните скрипт.
Создайте 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
Сделайте его исполняемым:
chmod +x llama-router-unload-all.sh
Запустите его против локального сервера по умолчанию:
./llama-router-unload-all.sh
Запустите его против другого хоста:
LLAMA_SERVER_URL=http://192.168.1.50:8080 ./llama-router-unload-all.sh
Это версия, которую я бы действительно сохранил в директории инструментов. Он использует curl -f, поэтому ошибки HTTP завершают скрипт с ошибкой, и он позволяет вам переопределить URL сервера без редактирования файла.
Адаптация скрипта к структуре вашего JSON
Не полагайтесь слепо на то, что каждая сборка llama.cpp всегда будет возвращать точно одни и те же поля. Режим маршрутизации все еще развивается, и ваша сборка может экспонировать немного другую структуру JSON.
Начните с изучения ответа:
curl -s http://localhost:8080/models | jq
Скрипт использует этот фильтр:
jq -r '.data[] | select(.status == "loaded") | .id'
Если ваш идентификатор модели находится в .name, измените его на:
jq -r '.data[] | select(.status == "loaded") | .name'
Если поле статуса использует другое значение, скорректируйте фильтр соответственно. Важна суть: выберите загруженные модели, извлеките идентификатор, принимаемый /models/unload, затем вызовите выгрузку для каждой из них.
Почему модели могут загрузиться снова после выгрузки
Это самый частый источник путаницы.
Режим маршрутизации поддерживает загрузку по запросу. Если клиент отправляет запрос на завершение чата для модели, которая в данный момент выгружена, маршрутизатор может загрузить ее снова автоматически.
Это означает, что возможен такой сценарий:
- Вы выгружаете все модели.
- Open WebUI, тестовый скрипт или агент отправляют запрос.
- llama.cpp снова загружает запрошенную модель.
- Вы думаете, что выгрузка не удалась, но это не так.
Решение операционное, а не техническое. Сначала остановите клиентский трафик, если ваша цель — сохранить VRAM свободным.
Например:
- Остановите скрипты бенчмарков.
- Приостановите агенты и задания cron.
- Закройте или отключите сеансы Open WebUI.
- Отключите проверки здоровья, которые случайно выполняют реальные запросы к моделям.
Выгрузка — это не файрвол. Если клиенты продолжают запрашивать модели, режим маршрутизации выполняет свою работу, обслуживая их.
Open WebUI и кнопка Eject (Извлечь)
Open WebUI может интегрироваться с поддержкой выгрузки моделей llama.cpp. Когда провайдер настроен как llama.cpp, Open WebUI может отображать состояние загруженных моделей и предоставлять действие Eject для администраторов.
Под капотом это действие вызывает собственный API выгрузки Open WebUI, который затем вызывает конечный пункт /models/unload llama.cpp на настроенном соединении.
Это хорошо для ручного управления, но я бы все равно сохранил скрипт оболочки. Кнопка в интерфейсе удобна. Скрипт проверяем, воспроизводим и usable на безголовом (headless) сервере в 2 часа ночи.
Когда использовать “выгрузить все”
Выгрузка всех загруженных моделей полезна, когда вы хотите:
- Освободить память GPU перед запуском более крупной модели.
- Сбросить рабочую станцию разработки, не перезапуская
llama-server. - Подготовиться к запуску бенчмарка с чистым состоянием памяти.
- Освободить локальные рабочие нагрузки инференса перед обслуживанием.
- Восстановиться после неудачного сеанса, когда было загружено слишком много моделей.
Это не правильный инструмент, когда активные пользователи зависят от “теплых” моделей. В этом случае настройте --models-max, используйте целенаправленную маршрутизацию и позвольте отклонению LRU выполнить часть работы. Если вам нужна более умная выгрузка на основе таймаутов с контролем жизненного цикла для каждой модели, llama-swap — это прокси, специально разработанный для наложения именно этого поверх любой настройки llama-server.
Мое правило простое: используйте LRU для нормального давления, используйте явную выгрузку для намерений оператора.
Устранение неполадок
Конечный пункт моделей возвращает 404
Возможно, вы не используете сборку с поддержкой маршрутизации, или вы обращаетесь к неправильному порту.
Проверьте процесс сервера и доступные параметры:
llama-server --help | grep -i models
Затем протестируйте оба конечных пункта:
curl -s http://localhost:8080/models | jq
curl -s http://localhost:8080/v1/models | jq
Конечный пункт /v1/models — это список моделей, совместимый с OpenAI. Конечный пункт /models — это конечный пункт управления моделями маршрутизатора. Они связаны, но не являются одним и тем же.
jq не установлен
Установите его перед написанием скриптов для разбора JSON.
На Ubuntu или Debian:
sudo apt-get update
sudo apt-get install jq
На macOS с Homebrew:
brew install jq
Вызов выгрузки возвращает ошибку
Большинство неудач связаны с передачей неправильного идентификатора модели. Используйте точный идентификатор, возвращенный /models, а не имя файла, которое, как вам кажется, должно работать.
Также проверьте, содержит ли имя вашей модели кавычки, слэши или пробелы. Скрипт выше хорошо обрабатывает обычные строки, но необычные имена могут потребовать более тщательного построения JSON.
Для максимальной безопасности вы можете создать тело POST с помощью jq:
jq -n --arg model "$model" '{model: $model}'
Более защитный цикл выгрузки использовал бы это тело вместо вручную экранированного JSON.
VRAM не освобождается немедленно
Сначала убедитесь, что статус модели изменился. Затем проверьте, не перезагрузил ли другой запрос ее снова. Также помните, что инструменты мониторинга памяти GPU могут отставать или сообщать о поведении аллокатора, а не о немедленном намерении на уровне приложения.
Практический тест прост: остановите трафик, выгрузите модели, перечислите статус моделей, затем проверьте память GPU. Для измеренного использования VRAM для различных размеров моделей и окон контекста в llama.cpp бенчмарки llama.cpp для 16 ГБ VRAM дают конкретные цифры для проверки.
Более безопасная версия тела JSON
Если ваши идентификаторы моделей содержат необычные символы, используйте jq для генерации тела запроса 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
Эту версию следует использовать, если ваши модели названы идентификаторами в стиле репозиториев, пользовательскими псевдонимами или путями.
Финальное резюме
Режим маршрутизации llama.cpp — это большой шаг вперед для локальных операций с LLM. Он дает вам динамическую загрузку, переключение моделей и осведомленную об памяти отбраковку, не жертвуя прямотой llama-server.
Но не ждите идеального конечного пункта “выгрузить все”. Чистое решение уже существует: перечислите загруженные модели и выгрузите их по одной.
Этот паттерн явен. Он пригоден для скриптов. Он работает через SSH. Он хорошо сочетается с Open WebUI. И, что самое важное, он освобождает VRAM без перезапуска маршрутизатора.
Для локальной инфраструктуры ИИ это именно тот вид скучного интерфейса управления, который вам нужен.