Files
dreamweaver/docs/technical/voice-co-creation-phase-a-tech-spec.md

15 KiB
Raw Blame History

技术方案:语音共创 Phase A MVP

Version: 0.1
Date: 2026-04-19
Status: Draft / Companion to voice-co-creation-mode-incremental-prd.md


1. 目标

这份技术方案用于把 语音共创模式增量 PRD 收敛成一个可实现的 Phase A MVP。

0. 当前实现快照2026-04-20

远端 main 已经跑通以下 Phase A 主链路:

  • 独立 Voice Studio 入口页与最近会话恢复
  • voice_sessions / voice_turns / voice_session_events 数据模型与 API
  • 文本 fallback 回合、录音上传回合、turn 轮询结果查询
  • turn 级语音补发、失败重试、会话 abandon、finalize -> Story 持久化
  • 会话事件记录与最近 turn / 最近事件展示

本轮新增收束:

  • transcript_confidenceintent_confidence 偏低时,后端优先返回确认提示,而不是直接把这一轮写进故事正文
  • 已补完整确认流:支持“按这个理解继续”“重说本轮”“改成文本输入”
  • 前端明确展示“本轮系统理解为”与“建议家长确认后再继续”提示
  • 低置信度确认链路已有后端测试覆盖,可作为下一阶段继续接 ASR 与更细确认交互的基础
  • 已新增用户转写安全检查、assistant 输出柔性改写与 safety_flags 事件记录
  • finalize 会生成更稳定的标题/摘要,并在条件允许时自动排队封面补全 job
  • 已新增 voice session analytics 聚合指标,可跟踪 turn 成功率、ASR/TTS 失败、低置信度触发和 finalize 转化率
  • voice session finalize 现在会返回可追踪的 generation_job_id,让正式 Story 资产补全重新接回现有 generation trace 主干

Phase A 的核心目标不是做“完全实时的语音陪伴”,而是验证以下最小闭环:

  1. 孩子可以用语音发起一个故事
  2. 孩子可以在中途用语音修正故事走向
  3. 系统能用语音回应并继续讲述
  4. 会话结束后可以保存为正式故事,进入现有故事库

这一步必须尽量复用现有统一生成工作流、Profile、Universe、Memory、TTS 与故事持久化主干,而不是重新搭一套平行系统。


2. 非目标

Phase A 明确不做以下内容:

  • 不做 WebRTC / 全双工实时通话
  • 不做复杂 barge-in系统说到一半被自然打断
  • 不做多人共创
  • 不做绘本共创主链路
  • 不做每回合即时插图生成
  • 不把 ASR / Realtime 能力立刻并入当前 admin Provider 配置面板

换句话说Phase A 是一个 回合式 voice session MVP,不是最终形态。


3. 推荐体验形态

3.1 交互模式

  • 前端提供独立“语音共创”入口
  • 用户按住说话或点击开始录音
  • 一次提交一段语音
  • 后端完成:转写 -> 意图识别 -> 更新故事状态 -> 生成回复 -> 合成语音
  • 前端轮询或 SSE 获取这一轮结果
  • 播放系统语音并展示文本摘要
  • 进入下一轮

3.2 为什么先做回合式

  • 与现有 generation_job + 轮询模式更一致
  • 更容易做会话恢复和状态排障
  • 对延迟要求低于全双工实时
  • 能先验证“孩子是否愿意用语音改故事”

4. 与现有系统的关系

4.1 可以直接复用的能力

  • users
  • child_profiles
  • story_universes
  • build_enhanced_memory_context
  • stories
  • generation_jobs / generation_job_events
  • text 能力对应的现有文本模型
  • tts Provider Router
  • 现有故事库、故事详情页和后续资产补全链路

4.2 当前明显缺失的能力

  • 语音输入识别ASR / STT
  • 会话级状态模型
  • “剧情修正”语义解析
  • 会话级可观测事件
  • 从 voice session 保存为正式 Story 的收束服务

5. 核心设计原则

  1. 会话先于结果
    语音共创首先是一个 session不是一次性 generation request。

  2. 最终结果仍然落到 Story
    voice session 是过程对象Story 才是最终可回看、可补资产、可沉淀记忆的正式对象。

  3. 过程状态与结果状态分离
    voice_sessions 管过程,stories 管正式结果,避免把会话噪音直接污染正式故事结构。

  4. 先复用 text / tts 主干,再决定是否拆新 capability
    首版把复杂度压到最小,不急着把所有新能力都映射进 admin Provider 面板。

  5. 首版允许“文本可用但语音失败”降级
    这与当前 DreamWeaver 主结果优先可读的原则一致。


6. 数据模型建议

6.1 voice_sessions

用于承载一个完整的语音共创会话。

建议字段

  • id: str
  • user_id: str
  • child_profile_id: str | None
  • universe_id: str | None
  • status: str
  • target_mode: str
    Phase A 固定为 story
  • current_turn_index: int
  • working_title: str | None
  • story_state: dict
  • latest_user_transcript: str | None
  • latest_assistant_text: str | None
  • final_story_id: int | None
  • last_error: str | None
  • created_at
  • updated_at

story_state 建议结构

{
  "premise": "小猫去太空",
  "characters": ["小猫", "月亮朋友"],
  "tone": "温暖冒险",
  "beats": [
    "准备出发",
    "遇到困难",
    "得到帮助"
  ],
  "narrative_segments": [
    "第一段故事",
    "第二段故事"
  ],
  "latest_direction": "不要让它哭,给它一个朋友",
  "safety_flags": []
}

6.2 voice_turns

用于记录会话中的每一轮输入与响应。

建议字段

  • id: str
  • session_id: str
  • turn_index: int
  • status: str
  • user_audio_path: str | None
  • user_transcript: str | None
  • transcript_confidence: float | None
  • detected_intent: str
  • intent_confidence: float | None
  • story_patch: dict
  • assistant_text: str | None
  • assistant_audio_path: str | None
  • assistant_duration_ms: int | None
  • error_message: str | None
  • created_at
  • updated_at

detected_intent 首版建议值

  • start_story
  • continue_story
  • correct_story
  • end_story
  • save_story
  • unknown

6.3 voice_session_events

建议新增轻量 append-only 事件表,而不是强行复用 generation_job_events

原因很简单:

  • generation_job_events 的主语是“后台生成任务”
  • voice_session_events 的主语是“语音会话过程”

二者关注点不同,不应混在一个表里。

建议字段

  • id: int
  • session_id: str
  • turn_id: str | None
  • event_type: str
  • status: str
  • message: str | None
  • event_metadata: dict
  • created_at

6.4 为什么首版不单独建 voice_story_snapshots

首版先把故事中间态压缩进 voice_sessions.story_statevoice_turns.story_patch 即可。

如果后续需要:

  • 可回滚到任意 turn
  • 支持剧情分支
  • 做更复杂的 diff / merge

再考虑独立 voice_story_snapshots 表。


7. 状态机建议

7.1 Session 状态

建议值

  • draft
  • active
  • processing_turn
  • waiting_user
  • finalizing_story
  • completed
  • abandoned
  • failed

状态含义

  • draft: 会话已创建但还没有第一轮输入
  • active: 已进入共创流程
  • processing_turn: 正在处理某一轮语音
  • waiting_user: 系统已经返回,等待下一轮输入
  • finalizing_story: 正在把会话收束为正式 Story
  • completed: 已完成并成功保存
  • abandoned: 用户主动结束但未保存
  • failed: 当前会话不可继续,需要用户重新开始

7.2 Turn 状态

建议值

  • received
  • transcribing
  • intent_resolved
  • narrative_ready
  • audio_ready
  • failed

8. API 草图

8.1 创建会话

POST /api/voice-sessions

Request

{
  "child_profile_id": "profile-id",
  "universe_id": "universe-id",
  "target_mode": "story"
}

Response

{
  "id": "session-id",
  "status": "draft",
  "target_mode": "story",
  "current_turn_index": 0,
  "final_story_id": null
}

8.2 查询会话详情

GET /api/voice-sessions/{session_id}

返回:

  • session 基础信息
  • 当前 story_state
  • 最近若干 turn
  • 是否可继续
  • 是否可保存

8.3 提交一轮语音输入

POST /api/voice-sessions/{session_id}/turns

建议首版使用 multipart/form-data

  • audio_file
  • duration_ms
  • client_turn_id(可选)

如果后续为了调试和降级,也可以兼容:

  • transcript_text

Response

{
  "turn_id": "turn-id",
  "session_id": "session-id",
  "status": "received"
}

后续由前端轮询 turn 或 session detail。

8.4 查询 turn 结果

GET /api/voice-sessions/{session_id}/turns/{turn_id}

返回:

  • user_transcript
  • detected_intent
  • assistant_text
  • assistant_audio_url 或音频状态
  • status

8.5 结束并保存为正式故事

POST /api/voice-sessions/{session_id}/finalize

Request

{
  "save_story": true,
  "generate_cover": true,
  "generate_final_audio": false
}

Response

{
  "session_id": "session-id",
  "status": "completed",
  "story_id": 123,
  "generation_job_id": "optional-asset-job-id"
}

8.6 放弃会话

POST /api/voice-sessions/{session_id}/abandon

用于显式结束但不保存。


9. 后端处理链路

9.1 一轮 turn 的推荐处理顺序

  1. 接收语音文件
  2. 创建 voice_turns 记录,状态为 received
  3. 保存原始音频文件到会话目录
  4. 调用 ASR更新 user_transcript
  5. 调用意图解析 / 对话编排器,得到:
    • detected_intent
    • story_patch
    • assistant_text
  6. story_patch 合并进 voice_sessions.story_state
  7. 调用 TTS 生成本轮系统回应语音
  8. 更新 turn 为 audio_readynarrative_ready
  9. 更新 session 为 waiting_user

9.2 为什么首版不直接放进 Celery worker 主链路

当前 generation_jobs 很适合“提交一个任务,然后后台处理较长流程”,但语音共创 turn 的特点是:

  • 频率更高
  • 单轮更短
  • 更接近交互而不是离线任务

因此首版建议:

  • turn 处理由应用层直接编排
  • 必要时用短后台任务或轻量异步任务承接
  • 只有最终“保存为正式故事 + 补资产”再接回现有 generation 主干

9.3 最终收束为 Story 的方式

当用户选择 finalize 时:

  1. story_state.narrative_segments 合并为最终正文
  2. 生成标题和摘要
  3. 写入 stories
  4. 若需要封面或正式整篇朗读音频,再触发已有资产补全链路

首版建议不要让 finalize 再走完整“文本生成主任务”,因为正文已经在会话中逐步生成出来了。


10. Provider 与模型接入建议

10.1 Phase A 的最小能力分层

A. ASR

新增会话内语音转写能力。

建议

  • Phase A 先接单一稳定供应商
  • 暂不并入当前 admin Provider CRUD
  • 先通过配置文件或单独 service 封装

理由是:

  • 当前 admin Provider 只有 text/image/tts/storybook
  • 如果一开始把 asr 也并进全套管理能力,改动面会大很多

B. Dialogue Orchestrator

首版建议直接复用当前 text capability而不是另开一套 provider type。

对 orchestrator 来说,我们更需要:

  • 意图识别稳定
  • 遵循结构化输出
  • 能基于已有 story_state 生成下一段文本

这本质上仍然可以由现有文本模型承担。

C. TTS

直接复用当前 tts Provider Router。

D. Final Story Asset Generation

继续复用已有 story asset completion 逻辑。


11. 文件与存储建议

11.1 音频文件目录

建议新增目录:

storage/voice_sessions/<session_id>/

目录下可包括:

  • turn-001-user.webm
  • turn-001-assistant.mp3
  • turn-002-user.webm
  • turn-002-assistant.mp3

11.2 为什么不直接复用 story audio cache

因为二者语义不同:

  • story audio cache 是正式故事整篇或固定文本的可复用结果
  • voice session audio 是会话过程资产

不应混存。


12. 安全与家长控制

Phase A 至少要有以下机制:

  1. 儿童内容安全 prompt
  2. 转写后文本安全检查
  3. 低置信度识别提示
  4. 必要时家长可见的“系统理解为”

首版推荐策略

  • 默认不要求每轮家长确认
  • transcript_confidenceintent_confidence 低于阈值时,再提示确认
  • 如果内容越界,则给出柔性改写或安全拒绝

13. 可观测性建议

13.1 需要记录的关键事件

  • session_created
  • turn_received
  • turn_transcribed
  • intent_resolved
  • story_state_updated
  • assistant_text_ready
  • assistant_audio_ready
  • session_finalizing
  • session_saved_as_story
  • session_failed

13.2 成本记录建议

Phase A 就应该按 turn 记录:

  • ASR 成本
  • 对话生成成本
  • TTS 成本

这部分后续可以汇总到新的语音共创 analytics而不是一开始就挤进现有故事生成 dashboard。


14. 前端建议

14.1 Phase A 最小 UI

  • 独立入口页或 modal
  • 录音按钮
  • 当前会话卡片
  • 本轮识别文本
  • 系统文本回应
  • 系统音频播放按钮
  • “继续说”按钮
  • “保存到故事库”按钮

14.2 状态反馈必须有

至少显示:

  • 正在转写
  • 正在理解故事走向
  • 正在讲述
  • 本轮失败,可重试

14.3 恢复策略

页面重新打开后:

  • 如果存在 active / waiting_user session优先恢复最近会话
  • 展示最近一轮系统回应
  • 允许继续说或结束保存

15. 推荐实现顺序

Step 1

  • 新增 voice_sessions
  • 新增 voice_turns
  • 新增 voice_session_events
  • 新增基础 schema

Step 2

  • 新增会话 API
  • 新增 turn API
  • 打通音频文件存储

Step 3

  • 接入 ASR
  • 实现 turn 编排 service
  • 复用现有 texttts 能力

Step 4

  • 实现 finalize -> Story 持久化
  • 接入现有故事库入口

Step 5

  • 补最小 UI
  • 补会话恢复
  • 补日志和成本记录

16. 测试建议

单元测试

  • story_patch 合并逻辑
  • 意图映射逻辑
  • finalize 正文拼接逻辑

API 集成测试

  • 创建会话
  • 提交 turn
  • 查询 turn
  • 恢复会话
  • finalize 为 Story

失败场景测试

  • ASR 失败
  • TTS 失败但文本成功
  • 低置信度输入
  • finalize 重复提交

17. 当前最合理的技术判断

如果目标是“尽快验证孩子能否通过声音共创故事”,那么最合理的 Phase A 路线是:

  1. 新增 voice session 层,而不是修改现有 generation_jobs 去硬扛会话语义
  2. 复用现有 texttts 主干,不急着把所有新能力都产品化成新的 Provider 管理界面
  3. 先做回合式体验,不做复杂实时双工
  4. 最终结果仍然沉淀为 Story,继续接入已有故事库、资产补全和记忆系统

这条路线的优点是:

  • 架构增量最小
  • 与当前 DreamWeaver 主线一致
  • 既能验证产品价值,也不会过早引入过重的实时系统复杂度

18. 下一步建议

如果这份技术方案方向确认无误,下一轮最值得继续做的是两件事之一:

  1. voice_sessions / voice_turns / voice_session_events 进一步细化成数据库迁移草案
  2. 把 Phase A 的 API request/response schema 写成后端伪代码或 OpenAPI 草图

相比直接写实现代码,这两步能更稳地把范围钉住。