Гайд · TNWS AI

Как получить один JSON-ответ без стрима в Ollama API

WebLinuxmacOSWindows#Ollama#API#JavaScript
6 мин

Устанавливаем stream:false в /api/generate, получаем единый JSON-документ и проверяем done, response и служебные поля.

Задача и применимость

Цель этого руководства — получить один завершённый JSON для cron-задачи или серверного обработчика без NDJSON-парсера. Используется точный интерфейс: POST /api/generate: stream=false. Материал подходит для локального сервиса, тестового контура или внутреннего API, где Ollama доступна по доверенному адресу. Не публикуйте порт 11434 напрямую в интернет и не подставляйте в примеры реальные клиентские данные.

В отличие от общего обзора здесь есть проверяемый контракт: запрос, структура ответа, позитивные assertions и негативный сценарий. Название модели в коде — пример; выберите установленную модель, которая поддерживает нужную capability, и зафиксируйте её имя в конфигурации проекта.

Проверенные факты и первоисточники

Сведения проверены 13 сентября 2026 года по официальной документации Ollama. У /api/generate stream по умолчанию включён; при stream=false сервер возвращает один завершённый JSON-ответ. Дополнительно структура базовых ответов сверена с Generate API, Chat API и Embed API.

Цены и тарифы не приводятся. Доступность конкретной модели, качество ответа и аппаратная производительность зависят от локальной установки, поэтому они не выдаются за универсальный факт. Метрики берутся только из фактического JSON-ответа вашего запуска.

Подготовка безопасного тестового стенда

  1. Убедитесь, что Ollama запущена локально и endpoint доступен приложению.
  2. Получите список установленных моделей через официальный endpoint tags и выберите модель, подходящую capability.
  3. Не зашивайте имя модели в десятки файлов: храните его в переменной окружения или одном конфигурационном модуле.
  4. Используйте искусственные значения DEMO, тестовые SKU и вымышленные тексты.
  5. Задайте timeout и проверяйте HTTP status до разбора JSON.
  6. Сохраняйте исходный request без секретов, версию приложения и имя модели для воспроизводимости.
  7. Разделяйте сетевую ошибку, ошибку JSON, нарушение схемы и неверный бизнес-результат.

Ollama генерирует вероятностный результат. Даже при стабильном запросе текст может отличаться, поэтому тестируйте инварианты: наличие поля, роль сообщения, допустимое имя tool, формат аргумента, число векторов или ожидаемое вычисление. Не сравнивайте длинный ответ целиком со snapshot без необходимости.

Пошаговая реализация

  1. Сформируйте payload только из документированных полей.
  2. Укажите model и обязательный prompt, messages либо input.
  3. Для примеров, которым нужен один JSON, явно задайте stream:false.
  4. Отправьте POST с Content-Type: application/json.
  5. При status вне диапазона 200–299 прочитайте текст ошибки и остановите обработку.
  6. Разберите JSON и проверьте типы нужных полей до использования.
  7. Проверьте связь ответа с входом: роли, индексы, tool_name или сохранённые фрагменты.
  8. Не исполняйте tool по имени напрямую через eval или динамический import; используйте allowlist dispatcher.
  9. Ограничьте число шагов agent loop и общий timeout.
  10. Запишите технические счётчики отдельно от пользовательского результата.
  11. Выполните негативный тест и убедитесь, что он действительно отклоняется.
  12. Только после этого переносите код в основной сервис.

Готовый пример

const r=await fetch("http://localhost:11434/api/generate",{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({model:"gemma4",prompt:"Ответь одним словом: столица Франции",stream:false})});
if(!r.ok)throw Error(await r.text());
const data=await r.json();
if(data.done!==true)throw Error("ответ не завершён");
if(typeof data.response!=="string"||!data.response.trim())throw Error("пустой response");
console.log({answer:data.response.trim(),done_reason:data.done_reason});

Код рассчитан на Node.js с глобальным fetch. Его можно адаптировать к Python, сохранив ту же JSON-схему и последовательность проверок. Перед production добавьте AbortController, структурированный журнал без содержимого пользовательского prompt и retry только для безопасных транспортных сбоев.

Не повторяйте автоматически запрос, который уже мог вызвать внешний tool с побочным эффектом. Для инструментов записи используйте idempotency key на стороне приложения и сохраняйте состояние выполненного вызова. Модель предлагает аргументы, но доверять им без валидации нельзя.

Пример входа и ожидаемого результата

Вход: Короткий запрос о столице Франции с stream:false.

Ожидаемый результат: fetch разбирает один JSON; done=true, response непустой и содержит ответ «Париж» без необходимости собирать чанки.

Фактическая формулировка модели может отличаться. Критерием служат явно перечисленные свойства. Если критерий зависит от знания модели, используйте небольшой набор тест-кейсов и ручную выборочную проверку, но не выдумывайте процент точности без измерения.

Негативный тест

Удалите stream:false и попробуйте вызвать response.json(). Поток NDJSON нельзя считать одним JSON-документом; тест должен использовать построчный parser либо вернуть параметр.

Негативный тест запускайте автоматически рядом с позитивным. Он должен завершаться конкретной ошибкой и не оставлять частично записанное состояние. Для tools проверьте неизвестное имя, лишние аргументы, пропущенное обязательное поле и неверный тип. Для embeddings проверяйте количество, размерность и конечность каждого числа.

Production-обёртка

Создайте одну функцию client.request, которая добавляет endpoint, timeout, заголовок и обработку статусов. Поверх неё сделайте отдельные функции generate, chat и embed с собственными схемами валидации. Так изменение одного endpoint не разнесётся по всему приложению.

В журнал пишите request_id, model, endpoint, status, done_reason и длительности, но не полный prompt, системную инструкцию, tool output или embedding. Вектор сам по себе не следует считать анонимным: применяйте к хранилищу те же правила доступа и срока хранения, что и к исходному документу.

Для agent loop храните messages в рамках одного задания, ограничивайте их размер и число ходов. Assistant message с tool_calls нужно добавить в историю до сообщений role=tool. Все результаты независимых параллельных вызовов добавьте до следующего обращения к модели.

Копируемый шаблон проверки

Endpoint: POST /api/generate: stream=false
Задача: получить один завершённый JSON для cron-задачи или серверного обработчика без NDJSON-парсера
Модель и capability проверены: <да/нет>
Искусственный вход: <значение>
Обязательные поля request: <список>
Обязательные поля response: <список>
Позитивный invariant: <условие>
Негативный сценарий: <условие>
Timeout: <значение проекта>
Max turns для цикла: <значение>
Allowlist tools: <имена>
PII и секреты отсутствуют: <да>
Источник: https://docs.ollama.com/api/generate
Дата проверки: 13 сентября 2026 года

Финальная проверка результата

Выполните тест дважды: после холодного старта и при загруженной модели. Не смешивайте эти измерения. Для времени переводите наносекунды явно и сохраняйте исходные значения. Для text generation проверяйте done и done_reason, если они участвуют в логике. Для chat проверяйте role и непустой content либо валидные tool_calls.

После обновления Ollama или модели снова прогоните контрактные тесты. Имя поля из документации важнее визуального сходства ответа. Если capability не подтверждена для выбранной модели, не маскируйте это повторными запросами: выберите совместимую модель или измените UX.

Чек-лист

  • Slug и title уникальны.
  • Endpoint и поля сверены 13 сентября 2026 года.
  • Модель поддерживает нужную capability.
  • Вход содержит только искусственные данные.
  • Установлен timeout.
  • HTTP status проверяется до JSON.
  • Типы ключевых полей проверяются.
  • Никакие tool calls не исполняются через eval.
  • Аргументы tools проходят схему и allowlist.
  • Agent loop имеет max turns.
  • Индексы embeddings не смещаются.
  • Метрики считаются из реальных полей ответа.
  • Негативный тест завершается ожидаемо.
  • Логи не содержат prompt, PII и секреты.

FAQ

Почему в примерах указан stream:false?

Так проще получить один JSON и проверить итоговые служебные поля. Для потокового UX нужен отдельный NDJSON-парсер, аккумуляция чанков и обработка финального done.

Можно ли считать ответ модели детерминированным?

Нет. Проверяйте структуру и бизнес-инварианты. Если нужна воспроизводимость, фиксируйте поддерживаемые параметры и модель, но всё равно оставляйте валидацию результата.

Нужно ли доверять аргументам tool_calls?

Нет. Это сгенерированный ввод. Проверяйте имя по allowlist, аргументы по JSON Schema и полномочия пользователя до выполнения функции.

Можно ли логировать весь payload для отладки?

Только на искусственном стенде. В production журнал ограничивают техническими идентификаторами, статусом и измерениями; чувствительное содержимое редактируют или исключают.

Что делать при несовместимой модели?

Остановить сценарий с понятной диагностикой и выбрать модель, для которой нужная capability подтверждена. Нельзя выдавать случайный текст за корректный structured/tool/FIM результат.

Читайте также

Комментарии

Пока тихо. Скажите первое слово