Гайд · TNWS AI
Как вести двухшаговый диалог через llama.cpp /v1/chat/completions
Собираем messages, добавляем ответ assistant в историю и проверяем, что модель использует предыдущий вопрос.
Задача и применимость
Поисковый вопрос: «как сохранить контекст диалога llama.cpp chat completions messages». Сохранить контекст между HTTP-запросами, не полагаясь на неявную память сервера. llama.cpp server с моделью, у которой корректно работает chat template; документированный OpenAI-подобный /v1/chat/completions на 20 сентября 2026 года. Совместимость с произвольными клиентами не гарантирована разработчиками.
Пример рассчитан на уже имеющийся файл модели, полученный законным способом по условиям его лицензии. Здесь не утверждается, что любая GGUF-модель одинаково понимает русский язык или имеет один и тот же чат-шаблон. Фактический номер сборки узнавайте локально через llama-server --version и повторяйте контрольный опыт после обновления бинарника.
Что подтверждено документацией
README говорит: /v1/chat/completions принимает messages и поддерживает обычный и потоковый режим. Лучше использовать модель с поддерживаемым chat template; по умолчанию может применяться ChatML. Формат ответа содержит choices/message, а повторная передача истории находится под контролем клиента. Имя модели для запроса сверяйте с фактическим /v1/models.
Порядок действий
- Получите список моделей с GET /v1/models и выберите реальный идентификатор.
- Отправьте первую пару system/user без stream и сохраните message.content.
- Добавьте полученный ответ с role=assistant к локальному списку messages.
- Добавьте второй user-вопрос, зависящий от первого, и выполните новый запрос.
- Убедитесь, что вторая реплика соответствует сохранённому примеру и ограничениям контекста.
Локальный запуск для описанного сценария (замените путь к файлу на ваш):
./build/bin/llama-server -m /models/your-chat-model.gguf --host 127.0.0.1 --port 8080
Готовый шаблон запроса и проверки
import json, urllib.request
base = "http://127.0.0.1:8080"
models = json.load(urllib.request.urlopen(base + "/v1/models", timeout=10))
model_id = models["data"][0]["id"]
messages = [{"role": "system", "content": "Не придумывай сведения."},
{"role": "user", "content": "Запомни код заказа K-17."}]
def ask(history):
data = json.dumps({"model": model_id, "messages": history,
"stream": False}, ensure_ascii=False).encode("utf-8")
req = urllib.request.Request(base + "/v1/chat/completions",
data=data, headers={"Content-Type": "application/json"}, method="POST")
with urllib.request.urlopen(req, timeout=90) as reply:
return json.load(reply)["choices"][0]["message"]
first = ask(messages)
messages.append({"role": "assistant", "content": first["content"]})
messages.append({"role": "user", "content": "Какой код заказа я назвал?"})
print(ask(messages)["content"])
Входные данные и ожидаемый результат
Вход первого шага: код K-17. Второй вопрос просит назвать этот код. Ожидаемый смысл ответа — K-17; если придуман иной номер, тест не пройден. Исходные messages и ответ assistant сохраняются в клиенте, затем полностью отправляются во втором запросе.
Почему важна именно эта проверка
Протокол HTTP не означает, что модель автоматически помнит другой запрос того же пользователя. Передайте историю явно. Список messages должен сохранять порядок и роли; если стереть первый user-вопрос или ответ assistant, у модели не будет полноценного контекста. Даже если кэш промпта на сервере ускоряет вычисления, кэш не является надёжным хранилищем разговора. В приложении храните диалог с идентификатором сессии и отдельно ограничивайте доступ между пользователями: никогда не подмешивайте чужую историю в текущие messages.
Работа с реальными данными
При длинной беседе число входных токенов растёт. Сокращение истории нельзя делать случайным удалением критичных инструкций. Разделите неизменное системное правило, последние реплики и проверенные факты; для важных задач лучше явно извлекать факты из хранилища и подтверждать их. В контрольном примере K-17 — синтетический номер, не персональные данные. При работе с реальными обращениями маскируйте контактную информацию до записи логов и вводите срок хранения. Если модель неверно отвечает, сначала распечатайте и проверьте именно отправленные messages, затем — шаблон модели.
Проверка после изменения модели или нагрузки
Поля choices и message зависят от OpenAI-подобного маршрута, но разработчики не обещают стопроцентную совместимость со сторонними API. После обновления бинарника прогоните интеграционный тест структуры JSON и контрольный диалог; не предполагаете, что старый SDK всегда останется полностью совместимым. Отдельно проверяйте пустой content или ошибки загрузки модели: это не повод сохранять пустой ответ как реплику assistant. Семантический тест проверяет сохранение K-17, но не доказывает, что любой длинный диалог будет точен.
Если поле model по умолчанию является путём к файлу, оно может содержать локальные директории; README также описывает --alias для более удобного публичного идентификатора. Используйте GET /v1/models для интеграции, но не выводите сырой путь к внутреннему хранилищу модели в пользовательский интерфейс. При смене alias пересмотрите клиентский код и тесты, потому что хардкод прежней строки сломает запросы. Для многоарендного приложения храните сессии по проверенному ID пользователя, а не по имени модели.
Критерии приёмки
- Идентификатор модели взят из /v1/models, а не выдуман.
- Вторая отправка содержит первую реплику пользователя и ответ assistant.
- Контрольный код K-17 правильно воспроизведён или ошибка зафиксирована.
Частые ошибки и чего не делать
- Не рассчитывать на скрытую «память» между HTTP-запросами.
- Не смешивать истории разных пользователей.
- Не считать успех одного короткого диалога гарантией работы с длинным контекстом.
Краткие ответы на вопросы
Сохраняет ли сервер мой диалог автоматически?
В примере историю формирует и отправляет клиент.
Нужен ли chat template?
Для оптимальной работы требуется поддерживаемый шаблон модели.
Можно ли передать stream:true?
Да, но тогда нужен обработчик потокового ответа.
Официальные источники
Частые вопросы
Сохраняет ли сервер мой диалог автоматически?
В примере историю формирует и отправляет клиент.
Нужен ли chat template?
Для оптимальной работы требуется поддерживаемый шаблон модели.
Можно ли передать stream:true?
Да, но тогда нужен обработчик потокового ответа.
Читайте также
Как ограничить генерацию llama.cpp через n_predict и проверить причину остановки
Используем native /completion и поля остановки, чтобы отличить заданный лимит от нормального конца ответа.
Как переранжировать найденные документы через llama.cpp /v1/rerank
Запускаем reranker GGUF, подаём список кандидатов и проверяем порядок и связь результата с исходными документами.
Как получить embeddings через llama.cpp /v1/embeddings и проверить pooling
Запускаем специализированную embedding-модель, кодируем две строки и проверяем размерность и числовой формат.
Комментарии
Пока тихо. Скажите первое слово