package prompts import ( "fmt" "strings" ) const ( OperationStoryboardPlan = "storyboard_plan" OperationStoryboardRepair = "storyboard_repair" OperationStoryboardFirstFrame = "storyboard_first_frame" OperationStoryboardVideo = "storyboard_video" OperationCharacterExtract = "character_extract" OperationChapterAssetsExtract = "chapter_assets_extract" OperationCharacterTurnaround = "character_turnaround" OperationShortDramaOutline = "short_drama_outline" OperationSkillDraft = "skill_draft" ) const legacyStoryboardVideoPromptPreamble = "生成单一连续镜头的视频执行提示词。一个镜头只保留一个叙事目标、一个主运镜和一条主要动作链;摄影机运动必须有起点、动机和停止点。优先保证角色身份、表演、关键动作和连续性,次要环境效果可以简化。\n\n" func defaultPromptDefinitions() []PromptOperationDefinition { return []PromptOperationDefinition{ { Operation: OperationChapterAssetsExtract, Label: "章节角色、场景与道具提取", Category: "角色", OutputType: "json", SchemaKey: "chapter-assets/v1", Description: "从章节正文提取角色、实际发生剧情的场景和影响剧情的道具,分别进入资产待确认列表。", Variables: []PromptTemplateVariable{{Label: "项目名称", Placeholder: "{{项目名称}}"}, {Label: "章节名称", Placeholder: "{{章节名称}}"}, {Label: "项目画风", Placeholder: "{{项目画风}}"}}, DefaultContent: `你是短剧资产导演。按章节事实提取角色、场景和道具。同一身份的别名合并,不编造正文未出现的资产;未明确的视觉信息写“正文未明确”。角色包含剧情定位、稳定外貌服装体态、性格、一致性提示、三视图提示及语言、声音年龄、音色。场景描述空间布局、环境和光照;道具描述材质、形状及剧情用途。无对应资产时返回空数组。`, }, { Operation: OperationStoryboardPlan, Label: "分镜规划", Category: "分镜", OutputType: "json", SchemaKey: "storyboard-plan/v3", Description: "把剧情、项目画风和当前角色版本规划为可执行镜头。", Variables: []PromptTemplateVariable{{Label: "项目名称", Placeholder: "{{项目名称}}"}, {Label: "项目画风", Placeholder: "{{项目画风}}"}, {Label: "用户要求", Placeholder: "{{用户要求}}"}}, DefaultContent: DefaultStoryboardPromptTemplate(), }, { Operation: OperationStoryboardRepair, Label: "分镜修复", Category: "分镜", OutputType: "json", SchemaKey: "storyboard-plan/v3", Description: "修复模型返回的分镜结构、字段和镜头复杂度。", Variables: []PromptTemplateVariable{{Label: "校验错误", Placeholder: "{{校验错误}}"}, {Label: "项目画风", Placeholder: "{{项目画风}}"}}, DefaultContent: `你是影视分镜 JSON 修复导演。修复结构时必须保留剧情信息,通过拆镜或重新分配内容解决复杂度超限,不得用删除关键剧情掩盖错误。若校验错误是台词/旁白超长,必须拆镜或精简为单镜头可念完的台词,保留关键情节,不要把超长 dialogue 原样放回。保持原项目的视觉媒介、角色身份、服装、道具和连续性,不要擅自改写画风。只修复校验错误和由此引发的镜头组织问题。`, }, { Operation: OperationStoryboardFirstFrame, Label: "分镜首帧", Category: "生成", OutputType: "text", Description: "把单镜头结构转换为图片模型使用的首帧提示词。", Variables: []PromptTemplateVariable{{Label: "项目视觉", Placeholder: "{{项目视觉}}"}, {Label: "首帧构图", Placeholder: "{{首帧构图}}"}, {Label: "表演起始状态", Placeholder: "{{表演起始状态}}"}, {Label: "负面要求", Placeholder: "{{负面要求}}"}}, DefaultContent: `生成单一、可执行的分镜首帧。严格继承项目视觉媒介和角色资产;画面明确主体左右位置、视线、前中后景、遮挡、视觉焦点、可信光源与材质。只描述静止首帧,不提前写后续运动,不添加画外人物、无来源光线、文字或水印。 【项目视觉】 {{项目视觉}} 【首帧构图】 {{首帧构图}} 【表演起始状态】 {{表演起始状态}} 【负面要求】 {{负面要求}}`, }, { Operation: OperationStoryboardVideo, Label: "分镜视频", Category: "生成", OutputType: "text", Description: "把单镜头结构转换为视频模型使用的紧凑执行提示词。", Variables: []PromptTemplateVariable{{Label: "项目视觉", Placeholder: "{{项目视觉}}"}, {Label: "镜头意图", Placeholder: "{{镜头意图}}"}, {Label: "首帧构图", Placeholder: "{{首帧构图}}"}, {Label: "表演与调度", Placeholder: "{{表演与调度}}"}, {Label: "摄影机", Placeholder: "{{摄影机}}"}, {Label: "时间节拍", Placeholder: "{{时间节拍}}"}, {Label: "运动与结尾", Placeholder: "{{运动与结尾}}"}, {Label: "声音", Placeholder: "{{声音}}"}, {Label: "执行优先级", Placeholder: "{{执行优先级}}"}, {Label: "负面要求", Placeholder: "{{负面要求}}"}}, DefaultContent: `【项目视觉】 {{项目视觉}} 【镜头意图】 {{镜头意图}} 【首帧构图】 {{首帧构图}} 【表演与调度】 {{表演与调度}} 【摄影机】 {{摄影机}} 【时间节拍】 {{时间节拍}} 【运动与结尾】 {{运动与结尾}} 【台词与声音】 {{声音}} 【执行优先级】 {{执行优先级}} 【负面要求】 {{负面要求}}`, }, { Operation: OperationCharacterExtract, Label: "角色卡提取", Category: "角色", OutputType: "json", SchemaKey: "character-breakdown/v1", Description: "从章节正文提取需要跨镜头保持一致的角色资产。", Variables: []PromptTemplateVariable{{Label: "项目名称", Placeholder: "{{项目名称}}"}, {Label: "章节名称", Placeholder: "{{章节名称}}"}, {Label: "项目画风", Placeholder: "{{项目画风}}"}}, DefaultContent: `你是短剧角色资产导演。只提取章节中实际出场、发言或对剧情产生明确作用,并且后续制作需要保持视觉或声音一致的角色。忽略系统播报、纯物件、无身份群众和没有持续角色价值的一次性路人;合并同一角色的姓名、专属称谓和别名。正文未明确的信息必须写“正文未明确”,不得自行改变人物关系、时代背景或编造编号式姓名。角色设定要具体、稳定、可用于后续三视图和视频一致性控制。`, }, { Operation: OperationCharacterTurnaround, Label: "角色三视图", Category: "角色", OutputType: "text", Description: "按当前角色版本和项目画风生成正、侧、背三视图。", Variables: []PromptTemplateVariable{{Label: "角色名称", Placeholder: "{{角色名称}}"}, {Label: "项目画风", Placeholder: "{{项目画风}}"}, {Label: "角色设定", Placeholder: "{{角色设定}}"}}, DefaultContent: `制作专业人物三视图设定表。画面严格分成三个等宽竖向区域,从左到右依次为正面全身、右侧面全身、背面全身。三个视角必须是同一角色、同一服装、同一发型、同一体型和同一比例,采用站立中性姿势,完整显示头顶到脚底。背景使用纯净中性浅色和均匀设定稿光线,只负责分离轮廓,不得改变项目画风的绘画或渲染媒介。禁止文字、边框、道具说明、表情变化和额外人物。`, }, { Operation: OperationShortDramaOutline, Label: "短剧大纲", Category: "项目", OutputType: "json", SchemaKey: "short-drama-outline/v1", Description: "根据一句话故事生成短剧标题、简介和章节正文,供项目创建导入。", Variables: []PromptTemplateVariable{{Label: "章节数量", Placeholder: "{{章节数量}}"}, {Label: "叙事结构", Placeholder: "{{叙事结构}}"}, {Label: "每章字数", Placeholder: "{{每章字数}}"}, {Label: "叙事视角", Placeholder: "{{叙事视角}}"}, {Label: "整体基调", Placeholder: "{{整体基调}}"}, {Label: "角色规模", Placeholder: "{{角色规模}}"}, {Label: "章节篇幅", Placeholder: "{{章节篇幅}}"}}, DefaultContent: `你是短剧编剧。根据用户的一句话故事,生成一部短剧的标题、一句话简介和 {{章节数量}} 个章节。生成要求:叙事采用{{叙事结构}}结构,每章约 {{每章字数}} 字,使用{{叙事视角}}视角,整体基调{{整体基调}},主要角色约 {{角色规模}},章节篇幅{{章节篇幅}}。`, }, { Operation: OperationSkillDraft, Label: "技能草稿", Category: "技能", OutputType: "json", SchemaKey: "skill-draft/v1", Description: "根据用户想法生成可复用创作技能的名称、分类、简介和指令草稿。", Variables: []PromptTemplateVariable{}, DefaultContent: `你是一位技能编写助手。根据用户的想法,为一个「可复用的创作技能」生成一份草稿。技能名称简短,不超过 20 个字。分类 tag 必须是 drama、ecommerce、creative、social、others 之一。简介不超过 120 字,说明适用场景、输入条件和最终产出。指令使用 Markdown,至少 300 字,写给后续在画布中使用该技能的模型阅读,必须包含角色设定、输入与约束、分步执行流程、检查清单和输出格式。工具步骤只描述所需能力、输入、输出和确认点,不虚构具体工具名,不把工具、节点、权限、预算或审批写成技能授予的能力;执行时始终以运行环境实际暴露的能力清单为准。`, }, } } func protectedPromptContext(operation string, values map[string]string) string { switch operation { case OperationStoryboardPlan: return storyboardProtectedContext(values) case OperationStoryboardRepair: return storyboardRepairProtectedContext(values) case OperationCharacterExtract: return characterExtractProtectedContext(values) case OperationChapterAssetsExtract: return strings.Join([]string{ fmt.Sprintf("【任务】\n从短剧项目《%s》的章节“%s”提取角色、场景和道具。", values["项目名称"], values["章节名称"]), "【项目画风】\n" + values["项目画风"], "【章节正文】\n" + values["章节正文"], "【受保护输出契约】\n" + promptOutputContract(operation), }, "\n\n") case OperationCharacterTurnaround: return strings.Join([]string{"【角色名称】\n" + values["角色名称"], "【项目画风】\n" + values["项目画风"], "【角色设定】\n" + values["角色设定"]}, "\n\n") case OperationShortDramaOutline: return strings.Join([]string{ "【用户故事】\n" + values["用户故事"], "【受保护输出契约】\n" + promptOutputContract(operation), }, "\n\n") case OperationSkillDraft: return strings.Join([]string{ "【用户想法】\n" + values["用户想法"], "【受保护输出契约】\n" + promptOutputContract(operation), }, "\n\n") default: return "" } } func promptOutputContract(operation string) string { switch operation { case OperationStoryboardPlan, OperationStoryboardRepair: return "服务端固定 JSON Schema storyboard-plan/v3(不可由运营模板或用户定制覆盖):\n" + storyboardPlanJSONSchema case OperationCharacterExtract: return "服务端固定 JSON Schema character-breakdown/v1(不可由运营模板或用户定制覆盖):\n" + characterBreakdownJSONSchema case OperationChapterAssetsExtract: return "服务端固定 JSON Schema chapter-assets/v1(不可由运营模板或用户定制覆盖):\n" + ChapterAssetsJSONSchema case OperationShortDramaOutline: return "服务端固定 JSON Schema short-drama-outline/v1(不可由运营模板或用户定制覆盖):\n" + shortDramaOutlineJSONSchema + "\n只输出一个 JSON 对象,不要输出 markdown 代码块或其他文字。" case OperationSkillDraft: return "服务端固定 JSON Schema skill-draft/v1(不可由运营模板或用户定制覆盖):\n" + skillDraftJSONSchema + "\n只输出一个 JSON 对象,不要输出 markdown 代码块或其他文字。" default: return "当前操作输出普通文本提示词,没有 JSON Schema。" } } const shortDramaOutlineJSONSchema = `{ "type": "object", "additionalProperties": false, "required": ["title", "synopsis", "chapters"], "properties": { "title": {"type": "string"}, "synopsis": {"type": "string"}, "chapters": { "type": "array", "minItems": 1, "items": { "type": "object", "additionalProperties": false, "required": ["title", "content"], "properties": { "title": {"type": "string"}, "content": {"type": "string"} } } } } }` const skillDraftJSONSchema = `{ "type": "object", "additionalProperties": false, "required": ["skillName", "tag", "description", "instruction"], "properties": { "skillName": {"type": "string"}, "tag": {"type": "string", "enum": ["drama", "ecommerce", "creative", "social", "others"]}, "description": {"type": "string"}, "instruction": {"type": "string"} } }` const storyboardPlanJSONSchema = `{ "type": "object", "additionalProperties": false, "required": ["title", "logline", "styleGuide", "characters", "locations", "shots"], "properties": { "title": {"type": "string"}, "logline": {"type": "string"}, "styleGuide": {"type": "string", "maxLength": 120}, "characters": {"type": "array", "items": {"type": "string"}}, "locations": {"type": "array", "items": {"type": "string"}}, "shots": { "type": "array", "minItems": 1, "items": { "type": "object", "additionalProperties": false, "required": ["title", "description", "durationSeconds", "dialogue", "characterIds", "narrativeIntent", "viewerPOV", "performanceBlocking", "shotSize", "emotion", "lightingAndAtmosphere", "audioEffects", "visualPrompt", "videoPrompt", "camera", "motion", "timeBeats", "mustHave", "optionalDetails", "continuityOut", "negativePrompt", "assetRefs"], "properties": { "title": {"type": "string"}, "description": {"type": "string"}, "durationSeconds": {"type": "integer", "minimum": 1, "maximum": 60}, "dialogue": {"type": "string"}, "characterIds": {"type": "array", "description": "优先填写当前角色资产 ID;尚未确认资产的角色填写角色名称", "items": {"type": "string"}}, "narrativeIntent": {"type": "string"}, "viewerPOV": {"type": "string"}, "performanceBlocking": {"type": "string"}, "shotSize": {"type": "string"}, "emotion": {"type": "string"}, "lightingAndAtmosphere": {"type": "string"}, "audioEffects": {"type": "string"}, "visualPrompt": {"type": "string"}, "videoPrompt": {"type": "string"}, "camera": {"type": "string"}, "motion": {"type": "string"}, "timeBeats": {"type": "string"}, "mustHave": {"type": "array", "maxItems": 3, "items": {"type": "string"}}, "optionalDetails": {"type": "array", "items": {"type": "string"}}, "continuityOut": {"type": "string"}, "negativePrompt": {"type": "string"}, "assetRefs": { "type": "array", "maxItems": 6, "items": { "type": "object", "additionalProperties": false, "required": ["nodeId", "role", "priority"], "properties": { "nodeId": {"type": "string"}, "role": {"type": "string", "enum": ["character", "environment", "wardrobe", "prop", "weapon", "style", "motion", "audio"]}, "priority": {"type": "integer", "minimum": 0, "maximum": 100} } } } } } } } }` const ChapterAssetsJSONSchema = `{ "type": "object", "additionalProperties": false, "required": ["characters", "scenes", "props"], "properties": { "characters": {"$ref": "#/$defs/characterBreakdown/properties/characters"}, "scenes": {"type": "array", "items": {"$ref": "#/$defs/asset"}}, "props": {"type": "array", "items": {"$ref": "#/$defs/asset"}} }, "$defs": { "asset": { "type": "object", "additionalProperties": false, "required": ["name", "description", "prompt"], "properties": {"name": {"type": "string"}, "description": {"type": "string"}, "prompt": {"type": "string"}} }, "characterBreakdown": ` + characterBreakdownJSONSchema + ` } }` const characterBreakdownJSONSchema = `{ "type": "object", "additionalProperties": false, "required": ["characters"], "properties": { "characters": { "type": "array", "items": { "type": "object", "additionalProperties": false, "required": ["name", "aliases", "role", "appearance", "clothing", "physique", "personality", "props", "consistencyPrompt", "multiViewPrompt", "voiceLanguage", "voiceAge", "voiceTimbre"], "properties": { "name": {"type": "string"}, "aliases": {"type": "array", "items": {"type": "string"}}, "role": {"type": "string"}, "appearance": {"type": "string"}, "clothing": {"type": "string"}, "physique": {"type": "string"}, "personality": {"type": "string"}, "props": {"type": "string"}, "consistencyPrompt": {"type": "string"}, "multiViewPrompt": {"type": "string"}, "voiceLanguage": {"type": "string"}, "voiceAge": {"type": "string"}, "voiceTimbre": {"type": "string"} } } } } }` func storyboardProtectedContext(values map[string]string) string { return strings.Join([]string{ "【剧情】\n" + values["剧情"], "【用户本次要求】\n" + values["用户要求"], "【当前画布资产】\n" + values["画布资产"], "【当前项目画风】\n" + values["项目画风"], "【当前角色版本】\n" + values["角色版本"], StoryboardExecutionContract(values["单镜头时长规则"], values["镜头数量规则"]), }, "\n\n") } func storyboardRepairProtectedContext(values map[string]string) string { return strings.Join([]string{ "【剧情】\n" + values["剧情"], "【用户本次要求】\n" + values["用户要求"], "【当前画布资产】\n" + values["画布资产"], "【原始校验错误】\n" + values["校验错误"], "【当前项目画风】\n" + values["项目画风"], "【当前角色版本】\n" + values["角色版本"], StoryboardExecutionContract(values["单镜头时长规则"], values["镜头数量规则"]), "【需要修复的原始输出】\n" + values["原始输出"], }, "\n\n") } func StoryboardExecutionContract(durationRule string, countRule string) string { return `【受保护执行契约】 - ` + durationRule + ` - ` + countRule + ` - 单镜头最多 2 名主要角色、1 个主运镜、1 条主要动作链、3 个 timeBeats 和 3 个 mustHave;超限必须拆镜或在固定镜头数内重新分配。 - dialogue 只写本镜头实际念出的台词或简短旁白,字数上限按 1 秒最多约 5 个中文字符计算(至少 24 字);超长台词/旁白必须拆镜或精简,不得用 dialogue 承载长段叙述。 - characterIds 优先填写当前角色版本中的 assetId;角色只有名称、尚未确认资产时填写角色名称,服务端会保留名称引用。不要编造 ID;没有角色时返回空数组。 - assetRefs 只能引用当前画布资产中的 nodeId;不要根据相似名称编造 ID。每镜最多 6 个,priority 越大表示越重要。 - styleGuide 最多 120 个中文字符;visualPrompt 只描述首帧,videoPrompt 只描述运动和结尾状态。 - 画幅比例由视频节点参数控制,提示词不得写入具体比例,也不要讨论画幅配置。 - 只返回完整 JSON,不要 Markdown 或解释。 - ` + promptOutputContract(OperationStoryboardPlan) } func characterExtractProtectedContext(values map[string]string) string { return strings.Join([]string{ fmt.Sprintf("【任务】\n从短剧项目《%s》的章节“%s”提取角色。", values["项目名称"], values["章节名称"]), "【项目画风】\n" + values["项目画风"], "【章节正文】\n" + values["章节正文"], "【受保护输出契约】\n" + promptOutputContract(OperationCharacterExtract) + "\n严格 JSON 示例:{\"characters\":[{\"name\":\"角色名\",\"aliases\":[],\"role\":\"剧情定位与人物关系\",\"appearance\":\"稳定外貌\",\"clothing\":\"固定服装\",\"physique\":\"体型体态\",\"personality\":\"表演基线\",\"props\":\"\",\"consistencyPrompt\":\"跨镜头一致性约束\",\"multiViewPrompt\":\"三视图结构重点\",\"voiceLanguage\":\"语言口音\",\"voiceAge\":\"声音年龄感\",\"voiceTimbre\":\"音色语速力度\"}]}", }, "\n\n") }