第一部分:产品需求规格说明书(PRD):情境感知智能英语学习系统
1. 项目概述
1.1 项目背景
现有英语学习产品多为离散的“题库式”或“课本式”教学,缺乏语境与应用结合。本产品通过引入边缘计算与大模型技术,实现基于物理环境感知的沉浸式英语教学,解决“学而不用”的问题。
1.2 产品目标
- 实现“场景感知 -> 意图分析 -> 智能对话 -> 学习反馈”的闭环系统。
- 打造具备 IoT 基础控制、多功能集成与高隐私保护的学习终端。
- 构建云端 AI 教学矩阵,支持自适应难度调整与个性化学习轨迹。
2. 终端设备详细需求
2.1 交互状态机(FSM)
- 待机(Idle):实时进行 VAD 检测,低功耗运行。
- 对话模式(Dialogue):处理全双工语音流,支持打断(Barge-in)。
- 翻译模式(Translate):短时录音,云端或本地模型进行翻译并播放。
- 教学模式(Study):加载预设课程内容,支持点读、跟读、评分。
- 休眠(Sleep):仅保留心跳检测与预设唤醒词监听,禁用麦克风阵列。
2.2 核心功能技术指标
- 语音采集:支持 3 米远场拾音,信噪比 (SNR) > 15dB,支持声源定位 (DoA)。
- 降噪/回声消除 (AEC):支持 50dB 以上回声消除,确保终端播放音频时仍可拾取用户语音。
- 本地翻译:支持离线中英互译(首字延迟 < 300ms)。
- 响应速度:从采集到服务端下发响应的 RTT (Round Trip Time) 需控制在 1.5s 以内。
2.3 IoT 与系统集成
- 电源管理:支持 RTC 定时,支持异常断电自启动,支持电量低时自动进入省电模式。
- 网络连接:自动切换 Wi-Fi/4G/5G,支持断网本地缓存(最大支持 100MB 语音片段缓存)。
- 安全性:物理麦克风静音开关(硬件级断开),端侧数据加密存储 (AES-128)。
3. 服务端平台详细需求
3.1 场景识别引擎 (AI Context Engine)
- 场景解析:通过 LLM 对环境音语义理解,分类器定义为:居家(卧室/厨房/书房)、校内、通勤、运动等。
- 对话生成:集成 LLM Agent,基于用户画像历史与当前场景,动态生成下一轮提问(支持多轮上下文管理)。
- 置信度机制:若场景识别置信度 < 0.7,触发通用问候策略,避免干扰用户。
3.2 教学管控系统
- 用户模型:记录用户的CEFR等级、词汇库、错误率,通过反馈循环动态调节难度。
- 数据存储:符合 GDPR/PIPL 隐私合规,敏感语音数据定期脱敏处理,不留存未经许可的原始音频。
4. 非功能性需求(SLA)
- 可用性:系统可用性 99.9%。
- 性能瓶颈:大模型推理接口支持至少 1000 QPS 并发。
- 安全合规:通过加密通道(TLS 1.3)通信;设备绑定具备唯一的安全芯片校验(Secure Element)。
- 可扩展性:支持通过插件机制增加新语种或场景模板。
5. 交互协议与数据流(API规范)
- 请求接口:/api/v1/scene/analyze (POST)
- Payload: {timestamp, audio_blob, device_id, context_hints}
- 响应接口:/api/v1/dialogue/stream (SSE)
- Payload: {dialogue_id, text, audio_url, intent_type, difficulty_level}
- 指令同步:通过 MQTT Broker 下发控制指令(如:SetVolume, Sleep, UpdateFirmware)。
6. 典型场景逻辑流程定义
- 场景识别:检测到特定环境音纹 -> 判定为“用餐场景” -> 发送指令至终端播放引导提示音。
- 回复处理:检测用户回复 -> ASR识别 -> 分析语法与正确性 -> 返回反馈(赞赏或纠错)-> 进入下一轮提问。
第二部分:参照以上需求,初步一套mqtt消息的topic规则如下:
一、消息topic定义: snlp/v1/{device_id}/{direct}/{msg_type}
表格如下:
方向 Topic 路径 msg_type 说明
rpt (终端->云) snlp/v1/{device_id}/rpt/state 状态上报 包含心跳、电量、网络、当前工作模式
snlp/v1/{device_id}/rpt/event 事件上报 唤醒/休眠触发、ASR识别结果、异常报警
snlp/v1/{device_id}/rpt/log 系统日志 用于远程排障的系统运行日志
snlp/v1/{device_id}/rpt/ack 指令响应 关键: 对应 CMD 的执行结果 (成功/失败/进度)
snlp/v1/{device_id}/rpt/progress 过程进度 针对“异步文件下载”和“OTA升级”
cmd (云->终端) snlp/v1/{device_id}/cmd/ctl 实时指令 实时指令(音量、休眠、NTP)
snlp/v1/{device_id}/cmd/task 异步任务指令 异步任务指令(OTA、下载资源包)
v1 : 表示版本号1, 用于版本过渡时的兼容性处理
device_id: 是设备的唯一id,只读,是不能被修改的 (以后再考虑增加注册机制,换取正式的 Access Token)
direct: rpt (终端->云) , cmd (云->终端)
msg_type: state, event,log,ack,progress, task, ctl
二、Payload 设计规范,为了保持一致性,所有 Payload 均采用 JSON 格式,并统一包含 Header 区块。
1. 服务端下发指令: snlp/v1/{device_id}/cmd/ctl
{
"header": {
"version": "1.0", // 协议语义版本
"device_id": "...", // 必须包含
"req_id": "UUID-...", // 仅 CMD 类指令必须
"timestamp": 1718365200, // 统一 Unix 时间戳
"ttl": 1001 // 命令有效期,单位为秒
},
"action": "ACTION_TYPE", // 如: PLAY_MEDIA, SYNC_CLOCK, SET_ALARM
"payload": { ... } // 具体操作参数
}
具体的payload示例:
播放音频 (play_media): {"url": "...", "md5": "...", "volume": 50, "loop": false}
定时闹钟 (set_alarm): {"id": "alarm_01", "time": "07:00", "repeat": [1,2,3,4,5], "action": "add"}
下载任务 (download_task): {"url": "...", "md5": "...", "target_path": "/data/unit1/lesson1.mp3"}
任务进度反馈 (task_progress): { "task_id": "UUID-...", "status": "downloading", "progress": 45, "info": "Downloading assets..." }
2. 终端上报: 终端上报数据 (Topic: rpt/state, rpt/event, rpt/ack)
{
"header": {
"device_id": "SN-88293021",
"timestamp": 1718365205
},
"msg_type": "state", // 或 event, ack
"payload": { ... }
}
具体的payload示例:
心跳/健康 (state): {"cpu": 15, "mem": 45, "battery": 80, "net": "wifi", "mode": "idle"}
指令响应 (ack - 必回): { "req_id": "UUID-xxxx-xxxx", "status": "success", "error_code": 0 }
事件上报 (event): {"event": "asr_result", "text": "Good morning", "confidence": 0.95}
三、 action 字段与具体执行动作的对应关系表
1. 指令映射字典
指令分类 功能描述 Action 字段 参数定义 (Payload 示例)
基础控制 设置设备音量 (0-100) SET_VOLUME {"level": 50}
切换电源状态 SET_POWER {"state": "sleep/wake"}
同步 NTP 时间戳 SET_CLOCK {"ts": 1718365200}
媒体管理 触发音频播放 PLAY_MEDIA {"path": "/data/unit1/lesson1.mp3", "volume": 50, "loop": false}
停止当前播放 STOP_MEDIA {}
文件管理 触发异步文件下载 DOWNLOAD {"url": "...", "md5": "...", "target_path": "/data/unit1/lesson1.mp3"}
删除指定路径文件 DELETE_FILE {"path": "/data/..."}
学习配置 下发/更新课程资源索引 UPDATE_MANIFEST {"course_id": "...", "assets": [{"url": "...", "md5": "...", "target_path": "/data/unit1/lesson1.mp3"}]}
管理开机闹钟 SET_ALARM {"id": "alarm_01", "time": "07:00", "repeat": [1,2,3,4,5], "action": "add"}
删除开机闹钟 UNSET_ALARM {"id": "alarm_01"}
系统维护 触发固件在线升级 OTA_UPDATE {"url": "...", "version": "1.0.2"}
延时重启设备 REBOOT {"delay": 5}
2. 详细消息流逻辑表
设备开机/定时 心跳/状态 rpt/state 不需要
执行命令 指令响应 rpt/ack 必须 (即时反馈)
场景切换/ASR 事件上报 rpt/event 不需要
服务端操作 控制指令 cmd/operate 设备处理后必须回 rpt/ack
四、关于任务列表与文件管理:
下载任务: 下发任务时,Payload 应包含文件 URL 和 MD5。终端下载过程中通过 rpt/ack 定时上报 progress (0-100)。
任务列表: 服务端可以下发一个“课程资源清单”的 JSON 数组,终端收到后存入本地文件,实现资源的管理与对比。
五、 协议实施建议(Checklist)
1. QoS 设置策略:
cmd/ctl (实时控制): QoS 1 (至少送达一次)。
rpt/state (心跳): QoS 0 (不保证送达,减轻并发压力)。
rpt/ack (指令响应): QoS 1 (必须确保云端知晓任务完成情况)。
2. 安全性补充:
在 MQTT 连接阶段采用 TLS 双向认证 (MQTTS)。
Payload 内部虽然有 timestamp,但在 Broker 层建议开启 MQTT Client ID 唯一性校验,防止多设备抢占 ID。
3. 扩展性预留:
在 cmd/operate 的 Action 字段设计中,预留 USER_DEFINED_xxx 接口,以便后续快速增加如“远程调试”、“日志拉取”等功能,无需修改原有解析架构。
第三部分:视频文件目录结构和组成
1. 所有课程的根目录在:./data/courses, ./data/coursesindex.json文件内容是已确定发布的课程,结构如:
{
"courses": [
{
"course_id": "course01",
"config": "courses/course01/course.json"
},
{
"course_id": "course02",
"config": "courses/course02/course.json"
}
]
}
2. 每个课程目录下有一份course.json,该文件内容是当前目录所对应的课程的视频文件清单,结构如:
{
"course_id": "course01",
"course_name": "小智学习课程01",
"description": "课程01",
"chapters": [
{
"chapter_id": "chapter01",
"chapter_name": "第一章",
"videos": [
{
"id": "001",
"title": "课程001",
"path": "courses/course01/chapter01/resources/001.mp4"
},
{
"id": "002",
"title": "课程002",
"path": "courses/course01/chapter01/resources/002.mp4"
},
{
"id": "003",
"title": "课程003",
"path": "courses/course01/chapter01/resources/003.mp4"
}
]
}
]
}
#TODO:
根据以上需求描述和mqtt规范定义,请完成以下工作:
1. 完成server端的基础功能开发;
2. 定义一套基础课程,存放在本地目录(后期可以人工根据这个基础的版本进行课程增设);
3. 定义tasklist.json(制定一套基础的任务清单,用于下发到终端设备进行初始化),保证客户端可以自动初始化的功能;
4. 定义courseTask.json(制定某一门课程任务清单,用于下发到终端设备进行课程初始化),使客户端和服务端可以有一套自动机制,
客户端和服务端可以自动化的配合完成常规的功能和正常的工作流程;
以下是不改代码情况下的**手动更新课程**操作说明,分三种方式。
---
## 方式一:HTTP API 触发(推荐,最简单)
服务端会通过 MQTT 自动向终端下发 `SYNC_COURSE_TASK`。
### 1. 更新单门课程
```bash
curl -X POST http://你的服务器:9500/api/v1/devices/course-init \
-H "Content-Type: application/json" \
-d '{
"device_id": "SN-88293021",
"course_id": "course01"
}'
```
终端会收到 **1 条** `cmd/task` 消息,内容为 `course01` 的完整 `courseTask.json`(含 `assets` 列表)。
### 2. 重新同步全部基础课程(index.json 中所有课)
```bash
# 第一步:清除该设备的初始化记录
curl -X DELETE http://你的服务器:9500/api/v1/devices/initialized/SN-88293021
# 第二步:强制重新初始化(含全部基础课程同步)
curl -X POST http://你的服务器:9500/api/v1/devices/init \
-H "Content-Type: application/json" \
-d '{
"device_id": "SN-88293021",
"force": true
}'
```
会对 `index.json` 里每门课各下发 **1 条** `SYNC_COURSE_TASK`(当前为 course01、course02 共 2 条)。
---
## 方式二:直接发 MQTT 消息
用 MQTT 客户端(MQTTX、mosquitto_pub 等)连接 Broker,向终端发布消息。
**Topic:**
```
snlp/v1/{device_id}/cmd/task
```
**示例(course01):**
```json
{
"header": {
"version": "1.0",
"device_id": "SN-88293021",
"req_id": "手动填写-uuid",
"timestamp": 1718365200,
"ttl": 7200
},
"action": "SYNC_COURSE_TASK",
"payload": {
"version": "1.0",
"course_id": "course01",
"course_name": "小智学习课程01",
"description": "课程01终端初始化任务:下载章节视频并更新本地资源清单",
"assets": [
{
"url": "http://你的服务器:9500/static/courses/course01/chapter01/resources/001.mp3",
"md5": "...",
"title": "第一章 课程001",
"target_path": "courses/course01/chapter01/resources/001.mp3",
"cover_url": "http://你的服务器:9500/static/courses/course01/chapter01/resources/001.png",
"cover_target_path": "courses/course01/chapter01/resources/001.png"
}
]
}
}
```
**注意:**
- `url` / `cover_url` 必须是终端能访问的**完整 HTTP 地址**(不能用 `localhost`)
- `target_path` / `cover_target_path` 保持相对路径,终端按此路径存到存储卡
- 可先调 API 拿到完整 payload,再原样发到 MQTT:
```bash
# 获取已补全 URL 的 courseTask 内容
curl http://你的服务器:9500/api/v1/courses/course01/manifest
# 获取全部基础课程
curl http://你的服务器:9500/api/v1/courses/base-manifest
```
把返回的 JSON 作为 MQTT 消息的 `payload` 字段即可。
**mosquitto_pub 示例:**
```bash
mosquitto_pub -h 你的MQTT地址 -p 2100 \
-u 用户名 -P 密码 \
-t "snlp/v1/SN-88293021/cmd/task" \
-m '{"header":{...},"action":"SYNC_COURSE_TASK","payload":{...}}'
```
---
## 方式三:终端直接通过 URL 拉取(不经过 MQTT 触发)
若终端支持自行拉取,可直接访问服务端静态资源:
| 资源 | URL |
|------|-----|
| 课程任务包 | `http://服务器:9500/static/courses/course01/courseTask.json` |
| 课程配置 | `http://服务器:9500/static/courses/course01/course.json` |
| 课程索引 | `http://服务器:9500/static/courses/index.json` |
| 音频文件 | `http://服务器:9500/static/courses/course01/chapter01/resources/001.mp3` |
| 封面图 | `http://服务器:9500/static/courses/course01/chapter01/resources/001.png` |
**手动更新流程:**
1. 在服务端更新 `data/courses/` 下的 `courseTask.json` 和资源文件
2. 终端下载最新的 `courseTask.json`
3. 按 `assets` 里每一项的 `url` 下载文件,保存到 `target_path` 对应路径
本地 `courseTask.json` 里是相对路径,终端需自行拼成:
```
下载地址 = http://服务器:9500/static/ + assets[].url
保存路径 = 存储卡根目录 + assets[].target_path
```
---
## 方式对比
| 方式 | 适用场景 | 是否需要 MQTT |
|------|----------|---------------|
| `POST /devices/course-init` | 更新单门课 | 是(服务端代发) |
| `POST /devices/init?force=true` | 全量重同步 | 是 |
| 直接发 MQTT `SYNC_COURSE_TASK` | 调试、第三方系统对接 | 是 |
| 终端直接 HTTP 下载 | 终端自带拉取逻辑 | 否 |
---
## 更新服务端课程资源后的建议步骤
1. 修改 `data/courses/course01/courseTask.json`(及音频、封面文件)
2. 确认浏览器能访问:
`http://服务器:9500/static/courses/course01/courseTask.json`
3. 选手动触发方式:
- 只更新一门课 → `course-init`
- 更新全部基础课 → `force: true` 重新 init,或逐门课调 `course-init`
4. 终端执行完后通过 `rpt/ack` / `rpt/progress` 上报结果(若终端已实现)