# 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//`),路径自包含,为后续内容包分发做准备。存量 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 必做**:浏览器 `