# M2.1 PRD — 角色切换 + 语音互动 V1

## 0. 文档信息

- 里程碑：M2.1（Phase 2 首个功能里程碑）
- 版本：v1.0（草案，待 CEO/老板评审）
- 作者：张婉琳（产品总监）
- 日期：2026-07-03
- 需求源头（老板原话，最高优先级）：
  > "先用本地跑，同时预留云端接口。先做一个可用手机遥控切换宠物模式以及数字人或者数字二次元歌姬之类的功能，同时通过手机app录音和播放实现设备响应，可以的话本地运行0.5B或者2B的gemma4"
  >
  > 补充一（2026-07-03）："要支持切换模型和模型下载更改，因为以后设备会变，只不过测试设备是4G的这款全志A733运行linux"
  >
  > 补充二（2026-07-03）："clients里的安卓app别忘了改，还有设备的网页控制端"
  >
  > 补充三（2026-07-03，关键事实）："现在设备的扬声器麦克风没插入，尽量让对话和播放声音从网页和手机app实现"
- 关联文档：`clients/flutter/PRD.md`（App 现状）、`docs/M1.2_TEST_PLAN.md`、`configs/dog_state_machine.json`（内容包现状）
- 性能基线依赖：设备端小模型 spike（张明远，llama.cpp + whisper.cpp + piper 实测）进行中，**本文所有延迟/资源数字凡标注"待 spike 校准"者均以 spike 报告为准，评审时统一回填**

---

## 1. 产品定位

数字生命窗口从"单一宠物循环播放"升级为 **"可切换角色 + 语音互动"的数字生命 V1**。

- **现状**：设备开机播放单一宠物（狗/猫）的预渲染视频状态机，手机 App 只能做播放控制、状态机触发、配网与配置管理。角色是"布景"，不是"生命"。
- **升级后**：
  1. 手机一键切换角色——宠物（狗/猫）、数字人、二次元歌姬，角色即内容包，随时更换"住在窗口里的生命"；
  2. 对着手机说话，角色"听懂并回答"——客户端是话筒和喇叭，设备是大脑和脸：设备本地完成 ASR → LLM（0.5B~2B 小模型，人设跟随角色）→ TTS 计算，**所有声音一律从手机 App / 网页端播出，设备本地不出声**（设备音频硬件未接入——老板补充三确认的事实），回合期间画面同步切换"说话"状态；
  3. 模型即耗材、可管理——模型不烧死在系统里，手机可查看/下载/切换/删除设备上的推理模型，硬件升级换更大模型不换系统。
- **架构原则（老板明确要求）**：**先本地跑通，云端接口预留**。V1 全链路本地推理、不依赖公网；LLM 后端做成可配置（local/cloud），V1 只实现 local。
- **平台叙事**：角色内容包 = 状态机 JSON + 视频素材 + 角色人设，是本平台第一个"标准化内容单元"，为 Phase 2 内容生态（第三方角色包分发）奠基。
- **交付面（老板补充二）**：安卓 App 与设备内嵌网页控制端**两个客户端同为 M2.1 交付对象**，功能覆盖差异见 §5 客户端矩阵。

---

## 2. 功能一：角色内容包切换

### 2.1 内容包定义

一个角色内容包由三部分组成：

| 组成 | 内容 | 现状 |
|------|------|------|
| 状态机 JSON | display / playlist / transition / playback / scenes.state_machine / remote_control（结构见 `configs/dog_state_machine.json`） | 已有（dog、cat 两包） |
| 视频素材组 | playlist 引用的一组预渲染 mp4 片段 | 狗/猫已有；数字人/歌姬待制作 |
| 角色元信息 + 人设（**M2.1 新增**） | 角色显示名、类型（pet/human/singer）、封面图、人设 prompt、回复风格参数、TTS 音色标识 | 无，需新增 |

**角色元信息落地方式（供架构师决策，产品倾向前者）**：
- 方案 A（推荐）：在状态机 JSON 内新增 `character` 顶层块。注意：配置结构体已全面 `deny_unknown_fields`（commit 5770b70），旧解析器会拒绝新字段，**需内核侧同步扩展 schema**，这是本功能的工程依赖。
- 方案 B：内容包目录 + 独立 `manifest.json` sidecar。改动隔离但引入"包=目录"的新概念，迁移成本更高。

**内容包可移植性要求（新包必须遵守）**：现有 dog 包 playlist 使用仓库外相对路径（`../../宠物嵌入式开发视频2/...`），不可分发。新制作的内容包素材必须收敛到设备上约定目录（建议 `videos/<pack_id>/`），路径自包含，为后续内容包分发做准备。存量 dog/cat 包 V1 不强制迁移。

### 2.2 角色类型与必备状态集

| 角色 | 状态机 | 素材 | 人设风格 |
|------|--------|------|---------|
| 狗（已有） | 已有，11 状态 | 已有 | 拟声/简短俏皮（"汪！"开头、短句、宠物口吻） |
| 猫（已有） | 已有 | 已有 | 拟声/简短俏皮（慵懒、傲娇） |
| 数字人（新） | 新建，见必备状态集 | **待制作** | 亲切助理型，自然口语 |
| 二次元歌姬（新） | 新建，见必备状态集 | **待制作** | 活泼元气，二次元语气词 |

**新角色必备状态集（V1 最小集，语音互动依赖）**：

| 状态 | 用途 | 素材要求 |
|------|------|---------|
| `idle` | 默认待机循环 | 可无缝循环（首尾帧一致），建议 5~15s |
| `talk` | 语音回合中"说话" | 口部/肢体有说话感，可无缝循环，建议 2~5s；V1 不要求口型对齐 |
| `greet` | 被切换到/被唤起时打招呼 | 一次性播放，建议 3~8s |
| `sleep` | 长时间无交互后休眠 | 可无缝循环，建议 5~15s |

存量宠物包无 `talk` 状态：V1 允许优雅降级（语音回合期间宠物包映射到既有互动状态如 `called_name`，或保持当前状态），不阻塞上线；后续补素材。

**素材统一规格**：
- 分辨率 1920×1080（与现有 render/output 配置一致），mp4 / H.264，25 或 30 fps
- 背景：纯黑或深色背景优先（全息显示依赖 brightness_adjust 背景抑制；如用 chroma key 需符合现有 hsv 配置能力）
- 角色形象跨片段严格一致（同一人物/服装/机位/光照）
- 循环类素材首尾帧一致，切换类素材起止均回到中立姿态，保证 fade 转场（0.5s）自然

### 2.3 App 侧交互

- **角色页（新增 Tab 或首页入口，交互稿定稿时定）**：角色卡片列表（封面图 + 名称 + 类型标签），当前角色卡片高亮标记"使用中"。
- **切换**：点卡片 → 二次确认（可选，首版可直接切）→ 切换中 loading 态 → 成功后卡片高亮迁移 + 首页当前角色信息同步更新。
- **数据来源**：切换机制复用现有 `GET /api/config/available` + `POST /api/config/switch`（服务端已具备：文件名校验 → 配置验证 → 写入激活 → ConfigReloadRequest 热重载）。现有 available 接口只返回文件名列表，**需扩展携带角色元信息**（显示名/类型/封面），扩展形式由工程定（enrich 现接口或新增 `/api/characters`）。

### 2.4 行为与体验指标

| 项 | 要求 |
|----|------|
| 切换耗时 | 点卡片 → 设备呈现新角色首帧：目标 ≤ 3s，上限 5s（超时 App 显示"切换超时，请检查设备"） |
| 切换期间画面 | 不得黑屏挂死；允许短暂转场（fade/最后一帧定格） |
| 失败回退 | 目标配置验证失败 → 服务端拒绝（现有行为：400 + 激活配置不变），设备保持原角色，App 明确报错原因；热重载失败 → 依赖内核既有 AutoRollback 回滚到上一可用配置，App 通过 WS 状态感知并提示 |
| 状态一致性 | App 展示的"当前角色"以设备侧上报为准（available.active / WS 推送），不以本地点击乐观状态为准 |
| 切换副作用 | 切角色即切人设：对话上下文清空（见 3.5），语音回合若进行中则先完成或取消再切 |

---

## 3. 功能二：语音对话回合（V1 = 客户端是话筒和喇叭，设备是大脑和脸）

### 3.1 回合模型

一次"回合"= 用户说一段话，角色回一段话。V1 只做回合制，不做流式、不做打断。

```
App：按住说话录音 → 松开上传 → 等待（思考中动画） → 播放设备返回的回复音频
设备：接收音频 → ASR 转写 → 本地 LLM 生成回复（人设 prompt 跟随当前角色） → TTS 合成 → 返回音频（+转写文本+回复文本）
画面：回合应答期间角色切 "talk" 状态 → 结束回 "idle"
```

**声音归属铁律（老板补充三）**：设备音频硬件（扬声器/麦克风）当前未接入。TTS 合成的回复音频**一律返回给发起请求的客户端播放**（App 或 Web，见 §5），设备本地不播放、不依赖任何音频输出/输入硬件。设备角色收敛为：**ASR / LLM / TTS 计算 + 人物画面状态联动**。

### 3.2 App 侧需求

- **对话页（新增）**：
  - 底部"按住说话"大按钮：按住录音（波形/计时反馈），松开发送，上滑取消；单次录音上限 30s（超限自动截断发送并提示）
  - 发送后"角色思考中"等待态，支持取消本回合
  - 收到回复自动播放；播放器走手机扬声器/耳机
  - **对话历史时间线**：按时间排列，我方气泡显示转写文本（可折叠原音频回放），角色气泡显示回复文本 + 可重播的回复音频；历史仅存手机本地（隐私设计，见 §6）
- **权限与异常**：麦克风权限引导；录音失败/上传失败/设备忙/超时均有明确可理解提示与重试入口
- **并发**：同一时刻只允许一个进行中回合；进行中时按钮置灰或排队提示

### 3.3 设备侧需求

- **新增语音回合 HTTP 接口**（具体 URL/编码由架构师定，行为要求如下）：
  - 输入：一段录音（建议 16kHz 单声道，格式工程定），附当前会话标识
  - 输出：转写文本 + 回复文本 + 回复音频（三者 App 时间线都需要）
  - 同一时刻只处理一个回合，忙时返回明确的"设备忙"错误（如 409）
  - 全程本地推理：ASR（whisper.cpp 系）→ LLM（llama.cpp 系，0.5B~2B 档小模型，老板点名 gemma 系列优先，**最终模型与档位以 spike 实测为准**）→ TTS（piper 系）
- **人设注入**：LLM system prompt = 当前角色内容包的人设 prompt + 风格参数（最大回复长度、语气约束）。宠物 = 拟声/简短俏皮；数字人 = 亲切自然；歌姬 = 活泼元气。**角色人设是内容包的一部分**，切角色即切人设。
- **TTS 音色**：内容包元信息含音色标识，不同角色音色可区分（受限于 piper 中文音色可用性，最低要求：至少可用一款中文音色全角色共用，音色差异化作为增强项，以 spike 结论为准）。
- **画面联动**：回合应答开始 → 状态机切 `talk`；回合结束 → 回 `idle`。因 V1 音频在客户端（App/Web）播放，"说话画面"与客户端放音的对齐采用近似策略（按回复音频时长估算保持 talk 态，或由客户端回报播放完成，二选一由架构师定），对齐误差 ≤ 1.5s 可接受。角色包无 `talk` 状态时优雅降级（见 2.2），不报错。
- **上下文**：会话内保留滑动窗口短上下文（建议最近 3~5 回合，以 spike 内存数据定），存内存即可；切角色/重启即清空。**不做持久化**（V1 边界）。

### 3.4 云端预留（老板明确要求，验收必查）

- LLM 后端抽象为可配置项：`llm.backend = "local" | "cloud"`（含 cloud 的 endpoint/key 等预留字段）
- **V1 只实现 local**；配置为 cloud 时行为 = 明确报"暂未支持"，不 panic 不静默
- 接口抽象层（trait/消息协议）在 V1 落地，保证 V2/V3 接云端时不动调用方
- ASR/TTS 是否同样抽象后端由架构师定，产品只强制 LLM 一层

### 3.5 回合与角色切换的互斥

- 回合进行中收到切换角色请求：拒绝并提示"角色正在回应，稍后再试"（或 App 侧先行禁用切换入口）
- 切换角色成功后：对话上下文清空，时间线插入系统分隔（"已切换到 ×××"），历史记录保留（属手机本地数据）

---

## 4. 功能三：模型管理（老板补充需求，2026-07-03）

### 4.1 设计动机（必须写清楚的产品逻辑）

**设备硬件会演进，模型不能写死在固件里。** 当前测试机是全志 A733（4G 内存，Linux ARM64）——这只是**最低档位**，不是目标终点。同一套 ShowenV2 系统未来跑在更强的板子上时，应能直接下载并切换到更大的模型（如 0.5B → 2B → 更高档），获得更强的对话能力，而**不需要重新烧录或重新部署系统**。"模型能力档位与设备能力匹配"是本产品的长期竞争力：硬件卖点升级 = 角色变聪明，软件系统不动。

### 4.2 功能需求

模型（LLM，及后续 ASR/TTS 模型同理）作为设备上的可管理资产，支持：

| 能力 | 要求 |
|------|------|
| 可用模型清单 | 查看模型仓库清单：每个模型含名称、版本、文件尺寸、运行内存需求、**推荐设备档位**（如"4G 内存档可用 / 建议 8G 以上"）；同时展示已下载/未下载状态 |
| 下载模型 | 从**国内镜像源**下载；下载进度实时展示（百分比/速度）；完成后完整性校验（哈希）；失败可重试，**支持断点续传或失败后从断点重试**（二选一由工程定，不允许每次从零开始） |
| 切换模型 | 从已下载模型中选择一个作为当前推理模型并生效；切换期间语音功能短暂不可用需有明确状态提示，视频播放不受影响；切换失败自动回退到上一个可用模型 |
| 删除模型 | 删除已下载的旧模型释放空间；当前使用中的模型不可删除（或先强制切换）；删除有二次确认 |
| 存储配额 | 设备磁盘有限（当前可用 18G），模型存储有配额概念：展示"模型已用空间 / 配额上限"，下载前预检剩余空间不足则明确拒绝并引导删除旧模型 |
| 档位守门 | 下载/切换超出当前设备内存能力的模型时给出明确警告（可强制继续，标注"可能不稳定"），推荐档位默认高亮匹配当前设备的选项 |

### 4.3 App 侧交互：模型管理页

- 入口：设置页或角色/语音相关页内"模型管理"
- 页面内容：
  - **当前模型**卡片：名称、档位、内存占用
  - **设备资源水位**：磁盘（模型配额已用/剩余）、内存（总量/可用），让用户直观看到"我的设备还装得下什么"
  - **可用模型列表**：每项显示尺寸/内存需求/推荐档位/状态（未下载 / 下载中+进度 / 已下载 / 使用中），操作按钮随状态变化（下载 / 取消 / 切换 / 删除）
- 下载过程可退出页面后台继续，回到页面恢复进度显示；下载失败有原因提示与重试按钮

### 4.4 与现有机制的关系

产品行为层面**参照项目已有的"插件仓库 + 版本管理"模式**（plugin_repo 下载安装、version_manager 版本切换/回滚/GC 保护当前版本）：模型清单 ≈ 插件仓库索引，模型切换 ≈ 版本切换（先验证后生效、失败回退），删除保护 ≈ GC 的 protected 机制。是否复用同一套基础设施属架构决策，**留给王思远**；PRD 只约束上述外部行为一致性。

### 4.5 V1 边界（模型管理部分）

- 模型仓库源 V1 可以是静态清单（自建镜像/OSS 上的 index 文件），不做动态商店
- 不做模型自动更新推送；检查更新为手动触发
- ASR/TTS 模型 V1 允许随系统预置固定版本，仅 LLM 必须实现完整"清单/下载/切换/删除"闭环（ASR/TTS 纳入同一框架为增强项）

---

## 5. 客户端矩阵（老板补充需求二，2026-07-03）

### 5.1 两个客户端都是 M2.1 交付对象

| 客户端 | 位置 | M2.1 改动 |
|--------|------|-----------|
| 安卓 App | `clients/flutter` | 角色切换页（§2.3）+ 语音对话页（§3.2）+ 模型管理页（§4.3），前文已覆盖 |
| **设备内嵌网页控制端** | `src/plugins/http/routes.rs:1961` 的 `WEB_UI_HTML` 字符串常量，设备 `/` 与 `/index.html` 直接服务（routes.rs:162-172）；现有能力：状态监控、播放控制、状态机触发、WiFi、配置编辑、视频管理 | **必须新增**：① 角色切换界面（角色列表 + 当前角色标识 + 一键切换，行为指标同 §2.4）；② 模型管理界面（当前模型 / 资源水位 / 下载进度 / 切换 / 删除，行为同 §4.2）；③ 文字对话 + **回复音频浏览器播放（V1 必做）**（见 5.2） |

两端共用同一套设备 API：一端发起的角色切换/模型下载，另一端可见状态同步（Web 通过既有 WS 推送或轮询回显）。Web 端体验标准允许低于 App（工具属性优先），但错误提示、进度反馈、危险操作二次确认不可缺。

### 5.2 Web 端语音的技术约束（如实声明）

- **播放不受限，V1 必做**：浏览器 `<audio>` 播放没有安全上下文限制。Web 端文字对话的回复**必须带语音播放**（回复文字 + TTS 回复音频在浏览器播出）——设备无扬声器（老板补充三），"角色的声音"在 Web 场景就靠浏览器播放兜底，这是 V1 硬指标。
- **录音受限，降为评估项**：浏览器 `getUserMedia` 麦克风权限要求 HTTPS 安全上下文（localhost 除外），设备当前为纯 HTTP —— 因此 **V1 录音语音回合只落 App，Web 端录音不作为 V1 验收硬指标**。
- **Web 录音的技术路径（V1.5 增强候选，评估项）**：设备配自签名 HTTPS 证书后 `getUserMedia` 即可用，代价是浏览器首次访问出现一次证书警告需用户确认。方案技术可行、体验有瑕疵，列为 V1.5 增强候选，是否做、何时做在 V1 交付后按用户反馈评估。
- **Web 端 V1 主路径：文字输入对话** —— 文字 → LLM（同一人设链路，跳过 ASR）→ 回复文字 + TTS 回复音频（浏览器播放）。既绕开录音限制，又多出一个可演示入口，还使 LLM/TTS 链路可独立于 ASR 单独调试。
- 画面联动与并发规则同 §3.3/§3.5：文字回合期间角色切 `talk`，与语音回合互斥（同一时刻全设备只有一个进行中回合）。

### 5.3 功能 × 客户端对照表

| 功能 | 安卓 App | Web 控制端 | 说明 |
|------|---------|-----------|------|
| 角色切换 | ✅ 角色卡片页 | ✅ 必须新增 | 同一套 `config/available` + `config/switch` API |
| 录音（语音输入） | ✅ 主路径 | 🔍 评估项（V1.5 候选，非 V1 验收项） | getUserMedia 需 HTTPS；自签证书方案见 §5.2 |
| 回复音频播放 | ✅ 必做 | ✅ **必做** | 设备无扬声器，声音一律客户端播出（§3.1 铁律）；浏览器 `<audio>` 无限制 |
| 文字对话 | ➖ 增强项（可复用时间线 UI，非 V1 强制） | ✅ 必须新增 | 文字 → LLM → 回复文字 + TTS 音频播放，跳过 ASR |
| 模型管理 | ✅ 模型管理页 | ✅ 必须新增 | 同一套模型管理 API，含下载进度 |
| 状态监控/播放控制/状态机/WiFi/配置/视频 | ✅ 已有 | ✅ 已有 | M1 存量能力，本期不动 |

### 5.4 Web 端可维护性风险（前置声明）

`WEB_UI_HTML` 是嵌在 Rust 源码内的单文件 HTML 字符串常量（routes.rs:1961-2384，约 420 行高密度单行 HTML/JS，routes.rs 全文件已达 2384 行）。M2.1 要再塞入角色切换 + 模型管理 + 文字对话三块界面，继续以字符串常量方式堆积将迅速不可维护。**建议王思远在架构评审时议**：改为 `include_str!` 引入独立 `.html` 文件（编译期仍内嵌进二进制、零运行时成本、部署形态不变）或静态资源目录。PRD 不强制实现方式，只要求交付前解决可维护性问题（详见 §9 风险表 #11）。

---

## 6. 非功能需求

| 维度 | 要求 |
|------|------|
| 回合延迟 | 松开录音 → 手机开始播放回复：**目标值待 spike 数据校准后回填**（产品期望：短语音（≤5s 说话、简短回复）体验可接受线 ≤ 5s、良好线 ≤ 3s，仅作 UX 参照，不作承诺；spike 若证明不可达则调整模型档位或预期，见 §9） |
| 分项预算 | ASR / LLM 首 token+生成 / TTS / 传输 各分项延迟以 spike 报告为准，PRD 评审时回填成正式指标表 |
| 离线可用 | 本地链路（角色切换 + 语音回合）在仅局域网、无公网环境下 100% 可用；断公网不得出现任何功能降级或报错 |
| 资源共存 | 测试机全志 A733（4G 内存，Linux ARM64，**最低档位基准机**）实测基线（2026-07-03）：可用内存 3.3G、视频渲染已占 ~1.25 核。语音三模型（常驻或按需加载，策略以 spike 定）峰值内存预算初定 ≤ 2.5G（保留 ≥ 800M 余量，**以 spike 实测修正**）；推理期间视频渲染不得出现可感知掉帧/卡顿，状态机触发仍需及时响应。更高档设备放宽预算由模型管理的档位机制承接（§4） |
| 模型落盘 | 模型文件占用设备磁盘（当前可用 18G），模型存储配额初定 ≤ 4G（含多模型共存），具体以选型定；配额行为见 §4.2 |
| 隐私（本地模式核心卖点） | 录音与对话内容**不出本地网络、不上云**；设备端不持久化用户录音与对话文本（处理完即弃）；对话历史仅存用户手机本地，可一键清空；App 隐私文案明示"语音在你的设备上本地处理" |
| 稳定性 | 语音链路任何一环失败（ASR 空结果/LLM 超时/TTS 失败）都必须给发起请求的客户端（App/Web）返回结构化错误，不得拖垮视频主循环；连续失败不影响下一回合重试 |

---

## 7. 不做什么（V1 边界）

1. **设备端麦克风/扬声器直接对话** — V2（老板补充三确认：设备扬声器/麦克风当前**未接入**；V1 全部声音走客户端，不依赖设备音频硬件；音频硬件接入后启动 V2，见 §10）
2. **唤醒词**（"小狗小狗"类免手动唤起） — V3
3. **精细口型同步** — 不做；talk 状态只要求"在说话的感觉"
4. **多轮长上下文记忆的持久化** — 不做；仅会话内内存滑动窗口
5. 流式 ASR / 流式 TTS / 回合打断 — 不做，V1 纯回合制
6. 云端 LLM 的实际接入 — 只留接口不实现
7. 歌姬"真唱歌"（歌声合成） — 不做；歌姬 V1 是"会聊天的歌姬人设"，唱歌能力另立项
8. 角色内容包的在线商店/分发 — Phase 2 后续里程碑
9. **Web 端录音语音对话** — 受 getUserMedia HTTPS 安全上下文限制（§5.2），V1 Web 用"文字对话 + 回复音频浏览器播放"替代；自签 HTTPS 解锁录音为 V1.5 增强候选（评估项，非 V1 验收）

---

## 8. 验收标准（可演示的用户旅程，逐条过）

1. 打开 App 进入角色页，看到 狗 / 猫 / 数字人 / 歌姬 至少 4 张角色卡片，当前角色卡片有"使用中"标记
2. 点击"歌姬"卡片 → 设备画面在目标时限内切换为歌姬 idle 循环 → App 当前角色标记同步更新为歌姬
3. 构造一个非法/损坏的内容包并尝试切换 → 设备保持原角色继续播放，App 显示明确错误原因
4. 在对话页按住说话："你好，你叫什么名字？" → 松开 → 思考中动画 → 手机播出歌姬人设风格的语音回复；回合期间设备画面为 talk（或宠物包降级状态），结束后回到 idle
5. 对话历史时间线正确显示：我的转写文本 + 歌姬回复文本 + 回复音频可重播
6. 断开公网（仅保留局域网）重复旅程 2 和 4 → 全部成功，体验无差异
7. 切换到"狗"再问同一问题 → 回复风格明显变为拟声/简短俏皮，且时间线出现"已切换到狗"分隔、上下文已重置
8. 回合进行中再次按住说话或点击切换角色 → 被拒绝且有友好提示，不产生错乱
9. 语音回合全程观察设备画面 → 视频播放无可感知卡顿
10. 检查设备配置：`llm.backend` 配置项存在，切为 `cloud` 时得到"暂未支持"的明确响应（云端预留验收）
11. 设备端抽查：回合结束后无录音文件/对话文本落盘残留（隐私验收）
12. 打开模型管理页 → 看到当前模型、设备磁盘/内存水位、可用模型清单（含尺寸/内存需求/推荐档位标注）
13. 下载一个新模型 → 进度实时显示 → 完成后校验通过、状态变"已下载"；中途断网 → 失败有提示，重试不从零开始
14. 切换到新下载的模型 → 语音回合使用新模型正常应答，视频播放全程不受影响；**全程不重新烧录/不重新部署系统**（老板点名验收项）
15. 删除一个未使用的旧模型 → 空间水位下降；尝试删除使用中的模型 → 被拒绝并提示
16. 磁盘配额不足时下载 → 被明确拒绝并引导删除旧模型
17. 浏览器访问设备 `http://<device-ip>:8080/` → Web 控制端可见当前角色并一键切换，设备画面切换成功，App 侧角色状态同步更新（两端一致性验收）
18. Web 控制端模型管理：查看当前模型与资源水位 → 下载新模型见实时进度 → 切换 → 删除旧模型，全流程可完成且与 App 侧状态互通
19. Web 控制端文字对话：输入文字 → 收到当前角色人设风格的回复文字 + **回复音频在浏览器内播放**（V1 必做，设备无扬声器由浏览器出声）；回合期间设备画面 talk 联动，与语音回合互斥规则生效
20. 全部既有回归：147 项测试 + Flutter 测试继续全绿，零 warning

---

## 9. 风险与依赖

| # | 风险/依赖 | 说明 | 应对 |
|---|----------|------|------|
| 1 | **数字人/歌姬素材从哪来**（最大外部依赖） | 每角色最少 4 状态 × 规格素材（§2.2），且跨片段角色一致性要求高 | 建议方案见下方"素材方案建议"；**需老板拍板** |
| 2 | **spike 结论可能降档** | 2B 模型若延迟/内存超预算，降 0.5B 档甚至更小；ASR/TTS 模型同理 | PRD 指标留待回填；人设 prompt 按小模型能力设计（短、指令简单）；回复长度上限收紧是首选降级手段 |
| 3 | 设备音频硬件未接入（~~扬声器现场验证~~ 已关闭：老板 2026-07-03 确认扬声器/麦克风未插入，无需再验证） | V1 全链路不依赖设备音频硬件（声音一律客户端播出，§3.1 铁律），**对 V1 零影响** | 若后续插入 USB/3.5mm 麦克风+扬声器，作为 V2（设备直接对话）的启动前置条件，届时再做接入验证 |
| 4 | CPU/内存争抢 | 视频渲染 ~1.25 核基础负载上叠加三模型推理 | spike 明确并发余量；必要时推理限核/降优先级；验收标准 9 兜底 |
| 5 | 配置 schema 演进 | `deny_unknown_fields` 下新增 `character` 块需内核同步改 schema，旧包/新包兼容策略要设计 | 列为内核工程依赖，架构评审首个议题（方案 A/B，§2.1） |
| 6 | piper 中文音色可用性 | 中文 TTS 音色质量/数量待验证 | spike 覆盖；最低验收线为"一款可懂中文音色"，音色差异化降为增强项 |
| 7 | 存量包路径不可移植 | dog/cat 素材在仓库外相对路径 | 新包强制自包含目录规范；存量包 V1 不动，迁移列 backlog |
| 8 | App 录音权限与机型差异 | iOS/Android 麦克风权限、录音格式差异 | 沿用 BLE 权限引导的成熟模式；录音格式在 App 侧归一后上传 |
| 9 | 模型镜像源建设与维护 | 国内可达、带宽/存储成本、清单（index）维护责任人未定 | V1 用静态清单 + 自建镜像/OSS（§4.5）；源地址做成可配置；**清单维护归属需老板/CEO 定** |
| 10 | 大文件下载对设备的冲击 | GB 级下载占带宽/磁盘 IO，可能影响视频播放与 HTTP 服务 | 下载限速/低优先级；沿用 M1.2 已解决的"上传流式化"经验（避免内存尖峰）；下载中不允许同时切模型 |
| 11 | **Web 控制端可维护性** | `WEB_UI_HTML` 为嵌在 routes.rs 内的单文件 HTML 字符串常量（routes.rs:1961-2384，约 420 行高密度单行 HTML/JS，routes.rs 全文件已 2384 行），M2.1 需再加角色切换/模型管理/文字对话三块界面，继续内嵌堆积将不可维护 | **列王思远架构评审议题**：`include_str!` 拆独立 .html（编译期内嵌不变、部署形态不变）或静态资源目录；实现方式工程定，交付前必须解决（§5.4） |

### 素材方案建议（需老板拍板）

| 方案 | 优点 | 缺点 | 建议 |
|------|------|------|------|
| A. AI 视频生成（可灵/即梦/Wan 等图生视频，先定角色立绘再批量生成状态片段） | 快、便宜、可迭代；一致性靠同一张立绘做参考帧可控 | 循环无缝性和动作质量需筛选返工；生成额度成本 | **推荐**：先各做 1 个角色 × 4 状态的试产验证管线 |
| B. 外包（Live2D/三维渲染工作室） | 质量与循环可控 | 周期长、贵、迭代慢 | 备选：A 试产不达标再启动 |
| C. 老板提供素材 | 零制作成本 | 是否存在、规格是否匹配未知 | 若老板有现成资源优先评估 |

**所需素材清单（无论哪种方案）**：数字人、歌姬各一套 —— idle / talk / greet / sleep 四状态片段，规格按 §2.2；每角色另需 1 张封面图（角色卡片用）+ 人设一句话设定（供撰写 persona prompt）。

---

## 10. Roadmap 对齐

| 版本 | 定位（一句话） |
|------|---------------|
| **V1（本 PRD，M2.1）** | 手机遥控切换角色 + 客户端当话筒喇叭（设备本地不出声）的本地对话回合 + 模型可下载/切换（不重烧系统），App 与 Web 控制端双端交付，云端接口预留 |
| **V2** | 设备自带麦克风/扬声器直接对话，手机退为管理端（**前置条件：设备音频硬件接入后启动**——当前扬声器/麦克风未插入；接入后还需回声/拾音工程） |
| **V3** | 唤醒词免手动唤起 + 本地/云端 LLM 协同（本地兜底、云端增强），走向真正"常驻的数字生命" |
