Files
shturman/docs/domains/d-assistant.md
T
kk0t9 02f13db1f3 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>
2026-06-21 01:25:11 +03:00

166 lines
14 KiB
Markdown
Raw 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, мгновенно.
- **Свободный диалог / объяснения** — 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; место (WirePlumber/
filter-chain vs внутри ассистента). → этот домен + architecture (audio plane).
- ◻️ **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 |