# Changelog: 对白格式优化 - 支持多对白和角色关联 **日期**: 2026-02-09 **类型**: Feature Enhancement **影响范围**: AI Skill Prompt, Screenplay Service, AI Tasks **状态**: ✅ 已完成 --- ## 变更概述 优化剧本解析 AI Skill 中的对白格式: 1. 将 `dialogue` 改为 `dialogues` 数组,支持一个分镜包含多个对白 2. 对白对象包含 `content`、`dialogue_type`、`character_name`、`emotion`、`sequence_order` 字段 3. 支持对白与角色的直接关联和情绪标记 --- ## 问题背景 ### 旧版格式问题 **AI Skill 返回格式**: ```json { "storyboards": [ { "dialogue": "你好,世界!" // ❌ 简单字符串,无法关联角色,不支持多对白 } ] } ``` **问题**: 1. 一个分镜只能有一个对白(实际场景中可能有多个角色对话) 2. 无法直接指定对白的角色 3. 无法明确指定对白类型(普通对白、内心OS、旁白) 4. 无法标记对白的情绪 5. 导致 `storyboard_dialogues.character_id` 和 `character_name` 经常为 `NULL` --- ## 解决方案 ### 1. 更新 AI Skill Prompt (v1.5.0) **文件**: `server/app/resources/ai_skills/screenplay_parsing.md` **新版格式**: ```json { "storyboards": [ { "dialogues": [ { "content": "师父,我们去哪里?", "dialogue_type": 1, "character_name": "孙悟空", "emotion": "好奇", "sequence_order": 0 }, { "content": "去西天取经。", "dialogue_type": 1, "character_name": "唐僧", "emotion": "平静", "sequence_order": 1 } ] } ] } ``` **字段说明**: - `content`: 对白内容(必填) - `dialogue_type`: 对白类型(必填) - `1` = normal(普通对白) - `2` = inner_monologue(内心OS) - `3` = narration(旁白,character_name 为 null) - `character_name`: 角色名称(可选,旁白时为 null) - `emotion`: 情绪标记(可选,如:开心、愤怒、悲伤、平静等) - `sequence_order`: 对白顺序(必填,从 0 开始) --- ### 2. 更新 Screenplay Service **文件**: `server/app/services/screenplay_service.py` #### 2.1 兼容三种格式 在 `_create_storyboards_from_ai()` 方法中添加格式兼容逻辑: ```python dialogues_data = sb_data.get('dialogues') or sb_data.get('dialogue') # 标准化为数组格式 dialogues_list = [] if isinstance(dialogues_data, list): # ✅ 新版格式:dialogues 是数组 dialogues_list = dialogues_data elif isinstance(dialogues_data, dict): # ⚠️ v1.4.0 格式:dialogue 是单个对象 dialogues_list = [dialogues_data] elif isinstance(dialogues_data, str): # ⚠️ 旧版格式:dialogue 是字符串 dialogues_list = [{'content': dialogues_data, 'dialogue_type': 3, 'character_name': None}] # 遍历创建对白记录 for dialogue_data in dialogues_list: # 解析 content, dialogue_type, character_name, emotion, sequence_order # ... ``` #### 2.2 更新辅助方法 ```python def _has_dialogue(self, storyboard_data: Dict[str, Any]) -> bool: """判断分镜是否包含对白(兼容数组、对象、字符串格式)""" # 优先检查 dialogues 数组(新版) dialogues = storyboard_data.get('dialogues') if dialogues and isinstance(dialogues, list) and len(dialogues) > 0: for d in dialogues: if isinstance(d, dict) and d.get('content', '').strip(): return True return False # 降级检查 dialogue 单个对象/字符串(旧版) dialogue = storyboard_data.get('dialogue') # ... ``` --- ### 3. 更新 AI Tasks **文件**: `server/app/tasks/ai_tasks.py` **统计对白数量**(兼容三种格式): ```python dialogues_count = 0 for sb in parsed_data.get('storyboards', []): # 优先检查 dialogues 数组(新版) dialogues = sb.get('dialogues') if dialogues and isinstance(dialogues, list): for d in dialogues: if isinstance(d, dict) and d.get('content', '').strip(): dialogues_count += 1 else: # 降级检查 dialogue 单个对象/字符串(旧版) dialogue = sb.get('dialogue') if dialogue: if isinstance(dialogue, str) and dialogue.strip(): dialogues_count += 1 elif isinstance(dialogue, dict) and dialogue.get('content', '').strip(): dialogues_count += 1 ``` --- ## 技术细节 ### 对白类型映射 **数据库枚举** (`DialogueType`): ```python class DialogueType(IntEnum): NORMAL = 1 # 普通对白 INNER_MONOLOGUE = 2 # 内心OS NARRATION = 3 # 旁白 ``` ### 情绪标记 **数据库字段**: `storyboard_dialogues.emotion` (VARCHAR) **示例值**: - 开心、愤怒、悲伤、平静、惊讶、恐惧、厌恶、期待等 - AI 可以根据对白内容自动推断情绪 --- ## 向后兼容性 ✅ **完全兼容旧版格式** 支持三种格式: 1. **新版(v1.5.0)**: `dialogues` 数组 2. **v1.4.0**: `dialogue` 单个对象 3. **旧版**: `dialogue` 字符串 --- ## 测试建议 ### 1. 测试多对白场景 ```python storyboard_data = { "dialogues": [ { "content": "师父,我们去哪里?", "dialogue_type": 1, "character_name": "孙悟空", "emotion": "好奇", "sequence_order": 0 }, { "content": "去西天取经。", "dialogue_type": 1, "character_name": "唐僧", "emotion": "平静", "sequence_order": 1 } ], "characters": ["孙悟空", "唐僧"] } # 预期结果:创建 2 条对白记录 ``` ### 2. 测试旁白 ```python storyboard_data = { "dialogues": [ { "content": "西游记第一回,灵根育孕源流出。", "dialogue_type": 3, "character_name": null, "emotion": null, "sequence_order": 0 } ] } # 预期结果: # - character_id: None # - character_name: None # - dialogue_type: NARRATION (3) # - emotion: None ``` --- ## 影响范围 ### 修改的文件 1. ✅ `server/app/resources/ai_skills/screenplay_parsing.md` (v1.4.0 → v1.5.0) 2. ✅ `server/app/services/screenplay_service.py` - 修改 `_create_storyboards_from_ai()` 方法(支持 dialogues 数组) - 更新 `_has_dialogue()` 辅助方法 3. ✅ `server/app/tasks/ai_tasks.py` - 更新对白统计逻辑 ### 需要重启的服务 ```bash # 重启 Celery Worker(加载新的 AI Skill) docker restart jointo-server-celery-ai # 重启 FastAPI(加载新的 Service 逻辑) docker restart jointo-server-app ``` --- ## 预期效果 ### Before(旧版) ```sql SELECT character_id, character_name, content, emotion, sequence_order FROM storyboard_dialogues WHERE storyboard_id = 'xxx'; -- 结果(单条对白,无角色关联) character_id | character_name | content | emotion | sequence_order -------------|----------------|----------------------|---------|--------------- NULL | NULL | 师父,我们去哪里? | NULL | 0 ``` ### After(新版) ```sql SELECT character_id, character_name, content, emotion, sequence_order FROM storyboard_dialogues WHERE storyboard_id = 'xxx' ORDER BY sequence_order; -- 结果(多条对白,有角色关联和情绪) character_id | character_name | content | emotion | sequence_order --------------------------------------|----------------|----------------------|---------|--------------- 01936e8f-7b2a-7890-8f3e-123456789abc | 孙悟空 | 师父,我们去哪里? | 好奇 | 0 01936e8f-7b2a-7890-8f3e-987654321cba | 唐僧 | 去西天取经。 | 平静 | 1 ``` --- ## 后续优化建议 1. **前端展示优化** - 在分镜编辑器中显示对白的角色头像 - 支持对白类型的可视化标识(普通对白、内心OS、旁白) - 显示情绪标记(表情图标) 2. **AI 提示词优化** - 增加对白类型识别的示例 - 强化角色名称匹配的准确性 - 提供情绪识别的指导 3. **数据迁移** - 为历史数据中的对白补充角色关联(基于内容解析) --- **维护人员**: AI Agent **最后更新**: 2026-02-09