short-drama-video-prompts
把短剧分镜和冻结关键帧写成可直接复制的视频提示词 Markdown,也可按用户要求写时间线配乐/主题曲意图。用户提到文生/图生视频动作、人物表演、运镜、口型、环境运动、镜头时长、起止状态、把分镜转成视频提示词或写配乐提示词时使用;不生成媒体、不创作歌词、不改分镜边界。
Install
npx skills add https://github.com/zenstory-ai/drama-skills/tree/main/skills/short-drama-video-prompts
claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install zenstory-ai-drama-skills@llmmart
git clone https://github.com/zenstory-ai/drama-skills.git
The skills CLI installs just this skill, for any of its supported agents. Claude Code installs the whole zenstory-ai/drama-skills collection as a plugin from our marketplace. Git is the plain clone.
Skill manifest
短剧视频与时间线音乐提示词
把已确认镜头和起始帧写成有边界、可生成、可直接复制的 剧集/<EP>/视频提示词.md。
每镜使用 MOTION-... 标题并引用对应 SHOT-...。
Quick Start
用 $short-drama-video-prompts 把 EP001 分镜逐镜写成可直接复制的视频提示词
入口与边界
有当前分镜、冻结关键帧和必要视觉事实即可检查参考准备;图片提示词不是已有图片的证明。运动提示词拥有从已确认 起点到终点的变化:表演动作、道具状态、运镜、节奏和声音提示。先按分镜的「输入参考图」选择路径:
- 有可读的真实
REF-...图片时是图生视频;原样携带分镜中的槽位、顺序、项目相对路径、 中文名称、用途、控制与不得控制范围,静态身份、造型、地点和构图可由参考帧与视觉设定共同拥有。 这一行就是本镜的素材清单:路径回答「送哪一张」,用途回答「它在本镜负责什么」,顺序回答 「在可复制正文里叫哪个标签」。回答不了这三问的槽位不算准备好。 - 「输入参考图:无(创作者已明确选择文生视频)」时才是文生视频;视频文档必须写非空「静态视觉锚点」,且把它原样纳入
可复制正文,让提示词本身交代人物、造型、地点、构图和光线中本镜必要的事实。不得只写
preserve/keep/保持已有外观,因为执行端没有收到可供保持的图片。 - 有
PLAN-...槽位时同样是图生视频:图片由创作者在生成时自行挂载,本套件没有文件可送。 正文照常按图生视频写,顺序就是创作者挂图的次序,用途说明每一张在本镜负责什么。 这些镜头不能转$short-drama-produce;要投产就先把真实文件放进项目、改写成REF-...。 - 「输入参考图:无(待补参考图:……)」、已有
REF-...或PLAN-...后仍带「;待补参考图:……」,或普通的「无」都表示未准备好,不是文生视频授权。先自动查找可用真实图片并请分镜 owner 刷新绑定;仍有缺失时,列出镜头、缺失图片及对应IMG-...或冻结关键帧,不写最终《视频提示词.md》。
创作者可读说明跟随项目语言。开始前读取目标模型档案里的版本、执行模式、提示词方言、原生时长和
参考方式;这些选择同时约束上游分镜和下游 adapter,不能只在正文阶段临时猜。创作者在本次请求里点名
目标模型(例如“按 MiniMax H3 写”)而项目档案还没有这一项时,先转 $short-drama 把这个选择写进
short-drama.json 的 production_profile,再回到本阶段;一句会话里的点名如果没有落到档案上,
下一轮就会退回通用路径,方言、原生时长和参考方式又要重猜。没有项目配置的独立任务把点名的模型
和已解析的方言写在本文档开头一行,供下游核对。视频可复制正文优先跟随
short-drama.json#/creator_authority/production_profile/choices/video_prompt_language;未声明时回退到
short-drama.json#/format/prompt_language。没有 short-drama.json 时,根据用户点名的目标视频模型
确认正文语言;目标模型也未声明时沿用用户当前语言,不固定为英文。对白、VO、OS 的口语语言仍由
剧本和声音方向决定,不能从提示词正文语言推断。
目标为 seedance-2.0 时按 Seedance 2.0 方言 写,目标为
seedance-2.5 时按 Seedance 2.5 方言 写,目标为 minimax-h3 时按
MiniMax H3 方言 写。只读命中的一份。其他目标或版本不
套用相近模型的语法,继续按 目标模型能力档案 的通用路径写。
沿用 creator-first 既有骨架:## MOTION-... · 中文名,字段名依次使用「分镜、时长、生成方式、
输入参考图、静态视觉锚点、起始帧、状态链、终点」,正文标题使用 ### 可复制提示词。静态视觉锚点
跟随本镜已解析的视频提示词语言并原样进入正文;正文每个非空行都以 > 组成 Markdown 引用块。
重写内容不能改名、另造替代字段或改成普通段落。
工作流
先检查每镜「输入参考图」。用户未手工点名图片时,根据本镜冻结关键帧和可见人物、地点、关键道具的一致性需求,在用户输入、 「制作成果」和文档已指向媒体中查找真实图片。内容和用途核对后才自动绑定;不靠相似文件名猜图。
已有匹配图片时,在当前请求内先让分镜 owner 原地刷新受影响镜头的
REF-...槽位,再回到本阶段;只绑本镜真正需要保持的图,不按资产数量凑槽位。 同一次刷新里,如果这些镜头还缺「视觉依据」这个必写字段(旧文档常常没有),一并按已成稿的冻结关键帧回填—— 两者读的是同一格画面,分开两轮只会让绑了图的镜头仍然说不清画面里有谁。 新槽位必须逐字使用REF-<slot>(顺序:<n>)· <项目相对路径>《<中文名称>》(用途:<用途>;控制:<范围>;不得控制:<范围>);用途取身份|造型状态|地理|构图|尺度|效果|起始帧|结束帧|风格之一,不得自创斜杠分隔写法。多图只用;连接,写完后先按格式回读,再复制到视频文档。仍有必要图片缺失时,一张都没有就把分镜写成「无(待补参考图:……)」;只缺一部分则保留已核对的
REF-...,并在末尾追加「;待补参考图:……」。一次列出缺失镜头、图片用途和对应的IMG-.../冻结关键帧,然后停在视频提示词之前。 不伪造路径,不把图片提示词当成图,不静默降级为文生视频。 报告缺口时把创作者真正有的三条路一起说清楚,不要只给「生成参考图」和「改成文生视频」: 把已有图片放进项目绑成REF-...;由创作者在自己的工具里出图、本轮先写成PLAN-...挂图计划; 或者明确改走文生视频。缺口清单本身就是那份挂图计划的草稿,逐镜写清要哪几张、每张管什么。只有创作者明确选择不用参考图时,才把分镜记为「无(创作者已明确选择文生视频)」。准备闭合后原样抄入分镜的「输入参考图」,据此标明 「生成方式:图生视频」或「生成方式:文生视频」。该字段只写这两个精确值,不追加模型名、方言或 full-reference 等说明;模型说明留在正文结构中。 具体走首帧、首尾帧还是多槽参考,由各槽位的
用途组合读出:只有一张起始帧走首帧,起始帧加结束帧走首尾帧,起始帧与人物/地点/道具图同时存在或只有这些图时走多槽参考。命中的模型方言负责把这个组合翻成它自己的素材 role 与正文结构; 多槽参考与首/尾帧在同一请求内互斥的模型,起始帧也随整组走多槽参考,不拆成两套输入。核对本镜时长落在目标模型档案的原生区间内;不在区间内就回到分镜修订,不能写完提示词后留给 adapter 报错,也不能在正文里暗自改变时长。 有对白、VO 或 OS 时,同时核对分镜「声音」中的估时依据与实际可用发声窗口;缺少依据就按 对白预算 补做判断。秒段加总正确不代表台词说得完。
写不能动的边框:图生视频以参考帧和视觉设定锁定;文生视频把必要静态事实写入「静态视觉锚点」和可复制正文。
原样读取分镜的「起点 → 唯一动作 → 终点」,确认人物、双手和持物的状态转换都发生在镜内。
写“静态锚点 → 起点 → 唯一动作(触发、接触、落点)→ 次级反应 → 运镜 → 声音 → 终点”, 必要时用秒数分段;下一镜从这个终点继续,不另造镜外转换。
只在真实参考帧已经说明静态外观时删除重复描述;《视觉设定.md》连续性锁的锁面不在可删之列。
把限制动作空间的已确认关系写成贯穿本镜的正向可见状态,只安排关系允许的动作通道;只有分镜明确改变关系时才写变化过程。优先用可见状态说明边界,必要时保留明确的排除条件。
逐镜读取文字义务。除非剧本或创作者明确要求,正文用本镜已解析的视频提示词语言直接写明任何时刻都不生成非画内字幕、caption 和对白文字叠加,不能只写抽象的“文字层为空”;这条默认与旗帜、标牌、屏幕等画内文字独立。画内
exact_readable逐字写明字符、语言、承载面和位置,不能用全局no-text覆盖。声音同轨时,把允许的发声按来源、说话人、口语语言和顺序写成完整时间线;每句从原文首字到末字, 且逐字对白在单镜可复制正文中只出现一次。视觉动作段只按时间和说话人引用该声音事件,不再抄一遍 台词。事件外回到已声明的环境声或静默。Brief、剧本或声音方向已经明确禁止外语前导、语气词、 额外人声或重复时,把这些已确认的发声边界在完整时间线末尾合并写一次;不靠“只包含”让执行端 猜,也不自行扩充新的禁用类别。时间线总结只列本镜实际有的声音层。 无对白镜写清底声或静默及其覆盖区间;嘴部动作按剧本表演,不默认强制闭嘴。 非画内配乐按目标方言声明,有需要时明确写空。
成稿时检查动作、声音和限制条件是否清楚且相容。正面状态有助于表达目标,必要的否定条件 可以保留;不按否定句数量判错。确认不跨下一镜、不要求互斥动作、不让人物或道具瞬移变形。
用户要整集就完成整集,镜头组只是内部批次。
同一连续时空中相邻正式生成段、且目标档案支持续接时,默认选择“从上一段生成结果续接”; 一镜一生成只决定每次装一个镜头,不等于各镜独立文生。只有创作者明确要求独立重生成,或上游 已接受时间/地点跳变时才重新起链。选择续接后,这就是该链唯一正式执行路由。第二段起同时绑定上一段 实际视频和从该视频取得的 实际尾帧:视频负责动作、节奏和声音连续,尾帧负责下一段的可见起点。具体素材 role 和续写措辞 由命中的模型方言决定,不能把通用职责直接翻译成供应商字段。未取得真实产物时,后续段在
视频提示词.md标为“待续接、不可提交”,写明缺少这两项真实输入并保留下一段内容草案;不得把它 降级成独立文生视频,也不用计划尾帧、关键帧提示词或文字描述冒充接力输入。逐镜MOTION-...仍是成员边界和内容依据,不是与正式容器路由并列的另一套提交方案。
提示词要求
- 从可见起点开始,以可验证终点结束。
- 可复制正文完整实现分镜的「起点 → 唯一动作 → 终点」;交接、换手和道具落位不跨镜省略。
- 主体、动作对象、方向、幅度、速度和先后关系明确。
- 表演写动作与反应,不写无法执行的抽象结果。
- 运镜与人物动作不争夺注意力;复杂运镜必须有叙事理由。
- 声音只写该镜需要的对白、画外音、环境或转场职责,不擅自补歌词。
- 非画内字幕、caption 和对白文字叠加默认不生成;正文直接声明全镜任何时刻都不生成这些内容,不用抽象工作流术语代替执行指令。只有剧本或创作者明确要求才开启。画内精确文字是独立允许集合,不因关闭字幕而消失,也不让未批准文字混入。
- 同轨对白/VO/OS 同时写逐字文本、口语语言和发声边界;不添加来源没有的声源或重复限制。
- 无对白镜同样说明底声或静默;时间线总结与实际声音层一致。H3 无非画内配乐时写
non_diegetic_music: N/A。 - 正文里被引号或
<d>包起来的中文必须逐字来自《剧本.md》,画内标牌文字可来自《视觉设定.md》; 分镜自己写下的画内文字与声音同样算数。creator_markdown_check.py会核对四字以上的引文(VID-25)。 台词不在剧本里就回到写作技能改剧本,不在提示词里改。 - 上游持续关系用贯穿状态和允许动作通道表达,避免重复限制或冲突动作。
- 本镜命中的连续性锁,锁面原样出现在可复制正文里;颜色、材质、形制这类跨镜不变事实不靠模型记忆。
- 正文可直接复制,不含占位符、流程说明、文件路径或 QA 结论。
按需知识
默认只读本 SKILL、当前分镜和必要视觉事实。遇到对应问题时只打开一份:
- 阶段边界与规则分级:阶段契约
- 单镜运动正文的最小配方:运动配方
- 外部执行需要的明确动作语法:生产提示词语法
- 多人表演、口型和精确时序:表演与动作时序
- 运镜、声音和相邻镜头连续性:摄影与声音连续性
- 动作负载、形变和生成风险:可生成性
- 目标执行端的时长、参考、声音同轨等能力:目标模型能力档案
- Seedance 2.0 的中文分镜、素材与声音语法:Seedance 2.0 方言
- Seedance 2.5 的长叙事、时间戳和任务类型语法:Seedance 2.5 方言
- MiniMax H3 的结构化正文与参考模式:MiniMax H3 方言
- 多镜容器或静态漫剧的交付方式:交付形态
- 完成前的边界检查:审查与示例
用户明确要求时间线音乐时,在本文档增加独立章节,写使用区间、剧情功能、进入/退出、动态曲线和 禁用项;歌词必须来自用户提供或明确接受的文本。静态漫剧只需要关键帧切换与配音时可以跳过逐镜 视频运动提示词,直接进入生产预览。
完成与投产
每个点名镜头都有明确起点、主变化、时序、运镜/声音边界和可验证终点,且与相邻镜头连续,即完成。
实际生成转 $short-drama-produce,先展示精确 job 并取得显式确认;本技能不调用外部服务。
五份创作文档齐备后,可转 $short-drama 对跨文档结构做一次机械核对;内容质量仍由创作者审查。
安装维护
只有安装、升级或排障时运行 python3 scripts/selftest.py。
Files (drama-skills)
-
agents
-
openai.yaml 295 B
interface: display_name: "短剧视频提示词" short_description: "基于镜头边界编写动作、表演、运镜、声音与节奏视频提示词" default_prompt: "使用 $short-drama-video-prompts 根据已接受镜头和关键帧编写以运动变化为核心的视频提示词。"
-
-
assets
-
coverage-scope.fragment.json 685 B
{ "coverage_scope": { "mode": "pickup | alternate", "master_motion_id": "MOTION-<master-id>", "supplements_motion_ids": [ "MOTION-<same-file-record-id>" ], "source_obligations": [ { "kind": "action | reaction | dialogue | reveal | directive | end_boundary", "source_ref": { "src": "screenplay-index", "record_id": "BLK-<EP>-<SC>-<kind><nn>" }, "disposition": "covered_now | retained_in_master | separate_pickup | requires_storyboard_revision", "motion_field": "/<current-motion-field-or-none>" } ], "replacement_intent": "does_not_replace_master | requests_supersession" } } -
delivery-container.jsonl.md 4.8 KB
# `delivery-containers.jsonl` 填写模板 每行一个交付容器对象。容器只是**已接受镜头的排布方式**:它记录哪些镜头按什么顺序装进 同一个交付单位,以及由此得出的容器时长。它不拥有镜头边界、目的、起止状态或时长—— 这些仍属分镜;容器只引用它们并做加法。 只有创作者声明了分段或多镜打包的交付方式时才需要本文件;单镜交付不必创建容器记录, `VID-13` 在那种情形下由运动规格自身的镜头时长满足。 文件第一行是 `sources` 声明:每个上游快照在这里写一次 `owner`、`artifact` 和已接受的 `artifact`,后面的记录用它的 key 引用。key 用产物文件名派生的短小写名字,在本文件内稳定且唯一。 ```jsonl {"record_type":"sources","schema_version":"1.0.0","sources":{"shots":{"owner":"short-drama-storyboard","artifact":"剧集/<EP>/storyboard/shots.jsonl"},"motion-specs":{"owner":"short-drama-video-prompts","artifact":"剧集/<EP>/storyboard/motion-specs.jsonl"},"short-drama":{"owner":"creator","artifact":"short-drama.json"}}} ``` 其后每行一个容器记录,引用写 `src` 加它指向的记录 `record_id` 或字段 `field`: ```json { "container_id": "CONT-<stable-id>", "status": "candidate", "production_profile_ref": { "src": "short-drama", "field": "/creator_authority/production_profile" }, "members": [ { "order": 1, "shot_ref": { "src": "shots", "record_id": "SHOT-<id>" }, "motion_ref": { "src": "motion-specs", "record_id": "MOTION-<stable-id>" }, "accepted_duration_ref": { "src": "shots", "record_id": "SHOT-<id>", "field": "/duration_seconds" }, "accepted_duration": "<从 accepted_duration_ref 读到的值,投影不改写>", "location_binding_ref": { "src": "shots", "record_id": "SHOT-<id>", "field": "/location_binding" }, "asset_bindings_ref": { "src": "shots", "record_id": "SHOT-<id>", "field": "/asset_bindings" } } ], "container_duration": "<各成员 accepted_duration 之和>", "membership_basis": { "source_order_contiguous": "<true | 说明为什么不连续及去向>", "binding_chain_equal": "<true | 说明哪一位成员的绑定不同、以及为什么仍同容器>", "scene_boundary_not_crossed": "<true | 说明跨越原因与已接受依据>" }, "unresolved": [], "provenance": "creator_project" } ``` ## 结构校验点 `VID-13` 靠这条记录本地可证,不依赖阅读渲染文本。带 ✓ 的由 [container_check.py](../scripts/container_check.py) 判定,其余由审查者判定——此前这份清单整体写成「本地可证」,而脚本只实现了其中两项: 1. ✓ `members[]` 非空,`order` 唯一、连续、升序; 2. 每条引用的 `src` 在本文件 `sources` 声明里找得到对应条目; 3. ✓ 每个成员的 `accepted_duration` 等于其 `accepted_duration_ref` 解析后指向的分镜值; 4. ✓ `container_duration` 等于各成员 `accepted_duration` 之和; 5. 每个成员的 `motion_ref` 指向的运动规格,其 `shot_ref` 与该成员 `shot_ref` 解析到同一镜头记录; 6. **逐成员**解析 `location_binding_ref` 与 `asset_bindings_ref`,各成员解析结果相同时 `binding_chain_equal` 才能为 `true`;只引用其中一条成员记录不构成证明。 7. ✓ `membership_basis` 三项都有结论,未成立的写进 `unresolved`,不留空。 任何一项不成立即为结构缺陷,按主技能的 `stale` 与恢复流程处理,不在渲染文本里补救。 ## 依赖方向是单向的 容器 → 运动规格 → 镜头,**不存在反向的文件引用**。运动规格不带 `container_ref`: 两端在各自 `sources` 里互相声明对方会形成环:依赖方向必须单向,否则谁都不能先落盘。 文字上写“只读”不能消除这个环。 要从一个镜头反查它属于哪个容器,扫描容器记录的 `members[]`,不在运动规格里存副本。 ## 与其他记录的关系 - **运动规格**:每条运动规格仍然只绑定一个 `shot_ref`,且不感知容器的存在。 - **渲染文本**:`video-prompts.md` 的容器一节由本记录派生,是缓存,不是权威;文本与本 记录不一致时以本记录为准。 - **成员时长**:一律是分镜的只读投影。要改时长就改分镜,然后重算 `container_duration`; 不得在容器里直接改数。 - **成员资格判据**:语义部分见 `references/delivery-profile.md` 的多镜容器成员资格; 本文件只负责让判据的结论可被引用与核对。 复制后删除不适用的可选字段。容器不跨越已接受的场次或时间跳跃;省略、闪回分支与声明过的 蒙太奇各自单独成容器。 -
motion-spec.jsonl.md 7.8 KB
# `motion-specs.jsonl` 填写模板 文件第一行是 `sources` 声明:每个上游快照在这里写一次 `owner`、`artifact` 和已接受的 `artifact`,后面的记录用它的 key 引用。key 用产物文件名派生的短小写名字,在本文件内稳定且唯一。 本阶段插入的片段([`performance.fragment.json`](performance.fragment.json)、 [`coverage-scope.fragment.json`](coverage-scope.fragment.json))落进同一个文件,它们用到的 key 也在这里声明。 ```jsonl {"record_type":"sources","schema_version":"1.0.0","sources":{"shots":{"owner":"short-drama-storyboard","artifact":"剧集/<EP>/storyboard/shots.jsonl"},"keyframes":{"owner":"short-drama-storyboard","artifact":"剧集/<EP>/storyboard/keyframes.jsonl"},"screenplay-index":{"owner":"short-drama-write","artifact":"剧集/<EP>/screenplay-index.jsonl"},"characters":{"owner":"short-drama-assets","artifact":"设定集/characters.jsonl"},"short-drama":{"owner":"creator","artifact":"short-drama.json"}}} ``` 其后每行一个候选运动规格对象。引用写 `src` 加它指向的记录 `record_id` 或字段 `field`; `boundary_refs` 只读,候选状态属于本运动规格,不向已经接受的上游引用传播。不要加入 `duration_override`、`end_override` 或 `next_shot_write`。以下字符串只说明怎样填写, 不是固定答案。 ```json { "motion_id": "MOTION-<stable-id>", "status": "candidate", "shot_ref": { "src": "shots", "record_id": "SHOT-<id>" }, "keyframe_ref": { "src": "keyframes", "record_id": "KEY-<id>" }, "production_profile_ref": { "src": "short-drama", "field": "/creator_authority/production_profile" }, "boundary_refs": { "duration": { "src": "shots", "record_id": "SHOT-<id>", "field": "/duration_seconds", "value_seconds": 0.0 }, "start": { "src": "shots", "record_id": "SHOT-<id>", "field": "/start_boundary" }, "primary_transition": { "src": "shots", "record_id": "SHOT-<id>", "field": "/primary_transition" }, "end": { "src": "shots", "record_id": "SHOT-<id>", "field": "/end_boundary" }, "next_start": { "src": "shots", "record_id": "SHOT-<next-id>", "field": "/start_boundary", "access": "comparison_only" } }, "reference_bindings": [ { "slot_id": "REF-<stable-slot>", "order": 1, "artifact_ref": { "src": "keyframes", "record_id": "KEY-<id>" }, "role": "start_frame | continuity_video | actual_tail_frame", "may_control": [ "<本镜 accepted 起始构图与可见状态>" ], "must_not_control": [ "<尚未发生的动作/终态/无权威文字>" ], "admission_status": "unverified | creator_described | visually_inspected", "reference_observation_ref": null, "unresolved_risks": [ "<没有观察证据时保留的文字/水印/裁切风险>" ] } ], "start_anchor": { "pose_balance": "<仅运动必需>", "gaze": "<目标>", "hands": { "left": "<状态>", "right": "<状态>" }, "held_props": [ "<exact binding + hand>" ], "spatial_relations": [ "<与行动对象的关系>" ] }, "ordered_subject_motion": [ { "order": 1, "actor": "<asset binding>", "trigger": "<accepted cue>", "action": "<可见动作>", "direction_or_path": "<方向/路径>", "object_or_contact": "<对象/接触>", "result": "<阶段结果>", "timing": { "mode": "relative | explicit", "value": "<顺序词或秒区间>" } } ], "camera": { "behavior": "locked | move | transition", "motivation": "reveal | pressure | alignment | relationship | transition | deliberate_stillness", "intervals": [ { "range": "<相对阶段或秒区间>", "mode": "<lock/pan/tilt/dolly/handheld/follow>", "path_tempo": "<方向/节奏>", "endpoint": "<在 accepted framing/boundary 内>" } ] }, "environment_motion": [ { "element": "<已有环境元素>", "motion": "<有剧情意义的变化>", "cause": "<连续性/主体动作>" } ], "audio": [ { "source_ref": { "src": "screenplay-index", "record_id": "BLK-<EP>-<SC>-D<nn>" }, "speaker_ref": { "src": "characters", "record_id": "CHAR-<id>" }, "voice_direction_ref": { "src": "characters", "record_id": "CHAR-<id>", "field": "/voice_direction" }, "kind": "dialogue | VO | OS | SFX | ambience | music", "exact_text": "<仅 source 有文本时逐字引用>", "delivery_or_spatial_intent": "<不改文本的表演/声源/层级>", "timing": "<相对阶段或秒区间>" } ], "timing_plan": { "mode": "relative | explicit", "phases": [ "<阶段、overlap 与 landing 空间>" ], "declares_overlap": false, "declared_total_or_endpoint_seconds": 0.0 }, "end_report": { "projection": { "pose": "<reported>", "position": "<reported>", "gaze": "<reported>", "hands": "<reported>", "held_props": "<reported>", "visible_state": "<reported>" }, "comparison": "match | mismatch | unrealized", "differences": [] }, "reference_frame_economy": { "frame_carries": [ "appearance", "composition", "base lighting" ], "repeated_for_motion_only": [ "<hand/prop/path 等必要局部>" ] }, "creator_overrides": [ { "rule_id": "<VID-*>", "choice": "<覆盖>", "rationale": "<理由>" } ], "generic_prompt": "<把本规格渲染成一段只含要拍出来的画面的交付文本:从已接受起点的姿态与持物说起,逐条写动作、接触、摄影机行为与终点状态;不写镜头/记录 ID、规则 ID、状态词、工艺备注与成段否定罗列;写满的样子见 references/production-prompt-grammar.md>", "derivation": { "recipe_version": "<version>" }, "provenance": "creator_project" } ``` 运动规格**不带指回交付容器的引用**。依赖方向只有一条:容器 → 运动规格 → 镜头。两端在各自 `sources` 里互相声明对方会形成环——依赖方向必须单向, 永远得不到可发布的稳定快照。要找某个镜头属于哪个容器,从容器记录的 `members[]` 反查,不在本文件里 存副本。容器记录见 [delivery-container.jsonl.md](delivery-container.jsonl.md)。 默认 master 不重复 `purpose_ref` 或 `coverage_scope`:镜头目的、场次计划与原文覆盖从准确 `shot_ref` 及其上游读取。只有 pickup/alternate 才按 [motion-recipe.md](../references/motion-recipe.md) 增加 `coverage_scope`,记录相对母版的补充、保留与去向。 `boundary_refs` 只保留结构校验需要的 duration/start/end/next-start 精确字段投影,不再重复镜头目的、 场次计划或原文职责;删除它前必须先让 timing 与 continuity 校验器能从 `shot_ref` 安全解析同一快照。 普通记录省略 `performance_arcs[]` 与 `attention_handoffs[]`;存在可见表演变化或注意交接时,按 [`performance.fragment.json`](performance.fragment.json) 插入完整字段。空镜、道具细节、纯空间 转场和只有物理动作的镜头不编造 arc。多参考的 `slot_id` 稳定且 `order` 唯一。 `reported_end` 只作比较;末镜没有真实下一镜时改用 `next_start_locator`。附加参考为空时使用空数组;对白说话者与声音方向只有在已接受引用存在时才填写。 母版、补拍和替代关系保留在同一规格文件内,替代决定由审查结论拥有。具体取舍按 `references/motion-recipe.md` 与 `references/review-and-fixtures.md` 判断。 -
motion-terminal.example.jsonl 925 B · in bundle
-
music-spec.jsonl.md 1.8 KB
# Timeline Music Spec Music is a timeline layer, not a default bed baked into every generated shot. Create a record only when accepted story or directing material gives music a specific dramatic job. The file opens with a `sources` header that declares each upstream snapshot once. A source key is short, lowercase and derived from the artifact basename; it stays stable and unique within the file. ```jsonl {"record_type":"sources","schema_version":"1.0.0","sources":{"screenplay":{"owner":"short-drama-write","artifact":"剧集/EP001/screenplay.md"}}} ``` Every record after the header names the snapshot through its key: ```json {"music_id":"MUS-EP001-01","scope":{"episode_id":"EP001","start_seconds":0,"end_seconds":12},"source_refs":[{"src":"screenplay","record_id":"EP001-SC001"}],"narrative_function":"<what changes for the audience, and why picture/performance alone is insufficient>","prompt":"<provider-neutral style, mood, instrumentation, energy and scene description>","mode":"instrumental | song","lyrics":null,"mix_intent":{"entry":"<relative entry against picture or dialogue>","exit":"<relative exit or transition>","duck_under_dialogue":true,"loop":false},"status":"candidate"} ``` - `source_refs` carry `src` plus the record or field they point at; the artifact's `owner` and `artifact` live in the header entry that `src` names. - `scope` is the accepted timeline interval; it does not promise a supplier can generate that exact duration. - `prompt` contains musical intent only, never model names, API fields, artist imitation, remote task identifiers or credentials. - `song` requires creator-owned or licensed `lyrics`; `instrumental` carries no lyrics. Do not ask a provider to silently rewrite accepted lyrics. - `mix_intent` is an editorial handoff. Production generates a source track; editing still owns exact fades, ducking and placement. -
performance.fragment.json 1.2 KB
{ "performance_arcs": [ { "actor_ref": { "src": "characters", "record_id": "CHAR-<id>" }, "trigger_ref": { "src": "screenplay-index", "record_id": "BLK-<EP>-<SC>-<kind><nn>" }, "agenda": "<本镜需要读出的当前策略;无增益时删除>", "receive": "<角色怎样接收触发;无内在处理时删除>", "mask": "<确有掩饰时才保留>", "visible_leak": "<当前景别可读的目光/尾音/手部/距离信号;没有则删除>", "choice": "<说、做或刻意不做的方向性选择>", "landing": "<选择怎样落到 accepted end>" } ], "attention_handoffs": [ { "from_ref": { "src": "<注意归属方产物的 sources key>", "record_id": "<exact-attention-owner-record-id>" }, "trigger_ref": { "src": "screenplay-index", "record_id": "BLK-<EP>-<SC>-<kind><nn>" }, "to_ref": { "src": "<注意归属方产物的 sources key>", "record_id": "<exact-attention-owner-record-id>" }, "signal": "<使注意转移的可见或可听信号>", "readability": "<当前景别、遮挡和声源为何读得到>" } ] } -
video-prompts.md 5.1 KB
# `video-prompts.md` 可复制输出模板 这是由已接受运动规格生成的文本,不是分镜权威来源。复制引用块即可;元信息帮助核对边界。 **这份模板属于结构化管线**(`motion-specs.jsonl` 一路)。creator-first 项目的《视频提示词.md》骨架 以 `SKILL.md` 为准:`## MOTION-... · 中文名`,字段依次是「分镜、时长、生成方式、输入参考图、 静态视觉锚点、起始帧、状态链、终点」。两边的字段名不一样,不要把这里的 `运动规格`、`边界核对`、 `声音引用` 或 `### 只读结束报告` 搬进 creator-first 文档。 ```markdown # EP<编号> · 视频提示词 > 来源:`motion-specs.jsonl` 的已接受记录 > 配方:`motion-generic@<version>` > 范围:本文件仅提供提示词,不触发媒体服务;实际视频生产交 `$short-drama-produce` > 正式执行路由:`逐镜 | 多镜容器 | 从上一段实际结果续接`(只保留本次已选择的一项) ## `SHOT-<id>` · <镜头目的短句> - **运动规格**:`MOTION-<id>` - **覆盖范围(仅补拍/替代版)**:`pickup | alternate`;母版/补充 `<master_motion_id / supplements_motion_ids>`;逐项内容 `<source_ref → motion_field/disposition>`;替代请求 `<replacement_intent>` - **起始帧**:`KEY-<id>` - **生成方式**:`文生视频 | 图生视频`;文生视频必须已有创作者明确选择 - **输入参考图**:`REF-<slot>(顺序:<n>)· <项目相对路径>《<中文名>》(用途:<purpose>;控制:<may_control>;不得控制:<must_not_control>)`,图片由创作者在生成时自行挂载时改用 `PLAN-<slot>(顺序:<n>)· <IMG-... 或 SHOT-...>《<中文名>》(同样的用途与控制字段)`;`用途` 取 `身份|造型状态|地理|构图|尺度|效果|起始帧|结束帧|风格`;必须与分镜一致且全部就绪;只有创作者明确选择文生视频时可写「无(创作者已明确选择文生视频)」,无图或部分有图的「待补参考图」都不得进入最终视频提示词 - **静态视觉锚点**:文生视频必须把本镜必要的身份、造型、地点、构图与光线事实写在这里并原样纳入可复制正文;图生视频只补充动作所需、参考图不能独立说明的可见起点 - **状态链**:`<起点> → <唯一动作:触发、接触与落点> → <终点>`;直接投影分镜,不补镜外转换 - **时长(只读)**:`<seconds>s` - **边界核对**:`end match | mismatch | unrealized` - **声音引用**:`<dialogue/VO/OS/SFX ids>` - **注意**:<无法执行的风险 / 交给负责技能的修改请求;无则写“无”> ### 可复制通用提示词 > 从<最少但够用的起点信息>开始。<按因果和物理顺序写主体动作>。<本镜确有表演变化时,写承担变化者可见的触发、选择或落点;否则删除本句。>摄影机<有动机地固定或移动,并写清节奏和终点>。<必要环境运动>。对白/声音:<逐字文本/引用、表演方式、声源与层级>。在<已确认时长>内<安排节奏>,最终<逐项实现已确认终点,不写下一镜>。 ### 只读结束报告 - **位置/姿态**:<当前描述 → 来源:是否匹配?> - **目光/双手/持物**:<当前描述 → 来源:是否匹配?> - **可见状态**:<当前描述 → 来源:是否匹配?> - **下一镜**:仅比较 `<下一镜起点引用>`,未改写 --- ``` 普通母版省略“覆盖范围”一行;没有补拍或替代关系时,不用 `master` 占位制造版本账目。 每个已经设计好的镜头独立一节,即使被打包进同一个交付容器,也不合并原镜头边界。 项目声明多镜容器时,容器一节**由 `delivery-containers.jsonl` 的对应记录派生**(模板见 [delivery-container.jsonl.md](delivery-container.jsonl.md)):按记录里的 `order` 列出成员 镜头及各自已接受时长,容器时长直接取记录的 `container_duration`。本文件是缓存,不是权威—— 不要在这里直接编辑成员、顺序或时长,也不要在没有对应记录的情况下自造一个容器一节。 成员镜头各自的边界、终点报告与审查入口保持独立。 若自然语言修改涉及时长、终点、对白或下一镜,展示交给负责技能的修改请求,而不是 修改本文件来掩盖来源变化。 连续交付段若选择从上一段生成结果续接,后续容器不得降级成普通文生视频。真实产物尚不存在时写: ```markdown - **就绪状态**:待续接、不可提交 - **接力输入**:等待上一容器实际视频与从该视频抽取的实际尾帧;两者缺一不可 ``` 此时可保留下一段内容草案,但不绑定虚构槽位、不声称已经续写。真实产物存在后才增加「接力输入」: 上一段实际视频使用 `continuity_video` 角色,直接从该视频取得的实际尾帧使用 `actual_tail_frame` 角色。 两者同时绑定;前者承接动作、节奏和声音,后者固定可见起点。计划尾帧、冻结关键帧或文字描述不能 代替任一项。逐镜章节在多镜/续接路由下是成员依据,不是另一套可提交任务。
-
-
examples
-
minimal-music-specs.jsonl 806 B · in bundle
-
-
references
-
camera-audio-continuity.md 14 KB
# 摄影机、环境声音与只读连续性 ## 摄影机合同 - `VID-06` — locked 与 moving 指令不得同时统治同一区间,除非明确的 transition 将两段分开。 - `VID-07` — locked/moving 的选择,以及 audio/lip-sync 细节量,都是从 production profile 继承的 `taste_option`。 ## 相连边界 (`CON-01`) Motion end report 与下一 accepted shot start 必须一致,否则指明用于协调的 上游 owner revision。不要用流畅的动态措辞把连续性矛盾正常化。 ## 目录 1. 摄影机为什么动或不动 2. Camera contradiction 3. 环境运动 4. 对白、口型与声音 5. 只读边界与修订路由 ## 1. 摄影机为什么动或不动 Camera behavior 要回答“观众注意为什么在此刻改变”。常见动机: - **reveal**:新信息进入视野; - **pressure**:距离收紧让压力增大; - **alignment**:从旁观转向贴近某角色立场; - **relationship**:重新组织两人距离/权力; - **transition**:把注意交给 accepted end 或出口。 没有动机时 lock-off 是积极选择:镜头稳定地等待角色完成一次艰难决定,比装饰性环绕更有力。 ### 描述 lock-off 锁定和 movement 一样要被写出来。一次 lock-off 至少写: 1. 写死的 framing:景别、机位高度、轴线哪一侧; 2. 这段时间里画面内还有什么在动——通常只剩表演和环境,明确列出来; 3. 保持多久。 “固定特写,俯拍,轴线左侧,取她的头肩;画面内只有她的呼吸和身后帘子在动;保持约两秒半不动。” 比只写“固定机位”可执行,也比什么都不写安全:摄影机一栏空着会被执行端读成可以自由漂移, 于是本该稳住的镜头长出一次没人要过的推近或环绕。 ### 描述 movement 一次 movement 至少写: 1. 起始 framing/相对位置(来自 shot/keyframe); 2. 何时、因何开始; 3. 路径/方向与节奏; 4. 跟谁/揭示什么; 5. 结束 framing 或空间关系(必须在 storyboard 边界内)。 “轻微推近”若不说明触发和落点,仍然含糊。“镜头在他把钥匙放上桌时开始短促推近,钥匙落下后停在双手与钥匙的近景”才有可执行注意路径。 移动不能重置地理:若机位沿同一场景移动,仍要保持已接受的工作侧、入口—主体—出口关系 和屏幕方向;确需越轴时沿用 storyboard 已接受的重新建立方法,不把一次漂亮移动写成新的空间。 - **`craft_default`**:一个 authored shot 优先一个主要 camera idea;细小修正服务同一 idea。 - **`reviewed_invariant`**:move 必须与 shot purpose、表演和 blocking 相容,不能穿过墙体或错过关键动作。 - **`taste_option`**:locked、handheld、dolly、pan/tilt、角色跟随或静观均可,由 visual direction 决定。 ## 2. Camera contradiction ### 同一区间的显式冲突 - `locked-off` 同时又要求 pan/dolly/handheld 漂移; - “机位完全不动”同时“跟随角色穿过房间”; - 同一时间既向左 pan 又向右 pan,且没有反向 transition; - camera endpoint 与 accepted shot framing/end boundary 显式不相容。 这些可在规格的 interval/behavior 中确定比较,属于 **`structural_invariant`**。修复是选择一个行为或写清时间 transition: ```text 0.0–1.2s locked:等待她抬眼; 1.2–4.0s slow push-in:从双人中景收至她的头肩,终点保持既定轴线。 ``` 这里不是同时 lock 和 move。若 transition 改变 shot 已接受的 camera design,仍需 storyboard 同意。 ### 摄影机与主体的相对运动 主体快跑与摄影机缓推可以同时成立,关键是写清两者的相对关系与画面结果。 需要跟随时说明跟随方向和构图;需要人物离开画面时,保留缓推或固定机位也可以。 不能为了消除速度差异而改写上游已确定的运镜。 ### 运动说明 按本镜需要说明运动方式、方向、速度或触发时机、终点以及要保持的空间关系。 与上一节的起点和落点一起核对,不要求每次恰好四项。 ### 运镜术语属于方言 推轨、变焦、摇、甩、升降这些词在不同目标模型里的解析并不一致——有的模型的官方指南 推荐 `push in` 而完全不收录 `dolly in`。**不要把一个模型上验证过的运镜词表照搬到另一个 模型的正文里**;术语与写法以目标模型的方言文档为准,方言里没有记载的词按未验证对待。 ### 非冲突但可能过载 pan 同时微调焦点、handheld 带自然呼吸、dolly 配合轻微 tilt 未必矛盾;是否太复杂由 reviewer 结合 purpose 判断,不能靠 camera 动词计数硬挡。 ### 切镜边界 “切到门外、再切回特写”是新的 editorial cut,不是 camera movement。保持各 authored shot 独立:把多个已接受镜头按来源顺序装进同一个交付容器是允许的排布方式,把它们融成一个边界不明、无法逐镜审查的 motion 不是。判据在于每个 source shot 是否仍能被单独引用、单独比对终点。容器分层见 [delivery-profile.md](delivery-profile.md)。 ## 3. 环境运动 环境 motion 应来自连续性与 shot purpose:风、雨、蒸汽、布料、机械、群演、光影变化、液体/尘埃对主体动作的物理响应。 选择规则: - 与主体接触或提供 cue 的优先; - 能加强焦点但不抢戏的保留; - 已由 reference frame 静态承载、且不会变化的省略; - 未在 accepted state 出现的天气、火、破坏、群众事件不得为“丰富画面”发明。 **`craft_default`**:每镜只写少量 story-relevant environment motion**。`reviewed_invariant`**:环境变化不能造成新的故事事实或 continuity teleport。 ## 4. 对白、口型与声音 若分镜已经明确整场声音策略,先把主导声源、撤出/恢复、距离变化、 主动留白与 sound bridge 分配到准确镜头,再写本镜 audio。逐镜实现必须能与相邻镜接上,不能 让每镜各自重新启动一套环境音。场次计划没有登记的故事声源不得由本层发明;音乐仍服从下文的 时间线边界,也不能替代表演或转向。 ### 精确引用 每个 `MOTION-...` 直接引用 `剧本.md` 的场景 ID、准确对白/VO/OS/SFX 和对应 `SHOT-...`。可复制正文 按交付需要呈现逐字台词或声源说明,但不得改字、增删、交换说话人或把对白改成 VO。 同一句对白跨镜延续时,把相邻提示词串读,说明它在本镜是开始、画外延续、 被打断还是到此结束;不得在前镜声称已经说完,后镜又让同一句继续。没有逐字原句时只保留 准确引用和延续关系,不为填满节奏补写台词。 若 source 写 `[VO]`、`[OS]`、`[SFX]`,保持声源性质: - dialogue:画内说话者与口型/表演相关; - VO:声音不要求当前画面口型; - OS:说话人不在画面或不见口部,保持空间方向; - SFX:说明事件与声源/时点; - ambience:连续底声,不能遮蔽剧情必需对白; - music:写叙事功能、进入/退出与层级,不用曲名模仿未授权作品。 ### 配乐属于时间线层 (`VID-14`) 先分清两件常被混在一起的事: - **标注配乐意图**:在规格里写清这一镜需要什么情绪的音乐、从哪里进、到哪里出、压在 对白之上还是之下。这是**应该写**的——它是给时间线层的输入,写清了才可能对齐。 - **让单镜产物自带配乐音轨**:交付出来的单镜素材内部已经烧进了一段音乐。这是缺陷。 **`craft_default`**:配乐意图可以逐镜标注,但配乐的**实现**归时间线层;单镜产物默认 只承载同期对白、画外声源、环境底声与事件音效,不自带配乐音轨。 理由是连续性方向:配乐的进入、退出与层级跨越多个镜头,是时间线上的决定;一旦每个镜头 各自烧进一段音乐,剪辑点就会出现无法对齐的硬切,而这个缺陷在单镜文本里看不出来——它只在 镜头相接处暴露。把音乐的实现留在时间线层,单镜之间才可以自由调换、补拍或替换。 - 逐镜标注时,写这一镜在音乐上的**相对位置**(延续上一镜、在此进入、在此退出、此处让路 给对白),而不是各镜独立描述一段音乐。相邻镜的标注必须能接上;接不上就是一处矛盾。 - 画内声源(收音机、现场演奏、手机铃声、隔壁排练)本就属于 SFX/ambience,随画面空间 变化,不受本条约束——但要写明它是画内声源,否则会被当成配乐层处理。 - 若项目已接受“单镜自带音乐”的做法(例如整集只有一个容器交付),按已接受方案写。 - 需要在提示词里显式排除音乐时,只写“不要配乐”这一层意图,不连带否定同期声与环境声—— 把三者一起否掉会让本该有的现场感也消失。 - 音乐的密度、风格与情绪仍是 `taste_option`(见下),本条只规定它归哪一层,不规定它多不多。 ### 环境底声跨镜相接 配乐的推理同样适用于底声,只是结论不同:底声**留在单镜产物里**,但它必须能和相邻镜接上。 **`craft_default`**:同一场次内相邻镜的底声成分与相对层级保持一致;只有换场次或换 Location/View 才允许变化,并写清变化来自什么。写法与配乐标注对称——延续、在此进入、 在此退出。 不这样做的后果比配乐更隐蔽:每个单镜各自生成一段自己的房间底噪,剪在一起每个切点都 "呼"一声换空间。配乐至少有上一条兜着,底噪没有;而单镜文本里永远看不出问题,只有素材 相接时才暴露。 ### Delivery 与 lip-sync 可以描述速度、音量、气息、停顿、打断、称呼重音、口型意图,但不得修改台词文本来配动作。 - **`craft_default`**:对白主导镜头减少竞争性动作/camera,让 reaction 有落点。 - **`reviewed_invariant`**:delivery 必须符合角色 agenda 和表演弧;不能把威胁台词提示成真诚道歉,除非剧本有反讽依据。 - **`taste_option`**:严格口型、近似同步、VO 优先、现场感/配乐密度由 production profile 决定。 若 Character 已有 `voice_direction`,运动规格分别绑定角色身份引用、逐字台词引用与声音 方向引用;本场的气息、音量、停顿和情绪仍写在 delivery,不能覆盖持续音色/发音锚点。 声音参考只在创作者授权且有可解析的本地产物记录时进入,缺失时保持文字方向,不编造服务 ID 或网址。即使引用齐全,本套件也只证明文本绑定一致,不能承诺真实音色、配音或音画同步。 ### 音频矛盾 - exact dialogue 同时要求“全程无人说话”; - 同一 SFX 既在动作前触发又由该动作产生; - source 是 VO,却要求清楚同步画内口型; - accepted quiet scene 被新增爆炸声改变故事; - 相邻镜头各自声明了互不衔接的配乐进入/退出,或单镜自带配乐床却没有已接受的依据。 Resolvable ref/显式否定可结构检查;delivery 是否语义背离则要 reviewer 证据。 ## 5. 只读边界与修订路由 ### Motion 可以改 - 同一边界内动作的可执行路径与细节; - accepted emotional change 的外显方式; - 既定 camera idea 的节奏/触发实现; - accepted audio 的 delivery、空间化和层级; - 不改故事事实的环境微动; - prompt 的语言经济性。 ### Motion 不可以改 - shot duration、start/end pose/position/gaze/hands/held props; - location/View、asset variant、axis/next-shot state; - screenplay action fact、dialogue/VO/SFX 文本或说话人; - 增删 editorial cut; - 连续性中谁知道什么、谁拥有物件、伤势/天气/光线状态。 ### 当上游为本镜同时交出首帧和尾帧 首尾帧接续是常用工作流:执行端拿到两端画面,中间由它补。这对运动规格的含义要说清楚, 否则容易两头落空。 **两端不是运动规格的替代品,但会实实在在收窄它**。 执行端会优先让成品对上这两张图, 中间怎么走由它决定。所以: - 中间**必须被看到**的动作不要只靠插值兑现。某个动作是本镜存在的理由时,两端之间没有 任何机制保证它发生——把它移到某一端,或者发修订请求把镜头拆开。 - 两端差异越大,插值越自由。首尾帧适合「状态改变清楚、路径无所谓」的镜头; 「路径本身是戏」的镜头(夺取过程、跟随移动、逐步逼近)不适合交给两端夹逼。 - 运动规格照旧要写清动作顺序、对白落点与摄影机行为,**不因为有了尾帧就省略**。 尾帧说明「到哪」,运动规格说明「怎么到、中途观众必须看到什么」,两者不重复也不互替。 - 终点报告仍然逐字段对照分镜的 `end_boundary`,**不对照尾帧**。尾帧只是该边界的投影, 画面对上了不等于终点事实到达了——尤其是持物归属和目光对象,两者最容易只在构图上成立。 ### Revision request ```markdown - **请求 owner**:`storyboard | write | assets` - **来源证据**:文件、场景/镜头 ID 与必要短引文 - **当前边界**:精确值 - **motion 需要**:期望变化与原因 - **影响**:purpose/feasibility/continuity - **建议**:split | extend | reblock | amend dialogue | reconcile asset - **未执行**:列明本轮没有修改的 owner 文件 ``` Video-prompts 可以提议,不能替 owner 应用。若实际终点不同于分镜终点:先判断是提示词没实现 (video-prompts 修)还是创作确需新边界(storyboard 修);无论哪种都不把观察到的终点当成下一镜权威。 ### Handoff 核对 逐字段比较运动终点与镜头终点;下一镜仍直接读取分镜拥有的起点。匹配才是实现证据,不能用一句 “保持连续”代替位置、目光、双手、持物等关键事实核对。 -
delivery-profile.md 19.9 KB
# 交付档案:容器算术与槽位语义 ## 目录 - [档案归属](#一档案由创作者声明本文只规定槽位职责) - [容器之外的交付路由](#容器之外的交付路由) - [容器算术(单镜容器与多镜容器)](#二容器算术唯一允许的数字规则) - [全集账目:一个镜头只能被算一次](#全集账目一个镜头只能被算一次vid-15) - [槽位语义](#三槽位语义表) - [必须原样保留的文本](#四必须原样保留的文本) - [合成示例](#五合成示例记号来自创作者档案) - [自检](#六自检) ## 一、档案由创作者声明,本文只规定槽位职责 交付档案是创作者**可选**声明的一份排布约定:把已接受的运动规格摊成执行端逐行读取的 文本。本套件只规定每个槽位**必须回答什么**(语义),不规定它**写成什么样子**(语法)。 - **槽位语义属于本套件**:一个位置槽必须回答“相对哪个画面内可命名的锚点”,换任何 执行端这个问题都成立,也不含任何供应商内容; - **槽位拼写属于创作者档案**:用什么括号、分隔符、编号前缀和字段名,随执行端更换而 作废,本套件不规定,也不把任何一种拼写当作标准格式; - **项目没有声明档案时**,按 [motion-recipe.md](motion-recipe.md) 写连贯的自然语言即可。 不要为了“像个格式”自造槽位,空槽位比缺槽位更难审查。 数字规则仍然一律拒绝,只有两处例外,因为它们是对**创作者已接受值**做算术,不引入 任何新标准:时长在两个层级上都必须精确加总(分段之和等于所属镜头的已接受时长,容器 时长等于其成员镜头已接受时长之和);分段标识必须唯一且有序。除此之外的档位、配额、 每镜增量和元素上限都不由本文档提出。 ### 容器之外的交付路由 容器回答的是“一次交付里装几个已接受镜头”。有些执行方式还会改变**这一次从哪里开始生成**, 那是另一个维度,容器字段表达不了: | 路由 | 起点来自 | 对本套件产物的影响 | |---|---|---| | 逐镜(默认) | 该镜已接受的起始边界 | 无 | | 多镜打包 | 首个成员镜头的已接受起始边界 | 无,按本文容器算术记账 | | 单次长生成 | 覆盖段落中第一个镜头的已接受起始边界 | 无;**按多镜容器记账**(见下) | | 从已生成结果续接 | **上一次生成出来的结果** | 多一层证据义务,见下 | 前三种只改变打包粒度。镜头目的、起止边界与逐镜可审查性不因为”这次一起生成”而消失。 **单次长生成不是容器之外的第三种账**。 它承载多个已接受镜头,所以就是一个多镜容器, 照 `VID-13` 与 `VID-15` 记账:成员来源顺序连续、共享同一条绑定链、不跨场次边界, 容器时长等于成员已接受时长之和,且在全集账目里只被算一次。它只是把切换交给执行端 在**已接受的成员边界上**完成,不把若干镜头合并成一个没有内部边界的长镜。 若一次长生成想跨越 Location/View 变化或场次边界,先拆成两个容器——这一条没有例外, “执行端一次能读多长”不是创作决定。 **第四种不一样**:它的起点是一段已经生成出来的媒体,而这段媒体在本套件里不是权威产物, 最多只能是一条 `generated_result` 观察记录(两种观察的区别由核心的参考资料定义)。 因此续接路由要同时守住两条: - **已接受的镜头起始边界仍是唯一权威**。续接不产生第二个起点取值权威;不能因为“结果看起来 是这样”就把观察到的状态回写成镜头边界。两者不一致时先回分镜发修订请求,不在交付层调和。 - **对结果状态的任何主张都要绑定观察记录**。没有创作者提供或授权形成的观察时写 `unverified`, 并说明续接依据缺失。不要凭想象补一个结尾画面:续接提示词的每一条连续性指令都建立在那个 起点上,起点是编的,后面整条链一起作废。 连续段真正进入生产时,下一段同时使用两项真实输入: - **上一段实际视频**:承接已经生成出来的动作速度、人物表演、运镜、环境运动与声音空间; - **上一段实际尾帧**:从该视频直接取得,作为下一段第一帧的人物姿态、双手、持物、光线与构图起点。 二者职责不同,不能只给其中一项,也不能用计划尾帧、分镜关键帧或文字描述冒充。提示词仍按下一段 已接受的「起点 → 唯一动作 → 终点」写;实际尾帧若与已接受起点冲突,先处理上一段结果,不能把漂移 写进下一段继续放大。 创作者已经选择续接后,缺素材只改变**就绪状态**,不改变执行路由:后续容器标成“待续接、不可提交”, 列明缺少上一段实际视频与实际尾帧,继续保留从已接受起点写出的内容草案;不得把它改称普通文生视频。 真实素材到位后再按模型方言加入续接触发词与实际槽位。逐镜运动正文继续承担成员边界和内容依据, 不能被当作绕开续接的平行提交方案。 路由由创作者档案声明。项目没有声明时按逐镜写,不为“支持更多路由”预留空字段。声明后在 `视频提示词.md` 只标一个正式执行路由;其他粒度的正文若因可审查性而保留,要明确标成成员依据。 这里的“逐镜 / 多镜容器”是打包粒度,不自动表示独立生成。同一场次、时间连续、地点与资产绑定链 不变的相邻正式段,在目标档案支持续接时默认组成实际结果接力链:第一段从已接受起点生成,第二段起 等待上一段实际视频和实际尾帧。只有创作者明确选择独立重生成,或已接受内容发生时间/地点跳变时才 重新起链;不能仅凭 `one-shot-per-generation` 把每镜都解释成互不相干的文生任务。 ## 二、容器算术(唯一允许的数字规则) 容器是执行端一次读取的交付单位。它可能正好等于一个已接受镜头,也可能按创作者声明的 打包方式承载**若干个连续的已接受镜头**。两种形态下算术都只对创作者已接受的值做加法, 不引入新标准: - **单镜容器**:容器时长 = 该镜头的已接受时长。分段是容器内的有序切片,用来给动作 因果分配时间,**不是新的剪辑边界**——一个分段内不允许出现第二次切换(见 [review-and-fixtures.md](review-and-fixtures.md) 的段内藏切)。 - **多镜容器**:容器时长 = 各成员镜头已接受时长之和。成员之间的边界仍然是真实剪辑 边界,打包不合并、不改写、也不重新分配它们;每个成员镜头内部再按上一条切分段。 两种形态的区别只在“容器里有几个镜头”,不在“镜头是什么”。把多个镜头装进一个容器 不会让它们变成一个长镜,也不会让它们失去各自的目的、边界与可审查性;反过来,容器时长 若不等于成员时长之和,多出或少下的时间会落在没有任何镜头负责的区间里。 **`structural_invariant`**:容器只能承载已接受镜头;容器时长等于其成员镜头已接受时长 之和;镜头边界、镜头目的与逐镜独立可审查性不因打包发生变化(VID-13)。 这条不变式必须**可本地核对**,所以需要多镜打包时,在 `视频提示词.md` 增加“交付分组”章节: 用稳定组 ID 依次列出成员 `MOTION-...` / `SHOT-...`、各镜时长、容器总时长,以及同场、连续、 同一地理/资产链的理由。单镜交付不需要额外章节。 ### 双向和不变式 **`structural_invariant`**:在每个成员镜头内部,所有非重叠分段的时长相加必须**等于** 该镜头的已接受时长;重叠段要写明重叠关系。两个方向都是缺陷: - **超出**:落在**该成员镜头**已接受时长之外的那部分被执行端截断,段尾的动作、逐字 对白和终点姿态一起丢失,`end_report` 会声称到达了从未被拍出的终点。在多镜容器里, 它还会挤占下一个成员镜头的起点,把两个镜头的边界一起搞坏; - **不足**:未分配的余量不会空着。执行端会用未经批准的动作、表情或镜头运动把它填满, 而这些内容没有任何上游来源,审查时也无从追责。修法是把余量分配给已有阶段,或补一个 有内容的收尾段(落住反应、保持终点姿态),不要留白。 ### 多镜容器的成员资格 打包不是把相邻镜头凑够时长。可以同处一个容器的镜头必须同时满足: - **来源顺序连续**:在已接受的覆盖里相邻,且容器内顺序与来源顺序一致;不跳镜、不重排。 - **绑定链不变**:共享同一条已接受的地理与资产绑定。出现新的 Location/View、新的资产 变体、新的可读文字义务或任何未解决绑定时,必须换容器——否则执行端会把绑定切换当成 同一空间内的位置移动。 - **不跨场次边界**:容器不跨越已接受的场次或时间跳跃。省略、闪回分支与声明过的蒙太奇 各自单独成容器。 - **各成员仍可单独审查**:每个成员镜头保留自己的目的、起止边界与终点报告,容器只是 它们的排布方式,不是新的权威记录。 不满足时就拆容器。容器数量由绑定链和场次决定,本文不设配额;执行端一次能读多长也不是 创作决定,若打包方式与镜头边界冲突,改打包方式,不改镜头。 ### 全集账目:一个镜头只能被算一次(`VID-15`) 单个容器的时长加总正确,不代表全集的账是对的。容器逐个看都成立,而某个镜头同时出现在 两个容器里,或者某个镜头谁都没装——两种错误在单容器视角下都看不见。前者让全集时长凭空 多出一段,后者让一个已接受镜头无声消失,而它对应的台词、绑定与关键帧提示词都已经做完了。 **`structural_invariant`**:在一集之内,一个镜头最多属于一个容器。容器不必覆盖全部镜头, 未装容器的镜头可以直接交付;但**容器成员与散镜合起来,必须不重不漏地正好覆盖本集镜头集合**。 重复计入或无人认领都是缺陷,不是打包偏好。 对账方法就是集合运算,不需要判断:把各容器的 `members` 展开取并集,与覆盖记录里本集的 镜头集合相比——交集之外的差集两侧都必须能解释。任一侧非空且解释不了时,先修容器划分, 不要调整镜头时长去凑总数。这条与 `SHT-16` 的加总互为验算:镜头侧给出本集应有的总时长, 容器侧给出实际打包出去的总时长,两个数字必须相等。 有一种情况拆容器解决不了:**单个已接受镜头本身就长于执行端一次能读的长度**。容器只能 承载整镜,分段又不是剪辑边界,所以这里没有打包层的解法——回分镜环节发修订请求, 把该镜头拆成两个已接受镜头,而不是在交付层把它截断或硬塞。 ### 分段标识 分段标识必须唯一、连续,并按时间升序。重复或跳号会让引用无法解析,也让审查者无法把 问题定位到具体一段。分段数量由动作的因果节点决定,本文不设配额;但把整镜写成一个不 分段的整块,等于把全部时间交给执行端自行分配,这是最常见的失控形态——不是因为“段 太少”,而是因为没有任何一段说明这几秒里先后发生了什么。 ## 三、槽位语义表 | 槽位职责 | 该槽位必须回答什么 | 省略会怎样失败 | |---|---|---| | 空间 | 本段发生在已接受场景的哪个可命名区域?画面外紧邻的是什么? | 执行端沿用上一段残留或参考图猜测环境,同一场景在段与段之间换了地理,正反打穿帮 | | 姿态 | 段首人物的重心、朝向、双手与持物是什么?段尾变成什么? | 段与段之间人物瞬移、换手、道具凭空出现或消失,接触动作失去因果顺序 | | 位置 | 相对哪个**画面内可命名的可见锚点**?距离与朝向怎样可比较地表述?(裸写的左右指画面左右;人物自身的左右要带主体) | 只剩绝对坐标或“靠左一点”,执行端按自己的构图重排人物,轴线与遮挡关系随段变化 | | 表演 | 触发是什么,可见处理是什么,落到哪个状态?这个形容词要演到多硬? | 只剩情绪标签,执行端按平均值演;同一个形容词在不同段被演成完全不同的强度 | 用法: - 表演槽与剧本 `(情绪)` 括注同源:同一个括注同时决定表演状态和说话方式。三者 (标尺值、可见表演、说话方式)互相矛盾时以剧本括注为准,需要改动就发修订请求; 形容词要演到多硬的问题见 [performance-action-timing.md](performance-action-timing.md) 的表演标尺。 - 每个槽位只写本段执行需要的内容。参考帧已经承载的外貌、整套服装和静态构图不重复。 - 槽位留空好过槽位填废话。没有依据就留空并记为未解决,不要用“自然”“电影感”“氛围 拉满”占位——这类词占住了槽位,却没有回答槽位的问题。 - 交付文本里不出现 `hash`、参考图文件名、内部规则 ID、锁定标记、草图指代或任务备注, 见 [production-prompt-grammar.md](production-prompt-grammar.md) 的“交付文本只含交付内容”。 ## 四、必须原样保留的文本 交付文本里有两类内容不能被改写,失败方式相反:台词被改写会**念错**,触发词被改写会 **什么都不发生**。两类都由创作者档案声明,本套件都只规定它们必须回答什么。 ### 逐字对白围栏 必须逐字保留的文本要用档案声明的**定界符**与描述文本隔开:围栏内是原样保留的台词, 围栏外是可以改写的描述。围栏是一道文本边界,不是风格选择——没有它,执行端会把台词 当描述改写、翻译或压缩,也可能把描述句当成台词念出来。 - 围栏内只放台词本身与必要的说话人标识;情绪括注、导演提示和动作说明写在表演槽或 围栏外的相邻描述里。 - 围栏用哪一对符号来自创作者档案,本文不规定。 - 画外音、旁白与同期对白各自带明确的声源标识;把三者混进同一段无标识文本,执行端无法 决定要不要对口型。声源标识里一并写这句台词的**口语语言**:说话人的 `voice_direction.language` 已经定了就照写,没定就向创作者确认后再写,它与描述文本用什么 语言无关。不写,执行端就按描述语言念,得到错的配音语种和错的口型。 ### 执行触发词 有些执行方式要求交付文本里出现**固定字样**,才会走到对应的处理路径。它和逐字对白同属 不可改写的文本,但风险方向相反。 **按字面匹配的入口,通常不会因为字样被改写而报错**——改写后的句子仍然是一段合法描述, 于是那条路径没有被选中,而请求照常返回。本提示词环节不调用生成服务,因此不断言任何具体 入口的实际行为;这里只说明为什么这类改写值得当成缺陷防:**它的失败是安静的**, 交付文本读起来完好,不像超时或参数错误那样自己暴露出来。 至于某一次结果是否真的走了那条路径,属于结果服从度,只能由绑定的生产观察支持, 不能从提示词文本推断。 - 触发词由创作者档案声明。本套件不规定任何具体字样,也不推断某种执行方式需要哪些词; 档案没有声明就不写,凭空加入的“固定字样”只会变成画面上的文字或多余的旁白。 - 一旦声明,就在该路由的交付文本里**逐字节出现**:不同义替换、不翻译、不省略,也不为了 “读起来顺”改写语序。这与参考编号的稳定性同源——都是执行端按字面匹配的记账,不是措辞偏好。 - 触发词写在描述文本中,**不进逐字对白围栏**:围栏内的内容会被当作要念出来或要显示的台词。 - 触发词只声明路由,不替代内容。写了续接字样不等于说明了续接从哪个状态开始, 该写的起点、动作与终点一条都不能少。 **`reviewed_invariant`**:档案声明了某路由的必需字样时,该路由的交付文本逐字保留它们; 审查者拿档案声明与交付文本比对(`VID-19`)。档案给出可机读字样清单后校验器才可能接管, 在此之前不声称机械阻断。 ## 五、合成示例(记号来自创作者档案) 以下人物、场景与记号均为合成材料。这是一个**单镜容器**:镜头已接受时长 5.0 秒,容器 时长同为 5.0 秒,三段相加正好 5.0 秒。若同一场戏的下一个镜头也被装进这个容器,容器 时长就变成两镜已接受时长之和,而每个镜头内部各自重新分段。 ```text [段1 | 0.0–1.6] 〈空间〉檐廊内侧,右手边是通向内院的门;门外区域本段不入画 〈姿态〉角色甲半跪,重心在右膝,左手扶栏,右手在竹箱内翻找 〈位置〉角色乙站在角色甲右后方约两步,位于角色甲视线之外 〈表演〉角色甲听见身后一次落锁声,翻找动作停住,未回头 〈对白〉无 [段2 | 1.6–3.4] 〈空间〉同上 〈姿态〉角色甲右手离开竹箱、按住箱盖;角色乙双手仍垂在身侧 〈位置〉角色乙向前一步,进入角色甲右侧余光 〈表演〉角色甲从警觉转为压住不动;角色乙保持不变,只等对方先开口 〈对白〉〔角色乙 · 同期 · 汉语〕「你找的东西不在里面。」 [段3 | 3.4–5.0] 〈空间〉同上 〈姿态〉角色甲合上箱盖并把左手撤离栏杆,重心移回双膝 〈位置〉两人相对位置不变 〈表演〉角色甲落到承认被看见后的克制,未反驳 〈对白〉无 ``` `〈…〉`、方括号、竖线、分段编号和对白围栏的写法**都来自创作者档案**,本套件不把其中 任何一种拼写规定为交付格式。可迁移的只有每一行必须回答的问题。若只写到 3.4 秒就收尾, 剩下的 1.6 秒不会是静止画面,而会被执行端用它自己选的动作填满。 ## 六、自检 1. 容器里装了几个已接受镜头?容器时长是否正好等于这些镜头已接受时长之和? 2. 在每个成员镜头内部,各分段时长相加是否正好等于该镜头的已接受时长?余量是被分配了, 还是被留白了? 3. 多镜容器的成员是否来源顺序连续、共享同一条绑定链、不跨场次?是否每个成员仍能单独审查? 4. 分段标识是否唯一、按时间升序?有没有跳号或重复? 5. 每个分段内是否只有一个连续视角?切换是否都落在成员镜头边界上,而不是藏在段内叙述里? 6. 位置槽是否指向画面内可命名的可见锚点,而不是绝对坐标或模糊方向? 7. 姿态槽的段尾状态是否等于下一段的段首状态?最后一段是否落到已接受终点? 8. 表演槽是否写了触发与可见处理,而不是只有一个形容词或一个数字? 9. 逐字对白是否在围栏内原样保留?声源(同期、画外、旁白)与这句台词的口语语言是否分别标明? 10. 交付文本里是否只剩要被拍出来的画面内容,没有工艺备注与内部记号? 11. 档案声明的执行触发词是否逐字节出现、且留在围栏之外?没有声明时是否也没有自造字样? 12. 本次用的是哪条交付路由?若是从已生成结果续接,起点状态是绑定了观察记录,还是仍为 `unverified`?有没有把观察到的状态当成新的镜头边界? -
generability.md 5.1 KB
# 动作的可生成性 ## 目录 1. 什么时候适用 2. 判据:这个动作在日常影像里常见吗 3. 四类高风险写法 4. 戏剧信息从哪里来 5. 改写方法 6. 失败征兆与审查问题 ## 1. 什么时候适用 **只在项目声明的制作形态由生成模型承担画面时适用**(见 `视觉设定.md` 的制作形态)。实拍项目 不受本文件约束——演员能完成的精细动作, 生成模型不一定能。形态未声明时保持未决,不要预先按生成条件裁剪动作。 本文件不否定任何动作的戏剧价值,只判断**它能不能被当前交付方式实现**。同一个动作, 在实拍里是好写法,在生成管线里可能整镜不可用。 ## 2. 判据:这个动作在日常影像里常见吗 生成模型演的是它见过很多次的动作。判据只有一句:**这个动作在普通生活影像里是否大量出现**。 | | 写法 | 为什么 | |---|---|---| | 放心写 | 走进来、坐下、站起来、回头、点头、递东西、接过、抱住、推开门、把东西放在桌上、翻页、伸手去够 | 高频、整体、单一躯干或单臂完成 | | 需要改写 | 用工具挡住另一件正在运动的东西、以厘米计的位移、单指精确操作小部件、四步以上的双手编排、微表情作为承重信息 | 低频、精细、要求多部位同时精确 | 一个实用的自检:**把这个动作描述念给一个没读过剧本的人,他能不能马上比划出来**。 需要先解释一遍才能比划的,生成模型多半也做不出来。 ## 3. 四类高风险写法 - **精确拦截**:两个人同时争夺一件小物、用手挡住正在合上的东西、在运动中截住某物。 它要求两个主体的时序与位置同时精确,是最容易整镜崩掉的一类。 - **不可见的内部状态**:`像是听见了什么`、`神色一凛`、`忽然明白过来`。这些是推断, 不是画面。演员可以演,生成模型没有可执行的目标。 - **否定式动作**:`没有接话`、`没有按下去`、`不去看他`。画面里"没有发生的事"无法生成; 需要改写成一个**正在发生**的替代动作。 - **精细编排链**:一拍里串起三步以上的手部动作,尤其是左右手分工。四秒内四步双手动作, 平均每步一秒,物理上也不成立。 ## 4. 戏剧信息从哪里来 这是本文件最重要的一条:**戏剧信息来自常见动作的组合与时机,不来自动作本身的精巧**。 同一个"他不想让对方看见",可以写成: - 高风险:`他用手掌挡住正在合上的笔记本边缘`(精确拦截) - 可生成:`他把笔记本合上,手放在盖子上没有移开`(两个常见动作 + 一个停留) 后者信息量不减:谁控制了这台笔记本、对方现在不能打开它,都看得见。改的是实现方式, 不是戏。 **`craft_default · VID-21`**:项目声明由生成模型承担画面时,动作写成日常影像中高频、 整体、单一部位可完成的动作;精确拦截、不可见内部状态、否定式动作与三步以上双手编排 改写成等效的常见动作组合。戏剧信息由组合与时机承担。实拍形态或形态未声明时本条不生效。 ## 5. 改写方法 按顺序试,取第一个成立的: 1. **换承担者**:让物件或环境承担变化,而不是让手承担。`门被风带上` 好过 `他用脚勾住门`。 2. **拆到相邻镜**:把精细动作拆成"动作前"与"结果"两镜,中间的过程不拍。 写字的过程难生成,`笔停在纸上` 与 `纸上多了一行字` 都容易。 3. **换成停留**:把"精确地做某事"换成"做完之后手没有拿开"。停留是最容易生成的表演。 4. **交给声音**:动作不可见时,用已声明的声源承担(见 [camera-audio-continuity.md](camera-audio-continuity.md))。 5. **确实不可替代时**,在运动规格里写明该镜为高风险,并给出一个可接受的降级实现, 让制作环节决定是否改用实拍或后期。 改写不得改变已接受的镜头边界、起止状态与剧本事实。若唯一可行的改写会改变戏剧结果, 这不是本环节能解决的问题——路由回 owner 修订,而不是在提示词里硬写。 ## 6. 失败征兆与审查问题 ### 失败征兆 - 一拍里出现三步以上手部动作,或左右手各有分工; - 动作描述里出现推断词(像是、似乎、仿佛、忽然明白); - 关键信息由"没有做某事"承担; - 两个主体在同一物件上同时精确用力; - 承重信息落在微表情上,而画面里没有别的载体; - 把可生成性当成删戏的理由,而不是换实现方式的理由。 ### 审查问题 1. 本项目声明的制作形态是什么?本条是否生效? 2. 这一拍的动作在日常影像里常见吗?删掉解释还能不能被比划出来? 3. 承重的戏剧信息是什么?它现在挂在动作的精巧上,还是挂在组合与时机上? 4. 改写之后,镜头边界、起止状态与剧本事实是否保持不变? 5. 确实不可替代的高风险镜,是否已标注并给出降级实现? -
minimax-h3.md 17.7 KB
# MiniMax H3 提示词方言 只在目标档案明确写 `target_video_model: minimax-h3` 时使用。推荐档案: ```json { "target_video_model": "minimax-h3", "video_prompt_dialect": "minimax-h3", "video_prompt_language": "en", "native_duration_seconds": {"min": 4, "max": 15}, "supported_generation_modes": ["text", "first_frame", "first_last_frame", "reference"], "audio_generation": "same_pass" } ``` H3 的结构字段用英文;这不等于把中文对白翻成英文。每句中文对白保持原文并写成 `<d>[Chinese] 逐字台词</d>`,由稳定说话人 ID 引出。不要在对白前增加外语、语气词或未写入剧本的开场句。 点名 MiniMax H3 只选定模型与方言,不表示创作者选了文生视频。当镜头需要人物、场景、道具或起始构图一致性时, 先按主 Skill 完成真实图片发现与缺口列表。有任何必要图片仍缺失时,不因 H3 同时支持 Base 模式就默默改用文生视频。 ## Base / 首帧 / 首尾帧 文生视频、首帧和首尾帧使用三段结构: ```text integrated_multimodal_description: [Shot 1] ... overall_soundscape: ... non_diegetic_music: ... ``` - `integrated_multimodal_description` 按 `[Shot 1]`、`[Shot 2]` 写画面、动作、说话人、逐字对白和同步声音; 单镜也保留 `[Shot 1]`。第一镜不加切镜时间戳,后续切镜用 `[Shot 2] At 00:03.500, ...`,时间递增且落在本次生成时长内。 小样本观察到切点接近要求,但仍有偏差;交付要求精确时应检查实际切点并在剪辑中调整。 - `overall_soundscape` 汇总环境声、物理音效和非语言人声,不重复对白;对白事件留在上面的主时间线。 无非画内配乐时写 `non_diegetic_music: N/A`,不要留空让模型补乐——这一条有实测支撑,见 [无对白镜](#无对白镜)。 - 首帧模式从输入帧的可见姿态开始;首尾帧模式只能到达上游已接受终点,不发明过渡后的新状态。 ## 无对白镜 在 `overall_soundscape` 写清全镜的环境声、音效或静默。 **`craft_default`**:优先描述需要的声轨;必要时可以补「无对白」,不因用了否定句判错。 人物嘴部状态按实际表演写,不能把无对白一律改成全程闭嘴。 无非画内配乐时保留 `non_diegetic_music: N/A`。小样本中省略这一层出现过额外配乐, 显式写空可作为 H3 的默认写法,不保证所有生成都遵守。 时间线总结只列本镜实际存在的声音层,不照抄包含对白的模板。 ## 选哪一种模式 模式由本镜「输入参考图」里各槽位的 `用途` 决定,不由“想不想用多模态”决定: | 本镜绑定的图 | H3 模式 | 正文结构 | |---|---|---| | 没有图,且创作者已明确选择文生视频 | Base(文生) | 三段 | | 只有一张 `用途:起始帧` | `first_frame` | 三段 | | 一张 `用途:起始帧` + 一张 `用途:结束帧` | `first_last_frame` | 三段 | | 其他任何组合(起始帧与人物/地点/道具图同时存在,或只有人物/地点/道具图) | `reference`(full-reference) | 六段 | `REF-...` 和 `PLAN-...` 在这张表里同权:模式由 `用途` 组合决定,与图片此刻在不在项目里无关。 创作者在 H3 的网页界面里自己挂图时,正文照这张表写,`顺序` 就是他挂图的次序。 H3 官方另有只给尾帧、由模型推断开场的 L2VA 模式。本套件不使用它:开场由分镜已接受的起点决定, 把它交给模型推断会让 `SHOT-...` 的起点失去权威。因此 `结束帧` 只在同时绑定 `起始帧` 时出现。 第四行是最常见、也最容易写错的一种。H3 的首/尾帧输入与参考输入**互斥**,所以「起始帧 + 角色板 + 场景板」不能拆成 `first_frame` 加 `reference_image`:整组统一走 full-reference,起始帧也以 `reference_image` 送入,正文按下面的标签编号引用。用哪一种模式在《视频提示词.md》里由 `用途` 组合读出, 不写进「生成方式」字段——该字段仍只有「文生视频」和「图生视频」两个值。 ## Full-reference 任何 `reference_image`、`reference_video` 或 `reference_audio` 进入任务时,改用六段结构,段名和正文说明均用英文: ```text subject_definitions: ... summary: ... retention_analysis: ... detailed_description: [Shot 1] ... overall_soundscape: ... non_diegetic_music: ... ``` **这六段是一条提示词,一次整体提交。** 它们是同一个 `MOTION-...` 的可复制正文,从第一段到最后一段 一起放进那一次生成的提示词框,不是六次生成、也不是只提交第一段。三段结构的 Base / 首帧 / 首尾帧同理。 一镜一次生成,所以下一镜换成它自己那条完整正文。 ### 素材标签怎么编号 正文用 `<Picture N>`、`<Video N>`、`<Audio N>` 指向本次 job 的实际素材,`<Subject N>` 指向可复用的可见内容。 **编号按同类素材在本镜「输入参考图」里的 `顺序` 递增**:第 1 张图片是 `<Picture 1>`,第 2 张是 `<Picture 2>`, 视频和音频各自从 1 开始。生产端 compiler 按同一顺序附加引用契约,所以文档里手写的编号必须和 `顺序` 一致; 两套编号不一致时模型会收到互相矛盾的素材说明,而接口不会报错。 因此写 full-reference 正文前,先把本镜的槽位按 `顺序` 列一遍,例如: ```text REF-SHOT-START(顺序:1;用途:起始帧) -> <Picture 1> REF-XIAOYU-SHEET(顺序:2;用途:身份) -> <Picture 2> REF-STUDY-PLATE(顺序:3;用途:地理) -> <Picture 3> ``` 这份清单不依赖文件路径,只依赖 `顺序` 和 `用途`,所以图片由创作者自己挂载时它照样成立: 把上面的 `REF-` 换成 `PLAN-`,定位符换成 `SHOT-...` 或 `IMG-...`,正文一个字都不用改。 创作者按 `顺序` 在生成界面里挂图,`<Picture N>` 就对得上。 `PLAN-...` 的图套件没有看过,所以 `subject_definitions` 和 `retention_analysis` 写的是 **这张图按它的 `用途` 必须保留什么**,事实取自《视觉设定.md》和本镜冻结关键帧,而不是对像素的观察结论。 两种槽位的句式相同:`用途` 决定保留什么,本来就不是看图看出来的。不要因为图没进项目就改写成 「未验证」或省略保留强度——那会让执行端收不到本镜真正的保留要求。 `subject_definitions` 把每个主体绑到它的标签上,句式为 `<Subject 1> is the ... in <Picture 2>, with ...`;`retention_analysis` 逐条写保留强度和出现的镜次, 视觉素材用 `fully_preserved`、`partially_preserved`、`attribute_transfer`、`weak_reference`, 音频素材用 `fully_copy`、`partially_copy`、`reference`、`weak_reference`,例如 `<Subject 1> (appears in [Shot 1]): fully_preserved - 短发轮廓、连帽外套配色与鞋型保留`。 每个标签在六段里保持同一写法,不留下没有定义的标签。 ### 每张图带什么、不带什么 `用途` 决定这一张在正文里被要求保留什么,具体到哪几项由该槽位自己写下的 `控制` 范围决定。 需要保留构图时,在 `控制` 中点名构图、站位或机位高度。 `不得控制` 记录不应照搬的内容,生成后仍需检查实际参考影响,不能把文字声明当成模型保证。 `起始帧` 只锚定开场构图与姿态,`身份` 锚定这个人跨镜不变的长相与体态(角色板同时固定本集造型时, `控制` 里会写明), `地理` 只锚定空间关系,`造型状态` 只锚定服装与损污。把这条差异写进 `retention_analysis`, 不要让角色板顺带决定构图,也不要让场景板顺带决定人物长相。 角色始终使用同一个说话人 ID;中文对白只出现在 `<d>[Chinese] ...</d>` 内。禁字幕写进可见文字约束, 与获准的画内文字分开。 Full-reference 的逐字对白与同步声音放在 `detailed_description` 的对应事件里,`overall_soundscape` 仍只汇总底声与音效。 不要为了“多模态”把所有资产图都带上;每镜只绑定它真正可见且需要保持的起始帧、人物、地点或关键道具图。 ### 一个完整例子 分镜里的槽位(三张图:本镜起始帧、角色板、场景板): ```markdown - 输入参考图:REF-SHOT002-START(顺序:1)· 剧集/EP001/制作成果/images/SHOT-EP001-002.png《SHOT-EP001-002 起始帧》(用途:起始帧;控制:起始构图、双人站位;不得控制:尚未发生的动作、终态);REF-XIAOYU-SHEET(顺序:2)· 输入/参考图/小雨定妆.png《小雨定妆照》(用途:身份;控制:脸型、发型剪影、身高比例;不得控制:构图、动作、表情);REF-STUDY-PLATE(顺序:3)· 输入/参考图/家庭书房.png《家庭书房场景图》(用途:地理;控制:书桌方位、书架墙、灯位;不得控制:人物身份、动作、道具状态) ``` 对应的 full-reference 正文(`生成方式:图生视频`,模式为 `reference`): ```text subject_definitions: <Subject 1> is the elementary-school boy in <Picture 2>, with a round short black haircut and a light grey-blue hooded jacket over a white inner shirt. <Subject 2> is the home study in <Picture 3>, with the desk against the window wall, a full bookshelf on the left and a warm desk lamp. <Picture 1> is the opening composition of this shot. summary: The boy leans over an open textbook at his desk and stops when he notices the printed map. retention_analysis: <Picture 1> (appears in [Shot 1]): fully_preserved - overhead framing, desk edge position and the boy's seated placement open the shot exactly as supplied. <Subject 1> (appears in [Shot 1]): fully_preserved - face shape, hair silhouette and the jacket-over-shirt layering stay identical; his pose and expression are set by this shot, not by <Picture 2>. <Subject 2> (appears in [Shot 1]): partially_preserved - desk orientation, bookshelf wall and lamp position are kept; the props on the desk follow this shot. detailed_description: [Shot 1] ... overall_soundscape: ... non_diegetic_music: N/A ``` 三张图各自只负责一件事:`<Picture 1>` 给构图,`<Picture 2>` 给身份,`<Picture 3>` 给地理。 把这条差异写进 `retention_analysis`,生成后检查身份、构图与地理是否分别保留。 ## 素材数量上限 一次生成最多 1 张首帧、1 张尾帧、9 张 `reference_image`、3 段 `reference_video`、3 段 `reference_audio`; 视频与音频每段 2–15 秒,且各自合计不超过 15 秒。分辨率取 `768P` 或 `2K`(`MiniMax-H3-Max` 另为 `480P`/`768P`)。 超过上限时在分镜阶段按重要性取舍,并写明放弃了哪些参考,不在正文里假装它们仍然生效。 本镜还没有起始帧图片时有两条路:由 `$short-drama-produce` 从 `分镜.md` 的 `SHOT-...` 冻结关键帧生成, 拿到真实文件后由分镜 owner 绑成 `用途:起始帧` 的 `REF-...`;或者由创作者把该镜的冻结关键帧正文复制到 自己的工具里出图,本轮先记成 `PLAN-...(用途:起始帧)`、定位符写本镜 `SHOT-...`。 两种写法本阶段都原样抄入,正文写法相同。 `输入参考图` 只登记参考**图片**。连续段需要的上一段实际视频与实际音频不走这个字段,由下面的 「连续段」小节和生产 job 的绑定承担;不要为了凑成一份清单把视频路径塞进 `REF-...`。 ## 连续段 H3 的首/尾帧模式与 full-reference 模式互斥,不能在同一请求混用 `first_frame`/`last_frame` 和 `reference_image`/`reference_video`/`reference_audio`。连续段需要上一段实际视频和实际尾帧时,统一走 full-reference: - 上一段实际视频:`reference_video`,正文称 `<Video 1>`,控制动作、节奏和声音连续; - 该视频的实际尾帧:`reference_image`,正文称 `<Picture 1>`,只作为新段开场的可见姿态与构图锚点。 不要把尾帧标成 `first_frame`。这不是措辞偏好,而是 H3 官方接口的互斥输入契约。 ## 时长 **`structural_invariant`(执行端契约)**:MiniMax-H3 的 `duration` 必填,为 4–15 的任一整数秒, 并非只有 5/10/15 三档。网页界面或 job 的时长设置要与分镜时长和正文终点一致;写在提示词里的秒数 不能替代生成设置。短动作在合法镜长内完成并保持已接受终点;超过 15 秒的镜头回分镜按闭合状态拆开。 本阶段不偷改镜头秒数。 `MiniMax-H3-Max` 是另一型号:5–15 秒、480P/768P,仅支持文生和首/尾帧,不支持 full-reference。 不能把本页 H3 的参考音频路径或 4 秒下限套到 Max。 ### 对白容量与自然语速 **`craft_default`**:按 [对白预算](performance-action-timing.md#对白预算) 先核对完整发声需要多久, 再给主时间线安排开口、停顿与落点。分镜镜长够长但开口太晚,一样会赶词。 H3 中文小样本可参考约 4.1 个可发声字/秒;用实际可发声区间估算,生成后核对台词完整性。 样本出现过赶词与静默截断,不能据此确定通用截断阈值。 口语语言、声线与语速描述放在 `<d>` 外;`<d>` 内只放语言标签和逐字台词,不放秒数或表演指令。 稳定说话人用 `(S1)`、`(S2)`;full-reference 中说话人物同时写 `<Subject N> (Sx)`。 无参考音频时,以自然语言交代说话方式、开口 cue、句间停顿和说完后的动作;文本估时区间只留在镜头说明中, 不把每个词硬塞进小数秒槽。实际有音频对齐依据或已接受的精确时点时,才据此细化声音时序。 H3 官方接口未提供独立 `speech_rate` 参数,也没有公布「中文每秒固定几字」的可靠公式; 提示词中的语速与时机是生成指导,不能当作秒级执行保证。仅追加“自然语速”无法补上缺失的发声时间。 同一次生成内,已接受的对白跨切镜时可以用 `<scenetrans>` 并声明声音连续;它不会增加总时长,也不能 保证两个独立 job 的音频无缝衔接。`<cutoff>` 表示视频结束时有意截断发声,只用于剧本已要求的截断, 不用于处理装不下的完整台词。 ### 说话人绑定 **`craft_default`**:说话人第一次出现时,在 `<d>` 外交代他在画内还是画外、以及音色(年龄、性别、高低), 台词跟在他的可见动作句后面(仍只出现一次),不集中列在正文末尾: `<Subject 1> (S1), the boy on-screen with a clear high voice, opens his mouth and calls out: <d>[Chinese] ...</d>`。 台词期间画里还有别人的正脸时,把那个人明写成不说话: `The messenger does not speak; his lips remain completely closed.`——与官方画外音写法 `while his lips remain completely closed` 是同一句式。 一镜实测(远处小孩喊一句,近处骑手不说话,骑手在台词期间仍正对镜头):现行写法 3 次中 2 次口型落在骑手、 小孩闭嘴;加上述两条后 3 次全部落在小孩。19 条样本的发声基频都在儿童区间——模型没有选错声音, 只是把口型放到画面里最显眼的正脸上。骑手已出画或背对时,两种写法 12 次全部正确。所以分镜能安排时, 先让不说话的人出画或背身;不能时才靠这两条兜底,生成后仍核对口型落在谁脸上。 ### 有配音或参考音频时 **`craft_default`**:已有同句同表演音频时,先测实际发声与停顿,再确定镜长。没有现成音频仍可按文本估计 写提示词,不为完成本阶段自动调用 TTS 或生成服务。 - 只参照音色、情绪或说话方式:使用 `reference`;它会重新表演本镜台词,不能拿参考音频的文件长度 当作新台词时长,也不携带参考音频里无关的原话。 - 已接受完整声音轨原样复用:`fully_copy` 表示整条最终音轨;只用对白层或片段并另加环境声时使用 `partially_copy`,写清源片段与目标时间的对应。复用用到的发声区间要完整容纳,不为适配镜长压缩语速。 - 参考音频走 full-reference,配合至少一张参考图或一段参考视频;每段音频 2–15 秒,最多三段、合计 不超过 15 秒。图片改用 `reference_image`,不能与 `first_frame`/`last_frame` 混用。 `fully_copy` 等是官方提示词的复用意图,不是逐样本一致的验收证明。生成后核听逐字内容、语速、停顿、 尾音完整性与口型;项目需要精确锁定已有音轨时,以后期保留该音轨并核对画面同步作为最终依据。 ### Context-IR 的作用 H3-Context-IR 把多模态输入增强成结构化提示词,本身不生成视频,且仍要求调用方先给出 4–15 秒的 `duration`。它不能代替对白预算。若执行端使用该服务,检查增强后的逐字台词、声音时间线与原时长一致, 不因使用官方增强就跳过容量检查。 官方依据(2026-09-07 核对): - [Base 提示词指南](https://github.com/MiniMax-AI/MiniMax-H3/blob/main/skills/h3-prompt-writing/references/base-en.txt):对白位置、说话人、语速描述、切镜时间戳与跨镜发声。 - [Full-reference 指南](https://github.com/MiniMax-AI/MiniMax-H3/blob/main/skills/h3-prompt-writing/references/ref-en.txt):六段结构、音频引用与复用语义。 - [视频生成 API](https://platform.minimax.io/docs/api-reference/video-generation-v2-create):必填时长、型号差异和输入模式互斥。 - [官方 CLI 的 H3 指南](https://github.com/MiniMax-AI/cli/blob/main/skill/h3-video/references/h3-video.md):音频配合视觉参考,声音对齐与生成后检查。 - [Context-IR API](https://platform.minimax.io/docs/api-reference/video-generation-v2-h3-context-ir):提示词增强和独立的时长输入。 -
motion-recipe.md 20 KB
# 起点—变化—终点:视频运动说明 ## 目录 1. 目的、输入与权属 2. 起止边界清单 3. 运动说明的七个部分 4. 从结构化规格到自然语言 5. 输出、常见问题与检查 ## 本文负责的规则 - `VID-01` — 把已确认的起点、终点、时长、对白和下一镜状态视为只读内容。 运动规格只实现这个区间,不能改写它。 - `VID-02` — 按需填写起点、有序动作、表演变化、摄影机、环境/声音和时间, 最后输出结束报告。 - `VID-03` — 先区分文生视频与图生视频。只有真实参考帧已作为输入时,运动提示词才可省略它已 说明的外观和构图;没有真实图片时必须把静态视觉锚点写进可复制正文,不得假设执行端看过分镜。 - `VID-12` — 补拍版/替代版逐项绑定原文要求,说明本版本没有承担哪些内容, 并明确它是补充母版,还是请求经审查后替代母版;范围变小不等于完整覆盖。 ## 字段必须填写本镜的具体内容 **`VID-08 · reviewed_invariant`**:结构化运动规格必须写出本镜准确的主体、动作、 接触和结果;本镜确有可见表演变化时,再写承担变化的演员,不能只放可复制的占位句。 字段说明不是镜头内容。每个 `ordered_subject_motion` 都要写本镜准确的角色绑定、 触发条件、可见动作、方向/路径、接触对象、阶段结果和顺序。不得在所有镜头复制 “已确认主体”“已确认动作”“到达已确认终点”。若使用 `performance_arcs[]`,只写这次 变化真正需要的触发、处理、选择或落点,不为字段齐全复制一套固定步骤。 判断方法:若一条结构化记录原样复制到下一镜仍然成立,它多半没有写出本镜的具体内容。 保留字段,把值换成本镜事实,并删掉无关物件和动作。 ### 选择性变换边界 **`VID-11 · reviewed_invariant`**:当一段运动只让环境或主体的一部分折叠、消失、 替换、破碎、复原或改变形态时,必须写清: 1. **触发**:哪个已确认的动作、状态或声音开始变换; 2. **目标范围**:精确到哪一个人、物、表面、空间构件或局部,沿什么路径变化; 3. **结束几何/状态**:变换后留下、占据或暴露什么,不只写“消失了”; 4. **保持项 `preserve_set`**:同框但不参与变换的人物、手持道具、文字所在表面和空间锚点, 要保持什么位置、数量和连续性状态。 这需要审查内容是否正确、动作是否能执行,不能靠关键词检查证明。若目标模型会把邻近物一起 吞掉,先缩小目标范围、拆成多个运动阶段或增加阶段边界;不要只堆“其他不变”之类空泛的负面词。 ### 参考图用途与补拍范围 每张参考声明准确的稳定 `REF-...` 槽位、顺序、项目相对路径、中文名称、用途、允许控制、不得控制与观察状态, 且视频文档必须与分镜原文一致。起始帧可以决定本镜起始构图和 可见状态,却不能 提供尚未发生的终点;构图、尺度、效果参考也不能带入图中没有来源的人物、文字、 道具或事件。只有创作者/参考图权利人的可核对说明,或运行环境获授权后形成的准确 观察说明才能支持像素/文字结论;只有文本引用时保持“未验证”。 **`VID-12 · reviewed_invariant`**:普通母版不写覆盖清单;只有局部补拍或替代实现才在同一 `MOTION-...` 章节下写“版本关系”。用稳定 ID 指明母版与补拍,逐项列出动作、反应、对白、揭示、 结束边界和项目要求由本版本、母版、另一补拍承担,还是必须退回分镜。运动提示词只能提出替代; reviewer 结合完整故事义务决定,不能让未承担内容随更短提示词消失。 ## 末镜交接 **`VID-09 · structural_invariant`**:下一镜存在时直接引用真实 `SHOT-...` 起点。最后一镜可对照 已有分集规划;下一集尚未建立时明确写“下一集起点未定”,不得伪造一个不存在的 ID 并声称匹配。 ## 1. 目的、输入与权属 视频提示词不负责“想一个更精彩的镜头”,而是把已确认镜头的变化写成能按时间执行的过程。 输入包括:镜头与关键帧、准确的资产引用、时长、镜头目的、起止边界、对白、画外音、音效和声音引用、 进出连续性、视觉/声音方向和可选的制作能力配置。 分镜负责地点/时间、资产版本、构图意图、时长、对白、起止姿态/位置/目光/双手/持物、 连续性终点和下一镜起点。运动规格对这些内容只读,只能报告当前描述是否到达要求的终点。 ## 2. 起止边界清单 写提示词前先做内部卡片: ```text 镜头/关键帧:准确的 `SHOT-...`;`IMG-...` 只定位图片提示词条目,不表示已有关键帧图片 镜头目的:观众的注意、信息或情绪要怎样改变 时长:已确认时长 起点:姿态 / 重心 / 目光 / 双手 / 持物 / 空间关系 终点:同一组字段的已确认 `end_boundary` 对白/声音:准确来源引用 + 已确认的表演或声源要求 持续关系:限制本镜动作空间的身体—物件—空间关系及有效范围 下一镜:下一镜起点引用(只用于比较) ``` ### 最少但够用的起点信息 只重复“开始运动必须知道”的状态: - 重心在哪只脚、身体面向和是否坐/倚/跪; - 目光落点与注意对象; - 双手的位置、哪只手持什么、接触是否已发生; - 与门、桌、人等行动相关对象的距离/方位; - 正在持续的运动(若关键帧冻结的是运动中瞬间)。 真实参考帧已作为输入并包含脸、服装全貌、墙面材料等内容时不重复。**`craft_default`** 是“图生视频 只重述动作所需条件,不重述整份视觉设定”。文生视频没有这种省略资格,必须提供足以独立建立本镜 人物、造型、地点、构图与光线的静态视觉锚点;若提示词离开参考帧会把左右手或物体搞错,图生视频 也应保留这些局部事实。 ## 3. 运动说明的七个部分 ### 3.1 起点 `start_anchor` 用现在时、静态句准确写出起点,避免“她准备站起来”这种已经加入未来动作的写法。 若关键帧与镜头起点不一致,先请求分镜环节解决,不能任选其一。 ### 3.2 有序动作 `ordered_subject_motion` 每个动作阶段说明:谁行动、做什么、方向/路径、对象/接触、先后条件和到达状态。优先按因果关系连接: ```text 听见门外金属碰响后 → 目光先移向门缝 → 左手停止翻页并压住记录本 → 身体才从椅背离开 ``` 而非: ```text 她转头、翻书、站起、走向门、微笑、震惊,所有动作同时自然发生 ``` “缓缓、自然、电影感”不能替代路径与结果。只描述画面内可见动作;心理词要落为呼吸、 注视、肌肉张力、停顿、话语节奏或明确的画外音。 先把起点中的持续关系翻译成动作自由度:用正向状态写清贯穿本镜的支撑、固定、接触或空间关系, 再只安排允许的身体/物件通道。关系变化必须来自已接受动作并落到已接受终点;否则按 `VID-01` 保持只读。可复制正文写清持续状态和允许动作,避免重复约束;必要的排除条件可以保留。 成稿后在正文内直接改掉残留的这类否定结构,不另建检查步骤或报告。 **`craft_default`**:一条运动提示词优先只有一个主导的戏剧变化,必要反应、环境与摄影 都服务这项变化;这不是固定动作数量。若同一镜必须同时完成互不依赖的交接、追逐、身份 揭示和大范围效果,先向 storyboard 请求按注意或力学边界拆镜。反过来,也不能为了变短 删掉逐字对白、关键反应或终点义务;短与长都不是质量指标,可执行的因果路径才是。 若该动作包含选择性变换,按 `VID-11` 把触发、目标范围、结束几何/状态和 `preserve_set` 写进相应动作阶段;不能让相邻非目标人物、道具或已批准文字跟随目标一起改变。 ### 3.3 表演过程 `performance_arcs[]` 表演不是表情标签序列。找到最小变化: 1. **触发**:外部声音/动作/台词或内部决定(来源可追溯); 2. **接收**:注意被什么抓住; 3. **处理**:一两个细微信号,而非面部部件清单; 4. **决定**:角色采取/抑制什么行动; 5. **结果**:到达已确认终点的情绪与身体状态。 - **`craft_default`**:把副词换成节奏和行为,“愤怒地说”可具体为先压低音量、吞回半句话、最后直呼对方全名——前提是符合台词与人物。 - **`reviewed_invariant`**:不能发明新信息、关系转变或角色意图。审查者对照镜头目的和剧本资料判断。 - **`taste_option`**:强度与外显程度可从克制到爆发,由导演选择,不强制“先微表情后爆发”。 ### 3.4 摄影机 `camera` 选择有明确目的的固定机位或一条主要运动路径。移动机位要写动机、开始条件、运动方向/速度和 终点景别;例如“当信封被推入光区时,机位做一次短促前移,停在手与印章的近景”, 比“镜头电影感推进”更清楚。详见 `camera-audio-continuity.md`。 ### 3.5 环境与声音 只让环境中对故事或连续性有意义的元素动:风推动窗帘、远处警示灯扫过墙、杯中水因桌面撞击晃动。 不要给每个背景物同时加动作。声音部分引用准确的对白、画外音和音效,说明表演方式、同步要求、 空间声源和层级,不改台词。 目标执行端同轨生成声音时,把音频写成一个完整时间线: 1. 按发生顺序列出本镜全部对白、VO、OS、必要非语言发声和 SFX; 2. 每条台词同时标说话人、声源性质、口语语言和逐字文本,发声从原文首字开始、到末字结束; 3. 所列事件之外只保留已声明的 ambience 或静默;相邻两句之间若有停顿,也明确回到这一底声; 4. 用实际存在的声音层总结时间线,避免模板与本镜内容矛盾。 无对白镜写清底声或静默及覆盖区间。必要的「无对白」声明可以保留, 人物嘴部状态由本镜表演决定。非画内配乐的空形式按目标方言填写。 确有叹气、吸气、笑声或重叠说话时,把它作为有来源的事件列入。声音不同轨时仍只写后期职责。 ### 3.6 时间 `timing_plan` 默认把这几秒写成一条读得出先后的节拍链: ```text 首先<起手状态>;接着<动作、方向与幅度>;随后<这个动作造成的结果>;与此同时<另一条通道>;最终<结束状态> ``` - **幅度、角度、距离是你写的这一拍的组成部分**。“肩线下沉约五厘米”“上身向左转约三十度” “右手停在离杯沿一掌处”说的是本镜要拍成什么样,直接写,不必等上游先给数字。写不出量级, 通常说明这一拍还没想清楚该有多大——先把动作想清楚,再回来写这一句。 - **可以钉一到两个锚点秒数**:起手保持多久、终点定格多久、一次运镜走多久(“起手不动约半秒” “结束姿态定住约一秒”)。锚点是写在正文里的散文标注——不拆成 `timing_plan` 分段、不把 `mode` 写成 `explicit`、不参与 `VID-04` 求和。所有锚点相加应当明显小于镜头时长,链条其余 部分留给相对先后;锚点多到要铺满整镜时,改写成下面的显式分段。 - **并行通道由“与此同时”显式标出,一条链只给一条**,且落在串行链没占用的通道上:另一个 角色、主体的另一处身体通道,或环境。同一只手、同一道视线不能同时走两条。 只有制作需要按秒对齐时才写显式秒段。 - **`structural_invariant`**:明确标注的非重叠段落时长相加必须**等于**已接受的镜头时长;重叠段必须写明重叠关系,而不是靠猜。 - **超出**:落在时长之外的部分被截断,段尾动作、逐字对白和终点姿态一起丢失。 - **不足同样是缺陷**,而且后果更重:未分配的余量不会是静止画面,执行端会用未经批准的动作、表情或镜头运动把它填满,这些内容没有任何上游来源。把余量分配给已有阶段,或补一个有内容的收尾段(落住反应、保持终点姿态),不要留白。 - 两个方向都由 `VID-04` 管辖;审查者引用该编号时,超出与不足都算违约。 - **`reviewed_invariant`**:即使数学不超时,台词、动作、反应是否可完成仍由审查者结合语言、强度和物理路径判断。 - 不设通用“每秒几字/几个动作”硬门槛。 写显式秒段时按这三个字段落实: | 字段 | 含义 | |---|---| | `timing_plan.mode` | `explicit` 才做算术断言。写 `relative` 就不要再给分段标 `explicit`,两者同时出现是自相矛盾,校验器会直接报 `VID_TIMING_MODE_INCONSISTENT` 而不是替你选一个 | | `timing_plan.declares_overlap` | 分段区间确实要重叠时置 `true`,此时占用时间按并集计算。留空或 `false` 而区间又重叠,视为漏写重叠关系而不是默认相加 | | `timing_plan.declared_total_or_endpoint_seconds` | 可以写并集占用时长,也可以写终点秒数;两种读法都接受,但必须与分段实际情况对得上 | 算术由 [motion_timing_check.py](../scripts/motion_timing_check.py) 执行:超出按**终点**判断, 不足按裁剪到镜头长度内的**并集**判断。两者分开量是因为中间留空加尾部超出会互相抵消—— `0.0-2.0` 加 `3.0-5.0` 在 4 秒镜头上"合计正好 4 秒",实际却同时发生了截断与留白。 ### 3.7 结束报告 `end_report` 把描述结果写回同一组起止边界字段:姿态、位置、目光、双手、持物和可见状态变化。 逐项对照分镜的 `end_boundary`: ```text reported_end == source end:match reported_end 不同:mismatch + 请求修改分镜 提示词未实现某字段:unrealized + 修改运动说明 ``` `end_report` 永远不是下一镜的权威输入。不能用运动规格中的“更自然结尾”覆盖已确认终点。 ## 4. 从结构化规格到自然语言 ### 可复制的写法骨架 ```text 从[最少但够用的起点信息]开始。首先[起手状态,可含一个锚点秒数];接着[动作、方向与幅度]; 随后[这个动作造成的结果与随之而来的可见处理];与此同时[另一条通道上并行的一件事]; 最终[已确认终点的可见状态]。摄影机[写死构图并说明保持多久,或一次有动机的移动,含触发、路径与终点景别]。 [必要的环境运动]。对白/声音:[准确来源与表演/空间意图]。 ``` ### 写完之后过一遍可执行检查 判据:**执行端照着这段能不能生成,而不用自己决定任何一件事**。逐条对照,缺哪条补哪条: | 必须写出来 | 缺了会发生什么 | |---|---| | 起手状态(这一拍从哪个姿态开始) | 执行端自选起点,接不上前一镜的终点 | | 串行链:接着→随后,每一步带方向与幅度 | 动作糊成一团,看不出先后 | | 至少一处量级(约五厘米/约三十度/约一掌) | 幅度由执行端决定,同一动作逐条漂移 | | 一条并行通道,用「与此同时」标出 | 要么全片只有一个人在动,要么所有人一起动 | | 结束状态(终点姿态定在哪) | 收尾被自由发挥,下一镜起点失效 | | 摄影机:锁定写死构图与保持多久,移动写触发、路径、终点景别;两种都补一句**这次要让观众看清什么** | 空着会被读成可以自由漂移;没有理由的运镜是装饰,执行端换一种也说得通,于是真换 | | 有台词时把台词逐字挂在它发生的那一拍上 | 口型对不上,或台词被当成画面内容渲染 | **写短**:这段是给生成器的执行指令,不是给人读的散文。上表七项在**两百字上下**就能写满, 超过三百字基本可以确定有冗余。省字的办法是合并而不是删项——压到一百八十字以内时, 最先掉的是台词挂载与声音这两项,把它们挤掉换来的字数不值: 把景别、机位、角度、焦段并进一句(「固定中近景,轴线左侧平视,中焦」),把并行通道挂在它 真正发生的那一拍后面而不另起一句,量级只给关键的那一到两处而不是每个动作都标。 形容词、情绪解释、把同一个动作换个说法再说一遍都不改变生成结果,只增加成本; 参考帧已经表达的长相、服装、场景装修一律不重复。 写出来是这样(合成材料,约两百字覆盖上表全部七项): ```text 从她双手交叠压膝、视线在脚边水洼的起点开始。首先保持不动约半秒;接着右手离膝向伞柄 伸出约二十厘米;随后指节扣住伞柄,把伞立起点在地面;与此同时水洼被风推出一层细纹; 最终她站直,右手持伞垂在身侧,视线抬到对面门口。摄影机固定,平视,取头肩,轴线左侧, 全程不动——让观众看清她起身前后手上的变化,而不是跟着她走。台词在「接着」这一拍:「我不等了。」 ``` 把元数据、`hash`、内部规则 ID 留在 Markdown 卡片,不放进可复制提示词。避免供应商参数、权重语法、任务指令和质量承诺。 参考图文件名、版本号、锁定标记、草图指代和重投备注同样不进交付文本——执行端会把它们当画面内容渲染出来;完整清单与改写方法见 [production-prompt-grammar.md](production-prompt-grammar.md) 的“交付文本只含交付内容”。若项目声明了分段交付档案,容器算术与槽位职责见 [delivery-profile.md](delivery-profile.md)。 ### 不重复参考帧内容 - 参考帧已经表达:人物长相、整套服装、空间装修、静态构图、基础光线;通常省略。 - 运动说明仍需表达:左右手/持物、即将行动的关节与路径、目光目标、运动相关遮挡、会变化的环境和镜头运动。 - 能删去而不改变动作结果的外观句,优先删。 - **`craft_default`** 不是“越短越好”;关键起止条件不能因为节省字数而丢失。 ## 5. 输出、常见问题与检查 ### 输出 - 已接受的运动规格:有序动作、表演、摄影机、环境、声音和时间; - 起点/终点的只读来源引用与结束比较报告; - 一段可复制的通用提示词,以及必要的警告或内容丢失说明; - 不含视频、音频、远程任务 ID、网址或接口状态。 ### 常见问题 - 从不同于关键帧的手势或持物开始; - 提示词大半重写人物服装和场景,动作只剩“自然移动”; - 所有动作“同时”,接触/方向/先后不明; - 选择性变换只写“消失/变形”,没有目标范围、结束几何或 `preserve_set`; - 表演只有“震惊—生气—悲伤”标签,没有触发和可见过程; - 终点新造了站位、道具转移或下一镜事实; - 单个镜头内部出现未声明的剪切(多镜容器在成员镜头边界上的切换不算,见 [delivery-profile.md](delivery-profile.md) 的多镜容器); - 明确时间段只用掉时长的一部分,剩下的秒数没有任何内容承接; - 交付文本里混入文件名、版本号、锁定标记或重投备注。 ### 检查 1. **结构**:来源引用、时长、明确时间段、摄影机区间、对白引用和结束比较。 2. **内容**:审查者引用镜头与运动说明,判断能否执行、能否表演、摄影动机是否成立、是否发明内容。 3. **常用做法**:动作精简、避免重复参考帧、单一主运镜都可以建议,但允许有理由地调整。 4. **创作者选择**:表演强度、固定/移动机位、节奏和声音密度由已接受的创作方向决定。 -
performance-action-timing.md 12.5 KB
# 表演弧、动作预算与节奏 ## 时长门槛 - `VID-04` — 显式标时的各段必须合计等于 accepted duration:超出与留下未分配余量同样违约。 - `VID-05` — 未标时的 action load 是 `reviewed_invariant`:根据本镜头证据审查 表演和故事变化是否仍可读,不套通用“每秒几个动词”公式。 ## 目录 1. 动作预算不是计数器 2. 构造可演的动作链 3. 构造可见的表演变化 4. 时间与对白共同预算 5. 超载修订与审查 ## 1. 动作预算不是计数器 动作预算问的是:在 accepted duration 内,演员能否保持故事重点,清楚完成必要动作/反应/对白和 end state?它不是“每镜最多三个动词”或“每秒固定字数”。动作距离、接触复杂度、语言节奏、停顿、表演强度与相机是否需要等待,都比动词数量重要。 **`reviewed_invariant`**:general feasibility 由审查者用 shot、motion、对白和时长证据判断。只有显式 segment 数学超时才是 `structural_invariant`。 ## 2. 构造可演的动作链 ### 先找不可删的 spine 从 shot purpose 反推: - 哪个动作造成信息/权力/情绪变化? - 哪个 reaction 让观众读懂变化? - 哪个动作必须完成才能到 accepted end? - 哪些只是“让画面忙起来”? 保留 spine,装饰动作最后加入。短镜头常见的有效结构是“一次决断动作 + 一次对方/自身 reaction”,但这只是 **`craft_default`**。 ### 给动作以条件与结果 动作 stage 至少明确 actor、方向/对象和到达结果。接触动作注意顺序:先靠近、再触碰、再施力、物体才变化;持物转手必须写清哪只手、何时释放/接住。角色不能在同一时刻用同一只手完成不相容任务。 空间位移要尊重 shot start 的距离和障碍。若要穿过房间、开锁、取物、返回、说完长对白,问题通常不在 prompt 措辞,而在 shot boundary/时长,需要 revision request。 ### 同时发生的合理用法 “同时”适合可自然叠加的通道:角色说话时缓慢抬眼,环境雨声持续,camera 平稳跟随。不要用它压缩互为条件的动作,或让双手/身体执行物理冲突。 ## 3. 构造可见的表演变化 ### 角色不是情绪滑块 不要机械排列“平静 30% → 紧张 70% → 震惊 100%”。从角色 agenda 和触发选择外显通道: - 目光:寻找、回避、锁定、短暂失焦; - 呼吸/声音:吸气被截断、语速变化、音量反常降低; - 身体:重心撤后、肩线防御、手指停止或反复动作; - 语言:停顿、吞字、改称呼、打断或刻意完整地说完; - 决断动作:收回手、让路、把物件推回、关门。 选择能被当前景别读到的信号。远景不应依赖细微眼睑变化;近景不必用夸张全身后退替代内心处理。 ### 可选:表演标尺只用来校准形容词 只有创作者已经声明并接受一套表演标尺时才使用它。**本文档不提议任何刻度、增量公式或 每镜升压规则**;没有创作者标尺时就写可见表演,不写数字。 标尺唯一的职责是**消歧一个形容词要演到多硬**。“淡然”“不安”“恐惧”之间有清楚的高低 次序,而同一个词在不同人物、不同场合的合理落点其实相当集中——所以标尺回答的是“这里 的‘不安’是欲言又止,还是手抖到握不住杯子”,不是“这一镜该比上一镜更用力”。这也是 [production-prompt-grammar.md](production-prompt-grammar.md) 里“哪些情绪形容词可删”的 另一面:能被标尺定住落点的形容词才值得留下,定不住的应当换成可见动作。 用法: - **取值来自形容词本身**,由人物、触发和后果决定落点;不从镜号、段序或“第几次升级” 推导。同一个词在本集第 3 镜和第 30 镜可以是同一个落点。 - **保持与变化同样合法,而且保持更常见**。相邻段之间不变是最常出现的正确选择:一个 角色可以在整场戏里维持同一强度,变化发生在别人身上或发生在信息上。为了“有层次” 每段都动一格,是把标尺当节拍器用。 - **按最小可见增量变化**。相邻段之间的改变小到刚好能被当前景别看见即可;跨段一次跳 多级必须有段内可指认的触发(一句台词、一次接触、一个揭示),否则观众看到的是换了 个人,而不是同一个人变了。 - **只标注本段真正参与变化的角色**。旁观者、画外角色和状态未变的人不进标注;为“字段 齐全”给全场人各标一个值,会让审查者分不清哪一处变化是有意的。 - **数字不替代表演**。标尺值旁边必须仍有可见通道(目光、呼吸、身体、语言、决断动作); 只有数字的表演等于没写表演。 与剧本的关系:剧本的 `(情绪)` 括注是表演状态和说话方式的同一个来源。标尺值、可见 表演与台词的说话方式都应当从这一个括注派生;三者互相矛盾时以剧本括注为准,需要改动 就发修订请求,不在提示词里自行折中。 ### 单人或多人表演弧 每个真正承担本镜可见变化的角色才写一条;状态未变的旁观者不因字段完整度进入主表演通道。 以下是可选词汇,不是六项必填拍表: 1. **agenda**:他此刻想维持或取得什么; 2. **receive**:准确触发怎样被角色接收; 3. **mask**:角色确实在掩饰、继续或压住什么时才写; 4. **visible_leak**:掩饰漏出的目光、尾音、手部、距离或物件动作,且当前景别读得到; 5. **choice**:说、做或刻意不做一个有方向的选择; 6. **landing**:选择落在分镜明确的终点状态。 需要时在对应 `MOTION-...` 下按人物分条写;人物使用 `视觉设定.md` 的稳定 ID,触发与注意交接指向 场景或镜头的可见 ID,不靠同名猜身份。 只保留能让本镜更可演、更可读的字段。例如角色认出桌上印章后仍试图把原句说完,尾音降低、 右手离桌,最后停止阻拦并让出通道;若没有掩饰,就删除 `mask` / `visible_leak`,不要填 `none`。 若本镜只需要“听见门响后停止翻页”,写触发和可见动作即可,不必把其余字段补成同义句。 空镜、道具细节、纯空间转场、无人物镜头,或只有物理动作而没有可见内在处理的镜头,省略 表演弧。单人镜也不因人数自动生成;多人镜更不是给每个人复制同一“震惊”。不同人物可以早认出但继续说、 晚半拍才让路、故意制造噪声掩饰,或看见别人停顿后才理解变化。所有差异都要来自角色立场、 已接受信息和空间关系。 只在注意真正交接时,简写“谁/什么拥有注意 → 哪个可见或可听触发 → 注意转给谁/什么”;没有交接 就不补这一段。交接两端可以是角色、道具、证据、声源或已建立的空间区域;使用它们在 `视觉设定.md` / `分镜.md` 中的可见 ID,不要为了套人物格式把道具或空间伪装成角色。 交接不是全员同步转头:例如说话者尾音降低 → 最近的听者先停手 → 群体让路 → 主角获得空间。 每一跳说明为何当前景别、遮挡和声音能读到;细微眼神在远景不可读时,改成视线转移加手部停顿, 而不是只增加情绪形容词。 - **`craft_default`**:若一镜只有信息接收,宁可把变化做小而清楚,不必每镜“情绪大爆发”。 - **`reviewed_invariant`**:表演选择应与剧本 agenda/shot purpose 相容,不能凭 prompt 改写人物立场。 - **`taste_option`**:停顿多寡、微表演或类型化表演由导演风格决定。 ### 反应的对象 明确角色对什么反应,而不是孤立写“她突然震惊”。触发必须来自 accepted shot/script/audio,或是 already-visible 环境事实。Reaction 若揭示新知识,要确认角色在这一刻确实获得它。 ## 4. 时间与对白共同预算 ### 相对时间优先 在不需要精确秒段时,用“开头保留一拍”“话音落下后”“动作完成前”“结尾停留”表达节奏。这样保留演员和剪辑弹性。 ### 显式时间 需要 timing 时,列阶段与 overlap: ```text 0.0–0.8s:保持 start,听见门闩声后目光转向门 0.8–2.6s:左手压住册页,身体慢慢前倾(对白从 1.1s 开始,与动作重叠) 2.6–4.0s:说完后停住,右手仍握笔,落到 accepted end ``` 区间不可无意重叠;显式非重叠总量/最大终点不得超 duration。总量小于 duration 时也要处理:把余量分配给已有阶段或补一个有内容的收尾段,未分配的秒数会被执行端用自选动作填满(见 [motion-recipe.md](motion-recipe.md) 3.6)。不要为了塞进数学窗口而静默删掉 exact dialogue。 ### 对白预算 评估:句长与语言、人物语速、情绪造成的停顿/吞吐、是否需要听见对方/环境 cue、口型要求、动作是否能与说话叠加、结尾是否有 reaction landing。固定每秒字数只能作为 production profile 的可覆盖估算,不能成为通用规则。 **`craft_default`**:先读取分镜「声音」的预算依据。有同句同表演录音时用实测发声区间;没有时按完整原文、 语言和表演方式给出「文本估计」区间,以较慢一端检查容量,不假称朗读或 TTS 实测。对白可用窗口要扣除 开口前等待、不能同时进行的动作、轮流说话的其他发声与末尾反应;句内停顿已经计入时不再重复扣除。 并行动作可以共享时间,顺序事件必须累加。把依据简写在当前镜头说明里,不放进可复制正文当成模型指令。 例如 accepted duration 为 7 秒,开场与收尾各占 0.5 秒,实际只剩 6 秒发声;若完整台词估计需要 8–10 秒(含句内停顿),即使写成「0–7 秒」也不成立。请求分镜按至少 11 秒重新安排,或按语义拆镜, 待上游修订后同步本镜时长、正文时序和生成设置。不要用更快语速、截断标记或静默删词让算术看起来成立。 ### H3 中文的估算参考 **`craft_default`**:一次同镜头、20 次生成的中文对照中,自然语速约 4.1 个可发声字/秒。 短镜装入更多台词时先出现赶词,较长输入还出现未报错的截断;延长镜头改善了该组表现。 样本不能确定通用截断阈值,其他声音、表演和模型需要另行校准。 无录音时可用约 4.1 字/秒辅助估算实际可发声区间,并另计开口等待、停顿和顺序动作。 这不是按整个镜长计算的保证容量;有音频时以实际发声区间为准,生成后回听是否完整。 这里的字数不含标点。`duration_estimate.py` 的 `dialogue_characters` 包含标点, 不能直接按这一语速换算发声时长。 对白太长时选项: 1. 简化非必要动作,让对白成为主事件; 2. 若 script 允许,把动作与对白合理重叠; 3. 请求 storyboard 延长/拆镜; 4. 若要改台词,请求 write owner。 Video-prompts 不能自行删字、改词或把对白变 VO。 ## 5. 超载修订与审查 ### 超载征兆 - 一个角色完成多次远距离往返和复杂物件操作; - 多个角色都有独立大动作,但 shot purpose 只需要一次揭示; - 长对白、强情绪转折、精细手部动作和复杂 camera move 争夺同一几秒; - 结尾没有时间落住,end state 只被一句“最后到达”宣告; - 为了赶时长,动作顺序违反接触因果或表演没有接收过程。 ### 修订优先级 1. 删除无故事功能的环境/手势活动; 2. 合并可自然连续的动作,保留因果节点; 3. 选择一个主要注意中心,其他角色用必要 reaction; 4. 保留对白与 accepted end; 5. 仍超载则发 storyboard revision request(split/extend/reblock),不越权执行。 ### Reviewer 证据格式 ```text Evidence:SHOT duration 4s;dialogue ref DLG-... 为完整两句;motion 要求角色从窗边走到门、开三道锁、回身交物,并保留结尾反应。 Impact:关键交物和反应难以清楚落地,accepted end 可能只被宣告而未实现。 Required fix:删掉非必要回身动作,或请求 storyboard 拆为两个 authored shots;不得删对白。 Classification:reviewed_invariant Owner:video-prompts(精简)/ storyboard(改边界) ``` 不要只写“动作太多”。也不要因 prompt 有四个动词就自动报错。 -
production-prompt-grammar.md 21.4 KB
# 生产视频提示词:边界、连续性与写法选择 ## 目录 - [表达选择](#一先写核心动作再选择交付格式) - [连续性](#二连续性不同镜头类型都适用) - [已知能力](#三按已知能力调整写法) - [项目参数](#四文本交付读取的项目参数) - [文字与缺陷约束](#五文字与缺陷约束) - [决策规则](#六决策规则) - [范例](#七范例从边界写成通用提示词) - [交付文本边界](#交付文本只含交付内容) - [自检](#八自检清单) 来自对剧本、分镜边界、参考帧与视频任务之间完整链路的定性案例阅读,示例均为合成 改写。本套件的运动说明格式见 [motion-recipe.md](motion-recipe.md);本文档是 生产端语法的对照参考:写通用提示词时借用其成熟约定,但套件产物仍以该文档规定的 结构为准,且**不调用任何生成服务、不出现供应商与模型产品名**—— 模型相关规则一律写成"按目标模型确认"的条件规则。 核心认知: 1. **从已接受的镜头起点出发**:若项目使用首帧,它提供起始构图与可见状态,但人物 可以按剧本要求从运动中开始。提示词只补足人物和画面如何变化到明确终点。 2. **连续性靠前后镜比较,不靠记忆或全文复写**:上一镜终点与下一镜起点逐字段核对 (CON-01);提示词只携带本次执行容易漂移的关键状态,避免冗长说明变成第二套 状态记录。 3. **指代必须可解析**:多人同镜或目标模型容易串角色时重复角色名并写物理成因; 单人且指代无歧义时不把“禁代词”当成通用规则。比喻若不能转成可见动作,再改写 成物理表现。多人同镜时,除了重复角色名,还应给每个参与者附一个**稳定的资产索引** (项目档案决定它长什么样),并在该角色每次被提及处一并出现——只在首次出现时标一次, 后面的提及仍然要靠模型自己记住是谁,多人场面正是它最容易记错的地方。索引在同一交付 范围内保持同一映射,场景与关键道具同样适用。唯一的例外是逐字对白:索引写在说话人 标识与描述里,**不写进围栏内的台词文本**,否则会被当成要念出来或要显示的内容 (见 [delivery-profile.md](delivery-profile.md) 的逐字对白围栏)。 4. **上游顺序不可重写**:台词发生在动作前、动作中还是动作后都属于故事语义;提示词 必须逐项核对台词相对动作的先后位置,不能只保留同一批内容。 5. **手部接触与道具交接必须可执行**:写清放下、换手、双手分离、递交、取回 和画外持有;一只手不能同时握笔、分纸并操作另一件工具。 6. **文字策略先于通用禁字**:上游或资产声明的 `exact-readable` 字段必须保留并指定 受控显示位置;只有没有可读文字义务的镜头才能使用全局 `no-text`。 ## 一、先写核心动作,再选择交付格式 先写不绑定供应商的核心动作说明,再决定交付格式。它只回答:从哪个已接受的镜头起点 出发,谁因什么触发做什么,摄影机和声音怎样服务本镜目的,最后落到哪个已接受的镜头 终点。构图、景别、机位、焦段、光线、情绪标尺或位置式引用只有项目确实需要时才加入, 不为凑齐栏目重复参考帧已经承载的事实。 ### 可选的自然语言写法 下面只示意一种顺序,不是必填链;已由参考帧或项目字段说明的内容可省略: ```text 从 <执行所需的已接受起点状态> 开始。 因 <上游触发>,<主体按上游顺序完成动作/可见处理>;<必要反应或并行变化>。 摄影机 <固定或运动及其职责>;声音 <必须逐字保留的内容及相对时机>。 最终到达 <已接受终点>,并保持 <当前最易漂移的非目标事实>。 ``` 拟声词与实景距离(房间尺度、两处地点的间隔这类空间事实)取自上游内容或已接受项目方案。 动作的幅度、角度与锚点秒数属于另一类:它们是本环节自己写的这一拍的组成部分,正常写—— “肩线下沉约五厘米”“上身向左转约三十度”“起手不动约半秒”说明的是这个动作要做多大、 往哪个方向、停多久,是本次的动作决定,不需要上游先给数字。摄影机的角度与轴线侧属于镜头 记录,按 `framing.angle` 已接受的值写。越权只发生在这些量替上游做决定的时候:改写已接受 时长、改掉镜头已接受的机位角度或轴线侧、声称一个未被接受的空间尺寸、给参考帧里没有的 地理定尺。 可比较的方向、接触、占画关系与相对阶段同样好用,与量级并列,按哪一种更能消除执行歧义来选。 ### 多段交付格式(仅项目已有明确要求时) 交付格式可以要求片段编号、明确时间、位置式参考或文字策略,但这些只是外层格式,不 改变创作者确认的镜头边界,也不创造额外动作。每个片段只写执行所需的起点状态;明确 区间不得重叠冲突,且各段相加要正好用完已接受时长——多出来会被截断,少下来的余量会被 执行端用未经批准的动作填满。若项目使用位置式引用,映射保持稳定且不侵入逐字对白; 没有该要求就不生成这些槽位。分段与槽位各自要回答什么,见 [delivery-profile.md](delivery-profile.md)。 ## 二、连续性(不同镜头类型都适用) ### 本镜关键状态 ```text <角色/道具>-<本镜执行易漂移的字段>:<相对位置、朝向、手、持物或可见状态>。 来源:<已接受镜头边界/资产引用>;保持到:<触发、接触或终点>。 ``` 规则: - 覆盖本镜动作、反应、连续性或下一边界依赖的角色;不入画且状态未变化者留在权威 状态表,通过引用继承,不为“字段齐全”把全场名单倾倒进提示词; - 只有提示词需要执行的继承才用短语重述,其余通过精确引用保持; - 动作进程、头身朝向分离、服装或伤势只有参与本镜变化/保留时才写; - 画外持物必须来自已接受镜头边界或状态表,不用“可能在背后”等猜测补连续性。 ### 表演状态变化 写触发、可见处理、选择与最终状态;跨镜变化或保持都要有人物与后果依据。若创作者已有 自己的表演标尺,只把已接受值作为项目记录读取,不由本文档提议档位、增量或 “每镜升压”公式,也不让数字替代可见表演。 ### 空间连续 - 位置写**相对参照**("病床右侧""右肩上"),不写绝对坐标; - **裸写的"左/右"一律指画面左右**(观众看到的左右);指人物自身的左右必须带主体 ("他的左手""她的右肩")。两种读法混用是正反打方向翻转的主要来源,而单看每一句 都成立,所以文字审查抓不到——必须在写的时候就分清; - 场景资产用方位变体逐镜选用(`探视室(北)`/`篝火(近)`),正反打双方各绑 各的背景资产防穿帮; - 普通连续时间中的位移要有上游动作或过渡;已声明的省略、蒙太奇、梦境或 有意跳切可不同,但要记录权限,不能伪装成普通连续。 - 选择性**折叠、消失、替换**或形变必须写触发、目标范围、结束几何/状态和 保持项;非目标人物、道具、已批准文字显示位置和固定空间关系不能随邻近目标一起消失、 复制或改形。镜头时长和表演负荷放不下变换路径与保持项核对时拆镜(VID-11)。 ### 参考绑定纪律 每条结构化参考先有稳定 `slot_id` 和显式唯一 `order`。若创作者档案使用位置式参考(把角色、 道具、场景绑到稳定编号),编号顺序是确定性记账, 不是审美选择: - **编号方案由创作者档案声明,本套件不规定具体次序**(角色在前还是场景在前,都是档案 的事)。可迁移的要求只有两条:方案一旦发布就固定;新增对象追加到方案指定的位置, **不重排已经发出的编号**。重排不会报错,只会拍错——提示词、审查者与执行端对同一 编号的理解就此分叉。 - **同一编号在整条提示词内只指一个对象**,不因为“这次它更像背景”而改指别的东西。 **每一次提及都要带绑定**。 一旦某个角色在本条提示词里绑定了参考,该角色的每一次出现 都必须带同一个绑定,包括代词式回指和分段之间的重复提及。 - 只在首次提及绑定、后文改用裸角色名,比完全不绑定更危险。完全不绑定时,执行端把该 角色当作一个未指定的人,前后至少一致;**部分绑定**时,执行端把带绑定的提及和不带 绑定的提及读成两个人,同一角色在同一镜里分裂成两副外貌,或在段与段之间换脸。 - 因此“绑定一半”不是折中方案:要么这条提示词里该角色处处绑定,要么处处不绑定并用 重复角色名保持指代(呼应核心认知 3)。 - 绑定只回答“这是谁、这是哪个物件”。它不自动带入参考图里的构图、服装、文字、道具和 故事状态;`role`、`may_control`、`must_not_control` 与检查状态仍按 [motion-recipe.md](motion-recipe.md) 的参考图用途声明。 - 项目没有声明位置式参考时不生成占位编号,也不为“字段齐全”给未出场对象编号。 ## 三、按已知能力调整写法 只有创作者已经提供或接受目标能力说明时,才按本集主导戏型调整提示词写法;本技能 不选购、调用或测试模型,未设置时保留不绑定供应商的通用文本: | 已知能力/限制 | 文本适配问题 | |---|---| | 支持多段声画 | 哪些已确认镜头可只在交付格式中编组,同时仍能单独审查? | | 动作或并发能力有限 | 哪项上游必保内容需要拆镜或请求修订,而不是靠删词假装完成? | | 表演依赖清楚触发 | 哪个反应、处理或选择必须可见,哪些情绪形容词可删? | | 受控文字、对白或口型 | 哪些上游内容允许进入,哪些必须留给后期或标记为未解决? | | 能力未知 | 保留通用文本,不猜时长、质量、并发、文字或声音结果。 | ### 能力说明有三种状态,不是两种 “支持/不支持”会漏掉中间那一段。创作者提供的能力说明通常有两条线,不是一条: | 区域 | 含义 | 对写法的影响 | |---|---|---| | 稳定区 | 在此范围内按说明可执行 | 正常写,不额外收窄 | | 不稳定区 | 仍在允许范围内,但需要反复重试才拿到可用结果 | **在写的时候就收窄**:减少同镜并发主体、拆镜、把复合动作分到两镜 | | 超出范围 | 说明明确不支持 | 回上游改边界,不靠措辞绕过 | 中间这一区最容易被漏掉,因为落在这里的请求**仍然是合法请求**:允许范围的上界回答的是 “能不能提交”,稳定区的上界才回答”提交一次大概率可用”。两者之间的差额由重试补齐, 而重试的代价由项目承担,不由文字承担。所以它是一个**写作阶段的决策点**,不是提交之后 再观察的现象——交出去之后,收窄的机会就没有了。 判断方法不依赖任何具体数字:凡是能力说明里出现“效果较好”“稳定性下降”“可能需要多试几次” 这类措辞的地方,就是两条线之间的位置。把它当成本镜的并发预算读,而不是当成质量描述读。 若创作者的说明只给了允许范围、没有区分稳定区,就按只有允许范围处理,**不自行推断一个更 保守的数字当成事实**;可以据此提出“是否先小规模试一次再铺开”的问题,由创作者决定。 ## 四、文本交付读取的项目参数 | 参数 | 规则 | |---|---| | duration | 从必须保留的动作、反应、对白与最终状态反推,并限制在项目已接受的能力范围;没有已接受值时保持未解决。 | | aspectRatio | 读取项目发行/构图约束,本文不自定常见比例。 | 本技能不产出 `amount`、`resolution`、`task` 或其他执行参数。运动幅度与效果范围只有在 帮助解释已接受的运动要求时进入提示词,不用供应商字段替代故事边界。 ## 五、文字与缺陷约束 1. **字幕层默认关闭**:除非剧本或创作者明确要求字幕、caption 或对白文字叠加,每条视频正文都 用项目提示词语言直接声明全镜任何时刻都不生成这些内容,让对白只通过声音与口型发生;不能只写 抽象的“文字层为空”。这个默认与旗帜、标牌、屏幕等画内文字政策独立;有 `exact-readable` 时 仍然直接禁止字幕层。 2. **画内无文字要求**(项目确实没有可读文字义务时): `画面不要出现任何文字`。若上游或资产要求 `exact-readable`,改为列出唯一允许 字段与受控显示位置,禁止同时写全局 `no-text`。 3. **参考图检查**:提示词中的无文字要求只约束生成意图,不能擦除参考图像素。人物、场景、道具 或关键帧参考若带未批准标牌、界面、号码、水印、字幕或伪字,先**裁切、清理、 遮罩或替换**;会破坏身份、地理或构图时退回参考图归属方决策。`exact-readable` 镜头只允许批准的显示位置和文字版本,不让旧字样从参考图回流。 4. **文字要求一致**:只复述已选择的文字策略;不能用`【不出现任何文字字幕】`抹掉证据文字。 5. **语言与文字分别守上游要求**:对白保留原语言;字幕、屏显或无字策略读取已接受的 文字方案,不把“出海”自动等同于无字幕,也不让翻译覆盖逐字对白。 6. **模型条件只来自已记录观察**:若创作者提供的项目反馈指出身份混淆、比例漂移 或其他具体风险,针对该操作补最少的正向状态说明与保持项;不复制通用缺陷补丁, 不靠首尾重复同一句增加权重。 7. **后处理不在本技能执行**:若项目已有受控文字层或清理决定,只在文本中保留其 归属方引用和交接说明;不把未来清理能力当成当前参考图已检查的证据。 8. **不要用词表判断审美**:项目已知能力若明确某种表达不可执行,记录它为什么失败、当前 替代表达和适用范围;不把一串形容词或生理细节词提升为跨题材禁用清单(VID-10)。 ## 六、决策规则 1. **并发过载就拆**:当人物、道具、特效、可读文字与大动作无法在同一镜头时长和表演 负荷内各自落地时拆镜。并发上限由目标模型试验与项目记录,不从案例次数推成 固定元素配额。 2. **台词与动作按注意力判断**:若大动作遮掉逐字对白、反应或口型职责,拆镜 或请求修订;若同一表演目的需要声画同在,不为分类把它们机械拆开。 3. **人物反应**:权力、关系、信息或代价落到听者身上时必须给可见反应;是否独立成 特写和停留多久由后果与剪辑节奏决定,不按台词句数配额插镜。 4. **台词负载**:从所选时长、语速、表演动作和口型能力反推;放不下就删减、拆镜或 请求剧本修订,不用固定字数替代试听。 5. **收到生产反馈后的修订要可解释**:只改变当前诊断所需变量(拆元素、补关键状态 或换措辞);必须联动的变量一起记录。若形成补拍或替代版,在 `视频提示词.md` 中写明它与母版 的关系、修改前后差异,以及每项原文义务由哪一版承担;普通母版不增加版本账目。本技能不负责重投。 6. **生产预算不由本技能猜**:可读取创作者提供的项目观察来收窄文本,但不移植 无来源的通用成本比例,也不从文字规格声称模型效果。 ## 七、范例(从边界写成通用提示词) 已接受镜头:乙把仍由右手持有的账页交给甲;甲读到签名后停止辩解。参考帧已经承载 身份、服装、桌面地理和基础光线,文字政策只允许账页签名位置显示文字。 ```text 从乙右手仍握住账页、甲的视线停在乙脸上的已接受起点开始。乙把账页沿桌面推到 甲面前,纸张停住后松手;甲先把视线移到签名位置,原本准备开口的动作停下。摄影机 保持两人和账页交接都可读的固定构图,不新增纸面文字或旁观者动作。最终账页归甲一侧, 乙双手离开纸面,甲注视签名位置并停止辩解。 ``` 这里没有重写外貌、完整场景或所有角色状态;本镜关键状态只保护道具交接、视线、文字显示 和镜头终点。若当前项目另需片段编号、位置式参考或明确区间,再从同一动作说明写成交付 格式,不能反过来让格式字段创造剧情。 ## 交付文本只含交付内容 **`VID-22 · reviewed_invariant`**:交给执行端的那段文本里,只能出现要被拍出来的画面内容。 任何描述“我们内部怎么做这件事”的流程词都不得进入。本条同样管关键帧的交付文本。 可复制正文只保留生成所需的画面、动作、声音和限制条件。 内部流程备注留在正文外,避免与生成内容混淆;不假定出现某个词就一定被模型画出来。 不得进入交付文本: 1. 参考图文件名、版本号、切片编号与目录路径; 2. 内部锁定标记、规则 ID、`hash`、字段路径与状态词(`accepted`、`stale`、`provisional`); 3. 走位草图、示意图、分镜板格号的指代(“按草图第二格”“沿用上次走位”); 4. 任务备注、重投说明、失败记录与待办(“这次重生成”“上次手部崩坏”“重点修手”); 5. 与本镜无关或重复的否定罗列。必要的排除条件可以保留,文字限制须服从本镜的文字政策。 6. 把规则说明原样抄入正文,却没有描述主体、环境和具体动作。 将参考范围与修订要求落实为本镜需要的状态和动作;失败记录、规则 ID 和维护说明留在正文外。 ### 合成反例与改写 ```text 参考图:角色甲定妆_v3_最终.png / 场景_厅堂_02.png(已锁定,勿改) 分镜草图第 2 格,沿用上次走位。上一版手部崩坏,这次重点修手。 负面:不要多人、不要变形手、不要模糊、不要出现文字、不要换服装。 画面:角色甲从长桌旁站起,走向门口。 ``` 前三行没有一句是要拍的画面:执行端会尝试把文件名与版本号渲染成画面上的字、把“第 2 格” 读成分格构图、把“多人”“变形手”“文字”当作应当出现的物件;真正的动作只剩最后一行, 起点姿态、触发和终点全部缺失。改写成只含交付内容: ```text 角色甲坐在长桌右侧,双手平放桌面,目光落在对面的空椅上。听见门外一次落锁声后, 右手先撑住桌沿,身体离开椅背站直,随后沿桌沿走向门口,在门前半步停住并侧身回望空椅。 画面内始终只有角色甲一人,双手完整可见并保持在画面内,衣着与起点一致,桌面无字迹。 ``` 改写把每一条工艺备注换成了它对应的可见事实:锁定的参考图变成“衣着与起点一致”,修手 备注变成“双手完整可见并保持在画面内”,无文字要求变成“桌面无字迹”,“不要多人”变成 “画面内始终只有角色甲一人”。 ## 八、自检清单 只检查与当前镜头和项目设置有关的项目;不要为通过清单补写无来源内容。 1. 提示词是否从已接受的精确起点出发,并只重述当前执行需要的关键状态? 2. 动作、反应、连续性或终点依赖的角色/道具是否可解析?无关画外状态是否留在状态表? 3. 多人同镜的指代能唯一解析吗?比喻能转成目标模型可执行的物理表现吗? 4. 表演状态的变化或保持有触发与原因吗?表演收束是否承接了后果? 5. 文字策略与上游内容及资产一致吗?`exact-readable` 是否被全局 `no-text` 误删? 每张参考图是否也完成检查,或已记录裁切、清理、遮罩或替换决定? 6. 并发元素是否都能在镜头时长和表演负荷内完成?若不能,拆镜或请求改边界了吗? 7. 台词、动作与反应能在所选时长和语速内完成吗?听者承担后果时有可见反应吗? 8. 若项目使用位置式参考,映射是否稳定、角色是否明确且未侵入台词?没有该要求 时是否避免生成占位引用? 9. 是否只使用创作者已接受、带适用范围和证据的能力约束,而非通用负面词表? 10. 上游台词相对动作的先后位置逐项一致吗?重写有没有把“事前选择”变成事后解释? 11. 手部接触与道具交接可执行吗?放笔、换手、分离、递交和取回都有动作来源吗? 12. 交付文本里还剩下工艺词吗?文件名、版本号、锁定标记、草图指代、任务备注或成段 否定罗列是否已经改写成可见事实? 13. 角色参考是每次提及都绑定,还是只绑了第一次?编号是否始终遵循档案声明的方案、 没有重排已发出的编号,并且同一编号始终只指一个对象? -
review-and-fixtures.md 9.1 KB
# 视频提示词审查量表与合成案例 ## 目录 1. 审查顺序 2. 证据量表 3. 完成前结构检查 4. 合成正例 5. 合成反例 ## 1. 审查顺序 先核对可见 ID、显式时长、摄影机区间、声音来源和终点一致,再由审查者判断表演可执行性、动作负载、 运镜动机与语义发明。Reviewer 写 finding,不替 motion、shot 或 screenplay owner 修改来源。 Finding 必须含文件与 `MOTION-...` / `SHOT-...`、必要短引文、影响、修订结果、owner、严重程度和状态。 禁止用提示词长度、动词数量、固定动作/秒或“AI 味”替代证据。 ## 2. 证据量表 | 维度 | 核心问题 | 必须引用 | |---|---|---| | Start fidelity | 第一动作是否从关键帧/镜头起点真正可接上? | 起点事实与正文起始句 | | Ordered action | 人物、方向、接触、先后和结果是否清楚? | 动作阶段 | | Performance | 触发、处理、选择和落点是否可见且符合人物目标? | 剧本节拍、镜头职责与运动正文 | | Action budget | 时长内能否保留故事动作、对白与反应? | 时长、距离、台词、动作、运镜 | | Camera | 固定/运动是否一致,且因注意、压力或揭示而发生? | 运镜区间与镜头职责 | | Environment | 是否只动了有依据的环境,未发明天气或事件? | 连续性与环境动作 | | Dialogue/audio | 逐字台词、说话人、VO/OS/SFX 与本场声音策略是否保持? | 场景 ID、声音事实与正文 | | End fidelity | 运动终点是否逐项等于分镜终点? | 终点说明与来源终点 | | Economy | 参考帧已承载的外观是否被无谓重复? | 参考内容与可复制正文 | | Shot boundary | 是否偷改时长/终点/下一镜,或在单镜内藏切? | `SHOT-...` 与 `MOTION-...` | | Segment integrity | 每个计时段是否只有一个连续视角,时间并集是否正好覆盖本镜? | 分段与镜头时长 | | Delivery grouping | 多镜分组是否连续、同场、同一地理/资产链,时长是否等于成员之和? | 分组章节与成员时长 | | Deliverable text | 可复制正文是否只剩要生成的内容? | 提示词正文 | 语义 finding 的修复应指出删/改哪一段运动,或该向哪个 owner 提出什么修订,不笼统说“动作自然一点”。 ## 3. 完成前结构检查 - 每个 `MOTION-...` 唯一对应真实 `SHOT-...`,并继承相同时长; - 起点、主要变化、分段时序、声音、运镜和终点没有互相冲突; - 显式分段的并集既不超时,也不留下由模型自行填充的空白;重叠必须说明; - 固定和运动摄影机不在同一区间同时生效; - 终点逐项对齐分镜,下一镜仍读取分镜自己的起点; - 同一镜头最多进入一个交付分组;分组加散镜恰好覆盖点名范围一次; - 静态漫剧只做关键帧切换加配音时可以没有逐镜运动正文; - 可复制正文不含文件名、版本号、ID、状态、哈希或任务备注。 未分组的散镜和时长尚未确定的镜头只报告为当前状态,不伪装成质量缺陷。动作可行性、隐藏剪辑、 语义发明和无动机运镜仍需 reviewer 结合剧情判断。 ## 4. 合成正例 以下人物、场景、对白均为虚构合成材料。 ### Accepted boundary 摘要 - `SHOT-EP001-014`,duration `5.0s`,purpose:罗静听见门外有人试锁后,选择隐藏登记簿而非立刻逃跑; - start:她坐在检修台边,左手翻开的登记簿,右手握笔,目光在页上;后方安全门位于她右后侧; - dialogue:画外男声 `[OS] “里面有人吗?”`; - end:她仍坐着,左手把登记簿压在工具盒下,右手握笔停在桌沿,目光锁向右后侧安全门; - camera:接受的单镜头、固定轴线,可在触发后轻微推进。 ### 合格 generic prompt > 从参考帧的坐姿开始:她的左手仍按在翻开的登记簿上,右手握笔,目光落在页上,右后方是安全门。门外先传来一次短促的试锁声;她的笔尖立刻停住,目光先移向安全门,但身体没有起身。画外男声问“里面有人吗?”,她屏住一拍,没有回答,左手才把登记簿平稳滑入旁边工具盒下方,动作克制,避免纸页发声;右手始终握笔,最后停在桌沿。表演由专注工作转为警觉,再落到压住恐惧后的主动隐瞒。摄影机开头保持固定,在她决定藏起登记簿时做一次很短、很慢的推进,终点仍保持既定轴线,将她的左手、工具盒和望向安全门的视线纳入同一画面。维修间底噪持续,试锁声和画外问话清楚置于门的方向,无音乐突入。5 秒内完成,结尾保持她仍坐着、左手把登记簿压在工具盒下、右手握笔停在桌沿、目光锁向右后侧安全门。 为何有效:开端只重复运动关键事实;动作按声音→接收→决定→隐藏排列;OS 不要求口型;camera 在选择时启动;结尾逐字段落到 accepted boundary,没有写下一镜。 ## 5. 合成反例 ### 反例 A:外观倾倒与边界改写 > 她有窄长脸、断眉、短卷发,穿墨绿工装、米白 T 恤、深色长裤和短靴,维修间墙壁每一处材料都清晰。她站起来跑到门外,把笔交给陌生人,然后下一镜已经来到街上。 Finding:reference 已携带外观而 motion 没写关键表演;更严重的是 start 从坐姿跳到站立、end 新增跑出/道具转手,并代写下一镜。修复需回到 accepted 坐姿 end;若逃跑是创作意图,向 storyboard 请求新 boundary/shot。 ### 反例 B:动作超载但不能靠计数器判 > 5 秒内,她听完整句问话,翻完三页,把册子锁入抽屉,走过房间关闭两扇窗,拆下墙上话筒,打电话说两句,再回到原座位保持完全静止。 Reviewer 应引用房间距离、物件操作、对白和 landing 说明不可行;不能只说“有七个动词”。优先保留与隐藏决定有关的动作,其余删减或请求 split/extend。 ### 反例 C:camera 显式矛盾 ```text 0–5s:摄影机绝对锁定、没有任何移动。 1–4s:摄影机持续向前 dolly 并手持环绕角色一周。 ``` 同一区间的 lock/move 可结构阻断。创作者可选择 lock 或 move;若需先锁后推,写不重叠 transition。 ### 反例 D:音频语义发明 > 画外问话后,突然响起爆炸,所有灯熄灭,她大喊“我承认了”。 若 source 没有爆炸、停电和这句对白,这会改变故事与 continuity。由 reviewer 引用 source 缺失与 prompt 新句给 `VID_SEMANTIC_INVENTION`,不是因关键词“爆炸”本身被正则禁止。 ### 反例 E:段内藏切 ```text 段 2(1.6–3.4s):她把登记簿推入工具盒下方;随即是走廊外一只手停在门把上的近景; 再回到她的侧脸,眉心收紧。 ``` 技能正文已经规定单个镜头内部只保持一个剪辑边界,但违规通常不出现在 `camera` 字段冲突里, 它藏在**一个计时段内部**的连续叙述中,读起来只是“镜头很有节奏”。量表要能抓到它, 而不只是禁止它:逐段列出空间锚点、被摄主体和机位关系,段内出现没有过渡的空间或主体 跳变,就是一次未申报的剪辑。修复是向 storyboard 请求拆成两个 authored shots,或删掉插入的 外景段落;不能靠补一个转场词把它说圆。 注意与多镜容器区分:容器内**成员镜头之间**的空间与主体跳变是已申报的剪辑,不是缺陷。 判据是跳变落在哪里——落在成员镜头边界上,且该边界能追溯到一个已接受镜头,就成立;落在 某个成员镜头的计时段内部,就是段内藏切。容器时长与成员时长之和不符时另记一条容器算术 缺陷,不要把两者混成同一条 finding。 ### 反例 F:不可执行的微计量 ```text 0.00–0.37s:眉心下压 0.4 厘米;0.37–0.92s:右手向左平移 3 厘米,头部旋转 7 度; 0.92–1.41s:瞳孔收缩,肩线下沉 1.2 厘米。 ``` 这类写法读起来像精度,实际没有指定任何可执行的东西:执行端无法把厘米和度数对到画面 尺度,审查者也无法验证是否照做;同时它挤掉了真正需要写清的触发、接触与结果。修复是 换成可比较的相对量与接触事实——“手指移到杯沿并停住”“视线从对方脸上落到桌面签名处”。 只有上游提供、且确实能消除歧义时才写具体秒段(见 `motion-recipe.md` 3.6)。 ### 反例 G:整段独白 ```text 段 1:她想起母亲临走前那句话。段 2:她意识到自己再也回不去了。段 3:她下定决心。 ``` 三段都只有内心活动,没有一件可拍的事。没有可执行内容时,执行端会自行发明动作与表情 去填满时间,结果与剧本无关。修复是让每段至少落到一个可见事件(目光、接触、位移、决断 动作)或一段有来源的声音;确实需要内心过程时,用已接受的画外音承载,并写清它与画面 事件的相对时机。内心独白本身不是缺陷,把它当作整条提示词的唯一内容才是。 -
seedance-2.0.md 3.4 KB
# Seedance 2.0 提示词方言 只在目标档案明确写 `target_video_model: seedance-2.0` 时使用。推荐档案: ```json { "target_video_model": "seedance-2.0", "video_prompt_dialect": "seedance-2.0", "video_prompt_language": "zh-CN", "native_duration_seconds": {"min": 4, "max": 15}, "supported_generation_modes": ["text", "reference", "extend"], "audio_generation": "same_pass" } ``` 这是正文方言,不是固定模板。仍以分镜的起点、唯一动作和终点为内容权威。 ## 正文组织 - 用中文写“主体 + 动作细节 + 场景 + 光色 + 运镜 + 视觉形态 + 约束”。具体可见事实优先,质量词只保留必要项。 - 单镜直接按动作先后写。一次生成含多个镜头时才用 `镜头1:`、`镜头2:`;每个内部镜头都写 “谁、在哪、做什么、镜头怎么动”。官方说明 2.0 响应镜头序号、不响应时间戳;秒数可作为内部预算, 正文用动作顺序与相对停顿表达,不把逐秒时间码当作执行保证。 三次小样本的切点没有按两镜均分。需要精确成员边界时,评估独立生成再剪或其他方言, 并核对实际输出;容器时长算术正确不代表切点准确。 - 每个可辨对白事件都必须写成 `{角色用中文说:“逐字台词”}`,不能只留下普通引号或把说话要求散落 在括号外;对白全文只用一种口语语言,专名除外,不在台词前增加外语暖场。 - 每个音效事件写 `<声音>`,音乐写 `(音乐)`。字幕只有创作者明确开启时才用 `【字幕】`;默认禁字幕时正文 明写“全程保持无字幕,不生成对白文字叠加”,并且不出现任何 `【】` 字幕指令。画内获准文字仍逐字说明承载面和位置。 ## 参考与续写 - 每次提及素材都重复绑定:`人物 @图片1`、`参考 @视频1 的运镜`、`参考 @音频1 的音色`,不要只在开头列素材清单。 - 普通参考任务才写“参考 @视频1”。若任务是接着上一段实际结果生成,写“向后延长 @视频1”,并用 `@图片1` 指向该视频的实际尾帧,说明它是新段开场的可见状态;不要再把任务写成“参考上一段视频”。 - 实际视频与实际尾帧缺一时不宣称连续生成。素材序号来自当次 job 的真实绑定,不沿用文档里的 REF ID 猜序号。 ## 时长与边界 Seedance 2.0 手动指定的整数时长是 4–15 秒。镜头进入本阶段前就应落在区间内。短于 4 秒的叙事动作放进 至少 4 秒的镜头,并让终点成为可见 hold;长于 15 秒的动作在分镜阶段按状态闭合点拆开,不能在这里 截断或跨镜省略交接。 对白、VO、OS 都按 [performance-action-timing.md](performance-action-timing.md) 的发声窗口复核; 同轨音画生成不代表正常语速必然容得下原文。API 另支持 `duration: -1`,由模型在 4–15 秒内自动选择。 固定镜长交付仍传已接受的正整数;采用自动时长时说明它是自适应生成,返回后按实际视频重新核对剪辑时长、 逐字完整性和语速。自动选择不会突破 15 秒上限,也不能替代超长对白的拆镜。 依据(2026-09-07 核对):[视频生成 API](https://docs.volcengine.com/docs/82379/1520757)、 [Seedance 2.5 指南中的 2.0/2.5 时间控制差异](https://docs.volcengine.com/docs/82379/2607689)。 -
seedance-2.5.md 8.4 KB
# Seedance 2.5 提示词方言 只在目标档案明确写 `target_video_model: seedance-2.5` 时使用。推荐档案: ```json { "target_video_model": "seedance-2.5", "video_prompt_dialect": "seedance-2.5", "video_prompt_language": "zh-CN", "native_duration_seconds": {"min": 4, "max": 30}, "supported_generation_modes": ["text", "reference", "edit", "extend"], "audio_generation": "same_pass" } ``` 2.5 沿用中文自然指令和 `@图片N`、`@视频N`、`@音频N` 绑定,但不能直接套 2.0:它支持 30 秒长叙事、 整数秒时间戳控制,以及明确的参考、编辑、延长三类全模态任务。 ## 生成与参考 - 短单镜按“主体 + 动作 + 场景 + 光色 + 运镜 + 风格 + 约束”写。 - 单镜内部的动作时机先写清先后与触发;散文秒数不保证精确落点。 - 多镜或长叙事按 `镜头 1 [0:00–0:03]:` 写时间段,不重叠、不留无意空档,最后终点等于总时长。 时间戳仍有生成偏差,不能作为音节同步或精确剪辑点的保证。 - 每次使用素材时重复绑定 `人物 @图片1`、`@视频1 的表演节奏` 等具体职责。普通参考任务说明保留和 借用什么,不让一个素材顺手控制身份、动作、构图等所有维度。 - 多镜容器使用一份参考清单,各镜的 `@图片N` 重新映射到这份清单,不能照抄独立生成时的编号。 - 多镜容器不自动保证跨镜一致性。一组对照没有改善,但容器的起始帧锚点也更少,不能归因于 一次生成本身。按当前参考输入检查结果,不把该次观察变成禁用规则。 - 多镜容器包含切口;一镜到底是连续画面。按叙事目的选择,并保留生成后剪辑的余地。 ### 两端都锚:`用途:起始帧` 与 `用途:结束帧` 接口提供 `first_frame` 与 `last_frame` 两个位置,各自只收一张。 终点状态重要时可增加尾帧约束,仍需检查生成后的中间动作与节奏。 按分镜已接受的终点制作尾帧并绑定 `用途:结束帧`;套件内生产入口见 `$short-drama-storyboard` 的「首尾成对」。未提供尾帧时,在正文中说明终点。 两个帧位仍然各占一个图片序号,所以 `@图片N` 的编号按素材出现顺序连续往下数,不跳过它们。 首尾帧与其他参考能否混用,按目标接口核对并验证。H3 的互斥规则不能直接套到 2.5, 2.5 的素材角色共用一个 `content` 数组也不能证明任意组合都被接受。 ### 音色参考 目标接口支持 `reference_audio` 时,可用已接受的角色参考音频辅助约束声音。 参考可以来自预置音色合成或已授权录音,选型由视觉设定记录,生成后回听确认。 当前 creator-first 的 `输入参考图` 只接受图片,封闭用途表也没有「音色」。 不要在该字段塞入音频或写 `用途:音色`;这会被校验器拒绝。 内置 adapter 可处理音频 role,但从五文档准备带音频的 job 尚无完整绑定路径。 需要该能力时使用已支持音频绑定的外部执行流程,并记录角色、音频来源和实际 role; 不能仅凭正文提到音频就声称已经绑定。 ## 编辑与延长不是普通参考 提示词意图必须与 job 的 `omni_reference_task_type` 一致: - `reference`:基于素材生成新视频,写“参考 @视频1 的运镜/动作”或具体素材职责。 - `edit`:直接点名 @视频1,写“保留 A、B,只修改 C”;不写成“参考它生成另一段”。该模式的 API 参数必须是 `ratio: adaptive`、`duration: -1`。 - `extend`:写“在 @视频1 的基础上续写 N 秒”,再从输入视频的实际终点描述唯一动作和新终点; API 使用 `ratio: adaptive`。连续创作可另绑实际尾帧为 `@图片1`,只控制新段开场可见状态。 若提示词写的是编辑而 job 标成参考,或反过来,2.5 仍会根据语义复判并报任务类型不匹配;因此任务 类型是创作选择,不由 adapter 从若干关键词猜。 **它写在哪里**:creator-first 的 `MOTION-...` 条目里加一行 `- 任务类型:reference | edit | extend`, 写在 `生成方式` 之后。这两个字段回答的不是同一个问题,谁也不能代替谁: | 字段 | 回答什么 | 取值 | 谁决定 | |---|---|---|---| | `生成方式` | 这一镜送不送图 | `文生视频` / `图生视频` | 由「输入参考图」是否为空机械推出,`creator_markdown_check.py` 会核对 | | `任务类型` | 这一次是参考、编辑还是延长 | `reference` / `edit` / `extend` | 创作决定,本套件不推断 | `edit` 与 `extend` 使用视频素材,不一定有图片;不能把“送素材”一律写成“图生视频”。 现有五文档的图片字段不足以表达纯视频输入,准备 job 时需核对实际绑定。`edit` 与 `extend` 在 API 上还各自要求 `ratio: adaptive`(`edit` 另要求 `duration: -1`),送错会被 2.5 判为任务 类型不匹配。目标模型不是 2.5 时不写这一行——其余方言没有这个概念。 ## 对白、声音与文字 - 每个可辨对白事件都必须整体写进 `{角色用中文说:“逐字台词”}`,不能只用普通引号;同一句带花括号 的逐字对白在单镜正文只出现一次,放进完整声音时间线。视觉时间线只写口型同步这一事件,不再重复 花括号和台词。每个音效事件写进 `<声音>`,音乐写进 `(音乐)`。明确开启的字幕才用 `【字幕】`。 中文对白不添加语言前缀;非中文对白才在台词前明确语言。 - 同轨声音按事件闭合。上游已限定只有中文并明确排除外语前导、语气词、额外人声或重复时,在完整 声音时间线末尾直接写“对白直接从指定中文首字开始;不生成外语前导、语气词、额外人声或重复对白”, 只写一次。没有这些上游边界时不自行追加固定负面后缀。 - 默认禁字幕时明确写“全程无字幕、无对白文字叠加”,且正文不出现 `【】`;画内获准文字仍独立说明。 ## 连续容器 同一连续时空的相邻正式生成段默认组成连续链;`one-shot-per-generation` 只表示每次提交一个镜头, 不取消连续关系。第一段按已接受起点生成,第二段起固定走 `extend`,不能因上一段尚未生成就改成 独立文生视频。创作者明确要求独立重生成,或上游存在时间/地点跳变时才重新起链。 真实输入尚不存在时,在容器元信息写“待续接、不可提交:等待上一容器实际视频与从该视频抽取的实际 尾帧”,正文只作为下一段内容草案;不绑定 `@视频1`、`@图片1`,也不声称已经延长。两项真实素材 存在后才把正文转成可提交版本:写“在 @视频1 的基础上续写 N 秒”,并明确 `@图片1` 只控制新段开场 可见状态。逐镜提示词是容器成员依据,不是另一条生产路由。 ## 时长 Seedance 2.5 手动指定的整数时长是 4–30 秒。项目采用一镜一生成时每镜均落在此区间;采用 30 秒多镜容器时, 先按交付形态确认容器,不把若干独立镜头擅自合并。短动作到达终点后 hold,超长动作按状态闭合点拆分。 对白、VO、OS 同样按 [performance-action-timing.md](performance-action-timing.md) 先预算再排时。 30 秒扩大了可用容量,不代表固定短镜中的长对白会自动恢复自然语速。 - 普通生成/参考任务支持 `duration: -1`,由模型在 4–30 秒内选择,2.5 默认也采用该值。固定时长交付 仍显式传已接受的正整数;采用自动时长时,不把规划镜长写成已经保证的输出长度。 - `edit` 的 `duration: -1` 表示基本保持待编辑视频的时长。换成更长对白时仍须检查原片发声窗口, 不能靠 `-1` 自动延长;放不下就回上游调整任务或台词,不静默切换为延长。 - `extend` 的时长针对新增片段,预算其中新增对白与过渡,不把原片长度当成新增发声容量。 实际回测需听台词完整性、语速、尾音,并测量媒体真实时长。API 返回的 `duration` 是按帧数折算后向下 取整的近似秒数;编辑结果也可能与源片略有时长差异,不能只看响应整数就判定逐秒满足要求。 依据(2026-09-07 核对):[视频生成 API](https://docs.volcengine.com/docs/82379/1520757)、 [Seedance 2.5 提示词指南](https://docs.volcengine.com/docs/82379/2607689)。 -
stage-contract.md 10.8 KB
# 视频提示词阶段契约 本阶段只拥有 `剧集/<EP>/视频提示词.md` 中的 `MOTION-...` 项与按需的时间线音乐章节。它继承镜头 起点、终点、时长、对白和冻结帧,不回写分镜或视觉设定:镜头边界、职责、起终状态和关键帧正文都不在 本阶段手里。 唯一的例外是参考准备。SKILL 工作流第 2、3 步要求在同一请求内刷新受影响镜头的「输入参考图」 (必要时一并回填「视觉依据」),那是**分镜 owner 在本请求内的动作**,按分镜阶段的字段规则执行, 不是本阶段改写分镜的授权。它只更新这两条依据字段,不动镜头边界、职责、起终状态或关键帧正文。 每项正文只描述从当前冻结起点到终点的变化;多镜打包、补拍或替代关系确有需要时,也写在同一文档 的可读章节中,不建立 motion spec、container、readiness、QA 或接受记录。 ## 本阶段规则 ### `VID` | ID | Class | Knowledge | |---|---|---| | VID-01 | structural_invariant | Motion reads but cannot rewrite shot start/end/duration/dialogue and next-shot state. The duration a MOTION declares is compared against its SHOT: they must be the same number, because 时长 is what reaches the execution end and what VID-04/VID-13 arithmetic is built on. A downstream copy left behind by an upstream revision is a defect even though every string still parses. | | VID-02 | craft_default | Write start anchor, ordered subject motion, camera behavior, timing, and end report; add performance change and environment/audio only when this shot actually carries them. | | VID-03 | structural_invariant | Choose image-to-video only when the matching storyboard's reference set is complete, declares readable REF inputs, and is copied unchanged. No REF is not by itself consent to text-to-video: first discover and bind matching real project images; preserve any verified partial bindings, but if required images remain missing, report them and stop before the final motion document. Choose text-to-video only after the creator explicitly selects it, and carry a non-empty static visual anchor in the copyable text. Only image-to-video may omit appearance/composition already carried by its real reference frame. | | VID-04 | structural_invariant | Explicit segment timing sums exactly to its shot's accepted duration—neither exceeding it nor leaving an unallocated remainder. | | VID-05 | reviewed_invariant | Untimed action load must be feasible enough to preserve the intended performance and story change. | | VID-06 | structural_invariant | Locked and moving camera instructions cannot govern the same interval without an explicit transition. | | VID-07 | taste_option | Camera may be locked or moving; audio/lip-sync detail follows the chosen production profile. | | VID-08 | reviewed_invariant | Structured motion names this shot's exact subjects, actions, contacts, and results rather than reusable placeholders; when a performance path is present, it names only the actors and visible changes this shot actually carries. | | VID-09 | structural_invariant | The next start names an existing `SHOT-...` or is explicitly unresolved; never invent a hidden source key, record or hash. | | VID-10 | craft_default | Resolve one accepted production profile for the current delivery scope; local variants may coexist when their range and precedence are explicit, without overriding source coverage or exact-readable obligations. | | VID-11 | reviewed_invariant | A selective transform names its trigger, exact target scope, end geometry/state, and preserve set so non-target people, props, text surfaces, and spatial anchors do not change with it. | | VID-12 | reviewed_invariant | A pickup or alternate names stable `MOTION-...` IDs and maps each source requirement to this version, the master, another pickup, or a requested storyboard revision; it never silently replaces the master. | | VID-13 | structural_invariant | A multi-shot delivery group contains contiguous shots from one scene with one geography/asset chain. Its duration equals the sum of member shots, and grouping changes neither shot boundaries nor per-shot reviewability. | | VID-14 | craft_default | Music intent may be annotated per shot as a relative entry/exit/duck against neighbours, but its realization belongs to the timeline layer; no deliverable—single-shot or multi-shot container—carries a baked-in music bed unless the project accepted otherwise or the source is diegetic. Dialogue, off-screen sources, ambience, and event effects stay with the deliverable. | | VID-15 | structural_invariant | Within one episode a shot belongs to at most one delivery group; grouped and loose shots account for the visible shot set exactly once. | | VID-16 | reviewed_invariant | When performance changes, multi-character motion differentiates the actors who actually carry it and keeps each chosen signal readable in the accepted framing; it does not require an arc for non-performing shots, force every craft field, or duplicate one emotion across the cast. | | VID-17 | reviewed_invariant | Multiple references use stable visible IDs and an explicit order, so reordering cannot silently change which reference controls which property. | | VID-18 | reviewed_invariant | Readiness is reported from the current visible inputs and real blocking gaps, not persisted as a motion fact; text readiness never claims generated identity, performance, lip-sync, mix, edit or market quality. | | VID-19 | reviewed_invariant | When the creator profile declares required literal tokens for a delivery route, that route's delivery text preserves them byte-for-byte and outside the verbatim-dialogue fence. Paraphrase, translation, reordering, or omission is treated as a defect because a literal-matching surface has no reason to reject the rewritten text—the failure is silent rather than reported. The suite asserts no specific surface's behaviour, and whether a given result took the route is returned adherence, provable only by a bound production observation. Tokens declare a route and never substitute for the start state, action, or endpoint. Absent a declared token list the suite invents none; until the profile exposes a machine-readable list at a pinned field path, the reviewer cites the profile against the delivery text rather than claiming mechanical enforcement. | | VID-20 | reviewed_invariant | Packing routes change delivery granularity only, leaving shot boundaries, shot purpose, and per-shot reviewability intact. A single long-form generation carrying several accepted shots *is* a multi-shot container and is billed under VID-13 and VID-15; it introduces no separate accounting and no exemption from the contiguity, binding-chain, and scene-boundary constraints. A continuation route instead starts from a previously generated result, which is observation evidence and not an accepted artifact: the accepted shot start boundary stays the sole authority, and any claim about the observed state binds a production observation record or remains `unverified`. | | VID-21 | craft_default | When the project's accepted production profile has generated imagery carry the frame, actions are written as high-frequency, whole-body or single-limb movements common in everyday footage; precise interception, invisible internal states, negative actions, and three-or-more-step two-handed choreography are rewritten into equivalent common-action combinations, with the dramatic information carried by combination and timing. The rule is inactive for live action or an undeclared profile, and a rewrite may never change accepted shot boundaries, terminal states, or screenplay fact. | | VID-22 | reviewed_invariant | Copyable text describes the intended image and action. IDs, workflow status, hashes and file paths stay outside it. Prefer observable states where they clarify execution; necessary negative constraints are allowed. Unless explicitly authored, prohibit non-diegetic subtitles, captions and dialogue overlays while preserving approved in-scene text and its exact content, carrier and placement. | | VID-23 | reviewed_invariant | For models generating audio with video, specify the intended dialogue, VO, OS, vocalizations, effects, ambience and silence. Keep source, speaker, language and verbatim dialogue boundaries. Summaries must match the actual sound layers; follow model-specific music syntax without assuming missing layers will always be generated. | | VID-24 | structural_invariant | An image-to-video shot carries the storyboard's `REF-...` and `PLAN-...` slots unchanged, each with its 用途, so the motion document itself answers which start frame and which identity, geography, and prop pictures this shot sends — and, for `PLAN-...`, in which order the creator attaches them. The reference mode — first frame, first and last frame, or multi-slot reference — is read from that 用途 combination and translated by the matched model dialect; where a model makes frame and reference conditioning mutually exclusive, the start frame joins the reference group rather than splitting the request. | | VID-25 | structural_invariant | Quoted Chinese in a copyable body is delivered, not authored. Every run of four or more Han characters inside a `<d>` tag or quotation marks resolves, ignoring punctuation and spacing, to text that already exists in `剧本.md`, `视觉设定.md` or `分镜.md`. A line the shot wishes the character had said belongs upstream in the screenplay, not in the prompt. Runs of three characters or fewer are in-frame labels and interjections far more often than lines, so they stay with review rather than the mechanical check. | ### `CON` | ID | Class | Knowledge | |---|---|---| | CON-01 | structural_invariant | Linked end and next start states match or have an explicit owner revision. | | CON-02 | reviewed_invariant | Knowledge, injury, ownership, weather, light, or physical state does not teleport/regress without story cause. | | CON-03 | craft_default | Track downstream-relevant deltas, not the whole 设定集 in every shot. | | CON-04 | structural_invariant | A continuity change states before, after, cause/source scene, effective range, and affected visible IDs. | | CON-05 | taste_option | Declared montage, ellipsis, dream, or subjective imagery may intentionally break ordinary continuity. | | CON-06 | structural_invariant | A continuity change names every existing downstream document it affects; future work is described, not pre-created. | | CON-07 | structural_invariant | A continuity lock declared in `视觉设定.md` fixes one verbatim surface plus its shot and image scope; every in-scope frozen keyframe, motion prompt, and named image prompt carries that surface unchanged. | 规则分级由高到低:`structural_invariant`(结构缺陷,阻断)、 `reviewed_invariant`(需证据判断)、`craft_default`(常用做法,可覆盖)、 `taste_option`(创作者选择,不作缺陷)。创作者已接受的事实优先于本表。 -
target-model-profile.md 8.8 KB
# 目标模型能力档案 ## 目录 - [这份档案解决什么](#这份档案解决什么) - [九条会改变正文的能力轴](#九条会改变正文的能力轴) - [声音同轨生成](#声音同轨生成) - [参考条件方式](#参考条件方式) - [未声明时怎么写](#未声明时怎么写) - [自检](#自检) ## 这份档案解决什么 本套件的模型相关规则一律写成“按目标模型确认”,但“确认”在实践中经常没有落点:没人知道要确认 哪几件事,于是提示词按最保守的写法交出去,执行端再靠自己补——补出来的往往就是漂移。 本文只做一件事:把**需要确认什么**列成有限的几条。通用档案不猜供应商能力;已核对官方契约的 模型可以由本技能的版本化方言文件提供推荐值。每一条取值都由创作者按自己接入的执行端声明一次, 本集共用。生产端具体接哪个供应商、用什么字段,属于 `$short-drama-produce` 的 adapter 档案,不进入创作文档。 **`craft_default`**: 能力轴是**声明**出来的,不是推断出来的。没有声明就按“未声明时怎么写”写; 不要因为某个执行端“据说支持”某种写法就改写已接受的镜头边界、时长或起止状态。 档案写在 `short-drama.json#/creator_authority/production_profile/choices`。最小结构如下;未知字段留空, 不能从模型简称补全: ```json { "target_video_model": "versioned-model-profile", "video_prompt_dialect": "dialect-id", "video_prompt_language": "zh-CN", "native_duration_seconds": {"min": 4, "max": 15}, "supported_generation_modes": ["text", "reference"], "audio_generation": "same_pass" } ``` ## 九条会改变正文的能力轴 | 能力轴 | 声明什么 | 声明后正文允许怎么变 | |---|---|---| | 模型版本 | 项目使用的版本化模型档案键;账号侧 Endpoint / model ID 留在生产配置 | 只路由到该版本的方言;不因名称相近套用另一版本 | | 提示词方言 | 通用自然语言 / 目标原生结构及版本 | 命中已有方言时按其字段、素材标记和声音语法写;语言标签不能代替方言 | | 单次原生时长 | 一次生成能覆盖的最短与最长秒数 | 分镜规划时就让每个投产镜头落在区间内;短动作在合法镜长内到达终点后 hold,长动作按闭合状态拆镜 | | 参考条件方式 | 无 / 首帧 / 首尾帧 / 多槽参考(及槽位数上限) | 见下节 | | 声音是否同轨生成 | `same_pass` 或 `separate` | 见下节 | | 画幅与分辨率 | 执行端实际接受的画幅与分辨率集合 | 只写项目已接受的那一个;不在正文里罗列备选 | | 正文长度上限 | 一次能接受的字符数 | 超限时先删已被真实参考帧承载的重复描述;《视觉设定.md》连续性锁的锁面、逐字对白和已批准文字显示位置不得删 | | 运镜词汇 | 执行端是否接受具名运镜术语 | 不接受时把运镜写成可见的画面关系变化(主体占画比、边缘进出、前后景遮挡关系),不是换一个术语 | | 视频提示词正文语言 | 目标视频模型本次使用 `zh-CN`、`en` 或其他明确语言 | 视频可复制正文使用该语言;逐字对白、VO、OS 仍保留原口语语言,不翻译、不加外语开场 | ## 声音同轨生成 有些执行端只产出画面,声音在后期补;有些执行端在同一次生成里同时产出对白、环境声与音乐。 两者对正文的要求不同,而且**这是本套件里最容易被忽略的一条**: - **不同轨**(默认假设):声音段落写本镜的声音**职责**——谁说话、什么环境声承担信息、 转场声落在哪里。它服务于后期与审查,不需要写成可执行指令。 - **同轨**:声音段落变成会被真的生成出来的东西。可复制正文写完本镜的完整声音时间线:需要的 对白、VO、OS、非语言发声、SFX 与相对时机,以及各事件之间承接的 ambience 或静默。逐字对白 仍按 [交付形态](delivery-profile.md) 的围栏处理。没有定义的时间与通道会被执行端自行填满, 和没写终点的动作会被自行补动作是同一件事。 有对白、VO 或 OS 时,把本镜所有允许的发声列成封闭事件集合,每句带口语语言和逐字起止边界, 事件间及首尾明确回到已声明环境声或静默。必要的叹气、笑声、拟声或重叠也必须有来源并进入集合。 Brief、剧本或声音方向若已经点名排除外语前导、语气词、额外人声、重复或其他发声,完整时间线末尾 必须用正文语言把这些已确认边界合并写一次;“允许事件只有这些”不能替代模型需要的直接指令。 一句“以上为完整声音时间线”闭合并结束声音段落;其后不再重复同一组排除项,也不自行添加上游没有 提出的声音禁令。 非画内字幕、caption 和对白文字叠加是独立文字层:除非剧本或创作者明确要求,每条可复制正文 都用项目提示词语言直接声明全镜任何时刻不生成这些内容,不能只写抽象的“文字层为空”,让对白只 通过声音与口型发生。这个默认不受画内文字影响。旗帜、标牌、 屏幕等表面确有 `exact_readable` 义务时,正文逐字保留准确字符、语言、承载面和位置,同时继续关闭 字幕层;没有画内可读文字义务时,才使用全局无文字要求(`VID` 与 `IMG` 的文字策略优先级不变)。 本套件不规定这些排除写成什么字样。字样属于本镜和项目已声明的提示词语言;不要跨项目搬运一句 固定后缀,也不要把它当成质量分。 ## 参考条件方式 | 声明 | 正文承担什么 | |---|---| | 无参考 | 文生视频:静态视觉锚点必须自足,写清人物、造型、地点、构图与光线中本镜必要的事实 | | 首帧 | 只写从该起点开始的变化;静态外观由参考帧与视觉设定共同拥有 | | 首尾帧 | 终点帧只能投影 `SHOT-...` 已写明的终点,不得发明新结果 | | 多槽参考 | 每张按 `REF-...` 槽位记账,绑定顺序、路径、中文名、用途与控制/不得控制范围;槽位数不超过声明上限,超出时按重要性取舍并写明放弃了哪些 | | 多槽参考(创作者自备) | 同上,但图片不在项目里,槽位改用 `PLAN-...`、定位符写 `IMG-...` 或 `SHOT-...`;正文写法与上一行完全相同,`顺序` 就是创作者在生成界面里挂图的次序。这一行与目标模型无关,任何支持多槽参考的执行端都适用 | 无论哪一种,《视觉设定.md》连续性锁的锁面都留在正文里:参考帧只约束它自己画到的部分, 跨镜不变的颜色、材质与形制仍然要逐镜带走。 参考条件方式由本镜各槽位的 `用途` 读出,不由“想不想用多模态”决定:只有一张 `起始帧` 是首帧, `起始帧` 加 `结束帧` 是首尾帧,`起始帧` 与身份/地理/道具图同时存在或只有这些图时是多槽参考。 不少执行端把首/尾帧输入与多槽参考输入设为互斥,这时整组统一走多槽参考,起始帧也按参考素材送入—— 不要把一次请求拆成“首帧 + 参考图”。正文里的素材标签按同类素材的 `顺序` 编号,与生产端绑定顺序一致; 文档手写编号和执行端实际编号不一致时,模型收到的是两套互相矛盾的素材说明,而接口通常不会报错。 “上一段实际视频”和“实际尾帧”描述的是两个通用职责,不等于供应商 role。命中版本化方言时再翻译: 例如 H3 的 full-reference 使用 `reference_video + reference_image`,不能把实际尾帧误写成与参考模式 互斥的 `first_frame`。生产 adapter 必须执行同一翻译,不能让文档和请求体使用两套模式。 ## 未声明时怎么写 按已接受的镜头边界写完整、自足、不依赖任何执行端特性的正文:视频正文语言回退到项目 `format.prompt_language`;独立任务没有项目配置时沿用用户当前语言,不固定为英文。写声音职责而不是可执行声音指令, 不做分段,不罗列画幅备选,用可见的画面关系而不是术语描述运镜。这样的正文在任何执行端上都成立, 只是没有针对性收敛。**未声明不是缺陷**,不能作为审查阻断理由。 ## 自检 - 每条能力轴的取值是创作者声明的,还是我推断的? - 视频正文语言来自目标模型档案还是项目回退值?逐字人声是否仍保留原口语语言? - 声音同轨时,允许的声音事件、口语语言及事件间底声/静默是否组成完整时间线? - 收敛写法有没有顺手改掉已接受的时长、起点或终点? - 删重复描述时,锁面、逐字对白和已批准文字显示位置留住了吗?
-
-
scripts
-
container_check.py 12.4 KB
#!/usr/bin/env python3 """Reconcile an episode's delivery containers against its shot set (`VID-15`). Every container can be correct on its own while the episode is wrong: a shot packed into two containers bills its seconds twice, and a shot packed into none disappears although its dialogue, bindings, and keyframe prompts are already done. Neither error is visible from inside a single container, so the check is a set comparison at episode scope. The script reads accepted creator files and writes nothing. """ from __future__ import annotations import argparse import json import sys from pathlib import Path from typing import Any # Creators run these scripts on whatever interpreter their machine provides, so # an unsupported version must say so instead of failing inside an import. MINIMUM_PYTHON = (3, 9) if sys.version_info < MINIMUM_PYTHON: raise SystemExit( "short-drama needs Python {}.{} or newer; this interpreter is {}.{}".format( *MINIMUM_PYTHON, sys.version_info.major, sys.version_info.minor ) ) SCHEMA_VERSION = "1.0.0" # A .jsonl file opens with a header record declaring the upstream snapshots its # references name. The header is a declaration, not one of the file's records. SOURCES_RECORD_TYPE = "sources" class CheckError(ValueError): """The inputs cannot be checked at all, as opposed to failing a check.""" def _load_jsonl(path: Path) -> list[dict[str, Any]]: records: list[dict[str, Any]] = [] try: text = path.read_text(encoding="utf-8") except (OSError, UnicodeError) as error: raise CheckError(f"unreadable JSONL: {path}") from error for number, line in enumerate(text.splitlines(), start=1): if not line.strip(): continue try: record = json.loads(line) except json.JSONDecodeError as error: raise CheckError(f"invalid JSONL at {path.name}:{number}") from error if not isinstance(record, dict): raise CheckError(f"JSONL needs one object per line: {path.name}:{number}") records.append(record) if records and records[0].get("record_type") == SOURCES_RECORD_TYPE: del records[0] return records def _finding(code: str, message: str, **detail: Any) -> dict[str, Any]: return {"code": code, "message": message, **detail} def _seconds(value: Any) -> float | None: if isinstance(value, bool) or not isinstance(value, (int, float)): return None return float(value) # The three conclusions `delivery-container.jsonl.md` requires a container to # state about why its members belong together. MEMBERSHIP_BASIS_KEYS = ( "source_order_contiguous", "binding_chain_equal", "scene_boundary_not_crossed", ) def _member_shot_id(member: Any) -> str | None: if not isinstance(member, dict): return None ref = member.get("shot_ref") if isinstance(ref, dict) and isinstance(ref.get("record_id"), str): return ref["record_id"] return None def reconcile( containers: list[dict[str, Any]], shots: list[dict[str, Any]], ) -> dict[str, Any]: findings: list[dict[str, Any]] = [] durations = { shot["shot_id"]: _seconds(shot.get("duration_seconds")) for shot in shots if isinstance(shot.get("shot_id"), str) } episode_shots = set(durations) owner_of: dict[str, str] = {} packed: set[str] = set() container_total = 0.0 for container in containers: container_id = container.get("container_id") if not isinstance(container_id, str): findings.append( _finding("VID15_CONTAINER_HAS_NO_ID", "a container record has no id") ) continue members = container.get("members") if not isinstance(members, list) or not members: findings.append( _finding( "VID15_CONTAINER_HAS_NO_MEMBERS", "a container carries no members", container_id=container_id, ) ) continue # Checkpoint 1: `order` unique, contiguous and ascending. A delivery # package is assembled in this order, so a repeated or skipped number is # an ambiguous cut list, not a cosmetic flaw. orders = [ member.get("order") for member in members if isinstance(member, dict) ] if not all( isinstance(value, int) and not isinstance(value, bool) for value in orders ) or orders != list(range(1, len(members) + 1)): findings.append( _finding( "VID15_MEMBER_ORDER_IS_NOT_A_SEQUENCE", "members must carry order 1..n, ascending and without gaps", container_id=container_id, orders=orders, ) ) # Checkpoint 7: the three membership_basis conclusions. Each is a claim # about why these shots belong in one container; leaving one blank makes # the container's own justification unreadable. basis = container.get("membership_basis") if not isinstance(basis, dict): findings.append( _finding( "VID15_MEMBERSHIP_BASIS_MISSING", "a container must record why its members belong together", container_id=container_id, ) ) else: blank = sorted( key for key in MEMBERSHIP_BASIS_KEYS if not str(basis.get(key) or "").strip() ) if blank: findings.append( _finding( "VID15_MEMBERSHIP_BASIS_INCOMPLETE", "every membership_basis conclusion must be stated", container_id=container_id, missing=blank, ) ) member_total = 0.0 for member in members: shot_id = _member_shot_id(member) if shot_id is None: findings.append( _finding( "VID15_MEMBER_HAS_NO_SHOT_REF", "a member does not name the shot it packs", container_id=container_id, ) ) continue if shot_id not in episode_shots: findings.append( _finding( "VID15_MEMBER_IS_NOT_AN_EPISODE_SHOT", "a container packs a shot that is not in this episode", container_id=container_id, shot_id=shot_id, ) ) continue previous = owner_of.get(shot_id) if previous is not None: findings.append( _finding( "VID15_SHOT_PACKED_TWICE", "a shot belongs to more than one container", shot_id=shot_id, container_ids=sorted({previous, container_id}), ) ) continue owner_of[shot_id] = container_id packed.add(shot_id) seconds = durations.get(shot_id) if seconds is None: findings.append( _finding( "VID15_MEMBER_SHOT_HAS_NO_DURATION", "a packed shot carries no numeric duration", container_id=container_id, shot_id=shot_id, ) ) continue # Checkpoint 3: the member's own `accepted_duration` against the # shot it projects. This field was never read: the total was summed # straight from `shots.jsonl`, so a member could claim 12.0s for a # 6.3s shot and pass, and the execution end would be handed the 12.0. # `motion_timing_check` guards exactly this staleness class; this # file had no equivalent. claimed = _seconds(member.get("accepted_duration")) if claimed is None: findings.append( _finding( "VID15_MEMBER_HAS_NO_ACCEPTED_DURATION", "a member must project the duration it packs", container_id=container_id, shot_id=shot_id, ) ) elif abs(claimed - seconds) > 1e-6: findings.append( _finding( "VID15_MEMBER_DURATION_IS_STALE", "a member's accepted_duration does not match its shot", container_id=container_id, shot_id=shot_id, claimed=claimed, accepted=seconds, ) ) member_total += seconds stated = _seconds(container.get("container_duration")) if stated is None: findings.append( _finding( "VID15_CONTAINER_DURATION_MISSING", "container_duration must be present and a number of seconds", container_id=container_id, ) ) elif abs(stated - member_total) > 1e-6: findings.append( _finding( "VID15_CONTAINER_DURATION_IS_NOT_THE_SUM", "container_duration does not equal its members' durations", container_id=container_id, stated=stated, computed=round(member_total, 6), ) ) container_total += member_total loose = sorted(episode_shots - packed) # A shot whose duration is still open is a legal state upstream, so it is # held out of the arithmetic instead of being reported as an error. Only a # shot packed into a container must already have one, because the # container's own duration claim depends on it. unmeasured = sorted( shot_id for shot_id in episode_shots if durations.get(shot_id) is None ) loose_total = sum( durations[shot_id] or 0.0 for shot_id in loose if durations.get(shot_id) is not None ) episode_total = sum(value for value in durations.values() if value is not None) if abs((container_total + loose_total) - episode_total) > 1e-6: findings.append( _finding( "VID15_EPISODE_TOTAL_DOES_NOT_RECONCILE", "containers plus loose shots do not add up to the episode total", packed_seconds=round(container_total, 6), loose_seconds=round(loose_total, 6), episode_seconds=round(episode_total, 6), ) ) return { "schema_version": SCHEMA_VERSION, "containers": len(containers), "episode_shots": len(episode_shots), "packed_shots": len(packed), # Loose shots are legal: containers need not cover everything. They are # reported so the count is a decision rather than an oversight. "loose_shots": loose, # Reported, not a finding: these shots are excluded from every total # above, so a caller can tell an incomplete episode from a wrong one. "unmeasured_shots": unmeasured, "packed_seconds": round(container_total, 6), "loose_seconds": round(loose_total, 6), "episode_seconds": round(episode_total, 6), "findings": findings, "status": "pass" if not findings else "fail", } def build_parser() -> argparse.ArgumentParser: parser = argparse.ArgumentParser( description="Reconcile delivery containers with the episode's shot set." ) parser.add_argument("containers", type=Path, help="the episode containers JSONL") parser.add_argument("--shots", type=Path, required=True) return parser def main(argv: list[str] | None = None) -> int: args = build_parser().parse_args(argv) try: result = reconcile(_load_jsonl(args.containers), _load_jsonl(args.shots)) except CheckError as error: print(f"{type(error).__name__}: {error}", file=sys.stderr) return 2 print(json.dumps(result, ensure_ascii=True, sort_keys=True)) return 0 if result["status"] == "pass" else 1 if __name__ == "__main__": raise SystemExit(main()) -
motion_timing_check.py 17.7 KB
#!/usr/bin/env python3 """Check explicit motion segment timing against accepted shot duration (`VID-04`). `VID-04` is arithmetic, and it is violated in two directions that fail differently: * **Overflow** — segments extend past the accepted duration. Whatever falls outside is truncated, taking the closing action, the verbatim dialogue tail and the end pose with it. * **Shortfall** — segments stop short. The unallocated remainder does not render as a held frame; the execution end fills it with motion, expression or camera movement that has no upstream source at all. Both are the same rule, so both are reported under the same ID with distinct diagnostic codes. Relative timing plans are out of scope by contract: only a plan that declares itself `explicit` makes a checkable arithmetic claim. The script reads accepted creator files and writes nothing. """ from __future__ import annotations import argparse import json import math import re import sys from pathlib import Path from typing import Any # Creators run these scripts on whatever interpreter their machine provides, so # an unsupported version must say so instead of failing inside an import. MINIMUM_PYTHON = (3, 9) if sys.version_info < MINIMUM_PYTHON: raise SystemExit( "short-drama needs Python {}.{} or newer; this interpreter is {}.{}".format( *MINIMUM_PYTHON, sys.version_info.major, sys.version_info.minor ) ) SCHEMA_VERSION = "1.0.0" # A .jsonl file opens with a header record declaring the upstream snapshots its # references name. The header is a declaration, not one of the file's records. SOURCES_RECORD_TYPE = "sources" # Seconds are authored by hand, so compare with a tolerance rather than by # equality: 0.1 + 0.2 != 0.3 in binary floating point, and a rule that fires on # that would be noise. A millisecond is far below anything a shot can express. TOLERANCE_SECONDS = 1e-6 # An interval reads `0.0-1.5`. Accept the en dash and the CJK wave dash too: # creators type these on Chinese input methods and the distinction carries no # meaning here. INTERVAL_RE = re.compile( r"^\s*(?P<start>\d+(?:\.\d+)?)\s*[-–—~~]\s*(?P<end>\d+(?:\.\d+)?)\s*(?:s|秒)?\s*$" ) class CheckError(ValueError): """The inputs cannot be checked at all, as opposed to failing a check.""" def _reject_json_constant(value: str) -> None: raise CheckError(f"non-finite JSON number is not allowed: {value}") def _finite_number(value: Any) -> float | None: if isinstance(value, bool) or not isinstance(value, (int, float)): return None number = float(value) return number if math.isfinite(number) else None def _load_jsonl(path: Path) -> list[dict[str, Any]]: records: list[dict[str, Any]] = [] try: text = path.read_text(encoding="utf-8") except OSError as error: raise CheckError(f"cannot read {path}: {error}") from error for number, line in enumerate(text.splitlines(), start=1): stripped = line.strip() if not stripped or stripped.startswith("#"): continue try: record = json.loads(stripped, parse_constant=_reject_json_constant) except json.JSONDecodeError as error: raise CheckError(f"{path.name} line {number} is not valid JSON: {error}") from error if not isinstance(record, dict): raise CheckError(f"{path.name} line {number} is not a JSON object") records.append(record) if records and records[0].get("record_type") == SOURCES_RECORD_TYPE: del records[0] return records def _finding(code: str, motion_id: str, message: str, **extra: Any) -> dict[str, Any]: return { "code": code, "rule": "VID-04", "severity": "error", "motion_id": motion_id, "message": message, **extra, } def _parse_interval(value: Any) -> tuple[float, float] | None: """Return an explicit ``(start, end)`` window, or None when unparseable.""" if isinstance(value, dict): start = _finite_number(value.get("start_seconds")) end = _finite_number(value.get("end_seconds")) if start is not None and end is not None: return start, end return None if isinstance(value, str): match = INTERVAL_RE.match(value) if match: return float(match.group("start")), float(match.group("end")) return None def _union_length(windows: list[tuple[float, float]]) -> float: """Total time occupied by ``windows``, counting overlap once.""" total = 0.0 cursor: float | None = None span_end = 0.0 for start, end in sorted(windows): if cursor is None or start > span_end: if cursor is not None: total += span_end - cursor cursor, span_end = start, end else: span_end = max(span_end, end) if cursor is not None: total += span_end - cursor return total def _accepted_duration( spec: dict[str, Any], shots_by_id: dict[str, dict[str, Any]] ) -> tuple[float | None, str | None]: """Resolve the accepted shot duration and any conflict with the projection. ``boundary_refs.duration.value_seconds`` is a read-only projection of the shot record. When both are present and disagree, the projection is stale and the arithmetic below would be checked against a number the storyboard never accepted, so that is reported instead of silently preferring one. """ boundary = spec.get("boundary_refs") duration_ref = boundary.get("duration") if isinstance(boundary, dict) else None projected: float | None = None record_id: str | None = None if isinstance(duration_ref, dict): projected = _finite_number(duration_ref.get("value_seconds")) if isinstance(duration_ref.get("record_id"), str): record_id = duration_ref["record_id"] shot_ref = spec.get("shot_ref") if record_id is None and isinstance(shot_ref, dict): if isinstance(shot_ref.get("record_id"), str): record_id = shot_ref["record_id"] authoritative: float | None = None if record_id is not None: shot = shots_by_id.get(record_id) if isinstance(shot, dict): authoritative = _finite_number(shot.get("duration_seconds")) elif projected is not None: # A reference naming no shot used to fall through to # `resolved = projected`, so the arithmetic was then checked against # the spec's own self-declared number and the staleness guard never # ran -- one mistyped character switched off VID-04. Only reported # when a projection exists: with neither a shot nor a projection # there is nothing to be wrong about, and `unmeasured` stays the # honest answer. return None, ( f"duration reference {record_id} resolves to no shot, so the " f"projected {projected}s is checked against nothing" ) if projected is not None and authoritative is not None: if abs(projected - authoritative) > TOLERANCE_SECONDS: return None, ( f"projected duration {projected}s does not match accepted " f"{record_id} duration {authoritative}s" ) return authoritative, None resolved = authoritative if authoritative is not None else projected return resolved, None def check( specs: list[dict[str, Any]], shots: list[dict[str, Any]] ) -> dict[str, Any]: shots_by_id = { shot["shot_id"]: shot for shot in shots if isinstance(shot.get("shot_id"), str) } findings: list[dict[str, Any]] = [] checked = 0 # Reported, not a finding: a relative plan makes no arithmetic claim, so it # is out of scope rather than passing. Keeping the count visible stops a # file of entirely relative plans from reading as a clean explicit check. relative: list[str] = [] unmeasured: list[str] = [] for index, spec in enumerate(specs): motion_id = spec.get("motion_id") if not isinstance(motion_id, str) or not motion_id: raise CheckError(f"motion spec {index} has no motion_id") plan = spec.get("timing_plan") mode = plan.get("mode") if isinstance(plan, dict) else None segments = [ entry.get("timing") for entry in spec.get("ordered_subject_motion") or [] if isinstance(entry, dict) ] explicit_segments = [ timing for timing in segments if isinstance(timing, dict) and timing.get("mode") == "explicit" ] # Whether the projection matches the shot is a fact about the data, not # about the timing mode, so it is checked before the mode gate below. # Gated behind `explicit` it never ran on a file of relative plans -- # which is every spec in the recorded run -- and a projection authored # wrong from the start was carried downstream unchallenged. duration, conflict = _accepted_duration(spec, shots_by_id) if conflict is not None: findings.append(_finding("VID_DURATION_PROJECTION_STALE", motion_id, conflict)) continue if mode != "explicit": # A relative plan makes no arithmetic claim, so it is out of scope # by contract rather than passing. Explicit segments underneath one # are a contradiction the creator has to resolve — silently doing # the arithmetic anyway would judge a plan the docs promise not to. if explicit_segments: findings.append( _finding( "VID_TIMING_MODE_INCONSISTENT", motion_id, f"timing_plan mode is {mode!r} but " f"{len(explicit_segments)} segment(s) declare explicit timing", ) ) else: relative.append(motion_id) continue if duration is None: # Cannot be judged either way, and staying silent here is the # failure mode this script exists to remove. unmeasured.append(motion_id) continue windows: list[tuple[float, float]] = [] unparseable: list[Any] = [] for timing in explicit_segments: interval = _parse_interval(timing.get("value")) if interval is None: unparseable.append(timing.get("value")) else: windows.append(interval) if unparseable: findings.append( _finding( "VID_EXPLICIT_TIMING_UNPARSEABLE", motion_id, "explicit segment timing is not a readable seconds interval", values=unparseable, ) ) continue if not windows: findings.append( _finding( "VID_EXPLICIT_TIMING_UNPARSEABLE", motion_id, "timing_plan declares explicit mode but no segment carries an interval", ) ) continue inverted = [ f"{start}-{end}" for start, end in windows if start < 0 or end < 0 or end < start ] if inverted: findings.append( _finding( "VID_EXPLICIT_TIMING_UNPARSEABLE", motion_id, "explicit segment ends before it starts", values=inverted, ) ) continue checked += 1 ordered = sorted(windows) declared_overlap = bool( isinstance(plan, dict) and plan.get("declares_overlap") is True ) overlaps = [ f"{ordered[position - 1][0]}-{ordered[position - 1][1]} / {start}-{end}" for position, (start, end) in enumerate(ordered) if position and start < ordered[position - 1][1] - TOLERANCE_SECONDS ] if overlaps and not declared_overlap: findings.append( _finding( "VID_EXPLICIT_TIMING_UNDECLARED_OVERLAP", motion_id, "segments overlap without an explicit overlap declaration", values=overlaps, ) ) continue # The two failure directions are measured from different quantities on # purpose. Comparing one total against the duration hides the plan that # commits both at once: 0.0-2.0 plus 3.0-5.0 covers exactly 4.0s of a # 4.0s shot, so the totals match — while a segment runs a second past # the end (truncated) and the 2-3s window sits unallocated (filled with # unsourced motion). Overflow is therefore read off the endpoint, and # shortfall off the union clipped to the shot. covered = _union_length(ordered) # max(), not ordered[-1][1]: sorting (start, end) tuples orders by # start, so the last element is the latest-starting segment, whose end # can sit well inside an earlier segment that encloses it. A sustained # motion spanning the shot with beats inside it — exactly what # motion-recipe.md tells creators to mark `declares_overlap` — would # otherwise have its overrun read off one of the inner beats. last_end = max((end for _, end in ordered), default=0.0) inside = _union_length( [ (max(start, 0.0), min(end, duration)) for start, end in ordered if min(end, duration) > max(start, 0.0) ] ) if last_end - duration > TOLERANCE_SECONDS: findings.append( _finding( "VID_EXPLICIT_TIMING_OVERFLOW", motion_id, f"explicit timing runs to {last_end}s on an accepted {duration}s shot; " "everything past the end is truncated", accepted_duration_seconds=duration, endpoint_seconds=round(last_end, 6), overflow_seconds=round(last_end - duration, 6), ) ) unallocated = duration - inside if unallocated > TOLERANCE_SECONDS: findings.append( _finding( "VID_EXPLICIT_TIMING_SHORTFALL", motion_id, f"explicit timing leaves {round(unallocated, 6)}s of an accepted " f"{duration}s shot unallocated; the remainder will be filled with " "unsourced motion", accepted_duration_seconds=duration, allocated_seconds=round(inside, 6), unallocated_seconds=round(unallocated, 6), ) ) declared_total = ( plan.get("declared_total_or_endpoint_seconds") if isinstance(plan, dict) else None ) # The field is named `declared_total_or_endpoint_seconds`, so both # readings are legitimate and they differ whenever overlap is declared. # Matching either is a pass; insisting on one would make a correct plan # fail under the other spelling. declared_number = _finite_number(declared_total) if declared_total is not None and declared_number is None: findings.append( _finding( "VID_DECLARED_TOTAL_MISMATCH", motion_id, "timing_plan declared total must be a finite non-negative number", ) ) elif declared_number is not None and declared_number < 0: findings.append( _finding( "VID_DECLARED_TOTAL_MISMATCH", motion_id, "timing_plan declared total must be a finite non-negative number", declared_seconds=declared_number, ) ) elif declared_number is not None and not any( abs(declared_number - candidate) <= TOLERANCE_SECONDS for candidate in (covered, last_end) ): findings.append( _finding( "VID_DECLARED_TOTAL_MISMATCH", motion_id, f"timing_plan declares {declared_total}s, which is neither the " f"{round(covered, 6)}s its segments occupy nor their " f"{round(last_end, 6)}s endpoint", declared_seconds=declared_number, covered_seconds=round(covered, 6), endpoint_seconds=round(last_end, 6), ) ) return { "schema_version": SCHEMA_VERSION, "motion_specs": len(specs), "explicit_checked": checked, "relative_plans": relative, "unmeasured_specs": unmeasured, "findings": findings, "status": "pass" if not findings else "fail", } def build_parser() -> argparse.ArgumentParser: parser = argparse.ArgumentParser( description="Check explicit motion timing against accepted shot duration (VID-04)." ) parser.add_argument("motion_specs", type=Path, help="the episode motion specs JSONL") parser.add_argument("--shots", type=Path, required=True) return parser def main(argv: list[str] | None = None) -> int: args = build_parser().parse_args(argv) try: result = check(_load_jsonl(args.motion_specs), _load_jsonl(args.shots)) except CheckError as error: print(f"{type(error).__name__}: {error}", file=sys.stderr) return 2 print(json.dumps(result, ensure_ascii=True, sort_keys=True, allow_nan=False)) return 0 if result["status"] == "pass" else 1 if __name__ == "__main__": raise SystemExit(main()) -
music_spec_check.py 12.2 KB
#!/usr/bin/env python3 """Validate provider-neutral, timeline-level short-drama music specs.""" from __future__ import annotations import argparse import json import math import re import sys from pathlib import Path from typing import Any, NamedTuple MINIMUM_PYTHON = (3, 9) if sys.version_info < MINIMUM_PYTHON: raise SystemExit("music_spec_check.py requires Python 3.9 or newer") # --------------------------------------------------------------------------- # REFERENCE RESOLVER # # Each skill carries its own copy of this block. A skill must stay runnable # after copying only its own directory, so the suite has no shared library and # this file imports nothing from outside its own skill. # --------------------------------------------------------------------------- SOURCES_RECORD_TYPE = "sources" SOURCES_SCHEMA_VERSION = "1.0.0" class ResolvedRef(NamedTuple): """An upstream reference with its snapshot resolved, whichever form it used.""" owner: str artifact: str record_id: str | None field: str | None authority: str | None class RefFinding(NamedTuple): """A structural defect in a reference object.""" code: str location: str detail: str def load_sources(document: Any) -> dict[str, dict[str, Any]]: """Return the ``sources`` declaration of a parsed file, or ``{}`` if absent. Accepts a parsed ``.json`` document (a dict) or the parsed record list of a ``.jsonl`` file, whose declaration lives on the first record. """ if isinstance(document, list): document = document[0] if document else None if not isinstance(document, dict): return {} declared = document.get("sources") if not isinstance(declared, dict): return {} return {key: value for key, value in declared.items() if isinstance(value, dict)} def resolve_ref( ref: Any, sources: dict[str, dict[str, Any]], location: str ) -> tuple[ResolvedRef | None, RefFinding | None]: """Resolve a reference object written in either the compact or expanded form.""" if not isinstance(ref, dict): return None, RefFinding("REF_IS_NOT_AN_OBJECT", location, f"got {type(ref).__name__}") src = ref.get("src") if isinstance(src, str): entry = sources.get(src) if entry is None: return None, RefFinding( "REF_SRC_IS_NOT_DECLARED", location, f"src {src!r} has no sources entry" ) owner, artifact = entry.get("owner"), entry.get("artifact") if not (isinstance(owner, str) and isinstance(artifact, str)): return None, RefFinding( "SOURCE_ENTRY_IS_INCOMPLETE", location, f"sources[{src!r}] needs owner/artifact", ) elif all(isinstance(ref.get(key), str) for key in ("owner", "artifact")): owner, artifact = ref["owner"], ref["artifact"] else: return None, RefFinding( "REF_HAS_NO_UPSTREAM_BINDING", location, "needs src, or owner+artifact" ) optional = { key: ref[key] for key in ("record_id", "field", "authority") if isinstance(ref.get(key), str) } return ( ResolvedRef( owner, artifact, optional.get("record_id"), optional.get("field"), optional.get("authority"), ), None, ) # --------------------------------------------------------------------------- # END REFERENCE RESOLVER # --------------------------------------------------------------------------- SKILL_ROOT = Path(__file__).resolve().parents[1] HASH_RE = re.compile(r"[0-9a-f]{64}") VENDOR_FIELDS = { "provider", "model", "endpoint", "api_key", "task_id", "remote_id", "callback_url", } TOP_LEVEL_FIELDS = { "music_id", "scope", "source_refs", "narrative_function", "prompt", "mode", "lyrics", "mix_intent", "status", } SCOPE_FIELDS = {"episode_id", "start_seconds", "end_seconds"} REFERENCE_FIELDS = {"src", "owner", "artifact", "record_id", "field", "authority"} SOURCES_HEADER_FIELDS = {"record_type", "schema_version", "sources"} SOURCE_ENTRY_FIELDS = {"owner", "artifact"} MIX_FIELDS = {"entry", "exit", "duck_under_dialogue", "loop"} MUSIC_ID_RE = re.compile(r"[A-Za-z0-9][A-Za-z0-9._-]{0,79}") EPISODE_ID_RE = re.compile(r"EP(?:[0-9]{3}|[1-9][0-9]{3,})") class ValidationError(ValueError): """A music spec cannot be handed to production safely.""" def resolve_input(value: str | Path) -> Path: path = Path(value).expanduser() if path.exists() or path.is_absolute(): return path return SKILL_ROOT / path def load_jsonl(value: str | Path) -> list[dict[str, Any]]: path = resolve_input(value) records: list[dict[str, Any]] = [] for number, line in enumerate(path.read_text(encoding="utf-8").splitlines(), 1): if not line.strip(): continue try: record = json.loads(line) except json.JSONDecodeError as exc: raise ValidationError(f"{path}:{number}: invalid JSON") from exc if not isinstance(record, dict): raise ValidationError(f"{path}:{number}: each record must be an object") records.append(record) if not records: raise ValidationError(f"{path}: no music specs") return records def _text(value: object, label: str, *, maximum: int = 10_000) -> str: if not isinstance(value, str) or not value.strip() or len(value) > maximum: raise ValidationError(f"{label} must be non-empty text up to {maximum} characters") return value def _number(value: object, label: str) -> float: if isinstance(value, bool) or not isinstance(value, (int, float)): raise ValidationError(f"{label} must be a number") number = float(value) if not math.isfinite(number): raise ValidationError(f"{label} must be finite") return number def _exact_fields(value: dict[str, Any], allowed: set[str], label: str) -> None: unknown = sorted(set(value) - allowed) if unknown: raise ValidationError(f"{label} has unsupported fields: {', '.join(unknown)}") def _reject_vendor_fields(value: object, label: str) -> None: if isinstance(value, dict): leaked = sorted(str(key) for key in value if str(key).casefold() in VENDOR_FIELDS) if leaked: raise ValidationError(f"{label}: provider execution fields are forbidden: {', '.join(leaked)}") for key, child in value.items(): _reject_vendor_fields(child, f"{label}.{key}") elif isinstance(value, list): for index, child in enumerate(value): _reject_vendor_fields(child, f"{label}[{index}]") def _validate_ref(value: object, label: str, sources: dict[str, dict[str, Any]]) -> None: if not isinstance(value, dict): raise ValidationError(f"{label} must be a reference object") _exact_fields(value, REFERENCE_FIELDS, label) resolved, finding = resolve_ref(value, sources, label) if finding is not None: raise ValidationError(f"{finding.location}: {finding.code}: {finding.detail}") if resolved is None: raise ValidationError(f"{label} could not be resolved") for key, text in ( ("owner", resolved.owner), ("artifact", resolved.artifact), ): _text(text, f"{label}.{key}") if not isinstance(value.get("record_id") or value.get("field"), str): raise ValidationError(f"{label} needs record_id or field") def split_sources_header( records: list[dict[str, Any]], ) -> tuple[dict[str, dict[str, Any]], list[dict[str, Any]]]: """Split a leading ``sources`` header off the file's own records. The header declares each upstream snapshot once, so a reference only names the snapshot key and the record it points at. """ if not records or records[0].get("record_type") != SOURCES_RECORD_TYPE: return {}, list(records) header, *specs = records _exact_fields(header, SOURCES_HEADER_FIELDS, "sources header") if header.get("schema_version") != SOURCES_SCHEMA_VERSION: raise ValidationError(f"sources header schema_version must be {SOURCES_SCHEMA_VERSION}") declared = header.get("sources") if not isinstance(declared, dict) or not declared: raise ValidationError("sources header must declare at least one source") for key, entry in declared.items(): label = f"sources[{key!r}]" if not isinstance(entry, dict): raise ValidationError(f"{label} must be an object") _exact_fields(entry, SOURCE_ENTRY_FIELDS, label) for field in sorted(SOURCE_ENTRY_FIELDS): _text(entry.get(field), f"{label}.{field}") return load_sources(header), specs def validate_records(records: list[dict[str, Any]]) -> dict[str, Any]: sources, specs = split_sources_header(records) if not specs: raise ValidationError("no music specs") identifiers: set[str] = set() for index, record in enumerate(specs, 1): label = f"music[{index}]" _reject_vendor_fields(record, label) _exact_fields(record, TOP_LEVEL_FIELDS, label) music_id = _text(record.get("music_id"), f"{label}.music_id", maximum=80) if MUSIC_ID_RE.fullmatch(music_id) is None: raise ValidationError(f"{label}.music_id must be a portable identifier") if music_id in identifiers: raise ValidationError(f"duplicate music_id: {music_id}") identifiers.add(music_id) scope = record.get("scope") if not isinstance(scope, dict): raise ValidationError(f"{label}.scope must be an object") _exact_fields(scope, SCOPE_FIELDS, f"{label}.scope") episode_id = _text( scope.get("episode_id"), f"{label}.scope.episode_id", maximum=40 ) if EPISODE_ID_RE.fullmatch(episode_id) is None: raise ValidationError(f"{label}.scope.episode_id is invalid") start = _number(scope.get("start_seconds"), f"{label}.scope.start_seconds") end = _number(scope.get("end_seconds"), f"{label}.scope.end_seconds") if start < 0 or end <= start: raise ValidationError(f"{label}.scope must have 0 <= start < end") refs = record.get("source_refs") if not isinstance(refs, list) or not refs: raise ValidationError(f"{label}.source_refs must be a non-empty list") for ref_index, ref in enumerate(refs, 1): _validate_ref(ref, f"{label}.source_refs[{ref_index}]", sources) _text(record.get("narrative_function"), f"{label}.narrative_function") _text(record.get("prompt"), f"{label}.prompt", maximum=2_000) mode = record.get("mode") if mode not in {"instrumental", "song"}: raise ValidationError(f"{label}.mode must be instrumental or song") lyrics = record.get("lyrics") if mode == "instrumental" and lyrics not in {None, ""}: raise ValidationError(f"{label}: instrumental music must not carry lyrics") if mode == "song": _text(lyrics, f"{label}.lyrics", maximum=3_500) mix = record.get("mix_intent") if not isinstance(mix, dict): raise ValidationError(f"{label}.mix_intent must be an object") _exact_fields(mix, MIX_FIELDS, f"{label}.mix_intent") if not isinstance(mix.get("duck_under_dialogue"), bool): raise ValidationError(f"{label}.mix_intent.duck_under_dialogue must be boolean") if not isinstance(mix.get("loop"), bool): raise ValidationError(f"{label}.mix_intent.loop must be boolean") _text(mix.get("entry"), f"{label}.mix_intent.entry") _text(mix.get("exit"), f"{label}.mix_intent.exit") if record.get("status") not in {"candidate", "accepted", "revise"}: raise ValidationError(f"{label}.status must be candidate, accepted, or revise") return {"status": "valid", "music_specs": len(specs)} def main(argv: list[str] | None = None) -> int: parser = argparse.ArgumentParser(description=__doc__) parser.add_argument("specs") args = parser.parse_args(argv) try: result = validate_records(load_jsonl(args.specs)) except (OSError, UnicodeError, ValidationError) as exc: print(str(exc), file=sys.stderr) return 2 print(json.dumps(result, ensure_ascii=True, sort_keys=True)) return 0 if __name__ == "__main__": raise SystemExit(main()) -
selftest.py 2.8 KB
#!/usr/bin/env python3 """Offline self-test for standalone video and music prompt tooling.""" from __future__ import annotations import copy import sys from music_spec_check import SKILL_ROOT, ValidationError, load_jsonl, validate_records MINIMUM_PYTHON = (3, 9) if sys.version_info < MINIMUM_PYTHON: raise SystemExit("selftest.py requires Python 3.9 or newer") def require(condition: bool, message: str) -> None: if not condition: raise AssertionError(message) def fail(records: list[dict], marker: str) -> None: try: validate_records(records) except ValidationError as exc: require(marker in str(exc), f"expected {marker!r}, got {exc!s}") else: raise AssertionError(f"expected failure containing {marker!r}") def main() -> int: records = load_jsonl(SKILL_ROOT / "examples/minimal-music-specs.jsonl") require(validate_records(records)["music_specs"] == 1, "valid fixture count") header, spec = records[0], records[1] require(spec["source_refs"][0]["src"] == "screenplay", "fixture uses a source key") # A file that was written before sources declarations existed still carries # the snapshot on every reference, and still validates. expanded = copy.deepcopy(spec) expanded["source_refs"] = [ {**header["sources"]["screenplay"], "record_id": "EP001-SC001"} ] require(validate_records([expanded])["music_specs"] == 1, "inline snapshot accepted") undeclared = copy.deepcopy(spec) undeclared["source_refs"] = [{"src": "screenplai", "record_id": "EP001-SC001"}] fail([header, undeclared], "REF_SRC_IS_NOT_DECLARED") unbound = copy.deepcopy(spec) unbound["source_refs"] = [{"record_id": "EP001-SC001"}] fail([header, unbound], "REF_HAS_NO_UPSTREAM_BINDING") duplicate = [header, spec, copy.deepcopy(spec)] fail(duplicate, "duplicate music_id") leaked = [header, copy.deepcopy(spec)] leaked[1]["model"] = "example" fail(leaked, "provider execution fields") song_without_lyrics = [header, copy.deepcopy(spec)] song_without_lyrics[1]["mode"] = "song" fail(song_without_lyrics, "lyrics") invalid_scope = [header, copy.deepcopy(spec)] invalid_scope[1]["scope"]["end_seconds"] = 0 fail(invalid_scope, "0 <= start < end") unsupported = [header, copy.deepcopy(spec)] unsupported[1]["token"] = "not provider-neutral" fail(unsupported, "unsupported fields") # A source entry now names an upstream artifact and nothing else; a stray # byte digest is an unsupported field rather than a malformed hash. stale_source = [copy.deepcopy(header), spec] stale_source[0]["sources"]["screenplay"]["hash"] = "not-a-sha256" fail(stale_source, "unsupported fields") print("10 self-tests passed") return 0 if __name__ == "__main__": raise SystemExit(main())
-
-
SKILL.md 15.5 KB
--- name: short-drama-video-prompts description: 把短剧分镜和冻结关键帧写成可直接复制的视频提示词 Markdown,也可按用户要求写时间线配乐/主题曲意图。用户提到文生/图生视频动作、人物表演、运镜、口型、环境运动、镜头时长、起止状态、把分镜转成视频提示词或写配乐提示词时使用;不生成媒体、不创作歌词、不改分镜边界。 license: MIT --- # 短剧视频与时间线音乐提示词 把已确认镜头和起始帧写成有边界、可生成、可直接复制的 `剧集/<EP>/视频提示词.md`。 每镜使用 `MOTION-...` 标题并引用对应 `SHOT-...`。 ## Quick Start ```text 用 $short-drama-video-prompts 把 EP001 分镜逐镜写成可直接复制的视频提示词 ``` ## 入口与边界 有当前分镜、冻结关键帧和必要视觉事实即可检查参考准备;图片提示词不是已有图片的证明。运动提示词拥有从已确认 起点到终点的变化:表演动作、道具状态、运镜、节奏和声音提示。先按分镜的「输入参考图」选择路径: - 有可读的真实 `REF-...` 图片时是**图生视频**;原样携带分镜中的槽位、顺序、项目相对路径、 中文名称、用途、控制与不得控制范围,静态身份、造型、地点和构图可由参考帧与视觉设定共同拥有。 这一行就是本镜的素材清单:路径回答「送哪一张」,`用途` 回答「它在本镜负责什么」,`顺序` 回答 「在可复制正文里叫哪个标签」。回答不了这三问的槽位不算准备好。 - 「输入参考图:无(创作者已明确选择文生视频)」时才是**文生视频**;视频文档必须写非空「静态视觉锚点」,且把它原样纳入 可复制正文,让提示词本身交代人物、造型、地点、构图和光线中本镜必要的事实。不得只写 `preserve/keep/保持已有外观`,因为执行端没有收到可供保持的图片。 - 有 `PLAN-...` 槽位时同样是**图生视频**:图片由创作者在生成时自行挂载,本套件没有文件可送。 正文照常按图生视频写,`顺序` 就是创作者挂图的次序,`用途` 说明每一张在本镜负责什么。 这些镜头不能转 `$short-drama-produce`;要投产就先把真实文件放进项目、改写成 `REF-...`。 - 「输入参考图:无(待补参考图:……)」、已有 `REF-...` 或 `PLAN-...` 后仍带「;待补参考图:……」,或普通的「无」都表示**未准备好**,不是文生视频授权。先自动查找可用真实图片并请分镜 owner 刷新绑定;仍有缺失时,列出镜头、缺失图片及对应 `IMG-...` 或冻结关键帧,不写最终《视频提示词.md》。 创作者可读说明跟随项目语言。开始前读取目标模型档案里的版本、执行模式、提示词方言、原生时长和 参考方式;这些选择同时约束上游分镜和下游 adapter,不能只在正文阶段临时猜。创作者在本次请求里点名 目标模型(例如“按 MiniMax H3 写”)而项目档案还没有这一项时,先转 `$short-drama` 把这个选择写进 `short-drama.json` 的 `production_profile`,再回到本阶段;一句会话里的点名如果没有落到档案上, 下一轮就会退回通用路径,方言、原生时长和参考方式又要重猜。没有项目配置的独立任务把点名的模型 和已解析的方言写在本文档开头一行,供下游核对。视频可复制正文优先跟随 `short-drama.json#/creator_authority/production_profile/choices/video_prompt_language`;未声明时回退到 `short-drama.json#/format/prompt_language`。没有 `short-drama.json` 时,根据用户点名的目标视频模型 确认正文语言;目标模型也未声明时沿用用户当前语言,不固定为英文。对白、VO、OS 的口语语言仍由 剧本和声音方向决定,不能从提示词正文语言推断。 目标为 `seedance-2.0` 时按 [Seedance 2.0 方言](references/seedance-2.0.md) 写,目标为 `seedance-2.5` 时按 [Seedance 2.5 方言](references/seedance-2.5.md) 写,目标为 `minimax-h3` 时按 [MiniMax H3 方言](references/minimax-h3.md) 写。只读命中的一份。其他目标或版本不 套用相近模型的语法,继续按 [目标模型能力档案](references/target-model-profile.md) 的通用路径写。 沿用 creator-first 既有骨架:`## MOTION-... · 中文名`,字段名依次使用「分镜、时长、生成方式、 输入参考图、静态视觉锚点、起始帧、状态链、终点」,正文标题使用 `### 可复制提示词`。静态视觉锚点 跟随本镜已解析的视频提示词语言并原样进入正文;正文每个非空行都以 `>` 组成 Markdown 引用块。 重写内容不能改名、另造替代字段或改成普通段落。 ## 工作流 1. 先检查每镜「输入参考图」。用户未手工点名图片时,根据本镜冻结关键帧和可见人物、地点、关键道具的一致性需求,在用户输入、 「制作成果」和文档已指向媒体中查找真实图片。内容和用途核对后才自动绑定;不靠相似文件名猜图。 2. 已有匹配图片时,在当前请求内先让分镜 owner 原地刷新受影响镜头的 `REF-...` 槽位,再回到本阶段;只绑本镜真正需要保持的图,不按资产数量凑槽位。 同一次刷新里,如果这些镜头还缺「视觉依据」这个必写字段(旧文档常常没有),一并按已成稿的冻结关键帧回填—— 两者读的是同一格画面,分开两轮只会让绑了图的镜头仍然说不清画面里有谁。 新槽位必须逐字使用 `REF-<slot>(顺序:<n>)· <项目相对路径>《<中文名称>》(用途:<用途>;控制:<范围>;不得控制:<范围>)`;`用途` 取 `身份|造型状态|地理|构图|尺度|效果|起始帧|结束帧|风格` 之一,不得自创斜杠分隔写法。多图只用 `;` 连接,写完后先按格式回读,再复制到视频文档。 3. 仍有必要图片缺失时,一张都没有就把分镜写成「无(待补参考图:……)」;只缺一部分则保留已核对的 `REF-...`,并在末尾追加「;待补参考图:……」。一次列出缺失镜头、图片用途和对应的 `IMG-...` /冻结关键帧,然后停在视频提示词之前。 不伪造路径,不把图片提示词当成图,不静默降级为文生视频。 报告缺口时把创作者真正有的三条路一起说清楚,不要只给「生成参考图」和「改成文生视频」: 把已有图片放进项目绑成 `REF-...`;由创作者在自己的工具里出图、本轮先写成 `PLAN-...` 挂图计划; 或者明确改走文生视频。缺口清单本身就是那份挂图计划的草稿,逐镜写清要哪几张、每张管什么。 4. 只有创作者明确选择不用参考图时,才把分镜记为「无(创作者已明确选择文生视频)」。准备闭合后原样抄入分镜的「输入参考图」,据此标明 「生成方式:图生视频」或「生成方式:文生视频」。该字段只写这两个精确值,不追加模型名、方言或 full-reference 等说明;模型说明留在正文结构中。 具体走首帧、首尾帧还是多槽参考,由各槽位的 `用途` 组合读出:只有一张 `起始帧` 走首帧,`起始帧` 加 `结束帧` 走首尾帧, `起始帧` 与人物/地点/道具图同时存在或只有这些图时走多槽参考。命中的模型方言负责把这个组合翻成它自己的素材 role 与正文结构; 多槽参考与首/尾帧在同一请求内互斥的模型,起始帧也随整组走多槽参考,不拆成两套输入。 5. 核对本镜时长落在目标模型档案的原生区间内;不在区间内就回到分镜修订,不能写完提示词后留给 adapter 报错,也不能在正文里暗自改变时长。 有对白、VO 或 OS 时,同时核对分镜「声音」中的估时依据与实际可用发声窗口;缺少依据就按 [对白预算](references/performance-action-timing.md#对白预算) 补做判断。秒段加总正确不代表台词说得完。 6. 写不能动的边框:图生视频以参考帧和视觉设定锁定;文生视频把必要静态事实写入「静态视觉锚点」和可复制正文。 7. 原样读取分镜的「起点 → 唯一动作 → 终点」,确认人物、双手和持物的状态转换都发生在镜内。 8. 写“静态锚点 → 起点 → 唯一动作(触发、接触、落点)→ 次级反应 → 运镜 → 声音 → 终点”, 必要时用秒数分段;下一镜从这个终点继续,不另造镜外转换。 9. 只在真实参考帧已经说明静态外观时删除重复描述;《视觉设定.md》连续性锁的锁面不在可删之列。 10. 把限制动作空间的已确认关系写成贯穿本镜的正向可见状态,只安排关系允许的动作通道;只有分镜明确改变关系时才写变化过程。优先用可见状态说明边界,必要时保留明确的排除条件。 11. 逐镜读取文字义务。除非剧本或创作者明确要求,正文用本镜已解析的视频提示词语言直接写明任何时刻都不生成非画内字幕、caption 和对白文字叠加,不能只写抽象的“文字层为空”;这条默认与旗帜、标牌、屏幕等画内文字独立。画内 `exact_readable` 逐字写明字符、语言、承载面和位置,不能用全局 `no-text` 覆盖。 12. 声音同轨时,把允许的发声按来源、说话人、口语语言和顺序写成完整时间线;每句从原文首字到末字, 且逐字对白在单镜可复制正文中只出现一次。视觉动作段只按时间和说话人引用该声音事件,不再抄一遍 台词。事件外回到已声明的环境声或静默。Brief、剧本或声音方向已经明确禁止外语前导、语气词、 额外人声或重复时,把这些**已确认的发声边界**在完整时间线末尾合并写一次;不靠“只包含”让执行端 猜,也不自行扩充新的禁用类别。时间线总结只列本镜实际有的声音层。 无对白镜写清底声或静默及其覆盖区间;嘴部动作按剧本表演,不默认强制闭嘴。 非画内配乐按目标方言声明,有需要时明确写空。 13. 成稿时检查动作、声音和限制条件是否清楚且相容。正面状态有助于表达目标,必要的否定条件 可以保留;不按否定句数量判错。确认不跨下一镜、不要求互斥动作、不让人物或道具瞬移变形。 14. 用户要整集就完成整集,镜头组只是内部批次。 15. 同一连续时空中相邻正式生成段、且目标档案支持续接时,默认选择“从上一段生成结果续接”; 一镜一生成只决定每次装一个镜头,不等于各镜独立文生。只有创作者明确要求独立重生成,或上游 已接受时间/地点跳变时才重新起链。选择续接后,这就是该链唯一正式执行路由。第二段起同时绑定上一段 **实际视频**和从该视频取得的 **实际尾帧**:视频负责动作、节奏和声音连续,尾帧负责下一段的可见起点。具体素材 role 和续写措辞 由命中的模型方言决定,不能把通用职责直接翻译成供应商字段。未取得真实产物时,后续段在 `视频提示词.md` 标为“待续接、不可提交”,写明缺少这两项真实输入并保留下一段内容草案;不得把它 降级成独立文生视频,也不用计划尾帧、关键帧提示词或文字描述冒充接力输入。逐镜 `MOTION-...` 仍是成员边界和内容依据,不是与正式容器路由并列的另一套提交方案。 ## 提示词要求 - 从可见起点开始,以可验证终点结束。 - 可复制正文完整实现分镜的「起点 → 唯一动作 → 终点」;交接、换手和道具落位不跨镜省略。 - 主体、动作对象、方向、幅度、速度和先后关系明确。 - 表演写动作与反应,不写无法执行的抽象结果。 - 运镜与人物动作不争夺注意力;复杂运镜必须有叙事理由。 - 声音只写该镜需要的对白、画外音、环境或转场职责,不擅自补歌词。 - 非画内字幕、caption 和对白文字叠加默认不生成;正文直接声明全镜任何时刻都不生成这些内容,不用抽象工作流术语代替执行指令。只有剧本或创作者明确要求才开启。画内精确文字是独立允许集合,不因关闭字幕而消失,也不让未批准文字混入。 - 同轨对白/VO/OS 同时写逐字文本、口语语言和发声边界;不添加来源没有的声源或重复限制。 - 无对白镜同样说明底声或静默;时间线总结与实际声音层一致。H3 无非画内配乐时写 `non_diegetic_music: N/A`。 - 正文里被引号或 `<d>` 包起来的中文必须逐字来自《剧本.md》,画内标牌文字可来自《视觉设定.md》; 分镜自己写下的画内文字与声音同样算数。`creator_markdown_check.py` 会核对四字以上的引文(VID-25)。 台词不在剧本里就回到写作技能改剧本,不在提示词里改。 - 上游持续关系用贯穿状态和允许动作通道表达,避免重复限制或冲突动作。 - 本镜命中的连续性锁,锁面原样出现在可复制正文里;颜色、材质、形制这类跨镜不变事实不靠模型记忆。 - 正文可直接复制,不含占位符、流程说明、文件路径或 QA 结论。 ## 按需知识 默认只读本 SKILL、当前分镜和必要视觉事实。遇到对应问题时只打开一份: - 阶段边界与规则分级:[阶段契约](references/stage-contract.md) - 单镜运动正文的最小配方:[运动配方](references/motion-recipe.md) - 外部执行需要的明确动作语法:[生产提示词语法](references/production-prompt-grammar.md) - 多人表演、口型和精确时序:[表演与动作时序](references/performance-action-timing.md) - 运镜、声音和相邻镜头连续性:[摄影与声音连续性](references/camera-audio-continuity.md) - 动作负载、形变和生成风险:[可生成性](references/generability.md) - 目标执行端的时长、参考、声音同轨等能力:[目标模型能力档案](references/target-model-profile.md) - Seedance 2.0 的中文分镜、素材与声音语法:[Seedance 2.0 方言](references/seedance-2.0.md) - Seedance 2.5 的长叙事、时间戳和任务类型语法:[Seedance 2.5 方言](references/seedance-2.5.md) - MiniMax H3 的结构化正文与参考模式:[MiniMax H3 方言](references/minimax-h3.md) - 多镜容器或静态漫剧的交付方式:[交付形态](references/delivery-profile.md) - 完成前的边界检查:[审查与示例](references/review-and-fixtures.md) 用户明确要求时间线音乐时,在本文档增加独立章节,写使用区间、剧情功能、进入/退出、动态曲线和 禁用项;歌词必须来自用户提供或明确接受的文本。静态漫剧只需要关键帧切换与配音时可以跳过逐镜 视频运动提示词,直接进入生产预览。 ## 完成与投产 每个点名镜头都有明确起点、主变化、时序、运镜/声音边界和可验证终点,且与相邻镜头连续,即完成。 实际生成转 `$short-drama-produce`,先展示精确 job 并取得显式确认;本技能不调用外部服务。 五份创作文档齐备后,可转 `$short-drama` 对跨文档结构做一次机械核对;内容质量仍由创作者审查。 ## 安装维护 只有安装、升级或排障时运行 `python3 scripts/selftest.py`。
Comments (0)
Sign in to join the conversation.
Reviews (0)
No reviews yet.
No comments yet.