本节摘要:本节做脚本段:把一句想法变成合法的剧本 JSON。第一件事是约束先行:题材、时长、风格三类约束全部写进提示词模板,时长换算成场景数建议,风格作为基调字段下传全片;同时把「原创、不出现真实人物与品牌」写进禁忌行,这是版权侧的第一道闸。第二件事是结构化输出:模板里直接给出目标 JSON 的形状,配合供应商的结构化输出能力,把「模型自由发挥」压成「模型填表」。第三件事是策略分界:60 秒以内的片子一次生成,更长的内容先出场景纲要、人确认后再逐场展开——单次输出越长,走形的概率越大。最后实现「生成、校验、修复」循环,处理 JSON 解析失败与字段缺失两类高频故障。
剧本质量的上限在提示词里就定了。模板四类约束各有职责:题材定内容方向,时长定规模(并换算成场景数建议),风格定基调并作为字段下传(后续每段的画面、配音都要读它),禁忌行守住版权与内容边界。
# script_prompt.py —— 剧本生成的提示词模板(结构化输出约束,写法示意) PROMPT = """你是短视频编剧。按以下约束写一个原创剧本: 题材:{genre} 时长:约 {duration} 秒,建议 {scene_count} 场 风格:{style} 禁忌:不出现真实人物与真实品牌,不模仿现有作品 只输出一个 JSON 对象,结构如下(不要输出其他文字): {{ "title": "片名", "scenes": [ {{"scene_id": 1, "location": "场景地点", "action": "场内动作描述", "lines": ["台词一"]}} ] }}""" def build_prompt(genre, duration, style): scene_count = max(2, round(duration / 15)) return PROMPT.format(genre=genre, duration=duration, style=style, scene_count=scene_count)
两个细节。其一,场景数由时长换算(经验值约 15 秒一场,示意口径)写进提示词,比让模型自己估可靠得多——模型对「60 秒能讲几场戏」的直觉极不稳定。其二,JSON 形状直接嵌在模板里并要求只输出 JSON:配合供应商的结构化输出参数(各家叫法不同,以官方文档为准),非法输出的比例能从「偶尔崩」压到「几乎不崩」。
拿 0.1 节的示例片把这个模板跑一遍。输入:题材「科普」、时长 60 秒、风格「温和的科普口吻,克制的幽默」。提示词里的场景数建议按公式算出 4 场,模型交回的成品是 3 场——建议值是软约束,模型按内容密度自行收敛属于正常现象,人审时以「每场有没有独立任务」为验收标准,不死抠场数:
# script_v1_preview.json —— 彩虹片剧本首版(示意,对应 0.1 节的镜头表) { "title": "六十秒看懂:彩虹为什么是弧形", "style": "温和的科普口吻,克制的幽默", "scenes": [ {"scene_id": 1, "location": "雨后街道", "action": "雨刚停,阳光从云缝里回来,行人抬头看天", "lines": ["雨停了,阳光回来", "天边挂出一道弧"]}, {"scene_id": 2, "location": "阳光与水滴的示意空间", "action": "一束阳光射进一颗水滴,光在内部折射、分色后再反射出来", "lines": ["你看到的弧,起点是一亿五千万公里外的阳光", "阳光进水滴时折一次,出来时再折一次", "不同颜色的光,偏折角不同"]}, {"scene_id": 3, "location": "观察者视角的天空", "action": "观察者背对太阳,视线抬起约 42 度,锥面上的水滴连成一个圆,地面挡住下半截", "lines": ["和视线成约 42 度角的水滴,颜色叠得最浓", "把这些点连起来是个圆", "地面挡住了下半截,你看到的就是弧"]} ] }
对着 0.1 节的镜头表回看这份剧本:镜头表的每一行都能在某个场里找到出处,每一场再按画面任务切成带号的镜头——第 2.2 节做的就是这一刀。另注意 action 的写法全部「看得见」:折射、分色、抬起视线、连成圆,没有一个「它很神奇」式的情绪词——这正是下一部分要盯的第三件事。
剧本 schema 在 1.2 节已定义:title、target_duration、style 加 scenes 列表,每场含 scene_id、location、action、lines。生成侧要额外盯三件事。场号连续:从 1 起不间断,这是后续「改一场不改全片」的定位键。台词短句化:一句台词别超过二十个字——这不是文学趣味,是字幕排版与 TTS 停顿的工程约束(第 6、7 章后半本会回来解释)。动作具象化:action 要写得「看得见」——「他很难过」是情绪,「他把镊子放下,盯着那枚齿轮」是画面;分镜段的原料是画面,不是情绪。
| 策略 | 做法 | 适用 | 风险 |
|---|---|---|---|
| 一次生成 | 全剧本一次请求产出 | 60 秒以内、三五场戏 | 长输出后半段质量下滑、场间重复 |
| 分段生成 | 先出场景纲要(每场一句话),人确认后逐场展开 | 分钟级以上、场次多 | 多轮请求、需要拼装与场间一致性看护 |
分界线的依据是经验观察(示意口径):单次生成超过千余字,模型开始重复句式、丢场号、台词趋同。分段生成的纲要轮是廉价的:一次小请求列出全场梗概,人花一分钟扫一眼节奏——烂纲要在这一步拦下,成本几乎为零;放到分镜之后再发现,重做的是整段。
两类高频故障:JSON 解析失败(模型输出带上了废话或截断)、字段缺失(lines 空缺、scene_id 跳号)。对策是三步循环:
# script_gen.py —— 生成、校验、修复的三步循环(写法示意,client 以你的 SDK 为准) import json def generate_script(client, prompt): reply = client.chat(prompt, response_format="json") try: data = json.loads(reply) except json.JSONDecodeError: fixed = client.chat("以下内容不是合法 JSON,请修正后只输出 JSON:\n" + reply) data = json.loads(fixed) data.setdefault("schema_version", 1) known_ids = [] for scene in data.get("scenes", []): scene.setdefault("lines", []) scene["scene_id"] = scene.get("scene_id") or (known_ids[-1] + 1 if known_ids else 1) known_ids.append(scene["scene_id"]) return data
修复的原则:能机械修的机械修,机械修不了的退回模型。缺 lines 补空列表是机械修;scene_id 缺失按前值递增是机械修;情节逻辑崩了只能退回模型重写。修复循环最多两轮——两轮还不过,问题基本出在提示词而非运气,该改的是模板。产出落盘为 build/01_script/script.v1.json,进入 2.2 节的分镜转换。
三步循环里的「校验」也可以机械化为一个纯标准库函数,让修复只在真正需要时启动:
# validate_script.py —— 剧本 JSON 的机械校验(纯标准库,红线阈值为示意值) import json MAX_LINE = 20 # 台词字数红线(工程口径) MOOD_WORDS = ("难过", "开心", "神奇", "震撼") # 情绪词黑名单(示例) def validate(path): data = json.load(open(path, encoding="utf-8")) problems = [] if not data.get("title"): problems.append("缺 title") scenes = data.get("scenes", []) if not scenes: problems.append("scenes 为空") for expect, scene in enumerate(scenes, start=1): sid = scene.get("scene_id") if sid != expect: problems.append("场号不连续:期望 %d,实际 %s" % (expect, sid)) if not scene.get("action"): problems.append("场 %s 缺 action" % sid) for i, line in enumerate(scene.get("lines", []), start=1): if len(line) > MAX_LINE: problems.append("场 %s 第 %d 句超 %d 字" % (sid, i, MAX_LINE)) if any(word in line for word in MOOD_WORDS): problems.append("场 %s 第 %d 句是情绪不是画面" % (sid, i)) return problems if __name__ == "__main__": for p in validate("build/01_script/script.v1.json"): print("待修:%s" % p) print("校验完成")
校验报告分两类处置:场号、缺字段这类结构问题走机械修复,回到上面的循环;台词超长、情绪词这类内容问题退回提示词层——在模板里把约束再写狠一格(例如把「台词短句化」升格为「每句不超过二十字,超出的场视为不合格」),比逐句手工改省。校验器与修复循环的关系是上下游:校验发现问题、归类问题,修复只处理归类为「机械」的那部分。
剧本段的故障绝大多数落在五张老脸上:
| 症状 | 根因 | 处置 |
|---|---|---|
| JSON 外面裹着说明文字或代码围栏 | 未开结构化输出,或「只输出 JSON」措辞不够硬 | 开结构化输出参数;修复轮剥壳后重解析 |
| 场号跳号或重复 | 长输出后半段注意力下滑 | 机械修复按前值递增;仍乱则改分段生成 |
| 台词书面化、一句三十字 | 模板没写短句约束,或模型忽视它 | 红线写进模板,校验器兜底 |
| 出现真实品牌或真实人物 | 禁忌行缺失或被其他要求稀释 | 禁忌行固定不删,人审第一项就查它 |
| 两版剧本风格明显不同 | 风格只写在提示词里,没进产物 | style 作为字段落进 JSON 并下传(第 1.2 节) |
最后一条最隐蔽:风格若只存在于提示词里,后续每一段都无从对齐——把它变成 JSON 字段,是「约束先行」从生成时刻延伸到全片时刻的关键一步。
剧本有了,下一节把它切成镜头——分镜 schema 的七个字段与三道校验关,是整条流水线信息密度最高的一张表。