# Changelog: 剧本解析自动创建分镜对白记录 **日期**: 2026-02-09 **类型**: Feature Enhancement **影响范围**: 剧本解析功能 **关联**: 2026-02-09-screenplay-ai-parse-format-fix.md --- ## 功能描述 在剧本 AI 解析过程中,自动将 `storyboards[].dialogue` 数据写入 `storyboard_dialogues` 表,实现分镜对白的自动化管理。 --- ## 背景 ### 现状 - AI 解析剧本时会返回 `storyboards` 数组,每个分镜包含 `dialogue` 字段 - 之前的实现仅将 `dialogue` 存储在 `storyboards.meta_data` 中 - 前端需要从 `meta_data` 中提取对白数据,不够规范 ### 问题 1. **数据冗余**: 对白数据存储在 `meta_data` 中,不利于查询和管理 2. **缺少结构化**: 无法利用 `storyboard_dialogues` 表的完整功能(角色关联、时间管理、情绪标记等) 3. **功能受限**: 无法支持多条对白、对白类型、TTS 配音等高级功能 --- ## 解决方案 ### 实现逻辑 **文件**: `server/app/services/screenplay_service.py` **方法**: `_create_storyboards_from_ai()` **新增代码**: ```python # 7. 创建分镜对白记录(如果有 dialogue 数据) dialogue_content = sb_data.get('dialogue', '').strip() if dialogue_content: from app.models.storyboard_resource import StoryboardDialogue, DialogueType from app.utils.id_generator import generate_uuid # 解析对白类型(默认为旁白) dialogue_type = DialogueType.NARRATION # 默认为旁白 # 尝试从 meta_data 中获取对白类型 dialogue_type_str = sb_data.get('meta_data', {}).get('dialogue_type') if dialogue_type_str: type_mapping = { 'normal': DialogueType.NORMAL, 'inner_monologue': DialogueType.INNER_MONOLOGUE, 'narration': DialogueType.NARRATION } dialogue_type = type_mapping.get(dialogue_type_str.lower(), DialogueType.NARRATION) # 创建对白记录 dialogue = StoryboardDialogue( dialogue_id=generate_uuid(), storyboard_id=created.storyboard_id, character_id=None, # AI 解析暂不关联具体角色 character_name=None, content=dialogue_content, dialogue_type=dialogue_type, sequence_order=0, # 默认为第一条对白 start_time=start_time, duration=estimated_duration, emotion=sb_data.get('meta_data', {}).get('emotion'), notes=None, created_at=datetime.now(timezone.utc), updated_at=datetime.now(timezone.utc) ) self.db.add(dialogue) await self.db.flush() logger.info( "创建分镜对白: storyboard_id=%s, dialogue_id=%s, type=%s, content_length=%d", created.storyboard_id, dialogue.dialogue_id, dialogue.dialogue_type_str, len(dialogue_content) ) ``` --- ## 数据映射 ### AI 返回格式 → 数据库字段 | AI 返回字段 | 数据库字段 | 类型 | 说明 | |------------|-----------|------|------| | `dialogue` | `content` | `TEXT` | 对白内容(必填) | | `meta_data.dialogue_type` | `dialogue_type` | `SMALLINT` | 对白类型:1=普通对白, 2=内心OS, 3=旁白 | | `start_time` | `start_time` | `NUMERIC(10,3)` | 开始时间(秒) | | `estimated_duration` | `duration` | `NUMERIC(10,3)` | 时长(秒) | | `meta_data.emotion` | `emotion` | `VARCHAR` | 情绪标记(可选) | | - | `sequence_order` | `INTEGER` | 顺序号(默认 0) | | - | `character_id` | `UUID` | 角色 ID(暂为 NULL) | | - | `character_name` | `VARCHAR` | 角色名称(暂为 NULL) | ### 默认值策略 | 字段 | 默认值 | 说明 | |------|--------|------| | `dialogue_type` | `NARRATION (3)` | 默认为旁白,因为 AI 解析时通常无法准确识别对白类型 | | `sequence_order` | `0` | 默认为第一条对白,后续可手动调整 | | `character_id` | `NULL` | AI 解析暂不关联具体角色,需要用户手动关联 | | `character_name` | `NULL` | 同上 | | `emotion` | `NULL` | 仅当 AI 返回时才填充 | | `notes` | `NULL` | 保留给用户手动添加备注 | --- ## 使用示例 ### AI 返回数据示例 ```json { "storyboards": [ { "shot_number": 1, "title": "开场镜头", "description": "海边日出,女孩独自站在沙滩上", "dialogue": "这是一个关于勇气和梦想的故事...", "shot_size": "full_shot", "camera_movement": "static", "estimated_duration": 10, "characters": ["女孩"], "locations": ["海边"], "meta_data": { "dialogue_type": "narration", "emotion": "平静" } } ] } ``` ### 数据库记录 **storyboards 表**: ```sql INSERT INTO storyboards ( storyboard_id, project_id, title, description, shot_size, camera_movement, estimated_duration, start_time, end_time, order_index, meta_data ) VALUES ( '019c4212-08db-7b32-8b00-b24e66817410', '019c4212-08db-7b32-8b00-b24e66817411', '开场镜头', '海边日出,女孩独自站在沙滩上', 3, -- full_shot 1, -- static 10.000, 0.000, 10.000, 0, '{"dialogue": "这是一个关于勇气和梦想的故事...", "emotion": "平静"}' ); ``` **storyboard_dialogues 表**: ```sql INSERT INTO storyboard_dialogues ( dialogue_id, storyboard_id, content, dialogue_type, sequence_order, start_time, duration, emotion, created_at, updated_at ) VALUES ( '019c4212-08db-7b32-8b00-b24e66817412', '019c4212-08db-7b32-8b00-b24e66817410', '这是一个关于勇气和梦想的故事...', 3, -- NARRATION 0, 0.000, 10.000, '平静', NOW(), NOW() ); ``` --- ## 功能特性 ### 1. 自动化创建 - ✅ AI 解析剧本时自动创建对白记录 - ✅ 无需手动操作,提升效率 - ✅ 保持数据一致性 ### 2. 类型识别 - ✅ 支持三种对白类型:普通对白、内心OS、旁白 - ✅ 默认为旁白(最常见的场景) - ✅ 可通过 `meta_data.dialogue_type` 自定义 ### 3. 时间同步 - ✅ 对白的 `start_time` 和 `duration` 与分镜保持一致 - ✅ 便于后续的时间轴管理和 TTS 配音 ### 4. 扩展性 - ✅ 预留 `character_id` 字段,支持后续关联角色 - ✅ 预留 `emotion` 字段,支持情绪标记 - ✅ 预留 `notes` 字段,支持用户备注 --- ## 后续优化建议 ### 1. 角色关联 **问题**: 当前 `character_id` 为 NULL,无法关联具体角色 **建议**: - 在 AI 解析时尝试识别对白的说话者 - 根据 `storyboards[].characters` 自动关联角色 - 提供 API 支持用户手动关联角色 **实现示例**: ```python # 尝试从分镜的角色列表中匹配 characters_in_shot = sb_data.get('characters', []) if characters_in_shot and len(characters_in_shot) == 1: # 如果只有一个角色,自动关联 char_name = characters_in_shot[0] char_id = character_id_map.get(char_name) if char_id: dialogue.character_id = char_id dialogue.character_name = char_name ``` ### 2. 多条对白支持 **问题**: 当前仅支持单条对白(`sequence_order=0`) **建议**: - 支持 AI 返回多条对白(数组格式) - 自动分配 `sequence_order` - 支持对白的时间分段 **AI 返回格式**: ```json { "storyboards": [ { "dialogues": [ { "content": "你好,我是小明", "character": "小明", "dialogue_type": "normal", "start_time": 0, "duration": 2 }, { "content": "你好,我是小红", "character": "小红", "dialogue_type": "normal", "start_time": 2, "duration": 2 } ] } ] } ``` ### 3. 对白类型智能识别 **问题**: 当前默认为旁白,可能不准确 **建议**: - 使用 NLP 技术识别对白类型 - 根据对白内容和上下文判断 - 提供置信度评分 **识别规则**: - 包含"他想"、"她想"等关键词 → 内心OS - 包含"旁白"、"解说"等关键词 → 旁白 - 其他 → 普通对白 ### 4. TTS 配音集成 **问题**: 对白创建后需要手动触发 TTS **建议**: - 在对白创建后自动触发 TTS 任务 - 生成 `storyboard_voiceovers` 记录 - 支持批量配音 --- ## 测试验证 ### 测试场景 1: 基本对白创建 **输入**: ```json { "storyboards": [ { "title": "测试分镜", "dialogue": "这是一段测试对白", "estimated_duration": 5 } ] } ``` **预期结果**: - ✅ 创建 1 条 `storyboards` 记录 - ✅ 创建 1 条 `storyboard_dialogues` 记录 - ✅ `dialogue_type` = 3 (NARRATION) - ✅ `sequence_order` = 0 - ✅ `duration` = 5.000 ### 测试场景 2: 自定义对白类型 **输入**: ```json { "storyboards": [ { "title": "测试分镜", "dialogue": "我在想什么呢...", "meta_data": { "dialogue_type": "inner_monologue", "emotion": "困惑" } } ] } ``` **预期结果**: - ✅ `dialogue_type` = 2 (INNER_MONOLOGUE) - ✅ `emotion` = "困惑" ### 测试场景 3: 空对白处理 **输入**: ```json { "storyboards": [ { "title": "测试分镜", "dialogue": "" } ] } ``` **预期结果**: - ✅ 创建 1 条 `storyboards` 记录 - ❌ 不创建 `storyboard_dialogues` 记录(对白为空) --- ## 影响范围 ### 受影响的功能 - ✅ 剧本 AI 解析 (`POST /api/v1/screenplays/{id}/parse`) - ✅ 分镜创建逻辑 (`ScreenplayService._create_storyboards_from_ai`) - ✅ 分镜详情查询(需要关联查询 `storyboard_dialogues`) ### 数据库变更 - ✅ 新增数据写入:`storyboard_dialogues` 表 - ✅ 无表结构变更(使用现有表) ### API 变更 - ✅ 无 API 接口变更 - ✅ 响应数据可能包含对白信息(如果前端查询) --- ## 相关文档 - 对白模型: `server/app/models/storyboard_resource.py:StoryboardDialogue` - 分镜服务: `server/app/services/screenplay_service.py:_create_storyboards_from_ai` - 数据库迁移: `server/alembic/versions/20260203_1527_*_add_storyboard_resources_tables.py` - 前端 Mock: `client/src/mocks/storyboard-dialogues.ts` --- ## 总结 本次增强实现了剧本解析时自动创建分镜对白记录的功能,将对白数据从 `meta_data` 迁移到专用的 `storyboard_dialogues` 表,提升了数据结构的规范性和可扩展性。 **核心价值**: 1. **自动化**: 无需手动创建对白记录 2. **结构化**: 利用专用表管理对白数据 3. **可扩展**: 支持角色关联、TTS 配音等高级功能 4. **向后兼容**: 保留 `meta_data` 中的对白数据,不影响现有功能