О проекте
Команда: alt tab
Кейс HackAlem.ai: Коммуникации — Halyk Bank · кейс 2
Стадия: Доведённый продукт
Задача кейса: «Voice Router»: голосовой AI, маршрутизирующий звонки в контакт-центре страховой компании
Суть
Голосовой AI-оператор страхового контакт-центра: принимает речь на русском, казахском и смешанном языке, маршрутизирует по 40 сценариям, извлекает слоты, выполняет mock-действия и отвечает голосом, показывая супервизору трассу решения.
Как устроено
Браузер → LiveKit (WebRTC) → voice worker (livekit-agents, Silero VAD, Soniox STT) → FastAPI /turns. Router-вызов GPT-4.1 mini получает весь каталог сценариев с границами not_this_if и возвращает строгий JSON (сценарии с confidence, слоты, язык, согласие); политика уверенности 0.75/0.45 выбирает действие, уточнение или передачу оператору. Data-driven исполнитель 31 действия из actions.json работает на копии mock_backend.json с preview → согласие → execute для необратимых операций, отдельный LLM-вызов формирует реплику, Soniox TTS озвучивает её.
Ключевые решения
- LLM-роутинг по каталогу с not_this_if
- пороговая политика уверенности
- слот-филлинг
- очередь мульти-интентов
- preview/consent/execute
- идемпотентность request_id
- Silero VAD
- LiveKit WebRTC
Метрики
evaluate.py, 104 реплики: primary_acc 0.952, full_match 0.942; kk 0.978, ru 0.923, mixed 1.0; multi_intent 0.846/0.769
Особенности
Одна бизнес-логика для голоса и текста (worker только вызывает тот же HTTP /turns); исполнитель полностью управляется JSON кита; согласие привязано к параметрам preview. Честная трасса хода с задержками по этапам и метрики официального оценщика по языкам и типам реплик.
Стек
Python 3.12 · FastAPI · OpenAI SDK · LiveKit · livekit-agents · React 19 · TypeScript · Vite · Zod · Vitest · Playwright · uv
Модели
GPT-4.1 mini · Soniox STT stt-rt-v5 · Soniox TTS tts-rt-v2
Проект создан за 5 часов на финале хакатона HackAlem.ai (23 сентября 2026, Астана). Код — BAITC-Hacks/hack-6554d21c-alt-tab.
README
README.mdSaqta Voice Router
Голосовой AI-оператор контакт-центра страховой компании. Клиент говорит в микрофон на русском, казахском или на смеси языков. LLM выбирает один из 40 сценариев Saqta Insurance, сервер выполняет шаг сценария на синтетических данных, клиент слышит ответ голосом. Супервизор после каждой реплики видит трассу решения.
Команда Alt-Tab · HackAlem AI 2026 · кейс 2 «Voice Router»
Все вводные организаторов собраны в папке case/: JSON-данные (
scenarios.json,slots.json,actions.json,knowledge_base.json,mock_backend.json,dev_utterances.json,dialogs_sample.json), оценщикevaluate.py, README кита на трёх языках и исходный текст задания. Файлы сохранены без изменений, приложение загружает их с диска. Все данные синтетические: Saqta Insurance, клиенты, полисы и цены вымышлены организаторами.
Содержание
- Зачем это нужно
- Что умеет
- Как выглядит один ход
- Архитектура
- Как принимается решение
- Метрики
- Быстрый старт
- Проверки
- Ограничения
- Структура репозитория
- Команда и документы
Зачем это нужно
Контакт-центр страховой компании принимает звонки, где одно и то же слово означает разные вещи. «Авария» — это ДТП прямо сейчас (срочно), заявление пострадавшего по ОГПО виновника или убыток по КАСКО. «Выплату одобрили, но мало» — не запрос статуса, а спор с решением. Клиент может начать на казахском, закончить на русском и в одной фразе попросить две вещи.
Классический IVR с кнопками здесь не работает, а энкодерный классификатор намерений запрещён правилами кейса. Наше решение: LLM читает весь каталог сценариев с границами not_this_if и сама объясняет, почему выбрала именно этот сценарий. Голос и текст проходят через одну и ту же бизнес-логику, поэтому поведение агента одинаково в любом канале.
Что умеет
| Возможность | Состояние |
|---|---|
| Живой голос в браузере: микрофон → STT → LLM → TTS → динамики | ✅ Проверено в Chrome на RU, KK и mixed репликах |
| Маршрутизация по 40 сценариям + 3 системным намерениям | ✅ Каталог загружается из scenarios.json, ID валидируются |
| Русский, казахский, смешанная речь | ✅ Язык определяется моделью, ответ — на языке клиента |
| Несколько намерений в одной фразе | ✅ Срочное первым, остальные — в очередь сессии и исполняются следом |
Извлечение и проверка слотов (43 типа из slots.json) | ✅ Один уточняющий вопрос за ход |
| Трасса решения после каждой реплики | ✅ Сценарии, confidence, альтернативы, обоснование, слоты, действия, задержки |
| Политика уверенности: выбор / уточнение / оператор | ✅ Пороги 0.75 и 0.45 из правил кита |
Все 31 mock-действие из actions.json | ✅ Data-driven исполнитель над копией mock_backend.json в памяти сессии |
| Идентификация клиента по телефону или ИИН | ✅ Номер полиса или заявления подставляется из данных клиента |
| Расчёты по формулам базы знаний | ✅ Цены ОГПО, travel, возврат при расторжении КАСКО сверены с диалогами кита |
| Preview → явное согласие → execute для необратимых действий | ✅ Согласие привязано к параметрам preview, новые параметры снимают старый preview |
Идемпотентность: повтор request_id не повторяет действие | ✅ |
| Честный отказ: ошибка провайдера или данных не превращается в фиктивный успех | ✅ Коды ошибок только из error_codes кита, клиент слышит понятное сообщение |
| Передача оператору с резюме контекста | ✅ Очередь из actions.json, в UI помечена как симуляция |
Как выглядит один ход
Реальный прогон в браузере 2026-09-23 (Chrome → LiveKit → Soniox → GPT-4.1 mini). Клиент говорит смешанной речью и просит две вещи сразу:
🎤 Клиент: «Маған терапевтке жазылу керек, и ещё список клиник в Алматы»
Что увидел супервизор в панели трассы (сокращённый TurnResult, значения из лога прогона):
{
"turn": 1,
"transcript": "Маған терапевтке жазылу керек, и ещё список клиник в Алматы.",
"language": "kk",
"response_language": "kk",
"scenarios": [
{"scenario_id": "SC21", "confidence": 0.9},
{"scenario_id": "SC23", "confidence": 0.8}
],
"decision": "collect_slots",
"slots": {"doctor_specialty": "терапевт", "city": "Almaty"},
"missing_slots": ["policy_number", "preferred_date"],
"queued_scenarios": ["SC23"],
"reply": "Полис нөмірі қолыңызда болса, айтып жіберіңізші.",
"latency_ms": {"stt": null, "router": 1820, "response": 696, "tts_first_audio": null, "total": null}
}
🔊 Агент (на казахском): «Полис нөмірі қолыңызда болса, айтып жіберіңізші» — просит номер полиса ДМС, чтобы записать к терапевту. Список клиник (SC23) ждёт в очереди сессии и исполнится после записи.
Оба намерения распознаны, слоты извлечены из одной фразы, ответ дан на языке клиента. Язык помечен kk, а не mixed: казахских слов больше. null в задержках означает «не измерено», а не «ноль»: worker пока не снимает STT и время до первого звука.
Архитектура
flowchart LR
subgraph Browser["Браузер · React + Vite"]
MIC[🎤 Микрофон]
UI[Чат + панель трассы]
SPK[🔊 Динамики]
end
subgraph Voice["Голосовой контур"]
LK[LiveKit<br/>WebRTC-сервер]
W[Voice worker<br/>livekit-agents]
STT[Soniox STT<br/>stt-rt-v5 · ru/kk]
TTS[Soniox TTS<br/>tts-rt-v2]
end
subgraph Backend["FastAPI · единая бизнес-логика"]
API["/api/sessions/{id}/turns"]
SVC[TurnService<br/>сессия · слоты · очередь · политика]
RT[LLM Router<br/>GPT-4.1 mini]
EX[Executor<br/>31 действие · preview/execute]
RP[LLM Reply<br/>GPT-4.1 mini]
KB[(Каталог + KB + mock_backend<br/>из JSON кита)]
end
MIC -->|WebRTC| LK --> W --> STT
STT -->|текст реплики| API
UI -->|текстовый резерв| API
API --> SVC --> RT
SVC --> EX --> KB
SVC --> RP
SVC -->|TurnResult + trace| W
W -->|data channel saqta.trace| UI
W --> TTS --> LK --> SPK
| Слой | Технологии | Роль |
|---|---|---|
| Frontend | React 19, TypeScript, Vite, livekit-client, Zod | Диалог слева, трасса хода справа. Микрофон, звук, autoplay, восстановление истории |
| Транспорт | LiveKit (локальный, WebRTC) | Звук в обе стороны и data channel для трассы |
| Voice worker | livekit-agents, Soniox STT/TTS, Silero VAD | Распознаёт речь, вызывает тот же HTTP /turns, озвучивает ответ |
| Backend | FastAPI, Python 3.12, OpenAI SDK | Сессии, LLM-router, слоты, политика уверенности, исполнитель, трасса |
| LLM | GPT-4.1 mini, temperature=0 | Выбор сценариев, извлечение слотов, разбор согласия, реплика агента |
Ключевые решения
- Одна бизнес-логика для голоса и текста. Worker не думает сам, он вызывает тот же
POST /turns, что и текстовый ввод. Состояние живёт только в HTTP-сервисе. - Исполнитель управляется данными. Интерфейсы действий, необратимость, очереди handoff и коды ошибок берутся из
actions.json, формулы — изknowledge_base.json. Каждая сессия работает с собственной копиейmock_backend.jsonв памяти. - Голосовой конвейер на LiveKit + Soniox, а страховая логика, роутер и API написаны для кита.
- Ключи провайдеров только на сервере. Браузер получает токен конкретной комнаты LiveKit на 15 минут.
Как принимается решение
Каждая реплика проходит один и тот же путь:
- Router-вызов LLM. В системный промпт входят все 40 сценариев с
descriptionиnot_this_if, 3 системных намерения, каталог слотов и бизнес-дата2026-10-01. Модель возвращает строгий JSON: упорядоченные сценарии с confidence, альтернативы, язык, извлечённые слоты, признак продолжения, согласие или отказ на ожидающий preview и краткое обоснование на русском. ID вне каталога отбрасываются. - Политика уверенности из правил кита:
confidence ≥ 0.75→ сценарий выбран;0.45 ≤ confidence < 0.75илиSYS_UNCLEAR→ один уточняющий вопрос;- два хода подряд ниже
0.45, сценарий сhandoff.when = alwaysили просьба клиента → передача оператору с резюме контекста.
- Слоты и очередь. Извлечённые слоты проверяются по форматам
slots.json. Если не хватает обязательного — один вопрос. Короткий ответ («Алматы») заполняет ожидаемый слот, а не открывает новую тему. Второе и следующие намерения ставятся в очередь сессии, срочные (urgent) обрабатываются первыми. Когда первый сценарий завершён, следующий из очереди исполняется в том же ходу, если его слоты уже известны. - Идентификация.
find_clientищет клиента по телефону или ИИН в копииmock_backend.json. Если у клиента ровно один подходящий полис или заявление, номер подставляется без вопроса. Неизвестный клиент: один переспрос, затем handoff поerror_handlingизactions.json. - Исполнение. Действия с
irreversible: trueостанавливаются на preview: результат считается на копии данных и озвучивается клиенту вместе с параметрами. Согласие или отказ распознаёт LLM в следующей реплике. Исполняются ровно сохранённые параметры; изменение параметров или новая тема снимают старый preview. Обратимые действия выполняются сразу. - Reply-вызов LLM. Отдельный запрос формирует реплику агента: 1–2 предложения, язык клиента, числа словами, имена собственные из KB не переводятся.
- Трасса. Всё выше собирается в один
TurnResultсturn_id, уходит в UI по data channel и сохраняется в истории сессии.
Метрики
Реальный прогон evaluate.py организаторов на 104 репликах dev-набора. Predictions получены настоящими вызовами GPT-4.1 mini без контекста сессии; поле expected модели не передавалось.
| Группа | n | primary_acc | full_match |
|---|---|---|---|
| all | 104 | 0.952 | 0.942 |
| lang=kk | 45 | 0.978 | 0.956 |
| lang=ru | 52 | 0.923 | 0.923 |
| lang=mixed | 7 | 1.000 | 1.000 |
| type=single | 84 | 0.988 | 0.988 |
| type=multi_intent | 13 | 0.846 | 0.769 |
| type=out_of_scope | 4 | 1.000 | 1.000 |
| type=unclear | 3 | 0.333 | 0.333 |
intent_recall для multi-intent: 0.885. Предыдущий прогон того же router дал 0.962 / 0.962: разница в 1–2 реплики — колебания модели.
6 ошибок из 104, все объяснимы:
| ID | Реплика | Ожидалось | Получено | Почему |
|---|---|---|---|---|
| U030 | «Шетелде аяғымды сындырып алдым, сақтандыруым бар» | SC15 | SC16 | Травма за границей: соседние сценарии «медслучай за рубежом» и «травма по НС» |
| U086 | «Шетелге шығуға сақтандыру керек, қалай төлеуге болады?» | SC06 + SC31 | SC06 | Пропущено второе намерение «как оплатить» |
| U087 | «Отказали в выплате, и ваш оператор ещё и нагрубил» | SC19 + SC35 | SC35 | Пропущено намерение «спор с решением» |
| U090 | «Соседи затопили квартиру, что делать и какие документы собирать?» | SC14 + SC18 | SC14 | Пропущено второе намерение «документы» |
| U102 | «Алло, я по поводу страховки» | SYS_UNCLEAR | SC37 | Слишком короткая реплика, модель предпочла сценарий |
| U104 | «Ну там с машиной вопрос» | SYS_UNCLEAR | SC13 | То же |
10 диалогов кита (case/dialogs_sample.json) прогнаны через живой API скриптом scripts/run_dialogs.py: 40 ходов, 0 ошибок HTTP, средний router 1,7–1,9 с. Полные циклы preview → согласие → execute прошли в D01 (покупка ОГПО, 38 000), D02 (заявление потерпевшего, KK), D07 (расторжение КАСКО, возврат 163 800, KK), D10 (travel, 15 400); идентификация по телефону с автоподстановкой полиса — D03, D05, D07; неизвестный клиент — переспрос и handoff. Промахи router: после «спасибо» модель иногда остаётся в сценарии вместо SYS_GOODBYE, а смену темы после жалобы или фрода (D06 t2, D09 t3) держит в прежнем сценарии.
Dev-набор используется как диагностика, а не как цель подгонки: хардкода проверочных реплик нет. Разница между прогонами в пределах 1–2 реплик — колебания модели.
Задержки на прогретом соединении в браузерном прогоне: router 2,0–3,3 с, генерация ответа 0,7–2,5 с, первый запрос после старта до 9 с (холодное соединение). Сквозная задержка от конца речи до первого звука worker'ом пока не измеряется и честно возвращается как null.
Быстрый старт
Нужны: Python 3.12, uv, Node.js 24, Chrome. Данные сессий живут в памяти и сбрасываются при перезапуске.
Ключи провайдеров (вставить в .env):
OPENAI_API_KEY— https://platform.openai.com/api-keys (модель GPT-4.1 mini).SONIOX_API_KEY— зарегистрироваться на https://console.soniox.com, раздел API Keys → Create. Новому аккаунту даётся стартовый бесплатный баланс, его хватает на демо. Один ключ работает и для STT, и для TTS.
1. Backend и LiveKit (Windows, PowerShell):
$env:UV_CACHE_DIR = Join-Path $PWD '.cache/uv'
$env:UV_PYTHON_INSTALL_DIR = Join-Path $PWD '.cache/python'
uv sync --python 3.12 --all-extras --locked
if (-not (Test-Path .env)) { Copy-Item .env.example .env } # заполнить OPENAI_API_KEY и SONIOX_API_KEY
powershell -ExecutionPolicy Bypass -File scripts/install-livekit.ps1
.venv/Scripts/python scripts/dev.py
На macOS и Linux: те же команды uv sync и копирование .env, вместо install-livekit.ps1 нужен запущенный Docker Desktop, путь к интерпретатору — .venv/bin/python.
Одна команда scripts/dev.py поднимает локальный LiveKit, HTTP API на :8000 и голосовой worker. Swagger: http://127.0.0.1:8000/docs. Установщик скачивает официальный LiveKit 1.13.7 и сверяет SHA-256. Если нативный LiveKit не установлен, dev.py сам поднимет контейнер из deploy/compose.yaml через Docker Desktop. Если LiveKit уже слушает порт 7880, запускать с флагом --external-livekit.
2. Frontend (отдельный терминал):
cd frontend
npm ci
npm run dev
3. Открыть http://127.0.0.1:5174, нажать «Начать разговор», разрешить микрофон, дождаться приветствия и говорить.
Примеры реплик для проверки:
- RU: «Где ближайший офис в Алматы?»
- KK: «Алматыдағы кеңселеріңіз қайда?»
- KK: «Терапевтке жазып қойыңызшы»
- mixed: «Маған терапевтке жазылу керек, и ещё список клиник в Алматы»
- preview → согласие: «Хочу расторгнуть КАСКО», затем «да»
Демо-сценарий с подтверждением (клиент Камила, КАСКО SQ-CASCO-204350):
- «Продал машину, хочу расторгнуть КАСКО и вернуть деньги» → бот просит номер полиса или телефон.
- «Плюс семь семьсот один ноль ноль ноль ноль ноль десять» →
find_clientнаходит клиента, полис подставляется сам, бот уточняет причину, если не понял её из первой фразы. - «Продал машину» → в трассе
get_policyиcancel_policy preview, заполненpending_confirmation; бот озвучивает возврат 163 800 тенге и спрашивает подтверждение. Данные ещё не изменены. - «Нет, подождите» → preview снят, ничего не изменилось. «Всё же расторгните» → новый preview.
- «Да, подтверждаю» →
cancel_policy execute ok, полис в статусе cancelled, повтор «да» ничего не исполняет.
Тестовые клиенты (телефоны +7 701 000 00 XX): 07 — Сергей, статус заявления CL-500330; 08 — Алибек, проверка полиса ОГПО; 09 — Рустем, повторная отправка документов; 02 — Айгерим, запись к терапевту (ДМС, Астана); 99 — нет в базе, переспрос и оператор. Полный список — case/mock_backend.json.
Без голосового контура: scripts/dev.py --text-only запускает только API. Текстовый ввод в UI работает как резервный канал. API стартует без ключей, но запрос к LLM вернёт 503: mock-успеха вместо провайдера нет. После изменения .env процессы нужно перезапустить.
Проверки
.venv/Scripts/python -m pytest -q # 43 backend-тестов: router, service, executor, API, voice
.venv/Scripts/python scripts/evaluate_router.py # predictions реальным LLM → artifacts/
.venv/Scripts/python case/evaluate.py artifacts/predictions.json case/dev_utterances.json
.venv/Scripts/python scripts/run_dialogs.py # 10 диалогов кита через живой API → отчёт
.venv/Scripts/python scripts/smoke_transport.py # API + LiveKit подключение
cd frontend && npm test && npm run build # 28 unit-тестов
Автоматическая браузерная приёмка голоса: node scripts/e2e_live_voice.mjs <реплика.wav> <папка отчёта> при запущенных dev.py и npm run dev. Скрипт подаёт WAV в fake-микрофон Chrome, проходит весь путь через LiveKit и worker, сохраняет лог и скриншот.
Результаты браузерного прогона 2026-09-23:
| Реплика | STT | Router | Ответ |
|---|---|---|---|
| RU «Где ближайший офис в Алматы?» | дословно | SC33 execute, city=Almaty | адрес и часы, RU |
| KK «Алматыдағы кеңселеріңіз қайда?» | дословно | SC33 execute | адрес и часы, KK |
| KK «Терапевтке жазып қойыңызшы» | дословно | SC21 collect_slots | просит номер полиса, KK |
| mixed «Маған терапевтке жазылу керек, и ещё список клиник» | дословно | SC21 + очередь SC23 | KK, язык помечен kk |
В одном из шести прогонов первый ход получил 503 от LLM: интерфейс показал ошибку с request_id, агент озвучил «Не удалось обработать реплику», фиктивного ответа не было.
Unit-тесты подменяют LLM и HTTP и проверяют логику, а не качество речи. Всё, что требует провайдеров, проверялось живыми вызовами.
Ограничения
Пишем честно, потому что это влияет на оценку:
- Сквозная задержка не измеряется:
stt,tts_first_audio,totalвозвращаются какnull. - Короткие неясные реплики («Алло, я по поводу страховки») модель иногда относит к сценарию вместо
SYS_UNCLEAR. - Mixed иногда помечается как
kk, если казахских слов больше. На язык ответа это не влияет. - Зона travel определяется списком стран в коде: база знаний даёт только описание зон.
check_coverageсравнивает услугу с пакетом по ключевым словам. - Согласие и отказ разбирает LLM в составе router-вызова, отдельного классификатора нет.
- После завершённого сценария короткое «спасибо» или смена темы иногда остаются в прежнем сценарии: router получает историю и слоты сессии и предпочитает продолжение.
- Телефон казахскими числительными («жеті жүз бір…») распознаётся нестабильно; неверный формат отбрасывается и переспрашивается, а не подставляется.
- Если клиент начинает говорить во время приветствия агента, STT может потерять первое слово. Система переспрашивает, а не угадывает.
- Удалённый доступ требует HTTPS/WSS и не настроен: демо работает на localhost.
Структура репозитория
backend/ FastAPI: app.py (HTTP), service.py (сессии, политика, слоты, preview/confirm),
executor.py (31 действие над mock_backend), router.py (LLM-router и reply),
voice.py (LiveKit worker), catalog.py (загрузка JSON кита)
frontend/ React + Vite симулятор: чат, микрофон, панель трассы, LiveKit-клиент
scripts/ dev.py (запуск всего), evaluate_router.py, run_dialogs.py,
e2e_live_voice.mjs, smoke_transport.py, install-livekit.ps1
tests/backend/ pytest: router, service, executor, API и voice-адаптер
case/ вводные организаторов без изменений: 7 JSON-файлов данных, evaluate.py,
README кита (en/ru/kz), task-original.txt — исходный текст задания
ТЗ/ api-contract.md — контракт API v2 между frontend и backend
deploy/ compose.yaml — LiveKit в Docker, если нативный сервер не установлен
Команда и документы
Alt-Tab. Backend, LLM, исполнитель и голосовой контур — Акежан. Frontend и браузерный симулятор — тиммейт команды.
| Документ | Содержание |
|---|---|
| ТЗ/api-contract.md | Контракт API v2 между frontend и backend |
| frontend/README.md | Симулятор: запуск, режимы Live/Mock, обработка ошибок |
Авторы коммитов
История коммитов
- 2026-09-23 17:49README: запуск на macOS/Linux, откуда взять ключи OpenAI и SonioxАкежан
- 2026-09-23 17:40Voice: приветствие агента на казахскомАкежан
- 2026-09-23 17:38Репозиторий: чистка документации, compose.yaml перенесён в deploy/Акежан
- 2026-09-23 17:30Frontend: кнопка голоса читаема после нажатия, виден статус подключенияАкежан
- 2026-09-23 17:29Voice: скорость TTS 1.1Акежан
- 2026-09-23 17:23README: метрики после B3, демо-сценарий с подтверждением, итоги прогона 10 диалоговАкежан
- 2026-09-23 17:12B3: правки по живому прогону 10 диалоговАкежан
- 2026-09-23 16:54Вводные организаторов собраны в case/Акежан
Все коммиты (28)
- 2026-09-23 16:51README: убрать ссылки на документы вне GitАкежан
- 2026-09-23 16:51README: главный README проекта для жюри вместо README кита и README.app.mdАкежан
- 2026-09-23 16:48B3: data-driven исполнитель 31 действия, идентификация, preview → согласие → executeАкежан
- 2026-09-23 16:23Reply: имена собственные из KB транслитерировать, не подменять; temperature 0Акежан
- 2026-09-23 16:18README: метрики evaluate.py после правил языкаАкежан
- 2026-09-23 16:17Router: явные правила языка реплики и языка ответаАкежан
- 2026-09-23 16:15Скрипт браузерной приёмки голоса и README после слияния frontendАкежан
- 2026-09-23 16:13Merge codex/backend-router: backend, LLM-router, LiveKit workerАкежан
- 2026-09-23 16:13Merge codex/frontend-simulator: браузерный симулятор с LiveKitАкежан
- 2026-09-23 16:04README: транзиентный 503 в браузерном прогоне и его обработкаАкежан
- 2026-09-23 16:04API: логировать класс причины ProviderError без тела ответа провайдераАкежан
- 2026-09-23 16:00Браузерный голос проверен на frontend тиммейта: voice_ready по конфигурацииАкежан
- 2026-09-23 15:49feat(frontend): implement Saqta simulator with LiveKit v2Almas
- 2026-09-23 15:29README: метрики реального прогона evaluate.pyАкежан
- 2026-09-23 15:27Контракт API v2 в репозитории для frontendАкежан
- 2026-09-23 15:22Исправления по code review: комната worker, валидация слотов, handoff из каталогаАкежан
- 2026-09-23 15:11README: статус живой проверки OpenAI и SonioxАкежан
- 2026-09-23 15:07Backend: FastAPI, LLM-router 40+3, сессии, LiveKit-адаптерАкежан
- 2026-09-23 14:46feat(frontend): scaffold Saqta simulator and typed REST contractAlmas
- 2026-09-23 13:56Вводные данныеAlmas
Структура репозитория (74 файлов)
.env.example .gitignore README.md backend/__init__.py backend/app.py backend/catalog.py backend/config.py backend/executor.py backend/models.py backend/router.py backend/service.py backend/voice.py case/README.kz.md case/README.md case/README.ru.md case/actions.json case/dev_utterances.json case/dialogs_sample.json case/evaluate.py case/knowledge_base.json case/mock_backend.json case/scenarios.json case/slots.json case/task-original.txt deploy/compose.yaml frontend/.env.example frontend/.gitattributes frontend/.gitignore frontend/DESIGN.md frontend/PLAN.md frontend/README.md frontend/e2e/simulator.spec.ts frontend/index.html frontend/package-lock.json frontend/package.json frontend/playwright.config.ts frontend/src/App.test.tsx frontend/src/App.tsx frontend/src/api/client.test.ts frontend/src/api/client.ts frontend/src/api/mock.ts frontend/src/api/types.ts frontend/src/components/Icon.tsx frontend/src/components/TracePanel.tsx frontend/src/components/VoiceControls.test.tsx frontend/src/components/VoiceControls.tsx frontend/src/data/labels.ts frontend/src/data/scenarios.json frontend/src/data/slots.json frontend/src/main.tsx frontend/src/styles.css frontend/src/test/fixture.ts frontend/src/test/setup.ts frontend/src/ui.css frontend/src/useConversation.test.ts frontend/src/useConversation.ts frontend/src/voice/livekit.test.ts frontend/src/voice/livekit.ts frontend/tsconfig.json frontend/vite.config.ts frontend/vitest.config.ts pyproject.toml scripts/dev.py scripts/e2e_live_voice.mjs scripts/evaluate_router.py scripts/install-livekit.ps1 scripts/run_dialogs.py scripts/smoke_transport.py tests/backend/test_api_voice.py tests/backend/test_executor.py tests/backend/test_router.py tests/backend/test_service.py uv.lock ТЗ/api-contract.md
Обсуждение 0 комментариев
Войдите, чтобы комментироватьПока нет комментариев — будьте первым.