Последний домен. Offline-first навигация: OSM-карты (MapLibre GL Native), оффлайн-роутинг (Valhalla), turn-by-turn, поиск/POI, связка с ассистентом. Не safety/ADAS (#1). Фаза v4. Многоагентный adversarial-ревью: 28 находок, 24 подтверждены (default-refute), все применены. Ключевое из ревью: - stale нав-промпт под звонком (проигрался бы ПОСЛЕ поворота) → продюсер I снимает заявку по окну релевантности; H остаётся контент-агностичным. - Нет секции отказ-путей → добавлена (no-route/geocoder-fail/битые-тайлы/ENOSPC, симметрично J/H/L). - Маршрут через границу регионов / назначение в нескачанном регионе → детект покрытия + предложение докачать / маршрут до края. - reroute-шторм в urban-canyon → детект схода с гистерезисом + HDOP/FixQuality-гейт; dead_reckoning трактуется как нет-фикса. - Якорь RDS-TMC G §7 → H §7 (FM-радио живёт в медиа-тракте H, не в сетевом G). - MapLibre Native = BSD-2-Clause (не BSD-3); ODbL-атрибуция (#12); единицы км/мили из локали (data-model §4, #10). - Интент «домой» разведён: D §6 = домашний экран (v1), навигация-домой = Nav-интент (v4). - Добавлены секции Зависимости и last-parked (K §2/L §5 ждали); Boot-Stage 2; speed-limit — пассивный бейдж, не предупреждение (#1). Кросс-док: d-assistant §6 («домой» = домашний экран; навигация-домой → Nav v4). (architecture §3/§6 уже содержат Nav; a-base apps-storage уже покрывает нав-PII.) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
14 KiB
Домен D — Голосовой ассистент
Русскоязычный со-пилот: слышит речь, видит данные машины (через E) и объясняет их по-человечески. Интегрирован в UI лаконично (как Grok в Tesla). Вторая половина killer-фичи. First-party ап на SDK (Rust + ONNX Runtime).
Статус: v2 (на ревью). v2 — после ретро-ревью (9 находок).
Связано с: домены E (Vehicle-Data), K (GPS/Location), B (power-гейт wake-word) · ipc.md (Assistant) · plugin-sdk.md (интенты) · security-privacy.md · tech-stack.md · principles.md (#3,#5,#6,#7,#8)
1. Назначение и границы
- Что делает: голосовой и текстовый диалог по-русски; объясняет состояние машины; выполняет интенты (свои + плагинов).
- Границы: не трогает CAN напрямую (контекст берёт у E); не даёт советов, опасных для вождения (констрейнт системного промпта); никаких safety-critical.
2. Пайплайн
Mic-массив → AEC → Wake-word → VAD → STT(RU) → Intent router
→ [локальные интенты] (громче/домой/… — без LLM, низкая латентность)
→ [LLM backend] (свободный диалог + объяснения + контекст машины)
→ TTS(RU) → Audio out
(AEC = эхоподавление с loopback-референсом TTS/медиа-выхода из PipeWire — нужно для wake-word во время воспроизведения и для barge-in.)
Компоненты: AEC (loopback-ref выхода) · wake-word (openWakeWord, Apache-2.0, RU-фраза
«Штурман»; Porcupine проприетарный → #12, избегаем) · VAD (Silero) · STT (Vosk/Silero, офлайн) ·
intent router · LLM backend (pluggable) · TTS (Silero, офлайн).
Инференс: ONNX-модели (Silero VAD/STT/TTS) — через ONNX Runtime (ort); Vosk — нативный
движок Kaldi (крейт vosk). См. tech-stack.md.
3. Функции
| функция | MVP/later | зависит от | фаза |
|---|---|---|---|
| Wake-word «Штурман» | MVP | mic, tech-stack | v1 |
| VAD (Silero) | MVP | — | v1 |
| STT RU офлайн (Vosk/Silero) | MVP | — | v1 |
| TTS RU офлайн (Silero) | MVP | — | v1 |
| Push-to-talk + текстовый лог диалога | MVP | Shell | v1 |
| Intent router | MVP | — | v1 |
| Локальные интенты (громче/домой/…) | MVP | — | v1 |
| LLM online (GigaChat/YandexGPT) | MVP | Connectivity | v1 |
| Multi-turn контекст | MVP | — | v1 |
| Vehicle-context injection | MVP (killer) | E, data-model | v2 |
| Online degradation/timeout UX («думаю…»/«нет сети») | MVP | — | v1 |
| Driver-distraction (приоритет голосу) | MVP | Shell (owner); K (GPS) v1; E (OBD) v2 | v1/v2 |
| Provider switch + офлайн-фолбэк (llama.cpp) | later | — | v3 |
Память о водителе (.md) |
later | storage | v3 |
| Plugin-интенты (dispatch в IntentHandler) | later | plugin-sdk | v3 |
| Barge-in (прерывание TTS голосом) | later | AEC, PipeWire loopback | later |
4. Vehicle-context injection (killer-фича)
Ассистент получает в системный промпт live-снимок данных машины (из E) и объясняет простым русским. Пример:
[SYSTEM] Ты — Штурман, голосовой со-пилот. Кратко, по-русски, для водителя в движении.
Не давай советов, опасных для вождения.
Данные машины (только валидные/свежие; недоступные опущены):
Скорость 64 км/ч · Обороты 2100 · Охлаждайка 92°C · Бортсеть 14.1В
Лампа Check: горит · Ошибки: P0420 (катализатор, банк 1 — низкая эффективность)
(расход: нет данных)
[Память о водителе]: …
[USER] Почему горит чек?
[ASSISTANT] Из-за P0420 — система считает, что катализатор работает неэффективно.
Часто это датчик кислорода или сам катализатор. Машина поедет, но на диагностику стоит заехать.
Снимок берётся из VehicleData (E); коды → стандартное описание из базы, человеческое объяснение — LLM.
Контракт инъектора контекста:
- Ассистент — first-party SDK-ап, декларирует
vehicle_read:[…](speed, rpm, coolant_temp, module_voltage, fuel_level, mil_on, dtc_count) +GetDtcs(); доступ гейтится тем же VehicleData-прокси, что и у плагинов (ipc §5) — first-party = авто-грант, не «свободный доступ». - В промпт — только сигналы
quality=valid(data-model §2/§3);stale/unavailable— исключаются или помечаются «нет данных» (LLM не утверждает устаревшее). - Состав гейтится состоянием машины (
off/accessory/running). - Деградация на простых авто (Lada): богатые PID часто Unavailable → снимок схлопывается
до
mil_on+ DTC — это всегда-доступный MVP-базис.
5. LLM backend (pluggable, provider-agnostic)
- Единый интерфейс бэкенда, реализации сменные (принцип #8).
- Online: GigaChat (OAuth2), YandexGPT (IAM) — HTTP, RU/152-ФЗ.
- Offline: локальная квантованная модель через llama.cpp (фаза v3).
- Авто-fallback: ошибка/нет сети → офлайн; восстановилась — обратно.
- Креды — свои у пользователя: проект не шипит ключи; пользователь подключает свой GigaChat/YandexGPT (токены в защищённом хранилище, refresh; облако — за его счёт).
- До офлайн-фолбэка (v1 online-only): сеть в движении нестабильна → мягкая деградация: «думаю…», таймаут → «нет сети», без зависаний. Офлайн-фолбэк (v3) делает ассистента надёжным.
- Принцип #3 (offline-first) в v1 — частично: wake-word, STT/TTS и локальные интенты (громче/домой) работают офлайн, ассистент не немеет без сети; офлайн-диалог (llama.cpp) — v3. (Полное #3-соответствие — v3; см. principles #3.)
6. Интенты (routing)
- Локальные латентно-критичные (громче, домой = домашний экран/лаунчер, отмена) — без LLM, мгновенно. (Навигация-домой «поехали домой» — отдельный Nav-интент: матч фразы здесь, резолв+исполнение в I §9, v4.)
- Свободный диалог / объяснения — LLM (с контекстом машины + памятью).
- Plugin-интенты — dispatch в
IntentHandlerплагина; коллизии разрешаются по политике plugin-sdk §5; LLM-роутер — арбитр по контексту.
7. Память о водителе (.md)
- Хранит факты о водителе/привычках/машине в локальных
.md(приватное хранилище/data/apps/assistant/, как контекст-память у Claude/Cursor). - Использование: подмешивается в контекст (как и данные машины).
- Приватность: локально; не уходит в облако без явного согласия (security-privacy §7); пользователь может смотреть/править/чистить.
- 🟡 Осторожно: что именно авто-запоминать — приватно-чувствительно; нужна аккуратная политика (по умолчанию минимум, прозрачно).
8. Приватность
- Голос обрабатывается локально (STT) до отправки чего-либо; в облако — только текст запроса при онлайн-LLM. Контекст машины уходит провайдеру только онлайн и по запросу (security-privacy §7).
- Wake-word по состояниям питания (не безусловно always-on): работает при заведённом /
ACC-on; в sleep/off — выключен (риск разряда АКБ на стоянке, принцип #5). Доступен только с
Stage 2 (в boot-окне голоса нет). Гейтится сигналами Power (
AccChanged/Sleep/Wake, ipc). Включённый микрофон — видимый индикатор. Политика прослушки на ACC-off + бюджет разряда — ◻️ домен B/hardware.
9. Зависимости
- Вниз/вбок: E (контекст машины,
vehicle_read+GetDtcsчерез прокси), K (GPS-скорость для distraction v1), Connectivity (online LLM), PipeWire (audio_in/audio_out+ loopback-tap выхода для AEC), Power/B (гейт wake-word по ACC/sleep), Settings, Shell (UI; политику distraction владеет Shell §7), security-privacy (mic-индикатор), tech-stack (ONNX Runtime,vosk, llama.cpp), data-model (коды/сигналы/quality для контекста).
10. Открытые вопросы
- 🟡 Память о водителе: что авто-запоминать, схема, consent (приватность). → этот домен + security-privacy.
- 🟡 Выбор офлайн-модели (YandexGPT Lite / T-lite / Qwen, квантизация) — по перфу/RAM RK3588. → отложено (бенч на железе).
- ◻️ Подход к матчингу интентов (грамматика/ключевые слова для локальных vs LLM-роутер). → реализация.
- ◻️ Бюджет латентности пайплайна (wake→ответ) — цель и измерение. → реализация.
- ◻️ STT: Vosk (стриминг, легче) vs Silero (качество) — выбрать/поддержать оба. → реализация.
- ◻️ Barge-in (прерывание TTS) — later.
- ◻️ Размер контекста: машина + память + история → токены (особенно у офлайн-модели с маленьким окном); нужна обрезка/суммаризация. → реализация.
- ✅ AEC (эхоподавление): подавление собственного TTS/медиа-выхода (loopback-ref из
PipeWire) — нужно для wake-word во время воспроизведения и barge-in. Резолв: узел audio-plane
на базе
module-echo-cancel(WebRTC APM), референс = monitor выхода, не внутри ассистента — loopback-tap публикует Media (H §4); ассистент потребляет уже очищенную capture-ноду. - ◻️ Beamforming/денойз (внешний шум салона): mic-массив; офлайн-STT фиксирован приватностью. → hardware.md.
- ◻️ Plugin-интенты как function-calling (tool-use LLM) vs фразы — зависит от поддержки провайдером (у GigaChat/YandexGPT может быть ограничена). → реализация.
- ◻️ Обучение wake-word «Штурман» (openWakeWord) — сбор данных/тренировка. → реализация.
Журнал решений (домен D)
| Решение | Выбор | Дата |
|---|---|---|
| Пайплайн | Mic→AEC→wake→VAD→STT→router→(локальные/LLM)→TTS; ONNX (Silero) + Vosk (Kaldi) | 2026-06-16 |
| LLM | provider-agnostic; online (v1) + офлайн-диалог (v3, llama.cpp); деградация UX в v1 | 2026-06-16 |
| Контекст-инъекция | только quality=valid, гейт по состоянию машины; vehicle_read через прокси |
2026-06-16 |
| Память о водителе | локальные .md в приватном хранилище; не в облако без согласия |
2026-06-16 |
| Локальные интенты | латентно-критичные — без LLM | 2026-06-16 |
| Wake-word | openWakeWord (Apache-2.0); Porcupine отвергнут (проприетарный, #12) | 2026-06-16 |
| Микрофон | wake-word по состояниям питания (не always-on), с Stage 2; AEC обязателен | 2026-06-16 |