short-drama-assets
从短剧剧本拆出人物/造型、地点/视图、道具/状态和跨场连续性,写成创作者可读的视觉设定。用户说“拆角色/场景/道具”“做资产设定”“判断复用还是新变体”“更新造型/道具状态”,或拿现成剧本直接做视觉资产准备时使用;不写图片提示词,不生成媒体。
Install
npx skills add https://github.com/zenstory-ai/drama-skills/tree/main/skills/short-drama-assets
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。不要把每次出场都
变成新资产,也不要抢写剧本语义、镜头走位或生成提示词。
Quick Start
用 $short-drama-assets 从 EP001 剧本拆完人物、造型、地点、道具和本集状态
入口与边界
现成剧本可以直接拆资产。先区分:
- 身份:换一个就不再是同一个人、地或物;
- 变体:身份不变,但服装、伤势、时段、天气、开合或持有状态改变;
- 镜头瞬态:姿势、视线、左右手、站位和相机角度,由分镜拥有;
- 故事语义:知识、目标、关系和情绪由写作/开发拥有,只引用可见后果。
工作流
- 只读当前范围的剧本和已决定的视觉方向。
- 逐场找实体、可见描述、状态变化和含混指代,先不创建资产。
- 对每项决定新建、复用、增加变体或标记未决;含混指代不猜。
- 写最小可识别设定:统一使用
识别锚点、本集状态和变化原因;会持续限制后续动作的身体—物件—空间关系写进有生效范围的状态,不当成一次姿势略过。 - 检查造型/伤势、持物/所有权、道具状态、地点时段/天气/光态与跨集 outgoing。
- 给跨镜出现且观众看得出不一致的可见事实写一条连续性锁,其余保持识别锚点即可。
- 项目提示词语言不是中文时,给每个人物条目写一行
画面代称:<正文里的拼写>;正文从不点名它就写画面代称:无。地点和道具按需。 - 整集请求自动处理完整集,末端一次回报关键身份决定和未决指代。
内容要求
- 每项先写什么不变使它仍是同一资产,再写本集变化。
- 识别锚点必须可见、可生成、可比较,不用空泛质量词替代事实。
- 服装、伤势、湿污、道具开合和光态通常是变体,不是新身份。
- 不复制剧情摘要,只保留下游生成、分镜或连续性真正需要的事实。
- 文字、标识和敏感信息遵守项目政策,不擅自补造品牌或私密信息。
- 多集状态写清生效与持续范围;镜头内瞬态留给分镜。
- 已确认且持续限制动作空间的关系(身体依托、固定、负重、占位或与功能物件的接触)不是普通镜头姿势:可独立识别的物件仍建资产,关系写进状态并交给分镜逐镜投影;只有剧本明确改变时才结束。
- 连续性锁只锁不随剧情改变的可见事实;锁面用提示词语言写成最小名词短语,会变的部分留给状态。
- 「画面代称」把提示词正文里的名字对回本条目:分镜的机械核对靠它判断关键帧里点名的人是谁。条目自身的中文名称
始终算一个代称,所以中文正文通常不必写这一行;提示词正文语言不是中文时,人物条目必须给出结论——写出正文使用的
拼写,或在正文从不点名它时写
画面代称:无。同一个名字全集只用一个拼写,大小写也一致(AST-10的同一条要求跨语言同样成立); 多个写法用、分隔。名字与常用词同形(道具「手机」撞上「手机店」)时也写画面代称:无,让核对不按名字找它。
按需知识
默认只读本 SKILL、当前剧本和视觉方向。遇到对应问题时只打开一份:
- 阶段边界与规则分级:阶段契约
- 从剧本找真正的资产出现证据:出现提取
- 新身份、复用、变体或未决:身份与变体
- 人物识别与造型层次:人物与造型
- 地点地理、视图和固定锚点:地点与视图
- 功能道具、所有权、状态变化,或画面上的可读文字与标识:道具与状态
- 声音参考的身份与授权边界:声音方向
- 跨场、跨镜、跨集状态:连续性变化
- 跨镜服装/道具颜色、材质漂移:跨镜一致性锁
- 完成前做一次资产漏项检查:资产审查清单
完成
所有明确出场都已复用、新建、变体化或标为真实未决;需要保持的视觉事实与状态变化已写入唯一
视觉设定文档,即完成。图片提示词、分镜、审查和生产不自动开始。
五份创作文档齐备后,可转 $short-drama 对跨文档结构做一次机械核对;内容质量仍由创作者审查。
安装维护
只有安装、升级或排障时运行 python3 scripts/selftest.py。
Files (drama-skills)
-
agents
-
openai.yaml 282 B
interface: display_name: "短剧资产拆解" short_description: "从剧本拆解角色场景道具、身份变体、状态变化与跨场连续性" default_prompt: "使用 $short-drama-assets 从已接受剧本拆解并核对角色、场景、道具、变体和连续性。"
-
-
assets
-
character-look.example.jsonl 2.8 KB · in bundle
-
continuity.example.jsonl 3.4 KB · in bundle
-
decisions.example.jsonl 4 KB · in bundle
-
location-view.example.jsonl 1.9 KB · in bundle
-
occurrences.example.jsonl 4.4 KB · in bundle
-
prop-state.example.jsonl 1.8 KB · in bundle
-
-
examples
-
minimal
-
characters.jsonl 652 B · in bundle
-
looks.jsonl 742 B · in bundle
-
-
-
references
-
asset-review-checklist.md 4.6 KB
# 资产拆解审查清单 ## Binding gate (`AST-05`) 资产设定完成前,每个下游引用都要解析到明确的身份条目,且变体必须在引用的剧情范围内有效。 未解决的出现项继续保持未决;不得为了让提示词或分镜继续工作,猜一个看似合理的 ID。 按“机械事实 → 语义判断 → 创作者接受”顺序检查。Owner 可按 finding 修订;修订动作本身 不等于审查 verdict。需要 verdict 时另起一次审查动作,自检也可以如实记录有证据支持的结论。 ## A. 机械检查(`structural_invariant`) - [ ] 每个制作相关的剧本段都有资产处理结果,或明确说明本段没有资产变化。 - [ ] 每个结果都指向可见场景 ID,并能从当前 `剧本.md` 找到依据。 - [ ] 来源位置、剧本明示事实、资产判断与连续性变化没有混写。 - [ ] 每个出现项恰为复用、新变体、新身份或未决之一。 - [ ] 新变体写清基底、变化、原因、生效位置与结束位置;没有明确终点时标为持续。 - [ ] ID 唯一;所有已接受 binding 精确解析到 Character+Look、Location+View 或 Prop+State。 - [ ] 未决项没有伪造成已确认身份,不能流入图片提示词或分镜。 - [ ] 每条变化有前态、后态、原因/来源、生效范围和受影响的可见 ID。 - [ ] linked outgoing/incoming 状态一致,或有明确待处理 reconciliation。 - [ ] 可读文字政策完整;`exact_readable` 不与 `no_readable_text` 冲突。 机械失败指出具体文件、标题 ID、owner 和修复方向,不回显整段创作文本。 ## B. 语义审查(`reviewed_invariant`) 逐条引用剧本/设定集 证据回答: - [ ] 含混代词、匿名人、同款多件物没有被猜成某个已有 ID。 - [ ] Character 身份锚点与服装、伤势、湿污、姿势等临时状态分离。 - [ ] Location 地理/固定锚点没有因角度、时段、天气、灯光被复制成新地点。 - [ ] Prop 形制/功能身份没有因 owner、开合、破损、内容或文字状态被复制。 - [ ] 每个新 asset 有不能复用的持久证据;每个 variant 说明 base 什么保持不变。 - [ ] 提取没有凭空补脸、品牌、门窗、内容物、伤势、灯光或可读文字。 - [ ] transfer、伤势、换装、停电、天气/光态和知识变化都有足以支持的剧情原因。 - [ ] occurrence 的 production disposition 合理:提及不被误当出镜,关键操作物不被 降成普通布景。 Reviewer finding 应含文件与标题 ID、证据、影响、修订结果、owner、严重程度和状态;不能只说 “资产不够细”或“看起来 AI”。 ## C. Craft 默认(可覆盖,不单独阻断) - [ ] 身份未变时优先复用或 variant,没有追求“一场一资产”。 - [ ] 瞬时 pose/camera/framing 留给 storyboard,没有制造 Look/View/State 爆炸。 - [ ] 设定集 只保留识别、复用、prompt、镜头和连续性所需事实。 - [ ] Location 先有地理/入口/固定锚点,再有氛围;Prop 有尺度/形制/材料/功能; Character 有可观察的区分锚点。 - [ ] outgoing 精简但足以让下一场/集无脑内补全地继续。 Creator 可用制作原因覆盖,例如需要逐件审批背景陈设;记录 override,而非改成硬规则。 ## D. Taste 选项(只确认选择) - [ ] 群演按 group 还是个体管理已选择。 - [ ] 同建筑空间拆分颗粒度能支持当前制作。 - [ ] 蒙太奇/梦境/主观断裂已声明边界。 - [ ] 临时可见状态用 delta 还是升为 variant 符合本项目复用策略。 不得仅因 reviewer 偏好另一个选项而判失败。 ## E. 创作者确认 给创作者看的摘要必须列出: 1. 复用项及匹配证据; 2. 新 variant 及 base/difference/cause/validity; 3. 新 asset 及持久区分证据; 4. unresolved 的原文证据、候选和不同选择的下游影响; 5. continuity deltas 与跨集 outgoing; 6. 确认后需要同步更新的下游文件。 把创作者确认的结果直接写进 `视觉设定.md`,未确认选择保留为“未决”并说明影响。创作者确认不等于 结构检查通过、审查通过或可以投产;这些动作各自独立发生。 ## 当前范围完成判据 本次范围内结构检查通过、每个出现项已决定、没有未决引用、连续性可交接,且创作者已确认需要选择的 新增/复用/变体/变化,即完成当前资产批次。审查与交付是后续独立动作, 不作为图片提示词或 storyboard 这两个并列分支的隐式前置门槛。 -
character-and-look.md 11.5 KB
# Character / Look 设计 ## 目录 1. 分工 2. Character:最小稳定识别集 3. Look:一次相容的外观组合 4. 可选声音方向:身份锚点不等于本场 delivery 5. 制作形态与身份锚点的可表达性 6. 合成例:顾禾 7. 角色 设定集 的实用检查 ## 分工 - **Character** 回答“这个人持续是谁、怎样被再次认出”。 - **Look** 回答“这个人在一个明确时段以怎样的完整外观出现”。 把两者拆开,是为了换装、受伤、湿污和年龄变化时仍能复用人物身份,同时让图片提示 词与分镜精确选择一个相容造型。 ## 画面代称要覆盖每一种正文语言 `画面代称` 是下游覆盖检查唯一用来匹配的字符串:冻结关键帧、图片提示词、视频提示词里出现了这个词, 才算「这一镜点了这个资产的名」。匹配是逐字的,不做翻译。 问题在于三份正文的语言不一定相同:图片提示词与冻结关键帧跟 `format/prompt_language`, 视频提示词优先跟已接受的 `video_prompt_language`。而校验器只按后者的解析结果决定要不要强制 「代称不能等于名字」——于是一个 zh + en 混配的项目很容易写成**只有英文代称**, 然后中文正文里这个资产永远匹配不上。 **这种失败是静默的**:匹配不上不会报错,只会让覆盖检查对这条资产整个不生效, 看起来一路绿灯。所以: - 一个资产的 `画面代称` 要**把它在各份正文里实际会出现的写法都列上**(中文名与英文代称并列), 不是二选一; - 正文里确实不该靠名字识别时才写 `画面代称:无`,那是明确的弃权,不是省事。 ## Character:最小稳定识别集 从剧本与 creator reference 中选择足以区别此人的持久锚点: - 叙事身份与关系(用于消歧,不是外观替代品); - 稳定的面部/头部比例、体态轮廓、永久特征; - 基础发质/发际等稳定部分;具体梳法可属于 Look; - 永久性饰物仅在确实不随 Look 变化时作为 anchor; - 表演/声音的持续事实可写入 设定集,供后续提示词或 casting 参考,但不伪装成可见外观。 锚点要**有区分度且能被观察**。“漂亮年轻女性”“霸总气质”“电影感”都不足以认人。 也不要过度指定剧本没有要求的肤色、年龄、五官或身体特征;以 `creator_decision_needed` 暴露空缺。 锚点集合里至少有一条在**侧面**成立、一条在**背面**成立。很多镜头只给到侧身、背影或 远景,只在正脸可读的特征(眉眼间距、瞳色、唇形)不能是唯一识别通道;轮廓与重心、 发型剪影与发际形状、肩背线、固定配饰的外形都可以承担这两个方向。写法示例: `["方额窄下颌", "后颈发际收成尖角", "右肩比左肩低约两指、起步时先出右肩"]`——第一条正面 成立,第二条背面成立,第三条侧面与背影都成立。 同时写出全组**可比较的尺度次序**:谁高于谁、差多少量级,并给一个绝对参照点。示例: 「甲最高,乙比甲矮约一个头,丙比乙再矮半头;甲站直时头顶与门框上沿齐平」。这条次序由 Character 拥有,换鞋、加厚外套、盘发都不改写它(那些属于 Look)。剧本没有给出身高 关系时,以 `creator_decision_needed` 暴露,由创作者定,不由角色表模板的默认体型补齐。 人物关系帮助理解谁控制空间、谁观察谁、谁会保护或攻击谁,但**不自动决定美丑、肤色、 体型或面部善恶感**。主角不因“主角”字段自动变漂亮,反派也不因“反派”字段自动变丑。 需要视觉层级时,从创作者已接受的社会位置、职业使用痕迹、轮廓区别、服装职责、姿态和 调度中取证;审美理想化、夸张或反类型都是创作者选择,不能由关系标签偷偷补完。 **`reviewed_invariant` AST-04**: 临时淤青、制服、雨水、当天妆容不得成为 Character 持久锚点,否则同一人恢复后会被误判成另一身份。 ## Look:一次相容的外观组合 一个 Look 只容纳能同时存在的组合: - 服装层次与鞋履; - 发型整理、妆容、伪装; - 伤势、包扎、污渍、湿度、天气化; - 与身体绑定的可见配件;手持道具仍绑定 Prop,不吞进 Look; - cause、valid_from、valid_until 和 source refs。 写 Look 时先引用 Character identity,再只写相对 base/前一 Look 的差异。不要复制整段 脸部描述形成第二份权威。 ## 可选声音方向:身份锚点不等于本场 delivery **`AST-07 · reviewed_invariant`**:项目需要跨集保持声音时,可在 Character 记录 `voice_direction`:语言/方言范围、 **创作者授权的参考音频绑定**、用于选型与验收的判据、已接受的专名发音。紧张、淋雨、受伤、 耳语、某句重音和临时音量属于场景/台词 delivery,不要固化成声音身份;变声、年龄跨度或 伪装若跨段持续,则记录有来源和有效范围的变体。 音色的载体是参考音频,不是一段文字描述。没有参考时保持 `reference: null` 并标「待选型」, 不要用形容词冒充音色身份。参考绑定要写清这次借用它的什么、哪些内容不能跟着来 (那条参考里的情绪、录音空间、背景内容永远不进身份),以及证据到了哪一步—— 详见 [声音方向](voice-direction.md)。声音方向直接写在对应人物的 `视觉设定.md` 条目中,不另建 配音表或第二份权威。 文本引用只能保证角色、台词、声音方向与引用关系没有被改写,不能保证任何真实合成结果、 口型或音色复现。引用使用 `视觉设定.md` 内的可见 ID,不把网址、外部任务字段或服务参数写进设定; 参考音频本身留在 `输入/`,不复制进 `视觉设定.md`,也不进公开交付。 **资产阶段不生成音频,也不调用任何语音、合成或克隆服务**。 记录先落进文件、由创作者 确认;实际 TTS 交给 `$short-drama-produce`,并须对它展示的本次任务预览明确确认。 ### 什么时候值得新 Look **`craft_default` AST-03**: 满足任一条件时提议:跨多个 block/scene 持续;会重复使用; 需要独立角色参考图;改变观众识别/剧情信息;下游必须精确绑定。瞬时表情、姿势、 视线和手位由 shot/continuity 处理,通常不新建 Look。 ### 造型不能互相污染 - “工作服”和“庆典礼服”如果时空不重叠,就不能在同一 Look 中同时出现; - 包扎在 source block 后才生效,前面镜头不能提前绑定; - 外套脱下后仍是同一 Character,但应切换 Look 或记录可追踪的 layer delta; - 若雨水只造成短暂局部湿痕,可用 delta;若跨集保持并需视觉复现,再升为 Look。 ## 制作形态与身份锚点的可表达性 写最小稳定识别集之前,先读 `视觉设定.md` 的“项目视觉方向”和已明确的制作形态;尚未决定且会 实质影响识别通道时,向创作者给出选择,不从对话记忆补造。 原因很直接:**形态决定观众靠什么通道认人**,因而决定哪些锚点在这个项目里根本可被表达。 | 主要识别通道 | 值得写的锚点 | 写了也看不见的锚点 | |---|---|---| | 面部结构 | 面部比例、眉眼鼻唇相对关系、下颌与颧骨体积 | 只有大远景才成立的体量差 | | 剪影与线条 | 轮廓与肩背重心、发型剪影与发际形状、固定配饰外形、服装廓形、步态负重 | 细微的五官不对称、浅色小痣、依赖真实皮肤纹理的痕迹 | | 材质与表面 | 皮肤/毛发/布料的可比较表面状态、长期使用痕迹 | 需要柔和过渡才能读出的体积差 | ### 写实细节与身份连续性 **`craft_default`**:写实项目可以在稳定识别特征之外,补充皮肤纹理、自然不对称或使用痕迹。 远景中不易读出的细节,不宜单独承担跨镜识别。 痣、疤和牙齿形态等如果是人物既定特征,仍须保持;不能因它们难生成就允许随镜变化。 毛孔、细纹等微观纹理不必逐像素一致。是否需要这些细节由画面尺度与已接受风格决定。 **`taste_option`**:偶像化、漫画或其他风格可采用更平滑的表面,不以写实程度判错。 ### 边界:形态选通道,身份定事实 - **`reviewed_invariant`**:形态可以改变锚点的**表达通道与颗粒度**,不得改写身份事实本身。 不能因为“不好画”把五十岁写成二十五岁、把厚重体格写成修长,也不能为了轮廓好看新增 疤痕、异色瞳或不存在的配饰。 - 已确认身份在当前形态下找不到可靠表达通道时,标为“需创作者决定”:由创作者决定 换形态还是改身份,资产环节不静默调整。 - Look 层同理:形态决定服装层次、材质响应能表达到多细,不决定 Look 由哪些单品组成,也不 决定 cause、valid_from、valid_until。 - 形态同样不改写地理、持物归属、可读文字政策与故事状态;这四项分别属于 Location/View、 Prop/State、text policy 与 write/develop owner。 ### 模板槽位不得覆盖身份锚点 **`reviewed_invariant`**:模板槽位不得覆盖已确认的身份锚点;当模板默认值与当前身份冲突时, **身份优先,模板让位**。 合成例:角色甲的锚点是五十岁上下、体格厚重、肩背略沉。若角色表模板对每一条记录 机械填入“九头身黄金比例、极度修长的双腿”,角色甲的身份就在没有任何创作者决定的情况下被 改写;而且因为每个角色都带同一句,下游会把它读成风格统一,不会当成冲突。角色乙即使确实 修长,也应由自己的当前视觉设定写出,不由模板默认值代写。 排查方式:把同一批角色记录并排看,找出**逐字重复且与该角色无关**的描述句;不能追溯到某条 当前视觉设定的,删除或改写成该角色的真实锚点,并在预览里说明改动。 ## 合成例:顾禾 Character `CHAR-GUHE` 的当前锚点:窄长脸、左眉尾短缺口、自然卷黑发、肩背略前倾。 这些锚点不会因本集换装改变。 - `LOOK-GUHE-WORK`:灰绿工作夹克、深色长裤、头发低束;渡站值班期有效。 - `LOOK-GUHE-RAIN`:在工作装外加橙色旧雨衣,兜帽放下;从 SC004 出门前穿上, 到 SC006 脱下。 - SC005 右肩湿痕:若只维持几镜,作为 delta 指向 `LOOK-GUHE-RAIN`,不是 `CHAR-GUHE-WET`。 反例:“橙色雨衣女人,湿头发,脸上有伤”被直接写成 Character identity。这样雨衣 脱下、头发变干、伤口愈合时,人物就失去所有所谓“身份锚点”。 ## 角色 设定集 的实用检查 - 去掉本集服装和伤污后,这个人还能与其他角色区分吗? - 每个 Look 是否只引用一个 Character,并且内部状态能同时成立? - 造型变化是否有 source/cause/validity,而不是凭审美换装? - 相同显示名是否误合并,不同称谓是否误拆分? - 手持物是否仍由 Prop/State 拥有,未被埋进角色描述? - creator 是否接受了新增身份锚点,而不是系统替其补完? 群演可以按制作需要使用 group Character;一旦某个个体有台词、关键交接或跨镜连续, 应提议独立身份。此颗粒度是 **`taste_option`**,但选择要能满足故事追踪。 -
continuity-delta.md 7.2 KB
# 资产连续性与变化记录 ## 目录 - [边界匹配](#边界匹配-con-01) - [状态表与变化记录](#状态表与变化记录的区别) - [各环节负责什么](#各环节负责什么) - [需要追踪的状态](#需要追踪的五组状态) - [记录步骤](#写变化记录的步骤) - [状态链示例](#合成状态链) - [非线性时间](#非线性时间与有意断裂) - [修订影响](#修订影响) - [交接检查](#交接检查) ## 边界匹配 (`CON-01`) 对相连镜头,逐字段比较前一镜已接受的结束状态与下一镜已接受的开始状态。 只有上游负责人已经记录变化,或负责人明确提出的修订仍在处理中,差异才有依据; 不得靠运动提示词悄悄“补过去”。 ## 状态表与变化记录的区别 - **状态表**回答某个边界“现在是什么状态”。 - **变化记录**回答“什么发生了变化、变化前后分别是什么、原因是什么、何时生效、影响谁”。 不要在每场复制完整设定表;只记录下游会依赖的状态和变化。这样既能发现无来源的跳变, 也能在剧本修改后准确标记 `stale`,不必重做整个项目。 ## 各环节负责什么 资产技能负责身份、资产版本和场景级资产状态变化,例如造型何时切换、道具何时转手、 场景光线何时由正常变为停电。故事开发技能负责计划中的故事状态;剧本技能负责已经 写出的知情情况、信念、目标、关系与权力、情绪变化及其故事意义。资产状态表只引用 这些来源,不得在 `after` 中另写一套故事事实。 分镜技能负责每个镜头的开始和结束边界,包括姿态、位置、目光、双手、持物和可见状态。 资产可以引用这些边界,但不能另写一套相冲突的手位和走位。 **`structural_invariant` CON-04**: 每条变化都要有前态、后态、原因或来源、有效范围,以及受影响的 现有 `IMG-...`、`SHOT-...` 或 `MOTION-...`。没有变化时不能只写“更新了”。 **`structural_invariant` CON-06**:受影响列表要覆盖所有已经存在的使用方;尚未建立的工作只说明 负责 owner 和需要同步的事实,不预建占位记录。 ## 需要追踪的五组状态 1. **人物的故事状态**:知情情况、信念、目标、关系与权力、情绪。只在它们会影响后续 行动或审稿时追踪,并指向故事开发或剧本中的准确来源;它们不属于资产技能自行创造的变化。 2. **人物的可见状态**:准确造型、伤势、污湿和场景级携带物。单镜内的姿势由分镜负责。 3. **地点状态**:地点、观察视角、可用入口、时段、天气、光线和关键陈设。 4. **道具状态**:准确版本、负责人、持有人、位置、状况、内容物和可读文字。 5. **场次或集间交接**:上一场或上一集的结束状态、下一场或下一集的进入状态,以及 依赖这些状态的提示词和镜头。 **`craft_default` CON-03**: 只跟踪后续可能引用的事实。顾禾鞋带的颜色若不影响识别或 动作,不必在每场重复;她带走的唯一号牌必须进入结束状态。 ## 写变化记录的步骤 1. 在资产出现记录中发现明确变化或边界不一致。 2. 读取 `视觉设定.md` 中上一处明确状态,绝不从下游提示词反推。 3. 写清单一的 `before` 和 `after`;一个复合事件可以拆成多条同因变化。 4. 写明导致变化的场景 ID、动作/对白或视觉设定条目,让原因能在当前五文档中直接核对。 5. 声明从哪个剧本段、场次或集开始生效,到何时结束;没有明确终点时写 `open_ended`。 6. 已经存在的使用方列出受影响的 `IMG-...`、`SHOT-...` 或 `MOTION-...`;未来工作只说明需要 同步,不预建占位引用。 7. 与下一处相连的开始状态比较:一致就通过,不一致就明确提出修订。 ### “未知”不等于“恢复默认” 上一集结尾人物仍带伤,本集没有提伤势,不能自动恢复为无伤。保留最后一次确认的状态, 并建立 `unresolved` 的连续性问题:仍然带伤、发生了有意省略的治疗,还是剧本遗漏? ### 知道事实不等于知道对方也知道 身份或秘密场景至少区分:甲是否知道事实、乙是否知道事实、甲是否知道乙已经知道。 如果本集进入状态已经声明“双方互知身份”,就不能把“他确认对方认出自己”当成本集 新的揭示。要么修正进入状态,要么重新说明本场真正改变的是哪种信念或策略。 ### 有来源不等于变化合理 引用某个剧本段只能证明变化有文字来源,不能自动证明因果成立。例如“灯闪了一下” 不足以解释整栋渡站永久断电。**`reviewed_invariant` CON-02**: 审查者要结合剧本 证据,判断伤势、知情情况、道具归属、天气和光线变化是否可信;字段非空不能代替判断。 ## 合成状态链 SC004 开始:顾禾穿 `LOOK-GUHE-RAIN`;铁皮匣闭合,号牌在里面;值班室灯亮。 1. 她开匣取牌:匣从“闭合、装有号牌”变为“打开、空”;号牌从匣内转到顾禾右手。 2. 她把号牌交给魏叔:号牌从顾禾右手转到魏叔右手,原因指向交付动作。 3. 配电箱跳闸:场景由正常夜间照明变为停电,直到维修;若集末仍未维修就写 `open_ended`。 4. 暴雨从破窗打湿雨衣右肩:人物可见状态变湿;若跨集复用或需要参考图,再建立新造型版本。 结束状态至少声明:魏叔持号牌、铁皮匣空且打开、值班室断电、顾禾雨衣右肩湿。 下一集若直接写顾禾手持号牌,必须有归还记录或由创作者接受的修订,不能静默“对齐”。 ## 非线性时间与有意断裂 蒙太奇、省略、梦境和主观画面可以不遵守普通的前后相接,但要明确写出断裂类型、 进入和退出边界,以及哪些状态仍可信。这属于 **`taste_option` CON-05**,不是逃避记录的 理由。闪回不会重写当前时间线的状态表;它在自己的时间分支中使用相应的历史版本。 ## 修订影响 - 修改伤势开始的剧本段:列出直接读取该段的造型、提示词、镜头或运动提示词;需要刷新时由各 owner 更新。 - 只修改道具显示名,ID 和事实不变:通常只更新显示文本,不重做无关分镜。 - 修改镜头构图:不自动修改资产文件。 - 修改截图说明或提示词文案:不得反向改写当前视觉状态。 ## 交接检查 - 每个相连镜头的结束状态与下一镜开始状态一致,或已有负责人明确提出修订。 - 转手同时说明从谁手中到谁手中,物件不会在两人手里复制。 - 伤势、造型、天气和光线不会无原因复位。 - 道具内容状态与人物知情情况分开记录。 - 变化的有效范围覆盖所有依赖镜头,而且不会提前生效。 - 受影响列表覆盖所有已经存在的提示词、镜头和运动提示词使用方;修订时重新 计算实际使用方,不只沿用旧清单。 - 结束状态足以让下一集继续,不依赖创作者脑内记忆。 -
continuity-lock.md 7.1 KB
# 跨镜一致性锁 ## 目录 - [为什么识别锚点还不够](#为什么识别锚点还不够) - [什么值得上锁](#什么值得上一把锁) - [锁面怎么写](#锁面怎么写) - [语法](#语法) - [谁负责把锁面带走](#谁负责把锁面带走) - [反例](#反例) - [校验](#校验) ## 为什么识别锚点还不够 识别锚点是写给创作者读的事实;执行端读不到它,只读得到每条提示词自己的正文。同一件毛衣第一镜 织的是蓝色、第二镜变成红色,通常不是资产写错了,而是**没有任何一条提示词把颜色带进去**:视觉 设定写了,关键帧写了一半,运动提示词以为参考帧已经交代过就把重复描述删掉了。 参考图也补不上这一层。一张角色板只能约束它自己画到的部分;正在编织、只成形一半的毛衣,被手挡住 的道具,每镜重新构图的服装细节,都落在身份参考“默认不能控制”的一侧。 一致性锁补的就是这一段:把跨镜不变的那几个字固定下来,并要求它原样出现在每条受影响的提示词里。 ## 什么值得上一把锁 三条同时成立才值得上锁: 1. 这个可见事实在本集出现于两个以上镜头,或一个镜头加一张资产板; 2. 观众能直接看出前后不一致; 3. 它不随剧情改变——会变的是状态,**状态不上锁**。 典型:服装主色与形制、道具主色与材质、发型长度、伤口位置与形状、招牌或界面的形制配色。 不典型:情绪、姿势、左右手、镜头角度、时段光线、破损进度、持有人——这些是状态或镜头瞬态, 分别归 `本集状态` 与分镜。 **`craft_default`**: 一集里的锁通常是个位数。把每条识别锚点都升级成锁,每条提示词都会被同一串 名词短语撑满,反而挤掉本镜真正要执行的动作。 ## 锁面怎么写 `锁面` 是**要被逐字带进提示词正文的那一小段字**,不是描述,也不是给人读的说明: - 锁面必须**逐字出现在它管到的每一份可复制正文里**。校验器会在三处查同一个字符串: 图片提示词正文、冻结关键帧提示词、视频提示词正文。 - 图片与冻结关键帧使用 `format/prompt_language`,视频可能使用另选的 `video_prompt_language`。 语言不同时,优先使用确实适合各份正文的共同色值、型号或专名。 当前 `镜头:` 同时约束本镜首帧与视频正文,不能靠拆成两把锁分别约束两种语言。 没有合适共同词组时,在视觉设定中保留事实并报告此限制,暂不声明这把逐字锁; 不缩小事实实际出现的镜头范围来绕过检查,也不自行改变用户选择的正文语言。 - 只保留最小可辨识名词短语,通常是「颜色 + 材质/形制 + 物体」,例如 `pale blue chunky knit wool sweater`; - 不含句号、动作、状态、数量、镜头信息和剧情; - 不写会随镜头变化的词(`in her hands`、`half-finished`、`on the sofa`); - 一个实体一把锁;同一实体的两种造型是两把锁,各写各的生效镜头。 比较时忽略大小写,换行按空格处理(可复制正文本来就渲染成一段,硬折行不影响匹配),其余字符 逐字比较。提示词可以在锁面前后接语法(`she keeps knitting the pale blue chunky knit wool sweater`),但不能改写锁面内部——少一个 `stand-collar`、多一个逗号都算漂移。 还有两处刻意不算数,因为它们看起来命中、实际并没有把事实写进画面: - **粘在词上的匹配不算**。锁面首尾是字母数字时,紧挨着的字母数字或连字符会让它落空: `chipped white enamel mug` 不被 `unchipped white enamel mug` 满足, `pale blue knit` 不被 `pale blue knitwear-print fleece` 满足。锁面要能作为一个完整的词组读出来。 - **负面提示词里的匹配不算**。写在 `no`/`not`/`without`/`avoid` 或 `不要`/`不能`/`没有`/`避免` 之后的锁面说的是「不要出现它」,正好与锁的目的相反;`..., no pale blue chunky knit wool sweater` 不能作为锁面在场的证据。锁面必须出现在描述画面内容的正文里。 「粘在词上」只对 ASCII 词生效——中文本来不分词,`织着浅蓝色粗棒针毛线` 正常命中。 ## 语法 写在 `视觉设定.md` 对应资产条目下,与识别锚点并列: ```markdown ## 道具 · 织了一半的毛衣 - 识别锚点:浅蓝色粗棒针手织毛线,竹制棒针两根,衣身只织到胸口。 - 本集状态:SC002 起衣身加长约一掌;颜色与针法不变。 - 连续性锁:LOCK-KNIT《织了一半的毛衣》(镜头:全集;图片提示词项:IMG-PROP-KNIT)· 锁面:pale blue chunky knit wool sweater ``` - 列表记号写 `-`、`*` 还是 `+`,缩进几格,都不影响识别;一条看起来像锁却写不完整的行会被 报成语法错误,不会被悄悄跳过——锁不能变成空转; - `LOCK-...`:本集唯一稳定 ID,与 `IMG-...`、`REF-...`、`SHOT-...` 互不混用; - `《中文名》`:给创作者读的名称,必须含中文; - `镜头:`:`全集`,或 `、` 分隔的 `SHOT-...` 列表;两者不能混写; - `图片提示词项:`:可选,`、` 分隔的 `IMG-...` 列表,整段省略即表示不约束图片提示词; - `锁面:`:被逐字带走的那段字,写在行尾。 ## 谁负责把锁面带走 | 文档 | 义务 | |---|---| | `图片提示词.md` | 被点名的 `IMG-...` 可复制正文含锁面,让参考图本身就是对的 | | `分镜.md` | 生效镜头的冻结关键帧提示词含锁面 | | `视频提示词.md` | 对应 `MOTION-...` 可复制正文含锁面 | **`structural_invariant` CON-07**: 锁面在生效范围内不得以“参考帧已经交代过”为由删除。图生视频 删掉重复的静态描述是常规做法,但锁面是这条常规做法写明的例外——它正是为了挡住跨镜漂移才存在。 状态变化不进锁面:毛衣越织越长写在 `本集状态`,颜色和针法写在锁面。两者同时存在时,提示词自己 写长度,锁面负责颜色和针法。 ## 反例 - `锁面:蓝色毛衣`——提示词正文是英文时永远匹配不上,且“蓝色”分不出浅蓝和藏青; - `锁面:a pale blue sweater that she has been knitting since the first scene`——含剧情和时态, 下一镜必须改写它,锁当场失效; - `锁面:high quality, detailed`——不是可辨识事实,锁住了也证明不了一致; - 只把锁的 `镜头` 范围写到「文字已经对的那几镜」——范围要按这个事实**实际出现在哪几镜**来写, 按已经写对的地方来写,等于把校验器指向没有风险的位置; - 把 `镜头:全集` 用在只出现两镜的道具上——其余镜头会被迫写入与本镜无关的名词。 ## 校验 ```bash python3 <core 技能目录>/scripts/creator_markdown_check.py 剧集/<EP> --project-root . ``` 它只机械核对语法、范围可解析,以及锁面是否真的出现在被点名的正文里。该不该上这把锁、锁面写得 准不准,仍由创作者和审查判断。 -
identity-vs-variant.md 7.7 KB
# 身份、版本与复用判断 ## 目录 - [三个问题](#三个问题) - [身份与状态](#持续身份与可变状态) - [判断步骤](#判断步骤) - [容易误判的情况](#容易误判的情况) - [合成示例](#合成判断例) - [决定记录](#决定记录要说明理由) - [制作案例](#制作案例说明) ## 三个问题 不要先问“要不要新建文件”,按顺序问: 1. **持续身份是否相同**? 去掉临时状态后,人物、空间或物体仍是不是同一个? 2. **下游是否需要区分一个可复用状态**? 变化会持续、再次出现,或影响参考图、 分镜绑定和连续性吗? 3. **若不建新版本,只写变化记录是否够用**? 一瞬的动作、姿势、表情或摄影机角度, 通常交给出现记录、连续性或镜头,不要为它们建立大量资产组合。 结果只能是 `reuse`、`new_variant`、`new_asset` 或 `unresolved`。 **`structural_invariant`**: 不能用“已处理”代替这四类,也不能让未决项带着假 ID 流入下游。 ## 持续身份与可变状态 | 类别 | 持续身份通常由什么区分 | 通常属于版本或状态 | 通常不建新版本 | |---|---|---|---| | 人物 | 同一人物、稳定面部与体态、永久特征、叙事身份 | 服装、发型整理、妆容、伤势、污湿、年龄阶段、伪装 | 瞬时表情、姿势、视线、景别 | | 地点 | 同一空间地理、分区关系、入口、固定锚点、主要材料 | 可复用观察视角、陈设阶段、时段、天气、光线状态 | 单一镜头角度、焦段、演员站位 | | 道具 | 同一物件或同一可复用型号的形制、尺度、材料、功能、唯一标记 | 持有人、持有手、位置、开闭、破损、内容物、文字、启停 | 被抬起的一瞬、画面左右位置 | 这张表属于 **`craft_default`**,不是见词建档的公式。变化是否值得成为新版本,取决于 持续时间、重复使用、故事辨识度和下游绑定需要。 ## 判断步骤 ### A. 先排除“只属于镜头的事实” “侧脸”“从背后看”“俯拍柜台”“杯子在画面右侧”都不改变资产。若事实只在一个镜头的 一瞬成立,就交给分镜边界或关键帧。 ### B. 寻找同一身份的证据 用已有 ID 的稳定识别点、剧情连续、来源与去向和唯一标记来对齐,不要只按显示名。 两个都叫“值班员”的人不一定是同一个;同一人换了称谓也不会因此变成另一个人。 ### C. 判断变化是否值得建立新版本 同时问: - 有明确原因和来源吗?例如换装、受伤、停电、拆封、转交。 - 有清楚的有效范围吗?从哪个剧本段、场次或集生效,到何时失效或仍未知。 - 下游需要准确选择吗?角色带伤跨三场时,造型版本值得建立;手指刚沾一滴水又擦掉, 一条变化记录可能就够了。 - 它能否只写差异,而不篡改基础身份?新版本只写变化,不重写成另一个身份。 在 `视觉设定.md` 中把基础身份与当前变体放在同一项目下。变体只写相对基础身份发生的变化; 跨文档需要引用视觉事实时直接使用该项目标题;只有引用《图片提示词.md》的可见提示词条目时才使用 稳定 `IMG-...` ID。`IMG-...` 不表示真实图片,不建立隐藏的来源声明。 ### D. 只有持续身份不同才建新资产 - 剧本确认是另一个人,即使穿着相同。 - 空间拥有不同地理、入口或固定锚点,即使同属一个建筑。 - 两件需要独立追踪的同款物件,或形制、功能、材料身份真正不同。 证据不足时选择 `unresolved`,不要靠常识补齐。 ## 容易误判的情况 ### 年龄、替身与伪装 - 同一人物的少年和成年阶段通常是不同造型版本。即使制作上由不同演员扮演,仍可以 保持一个人物身份,并在造型版本中写明表演或演员限制。 - 剧情中的双胞胎、复制体和冒名者是不同人物,即使视觉识别点近似。 - 易容或制服伪装是造型版本,不要把“伪装身份”写成真实人物身份。观众暂时不知道时, 可以限制显示名,但不能破坏内部事实。 ### 同址空间 “渡站大厅”和“渡站值班室”若有各自可制作的地理和入口,适合建立两个地点,再用关系 引用连接;不要把整栋建筑所有房间塞进一组观察视角。相反,同一值班室停电前后仍是 同一地点,不因光线变化新建地点。 ### 同款多件道具 两枚外观相同、分别由不同人物保管并且会被交换的号牌,需要两个道具身份;十只没有 连续性要求的白杯可以作为一类普通布景。是否逐件建立身份由故事追踪需要决定,不由 外观相似决定。 ### 损坏与改造 裂纹、血迹、装入文件通常属于道具状态。若物体被不可逆地改造成另一种功能,例如门牌 被熔成钥匙,而且之后按新物件使用,可以提出新资产,并保留由原物改造而来的关系。 这个决定需要创作者接受。 ## 合成判断例 已有 `CHAR-GUHE` 与 `LOOK-GUHE-WORK`。本集顾禾套上橙色雨衣进入渡站,第三场雨衣 右肩被淋湿。 - 橙色雨衣会跨两场并用于参考图:选择 `new_variant`,基础身份仍是顾禾,不是新角色。 - 右肩湿痕只持续半场:先写连续性变化,不必自动生成第三个造型版本。 - 若下一集仍需准确复现湿痕并绑定参考图,再由创作者接受一个湿雨衣造型版本。 若文本只写“穿橙雨衣的人”,且无法证明就是顾禾,决定必须保持 `unresolved`。 ## 决定记录要说明理由 每条决定至少展示:来源场景、选择、稳定证据、差异、原因、有效范围和下游影响。 - `new_asset` 要说明为什么不能复用现有资产。 - `new_variant` 要说明基础身份中哪些内容不变。 - `reuse` 要说明现有版本怎样覆盖本次事实。 能从剧本与既有视觉事实直接判断的内容直接写入 `视觉设定.md`;真正存在多个成立选择时,把它标为 “未决”并列出会改变的下游影响,等创作者选择后再改正文。不要伪造决定 ID、接受状态或额外决策文件。 **`reviewed_invariant` AST-04**: 审查者检查身份识别点与临时状态是否混写,不以 “资产越少越好”作为评价标准。 ## 制作案例说明 对完整制作链的定性案例阅读表明,下面这些命名和拆分做法可以减少换装、伤势和观察 视角变化带来的重复资产。它们属于可调整的常用做法,不是固定格式: - **版本后缀命名**:同一人物的状态或造型放在身份名之后,例如“苏某—火灾后无伤” 或“祁某—红色礼服”。下游一眼就能看出身份和当前版本。 - **空间按观察方向区分**:同一地点可以按方向或分区命名,例如“医院走廊(北)”和 “办公室(东)”,每个观察视角可以单独绑定参考图。 - **稳定识别字段**:性别、年龄段和人物类别可以用于制作追踪。少年到成年这样的大跨度, 通常用不同造型版本处理。 - **参考图绑定**:需要稳定识别的人物和空间逐一绑定参考图;没有个体连续性的路人按 普通布景处理,不必建立身份。 - **命名分层**:主角可用实名;功能人物可用姓氏加职务;只作背景的人物可用类型加序号。 资产名与描述中的名字必须一致,不能写甲的名字却描述乙。 - **持续多集的大状态**:被俘或重伤若会持续多集,可以直接建立新的造型参考。若状态只 在一镜内发生,就先留在镜头边界或变化记录中,不要提前扩张资产表。 -
location-and-view.md 3.9 KB
# Location / View 设计 ## 分工 - **Location** 保存空间身份:可行走的地理、分区关系、入口、固定锚点和主要材料。 - **View** 保存可复用的观看方向/生产状态:面向哪个区域、哪些锚点同时可见,以及 陈设阶段、时段、天气、光态。 Location 不是场景标题字符串,View 也不是每个相机角度。目标是让参考图、分镜和相邻 镜头共享同一空间逻辑,而不是为每镜创建“新场景”。 ## 先画脑内平面,再写氛围 读取剧本时先整理: 1. 内/外与可通行边界; 2. 区域之间的相对关系; 3. 人和道具会使用的入口/出口; 4. 不能移动的识别锚点; 5. 主要材料、尺度和功能; 6. 剧本明确的时间、天气与有因果的光源。 “潮湿、压迫、怀旧”可以辅助气氛,却不能替代“北门通走廊、窗口面向河面、配电箱 在柜台后”这样的地理事实。也不能为了画面好看新增一扇后来影响追逐路线的门。 ## Location 边界 ### 新 Location 当空间具有不同、可独立生产的地理/入口/固定锚点时新建。例如同一渡站建筑里的候船 厅和值班室各自有完整动作和明确连接,适合两个 Location,并记录 `connected_to`。 ### 同一 Location 停电前后、白天夜晚、晴雨、临时布置变化都不改变空间身份。若只因镜头转向就新建 地点,会让门窗方向和演员动线互相矛盾。 **`reviewed_invariant` AST-04**: 固定地理与临时时段/天气/灯光不得混成两个 Location identity。 ## View 边界 View 值得存在,是因为下游要反复引用一种稳定、能说明空间的方向或状态: - 从柜台侧朝北门,能同时看到门、河窗和配电箱; - 停电后的同方向,主灯灭、只剩河窗冷光; - 暴雨期外景,站牌、台阶和河岸关系保持,天气和地面状态变化。 单纯“低机位”“特写门把手”“35mm”属于 shot/keyframe,不建 View。演员站在左边也 不是地点事实。**`craft_default` AST-03**: 一组镜头能共用方向/状态并需要参考 plate 时才建 View。 ## 光、天气和陈设必须有来源 - 剧本写停电,则 View 可从 `normal_night` 变为 `blackout`,cause 指向断电 block; - 创作者选择蓝调夜景是 visual direction,可记录 creator source; - 不能因 prompt 惯性擅加霓虹、雾、逆光或移动窗户; - 临时海报、散落文件、积水若会影响故事/连续性,进入 View state 或 delta;普通可替换 装饰可留作 set dressing。 ## 合成例:旧渡站值班室 `LOC-FERRY-OFFICE`:狭长室内;北门通候船厅;东侧河窗;西侧整面旧柜台;配电箱 固定在柜台后墙。Location 不写“夜”“暴雨”“顾禾站在门口”。 - `VIEW-FERRY-OFFICE-NORTH-NIGHT`:从柜台内侧朝北门,河窗位于画面右后方;夜间 顶灯正常,柜台上只有铁皮匣。 - `VIEW-FERRY-OFFICE-NORTH-BLACKOUT`:同一地理和方向;断电后顶灯灭,河窗冷光 保留,铁皮匣位置不变。它是 View/state 变体,不是“黑暗值班室”新 Location。 如果剧本只需要一次门把手特写,无需 `VIEW-FERRY-OFFICE-DOOR-CLOSEUP`;镜头可以 引用 Location/既有 View 后自行拥有 framing。 ## 检查问题 - 不看氛围词,仍能判断入口、固定锚点和区域关系吗? - 同一空间各 View 的门窗/光向是否保持可调和? - View 是否错误拥有相机焦段、演员站位或 shot boundary? - 时段、天气、光态与陈设变化有 source/cause/validity 吗? - 相邻空间该拆 Location 还是做 View,是否依据地理和制作复用,而非名称习惯? - creator 是否明确选择空景/含角色政策?该政策由后续 image prompt 具体实现。 空间拆分颗粒度可以是 **`taste_option`**;但无论选粗或细,入口关系和连续性必须可 解释,不能用风格选择掩盖矛盾。 -
occurrence-extraction.md 5.9 KB
# 从剧本提取资产出现记录 ## 目录 - [目的](#目的保存证据不抢先下结论) - [逐段检查](#逐段检查而不是只找名词) - [事实分层](#事实分层) - [指代](#称谓代词和匿名对象) - [资产判断](#出现不等于视觉资产) - [状态](#状态摘取要点) - [完整性](#完整性检查) ## 目的:保存证据,不抢先下结论 出现记录说明“某段剧本在某处要求了什么”,它不是资产身份本身。先记录出现,再判断 它对应哪个资产,可以避免把同一人物的几套衣服拆成几个人,也能避免把两个同称谓的人 误并成一人。 **`structural_invariant` AST-01**: 每个出现判断都要指向 `剧本.md` 中准确的场景 ID,并用动作、 对白或必要短引文说明依据。行号只能帮助阅读,不能代替稳定场景来源。 ## 逐段检查,而不是只找名词 逐个检查与制作有关的剧本段,回答六个问题: 1. **谁或什么需要被看见、听见**? 人物、群体、动物、地点或可操作道具。 2. **以什么方式出现**? `on_screen`(出镜)、`voice_only`(只有声音)、 `represented`(照片、屏幕或画像中出现)、`mentioned_only`(只被提到)。 3. **此刻能直接看到什么**? 只摘录剧本已经给出的外观、状态、位置和动作后果。 4. **制作时必须实现什么**? 服装、伤污、可读文字、内容物、功能状态、关键入口等。 5. **它为什么重要**? 识别、行动、信息揭示、关系与权力、交接、气氛或普通布景。 6. **它从哪里来、要去哪里**? 进入状态、同场变化和结束状态;若有变化,只预留指向 连续性变化记录的位置,不在出现记录里另造第二份事实。 一个动作句常会带出多种资产。例如合成剧本段: > 顾禾推开旧渡站值班室的北门,把带裂口的白瓷号牌放进铁皮匣。匣盖内侧写着 > “潮位 7”。 至少记录:顾禾出镜;旧渡站值班室出现,北门可能是固定空间锚点;白瓷号牌有裂口; 铁皮匣被打开,匣盖内出现必须保留的文字。不要把“北门”“裂口”“潮位”各自建成资产。 ## 事实分层 出现记录中的内容按来源分开: | 内容 | 字段用途 | 写法 | |---|---|---| | 剧本段和场次位置 | 来源 | 场景 ID 加动作/对白标签或必要短引文;不复制整段正文 | | 剧本明示的可见事实 | 可见事实 | 简短摘义,并指回同一场景 | | 拟绑定的资产 | 资产判断 | 只保留复用、新变体、新身份或未决结论 | | 连续性变化 | 状态变化 | 指向 `视觉设定.md` 中对应身份与前后状态 | 不要把推测伪装成可见事实。可以直接推出的结果也要说明依据,例如“匣盖被推开”可以 记为 `open`;“顾禾很内疚”不能只因她低头就升级成资产事实。 决定尚未形成时就在对应条目标“未决”,列候选、缺失证据和影响。未来才会发生的连续性变化只写 “需要同步”的说明,不为尚不存在的文件建立占位记录。 ## 称谓、代词和匿名对象 **`reviewed_invariant` AST-02**: 只要两个候选都合理,就先不绑定。 - 原文写“她”,但本段内没有唯一先行人物:保留 `surface_form: 她`,把决定标为 `unresolved`,列出候选和需要确认的内容。 - “穿雨衣的人”后来被叫出姓名:可以提出合并,但必须展示前后剧本证据;不能只因服装相同就自动认定是同一人。 - “另一把钥匙”说明它不是上一把,但还不知道是否对应已有道具 ID:先建立独立出现记录,不能复用上一把。 - 同一人被职务、关系和名字交替称呼:别名只有创作者接受后才能进入人物资产。 - 群体称谓先按制作用途处理。没有个体连续性的“候车乘客”可以作为群组或普通布景; 后来接过关键道具的那个人应单独建立人物记录。 不要为了“让流程继续”做低把握的猜测。保留未决项,可以防止错误身份被下游放大成 参考图、分镜和视频提示词。 ## 出现不等于视觉资产 - `voice_only` 需要人物或声音事实,但本场不一定需要造型绑定。 - `represented` 可能需要同一人物的历史造型,也可能只是不可辨认的照片;按剧情要求处理,不默认露脸。 - `mentioned_only` 通常只保留故事追踪,不触发图片资产。若台词要求观众必须读到某份文件,还要建立道具出现记录。 - 环境中的普通桌椅可以标为 `set_dressing`;只有它们承担动作、识别或连续性时才升级为道具。 **`craft_default` AST-06**: 只有会改变识别、复用、提示词、镜头或连续性的事实才进入 资产记录。若项目有特别制作原因,例如品牌布景需要逐件批准,就写明原因后调整。 ## 状态摘取要点 ### 人物 记录本次造型线索、伤污与湿度、伪装、携带物和故事作用。不在这里发明脸型、年龄或 颜色搭配;设定表缺少的识别点交给创作者决定。 ### 地点 记录地点称谓、内外、可见分区、入口、固定锚点,以及剧本明示的时段、天气和光源。 “低机位看门”是镜头意图,不是新地点事实。 ### 道具 记录形制、材料、尺度线索、唯一标记、持有人与位置、持有手、开闭与启停、破损、 内容物和可读文字。文字保持原样并注明候选文字政策,不能私自润色剧情证据。 ## 完整性检查 - 对索引中的每个制作相关剧本段,都记录已提取内容,或写明 `no_asset_change`。 - 同一剧本段中的动作若改变状态,至少能找到一条变化候选。 - 同一对象使用不同称谓时,没有被静默重复或合并。 - 只被提到的名词不会无缘无故扩张视觉资产表。 - 每个拟议绑定在创作者接受决定前都保持提案状态。 -
prop-and-state.md 6.8 KB
# 道具与状态设计 ## 目录 - [道具与状态](#道具不是名词表) - [个体与集合](#个体同款与集合) - [文字政策](#可读文字政策) - [内容物与人物认知](#内容物与知识是两条连续性) - [关键道具的完整过程](#有明确用途的关键道具) - [示例与检查](#合成例铁皮匣) ## 道具不是名词表 只有会被识别、操作、传递、读取、揭示或保持连续性的物体,才值得成为受管道具。 普通背景物可保留为 `set_dressing` occurrence;一旦角色拿起它打开秘密、它跨场转手, 就要能以稳定身份追踪。 ## 道具:持续身份 Prop 保存不随本次动作轻易改变的识别信息: - 大小/尺度参照、基本形制和部件关系; - 主要材料、表面工艺、持久磨损; - 原本功能和操作方式; - 序列号、缺口、纹章等永久唯一标记; - 同款多件时的个体区分,或按类别和数量管理的方法。 “重要文件”“神秘盒子”“高级质感”只是剧情/氛围标签,不能保证再次生成时认出同一 件物体。反过来,也不要补写剧本没给的品牌和装饰。 ## 状态:此刻怎样 State 记录可变而需跟踪的事实: - 负责人、持有人、左手或右手,以及具体位置; - 完好/裂损、干湿/污渍、开/闭/锁、开机/关机; - 内容物及其是否已被看见; - 可读文字/图形的当前版本; - 组装、封条、消耗量或功能状态; - 变化原因与有效范围。 物件若持续承担人物的支撑、固定、负重或行动条件,状态同时记录**人物—物件关系**与有效范围, 再由分镜投影到起终边界;不能只写物件存在或只在角色板中画出。短暂坐下、靠墙或伸手接触仍是 镜头瞬态,不为此制造长期状态。 **`craft_default` AST-03**: 同一物体的转手、开合、破损、内容与文字变化优先做 记录状态变化,不重复创建道具。 ## 个体、同款与集合 - 两枚外观相同的白瓷号牌分别被交换、藏匿:创建两个道具,因为它们的流转记录必须独立; - 柜台上的一叠普通空白便签仅作背景:可以按类别和数量记录,不必逐张建立 ID; - 一把钥匙从顾禾手中转到值班员手中:仍是同一道具,只修改持有人和持有手; - “另一把钥匙”明确不是上一把,但尚不知对应哪个已有 ID:新建一次出现记录并标为 `unresolved`, 不能偷懒复用。 个体化程度是 **`taste_option`**,选择依据故事追踪与制作需求,而不是总数最少。 ## 可读文字政策 每个出现文字的 Prop 明确选一种: 1. `exact_readable`:剧情要求观众读到;保存准确字符、语言、大小写/标点及来源; 2. `graphic_only`:需要标签/印记形状,但不要求可辨文字; 3. `no_readable_text`:画面不应出现可读字; 4. `pending_creator_text`:剧本要求文字但内容未定,阻断依赖它的提示词。 文字是剧本事实时,资产环节只保存政策与来源位置,不自行润色。后续提示词不能 同时要求 `exact_readable` 和全局 `no_text`。 若剧情出现网址、号码、合同内容等敏感屏显,必须写明来源、准确屏显文字和创作者确认的处理方式; 不能把本地引用路径或内部地址带入交付。 **`craft_default`**: 可读文字或标识指向**现实中存在的品牌、商标、机构或人物**时,四个选项里 选哪一个不是资产环节的默认动作,而是创作者的决定。改编常把这类专名从原著原样带下来,此时 保留原名、只留形制而不留标识、换一个虚构名都可能是对的,取决于创作者对权利、题材真实感和 交付渠道的判断。 这里未定的是**政策本身**,不是文字内容,所以不要挪用 `pending_creator_text`(那一条针对 "剧本要求文字但内容未定")。按已有的未决决定写:`decisions.jsonl` 里一条 `decision_kind: unresolved`,把三种处理列进 `candidate_bindings` 并各写 support/conflict, `creator_question` 问创作者选哪一种,`downstream_impact` 列出会被挡住的下游, `creator_acceptance.status` 保持 `pending_choice`。`text_policy.mode` 在创作者选定前不改写, 原文来源位置照常保留;不替创作者定案,也不擅自改名——擅自改名同样是改写上游事实。 本套件不判断合规、不给禁令、不内置品牌清单;它只保证这个选择有人做、被记下来,而不是在 生成被拒之后由执行端顺手改掉。 ## 内容物与知识是两条连续性 盒中始终有号牌,是道具内容状态;顾禾在打开盒子后才知道号牌存在,是人物获得了新信息。 不要因道具早已装有物品,就让人物提前知道。相反,镜头没展示 内容物不代表它凭空消失。 ## 有明确用途的关键道具 若道具能保护人物、打开路线、储存能力或证明身份,不要只写外观。道具状态还要记录: 它从哪里取得、现在由谁拿着、是否启用、由什么触发、启用时观众能看到什么、还剩多少 可用次数或进度、本次使用造成什么后果,以及何时能再次使用。没有来源的“关键时刻突然 生效”应退回故事或资产负责人;只在提示词里加发光、震动等效果,不能补上缺失的过程。 ## 合成例:铁皮匣 `PROP-TIN-CASE`:手掌至小臂长度的扁长黑铁匣;铰链盖;右前角永久凹陷;用途为保存 渡站号牌。`“潮位 7”` 写在盖内,政策 `exact_readable`。 - `PSTATE-TIN-CLOSED-BADGE-IN`:匣闭合,白瓷号牌在内,位于柜台下层; - SC004 顾禾取出:状态从 `closed` 变为 `open`,内容从 `badge` 变为 `empty`;号牌另记 自己的持有人状态; - 匣盖内文在开启后才可见,但文字不是这时才“产生”。观众/角色知道它则另写 揭示记录和人物知情变化。 反例:开匣后创建 `PROP-OPEN-CASE`,拿出号牌后再创建 `PROP-EMPTY-CASE`。这样无法 表达同一匣子的状态演进,也容易让不同镜头各拿一个假副本。 ## 检查问题 - 去掉持有人、开合和内容物后,道具是否仍有可识别的形制、尺度和材料? - 同一物体的状态能否沿剧本来源段落逐步变化,而不是瞬移? - 同款多件是否因故事追踪需要而正确区分? - 可读文字是否精确、来源明确、与 no-text 政策不冲突? - 角色手里拿的物体是否既出现在道具状态中,也在后续分镜边界中被引用? - 不可逆功能改造若成为新道具,是否保留来源关系与创作者决定? **`reviewed_invariant` AST-04**: 审查者应指出身份与状态混写、无证据的内容或文字、 或无法解释的转手;简单关键词匹配不能替代这项判断。 -
stage-contract.md 4 KB
# 视觉设定阶段契约 本阶段只拥有 `剧集/<EP>/视觉设定.md` 中的人物/造型、地点/视图、道具/状态、声音身份与跨场连续性。 它继承剧本事实,不决定镜头瞬态、构图、动作终点或生成提示词。 每个判断在同一文档中写成复用、新身份、新变体或真实未决;来源用场景 ID,跨文档消费用可见标题和 稳定 ID。不要为身份决定建立 occurrence、decision、ledger 或接受记录。 ## 本阶段规则 ### `AST` | ID | Class | Knowledge | |---|---|---| | AST-01 | structural_invariant | Before creating or reusing an asset, point to the scene ID and visible evidence that establishes it. | | AST-02 | reviewed_invariant | Reconcile each occurrence as reuse, new identity, new variant, or unresolved—never guess an ambiguous name/pronoun. | | AST-03 | craft_default | Separate Character/Look, Location/View, and Prop/State. | | AST-04 | reviewed_invariant | Persistent identifying anchors and mutable state are not mixed. | | AST-05 | structural_invariant | Every downstream reference resolves to a visible identity item and the stated current variant in `视觉设定.md`. | | AST-06 | craft_default | Track only asset facts needed for recognition, reuse, prompt writing, or continuity. | | AST-07 | reviewed_invariant | Persistent voice identity and pronunciation refs stay separate from scene-level breath, emotion, volume, and delivery state. | | AST-08 | reviewed_invariant | A voice reference binding states what it controls and what it must not; the take's emotion, its recording space, and its background never enter identity. | | AST-09 | reviewed_invariant | A claim about what is audible in a reference requires a creator or rights-holder description, or an authorized listening observation bound to the inspected bytes; otherwise admission stays unverified. | | AST-10 | structural_invariant | One accepted pronunciation of a proper noun uses one spelling throughout `视觉设定.md`. | | AST-11 | reviewed_invariant | Characters designed together are not bound to confusable references; each names the audible trait telling it apart from its nearest neighbour, and names that character. | | AST-12 | craft_default | Selection criteria are few, audible and counter-exampled; they judge a candidate reference or a clone result, they do not stand in for one. | | AST-13 | structural_invariant | When the project's prompt language is not the language of the entry headings, every 人物 entry a shot's 视觉依据 references reaches a stated conclusion: `画面代称` carrying the exact spelling prompt bodies use, or `画面代称:无` when no body names it. One accepted name uses one spelling, case included. 地点/道具 declare one only when a body names them. | ### `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`(创作者选择,不作缺陷)。创作者已接受的事实优先于本表。 -
voice-direction.md 9.6 KB
# 声音方向 ## 目录 - [参考音频是载体](#参考音频是载体) - [参考可以是合成出来的](#参考可以是合成出来的) - [身份与表演的界线](#身份与表演的界线) - [一段参考音频可以决定什么](#一段参考音频可以决定什么) - [绑定不等于听过](#绑定不等于听过) - [选型判据](#选型判据) - [写出区分度](#写出区分度) - [专名发音](#专名发音) - [变体:伪装、年龄与跨段变化](#变体伪装年龄与跨段变化) - [voice-casting.md 的渲染规则](#voice-castingmd-的渲染规则) `voice_direction` 是 Character 记录的一部分(`AST-07`),跨集稳定。本场怎么说属于表演, 由 `$short-drama-write` 的配音本逐句决定。 ## 参考音频是载体 短剧的音色由**一段参考音频**承担:配音演员的试音、声音库里的一条、或创作者授权的克隆源。 文字不承担音色——它承担两件参考音频承担不了的事: 1. **选型与验收**:用什么判据挑一条参考、判一次克隆结果算不算这个角色; 2. **专名发音**:参考音频里没有的那些名字该怎么念。 所以 `voice_direction` 以 `reference` 为主字段。没有参考音频时,记录保持 `reference: null` 并写明「待选型」——**不要用一段文字描述冒充音色身份**。 下游拿着一段形容词去找声音,找回来的每次都不一样,而记录看起来是完整的。 ### 参考可以是合成出来的 使用声音库时,确认所选音色及其使用范围符合供应商提供的许可。 项目没有配音演员、也不打算克隆谁的时候,这条路是让 `reference` 从 `待选型` 变成实际路径的 办法——**挑一个预置音色,合成一条,绑上去**。合成由 `$short-drama-produce` 的 `tts` 模态执行。 合成出来的参考和录音来的参考在本文档里受同样的约束:它一样要写用途边界,一样要有 `admission_status`,一样不能用形容词代替。差别只有一处——**它的选型判据是拿来挑预置音色的, 不是拿来判克隆像不像的**,所以「与谁最易混」这条要在同一批预置音色里回答,而不是在角色之间 凭空回答。预置库里两个音色听起来就是同一个人时,换一个,不要靠判据文字假装区分。 音色 ID 本身写进 `reference` 那一行,和路径、用途边界一起。它是创作决定:同一个角色在两集之间 换 ID 就是换人说话,这件事必须在文档里看得见、diff 里查得出。 ## 身份与表演的界线 一句话判定:**这个特征在角色安静地坐着说一句无关紧要的话时还在吗**? 在,就是身份。不在,就是表演,留给场次与台词。 | 属于身份 | 属于表演 | |---|---| | 音区、音色质感、年龄印象 | 本场因奔跑造成的气息不稳 | | 语速与停顿习惯、句尾处理 | 某一句的重音位置 | | 口音与方言范围 | 这一场压低声音说话 | | 已接受的专名发音 | 情绪激动时的音量 | 界线画错的代价不对称:把表演写进身份,角色每一集都会被要求带着上一集的伤势说话; 把身份漏进表演,配音者每一场都要重新猜这个人是谁。 `not_voice_identity` 与身份字段必须同时写。只写身份,下游无从判断某个特征是被遗漏 还是被有意排除。 ## 一段参考音频可以决定什么 参考绑定必须自带用途边界:写清这次参考它的**什么**,以及哪些内容**不能**跟着来 (`AST-08`)。同一段录音承担多个用途时拆成多条绑定分别审查。 | `role` | 可以借用 | 默认不可借用 | |---|---|---| | `timbre` | 音色质感、共鸣、年龄与体量印象 | 这条里的情绪、语速、录音空间 | | `accent` | 口音与语域范围 | 音色、音区、说话人身份 | | `pace` | 语速基线与停顿习惯 | 音色、口音、这条的情绪 | | `age_impression` | 听感年龄 | 具体音色与口音 | 三类东西**永远写进 `must_not_control`**,因为它们在任何一条参考里都必然存在, 而且必然不属于角色身份: - **这条参考里的情绪**:试音稿在哭,不代表这个角色总是在哭; - **录音空间**:混响、房间声、话筒距离属于那次录音,不属于这个人; - **背景内容**:底噪、伴奏、另一个说话人。 一条参考同时要定音色和口音时,写两条绑定分别审查,不要把 `may_control` 加长。 ## 绑定不等于听过 文件名或可见引用只说明绑定了哪段素材,**不能证明有人真的听过这段音频**(`AST-09`)。 每条绑定写清证据到了哪一步: | `admission_status` | 什么情况 | 可以下什么结论 | |---|---|---| | `creator_described` | 创作者或参考权利人给了可核对的听感说明 | 按说明内容判断 | | `audibly_inspected` | 运行环境获授权检查该音频并完成检查 | 按检查记录判断 | | `unverified` | 只有文件名或文字描述,音频不可听或没有授权 | **不得**声称音色、口音、底噪或说话人数已经确认 | 没有证据就老实保持 `unverified` 并列出风险。负面描述擦不掉参考里已有的内容—— 参考里有底噪就是有底噪,写「不要底噪」不会让它消失;要么重录、清理、换一条, 要么退回参考权利人决策。 参考音频与授权说明属于创作者输入,放在 `输入/` 下;`视觉设定.md` 只写项目相对路径、用途、 允许控制/不得控制的内容与观察状态。**媒体本身不复制进视觉设定,也不进公开交付**。 ## 选型判据 `selection_criteria` 是三到五条可听的判据,用来**挑**一条参考、或**判**一次克隆结果 算不算这个角色(`AST-12`)。它不生成声音,也不替代参考。 从六类里挑,不要写满:音区(相对位置而非 Hz——「比同剧其他成年男性低半档」)、 质感、年龄与体量印象、节奏、口音与语域、可重复的标记性习惯(至多一条)。 每条带一个 `counter_example`,写清什么算过头。判据没有上界时,试音会一轮比一轮夸张, 克隆结果也没法判到底算不算过。 情绪词不是判据。锚点里出现「愤怒」「悲伤」「紧张」,说明写的是表演不是身份; 换成产生那个印象的可听特征。 ## 写出区分度 同一批角色对比着选(`AST-11`)。对每个角色回答:同剧里跟他最容易混的是谁, 用哪一条判据区分。 ``` CHAR-A 与 CHAR-B 最易混:两条参考同为中低音区、语速偏慢。 区分判据:A 句尾收住不拖长;B 句尾常带一段下滑的余音。 ``` 这条写不出来,说明两条参考实际上是同一个声音——观众会分不清谁在说话, 而这个问题在文本审查里看不出来,只在成片里被听见。回到创作者那里换一条参考, 不要靠堆形容词假装区分。 角色多时按易混组分批选型,而不是按出场顺序。 ### 对比选择预置音色 用同一组有代表性的台词试听候选,比较角色间是否易混,以及同一角色在不同情绪下是否稳定。 音色名称仅供检索,选择依据是实际声音。 需要测量时,F0 和谱特征可辅助定位近似音色;同一候选应有多句样本,才能估计自身变化。 不把音高间距或「组间距离大于组内极差」设为通用门槛。最终按角色需求与回听结果选择, 不为追求数字分离而改变已接受的声音方向。 ## 专名发音 已接受的专名发音只能有一种拼法(`AST-10`)。 这一栏是纯文字,而且参考音频通常帮不上忙:试音稿里多半没有这些名字。真人配音会按 自己的习惯念,语音合成会按模型的习惯念,两边都不会报错。同一个名字出现两种拼法, 配音就会在两集之间改口。 新增发音走正常的接受流程,不在渲染时就地决定。 ## 变体:伪装、年龄与跨段变化 角色装成另一个人、跨越十年、或中毒后长期失声,都**不改写基础身份**,而是按 [身份与变体](identity-vs-variant.md) 记一条变体: - `cause`:由哪条已接受的剧本事实引起; - `validity`:从哪里生效到哪里失效; - `delta`:换了哪条参考、或相对基础身份改了哪几条判据,其余继承。 短暂的一场变化不建变体,交给表演。判据和 Look 一样:跨多个场次持续、会重复使用、 需要独立参考、或下游必须精确绑定时才升为变体。 ## 写入 `视觉设定.md` 需要跨集保持声音身份时,在对应人物条目下增加“声音方向”,只保留: 1. **参考**:项目相对路径、用途、允许借用与不得借用;没有参考时写“待选型”; 2. **选型判据**:三到五条可听判据,每条带反例; 3. **对比角色**:最容易混淆的是谁,以及靠什么可听差异区分; 4. **专名发音**:唯一确认读法; 5. **变体**:跨多场持续的伪装、年龄跨度或音色变化,写原因与有效范围。 临时情绪、呼吸、重音、音量和空间混响仍由剧本/镜头/视频提示词拥有。声音方向是生产输入,不是 合成结果保证;实际 TTS 仍由 `$short-drama-produce` 在精确预览并显式确认后执行。 参考音频落地之后,它在下游有两个去处:作为视频模型的音色参考绑定(见目标模型的方言文档), 以及作为配音本的实际人声。配音使用同一已选音色逐句合成;参考音频用于选型或模型绑定,不能直接充当不同台词的成品音轨。 是否保持角色声音,仍需在生成后回听确认。
-
-
scripts
-
asset_check.py 10 KB
#!/usr/bin/env python3 """Validate the structural core of standalone character and look records.""" from __future__ import annotations import argparse import json 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("asset_check.py requires Python 3.9 or newer") SKILL_ROOT = Path(__file__).resolve().parents[1] HASH_RE = re.compile(r"[0-9a-f]{64}") ACCEPTANCE_STATUSES = {"accepted", "proposed", "pending_choice"} # --------------------------------------------------------------------------- # REFERENCE RESOLVER -- reference implementation. # # Each skill checker carries its own copy of this block. The suite has no shared # library on purpose: a skill must stay runnable after copying only its own # directory, so duplicating these few lines across skills is the correct shape. # Copy the block verbatim; do not import it. # --------------------------------------------------------------------------- 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 # --------------------------------------------------------------------------- class ValidationError(ValueError): pass class RecordFile(NamedTuple): """One JSONL file: its upstream snapshot declaration and its records.""" sources: dict[str, dict[str, Any]] records: list[dict[str, Any]] 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) -> RecordFile: path = resolve_input(value) parsed: 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 JSONL record must be an object") parsed.append(record) sources = load_sources(parsed) records = [ record for record in parsed if record.get("record_type") != SOURCES_RECORD_TYPE ] if not records: raise ValidationError(f"{path}: no records") return RecordFile(sources, records) def require_text(record: dict[str, Any], key: str, label: str) -> str: value = record.get(key) if not isinstance(value, str) or not value.strip(): raise ValidationError(f"{label}: {key} must be non-empty text") return value def require_list(record: dict[str, Any], key: str, label: str) -> list[Any]: value = record.get(key) if not isinstance(value, list) or not value: raise ValidationError(f"{label}: {key} must be a non-empty list") return value def validate_ref( value: Any, sources: dict[str, dict[str, Any]], label: str ) -> ResolvedRef: resolved, finding = resolve_ref(value, sources, label) if finding is not None: raise ValidationError(f"{label}: {finding.code}: {finding.detail}") assert resolved is not None if not resolved.record_id or not resolved.record_id.strip(): raise ValidationError(f"{label}: record_id must be non-empty text") return resolved def validate_acceptance( value: Any, sources: dict[str, dict[str, Any]], label: str ) -> None: if not isinstance(value, dict) or value.get("status") not in ACCEPTANCE_STATUSES: raise ValidationError(f"{label}: invalid creator_acceptance status") decision_ref = value.get("decision_ref") if value["status"] == "accepted" or decision_ref is not None: resolved = validate_ref(decision_ref, sources, f"{label}.decision_ref") # A decision_ref pointing at a block or an asset record binds acceptance to # something that never recorded a creator decision. if not str(resolved.record_id).startswith("CD-"): raise ValidationError( f"{label}.decision_ref: record_id must be a creator decision starting with CD-" ) def validate_records(characters: RecordFile, looks: RecordFile) -> dict[str, Any]: character_ids: set[str] = set() for index, record in enumerate(characters.records, 1): label = f"character[{index}]" character_id = require_text(record, "character_id", label) if not character_id.startswith("CHAR-"): raise ValidationError(f"{label}: character_id must start with CHAR-") if character_id in character_ids: raise ValidationError(f"{label}: duplicate character_id {character_id}") character_ids.add(character_id) require_text(record, "display_name", label) anchors = require_list(record, "identity_anchors", label) if any(not isinstance(item, str) or not item.strip() for item in anchors): raise ValidationError(f"{label}: identity_anchors must contain text") for ref_index, ref in enumerate(require_list(record, "source_refs", label), 1): validate_ref(ref, characters.sources, f"{label}.source_refs[{ref_index}]") validate_acceptance(record.get("creator_acceptance"), characters.sources, label) look_ids: set[str] = set() for index, record in enumerate(looks.records, 1): label = f"look[{index}]" look_id = require_text(record, "look_id", label) if not look_id.startswith("LOOK-"): raise ValidationError(f"{label}: look_id must start with LOOK-") if look_id in look_ids: raise ValidationError(f"{label}: duplicate look_id {look_id}") look_ids.add(look_id) character_ref = validate_ref( record.get("character_ref"), looks.sources, f"{label}.character_ref" ) if character_ref.record_id not in character_ids: raise ValidationError( f"{label}: character_ref does not resolve: {character_ref.record_id}" ) differences = record.get("differences") if not isinstance(differences, dict) or not any(differences.values()): raise ValidationError(f"{label}: differences must describe an observable change") validity = record.get("validity") if not isinstance(validity, dict) or not validity.get("from"): raise ValidationError(f"{label}: validity.from is required") validate_acceptance(record.get("creator_acceptance"), looks.sources, label) return { "status": "valid", "characters": len(characters.records), "looks": len(looks.records), # `source_declaration` is this file's own `sources` header resolving; # `look_binding` is a real cross-record lookup, and it is one only # because both files are handed to this checker. "checks": ["unique_ids", "acceptance_shape", "source_declaration", "look_binding"], } def validate_files(characters: str | Path, looks: str | Path) -> dict[str, Any]: return validate_records(load_jsonl(characters), load_jsonl(looks)) def main() -> int: parser = argparse.ArgumentParser(description=__doc__) parser.add_argument("--characters", required=True) parser.add_argument("--looks", required=True) args = parser.parse_args() try: result = validate_files(args.characters, args.looks) except (OSError, ValidationError) as exc: print(f"asset check failed: {exc}", file=sys.stderr) return 2 print(json.dumps(result, ensure_ascii=True, indent=2)) return 0 if __name__ == "__main__": raise SystemExit(main()) -
selftest.py 3.1 KB
#!/usr/bin/env python3 """Offline self-test for the standalone asset checker.""" from __future__ import annotations import copy import sys from pathlib import Path from asset_check import SKILL_ROOT, RecordFile, 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 expect_failure(characters: RecordFile, looks: RecordFile, marker: str) -> None: try: validate_records(characters, looks) except ValidationError as exc: if marker not in str(exc): raise AssertionError(f"expected {marker!r}, got {exc!s}") from exc else: raise AssertionError(f"expected validation failure containing {marker!r}") def edited(source: RecordFile, index: int) -> tuple[RecordFile, dict]: records = copy.deepcopy(source.records) return RecordFile(copy.deepcopy(source.sources), records), records[index] def main() -> int: example = Path(SKILL_ROOT, "examples/minimal") characters = load_jsonl(example / "characters.jsonl") looks = load_jsonl(example / "looks.jsonl") result = validate_records(characters, looks) require(result["characters"] == 1 and result["looks"] == 1, "valid fixture count") duplicate = copy.deepcopy(characters.records[0]) expect_failure( RecordFile(characters.sources, [*characters.records, duplicate]), looks, "duplicate character_id", ) broken_look, record = edited(looks, 0) record["character_ref"]["record_id"] = "CHAR-MISSING" expect_failure(characters, broken_look, "does not resolve") undeclared, record = edited(looks, 0) record["character_ref"]["src"] = "not-declared" expect_failure(characters, undeclared, "REF_SRC_IS_NOT_DECLARED") unbound, record = edited(looks, 0) record["character_ref"] = {"record_id": "CHAR-LIN"} expect_failure(characters, unbound, "REF_HAS_NO_UPSTREAM_BINDING") # A released project may still carry the snapshot inline on the reference. inline, record = edited(looks, 0) record["character_ref"] = { **inline.sources["characters"], "record_id": "CHAR-LIN", } inline.sources.pop("characters") require(validate_records(characters, inline)["status"] == "valid", "inline snapshot") candidate, record = edited(characters, 0) record["creator_acceptance"] = {"status": "proposed", "decision_ref": None} require(validate_records(candidate, looks)["status"] == "valid", "candidate status") invalid, record = edited(characters, 0) record["creator_acceptance"] = {"status": "approved", "decision_ref": None} expect_failure(invalid, looks, "invalid creator_acceptance status") misbound, record = edited(characters, 0) record["creator_acceptance"]["decision_ref"]["record_id"] = "SC001-A01" expect_failure(misbound, looks, "must be a creator decision starting with CD-") print("9 self-tests passed") return 0 if __name__ == "__main__": raise SystemExit(main())
-
-
SKILL.md 5.2 KB
--- name: short-drama-assets description: 从短剧剧本拆出人物/造型、地点/视图、道具/状态和跨场连续性,写成创作者可读的视觉设定。用户说“拆角色/场景/道具”“做资产设定”“判断复用还是新变体”“更新造型/道具状态”,或拿现成剧本直接做视觉资产准备时使用;不写图片提示词,不生成媒体。 license: MIT --- # 短剧资产拆解 把剧本文字变成可复用、可持续的视觉事实,统一写入 `剧集/<EP>/视觉设定.md`。不要把每次出场都 变成新资产,也不要抢写剧本语义、镜头走位或生成提示词。 ## Quick Start ```text 用 $short-drama-assets 从 EP001 剧本拆完人物、造型、地点、道具和本集状态 ``` ## 入口与边界 现成剧本可以直接拆资产。先区分: - **身份**:换一个就不再是同一个人、地或物; - **变体**:身份不变,但服装、伤势、时段、天气、开合或持有状态改变; - **镜头瞬态**:姿势、视线、左右手、站位和相机角度,由分镜拥有; - **故事语义**:知识、目标、关系和情绪由写作/开发拥有,只引用可见后果。 ## 工作流 1. 只读当前范围的剧本和已决定的视觉方向。 2. 逐场找实体、可见描述、状态变化和含混指代,先不创建资产。 3. 对每项决定新建、复用、增加变体或标记未决;含混指代不猜。 4. 写最小可识别设定:统一使用 `识别锚点`、本集状态和变化原因;会持续限制后续动作的身体—物件—空间关系写进有生效范围的状态,不当成一次姿势略过。 5. 检查造型/伤势、持物/所有权、道具状态、地点时段/天气/光态与跨集 outgoing。 6. 给跨镜出现且观众看得出不一致的可见事实写一条连续性锁,其余保持识别锚点即可。 7. 项目提示词语言不是中文时,给每个人物条目写一行 `画面代称:<正文里的拼写>`;正文从不点名它就写 `画面代称:无`。地点和道具按需。 8. 整集请求自动处理完整集,末端一次回报关键身份决定和未决指代。 ## 内容要求 - 每项先写什么不变使它仍是同一资产,再写本集变化。 - 识别锚点必须可见、可生成、可比较,不用空泛质量词替代事实。 - 服装、伤势、湿污、道具开合和光态通常是变体,不是新身份。 - 不复制剧情摘要,只保留下游生成、分镜或连续性真正需要的事实。 - 文字、标识和敏感信息遵守项目政策,不擅自补造品牌或私密信息。 - 多集状态写清生效与持续范围;镜头内瞬态留给分镜。 - 已确认且持续限制动作空间的关系(身体依托、固定、负重、占位或与功能物件的接触)不是普通镜头姿势:可独立识别的物件仍建资产,关系写进状态并交给分镜逐镜投影;只有剧本明确改变时才结束。 - 连续性锁只锁不随剧情改变的可见事实;锁面用提示词语言写成最小名词短语,会变的部分留给状态。 - 「画面代称」把提示词正文里的名字对回本条目:分镜的机械核对靠它判断关键帧里点名的人是谁。条目自身的中文名称 始终算一个代称,所以中文正文通常不必写这一行;提示词正文语言不是中文时,人物条目必须给出结论——写出正文使用的 拼写,或在正文从不点名它时写 `画面代称:无`。同一个名字全集只用一个拼写,大小写也一致(`AST-10` 的同一条要求跨语言同样成立); 多个写法用 `、` 分隔。名字与常用词同形(`道具「手机」` 撞上「手机店」)时也写 `画面代称:无`,让核对不按名字找它。 ## 按需知识 默认只读本 SKILL、当前剧本和视觉方向。遇到对应问题时只打开一份: - 阶段边界与规则分级:[阶段契约](references/stage-contract.md) - 从剧本找真正的资产出现证据:[出现提取](references/occurrence-extraction.md) - 新身份、复用、变体或未决:[身份与变体](references/identity-vs-variant.md) - 人物识别与造型层次:[人物与造型](references/character-and-look.md) - 地点地理、视图和固定锚点:[地点与视图](references/location-and-view.md) - 功能道具、所有权、状态变化,或画面上的可读文字与标识:[道具与状态](references/prop-and-state.md) - 声音参考的身份与授权边界:[声音方向](references/voice-direction.md) - 跨场、跨镜、跨集状态:[连续性变化](references/continuity-delta.md) - 跨镜服装/道具颜色、材质漂移:[跨镜一致性锁](references/continuity-lock.md) - 完成前做一次资产漏项检查:[资产审查清单](references/asset-review-checklist.md) ## 完成 所有明确出场都已复用、新建、变体化或标为真实未决;需要保持的视觉事实与状态变化已写入唯一 视觉设定文档,即完成。图片提示词、分镜、审查和生产不自动开始。 五份创作文档齐备后,可转 `$short-drama` 对跨文档结构做一次机械核对;内容质量仍由创作者审查。 ## 安装维护 只有安装、升级或排障时运行 `python3 scripts/selftest.py`。
Comments (0)
Sign in to join the conversation.
Reviews (0)
No reviews yet.
No comments yet.