AI 短剧每集只有两句对白?问题多半不在模型,在剧本的结构与计数

2026-08-06 · 约 9 分钟

2026-08-06 实录。所有代码位置、报错文本与前后数字都来自当天的真实排查,不是示意。

有个反复出现的现象:剧本明明写得挺满,导进 AI 短剧工具里,系统却说「本集有效对白约 2 句,至少需要 21 句」,直接卡住不让往下走。

第一反应通常是「门槛定得太苛刻」,或者「模型没读懂我的剧本」。我这两天把这件事从头查了一遍,两个猜测都不对。真实原因是四个互相独立的小问题叠在一起,其中三个会静默丢句——它们不报错,只是让你的对白在计数环节凭空消失。

这篇把四个原因拆开写。如果你在做类似的剧本结构化流程,或者只是想知道自己的剧本该怎么写才不被卡,都能直接用。

一、最常见的原因:你导入的不是那份带对白的文件

先说一个听起来很蠢、但发生率极高的情况。

我手上这套素材包里有两份剧本

文件 行数 内容
writer_pack_ep01-06.md 79 每集四条剧情要点,零对白,没有场景表和道具表
writer_pack_ep01-06_seedance25_4x30.md 370 六集 × 四段,每段九个字段,带完整对白,且只有这份带场景表与道具表

第一份是「大纲包」,第二份才是「可拍包」。它们文件名前缀一样,放在不同目录下,肉眼看上去都是「剧本」。

导进去的是第一份,系统当然数不出对白——那份文件里本来就没有。

自查方式:打开你要导入的文件,搜 对白 两个字。搜不到,或者只在标题里出现,那就是拿错文件了。真正的可拍稿每一段都应该有一行 - 对白:

这条听起来不像技术问题,但它是发生率最高的一条,值得先排除。

二、说话人后面加了括号批注,那句话就消失了

这条最阴险,因为它只吃掉一部分句子,让你以为是自己写得不够多。

短剧对白经常要标语气:

阿咎(气声):「爹……?」
谢明彰(对库吏):「下一批。」
库吏:「四十七箱。」
远处车轴:「……」

这一段有四句台词。但系统只数出两句。

原因在抓取说话人的正则里。它原本长这样:

/([\u4e00-\u9fff·A-Za-z]{2,12})\s*[::]\s*「([^」]{1,80})」/g

说话人名字的字符类只允许汉字、中点和字母。而 阿咎(气声): 里,冒号前面那个字符是 ——不在允许范围内,整句匹配失败。

真正致命的是下一步。抓完带说话人的句子之后,代码里有这么一行:

if (out.length) return out.slice(0, 8);

意思是:只要抓到过任何一句带说话人的台词,就立即返回,后面那段「无说话人时只抽引号句」的兜底逻辑永远不会执行

所以不是「带批注的句子漏了、然后被兜底捡回来」,而是带批注的句子被丢掉,且没有任何补救。上面那段四句台词里,两句带批注被丢,只剩两句进了计数,低于门槛,当场卡死。

修法有两处,缺一不可:

  1. 说话人正则容忍一段可选的括号批注,中英文括号都要认:
    /([\u4e00-\u9fff·A-Za-z]{2,12})(?:[((][^))]{0,16}[))])?\s*[::]\s*「([^」]{1,80})」/g
    
  2. 去掉提前返回,改成「带说话人的结果」与「兜底结果」合并去重。

第一处还有个容易做错的细节:批注不能进说话人的名字。这个函数的返回值后续要拿去锁定角色形象,如果名字变成「谢无咎(气声)」,跨段就对不上同一个角色了。所以括号那一段要用非捕获组,只做跳过、不进结果。

三、写「无」的字段,被当成没填

有些字段这一段确实用不上,习惯写个「无」:

- 服装道具:无

而字段解析里有个长度判断,要求取到的值至少 2 个字符才算有效。「无」只有 1 个字,返回空串,于是这一段被判定为「缺字段」,整段作废。

阈值降到 1 就行。但要顺带想清楚语义分层:暂无/ 这类值应该算「字段存在且显式为空」,对非对白字段放行;对白字段不要在这里特殊放行,让它照常走引号句数那道门槛——毕竟一段没有任何台词,本来就该被拦下来。

四、门槛本身算错了:单位错配

前三条修完,还是卡。因为门槛这一侧也有问题,而且是个典型的单位错配。

计算对白门槛的函数是这样推段数的:

const segs = Math.floor(targetSec / 15);       // 用固定 15 秒反推段数
const gateSegs = Math.round(segs * 5 / 6);     // 留白比例
minDlg = gateSegs * 3;                          // 每段 3 句

这套逻辑在「每段 15 秒」的年代是对的。但换成 4 段 × 30 秒 = 120 秒的成片结构之后:

集级门槛按 15 秒段算,段级可拍表按 30 秒段算,两套单位打架。 一份结构完全正确的 4×30 剧本,天然到不了 21 句。

修的时候有个反直觉的地方,写在这里省得别人踩:

正文字数门槛不能跟着段数一起改。 120 秒的集比 90 秒的集更长,字数要求只能更高不能更低。如果把「按真实段数算」这个改法同时套到字数上,4 段算出来的字数门槛会比 6 段的短档还松,等于门槛倒挂。

所以正确做法是两个门槛用不同的基准:

顺带一提,每段句数也不该是固定的 3 句。15 秒段 3 句合理,30 秒段还是 3 句就明显撑不满——这正是很多人抱怨「AI 短剧看着人物在发呆」的来源之一。我们的取法是 15 秒段 3 句、30 秒及以上 4 句。

四条修完,同一份文件跑出来的结果是每集 22/23/24/21/21/21 句,六集全部放行。剧本一个字没改。

一份能过门禁的分段可拍表长什么样

顺便把结构贴出来。每段九个字段,缺一个就该被拦:

#### 段01
- 意图:让观众感到血渗入残玉的异常,与现代秩序被瞬间撕开
- 对白:修复馆夜班:「别碰那裂缝。」谢无咎:「玉纹在吃血……」远处喊:「阿咎你死了?马都没饮!」谢无咎(气声):「谁在叫我?」
- 表演:谢无咎眉心紧锁,指尖沿玉纹一颤;灯灭时瞳孔骤缩,肩膀猛地一沉,像坠进水里。
- 场景:现代古籍修复馆(夜)
- 配色风格:冷白日光灯主色,玉面青白一点血锈
- 角色:谢无咎
- 服装道具:白大褂;残玉(双扣形,缺三分);锥子;修复灯
- 光影运镜:微距固定拍血珠入缝;灯灭过曝切黑,留约三秒死静,再硬切马厩草堆仰角手持坐起。
- 段内白描:1)血珠入缝特写 2)灯灭死静 3)草堆坐起眩晕跟拍 4)摸玉无缺口拉开见马厩

几个写法上的经验:

「意图」写观众的感受,不写剧情。 写「阿咎穿越到了雁门」没用,那是剧情;写「让观众感到血渗入残玉的异常」才是导戏锚点,模型知道该往哪个方向使劲。

「表演」必须含具体的身体或面部动作。 「他很生气」拍不出来,「眉心紧锁,指尖沿玉纹一颤」才拍得出来。判定表演是否可拍,我们用的办法是查一张动作词表(眼、眉、指、肩、颤、缩、蹲、攥这一类),配合最短长度。纯抽象的心理描写会被拦下——这道拦截有真实价值,别为了让剧本通过就把它拆掉。

对白按段分配,不要堆在一起。 30 秒段写 4 句,节奏大致是每 7 秒一句,中间留表演。全集台词写够了但集中在一两段,其余段落照样是空的。

表演里避免只写形容词。 「愤怒地」「悲伤地」这种副词对成片没有任何约束力,模型看到的仍然是一片空白。

如果你在做类似的解析流程

四条通用的经验:

一、静默丢弃是最贵的 bug。 上面三个解析问题都不报错,只是让数据凭空少一点。用户看到的是「门槛太严」,实际是「你的输入被吃掉了」。凡是「过滤」「跳过」「提前返回」的分支,都值得加一条计数或日志,让丢弃这件事可见。

二、提前返回要特别小心。 if (out.length) return 这一行读起来像个无害的性能优化,实际是把兜底路径整条禁用了。有兜底逻辑的地方,优先合并去重,而不是二选一。

三、门槛和结构必须共用同一个单位。 一边按「总秒数 ÷ 固定段长」推,一边按真实段数推,迟早对不上。改的时候要逐个确认每个门槛该跟时长走还是跟结构走——它们的答案可能不一样,像上面字数和对白就是相反的。

四、改门槛算法时,先把旧阈值写成回归测试。 我们那个函数的注释里明确写着「180 秒档必须精确落回 280 字 / 30 句」,这是前人踩坑后留下的护栏。改算法很容易在满足新场景的同时把旧场景打破,而这种破坏往往几周后才会被发现。


排查过程与代码位置均为 2026-08-06 当日实录。具体行号会随版本变动,判断方法可复用。