chat-agent · передача данных 01
От запроса до текста и карты
Пользователь работает на chat-agent.gis-master.ru. Core принимает текст или окончательную транскрипцию, а Agent ведёт запрос и запускает Pydantic AI внутри своего процесса. Модель работает удалённо на H100; результат возвращается в чат текстом и, при вызове инструмента, командой карте. Здесь восстановлены связи по коду и конфигурации на 07.10.2026 · 7747c4c; свежую проверку production эта схема не заменяет.
Код: Core → Agent · Agent + SDK · Адрес модели
chat-agent · передача данных 02
Core фиксирует данные до очереди
1. Браузер → Core
POST https://chat-agent.gis-master.ru/core/message
Content-Type: application/json
{
"text": "Покажи маршрут до SVO для TEST123. Сколько времени остаётся, если до вылета 120 минут, дорога 70, запас 30?",
"session_id": "11111111-1111-4111-8111-111111111111",
"request_id": "22222222-2222-4222-8222-222222222222",
"turn_id": "22222222-2222-4222-8222-222222222222"
}2. Core → API gateway → PostgreSQL
POST http://chat-agent_src_api_gateway:8100/api/domain/{S}/snapshots/{R}
{
"snapshot_id": "33333333-3333-4333-8333-333333333333"
}Снимок включает position, data_revision, demo и plans. Agent получает его идентификатор, затем читает снимок из БД.
S - сессия, R - запрос. В примерах используются вымышленные UUID и исходные демоданные.
Перед отправкой текста браузер ждёт сохранения последнего перемещения самолёта. Core закрепляет положение этой сессии, общую версию демоданных и планы в одном снимке, поэтому изменение карты во время ожидания не подменяет вход запроса. Текст TEST123 здесь - конкретный пример контракта; числа 120, 70 и 30 присутствуют и в исходном демонаборе, но редактор может их изменить.
Код: HTTP и beforeSend · Снимок перед NATS · Состав снимка · Демо TEST123
chat-agent · передача данных 03
У одного запроса два канала ответа
HTTP-запрос остаётся открытым до итогового результата. Параллельно браузер уже может получать текст по NATS WebSocket, поэтому ожидание полного ответа не мешает видеть начало. После каждого tool call Agent возвращает результат инструмента модели и может повторить шаг генерации; обычная реплика без инструментов завершается одним шагом. Сплошная стрелка обозначает вызов, пунктир - возврат или событие.
Код: Agent RPC · Шаг модели · Фрагменты · Ответ и карта
chat-agent · передача данных 04
NATS передаёт JSON, а не “просто строку”
Core → Agent: nats.agent.user123
{
"trace_id": "UUID трассы сообщения",
"request_id": "22222222-2222-4222-8222-222222222222",
"correlation_id": "UUID трассы сообщения",
"session_id": "11111111-1111-4111-8111-111111111111",
"ts_ms": 1791320000000,
"text": "Покажи маршрут до SVO для TEST123…",
"turn_id": "22222222-2222-4222-8222-222222222222",
"user_id": "user123",
"snapshot_id": "33333333-3333-4333-8333-333333333333"
}Agent → LLM: nats.llm.user123
{
"mode": "agent_step",
"input": {
"messages": [{"role": "user",
"content": "Покажи маршрут до SVO…"}],
"tools": [{"type": "function", "function": {
"name": "run_code",
"parameters": {"type": "object",
"properties": {"code": {"type": "string"}},
"required": ["code"]}
}}]
},
"constraints": {}
}Справа показана часть LlmRequest: он также содержит TraceEnvelope. Реально передаются system prompt, нужная история и все 4 объявленных инструмента с полными схемами.
Core отправляет UTF-8 JSON на nats.agent.user123; Agent вызывает модель на nats.llm.user123. Суффикс user123 общий, а принадлежность чату определяется session_id. Общий запрос и отдельный шаг модели имеют разные request_id: внешний ID, deadline, бюджет, attempt и generation передаются в NATS headers вместе с traceparent. Надписи вместо UUID и сокращённые payload здесь объясняют поля, а не служат готовым запросом.
Код: AgentInboundRequest · AgentStepInput · LlmRequest · NATS headers
chat-agent · передача данных 05
Модель возвращает текст либо tool calls
LLM gateway → существующий requester
POST https://nats-proxy-vllm.gis-master.ru/v1/chat/completions
{
"model": "Qwen3.8-27B-FP8",
"messages": [{"role": "user",
"content": "Покажи маршрут до SVO…"}],
"tools": [{"type": "function", "function": {
"name": "run_code",
"parameters": {"type": "object",
"properties": {"code": {"type": "string"}},
"required": ["code"]}
}}],
"stream": true, "temperature": 0,
"max_tokens": 2048, "parallel_tool_calls": false,
"stream_options": {"include_usage": true},
"tool_choice": "auto",
"chat_template_kwargs": {"enable_thinking": false}
}Requester → gateway: SSE
Content-Type: text/event-stream
data: {"choices":[{"delta":{"content":"Для деморейса "},"finish_reason":null}]}
data: {"choices":[{"delta":{},"finish_reason":"stop"}]}
data: [DONE]Tool call приходит как delta.tool_calls; аргументы могут идти частями. Gateway собирает вызов, проверяет имя и JSON Schema и только затем возвращает его Agent.
Показан один инструмент; реально передаются все 4 и полный контекст. Перед генерацией: POST /tokenize. Бюджет входа 6144 токена; ответ до 2048. Внутренний NATS subject requester → H100 в этой проверке не установлен.
Pydantic AI не подключается к H100 напрямую: наш адаптер обращается к LLM gateway по NATS, а gateway - к существующему vLLM requester по HTTPS. Он считает токены по действующей модели и сокращает только необязательную старую историю. SSE - поток HTTP-событий от requester; после разбора gateway публикует текстовые фрагменты в NATS. Внешний NATS-переход на H100 показан как граница соседнего сервиса, без придуманного имени subject.
Код: /tokenize · HTTP / SSE · messages / tool_call_id
chat-agent · передача данных 06
Расчёт и показ маршрута - разные действия
Шаг A: run_code → Monty
f = flight_get(flight_number="TEST123")
r = route_preview(code="SVO")
left = (f["departure_in_minutes"]
- f["travel_minutes"]
- f["buffer_minutes"])
{"minutes_left": left,
"destination": f["to"],
"distance_km": r["distance_km"]}120 - 70 - 30 = 20 минут
Расстояние зависит от положения самолёта в снимке. Время дороги берётся из деморейса, а не выводится из расстояния.
Шаг B: отдельный map_show_route
{
"id": "call_route_1",
"name": "map_show_route",
"arguments": {
"code": "SVO"
}
}Инструмент вычисляет тот же маршрут и добавляет BUILD_ROUTE в client_events. route_preview возвращает данные и сам ничего не рисует.
Чтение работает по снимку внутри Agent. Monty не имеет сети, файлов, environment или credentials: 64 МиБ и 10 секунд вычислений.
Это допустимая последовательность для примера. Модель может выбрать другой порядок; фиксированный граф TEST123 отсутствует.
Для TEST123 модель может вызвать run_code и получить результат вычитания из настоящего исполнения Python в Monty. Вложенные функции читают серверный снимок; они не отправляют произвольные HTTP-запросы. Чтобы пользователь увидел маршрут, нужен отдельный native tool map_show_route, после которого Agent возвращает проверенные результаты в следующий шаг модели. Маршрут здесь - прямая линия для демо, не навигационный план полёта.
Код: flight_get / route_preview · map_show_route · CodeMode / Monty · Формат маршрута
chat-agent · передача данных 07
Фрагменты доходят до своего чата
LLM → Agent: chat.agent.stream
{
"request_id": "22222222-2222-4222-8222-222222222222",
"session_id": "11111111-1111-4111-8111-111111111111",
"attempt_id": "22222222-2222-4222-8222-222222222222",
"generation_id": "33333333-3333-4333-8333-333333333333",
"generation_index": 3,
"sequence": 1,
"delta": "Для деморейса TEST123 осталось 20 минут. "
}Публикуется только текст. Tool calls возвращаются в окончательном LlmResponse.data.tool_calls после проверки.
Agent → браузер: nats.events.user123
{
"time": "2026-10-07T10:00:00.000+00:00",
"service": "src_agent", "type": "command",
"kind": "response_fragment",
"data": {
"request_id": "R", "session_id": "S",
"attempt_id": "A", "generation_id": "G",
"attempt_index": 0, "generation_index": 3,
"sequence": 7, "state": "generating",
"deadline_ms": 1791320120000,
"delta": "Для TEST123 осталось 20 минут. "
},
"name": "", "uid": "UUID события"
}S/R/A/G - сокращения UUID. UI sequence и sequence gateway различаются: Agent нумерует собственные события.
Agent принимает chat.agent.stream, проверяет владельца и порядок частей, затем оборачивает фрагмент в UI-событие. Браузер подписан на nats.events.user123 через wss://chat-agent.gis-master.ru/ws и проверяет session, request, attempt и generation. Пример generation_index=3 иллюстрирует третий шаг, а не обязательное число генераций. Старые части и чужие события не должны дополнять текущий ответ.
Код: Gateway fragment · Проверка фрагмента · UI sequence · Фильтр в браузере
chat-agent · передача данных 08
Карта меняется по команде, история - в БД
Команда карты через /ws
{
"type": "command", "kind": "client",
"data": {
"command": "BUILD_ROUTE",
"trace": {"session_id": "S", "request_id": "R",
"attempt_id": "A", "generation_id": "G"},
"artifacts": {"all": ["ui"], "last": "ui",
"payload": {"ui": {"data": {"route": {
"geometry": [[37.6, 55.7], [37.386677, 55.977486]],
"distance_km": 33.607, "demo": true
}}}}
}
}
}Показана часть события и route. GeoJSON-совместимый порядок точек: [lon, lat]. Координаты в примере иллюстративные.
Итог на POST /core/message
{
"ok": true, "status": "DONE",
"session_id": "11111111-1111-4111-8111-111111111111",
"request_id": "22222222-2222-4222-8222-222222222222",
"result": "Для деморейса TEST123 остаётся 20 минут: 120 - 70 - 30. Маршрут до SVO показан на карте.",
"errors": []
}Сокращённый AgentInboundResponse: также есть trace_id, correlation_id, ts_ms, client_handler и client_events. Команда BUILD_ROUTE в client_events содержит тот же route.
Native tool добавляет команду в результат Agent; UiEventPublisher переводит её в формат artifacts.payload.ui для браузера. Самолёт при показе маршрута не перемещается. Agent сохраняет итог и историю в PostgreSQL, а Core возвращает тот же результат по HTTP, чтобы UI мог восстановиться при потере события. После reconnect используются snapshot активного запроса или история; полного replay в Core NATS нет.
Код: Формат команды · Запись результата · Итог HTTP · Применение карты
chat-agent · передача данных 09
Голос сначала становится текстом
WebRTC и WSS - два входа в один аудиотракт. После нормализации ingress отправляет PCM в общий NATS; единственный live bridge в стэке voice выделяет речь и передаёт её удалённому Whisper worker. Core получает partial для интерфейса, а final превращает в тот же запрос Agent, что и текст. Здесь S - session_id, T - stream_id; ответ агента сейчас отображается текстом, синтез речи в этот путь не входит.
Код: WebRTC → PCM · WSS → PCM · NATS audio · Voice / GPU
chat-agent · передача данных 10
Аудио и текст имеют разные контракты
WSS: /audio/ws и пакет ingress
{"type": "start", "session_id": "S",
"format": "pcm16le", "channels": 1,
"sample_rate": 48000}Затем binary frames ≤ 8192 байт. Частота - фактическая частота браузера; сервер пересчитывает в 16 kHz.
+ UTF-8 JSON metadata
+ 640 байт PCM16LE на 20 мс
{"type": "frame", "seq": 1, "sample_rate": 16000,
"channels": 1, "sample_width": 2,
"session_id": "S", "stream_id": "T"}ASR → Core: JSON
webrtc.komaroff.asr.text.{S}
{
"version": 1, "result_type": "final",
"session_id": "S", "stream_id": "T",
"phrase_id": "P", "revision": 2,
"text": "Покажи маршрут до SVO для TEST123."
}partial обновляет транскрипцию, final запускает Agent. Проверяются stream, phrase и revision, чтобы старый или повторный final не запустил запрос ещё раз.
PCM не превращается в base64 и не передаётся модели LLM. end/abort имеют тот же бинарный конверт без PCM.
При WebRTC браузер сначала получает capabilities и отправляет SDP offer; ICE-кандидаты идут отдельно на /core/ice/{connection_id}. ICE-канал принимает первым сообщением {"type":"auth","token":"…"} и не переносит звук. Для WSS звук идёт непосредственно на /audio/ws. В обоих случаях NATS-пакет имеет заголовок длины, JSON и бинарный PCM; окончательная транскрипция возвращается уже JSON-сообщением. Эти три WebSocket-пути нельзя объединять в один блок.
Код: Start / binary · Бинарный конверт · ICE endpoint / auth · ASR protocol
chat-agent · передача данных 11
Сохранение ждёт отдельного подтверждения
{"session_id":"S","proposal_id":"UUID","version":1,"decision":"approve"}Тело подтверждения; request_id находится в URL.Просьба сохранить план создаёт предложение, а не запись плана. Agent сохраняет deferred call в PostgreSQL, завершает текущую работу и освобождает worker; ожидание человека не расходует 120 секунд активной работы. По confirm он проверяет версии положения, демоданных и плана в транзакции. Повторный клик не создаёт вторую запись; устаревшая карточка вызывает пересчёт и новое подтверждение. Обычное уточнение критериев при этом остаётся следующей репликой чата.
Код: DeferredToolRequests · Confirm HTTP / NATS · Проверка и запись · Новая карточка
chat-agent · передача данных 12
Что исправлено в вашей схеме
| На исходной схеме | Как реализовано |
|---|---|
| Пользователь сразу → Agent | Браузер → OAuth ingress → Core; голос сначала проходит ASR. |
| “Pydantic AI + модель” в одном блоке | SDK внутри Agent; модель отдельно на H100 через NATS и LLM gateway. |
| Все инструменты → NATS / API | 8 функций чтения доступны внутри Monty. Данные читаются из снимка в Agent; artifacts / планы - из PG. |
| “Уточнить / подтвердить / изменить” как один узел | Уточнение - реплика. Confirm принимает approve / reject; изменение - новый запрос и предложение. |
| Изменение карты вместе с “цель выполнена” | Нужен вызов native map tool. Команда попадает в client_events, затем в /ws или HTTP result. |
| “Краткие данные + artifact_ref” всегда | Малый результат передаётся целиком; большой - artifact_id, count, preview и постраничное чтение. |
Исходная схема правильно передаёт идею управляемого цикла и ожидания человека, но не разделяет процессы и форматы данных. Исправленная версия сохраняет эту идею и добавляет фактические границы: Core, SDK в Agent, удалённую модель, локальные инструменты снимка и отдельные команды UI. Универсальный каталог “прав” и готовый механизм компактной памяти целей не показаны как реализованные сервисы: в коде есть конкретные схемы, history, snapshot и проверки владения.
Код: Каталог инструментов · История и snapshot · Выход карты
Исправленная схема · 1 из 3 · каждая строка слева направо 13
Запрос и модель: читаем по строкам
Строки идут сверху вниз; внутри каждой следуем стрелкам слева направо. Повтор блока показывает обратный путь данных без длинной петли. Текст приходит из браузера, а голосовой ASR final — в Core по NATS, как на страницах 9–10. Полный адрес модели: https://nats-proxy-vllm.gis-master.ru/v1/chat/completions. Цифры в кружках — номера блоков для разбора на страницах 16–17.
Код: Цикл модели · Инструменты · Предложения · Состояние и выход
Исправленная схема · 2 из 3 · выбирается нужная ветка 14
Инструменты и ответ: отдельные пути
Первые две строки — возможные ветки, а не обязательные шаги каждого вопроса. Результат инструмента возвращается в SDK и, если работа продолжается, в следующий запрос модели. Monty читает снимок и считает; команды карты создаются отдельно и доставляются через /ws. Строка 3 показывает завершение: Agent сохраняет итог, а браузер применяет только события своей сессии и поколения.
Код: flight_get / route_preview · map_show_route · CodeMode / Monty · Формат маршрута
Исправленная схема · 3 из 3 · подтверждение отдельным запросом 15
Сохранение: сначала предложение, потом решение
Ожидание решения хранится в PostgreSQL и не занимает worker. Подтверждение — новый HTTP-запрос с версией предложения, а не продолжение старого сокета. Agent проверяет актуальность данных перед записью: повторный клик не дублирует план, отказ не меняет его, а устаревшее предложение требует новой карточки и нового подтверждения. Успешное сохранение сообщается без дополнительной генерации модели.
Код: DeferredToolRequests · Confirm HTTP / NATS · Проверка и запись · Новая карточка
chat-agent · передача данных 16
Что делает каждый блок: вход и модель
| Блок на схеме | Что принимает и за что отвечает | Что передаёт дальше |
|---|---|---|
| 1 · Браузер | Принимает текст и показывает транскрипцию. | POST /core/message; /ws для событий; карта применяет только события своего запроса. |
| 2 · Core | Принимает HTTP или ASR final; закрепляет вход. | Через API создаёт snapshot в PG; передаёт Agent JSON + execution headers по NATS. |
| 3 · Agent + Pydantic AI | Agent владеет очередью, историей, отменой. SDK ведёт цикл модели и tools. | Использует фиксированный каталог и снимок. Проверяет ответ; завершает итогом, ошибкой или предложением. |
| 4 · LLM gateway | Получает LlmRequest(mode=agent_step). | Проверяет input, считает токены, вызывает requester, собирает и валидирует tool calls; публикует delta. |
| 5 · Requester / vLLM | Получает messages, tools и параметры через внешний requester. | Генерирует текст / вызовы инструментов. Сам не пишет в нашу БД и не рисует карту. |
| 6 · Контекст запроса | История сессии + snapshot + схемы. | PG фиксирует положение, общие демоданные и планы. Незавершённые proposals не входят в обычный контекст. |
Первые шесть блоков готовят запрос и получают решение модели. Владельцем сессии остаётся приложение: модель не выбирает чужой snapshot и не назначает себе права. Схемы инструментов входят в модельный запрос, а фактические данные аэропортов попадают туда через результаты инструментов. При ошибке нового агента приложение возвращает ошибку; скрытого перехода на Ruby нет.
Код: Core · Контекст · Gateway · Pydantic / legacy
chat-agent · передача данных 17
Что делает каждый блок: действия и результат
| Блок на схеме | Вход и работа | Результат и ограничение |
|---|---|---|
| 7 · Code Mode / Monty | run_code выполняет Python с 8 разрешёнными функциями чтения. | 64 МиБ, 10 с, без сети / файлов. Результат - значения или artifact handle, не побочный эффект карты. |
| 8 · Native map tools | map_show_points(codes), map_show_route(code). | По snapshot формируют SET_AIRPORTS / BUILD_ROUTE. Команды уходят своей сессии; положение самолёта не меняется. |
| 9 · Предложение PG | plan_save(name, airport_codes, notes, flight_number). | Сохраняются действие, версии и сообщения deferred call. План ещё не записан; worker освобождён. |
| 10 · Человек | Видит сохранённое предложение в карточке. | Confirm передаёт session_id, proposal_id, version и approve / reject. Новое содержание задаётся новым запросом. |
| 11 · Проверка + запись PG | Agent сверяет предложение с решением и текущими версиями. | В транзакции сохраняет одну версию плана или фиксирует отказ. Stale вызывает новую карточку, не запись. |
| 12 · Результат инструмента | Проверенное значение, tool_call_id и при необходимости artifact_id. | SDK связывает tool result с вызовом модели. Для approve / reject факт записи выводится без новой генерации. |
| 13 · История и ответ | Итог цикла, ошибки, client_events. | Agent сохраняет результат. /ws доставляет события; Core возвращает HTTP JSON; GET history помогает после reload. |
Последние блоки разделяют чтение, UI-эффект и изменение постоянных данных. Вычисление может закончиться без карты и без плана, поэтому эти эффекты проверяются отдельно. Большой результат сохраняется как artifact и читается ограниченными страницами; сохранение плана требует решения пользователя. Переименование планов в карточки и настраиваемые шаблоны пока остаётся будущей доработкой и в эту схему не включено.
Код: Функции и native tools · Artifacts / proposals · Подтверждение без генерации
chat-agent · передача данных 18
Адреса, которые связывают весь путь
| Путь на https://chat-agent.gis-master.ru | Назначение | Формат / внутренний переход |
|---|---|---|
POST /core/message | Текст → Core → nats.agent.user123 | JSON request → итог AgentInboundResponse |
GET /core/history?session_id=S | История после reload | JSON; Agent читает PG через nats.agent.history.user123 |
GET / DELETE /core/requests/R?session_id=S | Состояние / отмена ответа | chat.agent.requests; cancel дальше в chat.agent.cancel.* |
POST /core/requests/R/confirm | Подтверждение плана | JSON; chat.agent.requests; версии проверяются сервером |
GET /core/domain/view?session_id=SPUT /core/domain/position?session_id=S | Карта и сохранение самолёта | JSON; position: lat, lon, revision |
GET /core/rtc-capabilitiesPOST /core/offer | WebRTC capability / SDP | JSON: sdp, type, session_id, trickle |
/core/ice/{connection_id} | ICE WebSocket / DELETE stop | JSON auth, ready, candidate, end, error |
/audio/ws · /ws | PCM WSS · NATS события | Разные сокеты: binary PCM и NATS protocol с JSON payload |
Все перечисленные внешние HTTP/WS-маршруты проходят штатный OAuth ingress. Это граница входа; внутри приложение дополнительно сверяет принадлежность запроса сессии и версии предложения. В коде текстовый путь: message_handler → handle_transcription → AgentRunner.run → PydanticRunner.run → NatsModel.request. Модули следуют той же цепочке: src_front → src_core → src_agent → src_llm, контракты находятся в src_shared. Ruby сохраняется для прежних active/pending workflows, а новые запросы проходят Pydantic AI.
Код: HTTP routes · History · Control / cancel · Ingress и владельцы
Путь запроса в chat-agent
← / →, Page Up / Page Down и Space переключают страницы. Home и End - начало и конец. F - полный экран. Escape - закрыть справку или полный экран. Свайп переключает страницы.
Связи подтверждены исходниками 7747c4c. Примеры данных сокращены и вымышлены; файл не отправляет запросы в сервисы. Ссылки на код открываются отдельно и требуют корпоративного доступа.