Все статьи
Разработка3 минутыОбновлено 24 сентября 2026 г.

Streaming ответов AI через SSE: практическое руководство

SSE показывает ответ по частям, но добавляет состояния, которых нет у обычного JSON: неполный chunk, ошибка после старта, отмена на полуслове. Статья — про чтение потока, которое не врёт.

Chunk не равен событию

Клиент читает response.body как поток байтов, декодирует UTF-8 и копит буфер. Один сетевой chunk может принести несколько событий или половину строки — события делятся пустой строкой, хвост сохраняется до следующего чтения.

Разбирайте data отдельно от служебных маркеров. Нормальное завершение, отмена и обрыв — три разных состояния интерфейса и журнала, а не «поток кончился».

  • накапливать неполный хвост
  • разделять data и служебные события
  • закрывать reader при отмене

Данные отдельно, служебное отдельно

В потоке смешаны содержательные фрагменты и маркеры завершения. Пользователю рендерятся только данные, маркеры обрабатывает код: по ним определяется, ответ завершён, оборван или отменён.

Склейка «сырых» событий в финальный текст без разбора даёт мусор: половина chunk плюс маркер — это не ответ.

Ошибка до и после заголовков

До старта ответа сервер может вернуть обычный JSON с HTTP-ошибкой — это простой случай. После отправки заголовков статус уже не изменить, и ошибка приходит отдельным SSE-событием. Клиент, считающий любой текст успехом, врёт пользователю.

Сервер при этом отдельно финализирует usage и статус. Закрытый браузер после трёх фрагментов не означает, что upstream не доработал, — не делайте выводов по клиентской картине.

Маршрут и fallback

Streaming — свойство конкретного endpoint и модели, а не платформы в целом. Перед включением проверьте каталог и короткий тест; если поток не поддержан — используйте обычный режим, а не бесконечные повторы.

Интеграция обязана иметь непотоковый fallback. Пользователь не должен узнать об отсутствии streaming по зависшему экрану.

Время до первого события

Главная метрика стриминга — пауза до первого фрагмента, а не скорость всего ответа. Долгое молчание перед первым событием обычно означает проблему маршрута, а не «задумчивую модель».

Отмена пользователем — нормальное завершение: закройте reader, зафиксируйте частичный usage и не запускайте повтор. Пользователь уже ушёл, догонять его не надо.

Минимальный тест-план

Перед релизом прогоните шесть сценариев и для каждого запишите, что видит пользователь и какой итог ложится в usage. Непротестированный сценарий — это инцидент с неизвестной датой.

Особое внимание — ошибке после первого события и обрыву: именно там теряются деньги и рвётся UX.

  • короткий и длинный ответы
  • отмена и обрыв сети
  • ошибка после первого события

Открыть эту статью в Markdown·Поделиться в Telegram

Следующий материалКак подключить OpenCode к OpenAI-compatible API