当前主流视频生成模型大多沿用了文生图模型的文本编码器,输入端对token数量有硬性限制,一部分模型甚至只接受77个token。对于包含角色动作、运镜方式、光效变化和场景细节的长提示词,这个长度往往不够。超出部分被静默截断后,画面中丢失的往往是后半段的运动描述,导致主体动作与预期完全偏离。更隐蔽的问题是,长提示词会稀释交叉注意力,让模型难以判断当前帧应该关注哪些词语。

一、先理解提示词过长为什么会影响视频质量
文本编码器在接收输入后,会把每个token映射为固定维度的向量。视频模型在生成每一帧时,利用这些向量计算交叉注意力。提示词越长,注意力分布越分散,模型对核心主体的约束力反而下降。这解释了为什么有些提示词写得很详细,画面却出现元素混合或动作缺失。
另外,视频生成需要保持时间一致性。如果整段提示词没有按时间轴拆分,模型只能从一长串文字中猜测哪些描述属于同一时刻。比如提示词里同时出现“清晨薄雾”和“正午阳光”,模型可能把两者叠加到同一帧,而不是按顺序切换。所以解决长度问题的关键不是单纯删词,而是引入时序结构。
在工程中可以采取两条并行路线:一是语义压缩,把必要信息压进更少token;二是关键帧提示词插值,把长镜头拆成时间关键点,再通过插值生成连续条件。下面分别展开。
二、语义压缩:保留信息而不是简单缩写
语义压缩的第一步是拆分提示词中的全局信息与局部信息。全局信息包括主体外观、场景风格、镜头语言,这些内容在整段视频中基本不变,可以只保留一次。局部信息包括动作变化、光线变化、物体位移,这些内容需要挂到具体时间点,不能混在全局描述里。把两者分离后,全局部分可以压缩到50至80个token,局部部分则变成多个短句。
如果希望压缩过程程序化,可以用句子嵌入向量来筛选内容。先将长提示词按句号或分号切分,再计算每个句子的语义向量,然后使用贪心算法选择与已选句子差异最大的那些句子,直到达到目标句数。下面的代码演示了基于句向量多样性的压缩方法:
from sentence_transformers import SentenceTransformer
import numpy as np
model = SentenceTransformer('paraphrase-multilingual-MiniLM-L12-v2')
def semantic_compress(prompt, max_sentences=8):
sentences = [s.strip() for s in prompt.split('。') if s.strip()]
if len(sentences) <= max_sentences:
return ' '.join(sentences)
embeddings = model.encode(sentences, normalize_embeddings=True)
selected = [0]
selected_vecs = [embeddings[0]]
while len(selected) < max_sentences:
sim = embeddings @ np.array(selected_vecs).T
max_sim = sim.max(axis=1)
max_sim[selected] = np.inf
next_idx = int(np.argmin(max_sim))
selected.append(next_idx)
selected_vecs.append(embeddings[next_idx])
selected.sort()
return ' '.join([sentences[i] for i in selected])
这种压缩方式能保留语义差异较大的句子,但它会丢失时间顺序。因此更合理的用法是先按关键帧拆分,再对每一段局部提示词单独压缩。例如将“第0秒到第4秒”的提示词压缩成两句,“第4秒到第8秒”的提示词压缩成两句,而不是在整段文字上跑一遍压缩器。
实际调参时,可以设置一个token预算,比如单次输入不超过220个token。全局提示词固定占用约60个token,剩下160个token分配给6到8个关键帧。这样可以避免某些帧被过度压缩,同时也给插值过程留下足够语义空间。
三、关键帧提示词插值:让画面过渡更平滑
关键帧提示词插值的前提是先把长视频描述拆成带时间戳的短提示词。比如第0秒描述清晨森林、薄雾、低机位推进;第4秒描述雾气散开、光线增强、镜头开始横移;第8秒描述正午森林、阳光穿透树叶、镜头稍快移动。直接将这些关键帧提示词输入模型,画面在切换点容易出现跳变,因为相邻关键帧的语义向量差异较大。
解决办法是在语义向量空间做插值。取两个相邻关键帧的提示词向量,按当前时间点的比例计算中间向量。对于归一化后的向量,球面插值比线性插值更能保持方向连续性。下面的代码实现了球面插值,并输出中间帧向量的模长和方向检查:
import numpy as np
def slerp_embedding(vec_a, vec_b, t):
dot = np.dot(vec_a, vec_b)
dot = np.clip(dot, -1.0, 1.0)
omega = np.arccos(dot)
if abs(omega) < 1e-6:
return (1 - t) * vec_a + t * vec_b
so = np.sin(omega)
return (np.sin((1 - t) * omega) / so) * vec_a + (np.sin(t * omega) / so) * vec_b
key_vectors = model.encode([
"清晨的森林,薄雾,低机位缓慢推进",
"正午的森林,阳光穿透树叶,镜头稍快横移"
], normalize_embeddings=True)
for i in range(1, 5):
t = i / 5.0
mid_vec = slerp_embedding(key_vectors[0], key_vectors[1], t)
print(f"中间帧 {t:.1f} 向量范数: {np.linalg.norm(mid_vec):.4f}")
如果视频生成模型只开放文本输入,可以在已有的关键帧描述库中检索与插值向量最接近的句子,作为中间帧提示词。如果模型支持直接传入嵌入向量或条件向量,则无需还原文本,直接把插值结果接入条件模块即可。后一种方式对时间连续性的帮助更明显,因为它避免了文本离散化带来的信息损失。
需要特别注意的是,插值只在两个关键帧语义相近时效果稳定。如果相邻关键帧描述的是完全不同的场景,比如从室内到室外,插值向量可能落在语义空间中的低密度区域,还原出的提示词会变得混乱。此时应该插入新的关键帧,而不是依赖插值强行过渡。
四、工程落地:压缩率、间距与漂移的平衡
实际项目中,压缩率和关键帧间距往往相互制约。压缩率太高,每个关键帧的提示词只剩一堆名词,画面细节明显下降;压缩率太低,总token数量又会超出限制。建议先固定模型输入上限,再根据关键帧数量分配每帧可用的token数。比如总预算512个token,8个关键帧,每个关键帧分配50个token左右,剩余预算留给全局描述。
关键帧间距的设置可以先用粗分镜验证。如果发现两个关键帧之间的过渡区域出现画面扭曲或主体丢失,说明间距过大,需要在该区间增加关键帧。相反,如果生成画面在多个关键帧之间几乎无变化,说明间距过小,可以合并部分提示词以降低整体长度。
另一个容易被忽略的问题是语义漂移。即使每个关键帧的提示词都正确,连续插值也可能让画面逐渐偏离原始设定,因为插值过程引入了模型没有显式约束的中间语义。缓解方法包括:在插值结果中定期注入全局提示词向量做约束,或者对中间帧向量做正则化,限制它与相邻关键帧向量的最大距离。下面这些要点可以作为上线前检查清单:
- 全局描述与局部描述是否已经分离;
- 每个关键帧是否只描述该时间点附近的变化;
- 相邻关键帧语义差异是否在可插值范围内;
- 是否保留了原始文本和向量两种表示以便切换模型接口;
- 压缩后的token总量是否低于模型输入上限。
把语义压缩和关键帧提示词插值组合成两阶段流水线后,长提示词不再需要被粗暴截断,视频生成的时间一致性也会明显改善。该方案尤其适合分镜复杂、动作连续、需要精细控制光效和运镜的视频项目。