О проекте
Команда: QosQanat
Кейс HackAlem.ai: Коммуникации — Halyk Bank · кейс 2
Стадия: Доведённый продукт
Задача кейса: «Voice Router»: голосовой AI, маршрутизирующий звонки в контакт-центре страховой компании
Суть
Голосовой и текстовый симулятор страхового контакт-центра: AI выбирает сценарий из 40 бизнес-сценариев, собирает поля, отвечает по базе знаний и передаёт обращение оператору с контекстом. Есть рабочее место оператора и супервизора.
Как устроено
Браузер передаёт PCM-аудио по WebSocket (AudioWorklet, детектор речи и пауз, перебивание) в gateway, где OpenAI делает потоковую транскрипцию. Realtime-модель возвращает структурированный вызов route_turn со сценариями и извлечёнными полями, который проверяется по Zod-схемам и правилам каталога. Scenario-service и core-движок выбирают факты из JSON-базы знаний, запрашивают недостающие слоты, считают стоимость и возврат кодом. Для 9 необратимых действий требуется отдельное подтверждение. Состояние, история и журнал операций хранятся в SQLite, после передачи человеку AI перестаёт отвечать.
Ключевые решения
- OpenAI Realtime API
- function calling (route_turn)
- streaming STT
- TTS
- VAD
- barge-in
- slot filling
- Zod validation
- idempotent actions
- knowledge base retrieval
Особенности
Решения модели проходят проверку: она не имеет доступа к БД, а необратимые операции требуют подтверждения с повторной проверкой параметров. Предложение отменяется при изменении данных или обрыве связи. Поддержано перебивание помощника, несколько тем в одном обращении с отложенными запросами и смешанная казахско-русская речь. Голосовые eval-тесты синтезируют входные фразы через TTS.
Стек
TypeScript · Node.js 22 · pnpm monorepo · React 19 · Vite 6 · Express · ws/WebSocket · Zod · SQLite (node:sqlite) · Docker Compose
Модели
gpt-realtime-2.1 · gpt-realtime · gpt-4.1-mini · gpt-live-transcribe · gpt-4o-mini-tts
Проект создан за 5 часов на финале хакатона HackAlem.ai (23 сентября 2026, Астана). Код — BAITC-Hacks/hack-7b90b090-qosqanat.
README
README.mdSaqta Voice Router
Проект команды QosQanat для HackAlem. Голосовой симулятор страхового контакт-центра с AI-маршрутизацией обращений.
Клиент описывает вопрос своими словами на русском, казахском или смешанном языке. Помощник выбирает сценарий, уточняет недостающие данные и отвечает по учебной базе Saqta. Если нужен сотрудник, он получает обращение вместе с историей и следующим шагом.
Проект решает задачу первичного обслуживания клиентов страховой компании: разобраться в запросе, собрать данные и сохранить контекст при передаче оператору. Клиенту доступен разговор в браузере, оператору и супервизору - рабочее место для обработки обращений.
Это демонстрационный прототип. Клиенты, полисы и тарифы синтетические. Покупка полиса, запись на приём и отправка документов изменяют только демонстрационное состояние; реальных страховых операций приложение не выполняет.
Что реализовано
- Голосовой и текстовый диалог. В голосовом режиме доступны промежуточная расшифровка, автоматический ответ после паузы, озвучивание и перебивание помощника. Есть отключение микрофона и резервный режим с ручным завершением реплики.
- Выбор сценария языковой моделью из каталога: 40 бизнес-сценариев и 3 системных исхода. Каталог охватывает страховые продукты, страховые случаи, ДМС, обслуживание полисов, платежи, офисы, жалобы и запрос оператора.
- Сбор и проверка полей, уточняющие вопросы, обработка нескольких тем с сохранением отложенных запросов.
- Ответы по базе знаний с указанием файла и раздела источника. Панель решения показывает выбранные сценарии, извлечённые данные и ход обработки.
- Расчёт стоимости и возврата по правилам в коде. В каталоге есть 31 действие; для 9 действий с признаком необратимости предусмотрено отдельное подтверждение параметров.
- Привязка демонстрационного клиента к телефону, просмотр прошлых обращений, начало нового и продолжение незавершённого разговора.
- Сохранение истории, состояния сценария и журнала операций в SQLite. Повтор запроса с тем же идентификатором не выполняет действие повторно.
- Рабочее место оператора: очередь, принятие обращения, переписка и передача коллеге. Супервизор может переназначать и закрывать обращения. После передачи человеку AI прекращает ответы и действия.
- Интерфейс на русском, казахском и английском. Его язык выбирается отдельно от языка разговора; сохранённые сообщения не переводятся при переключении.
Как работает решение
- Клиент вводит телефон. Сервер нормализует номер и находит либо создаёт демонстрационного клиента.
- На экране истории клиент начинает новое обращение или продолжает незавершённое. До выбора ввод сообщения недоступен.
- Клиент говорит в микрофон или печатает запрос. Голос поступает на сервер по WebSocket и преобразуется в текст через OpenAI.
- AI-маршрутизатор возвращает структурированный вызов
route_turn: сценарии и извлечённые поля. Сервер проверяет ответ модели по схемам и правилам каталога. - Сервис сценариев выбирает факты из JSON, запрашивает недостающие данные или выполняет расчёт. Для операции с обязательным подтверждением сначала сохраняет предложение с конкретными параметрами.
- После отдельного подтверждения сервер повторно проверяет параметры и выполняет действие. Изменение данных или обрыв связи отменяет прежнее предложение.
- Клиент получает текстовый ответ и, при включённом голосе, звук. История и результат сохраняются. При передаче оператору тот продолжает работу с этим же обращением.
Технологии
| Область | Используется в репозитории |
|---|---|
| Язык и среда | TypeScript 5.8, strict, ESM; Node.js от 22.13 |
| Организация проекта | pnpm 10.8.0, workspace-монорепозиторий |
| Интерфейс | React 19, Vite 6, CSS, иконки Lucide React |
| HTTP и соединения | Express 4, библиотека ws, WebSocket |
| Проверка данных | Zod 3 |
| Хранение | SQLite через встроенный node:sqlite, режим WAL |
| Звук в браузере | Web Audio API, AudioWorklet, PCM 24 кГц; детектор речи и пауз |
| AI и внешние API | OpenAI Realtime API и Responses API |
| Настройки и запуск | dotenv, Docker, Docker Compose |
| Проверки | TypeScript, встроенный node:test, tsx; собственные сценарии в evals |
Имена моделей по умолчанию заданы в .env.example. Их доступность зависит от OpenAI-аккаунта и проверяется при реальном вызове API.
| Переменная | Значение | Назначение |
|---|---|---|
REALTIME_MODEL | gpt-realtime-2.1 | Маршрутизация и работа Realtime |
REALTIME_FALLBACK_MODEL | gpt-realtime | Резервная модель маршрутизатора |
TEXT_MODEL | gpt-4.1-mini | Формирование ответа по результатам обработки |
TRANSCRIPTION_MODEL | gpt-live-transcribe | Потоковое распознавание речи |
В голосовых тестах дополнительно используется OpenAI Audio API с моделью gpt-4o-mini-tts для синтеза входных фраз.
Модель подключена через интерфейс RoutingProvider. Реализован провайдер OpenAI; адаптера NVIDIA в текущей версии нет. Graphify и humanizer-ru используются при разработке и не нужны для запуска приложения.
Архитектура проекта
flowchart LR
Client[Клиент и сотрудники] --> Web[web: React и Vite]
Web <-->|HTTP и WebSocket| Gateway[gateway: доступ и AI]
Gateway <-->|распознавание, маршрутизация, ответ и звук| OpenAI[OpenAI API]
Gateway -->|внутренний HTTP API| Scenario[scenario-service]
Scenario --> Core[core: правила и действия]
Core --> Knowledge[knowledge: каталог и база знаний]
Scenario --> DB[db: SQLite]
Core --> DB
| Каталог | Ответственность |
|---|---|
apps/web | Клиентский экран, история, голос и рабочее место сотрудников |
apps/gateway | Публичный HTTP API, WebSocket, проверка доступа, вызовы AI |
apps/scenario-service | Состояние обращения, внутренний API, запуск бизнес-правил |
packages/core | Диалоговый движок, расчёты, подтверждения, выполнение действий |
packages/db | Схема SQLite, клиенты, обращения, сообщения и журнал операций |
packages/ai | OpenAI, распознавание, маршрутизация и потоковые ответы |
packages/knowledge | Загрузка каталога и выбор фактов по сценарию |
packages/contracts | Общие типы и схемы проверки данных |
packages/i18n | Переводы интерфейса |
docs/voice_router_dataset | Исходные учебные данные и примеры |
evals | Проверки маршрутизации, диалогов, HTTP и голоса |
scripts | Запуск, настройка окружения, сброс демонстрации и граф проекта |
Бизнес-состояние меняют сервис сценариев и его движок через пакет db. Модель не получает прямого доступа к SQLite. Внутренний API защищён INTERNAL_SECRET; Compose не публикует порт сервиса сценариев.
Телефон клиента, обращение и сессия соединения учитываются отдельно. При перезапуске сервер восстанавливает состояние оборванных сессий. Запросы одной сессии обрабатываются последовательно; число одновременных обращений к модели ограничивается настройкой MAX_ACTIVE_MODELS.
Установка и запуск
Все команды ниже выполняются из корня репозитория, где находятся package.json и compose.yaml. Если открыт родительский каталог HackAlem, сначала выполните:
cd hack-7b90b090-qosqanat
Для диалога нужен интернет и ключ OpenAI с доступом к настроенным моделям, включая Realtime. Это относится и к текстовому режиму. Вызовы API платные. Для микрофона нужен браузер с доступом к Web Audio и разрешением на запись; открывайте приложение через localhost или HTTPS.
Локально через Node.js
-
Установите Node.js версии 22.13 или новее и pnpm 10.8.0. Если используете Corepack:
corepack enable corepack prepare [email protected] --activate -
Установите зависимости:
pnpm install --frozen-lockfile -
При первом запуске создайте файл настроек. Если
.envуже существует, сохраните его и измените нужные значения вручную.cp .env.example .env pnpm env:setupКоманда
env:setupзаполняет пустыеINTERNAL_SECRETиSTAFF_PASSWORD. Откройте.envи укажите свойOPENAI_API_KEY. Секреты остаются на сервере; не добавляйте.envв Git. -
Запустите три приложения:
pnpm dev -
Откройте клиентский экран или рабочее место сотрудников.
По умолчанию web слушает порт 3000, gateway - 3001, scenario-service - 3002. Локальная база хранится в var/voice-router.sqlite. Проверить HTTP-доступность gateway и сервиса сценариев можно командой:
curl http://localhost:3000/api/health
Успешный ответ этого адреса не проверяет доступ к AI-моделям. После изменения .env перезапустите процессы. Для отдельного запуска приложений предусмотрены pnpm dev:web, pnpm dev:gateway и pnpm dev:scenario.
Через Docker Compose
Нужны Docker Engine и Compose. Node.js на хосте для этого варианта не требуется.
-
Создайте
.envиз.env.example, если файла ещё нет. -
Заполните
OPENAI_API_KEY,INTERNAL_SECRETслучайной строкой длиной не менее 24 символов иSTAFF_PASSWORDдлиной не менее 12 символов. -
Запустите:
docker compose up --build -
Откройте localhost:3000.
Compose собирает интерфейс, запускает три сервиса и хранит SQLite в volume history. Для остановки с сохранением данных используйте docker compose down. Добавление флага -v удаляет volume с историей.
В отчёте проверки указано, что конфигурация Compose проверена, но полный контейнерный запуск не подтверждён из-за недоступного Docker Engine. Локальный запуск через pnpm dev описан там как проверенный.
Настройки и учётные записи
| Настройка | Назначение |
|---|---|
OPENAI_API_KEY | Серверный ключ OpenAI |
INTERNAL_SECRET | Защита внутреннего API |
STAFF_PASSWORD | Общий демонстрационный пароль сотрудников |
PUBLIC_ORIGIN | Разрешённый адрес интерфейса, по умолчанию http://localhost:3000 |
SCENARIO_URL | Адрес сервиса сценариев; Compose задаёт его отдельно |
DATABASE_PATH | Путь к SQLite; в Compose используется /data/voice-router.sqlite |
MAX_ACTIVE_MODELS | Ограничение одновременных вызовов моделей, в примере окружения 8 |
На /staff доступны operator1, operator2 и supervisor. Пароль при описанном выше запуске берётся из .env. Если запускать pnpm dev без заданных служебных переменных, он создаёт локальные значения в var/local-config.json. Пустые строки из скопированного .env нужно предварительно заполнить через pnpm env:setup.
Как проверить решение жюри
Для первого прохода используйте текст: он позволяет отдельно проверить сценарии без влияния микрофона. Затем повторите запрос голосом.
- На клиентском экране введите
+77010000001. Это номер синтетического клиентаC001из mock_backend.json. Начните новое обращение. - Напишите «Где находится ваш офис в Алматы?». Ожидается сценарий
SC33, ответ по офисам и источникknowledge_base.json#/officesв панели решения. При необходимости откройте эту панель в интерфейсе. - Спросите «Алматыдағы кеңсеңіз қайда?». Проверьте ответ на казахском. Смените язык интерфейса и убедитесь, что прошлые сообщения остались на исходном языке.
- Попросите «Измените мой email на [email protected]». Отвечайте на уточнения помощника. До подтверждения должно появиться предложение с параметрами изменения. Проверьте их и отдельно подтвердите замену. В истории должен появиться результат действия. Адрес предназначен только для демонстрации.
- Напишите «Соедините меня с оператором». В другом окне откройте
/staff, войдите какoperator1сSTAFF_PASSWORDи примите обращение. Проверьте историю, результат изменения и возможность ответить клиенту. - Передайте обращение
operator2. В отдельном профиле браузера войдите как второй оператор: история должна сохраниться, прежний сотрудник теряет право отвечать. Отдельный профиль нужен, чтобы учётные записи не делили одну cookie. - В новом AI-обращении включите голос, разрешите микрофон и повторите вопрос об офисе. После паузы должны появиться текст и звук ответа. Попробуйте перебить помощника. При ошибке сохраните её текст: эта проверка зависит от браузера, микрофона, сети и доступа к моделям.
Для проверки восстановления закройте клиентское соединение, затем снова введите тот же номер и выберите незавершённое обращение. Проверьте сохранённые сообщения и отсутствие повторного выполнения уже подтверждённого действия.
Автоматические проверки
Без вызовов AI API:
pnpm typecheck
pnpm test
pnpm build
pnpm build проверяет синтаксис аудиомодуля и собирает web-приложение. Автотесты проверяют правила, подтверждения, повторы операций, изоляцию истории, сохранение SQLite и обработку речи на уровне кода.
С ключом OpenAI и платными вызовами API:
pnpm eval
pnpm eval dialogs
Первый прогон проверяет маршрутизацию 104 реплик, второй - примеры диалогов через модель, движок и SQLite. Отчёты сохраняются в evals/artifacts. Для запущенного приложения также предусмотрены:
# Проверка телефона и истории через HTTP, без вызовов модели
node --import tsx evals/src/phone-http.ts
# Голосовые проверки с реальным API
node --import tsx evals/src/live-transcription.ts
node --import tsx evals/src/live-voice.ts
node --import tsx evals/src/live-interruption.ts
В docs/validation.md зафиксированы прошлые результаты: 104 совпадения из 104 на предоставленном наборе реплик и 38 из 40 на ходах примерных диалогов. Это отчёт о конкретных прогонах, а не гарантия качества на новых данных. Синтетические голосовые тесты не заменяют проверку человеком.
Для возврата локальной демонстрации к исходным данным остановите сервисы и выполните pnpm demo:reset. Команда архивирует локальную базу; при следующем запуске она создаётся заново. На Docker volume эта команда не действует.
Данные и интеграции
Исходный набор находится в docs/voice_router_dataset. Его описание доступно на русском.
| Файл | Содержимое |
|---|---|
scenarios.json | 40 бизнес-сценариев и 3 системных исхода, приоритеты и границы выбора |
slots.json | 43 поля: типы, допустимые значения и вопросы для уточнения |
actions.json | 31 действие, параметры, ошибки и требования подтверждения |
knowledge_base.json | Учебные продукты, тарифы, офисы, клиники и правила |
mock_backend.json | Синтетические клиенты, полисы, страховые случаи и платежи |
dev_utterances.json | Размеченные реплики для проверки маршрутизации |
dialogs_sample.json | Примеры многошаговых диалогов |
Поиск знаний выбирает разделы JSON по сценарию, без векторной базы. Исходные файлы не изменяются; операции записываются в SQLite. В расчётах используется фиксированная дата учебного набора 2026-10-01, поэтому результат не следует трактовать как расчёт на текущую календарную дату.
Реальная внешняя интеграция приложения - OpenAI: текст, контекст и аудио отправляются в API для обработки. По умолчанию аудио не сохраняется локально. Страховая система, платёжный шлюз, SMS-провайдер, телефония и сервис записи клиник не подключены. Адреса, контакты и ссылки из учебной базы не являются адресом развёрнутого приложения.
Ограничения текущей версии
- Ввод телефона не подтверждается по SMS. Зная демонстрационный номер, можно начать сессию этого клиента. Для настоящих клиентских данных такой способ идентификации непригоден.
- Учётные записи сотрудников демонстрационные, с общим паролем. Отдельной административной панели нет.
- Приложение рассчитано на одну копию каждого сервиса. Распределённая очередь и работа нескольких экземпляров не реализованы.
- Голос рассчитан на русский, казахский и смешанную речь. Английский доступен в интерфейсе, но не входит в заявленный объём голосового MVP.
- При паузе около 700 мс детектор завершает реплику. Длинная пауза внутри вопроса может преждевременно запустить ответ. Полная приёмка свободной речи, эха и перебиваний человеком в отчёте не завершена.
- Ответ зависит от нескольких обращений к AI, поэтому задержка заметна и зависит от сети и моделей. Тестовые измерения получения звука не равны времени слышимого воспроизведения в браузере.
- Для части возрастов автомобиля нет тарифа КАСКО; реальное расписание клиник отсутствует. При недостатке тарифа требуется оператор, запись использует демонстрационное расписание.
- Защита и тесты покрывают конкретные сценарии, но не доказывают устойчивость ко всем ошибкам модели или атакам. Для промышленной эксплуатации нужны проверка личности, отдельные аккаунты и политика хранения данных.
Подробности проверки и расхождения с заданием находятся в docs/validation.md и docs/task-audit.md.
Развёрнутая версия
Подтверждённая ссылка на публичную развёрнутую версию в репозитории не указана. Для проверки используйте локальный адрес http://localhost:3000 после запуска.
Авторы коммитов
История коммитов
- 2026-09-23 17:54fix: preserve request context and scope knowledge retrievalMaksim Mukharev
- 2026-09-23 17:48docs: prepare Russian README for hackathon juryMaksim Mukharev
- 2026-09-23 17:40fix: detect voice pauses and preserve utterances on muteMaksim Mukharev
- 2026-09-23 17:22feat: bind conversations to phones and add streaming voice chatMaksim Mukharev
- 2026-09-23 16:17feat: implement Saqta Voice Router MVPMaksim Mukharev
Структура репозитория (85 файлов)
.dockerignore .env.example .gitignore .graphifyignore AGENTS.md Dockerfile README.md apps/gateway/package.json apps/gateway/src/index.ts apps/gateway/src/live.ts apps/scenario-service/package.json apps/scenario-service/src/index.ts apps/web/index.html apps/web/package.json apps/web/public/voice-worklet.js apps/web/src/capture.ts apps/web/src/live-voice.ts apps/web/src/main.tsx apps/web/src/style.css apps/web/src/vad.ts apps/web/src/voice.ts apps/web/vite.config.ts compose.yaml docs/HackAlem AI_ Voice Router - гибридный голосовой AI-робот с LLM-слоем выбора сценария.docx docs/task-audit.md docs/tool-versions.json docs/validation-results.json docs/validation.md docs/voice_router_dataset/README.kz.md docs/voice_router_dataset/README.md docs/voice_router_dataset/README.ru.md docs/voice_router_dataset/actions.json docs/voice_router_dataset/dev_utterances.json docs/voice_router_dataset/dialogs_sample.json docs/voice_router_dataset/evaluate.py docs/voice_router_dataset/knowledge_base.json docs/voice_router_dataset/mock_backend.json docs/voice_router_dataset/scenarios.json docs/voice_router_dataset/slots.json evals/package.json evals/src/http.ts evals/src/live-interruption.ts evals/src/live-transcription.ts evals/src/live-voice.ts evals/src/phone-http.ts evals/src/run.ts evals/src/voice.ts graphify-out/GRAPH_REPORT.md graphify-out/cost.json graphify-out/graph.html graphify-out/graph.json graphify-out/semantic-docs.json graphify-out/source-manifest.json package.json packages/ai/package.json packages/ai/src/index.ts packages/ai/src/streaming.ts packages/ai/src/transcription.ts packages/contracts/package.json packages/contracts/src/index.ts packages/core/package.json packages/core/src/actions.ts packages/core/src/index.ts packages/core/src/language.ts packages/core/test/engine.test.ts packages/core/test/knowledge.test.ts packages/core/test/phone.test.ts packages/core/test/streaming.test.ts packages/db/package.json packages/db/src/index.ts packages/i18n/package.json packages/i18n/src/index.ts packages/knowledge/package.json packages/knowledge/src/index.ts pnpm-lock.yaml pnpm-workspace.yaml scripts/dev.mjs scripts/env.ts scripts/graph-docs.py scripts/graph-update.mjs scripts/reset.ts scripts/setup.mjs scripts/smoke-ai.ts tsconfig.base.json tsconfig.json
Обсуждение 0 комментариев
Войдите, чтобы комментироватьПока нет комментариев — будьте первым.