Стриминг ответов: зачем нужен и как обрабатывать
Зачем нужен стриминг ответов модели, как устроен SSE и как обрабатывать поток чанков на Python и JavaScript — с примерами кода для единого API.
По умолчанию API возвращает ответ целиком — приходится ждать, пока модель допишет последнее слово. Стриминг отдаёт ответ по мере генерации, токен за токеном, как печатающий собеседник. Для интерфейсов это ключевая деталь: пользователь видит текст сразу, а не смотрит на спиннер. Разберём, зачем это нужно и как обработать поток. Определение — в глоссарии.
Зачем нужен стриминг
- Отзывчивость — первые слова появляются через доли секунды, а не после полной генерации. Воспринимаемая скорость выше, даже если суммарное время то же.
- Длинные ответы — при генерации большого текста ожидание целиком было бы мучительным; поток показывает прогресс.
- Ранняя реакция — можно начать читать или обрабатывать ответ, не дожидаясь конца, и при необходимости прервать генерацию.
Как устроен SSE
Технически стриминг работает поверх Server-Sent Events (SSE) — сервер держит соединение открытым и шлёт события по мере готовности. Каждое событие — строка вида data: {…} с очередным фрагментом (чанком). Поток завершается маркером data: [DONE]. Чтобы включить режим, добавьте в запрос stream: true.
data: {"choices":[{"delta":{"content":"При"}}]}data: {"choices":[{"delta":{"content":"вет"}}]}data: {"choices":[{"delta":{"content":"!"}}]}data: [DONE]Обработка потока на Python
from openai import OpenAIclient = OpenAI(base_url="https://ai-gatewey.ru/v1",api_key="sk-aigate-ваш_ключ",)stream = client.chat.completions.create(model="anthropic/claude-sonnet-5.5",messages=[{"role": "user", "content": "Расскажи о стриминге в двух предложениях"}],stream=True,# без этой опции usage в потоке не придёт вовсеstream_options={"include_usage": True},)for chunk in stream:# последний чанк с usage приходит с пустым choices — без проверки будет IndexErrorif not chunk.choices:continuedelta = chunk.choices[0].delta.contentif delta:print(delta, end="", flush=True)print()Обработка потока на JavaScript
import OpenAI from "openai";const client = new OpenAI({baseURL: "https://ai-gatewey.ru/v1",apiKey: "sk-aigate-ваш_ключ",});const stream = await client.chat.completions.create({model: "anthropic/claude-sonnet-5.5",messages: [{ role: "user", content: "Расскажи о стриминге в двух предложениях" }],stream: true,});for await (const chunk of stream) {const delta = chunk.choices[0]?.delta?.content;if (delta) process.stdout.write(delta);}process.stdout.write("\n");usage сам собой в поток не попадёт: его присылают только при stream_options={"include_usage": True}, и приходит он последним чанком — у которого choices пустой, так что обращение к choices[0] без проверки уронит цикл. Копите фрагменты delta.content в буфер, если нужен полный ответ целиком.Список моделей с поддержкой стриминга и детали протокола — на странице возможности стриминг.
Частые вопросы
Стриминг ускоряет ответ модели?
Нет, сама генерация идёт с той же скоростью, и суммарное время не меняется. Стриминг улучшает воспринимаемую скорость: первые слова появляются сразу, поэтому интерфейс кажется живее, чем при ожидании полного ответа.
Стриминг стоит дороже обычного запроса?
Нет, расход токенов идентичен — меняется только способ доставки ответа. Поле usage с итоговым числом токенов приходит последним чанком, если запросить его параметром stream_options; иначе считайте расход по кабинету.
Как собрать полный ответ из потока?
Накапливайте фрагменты delta.content из каждого чанка в строку-буфер до маркера [DONE]. В примерах на Python и JavaScript это делается в цикле по потоку — добавьте конкатенацию вместо вывода на экран.