Вы запустили llama-server, крутите длинный диалог с AI-ассистентом, и вдруг - бац! Сервер упал, или вы решили обновить модель, а может, просто слот перегрузился. И вся ваша предыдущая беседа - коту под хвост. Модель снова переваривает гигабайты истории, а вы ждёте минуты, пока она "вспомнит", о чём речь. Знакомо? Это она, потеря KV-кэша. Сегодня разберемся, как заставить llama-server запоминать контекст между перезапусками и сохранять сессии без боли.
Почему ваш контекст сгорает каждый раз
KV-кэш (Key-Value cache) - это шпаргалка модели. Каждый раз, когда модель генерирует токен, она сохраняет промежуточные вычисления для всех предыдущих токенов. Без этого кэша пришлось бы пересчитывать весь промпт с нуля на каждый новый токен. В llama-server кэш живёт в оперативной памяти и умирает вместе с процессом. По умолчанию:
- При перезапуске сервера кэш полностью сбрасывается.
- При превышении
--ctx-sizeстарые токены вытесняются (скользящее окно). - При смене модели или изменении параметров квантования кэш становится невалидным.
Эту проблему усугубляет и повторная обработка больших промптов при использовании прокси-инструментов вроде llama-swap. Если вы не контролируете кэш, каждый новый запрос будет пережевывать всю историю заново.
Что нам даёт сохранение KV-кэша
Начиная с llama.cpp b2400 (актуальные билды июля 2026 уже давно это умеют), llama-server поддерживает флаги для дампа и загрузки KV-кэша в файл. Это значит, что вы можете:
- Сохранить сессию перед выключением сервера.
- Восстановить её при следующем старте без повторной обработки истории.
- Перенести кэш на другую машину (если модель и квантование совпадают).
- Замораживать состояние слота в многопользовательских сценариях.
Важно: кэш привязан к конкретной модели и её квантованию. Если вы обновите модель или смените тип квантования, старый кэш придётся выбросить. Подробнее о том, почему это критично, читайте в статье Почему coding agent 'глупеет' на длинном контексте.
Пошаговый план: от запуска до реставрации
1 Проверяем версию и флаги
Убедитесь, что ваша сборка llama-server поддерживает флаги --kv-cache-file и --slot-save-path. Запустите:
./llama-server --help 2>&1 | grep -i 'kv-cache'
Если вывод пустой - обновите llama.cpp до последней версии. Я рекомендую билды от llama.cpp b3020 или новее. Проверку на наличие критических уязвимостей тоже не забывайте - обязательно прочитайте Как проверить версию и не стать жертвой RCE-атаки.
2 Запускаем сервер с сохранением кэша
Укажите файл, куда будет сохраняться кэш при корректном завершении:
./llama-server -m /path/to/model.gguf \
--ctx-size 8192 \
--kv-cache-file /data/llama-cache/session.bin \
--host 0.0.0.0 --port 8080
Теперь при получении сигнала SIGTERM (или SIGINT) сервер запишет текущий кэш в файл /data/llama-cache/session.bin.
3 Восстанавливаем сессию
При следующем запуске укажите тот же файл - кэш загрузится автоматически (если файл существует и валиден):
./llama-server -m /path/to/model.gguf \
--ctx-size 8192 \
--kv-cache-file /data/llama-cache/session.bin
Сервер найдёт файл, загрузит кэш и продолжит работу с того места, где остановился. Никакой повторной обработки промпта.
Важный нюанс: если размер файла кэша больше, чем --ctx-size, загрузка провалится. Всегда задавайте --ctx-size не меньше, чем при сохранении. Иначе получите ошибку и пустой кэш.
Сохранение слотов для многопользовательского режима
Если вы используете --parallel или --slots, вам пригодится флаг --slot-save-path. Он сохраняет и восстанавливает состояние каждого слота отдельно в указанную директорию:
./llama-server -m model.gguf \
--parallel 4 \
--slot-save-path /data/slots/ \
--ctx-size 4096
В директории появятся файлы вида slot0.bin, slot1.bin. При следующем запуске они подгрузятся автоматически, и каждый пользователь продолжит свой диалог.
Автоматизация: systemd и корректное завершение
Чтобы кэш гарантированно сохранялся, нужно отправлять корректный сигнал. Если вы просто убьёте процесс kill -9, файл не запишется. Используйте в systemd unit:
[Service]
ExecStart=/opt/llama-server -m /models/model.gguf --kv-cache-file /cache/session.bin
KillSignal=SIGTERM
TimeoutStopSec=30
При systemctl stop сервер получит SIGTERM, запишет кэш и завершится. После запуска кэш восстановится.
Типичные ошибки и как их обойти
Даже с правильными флагами можно наступить на грабли. Вот что я видел в бою:
| Ошибка | Причина | Решение |
|---|---|---|
| Файл кэша создаётся, но при загрузке сервер игнорирует его | Модель не совпадает (даже хеш файла модели проверяется) | Используйте одну и ту же модель и квантование. Если модель обновляли - удалите старый кэш. |
| При restore сервер падает с segmentation fault | Размер кэша не соответствует выделенному контексту | Задайте --ctx-size >= тому, что был при сохранении. |
| Кэш сохраняется, но модель забывает половину диалога | Сработало скользящее окно (контекст переполнен) | Увеличьте --ctx-size или используйте внешнее управление историей (например, суммаризацию). |
--slot-save-path на уровне дочерних процессов. Иначе при переключении между моделями кэш будет сбрасываться - об этом подробнее в статье Prompt Caching в llama.cpp: почему не работает и как настроить.Борьба с памятью: не дайте кэшу сожрать весь RAM
KV-кэш даже с файловым дампом остаётся в памяти. На больших контекстах (32K токенов) он может занимать десятки гигабайт. Плюс фрагментация кучи glibc может неожиданно удвоить потребление. Что делать:
- Включите offloading KV-кэша на GPU (
--no-kv-offload- отключает, по умолчанию кэш на GPU, если используется CUDA). - Используйте квантование кэша (
--cache-type-kи--cache-type-v). q4_0 даёт 4-кратное сжатие с минимальной потерей качества. - Мониторьте
/proc/meminfoи не забывайте про лимиты cgroups.
Кстати, есть целый список багов llama.cpp, которые незаметно ломают пайплайн. Некоторые из них связаны именно с некорректной работой кэша при переключении слотов.
Когда сохранение кэша не спасёт (и что делать вместо этого)
Бывают ситуации, когда файловый кэш бесполезен:
- Сменилась архитектура модели. При апгрейде с LLaMA 2 на LLaMA 3 кэш придётся пересчитать.
- Вы перешли на другую машину с другим GPU. Кэш может быть непереносим из-за разных CUDA-ядер.
- Контекст слишком длинный. Файл кэша на 100K токенов весит ~10 ГБ - его долго загружать и сохранять.
В таких случаях советую использовать внешнее управление историей: пишите промпт в базу данных (SQLite, Redis) и при каждом новом запросе формируйте контекст заново, но уменьшайте его за счёт суммаризации или RAG. А для костылей с инвалидацией кэша в инструментах вроде Claude Code есть отдельное решение - как исправить инвалидацию KV-кэша в llama.cpp при использовании Claude Code.
Итог: три правила, которые нужно запомнить
- Всегда запускайте сервер с флагом
--kv-cache-file- это единственный способ пережить перезагрузку. - Корректно завершайте процесс через SIGTERM (kill -15), а не SIGKILL.
- Следите за размером кэша - он не может превышать
--ctx-sizeи должен помещаться в файловую систему.
И если вы всё-таки столкнулись с внезапной утечкой памяти или падением сервера из-за кэша, не стесняйтесь заглянуть в исходники llama.cpp. Но перед этим проверьте, не попали ли вы под недавнюю уязвимость Bleeding Llama, которая может маскироваться под обычную потерю кэша. Успешных сохранений!