chat-agent · состояние после C4 · 06.10.2026
chat-agent: от запроса к ответу
Чат принимает текст или голос, показывает ответ по частям и сохраняет историю. Исполнение ограничено очередями и общим сроком. Распознавание, модель, TURN и OAuth остаются у прежних внешних владельцев.
Код: src_core/main.py · stack/webrtc.drs
chat-agent · состояние после C4 · 06.10.2026
Два стэка связаны общим NATS
Диалогом и аудиоподключениями владеет chat-agent. Voice принимает прежний PCM-контракт и возвращает partial/final. Разделение позволяет менять чат без поставки voice; общий NATS и его доступность остаются общей зависимостью.
Код: stack/webrtc.drs · src_shared/audio.py · src_core/audio_admission.py
chat-agent · состояние после C4 · 06.10.2026
Каждый модуль отвечает за один участок
| Каталог | Ответственность |
| src_front / src_core | UI / HTTP, admission, ASR listener |
| src_agent / src_langgraph_rb_node | Очередь, история / диалог и сценарии |
| src_llm / src_api_gateway | Модель / пять tools |
| src_webrtc_ingress / src_audio_ws_ingress | WebRTC / PCM WebSocket |
Frontend отображает только свою сессию. Core владеет admission и ASR final, Agent — запросом и историей, Ruby runtime — диалогом и tools. Два ingress содержат медиабиблиотеки; из Core они удалены.
Код: src_core/main.py · src_agent/service.py · src_langgraph_rb_node/lib/src_langgraph_rb_node/runtime/dialog.rb
Схемы и контракты · сверка 06.10.2026
Модель вызываем через Ruby, части ответа идут в Agent
Core передаёт текст или ASR final в Agent. Ruby вызывает LLM и получает проверенный ответ; части текста параллельно идут прямо в Agent через NATS. Обычный разговор требует одной генерации, tools могут потребовать следующих. История хранится в PostgreSQL, текущая попытка - в памяти Agent.
Код: Agent + stream · Ruby RPC · LLM reply / delta · состояние попытки
Схемы и контракты · сверка 06.10.2026
RPC передаёт JSON, NATS доставляет ответ в inbox
| NATS subject, request/reply | Запрос | Ответ в reply inbox |
nats.agent.user123 | AgentInboundRequest: text + envelope | AgentInboundResponse: result / status / errors |
nats.workflow.run.ruby.node | WorkflowRunRequest: text + runtime | WorkflowRunResponse: result + next_runtime |
nats.llm.user123 | LlmRequest: mode / input / constraints | LlmResponse: response_text / tool_calls / error |
nats.tools.{name} | ToolCallRequest: tool_name / args | ToolCallResponse: ok / data / error |
Внутренний broker: nats://chat-agent_audio_nats:4222, credentials опущены. Typed payload - UTF-8 JSON с trace_id/correlation_id/request_id/session_id/ts_ms. Ruby получает каталог схем через nats.tools.discover. Управление audio/cancel и фрагменты ответа имеют отдельные компактные схемы.
Код: Agent · Workflow · LLM · Tools
Схемы и контракты · сверка 06.10.2026
LLM передаёт JSON в HTTP proxy
https://nats-proxy-ollama.gis-master.ru/api/chat
| Вызов HTTPS POST /api/chat | Отправляем JSON | Получаем |
dialog_step | messages + native tools, stream:true | NDJSON: content / tool_calls / done |
| Структурированные режимы | messages + format:JSON Schema, stream:false | Один JSON, затем server validation |
LLM adapter отправляет model:qwen3:30b, num_ctx:8192, num_predict:2048 и think:false. Текст dialog_step читается по строкам NDJSON; tool calls исполняются только после полной проверки. Внутренний переход proxy → Ollama в этой сверке заново не исследован.
Код: HTTPS / JSON / NDJSON · validation / stream · remote model / endpoint
chat-agent · состояние после C4 · 06.10.2026
Инструменты выбирает модель, проверяет сервер
Ruby получает каталог через nats.tools.discover, проверяет имена и JSON-аргументы до исполнения всего набора вызовов. Затем последовательно вызывает nats.tools.{name}. Пять tools пока возвращают демонстрационные fixtures. Активные и pending-графы сохранены; общий срок 120 s, бюджет до 10 действий.
Код: src_langgraph_rb_node/lib/src_langgraph_rb_node/runtime/dialog.rb · src_api_gateway/main.py · src_shared/contracts/tools.py
chat-agent · состояние после C4 · 06.10.2026
Независимые чаты не стоят в одной очереди
| Участок | Активно | Ожидает |
| Agent | 3 | 9, максимум 2 на сессию |
| Ruby runtime | 3 | 3 |
| LLM | 1 | 3 |
| Общий срок | 120 с | включает все очереди |
Agent сохраняет порядок внутри сессии и справедливо выбирает независимые чаты. Долгий инструмент не занимает единственного общего обработчика. Модель всё ещё имеет один активный слот: три Agent workers не создают три GPU генерации.
Код: src_shared/execution.py · src_agent/service.py · src_langgraph_rb_node/lib/src_langgraph_rb_node/runtime/request_executor.rb · src_llm/utils/base_service.py
Схемы и контракты · сверка 06.10.2026
Два аудиотранспорта сходятся в один PCM-контракт
WebRTC передаёт защищённый RTP; codec согласуется в SDP. WSS передаёт mono PCM16LE с частотой браузера. Каждый ingress отдельно переводит звук в 16 kHz и пакует его для NATS. Общий PCM-модуль - код внутри ingress; Core выдаёт допуск, но не принимает медиапакеты.
Код: WebRTC · WSS · PCM envelope · admission
Схемы и контракты · сверка 06.10.2026
Четыре адреса - четыре разных потока данных
| Адрес на chat-agent.gis-master.ru | Кто принимает | Что передаётся |
HTTPS POST /core/offer | WebRTC ingress | JSON с SDP offer → SDP answer |
WSS /core/ice/{connection_id} | WebRTC ingress | JSON auth / candidate / end |
WSS /audio/ws | WSS ingress | start JSON → binary PCM16LE → end JSON |
WSS /ws | Общий NATS | Протокол NATS; payload событий - JSON |
База HTTPS: https://chat-agent.gis-master.ru; для сокетов - wss://chat-agent.gis-master.ru. Все четыре маршрута проходят OAuth. ICE дополнительно проверяет token подключения. /ws возвращает статусы и ответ, /audio/ws передаёт звук. Текст, история и отмена идут в остальные /core/*, которые обслуживает Core.
Код: routes / OAuth · ICE JSON · PCM start/end · NATS over WSS
chat-agent · состояние после C4 · 06.10.2026
Trickle ICE убрал ожидание полного сбора
Браузер и сервер передают SDP и кандидаты по мере появления. Рабочая пара начинает ICE/DTLS до завершения зависшего STUN. После соединения незавершённый сбор не прерывает аудио. Private adapter закреплён на точных версиях и остаётся под нашим сопровождением.
Код: src_webrtc_ingress/main.py · src_webrtc_ingress/ice/trickle.py · tests/test_core_trickle_ice.py
Схемы и контракты · сверка 06.10.2026
PCM передаём в binary envelope
Один обычный frame содержит 320 samples / 640 PCM bytes за 20 ms. FIFO сохраняет остатки; padding нужен только последнему хвосту, меньше 20 ms. Flush выполняется раз в 250 ms на connection и при end/abort. Очередь ограничена двумя секундами; при переполнении поток завершается с явной ошибкой.
Код: wire / FIFO / flush · voice validation · PCM tests
Схемы и контракты · сверка 06.10.2026
Voice возвращает текст, Core запускает диалог
Ingress публикует звук в webrtc.komaroff.asr.stream.{session_id}. Voice выделяет фразы и отправляет PCM к GPU по NATS; ответ возвращает в Core как JSON. Partial показываем в UI, непустой final запускает Agent один раз. Файловый API voice и диаризация в live-путь не входят.
Код: voice bridge · GPU contract · final → Agent · NATS remap
chat-agent · состояние после C4 · 06.10.2026
Отмена и завершение имеют одного владельца
Agent согласует terminal transition, отменяет downstream и игнорирует поздний результат. Готовый ответ не перезаписывается отменой. Частичный текст сохраняется помеченным черновиком; при отмене редактирования исходная история остаётся.
Код: src_agent/request_state.py · src_agent/service.py · src_agent/utils/db.py
Схемы и контракты · сверка 06.10.2026
Части ответа и окончательный результат идут отдельно
Agent публикует response_fragment, request_state, итог message и команды client. Core публикует asr_result и audio_status в тот же nats.events.user123. Frontend проверяет session/request/attempt/generation/sequence до показа текста и команд карты. После reconnect берёт snapshot или историю; replay UI-событий здесь нет.
Код: fragments / snapshot · итог / команды · ASR / audio status · фильтр UI
chat-agent · состояние после C4 · 06.10.2026
Телеметрия связывает ожидание и результат
traceparent проходит NATS headers через наши сервисы. Tempo хранит этапы и UUID attributes, VictoriaMetrics — числовые длительности и очереди. Тексты, аудио, SDP и credentials не экспортируются; клиентские длительности помечены как client_reported.
Код: src_shared/observability.py · src_core/handlers/handle_client_timings.py · src_langgraph_rb_node/lib/src_langgraph_rb_node/runtime/telemetry.rb
chat-agent · состояние после C4 · 06.10.2026
Переименование меняет владельца сервисов
Поставка e9122e0 / CI 4930 подготовила образы и дождалась завершения работ. Точные 15 старых services удалены; 13 новых проверены до включения Frontend. Физические тома и shared network сохранены. По решению владельца перенос старой истории и backup/restore не входят в приёмку; схему БД можно менять.
Код: stack/release.rb · stack/runtime_probe.py · docker/mirror-postgres.rb · stack/webrtc.drs
Было → исправлено
Основные ошибки были в управлении работой
| Проблема | Изменение |
| Router генерировал отдельно от ответа | dialog_step: текст либо tools за одну генерацию |
| Agent последовательно ждал независимые чаты | Три исполнителя, очередь по сессиям, общий срок |
| UI принимал ответы и статусы соседей | Проверка session/request/generation до отображения |
| Каждый PCM-пакет вызывал flush | FIFO, publish сразу, flush раз в 250 мс |
Исправления убрали лишнее ожидание и сделали границы работы явными. Один долгий запрос больше не занимает единственный Agent callback. Общий NATS account сохранён: фильтр UI решает смешивание сообщений, но отдельные права каждого пользователя ещё нужны.
Код: dialog.rb · execution.py · requestState.ts · audio.py
Замеры C3 · 06.10.2026
Диалог ускорился, распознавание сохранило свою задержку
| C0 → C3, секунды | p50 | p95 |
| Обычный текст, n=12 | 14,97 → 3,11 | 20,95 → 3,98 |
| ASR final → ответ | 46,01 → 14,10 | 89,40 → 23,23 |
| RTC connected, n=12 | 1,39 → 1,63 | 1,56 → 1,88 |
| Начало WAV → ASR final | 3,80 → 3,82 | 3,99 → 4,02 |
В той же контрольной серии C3 завершил 33/33 хода с 36 генерациями. Обычный ответ ускорился благодаря одной генерации и новым очередям. Trickle был поставлен раньше; подключение здесь немного медленнее C0, но ниже цели 3 с. ASR p95 после конца WAV вырос на 2,8%. Короткая синтетическая речь иногда искажается; WER/DER не измерены.
Evidence: приёмка C3 · browser measurements · cases.json
Исправление C4 · Core и два аудио-ingress
Healthcheck больше не запускает Python
Каждые 10 секунд Docker проверяет Core и оба ingress. Для одного локального HTTP-запроса раньше запускался Python; теперь работает curl. Полный HTTP 200 и готовность NATS остались обязательными. В 90 пробах реальных образов CPU одной проверки был ниже 29 мс; это снизило фоновую нагрузку всего стэка.
Код: Core Dockerfile · ingress Dockerfile · проверки отказов · результаты C4
Итог после C4 · весь стэк · 06.10.2026
После healthcheck CPU в простое снизился на 75%
| Показатель и база сравнения | До → после |
| CPU в простое, перед → после C4 | 0,197 → 0,049 ядра, -75,0% |
| Память в простое, перед → после C4 | 547,57 → 553,41 МБ, +1,1% |
| Сумма rootfs образов, C0 → C4 | 3 685,21 → 3 256,25 МБ, -11,6% |
| Активный production-код, C0 → C4 | 14 762 → 15 890 строк, +7,6% |
CPU и память сравнили в одинаковых окнах простоя по 180 с: 13 сервисов, без активных запросов. RAM не сократилась. Сама пересборка healthcheck добавила 4,27 МБ rootfs относительно C3. Rootfs повторно учитывает общие слои, без томов и внешних моделей; свежий compressed-размер C4 не подтверждён. Полная серия диалога относится к C3, после C4 прошли короткие проверки текста, RTC и WSS.
Замеры: метод и результат C4 · CPU / RAM / runtime · подсчёт production-кода
Управление
←/→, Page Up/Page Down и Space переключают слайды. Home и End переходят к краям. F включает полный экран. Escape закрывает это окно.