# Phase 3 测试 > 对应施工单:`phase-3-plan.md` > 代码事实:`phase-2-review.md` > 语音回归底表:`phase-2-voice-test.md`(本阶段固件 **不必重烧**,除非你还停留在 Phase 1 bin) 本阶段验收两件事:**库里能查到跟读/复读实例**,以及 **Phase 2 语音行为没有回退**。 不要测数字倒背、口算、成语接龙,不要测分数。 --- ## 0. 开始前 1. manager-api 已跑 Liquibase(存在 `ai_task_instance` / `ai_task_step` / `ai_task_event`)。 2. xiaozhi-server 能连上 manage_api(与现网 chat report 同一套密钥)。 3. 设备仍是 Phase 2 固件:复读结束只招呼一次;跟读一句一 MP3。 4. SD 音频仍按: ```text /english_learning/shadowing/lessons/unit01/01.mp3 /english_learning/shadowing/lessons/unit01/02.mp3 /english_learning/repeater/lessons/unit01/full.mp3 ``` 5. pytest: ```text cd xiaozhi-esp32-server/main/xiaozhi-server python -m pytest tests/test_app_runtime.py -q ``` 应通过(含 upsert 失败不阻断 `save` 的新用例)。不过这一关,不要上设备。 准备: - 控制台登录 token(GET 用) - server secret(PUT 调试用,与 `/agent/chat-history/report` 相同) - 设备 `mac` / `device_id` --- ## 1. 语音回归(必须先过,再查库) 只跑 `phase-2-voice-test.md` 的这些节,勾选标准不变: | 节 | 内容 | 通过? | |----|------|--------| | §0 | 开机对话模式;唤醒;「今天天气怎么样」当闲聊 | | | §2 | 跟读整段:播完再跟;不讲笑话;最后一轮回 Chat;JSON 含全部有效轮 | | | §5-A | 复读自然结束 **只招呼一次** | | | §5-C | 播放中说「你好吗」继续播,不回答 | | 本阶段 **新增** 的语音失败标准: - 一跟读就卡死 / 一直不播下一句 → 多半是 upsert 同步堵住了 WS 线程(见计划 §10)。 - 跟读正常但 GET 404 → 只是上报失败,先记 §3,不要改口令逻辑。 翻译 / 中英 / 离线:若时间紧可抽测「打开翻译 → 退出翻译 → 讲个笑话」。离线复读仍不得连云。 --- ## 2. 跟读 Instance(核心) 从对话模式说 **开始跟读**,按音频跟完(或至少两句后说 **退出跟读**)。 ### 2.1 文件仍在 打开 `xiaozhi-esp32-server/main/xiaozhi-server/data/task_sessions/` 最新 JSON: | 检查 | 通过? | |------|--------| | `task_session_id` 为 32 位 hex | | | `app_id=english_learning`,`task_id=shadowing` | | | `turns` 条数 = 你跟读成功的句数(退出则少于音频总数) | | | 最后一轮 `asr_text` 不是空(若你说了最后一句) | | | `status` / `end_reason` 为 `completed`+`finished` 或 `cancelled`+`user_exit` | | | `events` 里有 `audio.completed`(每句播完) | | 记下 `task_session_id` = `{id}`。 ### 2.2 GET 与文件一致 ```text GET /task/instance/{id} Authorization: Bearer {oauth token 或与 chat report 相同的 server secret} (本阶段 Shiro 将 /task/instance/** 配成 server 过滤器,用 server secret 即可验通) ``` | 检查 | 通过? | |------|--------| | HTTP 200 | | | `status` / `end_reason` / `app_id` / `task_id` 与 JSON 相同 | | | `turns`(或响应里的 `steps`)条数与 JSON `turns` 相同 | | | 第 0 句 `prompt_text` 仍是 SD 路径,不是 TTS 英文范读 | | | 对应 `asr_text` 对得上你说的话(允许 ASR 误差) | | | `events` 含 `audio.completed` | | 若实现选了计划 §4.3-A:有效轮的 `audio_id` 非空,且能在 `ai_agent_chat_audio` 查到。选 B 则允许全空。 ### 2.3 列表 ```text GET /task/instance?macAddress={mac}&limit=20 ``` | 检查 | 通过? | |------|--------| | 刚那条 `{id}` 出现在 list | | | list 项 **没有** 整份 turns 正文(避免把跟读当聊天记录 dump) | | --- ## 3. 复读 Instance 说 **复读机**,等自然结束(或音频很短)。 | 检查 | 通过? | |------|--------| | 新 JSON:`task_id=repeater`,`end_reason=play_completed` | | | GET:`turns` 可以为空 | | | GET:`events` 含 `audio.completed` | | | 设备只招呼回 Chat **一次**(回归) | | 中途 **退出复读**:`end_reason` 为 `user_exit` 或 `play_stopped`(与 Phase 2 谁先到有关),GET 能看到,不丢。 --- ## 4. 失败隔离(必须测) **A. manage_api 不可用** 停掉 manager-api(或改错密钥),再说 **开始跟读** 跟一句再 **退出跟读**。 | 检查 | 通过? | |------|--------| | 设备仍播音频、仍能退出回 Chat | | | 本地 JSON **仍然**写出 | | | xiaozhi-server 日志有 upsert 失败,**没有**把 WS 打挂 | | | GET 可以 404 / 旧数据;这不算语音失败 | | 测完恢复 manager-api。 **B. 非法 PUT(可选)** 用 server secret PUT 一个 `app_id` 含 `../` 或超长 id → 400,不写库。 --- ## 5. Agent ≠ App 在 **对话模式** 说「今天天气怎么样」或「讲个笑话」。 | 检查 | 通过? | |------|--------| | `ai_agent_chat_history` 仍增加聊天行(现网 report) | | | **没有**因此新增一条 `app_id=chat` 的 task_instance(chat 不 begin_session;若实现误报了 chat instance 则失败) | | | 跟读 instance 仍然只在 `/task/instance` 查,不出现在「聊天记录就是音频」的错觉里 | | --- ## 6. 断线 跟读说到第二句过程中,拔掉设备 Wi‑Fi 或杀 xiaozhi-server 连接。 | 检查 | 通过? | |------|--------| | 本地 JSON `end_reason=disconnected` 或等价 cancelled | | | 若断线前至少成功 save/PUT 过一次:GET 能看到已有 turns,不是空实例把成功轮抹掉 | | (若断线发生在第一次 PUT 之前,库里可能没有行:以 JSON 为准,并在 PR 说明。) --- ## 7. 固件冻结抽检 | 检查 | 通过? | |------|--------| | `xiaozhi-esp32-2.2.6` 相对 Phase 2 **无业务 diff**(本阶段 PR 不应含该目录) | | | 未要求重烧 bin 即可完成 §1–§3 | | | 未出现新的 WS `type`、新 MQTT action、新 MCP 名 | | 新订阅 App 建目录属于 Phase 2 已交付能力,本阶段 **不要**当新功能再测一遍;若回归时可看连上后 SD 是否仍有 `/english_learning/.../lessons`。 --- ## 8. 不要测什么 - 分数、对错、再读一遍的评估策略 - 倒背 / 口算 / 接龙 - 家长小程序 UI、manager-web 新页面 - `TASK_PAUSE`、`wait` Skill、本地录音上传 - 把 SD 从 `/{app}/{task}/lessons` 搬到 `/resources/` --- ## 9. 失败时先看哪 | 现象 | 多半是 | |------|--------| | 跟读卡死 | upsert 同步阻塞;改为 end 时再 PUT 或后台任务 | | JSON 有、GET 404 | Python 未挂钩 / Shiro 把 PUT 拦了 / 密钥不一致 | | GET 200 但 turns 少最后一轮 | `end_session` 没在最后一轮 `add_turn` 之后 save(Phase 2 回归) | | 复读结束招呼两次 | 不是 Phase 3 问题;固件又发了 `[app_task]chat` | | chat 笑话进了 task_instance | begin_session 误绑 chat | | Liquibase checksum 失败 | 改了旧 SQL;应只加新 changeset | --- ## 口令速查(与 Phase 2 相同) | 意图 | 说其中一句即可 | |------|----------------| | 唤醒 | 你好小智 | | 跟读 | 开始跟读、我要跟读 | | 复读 | 复读机、开始复读 | | 回聊 | 退出跟读、退出复读、我们来聊天吧 |