Chat Agent · озвучка

Как ответ превращается в звук

Gateway отправляет готовый текст в чат. Если озвучка доступна и плеер готов, Gateway отдельно отправляет текст воркеру. Браузер начинает проигрывать звук по мере получения блоков.

Схема озвучки · 35 шаговОткрыть SVG в полном размере
На узком экране схему можно прокручивать в стороны.
Схема из 35 шагов: от подключения панели до завершения воспроизведения

Шаги по схеме

CHAT — NATS чата. Панель подключается к нему по WSS. INFERENCE — NATS воркера. Мост меняет префикс subject при передаче между контурами. U — ID панели, R — ID квитанции, G — ID экземпляра Gateway.

1. Панель подключается и сообщает, что готова играть звук

  1. Chrome → Gateway. HTTPS POST /auth/nats. Панель передаёт session_id и role=ui, чтобы получить доступ к NATS.
  2. Gateway → Chrome. HTTPS 200 OK. Gateway возвращает допуск, client_id, prefix, url и speech=1 при доступной озвучке. Панель подписывается на свои каналы, включая chat.ui.U.events.speech.
  3. Chrome → Gateway. WSS/CHAT, chat.ui.U.cmd.browser_context. Панель регистрирует контекст страницы, указывает protocol=2 и запрашивает speech=1.
  4. Gateway → Chrome. WSS/CHAT, chat.ui.U.reply.R. Квитанция 200 с data.speech=1 подтверждает, что озвучка доступна этому контексту.
  5. Chrome → Gateway. WSS/CHAT, chat.ui.U.cmd.speech_control. Команда ready с context_id и connection_id привязывает готовый плеер к подключению панели.
  6. Gateway → Chrome. WSS/CHAT, chat.ui.U.reply.R. Квитанция 200 с пустым объектом data подтверждает привязку.

2. Пользователь задаёт вопрос

  1. Chrome → Gateway. WSS/CHAT, chat.ui.U.cmd.message. Панель передаёт вопрос, ID запроса, сессии и контекста, а также source_page.
  2. Gateway → Agent. NATS/CHAT, chat.internal.agent.widget. Gateway передаёт action=submit, вопрос и проверенную привязку к браузеру.
  3. Agent → Gateway. NATS/CHAT, reply inbox из запроса №8. Статус 202 означает, что Agent принял вопрос в работу.
  4. Gateway → Chrome. WSS/CHAT, chat.ui.U.reply.R. Gateway подтверждает приём вопроса статусом 202. Готовый ответ придёт событием.

3. Готовый ответ уходит в чат и на озвучку

  1. Agent → Gateway. NATS/CHAT, nats.events.user123. Agent отправляет событие widget_stage с stage=done, текстом ответа и ID.
  2. Gateway → Chrome. WSS/CHAT, chat.ui.U.events.ui. Gateway пересылает исходное событие. Панель показывает текст.
  3. Gateway → NATS-мост. Локальный NATS, to.inference.tts.s2.command. Для готового плеера Gateway убирает разметку из текста, создаёт speech_id и публикует action=synthesize, текст и ID.
  4. NATS-мост → Worker. NATS/INFERENCE, inference.tts.s2.command. Мост доставляет то же JSON-тело. Worker проверяет текст и ID, отсекает повторы по speech_id и резервирует место в своей очереди.

4. Worker берёт задание и запускает Qwen

  1. Worker → Gateway. NATS через мост, inference.tts.s2.events.G → from.inference.tts.s2.events.G. queued в X-Speech сообщает, что задание принято. Текст остаётся в очереди worker, тело события пустое.
  2. Gateway → Chrome. WSS/CHAT, chat.ui.U.events.speech. Плеер получает queued, создаёт состояние этой озвучки и запускает таймер обратной связи.
  3. Worker → Gateway. NATS через мост, inference.tts.s2.events.G → from.inference.tts.s2.events.G. Когда доходит очередь, worker отправляет generating, модель и время ожидания.
  4. Gateway → Chrome. WSS/CHAT, chat.ui.U.events.speech. Плеер получает generating. Панель показывает, что идёт синтез.
  5. Worker → Qwen. Локальный вызов Python Qwen.pcm → generate_voice_clone_streaming. Worker преобразует время и десятичные числа по правилам русского произношения. Qwen получает текст, язык и настройки клонирования голоса Xenia.
  6. Qwen → Worker. Локальный генератор Python возвращает порции PCM. Это несжатый монофонический звук 24 кГц, float32 little-endian.

5. Worker сжимает звук и отправляет его в Chrome

  1. Worker → Gateway. Диагностика. NATS через мост, inference.tts.s2.events.G → from.inference.tts.s2.events.G. При появлении первого PCM worker отправляет замер first_pcm_ms. Gateway записывает его в журнал.
  2. Worker → FFmpeg. Локальный pipe stdin. Worker подаёт PCM порциями примерно по 100 мс звука.
  3. FFmpeg → Worker. Локальный pipe stdout. FFmpeg выдаёт поток WebM с Opus, моно 48 кГц, 32 кбит/с.
  4. Worker → NATS-мост. NATS/INFERENCE, inference.tts.s2.events.G. Worker берёт до 16 КиБ из потока FFmpeg, ставит type=chunk и номер seq в X-Speech и отправляет байты в теле сообщения.
  5. NATS-мост → Gateway. Локальный NATS, from.inference.tts.s2.events.G. Мост передаёт заголовок и байты. Gateway проверяет ID и номер блока.
  6. Gateway → Chrome. WSS/CHAT, chat.ui.U.events.speech. Gateway пересылает тот же блок. Плеер сверяет seq и добавляет байты в буфер.
  7. Worker → Gateway. Диагностика. NATS через мост, inference.tts.s2.events.G → from.inference.tts.s2.events.G. После первой публикации блока worker отправляет first_block_ms для журнала.

6. Плеер начинает играть и сообщает позицию

Шаги №30–33 идут параллельно с передачей блоков №24–26, пока браузер ещё не получил end.

  1. Chrome → Gateway. Диагностика. WSS/CHAT, chat.ui.U.cmd.speech_control. Обычно плеер запускает звук, накопив около 200 мс. При первом событии браузера playing он отправляет одноимённую команду для записи времени старта.
  2. Gateway → Chrome. Квитанция диагностики. WSS/CHAT, chat.ui.U.reply.R. Ответ 200 подтверждает playing. Воспроизведение уже началось.
  3. Chrome → Gateway. WSS/CHAT, chat.ui.U.cmd.speech_control. Плеер посылает action=position без reply. Значение played_ms показывает, сколько звука уже сыграло. До старта позиция может быть нулевой.
  4. Gateway → Worker. NATS через мост, to.inference.tts.s2.command → inference.tts.s2.command. Gateway пересылает позицию. Worker ограничивает подачу PCM в FFmpeg примерно двумя секундами вперёд и ждёт следующего обновления, когда достигает предела.

7. Worker заканчивает отправку, Chrome доигрывает

  1. Worker → Gateway. NATS через мост, inference.tts.s2.events.G → from.inference.tts.s2.events.G. После последнего блока worker отправляет end с числом блоков и итоговыми замерами.
  2. Gateway → Chrome. WSS/CHAT, chat.ui.U.events.speech. Плеер проверяет число блоков, прекращает отправлять позицию и доигрывает оставшийся звук.
  3. Chrome → Gateway. WSS/CHAT, chat.ui.U.cmd.speech_control. При окончании звука плеер отправляет ended, освобождает ресурсы и показывает «Воспроизведено». Gateway закрывает состояние озвучки.
  4. Gateway → Chrome. WSS/CHAT, chat.ui.U.reply.R. Статус 200 подтверждает приём ended.

Передаваемые данные

Команды панели · №3, 5, 7, 30, 32, 34
JSON-конверт {version:1, capability, data}. У speech_control внутри data находятся action, context_id, connection_id и, для конкретной озвучки, request_id и speech_id. Позиция добавляет played_ms — миллисекунды от начала аудио.
Задание и позиция для worker · №13–14, 33
JSON без браузерного конверта. action=synthesize несёт text, action=position — played_ms. gateway_instance определяет канал ответа воркера.
Идентификаторы задания и событий
session_id — сессия, client_id — панель, context_id — её контекст, connection_id — подключение плеера, request_id — вопрос, speech_id — конкретный синтез. Вместе они позволяют Gateway и плееру принять звук нужного запроса.
События worker · №15–18, 21, 24–29
В заголовке NATS X-Speech лежит JSON с version=1, type и шестью ID. У chunk тело содержит до 16 КиБ WebM/Opus, seq нумерует блоки с нуля. У end поле seq содержит количество отправленных блоков. У статусов и замеров тело пустое.
Замеры и завершение
queue_ms — ожидание воркера, first_pcm_ms — время от начала синтеза до первой порции PCM, first_block_ms — до первой публикации WebM. Итог worker содержит bytes, audio_ms и compute_ms. Gateway измеряет время от финального ответа до получения команды playing своим таймером. При остановке abort содержит reason.

Какие сообщения можно сократить