Files
kk0t9 2eaa561892 docs(domain I): v2 — навигация (offline OSM/роутинг/turn-by-turn), после adversarial-ревью (24 находки) + кросс-док
Последний домен. 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>
2026-06-23 14:23:34 +03:00

168 lines
14 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Домен 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](../contracts/ipc.md) (`Assistant`) · [plugin-sdk.md](../contracts/plugin-sdk.md) (интенты) · [security-privacy.md](../contracts/security-privacy.md) · [tech-stack.md](../tech-stack.md) · [principles.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](../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](../contracts/plugin-sdk.md) §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](../contracts/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 |