﻿# SSE reconnect не означает продолжение LLM-генерации

Почему SSE reconnect и Last-Event-ID не гарантируют продолжение генерации LLM: EventSource, POST fetch, replay, deduplication и границы контракта.

Автор: Команда Retrivium
Обновлено: 2026-11-01
Канонический URL: https://retrivium.ai/ru/journal/provider-sse-reconnect-generation-resume/
Language: ru

Поток пропал, клиент подключился снова, и текст продолжил появляться. Внешне это похоже на восстановление прежней генерации, но возможны разные механизмы: повторный запрос, новая генерация либо чтение сохранённых событий. Без явного контракта приложение не знает, какая работа выполнена и какие части текста уже доставлены. Для провайдера важно описать эти действия раздельно и не обещать resume только из-за использования SSE.

## Начните с механизма клиента

[Стандарт SSE](https://html.spec.whatwg.org/multipage/server-sent-events.html) описывает EventSource, reconnection и Last-Event-ID. Эти правила относятся к конкретной модели получения событий. Поток, читаемый через POST fetch с JSON body, не следует автоматически считать тем же интерфейсом, где браузер самостоятельно повторяет соединение по правилам EventSource.

Запишите используемый клиент и endpoint. Наличие data-строк в ответе не устанавливает способ восстановления. Если библиотека добавляет собственные retries, их политика должна быть видна отдельно. Иначе пользовательская операция может выполняться повторно, хотя приложение считает, что просто восстановило чтение прежних данных.

## Разделите три действия

Reconnect восстанавливает транспорт. Replay повторяет запрос или сохранённую последовательность. Resume продолжает определённую ранее операцию с согласованной границы. Эти слова нельзя использовать как синонимы, потому что они создают разные требования к идентификаторам, хранению состояния и учёту выполненной работы.

[RFC 9110](https://www.rfc-editor.org/rfc/rfc9110.html) задаёт семантику методов и ограничения автоматического повтора. Генерация через POST не приобретает идемпотентность от того, что её ответ передаётся SSE. Если собственный сервис вводит resume-протокол, это отдельное проектное решение, требующее определения срока хранения и допустимых переходов.

## Проверьте смысл идентификатора

Last-Event-ID может обозначать позицию в последовательности событий только там, где отправитель и клиент договорились о такой семантике. Его нельзя считать универсальным model request ID. Сохранённый идентификатор генерации тоже сам по себе не доказывает наличие API для продолжения вычисления.

Для технического описания нужны вопросы: какая операция продолжается, как определяется последняя подтверждённая позиция, допустимы ли повторы событий и что происходит после истечения срока состояния. Если ответы отсутствуют, контракт должен честно ограничиться новой попыткой, а не обещанием бесшовного продолжения.

## Создайте fixture разрыва

Используйте synthetic stream с нумерованными событиями. Прервите чтение после известной позиции, затем выполните документированное действие клиента. Сравните request IDs, доставленные события и число попыток. Повторённый префикс позволяет обнаружить replay, но не должен автоматически удаляться без правила deduplication.

[Документация streaming OpenRouter](https://openrouter.ai/docs/api_reference/streaming) описывает чтение и отмену потоков. Она не должна трактоваться как общее обещание resume любой upstream-генерации. Ограниченный fixture проверяет только объявленное поведение выбранного интерфейса; любые дополнительные механизмы требуют собственного источника и проверки.

## Сохраните неоднозначность явно

После разрыва может быть неизвестно, завершилась ли первоначальная работа на сервере. Не скрывайте эту неопределённость за надписью «соединение восстановлено». Итоговая запись должна показывать исход исходной попытки и поведение следующей отдельно, чтобы не смешивать пользовательский результат с числом реально выполненных операций.

Продолжить помогают [инкрементальный клиент](/ru/journal/openrouter-streaming-client/) и [fallback с лимитами](/ru/journal/router-failover-and-rate-limits/). Их чтение не заменяет resume-контракт: корректный parser и разумный retry решают другие задачи, чем продолжение одной ранее принятой генерации.
