Skip to content

Как стримить LLM ответы

Коротко:

LLM streaming — ключ к good UX. Без streaming пользователь ждёт 10s пустой экран до full response. С streaming — первый токен за 300-500ms. Paradigm: Server-Sent Events (SSE) или chunked HTTP. OpenAI / Anthropic / Gemini — все support stream: true. Frontend: fetch() + ReadableStream + TextDecoder → append к UI incrementally.

Ниже: пошаговая инструкция, рабочие примеры, типичные ошибки, FAQ.

Попробовать бесплатно →

Пошаговая настройка

  1. Backend: установите stream: true в LLM call
  2. Return Response с Content-Type: text/event-stream (или plain chunked)
  3. Forward каждый chunk к client: res.write("data: " + chunk + "\n\n")
  4. Client: fetch(...).then(r => r.body.getReader())
  5. Loop: read() → decoder.decode() → parse SSE → append к DOM
  6. UI: показывайте "typing" indicator, smooth scroll auto
  7. Error handling: abort, network fail, token limit

Рабочие примеры

СценарийКонфиг
Backend OpenAI streamingconst stream = await openai.chat.completions.create({ model: 'gpt-5', stream: true, messages: [...] }); for await (const chunk of stream) { const content = chunk.choices[0]?.delta?.content || ''; res.write(`data: ${JSON.stringify({ content })}\n\n`); } res.end();
Frontend fetch streamingconst response = await fetch('/api/ai/chat', { method: 'POST', body: JSON.stringify({ message }) }); 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); // parse SSE lines, extract content, append к UI outputEl.textContent += content; }
Vercel AI SDK (modern)import { streamText } from 'ai'; import { openai } from '@ai-sdk/openai'; const result = streamText({ model: openai('gpt-5'), messages: [...] }); for await (const chunk of result.textStream) { console.log(chunk); }
Anthropic Claude streamingconst stream = await anthropic.messages.stream({ model: 'claude-opus-4-7', messages: [...] }); stream.on('text', (text) => { console.log('chunk:', text); });
SSE client (EventSource)const es = new EventSource('/api/ai/stream?query=hello'); es.onmessage = (event) => { const { content } = JSON.parse(event.data); outputEl.textContent += content; };

Типичные ошибки

  • Buffering: nginx default buffers response. Add proxy_buffering off; для streaming location
  • Timeouts: не ставьте short timeout (10s) на streaming request — LLM иногда 30s
  • Abort: user может cancel, clean up AbortController и LLM request
  • Retries: не retry заново уже стримящиеся responses — двойные tokens в UI
  • Cache: не используйте intermediate cache (CloudFlare etc) для streaming endpoints

Больше по теме

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

SSE vs WebSocket?

SSE: one-way server→client, HTTP-based, proxies friendly. WebSocket: duplex, overkill для LLM (не нужно client→server streaming).

Первый токен latency?

Typical 300-800ms. Factors: server region, model size, prompt caching. OpenAI с cache — 150ms.

Как delay минимизировать?

TTFT (time to first token): cache prompt prefix, short prompts, use smaller model для low-latency path. Prompt cache 10x reduce.

Monitor latency?

TTFT + tokens/sec в analytics. <a href="/check">Enterno HTTP checker</a> для general endpoint. LangSmith / LangFuse для LLM-specific traces.