awesome-everything EN
↑ Обратно к восхождению

AI / LLM

Streaming: чтение кода и потока

Суть Читай реальные сниппеты парсинга SSE и streaming-клиента, предсказывай поведение и выбирай фикс с наибольшим рычагом — фрейминг событий, накопление delta, client abort и частичный JSON в аргументах tool.
Высота — путь к senior
НольJuniorMiddleSenior
Ты на senior-высоте — в орбите
◷ 14 min

SSE-парсер, аккумулятор и путь отмены — вот где реально живут баги streaming. Прочитай каждый сниппет, предскажи его поведение под реальным трафиком и выбери фикс, который senior сделает первым.

Цель

Отработай цикл, который ты гоняешь в каждом инциденте со streaming: прочитать код парсинга и клиента, предсказать, где он портит данные или зависает, и потянуться к фиксу фрейминга/накопления/отмены, а не винить модель.

Сниппет 1 — парсер SSE-кадров

const reader = res.body.getReader();
const decoder = new TextDecoder();
while (true) {
  const { value, done } = await reader.read();
  if (done) break;
  const text = decoder.decode(value);      // один сетевой чанк
  for (const line of text.split("\n")) {
    if (line.startsWith("data: ")) {
      const evt = JSON.parse(line.slice(6)); // парсим сразу
      handle(evt);
    }
  }
}
Викторина

Этот парсер работает в dev, но под нагрузкой бросает ошибки JSON.parse. В чём баг и фикс?

Сниппет 2 — аккумулятор delta

let text = "";
const toolArgs = {};                 // index -> строковый буфер
function handle(evt) {
  if (evt.type === "content_block_delta") {
    if (evt.delta.type === "text_delta") {
      text += evt.delta.text;
      render(text);
    } else if (evt.delta.type === "input_json_delta") {
      const i = evt.index;
      toolArgs[i] = JSON.parse(toolArgs[i] + evt.delta.partial_json); // (!)
    }
  }
}
Викторина

Путь для текста корректен. Что не так с путём аргументов tool?

Сниппет 3 — отмена на клиенте

function ask(prompt) {
  return fetch("/api/chat", {
    method: "POST",
    body: JSON.stringify({ prompt }),
  }).then(consumeStream);
}
// Пользователь жмёт "Stop". UI перестаёт рисовать токены.
// Но сервер продолжает генерацию, а биллинг — считать.
Викторина

Клик по Stop прячет токены, но модель продолжает генерировать, а ты — платить. Какой правильный фикс сквозь всю цепочку?

Сниппет 4 — backpressure на сервере

// SSE-эндпоинт в стиле Express, проксирующий upstream-stream модели
for await (const evt of upstreamStream) {
  res.write(`data: ${JSON.stringify(evt)}\n\n`); // игнорируем результат
}
res.end();
Викторина

Медленный клиент (мобайл, слабый сигнал) потребляет события медленнее, чем upstream производит. Что делает этот цикл и какой фикс?

Итог

Любой баг streaming читается в парсере, аккумуляторе, пути отмены или цикле записи: SSE-кадры надо буферизовать и делить по разделителю-пустой-строке, потому что TCP-чанки не совпадают с событиями; фрагменты input_json_delta для tool копятся как строка и парсятся только на content_block_stop; клиентский Stop должен запускать AbortController, чей signal прокидывается до провайдера, иначе ты продолжаешь генерировать и платить; а сервер, проксирующий stream, обязан уважать false от write() (приостановить upstream, возобновить на drain), иначе медленный клиент раздувает память. Прочитай путь кода, предскажи отказ под реальным трафиком, почини фрейминг/отмену/backpressure — и перепроверь на медленном, нестабильном клиенте.

Продолжить восхождение ↑Streaming: собери устойчивый streaming-эндпоинт и клиент
хоткеи развернуть
поиск
K
пред. пьеса
k
след. пьеса
j
тиры
t
это меню
?
sources2
expand
  1. 01
  2. 02

Trademarks belong to their respective owners. Editorial reference only.