Обычный запрос возвращает ответ целиком: пока модель не закончит генерацию, клиент ждёт. Длинный ответ так может собираться десятки секунд. Стриминг решает эту проблему — ответ передаётся по мере генерации, и первые слова появляются почти сразу.

В теле запроса указывается "stream": true. Всё остальное — адрес, ключ, model и messages — не меняется. Официальные библиотеки в этом режиме отдают итерируемый объект вместо готового ответа.

curl "https://api.proxyapi.ru/v1/chat/completions" \
    -H "Content-Type: application/json" \
    -H "Authorization: Bearer <КЛЮЧ>" \
    -d '{
        "model": "anthropic/claude-haiku-4-5",
        "messages": [
            {"role": "user", "content": "Расскажи о Байкале."}
        ],
        "stream": true
    }'

Ответ приходит по протоколу Server-Sent Events: последовательность строк, каждая из которых начинается с data: и содержит очередной фрагмент в JSON. Поток закрывается строкой data: [DONE].

data: {"choices":[{"index":0,"delta":{"content":"Байкал"}}]}

data: {"choices":[{"index":0,"delta":{"content":" — самое"}}]}

data: [DONE]

Главное отличие от обычного ответа: текст лежит не в message.content, а в delta.content, и в каждом фрагменте это лишь кусочек. Полный ответ получается склеиванием всех фрагментов подряд.

Признак завершения приходит там же, где и в обычном ответе: в последнем фрагменте с текстом заполняется finish_reason.

Самым последним, уже после текста, приходит служебный фрагмент: массив choices в нём пустой, зато есть объект usage с итоговым расходом токенов.

data: {"choices":[],"usage":{"prompt_tokens":18,"completion_tokens":214,"total_tokens":232}}

По этим числам считается стоимость запроса. Официальные библиотеки такой фрагмент обрабатывают сами, а вот самописный разбор потока лучше сразу писать с расчётом на него — иначе обращение к choices[0] на последнем фрагменте уронит код.

Стриминг работает там, где модель пишет ответ постепенно: /v1/chat/completions, /v1/responses и /v1/messages. У эмбеддингов, изображений и пакетной обработки промежуточного результата не бывает, поэтому ответ приходит целиком.

Частые вопросы

Чем delta отличается от message?

В обычном ответе весь текст лежит в message.content. В потоке каждый фрагмент несёт только добавку к уже полученному тексту, и лежит она в delta.content. Полный ответ собирается склеиванием фрагментов.

Почему в последнем фрагменте пустой массив choices?

Это служебный фрагмент с итоговым расходом токенов в поле usage. Текста в нём нет, поэтому choices пустой. Код, который разбирает поток самостоятельно, должен это учитывать.

Стриминг работает с моделями любого вендора?

Да. Достаточно добавить stream в запрос — формат потока при этом одинаковый для всех текстовых моделей каталога.

Всегда ли стоит включать стриминг?

Нет. Он нужен там, где ответ читает человек и важно не заставлять его ждать. Для фоновых задач обычный ответ удобнее: он приходит одним объектом, который не нужно собирать по частям.

Последняя редакция: 2 сентября 2026 г.