# AI 剧本解析字段映射对照表 > **文档版本**:v1.0 > **最后更新**:2026-02-06 > **用途**:说明AI输出的JSON字段如何映射到数据库表字段 --- ## 目录 1. [阶段1:元素提取字段映射](#阶段1元素提取字段映射) 2. [阶段2:分镜拆解字段映射](#阶段2分镜拆解字段映射) 3. [字段不匹配问题](#字段不匹配问题) 4. [后端处理逻辑](#后端处理逻辑) --- ## 阶段1:元素提取字段映射 ### 1.1 角色字段映射 | AI输出字段 | 数据库表 | 数据库字段 | 类型 | 必填 | 说明 | |-----------|---------|-----------|------|------|------| | name | screenplay_characters | name | TEXT | ✅ | 角色名称 | | description | screenplay_characters | description | TEXT | ✅ | 角色描述 | | role_type | screenplay_characters | role_type | SMALLINT | ✅ | 角色类型:1=main, 2=supporting, 3=extra | | is_offscreen | screenplay_characters | is_offscreen | BOOLEAN | ✅ | 是否为画外音角色 | | meta_data | screenplay_characters | meta_data | JSONB | ✅ | 额外元数据(性别、物种、性格等) | **枚举值映射**: - `role_type`: "main" → 1, "supporting" → 2, "extra" → 3 ### 1.2 角色标签字段映射 | AI输出字段 | 数据库表 | 数据库字段 | 类型 | 必填 | 说明 | |-----------|---------|-----------|------|------|------| | tag_key | screenplay_element_tags | tag_key | TEXT | ✅ | 标签键(如youth/adult/old) | | tag_label | screenplay_element_tags | tag_label | TEXT | ✅ | 标签显示名称(如"少年"/"青年"/"老年") | | description | screenplay_element_tags | description | TEXT | ✅ | 标签详细描述 | | meta_data | screenplay_element_tags | meta_data | JSONB | ✅ | 额外元数据(age/clothing/mood等) | **后端自动填充字段**: - `element_type`: 1 (角色) - `element_id`: 从 `character_id_map` 获取 - `element_name`: 从AI输出的角色名称获取 - `screenplay_id`: 从上下文获取 ### 1.3 场景字段映射 | AI输出字段 | 数据库表 | 数据库字段 | 类型 | 必填 | 说明 | |-----------|---------|-----------|------|------|------| | name | screenplay_locations | name | TEXT | ✅ | 场景名称 | | location | screenplay_locations | location | TEXT | ⚪ | 场景地点 | | description | screenplay_locations | description | TEXT | ✅ | 场景描述 | | meta_data | screenplay_locations | meta_data | JSONB | ✅ | 额外元数据 | ### 1.4 场景标签字段映射 | AI输出字段 | 数据库表 | 数据库字段 | 类型 | 必填 | 说明 | |-----------|---------|-----------|------|------|------| | tag_key | screenplay_element_tags | tag_key | TEXT | ✅ | 标签键(如daytime/night) | | tag_label | screenplay_element_tags | tag_label | TEXT | ✅ | 标签显示名称(如"白天"/"夜晚") | | description | screenplay_element_tags | description | TEXT | ✅ | 标签详细描述 | | meta_data | screenplay_element_tags | meta_data | JSONB | ✅ | 额外元数据(lighting/weather/season等) | **后端自动填充字段**: - `element_type`: 2 (场景) - `element_id`: 从 `location_id_map` 获取 - `element_name`: 从AI输出的场景名称获取 ### 1.5 道具字段映射 | AI输出字段 | 数据库表 | 数据库字段 | 类型 | 必填 | 说明 | |-----------|---------|-----------|------|------|------| | name | screenplay_props | name | TEXT | ✅ | 道具名称 | | description | screenplay_props | description | TEXT | ✅ | 道具描述 | | meta_data | screenplay_props | meta_data | JSONB | ✅ | 额外元数据(material/size/color等) | **⚠️ 字段缺失问题**: - AI输出包含 `prop_type`, `owner_character`, `owner_location` 字段 - 数据库表 `screenplay_props` **不包含**这些字段 - **建议**:将这些信息存储在 `meta_data` 中 **meta_data 建议结构**: ```json { "material": "金属", "size": "可变", "prop_type": "interactive", "owner_character": "孙悟空", "owner_location": null } ``` ### 1.6 道具标签字段映射 | AI输出字段 | 数据库表 | 数据库字段 | 类型 | 必填 | 说明 | |-----------|---------|-----------|------|------|------| | tag_key | screenplay_element_tags | tag_key | TEXT | ✅ | 标签键(如new/old/damaged) | | tag_label | screenplay_element_tags | tag_label | TEXT | ✅ | 标签显示名称(如"崭新"/"陈旧"/"破损") | | description | screenplay_element_tags | description | TEXT | ✅ | 标签详细描述 | | meta_data | screenplay_element_tags | meta_data | JSONB | ✅ | 额外元数据(condition/appearance等) | **后端自动填充字段**: - `element_type`: 3 (道具) - `element_id`: 从 `prop_id_map` 获取 - `element_name`: 从AI输出的道具名称获取 --- ## 阶段2:分镜拆解字段映射 ### 2.1 分镜基本字段映射 | AI输出字段 | 数据库表 | 数据库字段 | 类型 | 必填 | 说明 | |-----------|---------|-----------|------|------|------| | title | storyboards | title | TEXT | ✅ | 分镜标题 | | description | storyboards | description | TEXT | ✅ | 画面描述 | | shooting_description | storyboards | shooting_description | TEXT | ⚪ | 拍摄描述 | | shot_size | storyboards | shot_size | SMALLINT | ⚪ | 景别(1-8) | | camera_movement | storyboards | camera_movement | SMALLINT | ⚪ | 运镜(1-9) | | estimated_duration | storyboards | estimated_duration | NUMERIC(10,3) | ⚪ | 预估时长(秒) | | order_index | storyboards | order_index | INTEGER | ✅ | 顺序索引 | | start_time | storyboards | start_time | NUMERIC(10,3) | ✅ | 开始时间(秒) | | end_time | storyboards | end_time | NUMERIC(10,3) | ✅ | 结束时间(秒) | | meta_data | storyboards | meta_data | JSONB | ✅ | 额外元数据 | **后端自动填充字段**: - `project_id`: 从上下文获取 - `screenplay_id`: 从上下文获取(可选) - `storyboard_id`: 应用层生成 UUID v7 ### 2.2 分镜角色关联字段映射 | AI输出字段 | 数据库表 | 数据库字段 | 类型 | 必填 | 说明 | |-----------|---------|-----------|------|------|------| | name | storyboard_items | element_name | TEXT | ✅ | 角色名称(冗余) | | tag_label | storyboard_items | tag_label | TEXT | ✅ | 标签名称(冗余) | | action | storyboard_items | action_description | TEXT | ⚪ | 动作描述 | | position | storyboard_items | spatial_position | TEXT | ⚪ | 画面位置 | | is_visible | storyboard_items | is_visible | BOOLEAN | ✅ | 是否可见 | | order | storyboard_items | display_order | INTEGER | ✅ | 显示顺序 | **⚠️ 字段名称不匹配**: - AI输出:`action` → 数据库:`action_description` - AI输出:`position` → 数据库:`spatial_position` - AI输出:`order` → 数据库:`display_order` **后端自动填充字段**: - `item_type`: 1 (ElementTag) - `element_tag_id`: 从 `tag_id_maps['character_tags']` 获取 - `cover_url`: 从标签关联的资源获取(可选) - `z_index`: 默认 0 ### 2.3 分镜场景关联字段映射 | AI输出字段 | 数据库表 | 数据库字段 | 类型 | 必填 | 说明 | |-----------|---------|-----------|------|------|------| | name | storyboard_items | element_name | TEXT | ✅ | 场景名称(冗余) | | tag_label | storyboard_items | tag_label | TEXT | ✅ | 标签名称(冗余) | | order | storyboard_items | display_order | INTEGER | ✅ | 显示顺序 | **后端自动填充字段**: - `item_type`: 1 (ElementTag) - `element_tag_id`: 从 `tag_id_maps['location_tags']` 获取 - `cover_url`: 从标签关联的资源获取(可选) ### 2.4 分镜道具关联字段映射 | AI输出字段 | 数据库表 | 数据库字段 | 类型 | 必填 | 说明 | |-----------|---------|-----------|------|------|------| | name | storyboard_items | element_name | TEXT | ✅ | 道具名称(冗余) | | tag_label | storyboard_items | tag_label | TEXT | ✅ | 标签名称(冗余) | | action | storyboard_items | action_description | TEXT | ⚪ | 动作描述 | | position | storyboard_items | spatial_position | TEXT | ⚪ | 画面位置 | | order | storyboard_items | display_order | INTEGER | ✅ | 显示顺序 | **后端自动填充字段**: - `item_type`: 1 (ElementTag) - `element_tag_id`: 从 `tag_id_maps['prop_tags']` 获取 - `cover_url`: 从标签关联的资源获取(可选) ### 2.5 对白字段映射 | AI输出字段 | 数据库表 | 数据库字段 | 类型 | 必填 | 说明 | |-----------|---------|-----------|------|------|------| | character_name | storyboard_dialogues | character_name | TEXT | ✅ | 说话的角色名称 | | content | storyboard_dialogues | content | TEXT | ✅ | 对白内容 | | dialogue_type | storyboard_dialogues | dialogue_type | SMALLINT | ✅ | 对白类型(1/2/3) | | sequence_order | storyboard_dialogues | sequence_order | INTEGER | ✅ | 顺序(从0开始) | | emotion | storyboard_dialogues | emotion | TEXT | ⚪ | 情绪标记 | **后端自动填充字段**: - `storyboard_id`: 从上下文获取 - `character_id`: 从 `character_id_map` 获取(可选) - `dialogue_id`: 应用层生成 UUID v7 **枚举值映射**: - `dialogue_type`: 1=normal, 2=inner_monologue, 3=narration --- ## 字段不匹配问题 ### 问题1:分镜关联字段名称不一致 **AI输出**: ```json { "action": "大笑", "position": "center", "order": 0 } ``` **数据库字段**: ```sql action_description TEXT, spatial_position TEXT, display_order INTEGER ``` **解决方案**:后端在存储时进行字段名称映射 ```python # 后端映射逻辑 storyboard_item = StoryboardItem( action_description=char_data.get('action'), # action → action_description spatial_position=char_data.get('position'), # position → spatial_position display_order=char_data.get('order', 0) # order → display_order ) ``` ### 问题2:道具分类字段缺失 **AI输出**: ```json { "name": "金箍棒", "prop_type": "interactive", "owner_character": "孙悟空", "owner_location": null } ``` **数据库表**:`screenplay_props` 表**不包含** `prop_type`, `owner_character`, `owner_location` 字段 **解决方案**:将这些信息存储在 `meta_data` 中 ```python # 后端存储逻辑 prop = ScreenplayProp( name=prop_data['name'], description=prop_data['description'], meta_data={ **prop_data.get('meta_data', {}), 'prop_type': prop_data.get('prop_type'), 'owner_character': prop_data.get('owner_character'), 'owner_location': prop_data.get('owner_location') } ) ``` ### 问题3:缺少 `tag_key` 字段 **AI输出**: ```json { "tag_key": "youth", "tag_label": "少年" } ``` **数据库表**:`screenplay_element_tags` 表**包含** `tag_key` 字段 ✅ **结论**:无问题,AI输出已包含该字段 --- ## 后端处理逻辑 ### 阶段1:存储元素 ```python async def store_parsed_elements( self, screenplay_id: UUID, parsed_data: Dict[str, Any] ) -> Dict[str, Any]: """存储 AI 解析的剧本元素""" # 1. 存储角色 character_id_map = {} for char_data in parsed_data.get('characters', []): # 枚举值映射 role_type_map = {'main': 1, 'supporting': 2, 'extra': 3} role_type = role_type_map.get(char_data['role_type'], 2) character = await self.repository.add_character( screenplay_id, { 'name': char_data['name'], 'description': char_data['description'], 'role_type': role_type, 'is_offscreen': char_data.get('is_offscreen', False), 'meta_data': char_data.get('meta_data', {}) } ) character_id_map[char_data['name']] = character.character_id # 2. 存储道具(处理缺失字段) prop_id_map = {} for prop_data in parsed_data.get('props', []): # 将 prop_type, owner_character, owner_location 存入 meta_data meta_data = prop_data.get('meta_data', {}) if 'prop_type' in prop_data: meta_data['prop_type'] = prop_data['prop_type'] if 'owner_character' in prop_data: meta_data['owner_character'] = prop_data['owner_character'] if 'owner_location' in prop_data: meta_data['owner_location'] = prop_data['owner_location'] prop = await self.repository.add_prop( screenplay_id, { 'name': prop_data['name'], 'description': prop_data['description'], 'meta_data': meta_data } ) prop_id_map[prop_data['name']] = prop.prop_id # 3. 存储标签(包含 tag_key) tag_id_maps = await tag_service.store_tags( screenplay_id, parsed_data, character_id_map, location_id_map, prop_id_map ) return { 'character_id_map': character_id_map, 'location_id_map': location_id_map, 'prop_id_map': prop_id_map, 'tag_id_maps': tag_id_maps } ``` ### 阶段2:存储分镜 ```python async def create_storyboards_from_ai( self, screenplay_id: UUID, storyboards_data: List[Dict], tag_id_maps: Dict ) -> List[UUID]: """从 AI 解析结果创建分镜""" for storyboard_data in storyboards_data: # 1. 创建分镜记录 storyboard = Storyboard( screenplay_id=screenplay_id, title=storyboard_data['title'], description=storyboard_data['description'], # ... 其他字段 ) created_storyboard = await self.repository.create(storyboard) # 2. 创建角色关联(字段名称映射) for char_data in storyboard_data.get('characters', []): map_key = f"{char_data['name']}-{char_data['tag_label']}" element_tag_id = tag_id_maps['character_tags'].get(map_key) if element_tag_id: await self.repository.create_item(StoryboardItem( storyboard_id=created_storyboard.storyboard_id, item_type=1, # ElementTag element_tag_id=element_tag_id, element_name=char_data['name'], tag_label=char_data['tag_label'], action_description=char_data.get('action'), # action → action_description spatial_position=char_data.get('position'), # position → spatial_position is_visible=char_data.get('is_visible', True), display_order=char_data.get('order', 0) # order → display_order )) # 3. 创建对白 for dialogue_data in storyboard_data.get('dialogues', []): await self.repository.create_dialogue(StoryboardDialogue( storyboard_id=created_storyboard.storyboard_id, character_name=dialogue_data['character_name'], content=dialogue_data['content'], dialogue_type=dialogue_data['dialogue_type'], sequence_order=dialogue_data['sequence_order'], emotion=dialogue_data.get('emotion') )) ``` --- ## 总结 ### ✅ 完全匹配的字段 1. 角色基本字段(name, description, role_type, is_offscreen, meta_data) 2. 场景基本字段(name, location, description, meta_data) 3. 标签字段(tag_key, tag_label, description, meta_data) 4. 分镜基本字段(title, description, shot_size, camera_movement等) 5. 对白字段(character_name, content, dialogue_type, sequence_order, emotion) ### ⚠️ 需要映射的字段 | AI输出字段 | 数据库字段 | 处理方式 | |-----------|-----------|---------| | action | action_description | 后端映射 | | position | spatial_position | 后端映射 | | order | display_order | 后端映射 | ### ❌ 缺失的字段 | AI输出字段 | 数据库表 | 解决方案 | |-----------|---------|---------| | prop_type | screenplay_props | 存入 meta_data | | owner_character | screenplay_props | 存入 meta_data | | owner_location | screenplay_props | 存入 meta_data | ### 建议 1. **更新AI提示词**:将 `action`, `position`, `order` 改为 `action_description`, `spatial_position`, `display_order`,与数据库字段名称保持一致 2. **更新数据库表**:为 `screenplay_props` 表添加 `prop_type`, `owner_character`, `owner_location` 字段(可选) 3. **保持现状**:后端在存储时进行字段名称映射和数据转换(推荐,无需修改AI提示词) --- **文档版本**:v1.0 **最后更新**:2026-02-06