docs(domain D): v2 — ретро-ревью pass-2 (9 находок) + кросс-док

- AEC (эхоподавление, loopback-ref из PipeWire) добавлен в пайплайн — нужен для
  wake-word во время воспроизведения и barge-in (был полностью пропущен)
- auto-fallback: v1 = online degradation UX, офлайн-фолбэк (llama.cpp) = v3 (раньше
  фолбэкать в v1 было некуда); offline-first (#3) в v1 частично, полный — v3
- инференс: ONNX (Silero) + Vosk через нативный Kaldi (не «всё через ONNX»)
- vehicle-context: контракт инъектора — только quality=valid, гейт по состоянию машины,
  ассистент декларирует vehicle_read через прокси; деградация на простых авто
- wake-word по состояниям питания (не безусловно always-on, риск разряда; Stage 2; гейт Power/B)
- distraction: owner Shell §7, скорость K(GPS v1)/E(OBD v2); журнал wake-word/mic
- кросс-док: tech-stack (Porcupine отвергнут), principles #3 (фазировка офлайн-диалога)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-21 01:25:11 +03:00
parent d112f5397d
commit 02f13db1f3
3 changed files with 53 additions and 30 deletions
+51 -28
View File
@@ -4,8 +4,8 @@
> их по-человечески. Интегрирован в UI лаконично (как Grok в Tesla). Вторая половина > их по-человечески. Интегрирован в UI лаконично (как Grok в Tesla). Вторая половина
> killer-фичи. First-party ап на SDK (Rust + ONNX Runtime). > killer-фичи. First-party ап на SDK (Rust + ONNX Runtime).
Статус: **v1 (на ревью).** Статус: **v2 (на ревью).** v2 — после ретро-ревью (9 находок).
Связано с: домен E (Vehicle-Data) · [ipc.md](../contracts/ipc.md) (`Assistant`) · [plugin-sdk.md](../contracts/plugin-sdk.md) (интенты) · [security-privacy.md](../contracts/security-privacy.md) (mic, приватность) · [tech-stack.md](../tech-stack.md) · [principles.md](../principles.md) (#3,#6,#7,#8) Связано с: домены 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)
--- ---
@@ -19,16 +19,19 @@
## 2. Пайплайн ## 2. Пайплайн
``` ```
Mic-массив → Wake-word → VAD → STT(RU) → Intent router Mic-массив → AEC → Wake-word → VAD → STT(RU) → Intent router
→ [локальные интенты] (громче/домой/… — без LLM, низкая латентность) → [локальные интенты] (громче/домой/… — без LLM, низкая латентность)
→ [LLM backend] (свободный диалог + объяснения + контекст машины) → [LLM backend] (свободный диалог + объяснения + контекст машины)
→ TTS(RU) → Audio out → TTS(RU) → Audio out
``` ```
*(AEC = эхоподавление с loopback-референсом TTS/медиа-выхода из PipeWire — нужно для
wake-word во время воспроизведения и для barge-in.)*
Компоненты: wake-word (**openWakeWord**, Apache-2.0, RU-фраза «Штурман»; Porcupine — Компоненты: **AEC** (loopback-ref выхода) · wake-word (**openWakeWord**, Apache-2.0, RU-фраза
коммерческий/проприетарный → конфликт с принципом #12, избегаем) · VAD (Silero) · «Штурман»; Porcupine проприетарный → #12, избегаем) · VAD (Silero) · STT (Vosk/Silero, **офлайн**) ·
STT (Vosk/Silero, **офлайн**) · intent router · LLM backend (pluggable) · TTS (Silero, офлайн). intent router · LLM backend (pluggable) · TTS (Silero, офлайн).
Инференс — через ONNX Runtime (`ort`); см. [tech-stack.md](../tech-stack.md). **Инференс:** ONNX-модели (Silero VAD/STT/TTS) — через ONNX Runtime (`ort`); **Vosk — нативный
движок Kaldi** (крейт `vosk`). См. [tech-stack.md](../tech-stack.md).
## 3. Функции ## 3. Функции
@@ -44,12 +47,12 @@ STT (Vosk/Silero, **офлайн**) · intent router · LLM backend (pluggable)
| LLM online (GigaChat/YandexGPT) | **MVP** | Connectivity | v1 | | LLM online (GigaChat/YandexGPT) | **MVP** | Connectivity | v1 |
| Multi-turn контекст | **MVP** | — | v1 | | Multi-turn контекст | **MVP** | — | v1 |
| **Vehicle-context injection** | **MVP (killer)** | E, data-model | v2 | | **Vehicle-context injection** | **MVP (killer)** | E, data-model | v2 |
| Provider auto-fallback | **MVP** | — | v1 | | Online degradation/timeout UX («думаю…»/«нет сети») | **MVP** | — | v1 |
| Driver-distraction (приоритет голосу на скорости) | **MVP** | Shell, E(speed) | v1/v2 | | Driver-distraction (приоритет голосу) | **MVP** | Shell (owner); K (GPS) v1; E (OBD) v2 | v1/v2 |
| LLM offline fallback (локальная модель) | later | — | v3 | | Provider switch + офлайн-фолбэк (llama.cpp) | later | — | v3 |
| Память о водителе (`.md`) | later | storage | v3 | | Память о водителе (`.md`) | later | storage | v3 |
| Plugin-интенты (dispatch в IntentHandler) | later | plugin-sdk | v3 | | Plugin-интенты (dispatch в IntentHandler) | later | plugin-sdk | v3 |
| Barge-in (прерывание TTS голосом) | later | | later | | Barge-in (прерывание TTS голосом) | later | **AEC**, PipeWire loopback | later |
## 4. Vehicle-context injection (killer-фича) ## 4. Vehicle-context injection (killer-фича)
@@ -59,17 +62,27 @@ STT (Vosk/Silero, **офлайн**) · intent router · LLM backend (pluggable)
``` ```
[SYSTEM] Ты — Штурман, голосовой со-пилот. Кратко, по-русски, для водителя в движении. [SYSTEM] Ты — Штурман, голосовой со-пилот. Кратко, по-русски, для водителя в движении.
Не давай советов, опасных для вождения. Не давай советов, опасных для вождения.
Данные машины (только чтение, могут быть неточны): Данные машины (только валидные/свежие; недоступные опущены):
Скорость 64 км/ч · Обороты 2100 · Охлаждайка 92°C · Бортсеть 14.1В Скорость 64 км/ч · Обороты 2100 · Охлаждайка 92°C · Бортсеть 14.1В
Лампа Check: горит · Ошибки: P0420 (катализатор, банк 1 — низкая эффективность) Лампа Check: горит · Ошибки: P0420 (катализатор, банк 1 — низкая эффективность)
(расход: нет данных)
[Память о водителе]: … [Память о водителе]: …
[USER] Почему горит чек? [USER] Почему горит чек?
[ASSISTANT] Из-за P0420 — система считает, что катализатор работает неэффективно. [ASSISTANT] Из-за P0420 — система считает, что катализатор работает неэффективно.
Часто это датчик кислорода или сам катализатор. Машина поедет, но на диагностику стоит заехать. Часто это датчик кислорода или сам катализатор. Машина поедет, но на диагностику стоит заехать.
``` ```
Снимок берётся из `VehicleData` (E); коды → стандартное описание из базы, человеческое Снимок берётся из `VehicleData` (E); коды → стандартное описание из базы, человеческое объяснение — LLM.
объяснение — 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) ## 5. LLM backend (pluggable, provider-agnostic)
@@ -79,9 +92,11 @@ STT (Vosk/Silero, **офлайн**) · intent router · LLM backend (pluggable)
- **Авто-fallback:** ошибка/нет сети → офлайн; восстановилась — обратно. - **Авто-fallback:** ошибка/нет сети → офлайн; восстановилась — обратно.
- **Креды — свои у пользователя:** проект не шипит ключи; пользователь подключает - **Креды — свои у пользователя:** проект не шипит ключи; пользователь подключает
свой GigaChat/YandexGPT (токены в защищённом хранилище, refresh; облако — за его счёт). свой GigaChat/YandexGPT (токены в защищённом хранилище, refresh; облако — за его счёт).
- **До офлайн-фолбэка (v1 online-only):** сеть в движении нестабильна → мягкая - **До офлайн-фолбэка (v1 online-only):** сеть в движении нестабильна → мягкая деградация:
деградация: «думаю…», таймаут → «нет сети», без зависаний. Офлайн-фолбэк (v3) и «думаю…», таймаут → «нет сети», без зависаний. Офлайн-фолбэк (v3) делает ассистента надёжным.
делает ассистента по-настоящему надёжным. - **Принцип #3 (offline-first) в v1 — частично:** wake-word, STT/TTS и локальные интенты
(громче/домой) работают офлайн, ассистент не немеет без сети; офлайн-**диалог** (llama.cpp) — v3.
(Полное #3-соответствие — v3; см. principles #3.)
## 6. Интенты (routing) ## 6. Интенты (routing)
@@ -103,15 +118,19 @@ STT (Vosk/Silero, **офлайн**) · intent router · LLM backend (pluggable)
## 8. Приватность ## 8. Приватность
- Голос обрабатывается **локально** (STT) до отправки чего-либо; в облако — только - Голос обрабатывается **локально** (STT) до отправки чего-либо; в облако — только
**текст запроса** при онлайн-LLM. Микрофон always-on для wake-word, но локально + **текст запроса** при онлайн-LLM. Контекст машины уходит провайдеру только онлайн и по запросу (security-privacy §7).
**видимый индикатор** прослушки. Контекст машины уходит провайдеру только онлайн и - **Wake-word по состояниям питания (не безусловно always-on):** работает при заведённом /
по запросу (security-privacy §7). ACC-on; в sleep/off — выключен (риск разряда АКБ на стоянке, принцип #5). Доступен только с
**Stage 2** (в boot-окне голоса нет). Гейтится сигналами Power (`AccChanged`/`Sleep`/`Wake`, ipc).
Включённый микрофон — **видимый индикатор**. Политика прослушки на ACC-off + бюджет разряда — ◻️ домен B/hardware.
## 9. Зависимости ## 9. Зависимости
- **Вниз/вбок:** E (контекст машины), Connectivity (online LLM), PipeWire (`audio_in`/`audio_out`), - **Вниз/вбок:** E (контекст машины, `vehicle_read`+`GetDtcs` через прокси), **K** (GPS-скорость
Settings, Shell (UI-интеграция, driver-distraction), security-privacy (mic-индикатор), для distraction v1), Connectivity (online LLM), **PipeWire** (`audio_in`/`audio_out` +
tech-stack (ONNX Runtime, llama.cpp), data-model (коды/сигналы для контекста). **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. Открытые вопросы ## 10. Открытые вопросы
@@ -123,8 +142,10 @@ STT (Vosk/Silero, **офлайн**) · intent router · LLM backend (pluggable)
- ◻️ **Barge-in** (прерывание TTS) — later. - ◻️ **Barge-in** (прерывание TTS) — later.
- ◻️ **Размер контекста:** машина + память + история → токены (особенно у офлайн-модели - ◻️ **Размер контекста:** машина + память + история → токены (особенно у офлайн-модели
с маленьким окном); нужна обрезка/суммаризация. → реализация. с маленьким окном); нужна обрезка/суммаризация. → реализация.
- ◻️ **Точность офлайн-STT в шуме:** приватность фиксирует офлайн-STT → критичен - ◻️ **AEC (эхоподавление):** подавление собственного TTS/медиа-выхода (loopback-ref из
**mic-массив** (beamforming/шумоподавление). → этот домен + [hardware.md](../contracts/hardware.md). PipeWire) — нужно для wake-word во время воспроизведения и barge-in; место (WirePlumber/
filter-chain vs внутри ассистента). → этот домен + architecture (audio plane).
- ◻️ **Beamforming/денойз** (внешний шум салона): mic-массив; офлайн-STT фиксирован приватностью. → [hardware.md](../contracts/hardware.md).
- ◻️ **Plugin-интенты как function-calling** (tool-use LLM) vs фразы — зависит от - ◻️ **Plugin-интенты как function-calling** (tool-use LLM) vs фразы — зависит от
поддержки провайдером (у GigaChat/YandexGPT может быть ограничена). → реализация. поддержки провайдером (у GigaChat/YandexGPT может быть ограничена). → реализация.
- ◻️ **Обучение wake-word «Штурман»** (openWakeWord) — сбор данных/тренировка. → реализация. - ◻️ **Обучение wake-word «Штурман»** (openWakeWord) — сбор данных/тренировка. → реализация.
@@ -135,8 +156,10 @@ STT (Vosk/Silero, **офлайн**) · intent router · LLM backend (pluggable)
| Решение | Выбор | Дата | | Решение | Выбор | Дата |
|---------|-------|------| |---------|-------|------|
| Пайплайн | wake→VAD→STT→router→(локальные/LLM)→TTS; ONNX Runtime | 2026-06-16 | | Пайплайн | Mic→AEC→wake→VAD→STT→router→(локальные/LLM)→TTS; ONNX (Silero) + Vosk (Kaldi) | 2026-06-16 |
| LLM | provider-agnostic; online GigaChat/YandexGPT + offline llama.cpp; авто-fallback | 2026-06-16 | | LLM | provider-agnostic; online (v1) + офлайн-диалог (v3, llama.cpp); деградация UX в v1 | 2026-06-16 |
| Контекст машины | live-снимок из E в системный промпт (killer-фича) | 2026-06-16 | | Контекст-инъекция | только `quality=valid`, гейт по состоянию машины; `vehicle_read` через прокси | 2026-06-16 |
| Память о водителе | локальные `.md` в приватном хранилище; не в облако без согласия | 2026-06-16 | | Память о водителе | локальные `.md` в приватном хранилище; не в облако без согласия | 2026-06-16 |
| Локальные интенты | латентно-критичные — без LLM | 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 |
+1 -1
View File
@@ -34,7 +34,7 @@
### 3. Offline-first ### 3. Offline-first
- **Почему:** салон часто без сети (тоннели, глушь); ассистент не должен умолкать, навигация — теряться. - **Почему:** салон часто без сети (тоннели, глушь); ассистент не должен умолкать, навигация — теряться.
- **Как держим:** STT/TTS локальны; у ассистента офлайн-LLM фолбэк; карты офлайн; ключевые функции **деградируют, а не падают** без сети. - **Как держим:** STT/TTS локальны; у ассистента офлайн-LLM фолбэк; карты офлайн; ключевые функции **деградируют, а не падают** без сети. *(Фазировка: STT/TTS-офлайн + локальные интенты держат принцип с v1; офлайн-**диалог** LLM — v3, см. домен D §5.)*
### 4. Изоляция и graceful degradation ### 4. Изоляция и graceful degradation
- **Почему:** устойчивость; падение одного компонента не должно ронять систему. - **Почему:** устойчивость; падение одного компонента не должно ронять систему.
+1 -1
View File
@@ -43,7 +43,7 @@
| **OS base** | Armbian/Debian (RK3588), ядро ближе к mainline | read-only rootfs + overlay | | **OS base** | Armbian/Debian (RK3588), ядро ближе к mainline | read-only rootfs + overlay |
| **Init / lifecycle** | **systemd** | ядро; апы/плагины — App-Host | | **Init / lifecycle** | **systemd** | ядро; апы/плагины — App-Host |
| **CAN/OBD (read-only)** | **SocketCAN** (крейт `socketcan`) | ELM327 на старте; `python-OBD` — только в симуляторе | | **CAN/OBD (read-only)** | **SocketCAN** (крейт `socketcan`) | ELM327 на старте; `python-OBD` — только в симуляторе |
| **Wake-word** | openWakeWord / Porcupine | кастомная RU-фраза | | **Wake-word** | **openWakeWord** | RU-фраза «Штурман»; Porcupine отвергнут (проприетарный, #12) |
| **VAD** | Silero VAD | через ONNX Runtime | | **VAD** | Silero VAD | через ONNX Runtime |
| **STT** | Vosk · Silero | офлайн, RU; через биндинги / `ort` | | **STT** | Vosk · Silero | офлайн, RU; через биндинги / `ort` |
| **TTS** | Silero | офлайн, RU | | **TTS** | Silero | офлайн, RU |