Перейти к содержанию
modelium.
Начать с задачи
←
Абстрактная композиция: слои фактур и направленный свет в сиреневых тонах.

Structured output локально: JSON Schema в Ollama, llama.cpp и vLLM

Для структурированного ответа задайте модельному серверу JSON Schema и затем отдельно проверьте полученный объект валидатором. Ollama принимает схему в `format`, llama.cpp — внутри `response_format`, vLLM — через OpenAI-совместимый `response_format`. Валидный JSON не доказывает правильность извлечённых фактов: сравните их с исходным документом, прежде чем сохранять или отправлять дальше.

Сверено с документацией 25 сентября 2026 года. Ниже — инструкции для стенда читателя, а не отчёт о запуске модели Modelium. Никаких весов и платных вычислений для этой страницы мы не использовали. Частотность запроса «structured output локально» не измерялась; основание — актуальная документация трёх runtimes.

Что означает structured output — и чего он не обещает?

Structured output ограничивает форму ответа модели: вместо свободного текста сервер пытается вернуть JSON с заданными полями и типами. Ollama передаёт JSON Schema в format, а llama.cpp и vLLM принимают её через параметры response_format своих chat endpoints s1s3s5. Это полезно, когда локальную модель нужно подключить к обработке документов, но форма ответа сама по себе не доказывает, что сумма или имя поставщика извлечены верно.

Проверяйте три уровня отдельно: (1) ответ — корректный JSON, (2) объект проходит независимую проверку схемы, (3) каждое извлечённое значение соответствует исходному тексту. По стандартному описанию JSON Schema properties задаёт поля, required — обязательные поля, additionalProperties: false запрещает лишние ключи; это правила формы, не проверки истинности извлечения s7. Не отправляйте непроверенное значение в 1С или CRM.

Задача в примере: из строки «Поставщик Альфа, сумма 1250 рублей» получить vendor (строка) и amount (целое число). Мы сознательно не просим модель интерпретировать договор, считать НДС или оценивать надёжность поставщика. На реальных документах сравнивайте результат с оригиналом и фиксируйте пустые/спорные поля до автоматизации.

Как подготовить одинаковую схему для трёх серверов?

Создайте каталог и файл schema.json. Начните с простых типов и явно запретите дополнительные поля. Эти ограничения есть в JSON Schema, но не все её возможности одинаково поддерживаются генераторами: llama.cpp документирует лишь подмножество стандарта и предупреждает, что неподдерживаемые ключевые слова могут быть проигнорированы без ошибки s4s7.

mkdir -p structured-demo && cd structured-demo
cat > schema.json <<'JSON'
{
"type": "object",
"properties": {
"vendor": {"type": "string"},
"amount": {"type": "integer"}
},
"required": ["vendor", "amount"],
"additionalProperties": false
}
JSON
python3 -m json.tool schema.json > /dev/null

Последняя команда проверяет синтаксис файла схемы, а не полноту её поддержки конкретным сервером. Для сопоставимого теста возьмите одну и ту же исходную строку, один и тот же контракт и одну модель, если формат весов и runtime позволяют; иначе не сравнивайте качество как контролируемый бенчмарк. Подсказка в запросе должна объяснять смысл полей. llama.cpp специально отмечает, что схема ограничивает декодирование, но сама не вставляется в промпт s4.

Как получить JSON в Ollama?

Предусловие: Ollama запущена локально, выбранная модель уже доступна в ollama list; установка и выбор модели — в руководстве Ollama. Не подставляйте облачный URL: текущая документация Ollama отдельно предупреждает, что Ollama Cloud не поддерживает structured outputs s1.

ollama list
export MODEL='впишите-имя-модели-из-списка'
python3 - <<'PY'
import json, os
schema = json.load(open('schema.json', encoding='utf-8'))
request = {
'model': os.environ['MODEL'],
'messages': [{'role': 'user', 'content': 'Извлеки vendor и amount из: Поставщик Альфа, сумма 1250 рублей. Верни только JSON.'}],
'format': schema,
'stream': False,
'options': {'temperature': 0}
}
with open('request.json', 'w', encoding='utf-8') as f:
json.dump(request, f, ensure_ascii=False)
PY
curl --fail-with-body -sS http://127.0.0.1:11434/api/chat \
-H 'Content-Type: application/json' -d @request.json > response.json

Поле format здесь — объект схемы, а не просто строка "json"; Ollama показывает оба режима отдельно. stream: false упрощает разбор целого ответа; полезная нагрузка лежит в message.content s1s2. Если curl сообщает отказ соединения, проверьте адрес, процесс и модель, прежде чем менять схему. Если ответ HTTP успешен, но объект пустой или неправильный, сначала посмотрите ответ целиком и причины ограничения генерации.

Чем отличаются llama.cpp и vLLM?

Для llama.cpp нужен заранее поднятый llama-server с совместимым GGUF и chat template; гайд по llama.cpp описывает выбор файла и запуск. Его OpenAI-совместимый chat endpoint — http://127.0.0.1:8080/v1/chat/completions, а простой поддерживаемый вариант схемного ответа — response_format: {"type":"json_object","schema": ...} на закреплённой ревизии README s3. Здесь не используется Ollama-поле format:

export MODEL='имя-модели-на-вашем-llama-server'
python3 - <<'PY'
import json, os
schema = json.load(open('schema.json', encoding='utf-8'))
request = {
'model': os.environ['MODEL'],
'messages': [{'role': 'user', 'content': 'Извлеки vendor и amount из: Поставщик Альфа, сумма 1250 рублей. Верни JSON с этими полями.'}],
'response_format': {'type': 'json_object', 'schema': schema},
'stream': False
}
with open('request.json', 'w', encoding='utf-8') as f:
json.dump(request, f, ensure_ascii=False)
PY
curl --fail-with-body -sS http://127.0.0.1:8080/v1/chat/completions \
-H 'Content-Type: application/json' -d @request.json > response.json

В llama.cpp даже при одинаковом JSON Schema часть ключевых слов может не ограничить генерацию; не переносите сложные pattern, $ref или uniqueItems в продакшен без отдельной проверки на вашей версии. Документация перечисляет ограничения и различие с общим стандартом s4. Сверьте возвращённое поле choices[0].message.content и результат собственной валидации, а не только статус HTTP s3.

Для vLLM сначала запустите совместимую модель по гайду vLLM и проверьте доступность сервера: его документация показывает vllm serve и OpenAI-совместимый сервис; необходимое железо определяется конкретной моделью, версией vLLM и настройками, здесь минимальная VRAM не заявлена s6. Для схемного ответа документация приводит response_format с type: json_schema и объектом json_schema с именем и схемой s5:

export MODEL='id-модели-которую-обслуживает-vllm'
python3 - <<'PY'
import json, os
schema = json.load(open('schema.json', encoding='utf-8'))
request = {
'model': os.environ['MODEL'],
'messages': [{'role': 'user', 'content': 'Извлеки vendor и amount из: Поставщик Альфа, сумма 1250 рублей. Верни JSON.'}],
'response_format': {'type': 'json_schema', 'json_schema': {'name': 'invoice', 'schema': schema}},
'stream': False
}
with open('request.json', 'w', encoding='utf-8') as f:
json.dump(request, f, ensure_ascii=False)
PY
curl --fail-with-body -sS http://127.0.0.1:8000/v1/chat/completions \
-H 'Content-Type: application/json' -d @request.json > response.json

Каждый блок перезаписывает request.json и response.json: выполняйте один runtime за раз или сохраняйте результаты под разными именами. Адрес и ID модели проверяйте в собственном стенде. Не заключайте, что формат делает три разных модели сопоставимыми по качеству.

Как проверить ответ, а не только увидеть фигурные скобки?

Проверка python3 -m json.tool response.json относится к обёртке API и не проверяет message.content. Для строгой проверки объекта установите валидатор в отдельное виртуальное окружение; затем разберите вложенную строку и сопоставьте факты вручную. JSON Schema валидирует типы и обязательные поля, но не умеет узнать исходную сумму из документа s7.

python3 -m venv .venv
.venv/bin/python -m pip install jsonschema
export ENGINE=ollama # или: llama, vllm
.venv/bin/python - <<'PY'
import json, os
from jsonschema import Draft202012Validator
schema = json.load(open('schema.json', encoding='utf-8'))
response = json.load(open('response.json', encoding='utf-8'))
engine = os.environ['ENGINE']
content = response['message']['content'] if engine == 'ollama' else response['choices'][0]['message']['content']
obj = json.loads(content)
Draft202012Validator.check_schema(schema)
Draft202012Validator(schema).validate(obj)
print(json.dumps(obj, ensure_ascii=False))
assert obj['vendor'] == 'Альфа' and obj['amount'] == 1250, 'значение не совпало с исходной строкой'
print('Форма и значения тестовой строки совпали; проверьте свои документы отдельно.')
PY

Если нарушены типы или отсутствует поле, оставьте результат на ручную проверку, не исправляйте ответ постфактум скрытой подменой. Если схема проходит, но сумма отличается от исходника, это ошибка извлечения, не успех; на документах добавьте сверку с оригиналом, тесты на пустые поля и экспертный разбор спорных случаев. Особенно осторожно относитесь к юридическим и бухгалтерским данным: структурирование не создаёт достоверность.

Следующий шаг: выполните один из трёх путей и проверьте объект по тестовой строке, после чего перейдите к сценарию извлечения документов для 1С с ручной сверкой реквизитов. Это проверяемый маршрут «задача → выбранная модель → железо → runtime → команда → валидный и фактически сверенный результат», а не обещание, что модель любого размера правильно поймёт любой счёт.

Источники

  1. Ollama — Structured Outputs — format, stream false, validation; snapshot s1-ollama-structured.html.. Получено 25.09.2026.
  2. Ollama — Chat API — POST /api/chat, message.content, format; snapshot s2-ollama-chat.html.. Получено 25.09.2026.
  3. llama.cpp — llama-server README — revision 84e76d8a23162eca70490da131945ebec1f09bf4; OpenAI-style /v1/chat/completions, response_format; snapshot s3-llama-server.md.. Получено 25.09.2026.
  4. llama.cpp — GBNF and JSON Schema limitations — revision 84e76d8a23162eca70490da131945ebec1f09bf4; Subset, unsupported keyword behavior, schema not inserted into prompt; snapshot s4-llama-grammars.md.. Получено 25.09.2026.
  5. vLLM — Structured Outputs — OpenAI-compatible response_format json_schema; snapshot s5-vllm-structured.html.. Получено 25.09.2026.
  6. vLLM — Online Serving — vllm serve, server address and API routes; snapshot s6-vllm-online.html.. Получено 25.09.2026.
  7. JSON Schema — Objects — properties, required, additionalProperties; snapshot s7-json-schema.html.. Получено 25.09.2026.