Гайд · TNWS AI
Как получить JSON по схеме через response_format в llama.cpp server
Задаём объектную JSON Schema в chat endpoint, разбираем строку content и валидируем обязательные поля отдельно.
Задача и применимость
Поисковый вопрос: «как получить JSON по схеме llama.cpp response_format json_schema». Извлекать типизированные поля из короткой заметки без свободного текста вокруг JSON. llama.cpp server с генеративной GGUF-моделью, поддерживаемым чат-шаблоном и endpoint /v1/chat/completions; синтаксис response_format сверен с README проекта на 20 сентября 2026 года.
Пример рассчитан на уже имеющийся файл модели, полученный законным способом по условиям его лицензии. Здесь не утверждается, что любая GGUF-модель одинаково понимает русский язык или имеет один и тот же чат-шаблон. Фактический номер сборки узнавайте локально через llama-server --version и повторяйте контрольный опыт после обновления бинарника.
Что подтверждено документацией
Документация llama.cpp описывает response_format для plain JSON и JSON, ограниченного схемой: type json_object или json_schema со вложенным schema. Это ограничение синтаксиса генерации; оно не гарантирует истинность извлечённых значений. Схему надо сверять с поддерживаемым подмножеством JSON Schema в вашей сборке.
Порядок действий
- Составьте документ с однозначными полями, не включайте в него личные данные.
- Определите объектную JSON Schema с типами и обязательными ключами.
- Отправьте messages и response_format на /v1/chat/completions со stream:false.
- Разберите choices[0].message.content как JSON и проверьте значения против исходного текста.
- Запрещайте автоматическую запись результата, если поле отсутствует или подменено догадкой модели.
Локальный запуск для описанного сценария (замените путь к файлу на ваш):
./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"
model = json.load(urllib.request.urlopen(base + "/v1/models"))["data"][0]["id"]
schema = {"type": "object", "properties": {
"order_id": {"type": "string"}, "days": {"type": "integer"}},
"required": ["order_id", "days"]}
body = {"model": model, "stream": False,
"messages": [{"role": "user", "content":
"Извлеки только явные факты: Заказ K-17 доставят через 3 дня."}],
"response_format": {"type": "json_schema", "schema": schema}}
req = urllib.request.Request(base + "/v1/chat/completions",
data=json.dumps(body, ensure_ascii=False).encode("utf-8"),
headers={"Content-Type": "application/json"}, method="POST")
with urllib.request.urlopen(req, timeout=90) as reply:
raw = json.load(reply)["choices"][0]["message"]["content"]
data = json.loads(raw)
assert data["order_id"] == "K-17" and data["days"] == 3
print(data)
Входные данные и ожидаемый результат
Вход: «Заказ K-17 доставят через 3 дня». Ожидаемая проверка: словарь с order_id=K-17 и days=3, где days — integer, а не строка. Если модель выдаст 4, это валидный по типам JSON, но неверный факт; assert остановит процесс.
Почему важна именно эта проверка
Принудительный формат полезен там, где следующий модуль ждёт строгую структуру, например для заполнения тестовой карточки заказа. Однако JSON Schema регулирует форму, а не подтверждает источник данных. Отличайте три независимые проверки: удачный HTTP-ответ, успешно разобранный JSON и совпадение фактов с документом. В демонстрации третью проверку можно записать жёстким assert, потому что факт известен. В приложении с неизвестными документами потребуются правила извлечения, ручная проверка сложных случаев или сравнение с авторитетной базой.
Работа с реальными данными
Схема должна соответствовать содержанию входа. Если в тексте нет срока доставки, нельзя оставлять обязательное поле days и требовать догадку: лучше разрешить null через поддерживаемую конструкцию либо отправить запись на ручной разбор после проверки возможностей версии. Не задавайте слишком широкий объект «что угодно»: тогда ограничение теряет практический смысл. Не включайте конфиденциальные документы в debug-логи целиком. При обработке настоящих заказов сохраняйте ссылку на источник, версию схемы и исходный идентификатор, чтобы результат можно было перепроверить.
Проверка после изменения модели или нагрузки
При обновлении бинарника или замене GGUF сохраните серию контрольных кейсов: точный факт, отсутствующее поле, противоречивые цифры, пустой текст и невалидный ввод. Замеряйте долю удачных разобранных ответов отдельно от доли верных фактов. Документация показывает варианты json_object и json_schema, но не обещает совместимости всех возможных ключевых слов JSON Schema; проверяйте реально использованные ограничения на своей сборке. Не отправляйте сырое содержимое модели на выполнение как Python-код: json.loads трактует его как данные.
При построении карточки заказа помните о различии между «через 3 дня» и календарной датой. Пример извлекает ровно то число, которое явно написано во входе; он не рассчитывает дату доставки и не знает часовой пояс. Если бизнес-процессу нужна дата, её следует вычислять отдельно по проверенной дате заказа и правилам календаря. Такой разделённый конвейер проще тестировать: модель отвечает за извлечение, обычный код — за арифметику, а документ-источник остаётся доступным для проверки.
Критерии приёмки
- Ответ проходит json.loads и имеет необходимые типы.
- K-17 и 3 совпадают с текстом источника.
- Отсутствующие либо сомнительные поля не записываются как достоверные.
Частые ошибки и чего не делать
- Не считать синтаксически правильный JSON истинным.
- Не требовать от модели факт, которого нет во входе.
- Не исполнять сгенерированный JSON как программу.
Краткие ответы на вопросы
Достаточно ли json_object?
Он ограничивает формат, но точную структуру лучше проверять схемой и кодом.
Гарантирует ли схема правильные факты?
Нет, значения проверяются отдельно.
Какие данные в content?
Строка JSON, которую нужно разобрать.
Официальные источники
Частые вопросы
Достаточно ли json_object?
Он ограничивает формат, но точную структуру лучше проверять схемой и кодом.
Гарантирует ли схема правильные факты?
Нет, значения проверяются отдельно.
Какие данные в content?
Строка JSON, которую нужно разобрать.
Читайте также
Как ограничить генерацию llama.cpp через n_predict и проверить причину остановки
Используем native /completion и поля остановки, чтобы отличить заданный лимит от нормального конца ответа.
Как переранжировать найденные документы через llama.cpp /v1/rerank
Запускаем reranker GGUF, подаём список кандидатов и проверяем порядок и связь результата с исходными документами.
Как получить embeddings через llama.cpp /v1/embeddings и проверить pooling
Запускаем специализированную embedding-модель, кодируем две строки и проверяем размерность и числовой формат.
Комментарии
Пока тихо. Скажите первое слово