Глава 13

Stream продуктовые события, а не внутреннее рассуждение

Пользователю полезно видеть, что система проверяет политику или готовит расчёт. Ему не нужен сырой reasoning trace, внутренние tool arguments или персональные данные из результатов. Поэтому стримить надо продуктовые события, а не внутреннее рассуждение - публичный контракт событий вы задаёте сами и фильтруете провайдерский поток под него.

TypeScript
export type PublicAgentEvent =
  | { type: "status"; message: string }
  | { type: "source_found"; title: string }
  | { type: "answer_delta"; text: string }
  | { type: "outcome"; value: AgentOutcome }
  | { type: "error"; code: string; retryable: boolean };

function toSse(event: PublicAgentEvent) {
  return `event: ${event.type}\ndata: ${JSON.stringify(event)}\n\n`;
}

У каждого провайдера свой механизм стриминга, но публичный контракт остаётся один. OpenAI Responses streaming отдаёт типизированные semantic events - фильтруйте их перед отправкой клиенту. Anthropic Messages API передаёт SSE, а TypeScript SDK умеет и stream, и получение финального Message. Google Interactions поддерживает streaming. Ваша задача - свести все три к единому PublicAgentEvent на уровне приложения.

Отдельный случай - подтверждение действия, и здесь стриминг заканчивается не текстом, а карточкой.

UX подтверждения. Когда outcome равен approval_required, streaming заканчивается карточкой с точной суммой, причиной и сроком. Кнопка вызывает отдельный endpoint, а не отправляет модели фразу "да".

События под контролем. Осталась неприятная реальность сети и провайдеров - ошибки, - и повторять их можно только там, где повтор безопасен.

Ссылки