文档:小智 AI Server —— Task、语音分析与用户评估平台架构 Plan
Version: V1.1
Date: 2026-08-30
本文在 V1.0 概念稿上,依据《Xiaozhi AI 开放应用平台 Master Plan》与《Architecture Audit》修订。
只设计,不编码。
与《小智 AI 硬件开放应用平台 —— 总体架构与开发 Plan》V1.1 成对阅读。
本文只定义 Server(仓库 xiaozhi-esp32-server)如何从「实时语音对话后端」演进为「Task 编排 + 语音对象 + 评估」平台。
硬件执行细节见 Client Plan。本文只规定 对设备的契约 与 云端模块 / 数据 / API。
工作区另有 smartlearning/(课程 JSON + MQTT DOWNLOAD/PLAY_MEDIA)。它更接近 App Sync,但**未接入** Xiaozhi Server。本文把它当作可吸收的旁路,而不是第三套长期业务云。
相对 V1.0 的关键修正:
| V1.0 | V1.1(本次) |
|---|---|
| 一上来拆 Gateway/一堆 Assessment 微服务 | 逻辑模块分开,物理仍是 Python xiaozhi-server + Java manager-api |
| Phase 1 就做跟读+倒背+口算三套评估 | Phase 1 = 英语复读编排且 不评估;Phase 2 = 跟读 + ASR 文本比对 |
| 设备与 Server 各有一套完整 Task Runtime | 权威 Task Runtime 只在 Server;设备是 Task Executor |
| 音频规划为对象存储,但未对齐现状 | 承认现状是 ai_agent_chat_audio LONGBLOB;演进再迁对象存储 |
| Agent 未区分 | Agent ≠ App;Agent 继续做人设/模型配置 |
| 8~10 个平台阶段混在一起 | 与 Client 对齐为 Phase 1/2/3,其后评估平台再拆子阶段 |
双栈,不是微服务网:
| 模块 | 入口 | 职责 |
|---|---|---|
| xiaozhi-server | main/xiaozhi-server/app.py |
WS /xiaozhi/v1/、OTA HTTP、实时 ASR/LLM/TTS |
| manager-api | AdminApplication.java |
用户/设备/Agent/插件/聊天落库 |
| manager-web / mobile | — | 管理端 |
| digital-human | — | 测试端,非主链路 |
一次对话:
Device → ConnectionHandler (session_id = uuid4)
→ VAD → ASR → intent / chat()
→ UnifiedToolHandler (Plugin / MCP / IoT)
→ TTS → type=tts/stt/llm/mcp + Opus
→ 可选 report → POST /agent/chat-history/report
→ ai_agent_chat_history + ai_agent_chat_audio (WAV)
关键代码:
| 阶段 | 位置 |
|---|---|
| 入站 | core/connection.py _route_message |
| VAD/ASR | core/handle/receiveAudioHandle.py |
| 意图 | core/handle/intentHandler.py |
| LLM | ConnectionHandler.chat |
| TTS 出站 | core/handle/sendAudioHandle.py |
| 上报 | core/handle/reportHandle.py → WAV |
| 工具 | plugins_func/* + unified_tool_handler.py |
已有可复用: Agent、Plugin、MCP、function call、session_id、可选 WAV、play_music(但走 TTS 通道,**不适合**复读循环)。
不存在: App、Task、Step、TaskInstance、InteractionMode、Assessment、Plan、Subscription。session_id 是 WS 连接级,不是 TaskInstance。
Xiaozhi Server
├── Device Gateway ← 现有 WS / MQTT 网关,保留
├── Chat Orchestration ← 现有 ConnectionHandler.chat
├── Task Runtime ← 新增,权威编排
├── Media / Audio Object ← 从「聊天附件」演进为可挂 Step 的音频对象
└── Assessment ← Phase 2 起;先文本比对,后语音特征
最终数据流(目标态,不是 Phase 1):
Schedule → TaskInstance → Step → InteractionMode
→ Device Skill → 儿童语音
→ 保存 Audio 对象 → ASR → Assessment → Metric → 家长报告
Phase 1 截断在「Mode=PLAY_AUDIO 驱动设备播放」;不要求评估与家长报告。
Server
创建 / 推进 / 结束 TaskInstance
指定 InteractionMode
调用设备 Skill(MCP 或 type=task)
ASR / LLM / TTS / Assessment
音频对象与 step 关联
App JSON 与资源 URL 的权威
Client
执行 Skill、切换 Mode、回报 Event
不持久化 Step 历史、不评分
禁止在 Python 对话热路径里写死 if english_repeat 作为长期形态;Phase 1 允许一个 编排插件,Phase 2 必须升到 Task Runtime + JSON 定义。
V1.0 列出的 Gateway / Task Service / Assessment Service / Emotion … 全部作为逻辑边界。
物理部署保持:
xiaozhi-server (Python) 实时:连接、ASR、LLM、TTS、Mode 切换、Task 步进(运行时)
manager-api (Java) 配置与持久化:User、Device、Agent、Task 定义/实例、音频元数据、评估结果
对象存储(Phase 2+) 原始音频;第一期可继续 BLOB,但 API 按 audio_id 访问
不要为第一个复读 Task 拆新进程。
模块映射:
| 逻辑模块 | Phase | 落点 |
|---|---|---|
| Device Gateway | 0 已有 | websocket_server.py / MQTT gateway |
| Chat Orchestration | 0 已有 | ConnectionHandler |
| Interaction Mode 开关 | 1 | ConnectionHandler 上 interaction_mode;非 CHAT 跳过 chat() |
| Repeat Orchestrator | 1 | plugins_func 或独立薄模块;MCP self.mp3.play |
| Task Runtime | 2 | Python 运行时状态机 + Java 实例表 |
| Audio Object | 2 | 扩展上报:挂 step_instance_id;逐步迁出 LONGBLOB |
| Assessment v0 | 2 | expected_text vs ASR;Java 或 Python 均可,结果入库 |
| App / Resource | 3 | 吸收 smartlearning 清单 + 下载 URL;Java 为权威 |
| Scheduler / Plan | 3 | manager-api + 定时;不要用设备 RTC 闹钟冒充 |
| Voice Feature / Baseline / Emotion | 3+ | 异步队列;禁止进实时 TTS 路径 |
| Subscription / Developer API | 更后 | 不做 Phase 1–2 |
无 Task 时行为与现网一致:
listen → ASR → intent/LLM/tools → TTS
协议 hello/listen/abort/iot/mcp/ping 与下行 stt/tts/llm/mcp 禁止改语义。
插入方式:
_route_message / startToChat / chat()
之前判断 interaction_mode
CHAT → 原路径
PLAY_AUDIO / FOLLOW_READ → 不调用 chat()
FOLLOW_READ 的语音仍走 ASR.receive_audio,结果写入 Step 而非 Dialogue
play_music 继续服务「用户说播放歌曲」。**不要**用它实现英语复读(它占用 TTS 队列,和课程 SD 播放冲突)。
Agent(已有 ai_agent) |
App(新建,Phase 3) | |
|---|---|---|
| 是什么 | 人设 + ASR/LLM/TTS/插件配置 | 业务容器(英语老师、口算…) |
| 谁用 | 每台设备当前绑定一个 | 儿童可订阅多个,同时只 Active 一个 |
| Phase 1 | 继续用 Agent 跑 Chat | 不建 App 表也可以做复读 Demo |
Phase 1 复读可以挂在「当前 Agent + 一个插件」,不必先有 App 实体。Phase 3 再把 Task Definition 归到 App 下。
不要把 ai_agent 改名为 App,也不要在 Agent 上堆 Workflow JSON 作为长期模型(临时可以,正式模型见第 10 节)。
加载 Task Definition
创建 TaskInstance
按 Step 推进
设置 Connection.interaction_mode
下发 skill.execute / MCP
等待 Device Event 或 ASR 完成
写 StepInstance
决定下一步 / 结束 / 失败
运行时可活在 ConnectionHandler 旁的 TaskRuntime 对象(每连接一个当前 Instance)。持久化在 manager-api。
CREATED → READY → RUNNING
→ WAITING_DEVICE | WAITING_USER | WAITING_AI
→ COMPLETED | FAILED | CANCELLED | TIMEOUT
设备只缓存 RUNNING 的当前 step;重连后 Server 重放或结束。
Definition:模板(英语复读 10 遍)。
Instance:某日某设备的一次执行(进度、状态、结果)。
没有正式 Runtime 也可以:
插件 english_repeat(n, resource)
loop: MCP play → wait play.completed 或超时
mode=PLAY_AUDIO 期间禁止 chat()
这是为了验证链路;Phase 2 用 JSON Step 替换该循环。
Server 是 Mode 的 指定者:
Step.mode = PLAY_AUDIO | FOLLOW_READ | CHAT | LISTEN | …
→ conn.interaction_mode = …
→ 可选下行 mode.set(Phase 2)
| Mode | Server 行为 |
|---|---|
| CHAT | 现有 startToChat / chat() |
| PLAY_AUDIO | 不 ASR 对话、不 LLM;驱动 play;等完成事件 |
| FOLLOW_READ | 先 play,再收听;ASR 文本进 Step;**不**进 Dialogue/LLM |
| LISTEN | 只 ASR,结果给当前 Step |
| QUIZ/GAME | Phase 3:出题在 Server,设备 CHAT 或 TTS+LISTEN |
V1.0 的「Interaction Runtime 作为独立服务」降级为 ConnectionHandler 上的 mode 字段 + TaskRuntime 策略表。
chat_history_conf==2 时 PCM/Opus 转 WAV 进 ai_agent_chat_audio.audio(注释写 opus,实际 WAV)audio_id 仅在上报后存在;history 有 session_id,**无** task/step跟读/评估不要只存 ASR 文本。原始音频是可重分析资产。
但 Phase 1 复读 可以不存用户音频(用户可能没说话)。
Phase 2 最小:
沿用 reportHandle WAV 上报
字段增加 task_instance_id, step_instance_id, purpose=follow_read
audio_id 写入 step_instance
仍可暂存 BLOB
Phase 2+ 目标(V1.0 对象存储):
DB: audio_id, user/device, task/step, storage_key, duration, format, sample_rate
Blob: 对象存储 raw wav/opus
不要把长期课程音频继续堆在 MySQL LONGBLOB
Segment: 跟读一步一段用户音频即可;单词级切分放到评估模型需要时再做。不要 Phase 2 就上完整 AudioSession 树。
设备文件录音 / pending 上传: Client Phase 3;Server 预留 upload API,不阻塞跟读 v0。
时间戳:连接级足够做 v0;NTP 与精确 latency 放评估 Phase 3。Sening 已有时钟同步指令,可复用,不算 Server 新服务。
| 表 | 作用 |
|---|---|
sys_user |
账号 |
ai_device |
设备;user_id agent_id mac_address |
ai_agent |
人设与模型;chat_history_conf |
ai_agent_chat_history |
会话文本;session_id audio_id chat_type |
ai_agent_chat_audio |
音频 BLOB |
ai_agent_plugin_mapping |
Agent ↔ 插件 |
ai_ota / 模型 / RAG 等 |
与 Task 无关,不动 |
Chat 历史 不要改语义去存 Task;用新表或可空外键扩展。
task_definition id, app_id(可空), name, version, json_body, interaction 默认 mode
task_instance id, device_id, task_definition_id, session_id,
status, current_step, started_at, ended_at
step_instance id, task_instance_id, step_index, mode, status,
audio_id, asr_text, result_json, started_at, ended_at
第一期 json_body 可内嵌 steps,不必先拆 task_step 定义表。
app / app_version / app_subscription
plan / schedule
audio_segment # 从 chat_audio 迁出
assessment / assessment_metric
user_metric_baseline
daily_summary
V1.0 的 emotion_signal、developer 宽表 不要列入 Phase 2 DDL。
Child 实体:现网是 User+Device。Phase 3 若需要「一个家长多个孩子」再拆 child;Phase 1–2 用 user_id + device_id。
保留 V1.0 原则,收紧落地。
L1 任务对错 / 文本匹配 ← Phase 2 v0
L2 语音特征(语速、停顿) ← 异步,Phase 3
L3 趋势 / 基线 / 参与度 ← 要有足够 Instance
情绪只做 信号 不是诊断(V1.0 §20 保留),且 晚于 L1。
expected_text vs ASR text
→ 准确率 / 是否说完
→ 即时 TTS 反馈(走现有 TTS)
→ 结果写入 step_instance.result_json
不在 Phase 2 上发音模型、音素对齐、word timing(有则存 ASR 原始 JSON,评估器后补)。
归 Phase 3+ Task Definition,用 QUIZ/GAME + ASR 解析。**不要**为它们先改 Client。
assessment.model_version
同一 step 可追加新评估行,禁止覆盖旧结果
实时路径只做 L1;L2+ 走队列,避免卡住 TTS。
Phase 1:只增加 MCP 调用(已有 type=mcp)。Server 主动 tools/call 播放。
Phase 2:与 Client Plan 相同的 task.* / mode.set / 完成事件。Python TextMessageType 增加枚举,Handler 与 hello/listen/mcp 并列。
二进制 Opus 不改。MQTT 网关 16 字节头逻辑保持。
Phase 1:可无新 API(插件 + 管理端现有配置足够触发 Demo)。
Phase 2:
POST /task/instance 启动(deviceId, taskId, params)
GET /task/instance/{id} 查询状态与 steps
POST /agent/chat-history/report 扩展可选 task/step 字段(兼容旧客户端)
Phase 3:
/apps /resources /schedule /assessment /reports
V1.0 一次铺开 /analytics /developer —— 后置。
上报仍走现有 manage_api_client.report,避免再造一条聊天管道。
Server Plugin 编排、业务判断、调 ASR 结果
Device MCP 播放、音量、屏、下载(设备 Skill)
Server MCP 外部工具,与 Task 无强绑定
英语复读 Phase 1:Server Plugin 调 Device MCP。
不要把复读写进固件,也不要新建与 UnifiedToolHandler 平行的第二套工具总线。
| 已有能力 | 归属 |
|---|---|
| 课程 JSON / manifest | Phase 3 App Resource;权威迁入 manager-api |
| MQTT DOWNLOAD / PLAY_MEDIA | Client 板级已实现;Server 最终经 Xiaozhi 连接下发同等语义 |
| 场景识别 HTTP | 不进入 Xiaozhi 主链路,除非产品明确要 |
禁止让 Xiaozhi Server 与 smartlearning 长期双写同一设备状态。Phase 1 Demo 若只能跑 Sening,允许暂时打 snlp,但 Coding Plan 必须标明技术债。
与 Client Plan 编号一致。
ConnectionHandler.interaction_mode:CHAT | PLAY_AUDIOchat()、不把课程当用户意图PLAY_MEDIA(或等价)验收:管理端或一句触发可让设备复读;同时唤醒聊天与现网一致。
task_definition / task_instance / step_instancetask.* + mode.set(若 MCP 不够表达步进)验收:一次跟读 Instance 可查询到各 step 的音频与文本与分数。
play_music 冒充课程复读ai_agent_chat_history 当正式模型ConnectionHandler 就新写一套对话服务器connection.py、receiveAudioHandle.py、reportHandle.py、Agent 上报 API、现有表结构。Server 保证:
Server 要求 Client:
统一验收链(目标态):
Server 创建 Instance → mode + skill → Device 执行 → Event/Audio → ASR → Assessment → 查询 Instance
Phase 1 只验证前半:创建意图 → play 循环 → Chat 仍可用。
下一步不是实现评估平台,而是产出:
Phase 1 Coding Plan
├── Server:mode 旁路 + 复读插件
├── Client:复用 MCP/PLAY_MEDIA + 音频互斥(已有 PauseSdCardMp3)
└── 回归:现有 Chat E2E
V1.0 中的家长报告分级、跨任务分析、开发者分成等,保留为 Phase 3 以后的产品附录,不进入近期架构施工图。