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-стрімінгом текст з'являється токен за токеном — сприймається як миттєва відповідь, хоча загальний час не змінився. SSE-стрімінг AI-відповідей у 20 разів швидше за традиційне POST-очікування за часом до першого токена: на одному з проєктів з GPT-4 час до першого токена скоротився з 3 секунд до 150 мс, хоча повна відповідь генерувалася ті ж 10 секунд. За 5+ років досвіду ми реалізували стрімінг на 30+ проєктах — від чат-ботів до комплексних AI-асистентів з історією діалогу. Досвід включає інтеграцію з OpenAI, Anthropic, локальними моделями. Впровадження SSE дозволяє знизити час очікування на 80% і зменшити витрати на підтримку на 30% (економія до $3000 на рік для проєктів із 1000+ запитів). Прискорення виведення першого токена підвищує конверсію на 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 вручну — ми завжди включаємо таку логіку в рішення.

Що входить у роботу (deliverables)

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

Процес впровадження під ключ

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

Що ви отримуєте в результаті

  • Робочий серверний ендпоінт SSE з інтеграцією вашої LLM.
  • React-хук для клієнта з можливістю зупинки стріму.
  • Компонент для відображення стрімлінгового markdown-тексту.
  • Конфігурація Nginx для коректної роботи SSE.
  • Обробка помилок і механізм reconnect.
  • Документація API та приклади використання.
  • Підтримка після впровадження — гарантуємо стабільну роботу.

Терміни та вартість

Базова реалізація (ендпоінт + хук) — від $500 (2–3 дні). Повноцінне рішення з історією, markdown-рендерингом та кнопкою зупинки — від $1200 (4–5 днів). Ми оцінимо ваш проект безкоштовно — пишіть нам. Наша компанія має 5+ років досвіду в AI-інтеграціях та реалізувала 30+ проєктів зі стрімінгом. Отримайте консультацію — допоможемо впровадити стрімінг AI-відповідей на вашому сайті.

Інтеграція AI у веб-додатки: чат-боти, RAG, семантичний пошук

У 8 з 10 проектів «AI-чат-бот» виявляється дорогою обгорткою над GPT-4o з системним промптом. Без доступу до реальних даних компанії. Користувач питає «скільки коштує тариф Преміум» — бот галюцинує ціну з повітря. Питає «коли прийде замовлення» — отримує ввічливе «напишіть у підтримку». Це не інтеграція AI у веб-додатки — це імітація. Ми за 5 років впровадили RAG-рішення в 30+ проектах: від інтернет-магазинів до медичних порталів. Гарантуємо: корисна AI-допомога починається там, де модель читає ваші документи, а не загальні відповіді. Закажіть консультацію, щоб отримати план інтеграції для вашого проекту.

Побудова RAG-систем: чанкінг, ембедінги, векторна БД

Retrieval-Augmented Generation — стандартна архітектура: запит → пошук релевантних фрагментів у векторній БД → вставка знайденого в контекст → відповідь моделі. Але диявол у деталях реалізації.

Чанкінг. Різати документ на шматки по 500 токенів без огляду на структуру — гарантія втрати сенсу. Якщо розріз припав на середину абзацу, контекст розривається. Рішення — рекурсивний RecursiveCharacterTextSplitter з overlap 10–15% для документації. Для контрактів та інструкцій використовуємо семантичний спліттер: виділяємо заголовки, списки, блоки коду — кожен розділ стає незалежним чанком. Результат: на медичному проекті precision зріс з 0.55 до 0.84 лише завдяки правильному нарізанню, скорочення витрат на підтримку на 40% заощадило 80 000 грн на місяць.

Модель ембедингів. Для україномовних текстів intfloat/multilingual-e5-large дає помітний приріст точності проти застарілої text-embedding-ada-002. Наші виміри: NDCG@10 на 12% вищий. text-embedding-3-large хороший для англомовного контенту, але для української рекомендуємо BAAI/bge-m3.

Векторна БД. Якщо вже стоїть PostgreSQL — pgvector заощаджує ресурси. Ставимо розширення CREATE EXTENSION vector, додаємо колонку vector(1024), створюємо HNSW-індекс. На проекті з 80 000 статей p95 пошуку — 12 мс. Для каталогів з мільйонами одиниць — Qdrant або Weaviate: нативний гібридний пошук і шардування «з коробки».

Технічна реалізація pgvector Індекс будується за 3 хвилини на 100 000 позицій, займає ~400 МБ (1536-вимірні вектори). Пошук cosine distance з HNSW-індексом — 20 мс.

Кроки реалізації RAG:

  1. Збір та структурування даних (очищення, метадані).
  2. Вибір моделі ембедингів (multilingual-e5-large або bge-m3).
  3. Створення чанків (семантичний спліт із 10–15% overlap).
  4. Індексація в pgvector (або Qdrant).
  5. Інтеграція пошуку (гібрид BM25 + векторний через RRF) та реранкінг.

Що дає гібридний пошук?

Лише векторний пошук сліпий до точних збігів: артикули «ABC-123», власні назви, абревіатури губляться. Лише повнотекстовий не вловлює синоніми та перефразування. Комбінація через RRF (Reciprocal Rank Fusion) дає краще з двох світів: BM25 + векторний пошук, результати змішуються. На практиці recall@20 зростає з 0.65 до 0.92 — на 30% вищий за чисто векторний. Реранкінг через cross-encoder cross-encoder/ms-marco-MiniLM-L-6-v2 додає 50–100 мс до відповіді, але relevance піднімає ще на 5–10%.

Як реалізувати семантичний пошук у веб-додатку?

Пошук «зручні шкіряні крісла» має знаходити товари з описом «м'які стільці з натуральної шкіри» — звичайний LIKE-пошук не здатний. Архітектура: при додаванні товару/посту автоматично генеруємо ембединг через multilingual-e5-large, зберігаємо в pgvector. На запиті — ембедимо його тією ж моделлю, шукаємо найближчих сусідів через cosine distance з HNSW-індексом. Середній час пошуку — 20 мс.

Рекомендаційні системи: коли потрібна гібридна модель?

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

Стрімінг відповідей через SSE

Користувач не зобов'язаний чекати, поки модель згенерує весь текст — це вбиває 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 дозволяє додати інтеграцію в 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 з іграшки на інструмент, що приносить прибуток. Закажіть консультацію, щоб дізнатись, як інтегрувати AI у ваш веб-додаток.