Розробка потокового виведення LLM (Streaming Responses) під ключ

Ви запустили чат-бота на LLM, і користувачі скаржаться на довге очікування відповіді. Затримка в 5–10 секунд вбиває конверсію — кожен токен відображається на клієнті практично миттєво, створюючи ефект живого набору тексту. Це критично важливо для чат-ботів, асистентів і будь-якого інтерфейсу, де кор

Напрямки AI-розробки

Часті запитання

Останні роботи

  • image_website-b2b-advance_0.webp
    Розробка сайту компанії B2B ADVANCE
    1441
  • image_web-applications_feedme_466_0.webp
    Розробка веб-додатків для компанії FEEDME
    1301
  • image_websites_belfingroup_462_0.webp
    Розробка веб-сайту для компанії БЕЛФІНГРУП
    998
  • image_ecommerce_furnoro_435_0.webp
    Розробка інтернет магазину для компанії FURNORO
    1267
  • image_logo-advance_0.webp
    Розробка логотипу компанії B2B Advance
    713
  • image_crm_enviok_479_0.webp
    Розробка веб-додатків для компанії Enviok
    1003

Ви запустили чат-бота на LLM, і користувачі скаржаться на довге очікування відповіді. Затримка в 5–10 секунд вбиває конверсію — кожен токен відображається на клієнті практично миттєво, створюючи ефект живого набору тексту. Це критично важливо для чат-ботів, асистентів і будь-якого інтерфейсу, де користувач чекає відповіді. Одного разу ми впроваджували стрімінг для fintech-сервісу з 10 000 одночасних користувачів: після переходу на потоковий вивід latency p99 знизилась з 12 с до 400 мс, а конверсія зросла на 30%. Потоковий вивід прискорює час першої відповіді в 10–20 разів порівняно з пакетною видачею. Рішення — потоковий вивід (streaming responses). Ми впроваджуємо стрімінг відповідей LLM за допомогою Server-Sent Events (SSE) та WebSocket, знижуючи видиму затримку до 200–500 мс. Наш досвід — 50+ інтеграцій LLM для фінтеху, e-commerce та SaaS. Гарантуємо якість: latency p99 < 500 мс.

Згідно з документацією MDN, SSE використовує стандартне HTTP-з'єднання та автоматично перепідключається при обриві. Це робить його оптимальним для більшості сценаріїв чатів.

Приклад замірів продуктивності
  • latency p99 до впровадження: 12 с
  • latency p99 після впровадження: 400 мс
  • Конверсія зросла на 30%
  • Економія інфраструктури: до 40%

Як працює потоковий вивід LLM?

Замість того, щоб чекати повну відповідь, сервер відправляє клієнту токени в міру генерації. Використовується протокол SSE (Server-Sent Events) — текстовий потік по HTTP для потокової передачі даних. Клієнт отримує data: чанки з JSON-полем text. Коли модель завершує, відправляється done: true. SSE в 2–3 рази простіший у реалізації, ніж WebSocket, і не вимагає додаткових бібліотек на бекенді. Без стрімінгу користувач чекає 5–10 секунд — за цей час він йде. Зі стрімінгом перша літера з'являється через 200 мс. Таким чином, користувач бачить потоковий вивід відповідей у реальному часі, що забезпечує потокове виведення відповідей без затримок.

Чому SSE, а не WebSocket?

SSE простіший: немає рукостискання, автоматичне перепідключення, працює через HTTP/2. WebSocket дає двонаправленість, але для простого чату надлишковий. Порівняємо:

Критерій SSE WebSocket
Протокол HTTP/1.1, HTTP/2 власний (ws/wss)
Підтримка бекенду FastAPI, Django, будь-який ASGI FastAPI, Django Channels
Авто-перепідключення вбудоване (EventSource) потрібно реалізувати вручну
Двонаправленість ні (тільки сервер->клієнт) так
Пропускна здатність ~ 1-2 KB/s на з'єднання вища, залежить від реалізації

Для 90% випадків SSE — оптимальний вибір. WebSocket виправданий при двосторонній передачі (наприклад, голосові команди).

Підтримувані моделі

Практично всі сучасні LLM підтримують стрімінг: Claude (Anthropic), GPT-4o (OpenAI), LLaMA 3 (через vLLM), Gemini (Google), Mistral, Qwen. Для кожної моделі потрібно використовувати відповідний SDK з streaming-режимом. Наприклад, AsyncAnthropic і AsyncOpenAI надають асинхронні генератори токенів. Ми обираємо модель під ваш сценарій: для чат-ботів — Claude Sonnet або GPT-4o, для real-time асистентів — LLaMA 3 через vLLM з мінімальною затримкою. Claude Sonnet забезпечує в 2 рази меншу затримку, ніж GPT-4o при аналогічній якості.

Обробка переривань та backpressure

Клієнт може скасувати запит у будь-який момент. Якщо не обробити, модель продовжить генерацію — зайві токени та гроші. Ми реалізуємо cancel_event:

import asyncio async def stream_with_cancellation( messages: list[dict], cancel_event: asyncio.Event, ) -> AsyncGenerator[str, None]: """Стрімінг з підтримкою скасування""" async with anthropic_client.messages.stream( model="claude-sonnet-4-5", max_tokens=2048, messages=messages, ) as stream: async for text in stream.text_stream: if cancel_event.is_set(): stream.close() yield f"data: {json.dumps({'cancelled': True})}\n\n" return yield f"data: {json.dumps({'text': text})}\n\n" 

Додатково використовуємо backpressure: при переповненні буфера клієнта призупиняємо ітерацію генератора. Це запобігає відвалу з'єднання.

Backend: FastAPI + SSE

from fastapi import FastAPI from fastapi.responses import StreamingResponse from anthropic import AsyncAnthropic from openai import AsyncOpenAI import asyncio import json app = FastAPI() anthropic_client = AsyncAnthropic() openai_client = AsyncOpenAI() async def stream_anthropic(messages: list[dict], system: str = "") -> AsyncGenerator[str, None]: """Генератор для Claude streaming""" async with anthropic_client.messages.stream( model="claude-sonnet-4-5", max_tokens=2048, system=system, messages=messages, ) as stream: async for text in stream.text_stream: yield f"data: {json.dumps({'text': text})}\n\n" yield f"data: {json.dumps({'done': True})}\n\n" async def stream_openai(messages: list[dict]) -> AsyncGenerator[str, None]: """Генератор для OpenAI streaming""" async with await openai_client.chat.completions.create( model="gpt-4o", messages=messages, stream=True, ) as stream: async for chunk in stream: delta = chunk.choices[0].delta if delta.content: yield f"data: {json.dumps({'text': delta.content})}\n\n" yield f"data: {json.dumps({'done': True})}\n\n" @app.post("/chat/stream") async def chat_stream(request: dict): messages = request.get("messages", []) provider = request.get("provider", "anthropic") generator = ( stream_anthropic(messages) if provider == "anthropic" else stream_openai(messages) ) return StreamingResponse( generator, media_type="text/event-stream", headers={ "Cache-Control": "no-cache", "X-Accel-Buffering": "no", } ) 

Ми обираємо AsyncAnthropic та AsyncOpenAI для асинхронного стрімінгу. Важно: виставляємо X-Accel-Buffering: no для nginx — інакше nginx почне буферизувати SSE.

Frontend: React з потоковим читанням

import { useState, useCallback } from 'react'; function useStreamingChat() { const [response, setResponse] = useState(''); const [isStreaming, setIsStreaming] = useState(false); const sendMessage = useCallback(async (messages: Message[]) => { setIsStreaming(true); setResponse(''); const res = await fetch('/chat/stream', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ messages }), }); const reader = res.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\n'); for (const line of lines) { if (line.startsWith('data: ')) { const data = JSON.parse(line.slice(6)); if (data.done) { setIsStreaming(false); return; } setResponse(prev => prev + data.text); } } } setIsStreaming(false); }, []); return { response, isStreaming, sendMessage }; } 

Для POST-запитів використовуємо ReadableStream — це гнучкіше, ніж EventSource, і дозволяє відправляти тіло запиту.

Процес роботи

  1. Аналітика: розбираємо вашу архітектуру, обираємо модель і протокол (SSE/WebSocket).
  2. Проектування: малюємо діаграму потоків, визначаємо точки переривання.
  3. Реалізація: пишемо backend endpoint, frontend компонент, обробку помилок.
  4. Тестування: навантажувальний тест з імітацією N користувачів, замір p99 latency.
  5. Деплой та моніторинг: налаштовуємо nginx, CDN, моніторинг (Grafana + Prometheus) для відстеження стрімінгових з'єднань.

Що входить в роботу

  • Backend endpoint з SSE або WebSocket (FastAPI).
  • React-компонент (TypeScript) з обробкою переривань, індикатором завантаження, авто-скролом.
  • Документація: опис протоколу, конфіги nginx, приклади запитів.
  • Підтримка після запуску: 1 місяць інцидент-менеджменту.

Терміни орієнтовно

Етап Час
Базова інтеграція SSE 1–2 дні
Frontend компонент 1–2 дні
Обробка переривань 1 день
WebSocket альтернатива 2–3 дні
Тестування та деплой 2 дні

Вартість впровадження від $2,000 до $5,000 залежно від складності. Зв'яжіться з нами для оцінки вашого проекту. Замовте впровадження потокового виведення для вашого LLM-сервісу — покращте UX та конверсію.

Потоковий вивід — не просто фіча, а вимога сучасного UX для потокових чат-ботів. Ми реалізуємо його під ключ за 5–10 днів. Наш досвід — 50+ проектів з LLM. Отримайте консультацію прямо зараз.