Chat Agent · техническая схема

Распознавание, команды и озвучка

Схемы распознавания сверены 6 ноября 2026 года по исходникам Chat Agent 4ed6a545 и ASR b3e93c8b. Путь от готового ответа до плеера сверён 11 ноября 2026 года по Chat Agent 21ab3063.

Здесь описаны последовательность передач, адреса и форматы данных. Mermaid-схемы в Markdown требуют поддержки Mermaid. В HTML-версии рисунки встроены.

Как читать адреса и обозначения

В стрелках NATS указаны логические отправитель и получатель. Между ними находится брокер: отдельного прямого TCP-соединения Gateway → Agent или Ingress → bridge нет. JSON передаётся в UTF-8, бинарные блоки отмечены отдельно.

Обозначение Адрес / значение Назначение
HTTPS https://chat-agent.gis-master.ru Front, Monitor и POST /auth/nats
WSS расширения wss://chat-agent.gis-master.ru/chrome-chat-ws Подключение расширения к chat NATS
WSS веб-интерфейса wss://chat-agent.gis-master.ru/chat-ws Подключение Front и Monitor к chat NATS
CHAT nats://chat-agent_chat_nats:4222 Внутренние команды, управляющие сообщения и события
ASR nats://chat-agent_audio_nats:4222 Обмен Ingress, ASR bridge и inference-контуром
GPU leafnode nats://192.168.28.134:37422 Соединение аудиоброкера с inference-контуром H100
LLM https://nats-proxy-vllm.gis-master.ru HTTP API провайдера модели
U / C UUID UI-клиента / аудиоклиента Это разные подключения и разные client_id
S / T / P session_id / stream_id / phrase_id Сессия, аудиопоток и одна фраза
R / Q request_id / RPC UUID ASR Пользовательский запрос и отдельный обмен bridge с worker

На стороне Front nginx маршрут /chrome-chat-ws переводится в /chat-ws, который проксируется в chat-agent_chat_nats:9222. Backend использует TCP-порт 4222. Значения credentials в документ не включены.

1. От микрофона до транскрипции

1.1. Последовательность передач

Передача аудио и результата ASR
Расширение, Gateway, Audio Ingress, ASR bridge и worker обмениваются управляющими сообщениями, аудио и результатом.

Gateway участвует в разрешении запуска и управлении фразами. Сами аудиопакеты идут из расширения непосредственно в Ingress через chat NATS. Silero работает в браузере, поэтому сервер получает уже определённые координаты начала и конца речи. Кодирование Opus и определение границ выполняются во время одного захвата; показанный блок пакетов не означает, что сначала записывается весь поток, а затем запускается VAD.

Ingress один раз декодирует Opus, сохраняет исходные принятые пакеты и выделяет диапазон фразы. В ASR поступает готовый PCM16 mono 16 кГц. Bridge проверяет целостность, управляет очередью и передаёт задание worker. Worker вызывает модель и возвращает результат. Ingress принимает результат ожидаемой фразы один раз, а Gateway доставляет его исходной панели. Для rejected или error панель получает соответствующий исход вместо успешной транскрипции.

1.2. Блок-схема обработки

Обработка одной фразы
Браузер параллельно определяет границы и кодирует звук. Ingress собирает фразу, bridge ставит её в очередь, worker распознаёт.

Ветви Silero и Opus выходят из одного захвата: одна несёт координаты фразы, другая — звук. Они сходятся в Ingress, где проверяется наличие нужного диапазона. Два независимых микрофона или две аудиозаписи не создаются. Следующая речь может захватываться, пока готовая предыдущая фраза ожидает ASR.

Блоки ошибок показывают результат отказа, а не единый универсальный обработчик. Конкретная реакция зависит от причины: ошибка запуска не создаёт захват, а потеря аудиопакетов завершает соответствующий поток. Автоматическое повторное распознавание из этих стрелок не следует.

1.3. Поля сообщений

Общий конверт браузерной команды: {version:1, capability, data}. capability подтверждает права клиента. RPC-ответ имеет вид {status, data}; status здесь поле NATS-сообщения, а не HTTP-статус соединения WSS.

Шаг Subject / направление Формат и поля Для чего
A1 chat.audio.C.cmd.start, расширение → Gateway JSON data: audio_protocol:4, format:"opus", channels:1, sample_rate, segmentation:"browser_vad", asr_backend, context_id, ui_capability Выбрать модель и связать захват с панелью. Default клиента — gigaam_large_ctc
A2 chat.browser.audio, Gateway → Ingress JSON: command, data, session_id, client_id, profile, lease_exp; для расширения widget_route:{session_id,client_id,context_id} Передать проверенного владельца и команду. Внутренняя команда не использует внешний конверт capability
A3 Reply subjects исходных RPC, Ingress → Gateway → клиент JSON {status,data}; в READY: type:"ready", session_id, stream_id, asr_backend Подтвердить готовность и точное совпадение выбранного backend
A4 chat.audio.C.pcm, расширение → Ingress Бинарный протокол 4: sequence, valid_end_sample, длина и байты конфигурации, Opus-пакет Передать звук с порядком и точными координатами. Имя .pcm историческое: полезная нагрузка — Opus
A5 chat.audio.C.cmd.phrase → chat.browser.audio JSON data: type, phrase_id, start_sample, last_seq; для phrase_end также end_sample, last_speech_sample, reason, collection_ms, silence_ms, vad_ms; опционально vad Задать исходный диапазон и последний необходимый пакет
A5, VAD Поле vad в границе JSON: provider:"silero", revision, analysis_rate:16000, resampler, parameters:{pre_roll_ms,activation_ms,silence_ms,max_phrase_ms,start,continuation} Сохранить фактическую реализацию и параметры определения границ
A6 webrtc.komaroff.asr.stream.S, Ingress → bridge Бинарный протокол 3. batch_begin: version:3, type, seq:0, session_id, stream_id, phrase_id, request_id, asr_backend, sample_rate:16000, channels:1, sample_width:2, bytes, sha256, source, vad, capture_timings, prepare_ms, опционально monitor. Затем batch_pcm и batch_commit Передать готовую фразу и проверить её целостность
A7 Reply subject batch_commit, bridge → Ingress JSON: ok, phrase_id, state:"uploading" / "queued" / "finished"; при отказе error Подтвердить приём. Это ещё не текст модели
A8 to.inference.webrtc.asr.sample.Q → inference.webrtc.asr.sample.Q Тот же batch-протокол: JSON-заголовки и бинарные PCM-блоки Передать полное задание worker после допуска очередью bridge
A9 inference.webrtc.asr.sample.result.Q → from.inference.webrtc.asr.sample.result.Q JSON успеха: text, backend, model, model_revision, decode_ms, decode_count, scores, parameters, model_input, transport, words. Ошибка: error, иногда detail Вернуть результат одного вызова модели, параметры и измерения
A10 webrtc.komaroff.asr.text.S, bridge → Ingress JSON: version:1, revision:1, session_id, stream_id, phrase_id, asr_backend, result_type, text, code, source, vad, processing_mode:"batch", evaluation, parameters, model_input, timings, diagnostic Привести результат к исходу final, rejected или error
A11–A12 nats.events.user123 → chat.ui.U.events.ui JSON-конверт type:"command", kind:"asr_result", data. В data: request_id, session_id, stream_id, phrase_id, revision, result_type, text, code, turn_id, client_id, context_id, опционально timings Доставить исход распознавания конкретной панели. Не все поля диагностического ответа worker пересылаются в UI

source описывает исходный диапазон: координаты, частоту, формат, число отсчётов и SHA-256 PCM до нормализации. model_input описывает фактический вход модели: sample_rate:16000, format:"float32le", samples, sha256. Эти контрольные суммы относятся к разным представлениям звука.

1.4. Бинарные форматы

Браузерный аудиопротокол 4:

uint64 BE  sequence
uint64 BE  valid_end_sample       конец полезного звука в отсчётах 48 кГц
uint16 BE  config_length          0 или 23
[если config_length = 23:
    uint32 BE  decoder_rate
    19 bytes   исходный OpusHead  включая pre-skip кодера
]
bytes       один Opus-пакет

Конфигурация передаётся в начале нового сегмента захвата. Ingress учитывает pre-skip и фактическую длину, чтобы не сдвигать границы фразы и не добавлять хвост. Координаты границ браузерной фразы относятся к исходной частоте захвата; Ingress переводит их в координаты декодированного звука 48 кГц.

Внутренний batch-протокол 3, одинаковый по упаковке для Ingress → bridge и bridge → worker:

uint32 BE  json_header_length
bytes      UTF-8 JSON заголовка
bytes      PCM16LE mono 16 кГц, только для batch_pcm

batch_pcm содержит до 128 КиБ звука. Его заголовок несёт version, session_id, stream_id, phrase_id, type, seq. batch_begin и batch_commit не содержат PCM-хвост. Версия 3 этого контракта не означает поддержку удалённого браузерного PCM-протокола v3.

Аудиоброкер преобразует префикс to.inference. в inference. для worker; обратный inference. импортируется как from.inference.. Это account remap NATS, без изменения PCM или JSON результата.

2. От команды до ответа и вызова инструмента страницы

2.1. Последовательность передач

Передача команды и ответа
Панель отправляет команду в Agent через Gateway. Agent вызывает модель и при необходимости инструмент страницы, затем возвращает ответ.

Gateway проверяет доступ клиента и зарегистрированный каталог страницы. Agent получает пользовательскую задачу и проверенную привязку к документу. Запрос /tokenize проверяет размер контекста; затем провайдер возвращает текст и/или вызов инструмента через SSE. Отдельного сервиса src_llm в этом пути уже нет: адаптер и общая очередь модели находятся внутри Agent.

Если нужен run_js, Agent отправляет событие через Gateway в расширение. Код выполняется в документе, а результат возвращается к ожидающему вызову по tool_call_id. Следующая генерация получает этот результат и может сформировать ответ либо запросить следующее действие. Число генераций не фиксировано. Ошибка с неизвестным эффектом или занятость документа останавливает повторные действия; обычные ошибки обрабатываются по действующим правилам Agent и его лимитам.

2.2. Блок-схема обработки

Обработка команды и вызов инструмента
Схема показывает допуск запроса, вызов модели, ветку run_js, обработку отказов и завершение команды.

Развилка dom / pageTools относится к действиям кода, который сгенерировала модель, а не к двум независимым серверным инструментам. Серверный каталог содержит один run_js. Helper pageTools использует непустой нативный каталог документа; иначе обращается к window.webMcp. После ошибки вызова другой API автоматически не пробуется. Возврат к контексту означает новую генерацию с сохранённым исходом инструмента, а не повторное выполнение того же действия.

Квитанция допуска, квитанция получения результата инструмента и окончательный ответ — три разных сообщения. Только терминальное событие завершает задачу. Подтверждение панели фиксирует, что ответ был получен и добавлен в чат; оно не запускает дополнительную генерацию.

2.3. Поля сообщений

Для публичных команд используется тот же JSON-конверт {version:1, capability, data}. Для внутренних команд Gateway → Agent — собственное JSON-тело без capability: доверенная привязка подготовлена Gateway.

Шаг Subject / адрес Формат и поля Для чего
C1 chat.ui.U.cmd.browser_context data:{kind:"widget",protocol:2,context_id,tools:[{name,description,inputSchema,provider,purpose}]} Зарегистрировать каталог. Текущий фиксированный инструмент — run_js с аргументом code
C2 chat.ui.U.cmd.message data:{text,request_id,context_id,source_page?} Отправить вопрос. Для голосовой фразы сохраняется связь с её детерминированным request ID
C3 chat.internal.agent.widget JSON: action:"submit", message, browser:{kind,context_id,client_id,tools}, session_id, client_id. Заголовок X-Widget-Admission-Deadline-Ms Передать задачу и проверенный каталог; ограничить срок допуска
C4 https://nats-proxy-vllm.gis-master.ru/tokenize JSON: model, messages, tools?, add_generation_prompt:true, chat_template_kwargs:{enable_thinking:false}. Ответ: count Проверить фактический размер подготовленного контекста
C5 https://nats-proxy-vllm.gis-master.ru/v1/chat/completions JSON: model, messages, tools?, tool_choice, parallel_tool_calls:false, stream:true, stream_options:{include_usage:true}, temperature:0, max_tokens:2048, chat_template_kwargs Сгенерировать текст и/или вызовы инструментов. Часть полей tools добавляется только при наличии каталога
C5, ответ HTTP text/event-stream SSE-строки data: с JSON: choices[].delta.content, choices[].delta.tool_calls, finish_reason, usage; окончание [DONE] Передать ответ провайдера. Fragment arguments сначала является строкой, затем Agent разбирает и проверяет JSON
C6 nats.events.user123 → chat.ui.U.events.tools Конверт type:"command", kind:"widget_tool"; data:{request_id,session_id,client_id,context_id,stage:"tool_call",sequence,elapsed_ms,name:"run_js",arguments:{code},tool_call_id,generation_id} Выполнить определённый вызов на исходном документе
C7 Chrome USER_SCRIPT; для legacy WebMCP фиксированный вызов через scripting в MAIN Локальные вызовы dom.*, pageTools.list(), pageTools.call(name,args); JSON-совместимый результат. NATS subject у обращения к DOM/WebMCP отсутствует Действовать в документе. Для window.webMcp в MAIN передаётся фиксированная операция, имя и JSON-аргументы; произвольный код модели остаётся в USER_SCRIPT
C8 chat.ui.U.cmd.browser_tool_result → chat.internal.agent.widget Публичные поля: request_id, context_id, name:"run_js", tool_call_id и ровно один исход: result либо error. Внутренний запрос: action:"browser_tool_result", outcome, request_id, context_id, session_id, client_id Связать результат с ожидающим вызовом; не создать новый вызов инструмента
C8, успешный результат Поле result {ok:true,value} Вернуть значение. Результат void преобразуется в null
C8, исключение Поле result {ok:false,error,message,effect:"not_started" / "unknown"} Сохранить фактическую ошибку и степень определённости побочных действий
C9 nats.events.user123 → chat.ui.U.events.ui Конверт type:"command", kind:"widget_stage". В data: общие ID, stage:"done" / "failed" / "cancelled", sequence, elapsed_ms; text либо error, error_message, error_details; диагностическая сводка timings, diagnostic Передать итог задачи и сохранённые измерения
C10 chat.ui.U.cmd.widget_client_receipt data:{request_id,client_ms}; необязательная группа целиком: parent_event_id, subject, received_at, displayed_at Связать отправку Gateway с получением и добавлением ответа панелью
C11 to.inference.tts.s2.command → inference.tts.s2.command JSON: action=synthesize, текст для озвучки, ID запроса, сессии, панели, подключения, контекста и озвучки, gateway_instance Запустить озвучку после финального ответа
C12 inference.tts.s2.events.G → from.inference.tts.s2.events.G → chat.ui.U.events.speech X-Speech: тип, ID и номер блока. Тело у chunk содержит WebM/Opus, у статусов тело пустое. G — ID Gateway Доставить звук нужному плееру

messages содержит фактически подготовленные сообщения модели, включая системную инструкцию, вопрос, историю текущего исполнения и результаты инструментов. tools — OpenAI-совместимые определения: type:"function", function:{name,description,parameters}. Текст публичного объяснения решения может прийти вместе с вызовом инструмента; внутренние рассуждения не включаются, дополнительного запроса модели ради объяснения нет.

3. От готового ответа до звука

После публикации финального текста в chat.ui.U.events.ui Gateway проверяет сессию, контекст и готовность плеера. Задание synthesize идёт через NATS-мост на inference.tts.s2.command. Worker вызывает Qwen, подаёт PCM в FFmpeg и возвращает поток WebM/Opus через Gateway в chat.ui.U.events.speech.

Браузер начинает играть после накопления примерно 200 мс звука. Пока приходят блоки, он сообщает played_ms. По этой позиции worker ограничивает объём звука впереди плеера. end сообщает, что worker отправил все блоки. ended приходит от браузера после воспроизведения.

Нумерованная схема озвучки, 35 шагов и поля сообщений.

Передача ответа на озвучку и воспроизведение
35 шагов от подключения панели до окончания воспроизведения

4. Сквозные связи и границы чтения

Поле / механизм Что связывает
session_id Контекст клиента, запись и пользовательскую сессию
stream_id, phrase_id Конкретный захват и одну фразу; соседние фразы не объединяются по близости времени
request_id Пользовательскую команду и её исполнение. Для фразы вычисляется UUIDv5 от chat-agent:S:T:P
generation_id Один фактический вызов модели и связанные инструменты
tool_call_id Вызов инструмента и единственный ожидаемый исход
Q в subject worker Конкретный обмен bridge ↔ worker; это не замена пользовательского request_id
X-Monitor-Transfer, X-Monitor-Source, X-Monitor-Subject Исходную отправку и приём, в том числе после remap NATS
X-Widget-Diagnostic-Parent Причинную связь диагностических наблюдений клиента и серверов

RPC возвращается на reply subject, указанный в исходном NATS-сообщении. Для браузера он ограничен областью chat.<role>.<client_id>.reply.>, для backend обычно используется _INBOX.…. Поэтому у ответов RPC нет одного постоянного предметного subject. В схемах -->> означает ответ или асинхронное событие; это не обещание синхронного завершения всей задачи.

received_at и displayed_at панели — epoch milliseconds часов браузера. observed_at серверных наблюдений — исходное время их производителя. Разность времён разных машин не используется как измеренная задержка сети. Ошибка, отмена и неопределённый результат не превращаются в успешный ответ только из-за полученной квитанции.

Архив, SQL-чтение Monitor, служебная диагностика и управление поставкой не развёрнуты отдельными участниками этих двух сценариев. Они не создают дополнительный вызов модели для объяснения диаграммы. Сам архив хранит принятые Opus-пакеты, постоянная вторая PCM-копия не создаётся.

Найденное расхождение метаданных

ASR bridge указанной версии в диагностическом наблюдении отправки результата всё ещё записывает target="chat-agent_src_core" и подпись «в Core». Фактически результат на webrtc.komaroff.asr.text.S принимает chat-agent_audio_ws_ingress. На схеме показан действующий consumer, подтверждённый подпиской и обработчиком Ingress. Это дефект подписи/метаданных наблюдения; Core как работающий сервис из него не следует. Исходники при подготовке документа не исправлялись.

5. Источники

Ссылки закреплены на версиях, по которым составлены схемы.

Служебные вложения доступны в исходном архиве.