Jabobo Backend — FastAPI · GitNexus 索引分析
274 符号 · 605 关系 · 20 执行流
%%{init: {'theme': 'dark', 'themeVariables': { 'primaryColor': '#1e293b', 'primaryTextColor': '#f1f5f9', 'primaryBorderColor': '#475569', 'lineColor': '#38bdf8', 'secondaryColor': '#334155', 'tertiaryColor': '#0f172a'}}}%%
graph TB
subgraph 前端层["前端 Dashboard"]
UI[Web 管理界面]
end
subgraph API层["jabobo-backend REST API (FastAPI, port 8007)"]
TrainAPI["POST /api/user/wake-word/train
trigger_wake_word_training()"]
StatusAPI["GET /api/user/wake-word/train/status
get_wake_word_status()"]
SyncAPI["POST /api/user/sync-config
sync_config()"]
GetConfig["GET /api/user/config
get_user_config()"]
end
subgraph 核心业务层["核心业务流程"]
direction TB
NORMALIZE["_normalize_wake_word()
标准化唤醒词名"]
DISPLAY["_wake_word_to_display()
生成显示名"]
TRAIN["_run_wakeword_training()
异步训练任务"]
UPDATE_STATUS["_update_wake_word_status()
更新数据库状态"]
end
subgraph 外部系统["外部系统"]
CMD1["train_wakeword.py
--generate-samples
(TTS 生成 500 条样本)"]
CMD2["train_wakeword.py
(特征提取+训练)"]
CMD3["deploy_wakeword.py
(OTA 部署 .tflite)"]
end
subgraph 数据层["MySQL 数据库"]
DB[("user_personas 表")]
end
subgraph 固件层["ESP32 设备"]
DEVICE["捷宝宝硬件"]
OTA_RECEIVER["OTA 接收+模型加载"]
end
UI -->|"POST /user/wake-word/train"| TrainAPI
UI -->|"GET /user/wake-word/train/status"| StatusAPI
UI -->|"POST /user/sync-config"| SyncAPI
UI -->|"GET /user/config"| GetConfig
TrainAPI -->|"asyncio.create_task"| TRAIN
SyncAPI -->|"if wake_word_text changed"| TRAIN
GetConfig -.->|"read model_status"| DB
StatusAPI -->|"查询状态"| DB
TRAIN --> NORMALIZE
NORMALIZE --> DISPLAY
TRAIN --> UPDATE_STATUS
TRAIN -->|"检查已有模型"| CMD2
TRAIN -->|"生成样本"| CMD1
CMD1 --> CMD2
CMD2 --> CMD3
CMD3 -->|"OTA 推送"| OTA_RECEIVER
OTA_RECEIVER --> DEVICE
UPDATE_STATUS -->|"UPDATE model_status"| DB
style 前端层 fill:#0f766e,stroke:#14b8a6,color:#fff
style API层 fill:#1d4ed8,stroke:#3b82f6,color:#fff
style 核心业务层 fill:#15803d,stroke:#22c55e,color:#fff
style 外部系统 fill:#b45309,stroke:#f59e0b,color:#fff
style 数据层 fill:#7c3aed,stroke:#a855f7,color:#fff
style 固件层 fill:#b91c1c,stroke:#ef4444,color:#fff
%%{init: {'theme': 'dark', 'themeVariables': { 'primaryColor': '#1e293b', 'primaryTextColor': '#f1f5f9', 'primaryBorderColor': '#475569', 'lineColor': '#38bdf8', 'secondaryColor': '#334155', 'tertiaryColor': '#0f172a'}}}%%
graph LR
subgraph API入口["API 入口"]
T["trigger_wake_word_training()
POST /user/wake-word/train"]
S["sync_config()
POST /user/sync-config"]
ST["get_wake_word_status()
GET /user/wake-word/train/status"]
GC["get_user_config()
GET /user/config"]
end
subgraph 辅助函数["辅助函数"]
NW["_normalize_wake_word()"]
WD["_wake_word_to_display()"]
UV["verify_user()
用户认证"]
GVC["get_valid_cursor()
数据库游标"]
end
subgraph 核心训练["核心训练"]
RT["_run_wakeword_training()"]
US["_update_wake_word_status()"]
end
subgraph 数据库["MySQL"]
CONN["db.connect()"]
QUERY["SELECT / UPDATE
user_personas"]
end
subgraph 外部进程["外部进程"]
CMD1["train_wakeword.py
--generate-samples"]
CMD2["train_wakeword.py
训练"]
CMD3["deploy_wakeword.py
OTA"]
end
T -->|"asyncio.create_task"| RT
S -.->|"if wake_word_text"| NW
NW --> RT
ST --> UV --> CONN --> QUERY
GC --> UV
GC --> GVC --> CONN
RT --> WD
RT --> CMD1 --> CMD2 --> CMD3
RT --> US --> CONN -->|"UPDATE model_status"| QUERY
style API入口 fill:#1e3a5f,stroke:#38bdf8,color:#fff
style 辅助函数 fill:#1e3a5f,stroke:#38bdf8,color:#fff
style 核心训练 fill:#1e3a5f,stroke:#22c55e,color:#fff
style 数据库 fill:#4a1a7a,stroke:#a855f7,color:#fff
style 外部进程 fill:#5c3d0a,stroke:#f59e0b,color:#fff
%%{init: {'theme': 'dark', 'themeVariables': { 'primaryColor': '#1e293b', 'primaryTextColor': '#f1f5f9', 'primaryBorderColor': '#475569', 'lineColor': '#38bdf8', 'secondaryColor': '#334155', 'tertiaryColor': '#0f172a'}}}%%
sequenceDiagram
participant U as 用户/前端
participant API as jabobo-backend
participant MEM as 进程缓存
_wake_word_tasks
participant DB as MySQL
participant CONDA as Conda wakeword
TTS + 训练脚本
participant DEV as ESP32 设备
U->>API: POST /user/wake-word/train
{device_id, wake_word: "Hey Jabra"}
Note over API: 标准化: hey_jabra
显示名: Hey Jabra
API->>MEM: {status: "running"}
API-->>U: 200 任务已启动
API-->>API: asyncio.create_task()
rect rgb(30, 58, 95)
Note over API,CONDA: 阶段1: TTS 生成 500 条样本
alt 已有 .tflite
API-->>API: 跳过训练,直接部署
else 首次训练
API->>CONDA: train_wakeword.py --generate-samples
--text "Hey Jabra" --max-samples 500
--voices lessac,amy,joe --length-scales 0.5~1.0
Note over CONDA: 多种音色 × 5 种语速
= 数千条 WAV 样本
CONDA-->>API: 样本生成完成
end
end
rect rgb(21, 128, 61)
Note over API,CONDA: 阶段2: 模型训练
API->>CONDA: train_wakeword.py
Note over CONDA: MFCC 特征提取 → CNN 训练 → TFLite 量化导出
CONDA-->>API: trained_models/hey_jabra/hey_jabra.tflite
end
rect rgb(180, 83, 9)
Note over API,DEV: 阶段3: OTA 部署
API->>CONDA: deploy_wakeword.py
CONDA-->>DEV: OTA 推送 .tflite 模型
DEV-->>CONDA: 部署确认
end
rect rgb(124, 58, 237)
Note over API,DB: 阶段4: 数据库持久化
API->>DB: UPDATE user_personas SET wake_word_model_status=1
API->>MEM: {status: "done", elapsed: 187s}
end
U->>API: GET /user/wake-word/train/status
API->>MEM: 读取状态
API-->>U: {status: "done", elapsed_seconds: 187}
%%{init: {'theme': 'dark', 'themeVariables': { 'primaryColor': '#1e293b', 'primaryTextColor': '#f1f5f9', 'primaryBorderColor': '#475569', 'lineColor': '#38bdf8', 'secondaryColor': '#334155', 'tertiaryColor': '#0f172a'}}}%%
stateDiagram-v2
[*] --> 未训练 : 设备绑定
status = 0
未训练 --> 训练中 : POST /user/wake-word/train
训练中 --> 已部署 : 训练+部署成功
status = 1
训练中 --> 训练失败 : 异常退出
status = 2
已部署 --> 训练中 : 重新训练
训练失败 --> 训练中 : 重试
%%{init: {'theme': 'dark', 'themeVariables': { 'primaryColor': '#1e293b', 'primaryTextColor': '#f1f5f9', 'primaryBorderColor': '#475569', 'lineColor': '#38bdf8', 'secondaryColor': '#334155', 'tertiaryColor': '#0f172a'}}}%%
erDiagram
user_login ||--o{ user_personas : has
user_login {
int id PK
string username UK
}
user_personas {
int id PK
string username FK
string jabobo_id UK
json personas
text memory
string websocket_url
string asr_provider
string tts_provider
string llm_provider
boolean rag_enabled
string wake_word_text "用户设定的唤醒词"
int wake_word_model_status "0=未训练 1=已部署 2=失败"
string current_version
string expected_version
json voiceprint_list
}
| 参数 | 值 | 说明 |
|---|---|---|
| _TRAIN_WORK_DIR | ~/Jabobo/wakeword train/ | 训练工作目录 |
| _TRAIN_CONDA_ENV | wakeword | Conda 训练环境 |
| _TRAIN_LOG_DIR | {WORK_DIR}/_training_logs/ | 每轮训练日志 |
| max-samples | 500 | 每轮 TTS 生成样本数 |
| length-scales | 0.5 0.6 0.75 0.9 1.0 | 5 种语速增强鲁棒性 |
| EN_VOICES | lessac, amy, joe, alan, norman, libritts_r | 英文 TTS 音色(6 种) |
| ZH_VOICES | chaowen, huayan, xiao_ya | 中文 TTS 音色(3 种) |
| wake_word_text | 用户自定义 | 存入 user_personas 表 |
| model_status 0 | 未训练 | 设备绑定后默认状态 |
| model_status 1 | 已部署 ✓ | 训练+部署成功 |
| model_status 2 | 失败 ✗ | 训练异常退出 |
| 函数名 | 文件 | 行号 | 职责 |
|---|---|---|---|
| trigger_wake_word_training | jabobo_config.py | — | API 入口,异步启动训练 |
| get_wake_word_status | jabobo_config.py | — | API 查询训练状态 |
| sync_config | jabobo_config.py | 420 | 同步配置时触发训练 |
| get_user_config | jabobo_config.py | 274 | 读取设备配置含模型状态 |
| _run_wakeword_training | jabobo_config.py | 55 | 🔥 核心异步训练任务 |
| _normalize_wake_word | jabobo_config.py | 22 | 标准化唤醒词为 code name |
| _wake_word_to_display | jabobo_config.py | 49 | 生成前端显示文本 |
| _update_wake_word_status | jabobo_config.py | 175 | 写 model_status 到 DB |
| verify_user | security.py | — | API 认证校验 |
| get_valid_cursor | security.py | — | 获取数据库游标 |