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

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

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

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

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

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

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

Этапы разработки

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

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

Без стриминга пользователь смотрит на пустой экран 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-ответов на вашем сайте. Свяжитесь с нами для расчёта сроков и стоимости.

Интеграция AI: чат-боты, RAG, семантический поиск, рекомендации

В 8 из 10 проектов «AI-чат-бот» оказывается дорогой обёрткой над GPT-4o с системным промптом. Без доступа к реальным данным компании. Пользователь спрашивает «сколько стоит тариф Премиум» — бот галлюцинирует цену из воздуха. Спрашивает «когда придёт заказ» — получает вежливое «напишите в поддержку». Это не интеграция — это имитация. Мы за 5 лет внедрили RAG-решения в 30+ проектах: от интернет-магазинов до медицинских порталов. Гарантируем: полезная AI-помощь начинается там, где модель читает ваши документы, а не общие ответы.

Как мы строим RAG-системы?

Retrieval-Augmented Generation — стандартная архитектура: запрос → поиск релевантных фрагментов в векторной БД → вставка найденного в контекст → ответ модели. Но дьявол в деталях реализации. Разберём ключевые узлы, которые определяют качество.

Chunking. Резать документ на куски по 500 токенов без оглядки на структуру — гарантия потери смысла. Если разрез пришёлся на середину абзаца, контекст разрывается. Решение — рекурсивный RecursiveCharacterTextSplitter с overlap 10–15% для документации. Для контрактов и инструкций используем семантический сплиттер: выделяем заголовки, списки, блоки кода — каждый раздел становится независимым чанком. Разница в качестве поиска: на одном медицинском проекте precision вырос с 0.55 до 0.84 только за счёт правильной нарезки.

Модель эмбеддингов. Для русскоязычных текстов intfloat/multilingual-e5-large даёт заметный прирост точности против устаревшей text-embedding-ada-002. По нашим замерам, NDCG@10 на тестовой выборке из 10 000 пар «запрос-документ» на 12% выше. OpenAI text-embedding-3-large — хороший вариант для англоязычного контента, но для русского рекомендуем BAAI/bge-m3 или упомянутую e5-large.

Векторная БД. Если у вас уже стоит PostgreSQL — pgvector экономит ресурсы. Ставим расширение CREATE EXTENSION vector, добавляем колонку vector(1024), создаём HNSW-индекс. На проекте с 80 000 статей поддержки p95 поиска — 12 мс. Этого хватает. Для каталогов с миллионами единиц — Qdrant или Weaviate: нативный гибридный поиск и шардирование «из коробки».

Что даёт гибридный поиск?

Только векторный поиск слеп к точным совпадениям: артикулы «ABC-123», имена собственные, аббревиатуры теряются. Только полнотекстовый поиск не улавливает синонимы и перефразирования. Комбинация через RRF (Reciprocal Rank Fusion) даёт лучшее из двух миров: BM25 + векторный поиск, результаты смешиваются. На практике recall@20 растёт с 0.65 до 0.92 — разница заметна пользователю.

Reranking — финальный фильтр: top-20 кандидатов из гибридного поиска прогоняем через cross-encoder cross-encoder/ms-marco-MiniLM-L-6-v2. Он добавляет 50–100 мс к ответу, но relevance поднимает ещё на 5–10%. Без reranking чат-бот может показывать нерелевантные документы.

Семантический поиск на сайте

Поиск «удобные кожаные кресла» должен находить товары с описанием «мягкие стулья из натуральной кожи» — обычный LIKE-поиск не способен. Наша архитектура: при добавлении товара/поста автоматически генерируем эмбеддинг через multilingual-e5-large, сохраняем в pgvector. На запросе — эмбеддим его той же моделью, ищем ближайших соседей через cosine distance с HNSW-индексом. Для каталога из 100 000 позиций индекс строится 3 минуты, в памяти ~400 Мбайт (1536-мерные векторы). Cреднее время поиска — 20 мс.

Рекомендательные системы

Коллаборативная фильтрация («пользователи, похожие на вас, покупали X») требует истории — минимум 2–3 месяца данных с 1000+ активных пользователей. Для стартапов или малых проектов используем content-based: эмбеддинг текущего товара → поиск ближайших соседей по косинусной близости. Когда накапливается статистика (обычно 15–20 взаимодействий на пользователя), переключаемся на гибридную модель LightFM. Она объединяет и поведение, и признаки товаров. У нас в e-commerce проекте с 50 000 SKU гибридная модель повысила конверсию в рекомендательный блок на 18% (A/B-тест длился 2 недели).

Стриминг ответов

Пользователь не обязан ждать, пока модель сгенерирует весь текст — это убивает UX. Server-Sent Events (SSE) — протокол для стриминга токенов. OpenAI SDK поддерживает stream: true, возвращая AsyncIterator. На фронтенде — Vercel AI SDK (useChat) или самописный EventSource. Типичная ошибка: использовать WebSocket для однонаправленного стрима — SSE проще (меньше кода, встроенный реконнект). Стек: Node.js + SSE + React.

Оркестрация агентов

Простой чат-бот отвечает. Агент — выполняет действия: создаёт тикет в Jira, проверяет статус заказа в CRM, бронирует слот в календаре. Для оркестрации используем LangGraph: граф состояний, где каждый узел — вызов модели или инструмента. Vercel AI SDK useChat + tools для Next.js позволяет добавить интеграцию в 10 строк кода. Главная сложность — надёжность: модель иногда вызывает не тот инструмент или передаёт кривые параметры. Защита — Zod-схемы на каждый инструмент и structured outputs для гарантии JSON.

Что входит в работу

Этап Результат Срок
Аудит данных и бизнес-логики Карта источников, формат документов, оценка качества 1–2 дня
Прототип RAG или рекомендательной системы Демонстрация с метриками (recall, precision, latency) 1–2 недели
Интеграция в существующее веб-приложение API-эндпоинты, интерфейс для чат-бота/поиска 1–2 недели
A/B-тестирование и оптимизация Отчёт по метрикам (CTR, конверсия, hallucination rate) 1 неделя
Документация и обучение команды Руководство по эксплуатации, код-ревью 2–3 дня

Дополнительно: мы передаём исходный код векторизатора, дашборды мониторинга (Langfuse), доступ к админке для обновления базы знаний. Постпродакшн-поддержка — 1 месяц бесплатно.

Сроки

Задача Ориентировочный срок
RAG-чат-бот на базе существующей базы знаний 3–6 недель
Семантический поиск по каталогу 2–4 недели
Рекомендательная система с A/B-тестированием 6–10 недель
Мультиагентная система с интеграциями от 8 недель

Стоимость рассчитывается индивидуально после знакомства с проектом. Оценим ваш проект за 1 день. Свяжитесь с нами — расскажем, как превратить AI из игрушки в инструмент, который приносит прибыль.