Implement unified story generation flow

This commit is contained in:
2026-06-18 14:48:27 +08:00
parent 0ccfd00a23
commit 7ebdfb2582
27 changed files with 1323 additions and 215 deletions

View File

@@ -0,0 +1,54 @@
# 环境变量配置约定
DreamWeaver 只把 `backend/.env` 视为应用运行配置文件。根目录 `.env` 可以存在,但它只服务 Docker Compose 本身,不参与后端配置加载。
## 文件职责
| 文件 | 读取方 | 放什么 | 不放什么 |
| --- | --- | --- | --- |
| `backend/.env` | FastAPI、Admin API、Celery worker、Celery beat、Docker 后端服务 | `SECRET_KEY``DATABASE_URL`、Redis/Celery URL、Provider 列表、AI key、OAuth key、Admin 账号 | Docker 镜像源、npm registry |
| `.env` | Docker Compose 插值 | `PYTHON_BASE_IMAGE``NODE_BASE_IMAGE``NGINX_BASE_IMAGE``NPM_REGISTRY` 等镜像源/registry 覆盖 | AI key、OAuth key、`SECRET_KEY`、后端运行配置 |
| `backend/.env.example` | 人读/复制模板 | `backend/.env` 的完整示例 | 真实密钥 |
## 为什么不让后端读取根目录 `.env`
`pydantic-settings` 的相对 `env_file=".env"` 会受当前工作目录影响:在仓库根目录启动会读根 `.env`,在 `backend/` 目录启动会读 `backend/.env`。这会导致同一条启动命令在不同目录下使用不同配置。
当前代码在 `backend/app/core/config.py` 中固定使用绝对路径 `backend/.env`。因此后端从任意工作目录启动时都读取同一个文件。
## Docker 演示
Docker 后端服务通过 `env_file: ./backend/.env` 读取应用配置。默认容器内地址应保持为服务名:
```env
DATABASE_URL=postgresql+asyncpg://dreamweaver:dreamweaver_password@db:5432/dreamweaver_db
CELERY_BROKER_URL=redis://redis:6379/0
CELERY_RESULT_BACKEND=redis://redis:6379/0
REDIS_URL=redis://redis:6379/0
```
Postgres 容器只接收 `docker-compose.yml` 中固定的 demo 账号和数据库名,避免把 AI/OAuth key 注入基础设施容器。后端服务读取 `backend/.env` 中的 `DATABASE_URL`。需要改 Docker demo 的数据库账号时,同时修改 `docker-compose.yml``db.environment``backend/.env``DATABASE_URL`。Docker demo 固定暴露 `52432:5432``52379:6379`,本机直跑后端时按这些宿主机端口连接。
## 本机直跑后端
本机直接运行 `uvicorn``celery``alembic` 时也只改 `backend/.env`,把数据库和 Redis URL 改成宿主机端口:
```env
DATABASE_URL=postgresql+asyncpg://dreamweaver:dreamweaver_password@localhost:52432/dreamweaver_db
CELERY_BROKER_URL=redis://localhost:52379/0
CELERY_RESULT_BACKEND=redis://localhost:52379/0
REDIS_URL=redis://localhost:52379/0
```
## 检查命令
```bash
# 后端实际读取哪个 env 文件
backend/.venv/bin/python - <<'PY'
from app.core.config import BACKEND_ENV_FILE
print(BACKEND_ENV_FILE)
PY
# Docker 后端容器实际环境,注意不要把输出贴到公共渠道
docker compose exec backend env | sort
```

View File

@@ -25,7 +25,7 @@
-`transcript_confidence``intent_confidence` 偏低时,后端优先返回确认提示,而不是直接把这一轮写进故事正文
- 已补完整确认流:支持“按这个理解继续”“重说本轮”“改成文本输入”
- 前端明确展示“本轮系统理解为”与“建议家长确认后再继续”提示
- 低置信度确认链路已有后端测试覆盖,可作为下一阶段继续 ASR 与更细确认交互的基础
- 低置信度确认链路已有后端测试覆盖,可作为下一阶段继续验收真实 ASR Key 环境与更细确认交互的基础
- 已新增用户转写安全检查、assistant 输出柔性改写与 `safety_flags` 事件记录
- finalize 会生成更稳定的标题/摘要,并在条件允许时自动排队封面补全 job
- 已新增 `voice session analytics` 聚合指标,可跟踪 turn 成功率、ASR/TTS 失败、低置信度触发、finalize 转化率、输入构成、语音时长、Provider 分布、确认率和平均置信度,并支持按转写 Provider 与会话状态筛选
@@ -52,7 +52,7 @@ Phase A 明确不做以下内容:
- 不做多人共创
- 不做绘本共创主链路
- 不做每回合即时插图生成
- 不把 ASR / Realtime 能力立刻并入当前 admin Provider 配置面板
- 不把 Realtime 能力立刻并入当前 admin Provider 配置面板ASR 已作为 Alpha 运营观测能力进入 Provider 体系
换句话说Phase A 是一个 **回合式 voice session MVP**,不是最终形态。
@@ -93,13 +93,13 @@ Phase A 明确不做以下内容:
- `tts` Provider Router
- 现有故事库、故事详情页和后续资产补全链路
### 4.2 当前明显缺失的能力
### 4.2 初始设计时明显缺失、Alpha 已补齐的能力
- 语音输入识别ASR / STT
- 会话级状态模型
- “剧情修正”语义解析
- 会话级可观测事件
- 从 voice session 保存为正式 Story 的收束服务
- 语音输入识别ASR / STT:已通过 `asr` Provider capability、demo fallback 和 `openai_asr` 适配器补齐,真实 Key 环境仍需验收。
- 会话级状态模型:已落地 `voice_sessions / voice_turns / voice_session_events`
- “剧情修正”语义解析Alpha 已支持 start / continue / correct 等回合意图。
- 会话级可观测事件:已支持 voice session analytics、事件列表和管理端 ASR 摘要。
- 从 voice session 保存为正式 Story 的收束服务:已支持 finalize 保存为 Story并接回 generation job 资产补全。
---
@@ -115,7 +115,7 @@ Phase A 明确不做以下内容:
`voice_sessions` 管过程,`stories` 管正式结果,避免把会话噪音直接污染正式故事结构。
4. **先复用 `text` / `tts` 主干,再决定是否拆新 capability**
首版把复杂度压到最小,不急着把所有新能力映射进 admin Provider 面板。
首版把复杂度压到最小,不急着把 realtime / barge-in 等新能力映射进 admin Provider 面板。ASR 现在只作为回合式转写能力进入 Provider 体系。
5. **首版允许“文本可用但语音失败”降级**
这与当前 DreamWeaver 主结果优先可读的原则一致。
@@ -440,14 +440,29 @@ Phase A 明确不做以下内容:
**建议**
- Phase A 先接单一稳定供应商
- 暂不并入当前 admin Provider CRUD
- 先通过配置文件或单独 service 封装
- Phase A 先接单一稳定供应商,并保留 demo fallback
- 并入当前 admin Provider CRUD 和运营摘要,但不引入 realtime 复杂配置
- 先通过配置文件或单独 service 封装真实 Key 环境差异
- 真实 Key 验收用 `SMOKE_REAL_ASR=1 ./scripts/demo_smoke.sh`,只在显式打开时调用外部 ASR
理由是:
- 当前 admin Provider 已扩展到 `text/image/tts/storybook/asr`
- Phase A Alpha 已把 ASR 纳入最小 Provider 能力,但仍保留 demo fallback避免真实转写不可用时阻塞演示
- `openai_asr` 默认读取 `OPENAI_API_KEY`、可选 `OPENAI_API_BASE``VOICE_TRANSCRIPTION_MODEL``VOICE_TRANSCRIPTION_LANGUAGE`
真实 ASR 验收最小 `.env`
```env
ASR_PROVIDERS=["openai_asr", "demo"]
OPENAI_API_KEY=sk-...
OPENAI_API_BASE=
VOICE_TRANSCRIPTION_MODE=provider
VOICE_TRANSCRIPTION_MODEL=gpt-4o-mini-transcribe
VOICE_TRANSCRIPTION_LANGUAGE=zh
```
失败时优先看三处:上传接口响应、`turn_transcription_failed` 事件、Admin Provider analytics 的 `capability=asr` failure reasons。常见原因是 key 没进容器、401/403、429/额度不足、模型不可用、`OPENAI_API_BASE` 指向错误或音频格式不被接受。
### B. Dialogue Orchestrator
@@ -537,7 +552,36 @@ Phase A 就应该按 turn 记录:
- 对话生成成本
- TTS 成本
这部分后续可以汇总到新的语音共创 analytics而不是一开始就挤进现有故事生成 dashboard
当前 Alpha 已把 ASR 成本和调用摘要接入管理端 Provider analytics。短期这样可以让运营视角统一看到 text/image/tts/storybook/asr中期如果语音共创继续扩大应把 voice session analytics 保持为主视图,把 admin Provider analytics 只作为跨能力成本与失败原因摘要
### 13.3 服务复杂度自审2026-05-06
当前 Alpha 已经验证主链路,但服务边界开始接近需要拆分的程度:
| 模块 | 当前职责 | 复杂度信号 | 建议拆分 |
| --- | --- | --- | --- |
| `voice_session_service.py` | 会话 CRUD、turn 创建、意图识别、故事 patch、低置信度确认、安全改写、TTS、finalize、analytics | 文件已接近 2000 行同步处理状态机、AI 编排和响应序列化,单次改动容易波及多条路径 | 优先拆 `voice_turn_orchestrator.py``voice_session_analytics.py``voice_session_finalizer.py` |
| `generation_jobs.py` + `admin_provider_analytics.py` | generation job/event、任务控制、provider stats、ops summary管理端跨用户 Provider/ASR 摘要已拆到独立 service | `generation_jobs.py` 仍偏大,但 ASR 管理端摘要已不再继续塞进 generation job 模块 | 后续继续把 `generation_jobs.py` 内部 provider telemetry helper 拆为共享小模块,保留 generation job 主流程聚焦任务状态 |
| `voice_transcription_service.py` | ASR mode 解析与 provider router 调用 | 仍较小但失败元数据不足admin ASR 失败只能从事件里读 `error` | 后续补 `VoiceTranscriptionAttempt` 风格的轻量结果结构,统一 provider、latency、cost、error |
| 前端 `VoiceStudio.vue` | 页面状态、录音上传、会话列表、turn 展示、analytics 卡片、确认/重试/finalize | 视图文件承担了太多 workflow 判断;继续加实时能力会变得难测 | 拆出 `useVoiceSessionWorkflow``VoiceTurnCard``VoiceAnalyticsPanel` |
建议拆分顺序:
1. 先拆只读 analytics风险最低测试可以复用现有 `test_voice_sessions.py``test_admin_providers.py`。2026-05-06 已先拆出管理端 `admin_provider_analytics.py`
2. 再拆 finalize边界清晰输入是 session输出是 Story / generation job。
3. 最后拆 turn orchestrator它耦合 ASR、意图、故事 patch、安全和 TTS应等回归矩阵更稳定后再动。
暂不建议在 Phase A Alpha 末尾做的大改:
- 不引入工作流引擎替代当前状态机。
- 不把 voice session 直接塞进 generation job 主模型。
- 不在 ASR 事件上新增迁移字段,除非要做精确延迟分布和供应商级 SLA。
触发必须拆分的信号:
- 单个 voice turn 改动需要同时修改 3 个以上测试文件。
- 新增一个 analytics 字段需要读写多个无关 service。
- Voice Studio 引入实时或准实时能力前,仍没有可复用 composable。
---