Схемы распознавания сверены 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. Последовательность передач
Gateway участвует в разрешении запуска и управлении фразами. Сами аудиопакеты идут из расширения непосредственно в Ingress через chat NATS. Silero работает в браузере, поэтому сервер получает уже определённые координаты начала и конца речи. Кодирование Opus и определение границ выполняются во время одного захвата; показанный блок пакетов не означает, что сначала записывается весь поток, а затем запускается VAD.
Ingress один раз декодирует Opus, сохраняет исходные принятые пакеты и выделяет диапазон фразы. В ASR поступает готовый PCM16 mono 16 кГц. Bridge проверяет целостность, управляет очередью и передаёт задание worker. Worker вызывает модель и возвращает результат. Ingress принимает результат ожидаемой фразы один раз, а Gateway доставляет его исходной панели. Для rejected или error панель получает соответствующий исход вместо успешной транскрипции.
1.2. Блок-схема обработки
Ветви 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. Последовательность передач
Gateway проверяет доступ клиента и зарегистрированный каталог страницы. Agent получает пользовательскую задачу и проверенную привязку к документу. Запрос /tokenize проверяет размер контекста; затем провайдер возвращает текст и/или вызов инструмента через SSE. Отдельного сервиса src_llm в этом пути уже нет: адаптер и общая очередь модели находятся внутри Agent.
Если нужен run_js, Agent отправляет событие через Gateway в расширение. Код выполняется в документе, а результат возвращается к ожидающему вызову по tool_call_id. Следующая генерация получает этот результат и может сформировать ответ либо запросить следующее действие. Число генераций не фиксировано. Ошибка с неизвестным эффектом или занятость документа останавливает повторные действия; обычные ошибки обрабатываются по действующим правилам Agent и его лимитам.
2.2. Блок-схема обработки
Развилка 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 шагов и поля сообщений.
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. Источники
Ссылки закреплены на версиях, по которым составлены схемы.
- Адреса, сервисы и account remap NATS.
- Публичные маршруты Front/nginx.
- NATS-клиент браузера и упаковка команд.
- Gateway: проверка и маршрутизация.
- Схемы команд, границ фразы и квитанций.
- Ingress: захват и управляющие команды.
- Ingress: приём результата ASR и доставка панели.
- Opus: координаты, конфигурация и декодирование.
- Выделение фразы и её request ID.
- Передача готового PCM в bridge.
- ASR bridge: очередь, remap subjects и результат.
- ASR worker: вход и вызов модели.
- Agent: генерации, инструмент и окончательный ответ.
- Общая очередь провайдера внутри Agent.
- HTTP/SSE-контракт провайдера.
- Каталог run_js и формат результата инструмента.