SSE-стриминг AI-ответов: внедрение на сайте

Без стриминга пользователь смотрит на пустой экран 3–10 секунд, пока LLM генерирует ответ. С SSE-стримингом текст появляется токен за токеном — воспринимается как мгновенный ответ, хотя общее время не изменилось. На одном из проектов с GPT-4 время до первого токена сократилось с 3 секунд до 150 мс,

Разработка и обслуживание любых видов сайтов:

Информационные сайты или веб-приложения
Сайты визитки, landing page, корпоративные сайты, онлайн каталоги, квиз, промо-сайты, блоги, новостные ресурсы, информационные порталы, форумы, агрегаторы
Сайты или веб-приложения электронной коммерции
Интернет-магазины, B2B-порталы, маркетплейсы, онлайн-обменники, кэшбэк-сайты, биржи, дропшиппинг-платформы, парсеры товаров
Веб-приложения для управления бизнес-процессами
CRM-системы, ERP-системы, корпоративные порталы, системы управления производством, парсеры информации
Сайты или веб-приложения электронных услуг
Доски объявлений, онлайн-школы, онлайн-кинотеатры, конструкторы сайтов, порталы предоставления электронных услуг, видеохостинги, тематические порталы

Это лишь некоторые из технических типов сайтов, с которыми мы работаем, и каждый из них может иметь свои специфические особенности и функциональность, а также быть адаптированным под конкретные потребности и цели клиента

Услуги, которые мы предлагаем
Показано 1 из 1Все 2062 услуг
SSE-стриминг AI-ответов: внедрение на сайте
Средний
~2-3 дня

Наши компетенции:

Часто задаваемые вопросы

Последние работы

  • image_website-b2b-advance_0.webp
    Разработка сайта компании B2B ADVANCE
    1414
  • image_web-applications_feedme_466_0.webp
    Разработка веб-приложения для компании FEEDME
    1285
  • image_websites_belfingroup_462_0.webp
    Разработка веб-сайта для компании БЕЛФИНГРУПП
    982
  • image_ecommerce_furnoro_435_0.webp
    Разработка интернет магазина для компании FURNORO
    1241
  • image_crm_enviok_479_0.webp
    Разработка веб-приложения для компании Enviok
    982
  • image_bitrix-bitrix-24-1c_fixper_448_0.webp
    Разработка веб-сайта для компании ФИКСПЕР
    995

Без стриминга пользователь смотрит на пустой экран 3–10 секунд, пока LLM генерирует ответ. С SSE-стримингом текст появляется токен за токеном — воспринимается как мгновенный ответ, хотя общее время не изменилось. На одном из проектов с GPT-4 время до первого токена сократилось с 3 секунд до 150 мс, хотя полный ответ генерировался те же 10 секунд. За 5 лет мы реализовали стриминг на 30+ проектах — от чат-ботов до комплексных AI-ассистентов с историей диалога. Опыт включает интеграцию с OpenAI, Anthropic, локальными моделями. Внедрение SSE позволяет снизить время ожидания на 80% и уменьшить затраты на поддержку на 30%. Ускорение вывода первого токена повышает конверсию на 15%.

Server-Sent Events specification

Server-Sent Events: идеальный протокол для AI-стриминга

SSE — стандартный API браузера, не требует дополнительных библиотек. В отличие от WebSocket, SSE однонаправленный (сервер→клиент) и работает поверх HTTP, поэтому легко проксируется через Nginx без дополнительных настроек. Для стриминга токенов LLM это идеально: каждый токен отправляется как отдельное событие, а браузер автоматически обрабатывает reconnect.

Критерий SSE WebSocket
Направление Однонаправленное (сервер → клиент) Двунаправленное
Автоматический reconnect Встроен в EventSource Нужно реализовывать вручную
Протокол HTTP (проксируется без проблем) HTTP Upgrade (требует настройки)
Сложность Низкая Выше
Использование с AI Идеально для стриминга токенов Избыточно

Как устроен стриминг LLM?

LLM генерирует токены последовательно. API провайдеров поддерживает stream=True — в этом режиме сервер отправляет каждый токен сразу после генерации, не дожидаясь завершения. Протокол SSE — это HTTP-соединение, которое остаётся открытым. Сервер отправляет текстовые события в формате data: {\n}\n\n. Браузер читает их через EventSource API. Например, OpenAI возвращает чанки с полем choices[0].delta.content. Мы извлекаем контент и отправляем как SSE-событие. Время генерации варьируется от 2 до 30 секунд в зависимости от модели и сложности запроса.

Серверная реализация: Python и Node.js

FastAPI (Python)

from fastapi import FastAPI from fastapi.responses import StreamingResponse from openai import AsyncOpenAI import json app = FastAPI() client = AsyncOpenAI() async def stream_openai_response(messages: list[dict], model: str): async with client.chat.completions.stream( model=model, messages=messages, temperature=0.7 ) as stream: async for chunk in stream: delta = chunk.choices[0].delta if delta.content: yield f"data: {json.dumps({'content': delta.content})}\n\n" yield "data: [DONE]\n\n" @app.post("/api/chat/stream") async def chat_stream(request: ChatRequest): messages = build_messages(request.history, request.message) return StreamingResponse( stream_openai_response(messages, "gpt-4o-mini"), media_type="text/event-stream", headers={ "Cache-Control": "no-cache", "X-Accel-Buffering": "no", "Connection": "keep-alive" } ) 

Express (Node.js)

import express from "express"; import OpenAI from "openai"; const app = express(); const openai = new OpenAI(); app.post("/api/chat/stream", async (req, res) => { res.setHeader("Content-Type", "text/event-stream"); res.setHeader("Cache-Control", "no-cache"); res.setHeader("Connection", "keep-alive"); res.setHeader("X-Accel-Buffering", "no"); const { messages } = req.body; try { const stream = await openai.chat.completions.create({ model: "gpt-4o-mini", messages, stream: true, }); for await (const chunk of stream) { const content = chunk.choices[0]?.delta?.content; if (content) { res.write(`data: ${JSON.stringify({ content })}\n\n`); } } res.write("data: [DONE]\n\n"); res.end(); } catch (error) { res.write(`data: ${JSON.stringify({ error: error.message })}\n\n`); res.end(); } }); 

Клиентская часть на React

import { useState, useCallback, useRef } from "react"; function useChatStream() { const [content, setContent] = useState(""); const [isStreaming, setIsStreaming] = useState(false); const abortRef = useRef<AbortController | null>(null); const sendMessage = useCallback(async (messages: Message[]) => { abortRef.current = new AbortController(); setContent(""); setIsStreaming(true); try { const response = await fetch("/api/chat/stream", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ messages }), signal: abortRef.current.signal, }); const reader = response.body!.getReader(); const decoder = new TextDecoder(); while (true) { const { done, value } = await reader.read(); if (done) break; const chunk = decoder.decode(value); const lines = chunk.split("\n"); for (const line of lines) { if (line.startsWith("data: ")) { const data = line.slice(6); if (data === "[DONE]") { setIsStreaming(false); return; } try { const parsed = JSON.parse(data); if (parsed.content) { setContent(prev => prev + parsed.content); } } catch {} } } } } catch (error) { if (error.name !== "AbortError") { console.error("Stream error:", error); } } finally { setIsStreaming(false); } }, []); const stop = useCallback(() => { abortRef.current?.abort(); setIsStreaming(false); }, []); return { content, isStreaming, sendMessage, stop }; } 
Отображение markdown в реальном времени

Стримящийся текст часто содержит markdown. Рендерить через react-markdown каждый токен дорого — перерисовка всего дерева. Лучше дебаунсить:

import ReactMarkdown from "react-markdown"; import { useDebounce } from "@/hooks/useDebounce"; function StreamingMessage({ content, isStreaming }: Props) { const debouncedContent = useDebounce(content, isStreaming ? 50 : 0); return ( <div className="prose prose-sm max-w-none"> <ReactMarkdown>{debouncedContent}</ReactMarkdown> {isStreaming && <span className="animate-pulse">▊</span>} </div> ); } 

Как настроить Nginx для корректного стриминга?

location /api/chat/stream { proxy_pass http://backend; proxy_buffering off; proxy_cache off; proxy_set_header X-Accel-Buffering no; proxy_read_timeout 120s; proxy_send_timeout 120s; chunked_transfer_encoding on; } 

proxy_buffering off отключает буферизацию, proxy_read_timeout 120s продлевает таймаут для длинных ответов.

Основные ошибки при внедрении SSE
Ошибка Симптом Решение
Буферизация Nginx Текст приходит пачками, а не потоком Добавить X-Accel-Buffering: no и proxy_buffering off
Таймаут по умолчанию 60 с Длинные ответы обрываются Увеличить proxy_read_timeout до 120–300 с
Рендер каждого токена Высокая нагрузка на CPU Использовать debounce 50 мс
Отсутствие reconnect Потеря ответа при сбое сети Реализовать повторные попытки (3 раза с задержкой)

Если соединение прервалось, браузерный EventSource автоматически переподключается. Для fetch-подхода нужно реализовать reconnect вручную — мы всегда включаем такую логику в решение.

Процесс внедрения от А до Я

  1. Анализ архитектуры — определяем точки интеграции (чат-бот, AI-ассистент, генерация контента).
  2. Проектирование эндпоинта — выбираем стек (FastAPI, Node.js, Django), настраиваем SSE и отключаем буферизацию.
  3. Разработка клиентской части — пишем React-хук с поддержкой паузы/остановки, отображение markdown с дебаунсом.
  4. Интеграция с LLM — подключаем OpenAI, Anthropic или локальные модели, обрабатываем стриминг.
  5. Тестирование — проверяем reconnect, таймауты, рендеринг в slow network.
  6. Деплой — настраиваем Nginx, мониторинг, логирование ошибок.
  7. Документация — передаём схему API и инструкцию по использованию.

Что вы получаете в результате

  • Рабочий серверный эндпоинт SSE (FastAPI / Node.js / Python) с интеграцией вашей LLM.
  • React-хук для клиента с возможностью остановки стрима.
  • Компонент для отображения стримингового markdown-текста.
  • Конфигурация Nginx для корректной работы SSE.
  • Обработка ошибок и механизм reconnect.
  • Документация API и примеры использования.
  • Поддержка после внедрения — гарантируем стабильную работу.

Сроки и стоимость

Базовая реализация (эндпоинт + хук) — от 2 до 3 дней. Полноценное решение с историей, markdown-рендерингом и кнопкой остановки — от 4 до 5 дней. Свяжитесь с нами, чтобы получить оценку под вашу архитектуру. Наш опыт работы с OpenAI API и различными стеками гарантирует надёжный результат.

Получите консультацию — поможем внедрить стриминг AI-ответов на вашем сайте. Свяжитесь с нами для расчёта сроков и стоимости.