Commit 824a0e6d authored by yaoke.yk's avatar yaoke.yk

docs: add agent storyboard enrichment design

parent d0522a83
# Agent 分镜增强设计
## 背景
当前 Agent 制作流程在 `storyboard_gen` 阶段会批量生成分镜,并把以下信息落到 `storyboards` 表:
- `short_description`
- `detailed_description`
- `characters`
- `start_frame_prompt`
- `motion_script`
但实际使用中仍有两个明显缺口:
1. `characters` 里混杂了角色引用和场景引用,前端无法稳定区分“出镜角色”和“分镜场景”。
2. 视频生成提示词目前依赖单条接口 `POST /projects/{projectId}/storyboards/{storyboardId}/generate-prompt` 临时生成,Agent 跑完后并不会把这部分结果落库,因此 Agent 页面和分镜页都不能把“完整可用”的分镜成果直接展示出来。
本次设计目标是让 Agent 制作完成后,每条分镜都已经具备:
- 分镜描述
- 出镜角色
- 分镜场景
- 首帧提示词
- 镜头运动描述
- 视频生成提示词
## 目标
### 业务目标
- Agent 完成后,分镜结果不再只有“001 + 分镜描述”,而是可直接用于审核和后续视频生成的完整产物。
- 分镜场景从混杂字符串中拆出来,变成明确字段。
- 视频生成提示词自动生成并持久化,不再要求用户逐条手动点“生成提示词”。
### 非目标
- 不在本次改造中重做整个分镜编辑器。
- 不把 `storyboards` 改造成全量结构化 JSON 模型。
- 不改现有视频生成主流程的入参协议,只补充分镜默认可用数据。
## 方案对比
### 方案 A:只改前端展示
直接把现有 `characters / startFramePrompt / motionScript` 展示出来。
优点:
- 改动最小
缺点:
- 场景仍然混在 `characters`
- 没有持久化 `videoPrompt`
- Agent 结果仍不完整
### 方案 B:中等改版,新增明确字段并在 Agent 内补全
保留现有分镜字段,新增 `scene_ref``video_prompt`,Agent 在生成完基础分镜后自动补齐视频提示词并落库。
优点:
- 兼容旧数据
- 字段语义清晰
- 可复用现有 `generatePrompt` 能力
- Agent 结果直接可用
缺点:
- Agent 总耗时会增加
- 需要一次数据库迁移和一轮批量补全逻辑
### 方案 C:大改为全结构化分镜模型
把分镜彻底改成 `characterRefs[] / sceneRef / imagePrompt / videoPrompt / dialogueSegments[]` 等结构。
优点:
- 长期最清晰
缺点:
- 改动范围过大
- 需要同时改 API、前端编辑器、导入导出与视频生成链路
### 结论
采用方案 B。
## 核心设计
### 1. 数据模型
`storyboards` 表新增两个字段:
- `scene_ref VARCHAR(128) NULL`
- 存单个场景引用,格式统一为 `@场景名`
- 仅表示该分镜主场景
- `video_prompt LONGTEXT NULL`
- 存分镜最终视频生成提示词
保留原字段:
- `characters`
- `start_frame_prompt`
- `motion_script`
其中 `characters` 在本次改造后只表达“出镜角色引用”,不再混入场景引用。旧数据继续兼容,读取时允许为空或保留旧格式。
### 2. 分镜生成输出
更新 `StoryboardPipelineServiceImpl.generateStoryboards(...)` 的大模型输出 schema,要求模型返回:
- `characters`
- 仅角色引用,格式 `@角色A,@角色B`
- `scene_ref`
- 单个场景引用,格式 `@咖啡馆`
- `start_frame_prompt`
- `motion_script`
- 现有分镜描述字段
同时保留后端兜底:
- 若模型未返回 `scene_ref`,则从旧格式 `characters` 中按项目场景名单提取第一个场景引用写入 `scene_ref`
- 若模型仍把场景混入 `characters`,则在服务端拆分并清洗,最终只把角色引用落入 `characters`
这样既提升新数据质量,也兼容旧 prompt 风格和非理想输出。
### 3. 视频提示词持久化
复用现有 `StoryboardPipelineService.generatePrompt(Long storyboardId, Long tenantId)`,但新增一层“生成并保存”能力:
- 新增服务方法,例如:
- `populateVideoPrompt(Long storyboardId, Long tenantId)`
- `populateVideoPrompts(List<Long> storyboardIds, Long tenantId)`
行为:
1. 调用现有提示词生成逻辑
2. 将结果写入 `storyboards.video_prompt`
3. 返回更新后的分镜对象或提示词内容
为了避免重复维护 prompt 模板,本次不新写第二套视频提示词生成 prompt,而是直接复用现有 `generatePrompt` 主逻辑。
### 4. Agent 流程调整
保持 Agent 现有步骤数量不变,仍然保留 `storyboard_gen` 这一步,对前端步骤卡片和 SSE 事件名不做额外拆分。
`storyboard_gen` 内部流程改为两段:
1. 为每一集生成基础分镜
2. 对该集刚生成的分镜批量补全 `video_prompt`
建议每集内按“生成分镜 -> 生成该集视频提示词”的顺序执行,而不是全项目先生成完分镜再统一补 prompt。理由:
- 更利于日志输出和进度理解
- 单集失败时影响范围更小
- 方便未来扩展为“按集重跑”
`storyboard_gen` 结束时,产出应包含:
- 总分镜数
- 成功写入 `video_prompt` 的数量
SSE 日志建议新增类似文案:
- `第 1 集分镜:12 个镜头`
- `第 1 集视频提示词:12 条已生成`
### 5. API 与 DTO
扩展 `StoryboardDTO` 与前端 `Storyboard` 类型,新增:
- `sceneRef: string | null`
- `videoPrompt: string | null`
现有接口不新增新路由,直接在已有分镜列表/详情返回中带出新字段:
- `GET /projects/{projectId}/episodes/{episodeId}/storyboards`
- `GET /projects/{projectId}/storyboards`
- `POST /projects/{projectId}/episodes/{episodeId}/storyboards/generate`
- `PUT /projects/{projectId}/storyboards/{storyboardId}`
保留现有单条 `generate-prompt` 接口,但语义改成:
- 生成最新提示词
- 同步回写 `storyboards.video_prompt`
- 返回 `prompt`
这样前端无论走 Agent 自动生成,还是手动“重新生成提示词”,结果都会统一落库。
### 6. 前端展示
本次只做“补足信息展示”,不重做页面结构。
#### AgentStudio
当前 `AgentStudio` 主要展示步骤状态和日志。本次不要求在 Agent 页面直接展开全部分镜详情,仅要求:
- `storyboard_gen``output` 文案增加完整产出摘要
- 让用户知道已生成分镜数量与视频提示词数量
#### StoryboardWorkspace
在已有分镜工作台中为每条分镜补充可见信息:
- 出镜角色
- 分镜场景
- 首帧提示词
- 镜头运动描述
- 视频生成提示词
展示优先级:
1. `sceneRef`
2. `videoPrompt`
3. 现有 `characters / startFramePrompt / motionScript`
当旧数据没有 `sceneRef` / `videoPrompt` 时:
- `sceneRef` 可尝试从旧 `characters` 做只读推断展示
- `videoPrompt` 为空时显示“未生成”
### 7. 兼容与迁移
#### 新数据
新跑的 Agent 和新生成的分镜应直接落:
- `characters` 仅角色
- `scene_ref`
- `video_prompt`
#### 旧数据
旧分镜可能存在:
- `characters` 同时含角色和场景
- `video_prompt` 为空
兼容策略:
- 不做全库一次性回填迁移,避免首次部署过重
- 旧数据在重新生成分镜或手动重新生成提示词时逐步转正
- 前端读取时允许空字段并做柔性展示
#### 手动编辑
若用户手动更新分镜内容后再次点击“生成提示词”,后端应重新生成并覆盖 `video_prompt`,保持结果与当前分镜内容一致。
## 失败处理
### 分镜生成成功,视频提示词补全失败
不回滚已生成的分镜记录。
处理方式:
- `storyboard_gen` 步骤视为失败
- 日志写清楚是“分镜已生成,但视频提示词补全失败”
- 已落库的分镜可继续在工作台查看
- `video_prompt` 为空的分镜允许后续单条重试
这是比强事务回滚更合适的行为,因为分镜本身仍然是有价值产物。
### 单条提示词生成失败
建议默认整步失败,但日志里包含:
- 失败分镜 ID / 场景号
- 已成功条数 / 总条数
后续如有需要,可扩展为“容忍单条失败并记录失败列表”,但这不作为本次必做范围。
## 测试策略
### 后端
- `StoryboardPipelineServiceImpl`
- 新 schema 正常解析 `scene_ref`
- 旧格式 `characters` 能拆分出角色与场景
- `generatePrompt` 回写 `video_prompt`
- `AgentRunServiceImpl`
- `storyboard_gen` 完成后,分镜已持久化 `video_prompt`
- 任一提示词生成失败时,步骤状态正确标记为失败
- DTO/Mapper:
- 新字段能正确出入参
### 数据库迁移
- Flyway 迁移可重复执行
- 老库升级后,旧查询与插入不报错
### 前端
- 分镜列表能显示 `sceneRef`
- `videoPrompt` 有值时正确展示
- `videoPrompt` 为空时展示兜底态
- 旧数据不崩溃
## 实施边界
本次实现只覆盖以下内容:
- 数据库字段新增
- 后端分镜生成与提示词落库
- Agent `storyboard_gen` 内部补全逻辑
- 分镜工作台展示新增字段
以下内容明确不在本次范围:
- 新增 Agent 独立步骤卡片,例如 `storyboard_prompt_gen`
- 全量旧数据离线回填脚本
- 分镜编辑器的深度结构化改造
- 视频生成页面的大规模交互重构
## 预期结果
完成后,Agent 制作跑完后,用户进入分镜页应能直接看到:
- `001 / 002 / ...`
- 分镜描述
- 出镜角色
- 分镜场景
- 首帧提示词
- 镜头运动描述
- 视频生成提示词
同时,后续视频生成默认可以直接复用已持久化的 `video_prompt`,不再依赖“临时现生提示词”的操作链路。
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment