secure-virtual-assistant / spec_ModuleB_web_app.md
thales1020's picture
Deploy Module B web app
b617af0 verified
|
Raw History Blame Contribute Delete
9.29 kB
# Spec — Module B: Web App Trợ lý ảo Xe thông minh
**Người phụ trách:** Hiếu | **Đề 3 — Secure Virtual Assistant with Speaker Recognition**
**Nhận bàn giao từ:** Module A (Khoa) — `model/moduleA_artifacts_final/`
---
## 1. Phạm vi & ranh giới với Module A
Module B **kế thừa nguyên** `speaker_model.py` và `checkpoints/best.pt` từ Module A, không sửa code hay threshold mặc định (`DEFAULT_THRESHOLD_SV = 0.35`, `DEFAULT_THRESHOLD_SID = 0.20`). Việc của Module B:
- Xây web app hoàn chỉnh: ghi âm → ASR → orchestrator → 3 usecase (general/SV/SID) → TTS.
- Component enrollment & quản lý thành viên trong xe (thu giọng, lưu DB).
- `check_audio_quality()` cho luồng enrollment (Module A chỉ định hướng, không code).
- Convert audio `.webm` (MediaRecorder trình duyệt) → `.wav` 16kHz mono trước khi gọi `get_embedding()` — **trách nhiệm của Module B**, dùng `pydub`/`ffmpeg`.
- Đảm bảo logic gating SV/SID nằm cứng trong code Python (mục 3), không giao cho LLM quyết định.
## 2. Tech stack đã chốt
| Thành phần | Lựa chọn | Ghi chú |
|---|---|---|
| Backend | FastAPI (Python) | Chạy trực tiếp `speaker_model.py` (torch/speechbrain) trong cùng process, load model 1 lần lúc startup |
| Frontend | React (Vite) hoặc HTML/JS thuần | Gộp chung 1 Space với backend — FastAPI serve luôn static build, tránh CORS/multi-deploy |
| ASR | PhoWhisper (`transformers`) | Chạy CPU trong backend, đủ cho câu ngắn 2-6 giây |
| Orchestrator | Gemini API (key đã có trong `.env.txt`) | Ép **structured/JSON output** `{intent, entities}` — Gemini chỉ classify, KHÔNG tự quyết định có cần SV hay không |
| TTS | gTTS | Free, không cần key riêng |
| Speaker model | `speaker_model.py` (Module A) | Không đổi interface/threshold |
| Database | SQLite (SQLAlchemy) | Local trong container HF Space — **chấp nhận reset khi Space sleep/restart**, đủ cho demo, không cần vector DB (identify() đã dot-product thuần Python) |
| Nhạc (SID) | Deezer API | Free, không cần key cho search; **giới hạn: chỉ preview 30s/bài**, chấp nhận cho demo |
| Thời tiết/định vị (General) | OpenWeatherMap | Free tier; vị trí dùng **toạ độ cố định demo** (không có GPS xe thật) |
| Deploy | Hugging Face Spaces (Docker SDK, free CPU tier) | ~16GB RAM free, đủ chạy đồng thời PhoWhisper + speechbrain |
**Rủi ro đã biết, chấp nhận:** DB mất dữ liệu khi HF Space sleep (free tier không có persistent disk) → cần re-enroll sau mỗi lần Space ngủ dậy nếu demo cách nhau lâu. Nếu cần bền hơn, cân nhắc chuyển sang Supabase (free Postgres) sau — không bắt buộc cho demo báo cáo.
## 3. Kiến trúc pipeline tổng thể
```
[Browser: ghi âm .webm]
│
▼
convert .webm → .wav 16kHz mono (pydub/ffmpeg)
│
▼
ASR: PhoWhisper → text
│
▼
Orchestrator: Gemini API (JSON mode)
input: text output: { "intent": "...", "entities": {...} }
│
▼
[GATE CỨNG - code Python, KHÔNG phải LLM]
bảng mapping intent → usecase → có cần SV không
│
┌────┼─────────────┬─────────────┐
▼ ▼ ▼
General SV SID
(không auth) (verify() trước) (identify())
│ │ │
▼ ▼ ▼
Action Handler tương ứng (mục 4)
│
▼
TTS: gTTS → audio response → trả về Browser
```
## 4. Orchestrator (Gemini) — hợp đồng I/O
- **Input:** text đã ASR (tiếng Việt).
- **Output bắt buộc dạng JSON có schema cố định**, ví dụ:
```json
{ "intent": "sv_unlock_door" | "sv_start_engine" | "sid_play_playlist" | "general_weather" | "general_location" | "unknown", "entities": {} }
```
- Gemini **chỉ làm nhiệm vụ hiểu intent/entity**, không quyết định pass/fail xác thực.
- Code Python có 1 bảng mapping tĩnh (hardcode), ví dụ:
| intent | usecase | cần SV? |
|---|---|---|
| `general_weather`, `general_location` | General | Không |
| `sv_unlock_door`, `sv_start_engine` | SV | **Có** |
| `sid_play_playlist` | SID | Không (nhưng cần `identify()`) |
Bảng này quyết định route, **không phải Gemini quyết định**.
## 5. Ba usecase — chi tiết
### 5.1 General — thời tiết / nhiệt độ / định vị
- Không cần xác thực giọng nói.
- Gọi OpenWeatherMap current weather API với toạ độ cố định demo (vd TP.HCM).
- Trả lời qua TTS: "Hiện tại trời [mô tả], nhiệt độ [x]°C."
- Định vị: trả toạ độ/tên địa điểm demo cố định (do không có GPS xe thật) — cần nêu rõ giới hạn này trong báo cáo.
### 5.2 SV — mở khoá cửa / khởi động xe
1. Ghi âm runtime → `get_embedding()`.
2. So khớp với embedding đã enroll của user hiện tại (hoặc quét toàn bộ DB nếu chưa xác định ai đang nói — cần chốt thêm ở bước code: có yêu cầu chọn "tôi là ai" trước hay auto quét).
3. `verify(emb_enrolled, emb_runtime)` — threshold mặc định 0.35.
4. Nếu `True`: thực hiện action (mock: đổi trạng thái "đã mở khoang"/"đã khởi động"), ghi vào bảng `action_log` (timestamp, user_id, action, verified=True).
5. Nếu `False`: cho phép **retry 1-2 lần** trước khi báo "không xác thực được" (theo khuyến nghị README Module A, vì FRR thực nghiệm ~37.5%) — không chặn ngay từ lần đầu.
6. Log cả các lần verify thất bại (verified=False) để phục vụ báo cáo/demo.
### 5.3 SID — phát playlist cá nhân hoá
1. Ghi âm runtime → `get_embedding()`.
2. `identify(emb, db_embeddings)` — threshold mặc định 0.20 — trả về `user_id` hoặc `None`.
3. Nếu có `user_id`: lấy danh sách bài hát yêu thích của user từ bảng `preferences` → gọi Deezer API lấy preview URL từng bài → trả về frontend hiển thị playlist thật + phát qua `<audio>`.
4. Nếu `None`: phản hồi không nhận diện được, có thể fallback playlist mặc định hoặc yêu cầu enroll.
## 6. Enrollment flow
1. User nhập thông tin cơ bản (tên) + chọn bài hát yêu thích ban đầu (lưu vào `preferences`).
2. Đọc lần lượt 7 câu mẫu trong `enrollment_sentences.md` (nguyên văn, không đổi).
3. Với mỗi câu ghi âm:
- Convert `.webm` → `.wav` 16kHz mono.
- **Check chất lượng:** chạy qua PhoWhisper ASR, so khớp WER với câu mẫu gốc; kiểm tra độ dài audio tối thiểu (khuyến nghị ≥ vài giây, khớp câu mẫu ước tính 4-6s). Nếu WER quá cao hoặc audio quá ngắn/nhiễu → yêu cầu đọc lại câu đó.
4. Sau khi đủ 7 câu đạt chất lượng: tính `get_embedding()` cho từng câu, **gộp thành 1 embedding đại diện** (trung bình rồi L2-normalize lại, hoặc lưu cả 7 và dùng câu tốt nhất — quyết định lúc code, ghi rõ vào báo cáo).
5. Lưu embedding + metadata vào bảng `users`.
## 7. Database — schema đề xuất
```
users
user_id (PK)
name
embedding -- JSON array 192-dim, đã L2-normalize
enrollment_date
preferences
user_id (FK -> users)
favorite_tracks -- JSON list track_id/tên bài (khớp Deezer)
action_log
id (PK)
timestamp
user_id (nullable nếu chưa xác định được ai)
action -- "unlock_door" | "start_engine" | "play_playlist" | ...
verified -- bool, kết quả verify()/identify()
score -- điểm cosine similarity thực tế (để debug/báo cáo)
```
## 8. Việc còn mở / cần xác nhận thêm
- **Cách xác định "ai đang nói" trước khi gọi `verify()`:** chọn user từ UI trước, hay auto quét toàn bộ DB bằng `identify()` trước rồi mới `verify()` với đúng người đó? Ảnh hưởng UX, cần chốt lúc code.
- **Cách gộp 7 embedding enrollment thành 1** (trung bình vs giữ nhiều mẫu) — chưa chốt, ảnh hưởng độ chính xác `verify()`/`identify()`.
- DB reset khi HF Space sleep — chấp nhận cho demo, nêu rõ trong báo cáo là giới hạn hạ tầng free tier, không phải lỗi thiết kế.
- Deezer preview 30s — nêu rõ trong báo cáo là giới hạn nguồn nhạc free, không phải giới hạn hệ thống SID.
- Chưa quét lại threshold SID (0.20) ở nhiều mức khác — Module A đã note trong README, có thể thử nếu còn thời gian bằng `evaluate_identification.py --threshold <giá_trị>`.
## 9. Tài liệu liên quan
- `model/moduleA_artifacts_final/README_ban_giao.md` — interface, threshold, giới hạn model.
- `model/moduleA_artifacts_final/enrollment_sentences.md` — 7 câu mẫu, dùng nguyên văn.
- `Secure-Virtual-Assistant-with-Speaker-Recognition.pdf` — yêu cầu đề bài gốc.