1.2 JSON 中间表示


1.2 JSON 中间表示

本节摘要:本节给流水线立第二块地基:契约的载体。先回答选型问题——模块间为什么用 JSON 而不是 YAML、CSV 或二进制格式:可读可 diff、语言中立、大模型能可靠输出、标准库直读直写,四个理由在视频场景里全部命中。然后给出三个核心结构:剧本 JSON(场、动作、台词)、分镜 JSON(镜头号、时长、画面描述、台词、运镜、转场)、时间线 JSON(轨道、片段、入出点),三者构成「剧本到分镜到时间线」的推导链,每一步都是可校验、可人工修改的文件。最后讲契约的执行:字段校验代码怎么写,schema_version 字段为什么是长流水线的保险丝。本节的三个 schema 会被第 2、3、5 章直接引用。

学习目标

  • 说出 JSON 作为中间表示的四个选型理由,以及它的两处代价。
  • 默写剧本、分镜、时间线三个 schema 的核心字段与推导关系。
  • 为分镜 JSON 写一个不依赖第三方库的校验函数。
  • 解释 schema_version 的升级策略与向后兼容原则。

一、为什么是 JSON

中间表示的候选有四种,逐个过:

候选 可读可 diff 语言中立 模型可输出 标准库直读 判定
JSON 好 好 好(结构化输出的默认格式) 好 采用
YAML 好 好 一般(缩进易错、多义) 需第三方库 不用
CSV 差(长文本溢出单元格) 好 一般 需处理转义 仅表格数据用
二进制(如 protobuf) 差(diff 不可读) 好 差 需 schema 编译 不用

JSON 的代价有二:注释没有标准语法(本书用「字段即文档」的方式缓解,字段名自带含义);超长文本(整段台词)会挤掉纵向可读性(用缩进与转义换行缓解)。在「大模型是第一公民」的流水线里,这两处代价远小于收益——结构化输出 API 的默认目标格式就是 JSON,这一条几乎决定了选型。

二、剧本 JSON:故事的第一个结构化快照

剧本段把一句想法变成「场」的列表,每场有地点、动作、台词。示例使用原创短剧《修表匠与停摆的钟》:

# script_example.py —— 剧本 JSON 的结构示意(原创示例,文件名 script.v1.json) script = { "schema_version": 1, "title": "修表匠与停摆的钟", "target_duration": 60, "style": "温暖手作感,暖色调,慢节奏", "scenes": [ { "scene_id": 1, "location": "老城钟表铺,傍晚", "action": "老修表匠把一块停摆的怀表凑到台灯下,眯眼端详", "lines": ["这块表,四十年没停过。"], }, { "scene_id": 2, "location": "同一场,深夜", "action": "他拆开后盖,镊子夹起一枚细小的齿轮对着灯光看", "lines": ["不是齿轮坏了,是发条上得太满。"], }, ], } ​
字段 类型 含义与用途
schema_version 整数 结构版本,升级保险丝(见第五节)
title、style 字符串 全片标题与风格基调,下传给每一段
target_duration 数值 目标总时长(秒),分镜段的时长预算依据
scenes[].scene_id 整数 场号,改一场不改全片的定位键
scenes[].location 字符串 场景地点,素材与生成段的画面依据
scenes[].action 字符串 场内动作描述,翻译成分镜画面描述的原料
scenes[].lines 字符串列表 台词,音轨与字幕段的输入

三、分镜 JSON:全片的施工图

分镜段把每场切成镜头。它是流水线的信息枢纽:向上承接剧本,向下驱动素材、生成、剪辑三段,也是人工审片的把关点。

# storyboard_example.py —— 分镜 JSON 的结构示意(文件名 storyboard.v1.json) storyboard = { "schema_version": 1, "script_ref": "script.v1.json", "shots": [ { "shot_id": 101, "scene_id": 1, "duration": 4.0, "visual": "钟表铺内景,台灯暖光下,老人双手捧着怀表端详", "line": "这块表,四十年没停过。", "camera": "缓推,近景", "transition": "切", }, { "shot_id": 102, "scene_id": 1, "duration": 3.0, "visual": "特写:怀表表盘,秒纹丝不动", "line": "", "camera": "固定,特写", "transition": "叠化", }, ], } ​

镜头号采用「场号乘一百加序号」的编码(101、102、201……),好处一眼可见:从镜头号就能反查所属场,改一场时天然聚组。duration 之和要对照剧本的 target_duration 校验;camera 与 transition 是给生成段与剪辑段的指令,第 3.2 节与第 5 章分别展开。

四、时间线 JSON:剪辑段的输出契约

时间线把「镜头序列」翻译成「轨道与片段」,是 ffmpeg 与 moviepy 的直接输入:

# timeline_example.py —— 时间线 JSON 的结构示意(文件名 timeline.v1.json) timeline = { "schema_version": 1, "resolution": [1920, 1080], "fps": 30, "tracks": [ { "track": "video", "items": [ {"shot_id": 101, "clip": "04_clips/shot_101.mp4", "in": 0.0, "out": 4.0}, {"shot_id": 102, "clip": "04_clips/shot_102.mp4", "in": 0.0, "out": 3.0}, ], }, { "track": "subtitle", "items": [ {"start": 0.4, "end": 3.6, "text": "这块表,四十年没停过。"}, ], }, ], } ​

三个 schema 构成一条推导链,每一步都有明确的检查点:

idea.md ──▶ 剧本 JSON ──▶ 分镜 JSON ──▶ 时间线 JSON (场与台词) (镜头施工图) (轨道与片段) │ │ │ 校验:场号连续 校验:镜头号连续、 校验:片段存在、 台词非空 时长和达标 总长与轨道不越界 ​

五、契约的执行:校验与版本

契约不校验等于没有。分镜段的校验函数示例(不依赖第三方库,jsonschema 库是规模化后的升级选项):

# validate_storyboard.py —— 分镜 JSON 的手工校验(标准库实现) import json REQUIRED_KEYS = {"shot_id", "scene_id", "duration", "visual", "line", "camera", "transition"} def validate(path): with open(path, encoding="utf-8") as f: data = json.load(f) problems = [] total = 0.0 for shot in data["shots"]: missing = REQUIRED_KEYS - set(shot) if missing: problems.append("镜头 %s 缺字段:%s" % (shot.get("shot_id"), sorted(missing))) if shot.get("duration", 0) <= 0: problems.append("镜头 %s 时长非正" % shot.get("shot_id")) total += shot.get("duration", 0) target = data.get("target_duration") if target and abs(total - target) > 1.5: problems.append("镜头总时长 %.1f 秒偏离目标 %.1f 秒" % (total, target)) return problems ​

关于 schema_version:流水线活得比任何一次重构都长。字段要加、语义要改时,规则是「只加不改不删」——新增字段给默认值,旧字段保留但可标注废弃,大改则升主版本号并在读取处做迁移函数。所有段在校验失败时的正确行为是:报错并停下,而不是猜一个默认值继续跑——带病产物会在下游放大成不可定位的怪问题。

本节要点回顾

  • JSON 胜在可读可 diff、语言中立、模型可输出、标准库直读,代价是注释与超长文本。
  • 三个 schema 构成推导链:剧本管场与台词,分镜管镜头施工图,时间线管轨道与片段。
  • 镜头号用场号乘一百加序号编码,改一场天然聚组。
  • 校验用标准库即可起步,schema_version 只加不改不删,失败即停不猜默认。

契约写了,产物落在盘上还要有规矩——下一节给出目录分层、命名版本与配置驱动的可复现性方案。

作者与出处
原作者:灏天文库
整理人: 灏天文库整理
本站整理收录,版权归原作者/开源协议所有;欢迎通过原文链接访问源仓库。
发布者: 作者: 灏天文库 转发
评论区 (0)
U