写剧本这件事,我以前总觉得是纯手艺活,灵感到了才能动笔。直到有一次要在一晚上交出三条短视频脚本,才发现光靠灵感根本不靠谱。后来我把目光转向自动生成结构化剧本——不是让AI随手吐一段故事,而是让它严格按照分场、分幕、对白、冲突、钩子的固定骨架去产出内容。这套做法解决的不只是效率问题,更让剧本从一开始就具备可直接拍摄、剪辑和评审的规范格式。如果你也在做短视频口播、微短剧、广告策划、游戏剧情,或者帮客户出创意方案,想减少从“脑海里的一个点”到“能开拍的剧本”之间的空转,这篇文章值得看完。我会把这个项目从设计思路、技术选型到踩坑修复的完整过程,一次性讲透。
1. 项目起源:为什么是“结构化”而不是单纯写剧本
1.1 没有结构的AI文本只能算素材
很多人试过大模型写故事,输入一个题目,三五秒就能冒出几百字的桥段。但真拿去拍摄或做分镜时根本没法用:没有场景号、没有内外景标注、不知道人物在这个位置想达成什么目标,情绪转折也藏在长段落里。编剧行话说“一场戏必须有起承转合”,这句话落到实操里就是结构。自动生成结构化剧本的本质,是给自由散漫的文本套上一套工业级的“施工图”。先定梁、再定柱、最后填装修材料,编剧只需要在结构上做艺术加工。
举个例子,普通AI生成一段情节可能是:
她走进餐厅,看到窗边的座位被占,心里很不舒服。
这句话信息量太少。到了结构化剧本里,至少会变成:
- 场景编号:3-2
- 地点:某餐厅·靠窗座位
- 时间:傍晚
- 人物目标:女主想确认前任是否在跟踪自己
- 动作:扫视全场,停顿,攥紧包带
- 对白:无
- 冲突:目标与阻碍(座位被占)被看见
- 时长:20秒
两种文本的差别,就是“故事碎片”和“生产物料”的差别。自动生成结构化剧本的关键,不是让模型更有文采,而是让模型每次输出的内容都能被拆成可执行的字段。
1.2 谁最需要这套东西
我根据自己接到的需求统计了一下,四类场景的诉求完全不同,但对“结构”的依赖都极其明显:
| 场景类型 | 典型时长 | 对结构的要求 | 自动生成时最容易踩的坑 |
|---|---|---|---|
| 短视频口播/种草 | 15-90秒 | 开头钩子 + 痛点 + 方案 + 行动号召,顺序不能乱 | 语言书面化,不像说人话 |
| 微短剧 | 单集2-5分钟 | 冷开场 + 反转 + 悬念结尾,每集最后必须留钩子 | 场景切太碎,情绪线断裂 |
| 游戏支线任务 | 3-10分钟交互 | 对话树 + 多分支,分支要可控 | 分支越写越多,状态前后矛盾 |
| 广告TVC/宣传片 | 30-120秒 | 情绪曲线 + 卖点植入,画面描述与文案分离 | 画面和口播文案混在一起 |
这套需求我们在技术上是分开处理的。短视频和广告偏重“线性模板”,我会把它定义成顺序固定的JSON数组;游戏和互动短剧偏重“分支结构”,我则会使用节点+跳转引用的方式。模板层因此成了整个项目最重要的一环,这一节放在下一段展开说。
2. 整体方案设计:如何让大语言模型当一个听话的“码字员工”
2.1 模板层:先把剧本的骨头定义清楚
自动生成结构化剧本不是把提示词写长就行,而是要让模型明确知道“每一块输出往哪个格子填”。我的做法是设计一份剧本Schema,核心包括元信息、人物表、场景列表三层。
meta(剧本元信息)负责控制主旨:
{ "title": "剧名", "type": "short_film | short_video | game_quest | ad", "logline": "一句话故事", "total_est_seconds": 180, "target_audience": "目标受众" }character(人物表)负责全局设定一致性,生成每个场景前都会注入:
{ "character": [ { "name": "林姐", "age": 32, "job": "运营组长", "goal": "证明自己的方案可行", "speech_style": "短句,爱反问,不说专业术语", "taboo": "不会直接认错" } ] }scene(场景列表)是主体,每个场景都包含拍摄单位需要的最小信息:
| 字段 | 含义 | 为什么必须要 |
|---|---|---|
| scene_id | 场景唯一编号 | 后续剪辑和拍摄单全靠它定位 |
| location | 地点 | 场景表、分镜表直接引用 |
| time_of_day | 白天/夜晚/黄昏 | 影响灯光和情绪 |
| characters | 出场角色 | 角色预算和演员统筹 |
| objective | 本场角色的核心目标 | 防止AI写一场废戏 |
| conflict | 冲突/阻碍 | 没有冲突就没有推进 |
| action | 关键动作描写 | 镜头有画面可用 |
| dialogue | 对白列表 | 每条对白带说话人 |
| plot_hook | 本场结尾钩子 | 保证观众愿意看下一段 |
选择JSON而不是自然语言段落当输出格式,是因为JSON可以做到“字段级校验”。比如要求场景数量不少于4个、每个场景必须有conflict字段、对白总字数在200-400字之间,程序能一条条自动检查,不合格就重新生成。
2.2 生成层:提示词不是聊天,是生产参数
模型没有义务猜你脑子里想要什么,所以我把提示词拆成“身份指令 + 产出规格 + 少样本示例 + 本次任务参数”四段。这段的写法直接决定生成质量。
身份指令要少而具体:
你是一个有十年商业剧本写作经验的编剧,擅长把抽象创意转化成可直接拍摄的剧本。
产出规格要可验证:
- 只输出一个JSON对象,不要任何解释性文字
- 不使用Markdown代码块包裹
- 每个场景都必须包含conflict字段
- 所有对白必须口语化,单句不超过30字
- 单集目标时长按每秒4-5字控制
少样本示例的目的是“锁定格式感”。我通常会在系统提示词里放一个两场景的完整JSON输出,让模型模仿字段名称和嵌套层级,这样解析层不会出错。如果只靠指令,模型还是可能突然换字段名。
本次任务参数则放在用户消息里,例如:
{ "task": "生成一条60秒短视频口播脚本", "topic": "如何把卧室改造成高效办公区", "tone": "轻松接地气,不要贩卖焦虑", "cta": "引导点击评论区链接拿改造清单" }实际操作中,我还会把temperature调到0.5左右。自动生成结构化剧本的场景里,我们宁可要保守稳定的输出,也不希望模型放飞自我,输出一堆有文采但不符合格式的东西。
2.3 校验层:让程序盯住AI的质量底线
提示词写得再好,模型也会失手。我采用“校验失败就带着错误信息重试”的套路,模仿软件工程里契约设计的思维方式。主要校验以下内容:
- 是否能够解析为合法JSON
- 必填字段是否齐全,字段类型是否正确
- 场景数量是否落在预设范围
- 对白单句长度是否超限
- 角色表里出现的人是否都在场景中被使用
- 是否存在明显重复的场景开头
重试时不要把原始错误原样丢给模型,而是只提取最关键的报错信息,告诉模型“缺少xxx字段”。这样第二次生成的命中率会显著提升。极端情况下,连续重试三次仍失败,就降级输出半结构化文本,至少保住人工修复的基础。
3. 实操过程:从零搭一个可复用的剧本生成器
3.1 第一步:选定一个足够小的场景,把模板写细
做这种项目,我强烈建议你先选一个自己能手动完成的小场景,比如“60秒口播脚本”,而不是一开始就挑战两小时电影。短视频口播需要的结构很简单,我按“钩子-痛点-方案-证明-行动号召”五段式来扣:
| 部分 | 内容要求 | 建议时长 |
|---|---|---|
| hook | 0-3秒内出现反常现象或结果 | 5秒 |
| pain_point | 说出目标用户正在经历的具体麻烦 | 10秒 |
| solution | 给出方法或产品,说清“怎么做” | 25秒 |
| proof | 提供前后对比、数字或使用感受 | 15秒 |
| cta | 明确让用户做什么 | 5秒 |
这段模板我直接在提示词里用表格结构描述给模型,但它输出的时候还是需要JSON格式。五段之间的字数比例很重要,如果模型生成的hook只有一句话但pain_point占了半篇,人工就得大量删改。
3.2 第二步:写好一套可执行的提示词模板
下面是我实际在用的提示词模板。它本身不会变化太多,真正变动的只有任务参数:
系统提示词: 你是一个短视频剧本写手。你必须严格按照schema输出JSON。 schema定义: { "title": "字符串", "sections": [ { "name": "hook", "purpose": "用一句话制造停顿感", "content": "口播词", }, { "name": "pain_point", "purpose": "描述目标用户具体痛点", "content": "口播词,不超过三句话", }, ... ] } 1. 只输出JSON,不要解释; 2. 口播词要像真人说话,允许使用停顿词,比如“说实话”“你发现没有”; 3. 每句不超过25字; 4. 不要输出任何Markdown符号。提示词的细节越量化,模型越听话。“说人话”这种模糊要求没有用,改成“允许使用‘你发现没有’这种口语连接词”,效果立刻不一样。
3.3 第三步:用Python把生成、解析、校验串起来
我不是建议人人都写一遍接口,但自己留一套最小实现会很有价值。下面这个例子基于通用的大模型API封装,你只需要把call_llm替换成自己接入的模型服务:
# script_generator.py import json import re def call_llm(messages, temperature=0.5): # 这里接入你自己的模型服务 # 返回字符串 raise NotImplementedError def clean_json(raw: str) -> str: # 去掉可能包装JSON的markdown代码块 raw = raw.strip() if raw.startswith("```"): raw = re.sub(r"^```[a-zA-Z]*\n", "", raw) raw = re.sub(r"\n```$", "", raw) return raw def validate(data: dict) -> dict: required = ["title", "sections"] if not all(key in data for key in required): missing = [key for key in required if key not in data] raise ValueError(f"missing: {missing}") if len(data["sections"]) < 3: raise ValueError("sections should be >= 3") return data def generate_script(topic: str, retry: int = 3) -> dict: system = "你是短视频剧本写手,严格按schema输出JSON,不要输出解释。" user = f"主题:{topic}\n请生成60秒口播剧本。" messages = [ {"role": "system", "content": system}, {"role": "user", "content": user}, ] for attempt in range(retry): raw = call_llm(messages, temperature=0.5) try: raw = clean_json(raw) data = json.loads(raw) data = validate(data) return data except Exception as e: messages.append({"role": "assistant", "content": raw}) messages.append({ "role": "user", "content": f"生成失败:{e}\n请重新输出一个合法JSON,不要包含其他内容。" }) raise RuntimeError("连续重试三次仍未通过校验")带错误信息重试这个机制是整个项目的命脉。没有它,AI偶尔犯一次格式错误,你就得人工改半天;接上它之后,大部分错误都能在第二次修复。
3.4 第四步:角色一致性与对白优化
做微短剧和游戏剧情时,最大的难点不是格式,而是角色“前后不像一个人”。我的办法是在系统提示词里加入角色卡,并且在生成当前场景之前,把该角色此前做过的关键决定浓缩成两三条摘要喂回去。模型不知道“这个女人前两场已经发过火了”,但角色摘要会告诉它。
对白方面,初代输出经常像说明书。后来我在生成完成之后,单独加了一个“对白重写”步骤:
- 第一步:让模型生成带动作提示和情绪的对白
- 第二步:把所有旁白和动作描写暂时剥离,只保留对白文本
- 第三步:再让模型用指定角色的说话风格重写对白,要求“像人聊天的感觉,不要像新闻播报”
这样拆两步,比一次性生成要稳得多。第一次生成管信息量,第二次重写管语气。
3.5 第五步:人工修订要有固定清单
自动生成不代表直接可用,我给自己定了一套每次生成后必跑的检查清单:
- 每一场戏的主角目标是否清晰?
- 冲突是否来自“目标与阻碍”的真实对抗,而不是单纯嘴硬?
- 最后有没有留下一个让观众好奇的钩子?
- 对白有没有超过角色该有的信息量?
- 是否出现AI爱用的“意识到”“下定决心”等无效描写,改成“拿起手机又放下”这种可拍动作。
通常我只需要修改2-3处转折点,剩余内容就能直接进拍摄流程。相比从零开始写,单条脚本的时间从两小时压缩到二十分钟,修改比重大概是30%。
4. 实际踩坑与排查:这些问题几乎每次都会遇到
4.1 生成内容“假大空”,不够具体
这是最让我头疼的问题。比如让它写“文案感觉很专业”,模型会写“高效赋能”“专业闭环”,但观众听不懂。这是提示词缺少对词汇和句长的约束引起的。
我后来的解决办法是在提示词里增加“禁用词列表”:
- 禁止使用“赋能”“闭环”“助力”“极致”等营销黑话
- 禁止出现形容词堆砌,比如“非常”“十分”“超级”
- 每句话必须指出一个可被镜头拍到的动作或真实场景
试过之后,生成质量明显落地。不是模型变聪明了,而是你把它往具体方向上逼了一把。
4.2 JSON解析失败,连着报错
模型有时候会在JSON前后加markdown代码块,有时候中途截断,有时候把“JSON”这几个字也当内容输出来。我的排查顺序是:
- 先用
clean_json去首尾多余内容 - 如果
json.loads还是失败,就把原始输出里的错误信息拼到下一次请求里 - 如果连续失败,检查是否有一个字段的引号或括号没闭合,再提示模型“你上一次输出的JSON不完整,请补齐”
另一个隐藏坑是:模型可能在JSON字符串里加入换行导致对齐错误。因此我在Schema定义里尽量用简单类型,不允许嵌套过深的数组,把复杂度控制在两层以内。
4.3 长剧本超出上下文窗口
剧本越长,模型越容易忘掉前面的设定。强行把所有内容放进一个请求,轻则输出烂尾,重则直接长度溢出。我把生成过程分成“大纲-分幕-分场”三级:
- 第一级:让模型生成三幕剧整体大纲,每幕只保留目标、核心冲突和结局
- 第二级:每幕生成2-3个关键场景摘要
- 第三级:对照摘要逐场生成详细内容,并且把当前场景的“幕摘要”和“此前场景摘要”作为上下文传入
每一场生成完,我就把这一场的摘要追加到全局摘要文件里。这个方式类似“滚动大纲”,能保证单次请求的信息量始终可控。
4.4 所有角色说话都是一个味
角色同质化在自动生成里特别常见。原因是模型没有记住每个角色的“说话方式”,只会输出标准书面语。我建立了一个角色语音表,系统提示词里放着,需要谁就动态拼谁:
| 角色 | 平均句长 | 常用口头禅 | 绝对不能说的话 | 说话习惯 |
|---|---|---|---|---|
| 老板 | 6-10字 | “说重点” | 任何客套话 | 喜欢用祈使句 |
| 程序员 | 15-25字 | “理论上可以” | 确定性的肯定句 | 爱加“但是” |
| 新人助手 | 8-15字 | “我查一下” | 下结论 | 句尾常用“吧” |
加完这个表,不同角色之间终于能听着像不同的人。实际运营时,这个表要不断根据试拍结果补充。
另外整理一份速查表:
| 现象 | 原因 | 解决方案 |
|---|---|---|
| 台词书面化 | 缺少口语词约束 | 添加允许使用的连接词 |
| JSON报错 | 输出夹杂markdown或截断 | 清洗+带错误重试 |
| 场景衔接不上 | 上下文太长被遗忘 | 滚动摘要+分场生成 |
| 角色同质化 | 缺乏角色风格记忆 | 角色语音表 |
| 冲突不够强 | 只写情绪没有行动 | 要求写“行动+阻碍”字段 |
5. 继续往下走:让剧本生成接入内容生产流水线
5.1 从结构化剧本自动转成分镜脚本
当剧本以JSON保存后,分镜生成就变成了一个字段映射问题。每个场景的action字段可以转成“画面描述”,dialogue字段转成“拍摄台词”,我再用一套简单的景别映射规则决定镜头:
- 场景开始:全景,建立地点关系
- 人物对话中的情绪转折:中近景或特写
- 关键道具出现:推镜或特写
这个粒度虽然还不能直接给摄影组用,但作为拍摄前沟通底稿,已经能省掉大部分整理时间。
5.2 扩展互动叙事与游戏对话树
游戏支线剧情需要多分支结构。我用统一的对话节点来组织:
{ "node_id": "n1", "speaker": "铸剑的老人家", "dialogue": "你手里的剑,是从哪来的?", "options": [ {"text": "是家里长辈留下的", "next_node": "n3"}, {"text": "路上捡的,有问题吗", "next_node": "n4"} ] }模型每次只生成一个节点和最多三个选项,再逐个生成下一层节点,避免一次生成几十条分支导致漏设定。这样人工只需要检查关键分支的跳转逻辑,不用阅读整片森林。
5.3 接入设定库,让长篇项目不“吃书”
做长篇剧集时,角色关系、时间线、伏笔随时可能冲突。我的方案是单独维护一个“设定库”,用JSON记录人物关系、重大事件、当前线索状态。每次生成某场景前,程序先从设定库中检索与该场景相关的实体和事件,再拼进提示词里。
比如设定库里写“林姐在第三集发现方案被否的真相”,那么生成第五集涉及她情绪反应时,模型就会带着这条设定,不会突然把方案被否写成“正常调整”。这一步对短项目来说有点重,但一旦进入连续制作阶段,收益非常明显。
我自己现在的习惯是:每次生成完剧本,不会直接拿去用,而是先读一遍“场景冲突列表”,只修改那些真正影响转折的地方。自动化的意义不是替代编剧,而是把格式搭建、分场重复劳动吃进去,让人把精力留在需要创造力的关键节点。如果你也想做类似工具,我建议从小场景、小模板开始,先把结构定细一点,再慢慢扩大规模。最后一个小窍门:把每次调好的提示词和JSON Schema存成版本化模板,三个月后你会发现自己攒下的不是几段prompt,而是一套可以复用的内容生产基础设施。