编辑 | blame | 历史 | 原始文档

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 音频仍按:
/english_learning/shadowing/lessons/unit01/01.mp3
/english_learning/shadowing/lessons/unit01/02.mp3
/english_learning/repeater/lessons/unit01/full.mp3
  1. pytest:
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 与文件一致

        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 列表

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 相同)

意图 说其中一句即可
唤醒 你好小智
跟读 开始跟读、我要跟读
复读 复读机、开始复读
回聊 退出跟读、退出复读、我们来聊天吧