# Generation Job 状态落库决策 这份文档用于回答一个关键技术债问题:DreamWeaver 是否需要为每次 AI 生成单独建立 `generation_jobs` 表。 ## 当前结论 已新增轻量 `generation_jobs` 与 `generation_job_events` 表,但不引入复杂工作流引擎。 原因是当前 MVP 已经进入“请求接收与后台执行分离”的阶段:`POST /api/generations` 先接受请求并返回 `generation_job_id`,再由 Celery worker 完成主内容保存和后续资产补全。用户最关心的是“这个故事现在能不能读、任务跑到哪一步、哪些资产可补全”;系统侧则需要有足够的轨迹说明“这次生成做到了哪一步、哪里失败、是否被取消、哪些资产还能重试”。 因此当前采用轻量落库策略: - `stories` 继续承载用户可见结果和当前状态。 - `generation_jobs` 记录一次生成或资产补全尝试。 - `generation_job_events` 记录关键步骤事件,例如 `request_accepted`、`context_prepared`、`narrative_generated`、`story_saved`、`cover_image_succeeded`、`storybook_page_image_succeeded`、`audio_succeeded`、`provider_call_succeeded`、`asset_retry_completed`。 当前已提供三个查询入口: - `GET /api/generations/jobs/{job_id}`:查询单次生成/补全任务及其事件流。 - `GET /api/generations/{story_id}/jobs`:查询某个故事或绘本的生成与重试历史。 - `GET /api/generations/{story_id}/provider-stats`:按故事聚合 Provider 调用成功率、平均耗时、预估成本和 adapter 明细。 - `GET /api/generations/provider-analytics`:按当前用户聚合跨故事 Provider 调用、任务数、故事数、成功率、平均耗时和预估成本,并支持 `days` / `capability` 筛选。 - `GET /api/generations/ops-summary`:按当前用户聚合最近任务健康度,包括运行中数量、超时阈值、卡住任务数和最近失败摘要。 job 响应会返回 `progress_percent`、`progress_label` 和 `is_terminal`,用户端与管理端已经消费这些查询入口,在故事详情页和绘本阅读页展示最近任务、任务历史、事件时间线、进度条和 Provider 聚合指标;当任务未终止时,前端会自动轮询,为后台 worker 进度流预留体验形态。当前 analytics 还会聚合失败原因,便于快速解释“最近为什么失败”;ops summary 会额外把“哪些任务卡住了、最近哪些任务失败了”压缩成故事库首页能直接看的摘要。 ## 现有状态模型 当前 `stories` 表已承载演示所需状态: - `generation_status`: 主流程状态,例如 `narrative_ready`、`partial_ready`、`assets_generating`、`completed`、`degraded_completed`、`failed` - `text_status`: 主文本或绘本结构状态,当前用于区分主内容是否已经可读 - `image_status`: 封面或绘本插图状态 - `audio_status`: 语音状态 - `last_error`: 最近一次资产失败原因 `partial_ready` 表示主内容已经可读、但仍有封面、插图或音频可以继续补全;`degraded_completed` 表示主内容可读但至少一个资产失败。两者的区分能让前端把“正常待补全”和“需要重试失败资源”分开展示。 这些字段足够支撑前端展示、smoke 检查、失败降级、资产重试和生成轨迹解释。 ## 当前维护策略 - 音频缓存由 `STORY_AUDIO_CACHE_TTL_DAYS` 控制过期时间,Celery beat 会每日清理。 - 生成任务由 `GENERATION_JOB_STALE_MINUTES` 控制卡住阈值,Celery beat 会每 30 分钟扫描一次,将超时运行中的任务标记为 `generation_stale_failed`。 - 当某个故事已经有运行中的 job 时,封面补全、音频生成和统一资产重试会直接拒绝重复请求,避免用户连点造成重复成本。 - 统一生成请求已由 Celery worker 执行,前端会先拿到 `generation_job_id`,再轮询 job 详情直到主记录落库或任务终止。 - 当前已支持首版任务控制:队列中的任务可直接取消,运行中的任务可在安全检查点取消,失败或已取消任务可重新排队。 ## 为什么当前仍然需要扩展 job 模型 虽然 worker 化已经完成,但如果继续进入真实生产化,仍然需要扩展当前 job/event 模型: - 单个故事会产生多次生成尝试,需要对比每次任务的 provider 表现、取消原因、重试原因和资产结果。 - 需要展示比当前事件更细颗粒度的步骤,例如 prompt 构建、provider 选择依据、provider failover 原因、每次调用 token/图片/语音成本。 - 需要按 provider、成本、延迟和失败原因做运营分析。 - 需要继续扩展取消与重试队列的颗粒度,例如更细的中断点、任务依赖和断点续跑策略。 - 需要断点续跑,避免 Worker 重启后丢失中间状态。 ## 推荐未来扩展 当前已有两层记录,未来可以继续扩展字段和事件颗粒度: - 继续复用现有 job 查询和前端轮询进度条,为取消请求、重新排队和长任务通知提供统一入口。 - 当前环境的跨用户 provider dashboard 已在 admin 端落地,下一步应补跨环境汇聚和更细颗粒度的失败原因维度分析。 ## 面试表达 我没有一上来引入复杂工作流引擎,而是先用轻量 job/event 表把关键执行轨迹落下来。这样既能回答“生成过程是否可追踪”,又不会为了求职版 MVP 牺牲主链路稳定性。