name: video-storytelling description: | 使用AI生成的图像和配音音频创建连贯的视频故事序列。 结合图像生成和elevenlabs技能,生成完整的视频故事,在所有场景中保持视觉和叙事一致性。在整个故事中维护角色外观、风格、照明和声音一致性。
当用户请求以下内容时使用此技能:
- 带配音的视频故事
- 动画故事序列
- 教育视频内容
- 带有视觉的角色驱动叙事
- 多场景故事视频
- 配音图像序列
功能:视觉一致性锁定、角色持久性、多轮图像生成、角色声音配音、自动视频组装
默认:1个标题场景 + 5个故事场景 依赖:image-generation技能、elevenlabs技能、ffmpeg allowed-tools: [“*”]
视频叙事
目的
此技能通过结合AI生成的图像与配音音频创建连贯的视频故事序列。充当故事导演和视觉协调员,在所有场景中保持角色、视觉风格、照明和叙事语调的完美一致性。生成包含同步图像和角色配音的完整MP4视频。
何时使用
当用户要求以下内容时,应调用此技能:
- 创建视频故事或动画序列
- 生成带视觉的配音故事
- 制作带角色的教育视频内容
- 制作带配音的视觉故事书
- 创建角色驱动的视频叙事
- 生成多场景故事视频
- 制作带图片和配音的儿童故事
核心能力
视觉一致性系统
全局风格锁定:
- 固定宽高比、相机设置、照明
- 所有场景中一致的调色板
- 统一的视觉风格和后处理
- 防止视觉不连续
角色锁定:
- 在场景间保持角色外观
- 相同的服装、颜色、面部特征贯穿始终
- 一致的配饰和独特特征
- 视觉身份保留
多轮图像生成:
- 每个场景引用先前场景的图像
- 通过序列构建视觉连续性
- 防止角色/风格漂移
叙事系统
角色声音:
- 将角色映射到ElevenLabs声音
- 保持每个角色的声音一致性
- 支持对话中的多个角色
情感标签:
- 带有情感标记的表达性配音
- 音效和暂停
- 自然节奏和传递
叙事结构:
- 每个场景50-80词(15-20秒)
- 中性叙述者用于过渡
- 角色特定对话
视频组装
自动化流水线:
- 按顺序生成所有图像
- 创建角色配音
- 组合成同步的MP4视频
- 基于总音频长度等分时间每个场景
默认配置
场景结构
- 默认: 1个标题场景(场景0)+ 5个故事场景(场景1-5)
- 总计: 6个场景
- 可定制: 用户可以指定不同的场景数量
默认风格锁定
STYLE_LOCK:
- 宽高比:1080×1080(方形)
- 相机:50mm镜头,眼平视角
- 照明:柔和三点照明,暖主光(4500K)
- 调色板:#0B5FFF, #FFB703, #FB8500, #023047, #8ECAE6
- 材质:哑光饰面,无颗粒感或重光晕
- 背景:微妙渐变,简洁构图
- 风格:半写实卡通,线条清晰,柔和阴影
- 后处理:清晰对焦,无晕影或文本伪影
NEGATIVE_LOCK:
无文本错误,无拼写错误,无水印,无贴纸,
无额外字符,无视觉噪点,无剧烈照明变化
自定义: 用户可以用自定义风格锁定覆盖,但默认确保一致性。
默认声音映射
来自ElevenLabs声音:
叙述者:
- 中性叙述者(男性):George (
JBFqnCBsd6RMkjVDRZzb) - 中性叙述者(女性):Rachel (
21m00Tcm4TlvDq8ikWAM)
角色声音:
- 年轻男性(精力充沛):Josh (
TxGEqnHWrfWFTfGW9XjX) - 年轻女性(冷静):Rachel (
21m00Tcm4TlvDq8ikWAM) - 年轻女性(表达性强):Bella (
EXAVITQu4vr4xnSDxMaL) - 男性(权威):Adam (
pNInz6obpgDQGcFmaJgB) - 女性(温暖):Matilda (
XrExE9yKIg1WjnnlVkGX) - 年轻男性(友好):Antoni (
ErXwobaYiN019PkySvjV)
分配逻辑:
- 如果指定角色性别/年龄,匹配到适当声音
- 如果未指定,男性使用Josh,女性使用Rachel
- 叙述者默认使用George(男性)或Rachel(女性)
指令
步骤1:收集故事信息
从用户收集必要信息:
必需:
- 故事概念: 故事关于什么?
- 语调/类型: 教育、冒险、喜剧、戏剧等。
可选(如果缺失则提示):
- 场景数量: 默认为6(1标题+5故事),但用户可以指定
- 角色描述: 姓名、外观、性格
- 自定义风格锁定: 如果用户有特定要求,覆盖默认
示例提示:
"你的故事关于什么?"
"你想要多少场景?(默认:1标题+5故事场景)"
"描述你的主要角色:姓名、外观、性格"
"有特定的视觉风格偏好吗?(默认:半写实卡通)"
步骤2:定义角色
为故事中的每个角色创建角色档案:
角色档案模板:
character = {
"name": "角色姓名",
"species": "人类/动物/生物",
"description": "简短描述",
"colors": {
"primary": "#HEX",
"secondary": "#HEX"
},
"outfit": "服装描述",
"features": ["独特特征1", "特征2", "特征3"],
"personality": "性格描述",
"voice_id": "elevenlabs-voice-id",
"voice_name": "ElevenLabs声音名称"
}
示例:
pyter_python = {
"name": "Pyter Python",
"species": "友好蛇吉祥物",
"description": "一个愉快的编码导师蛇",
"colors": {
"body": "#0B5FFF", # 蓝色
"belly": "#FFB703" # 黄色
},
"outfit": "带圆形π标志的小白实验服",
"features": ["大眼睛棕色", "圆头", "愉快微笑"],
"personality": "热情、有帮助、好奇",
"voice_id": "TxGEqnHWrfWFTfGW9XjX",
"voice_name": "Josh"
}
声音分配:
- 询问用户声音偏好或基于角色自动分配
- 为常见类型使用默认映射
- 允许从ElevenLabs库中选择自定义声音
步骤3:规划故事序列
创建逐场景大纲:
场景0(标题场景):
- 视觉:带主要角色的标题卡
- 音频:故事介绍(叙述者或主要角色)
- 时长:约15-20秒
场景1-N(故事场景):
- 视觉:连续故事时刻
- 音频:带有角色对话的叙事
- 时长:每个约15-20秒
示例场景计划:
scene_plan = [
{
"number": 0,
"type": "title",
"visual_description": "Pyter Python带笔记本电脑,'Pyter的编码冒险'文本叠加",
"characters": ["Pyter Python"],
"narrative": "[愉快] 加入Pyter Python,开启激动人心的编码冒险!",
"speaker": "Narrator",
"voice_id": "JBFqnCBsd6RMkjVDRZzb"
},
{
"number": 1,
"type": "story",
"visual_description": "Pyter在桌前看着显示错误消息的计算机屏幕,困惑表情",
"characters": ["Pyter Python"],
"narrative": "[困惑] 嗯...这个错误消息是什么意思?[暂停] 我以为我的代码完美!",
"speaker": "Pyter Python",
"voice_id": "TxGEqnHWrfWFTfGW9XjX"
},
# ... 更多场景
]
步骤4:构建风格和角色锁定
准备全局风格锁定:
STYLE_LOCK = """
宽高比:1080×1080(方形)
相机:50mm镜头,眼平视角
照明:柔和三点照明,暖主光(4500K)
调色板:#0B5FFF, #FFB703, #FB8500, #023047, #8ECAE6
材质:哑光饰面,无颗粒感或重光晕
背景:微妙渐变,简洁构图
风格:半写实卡通,线条清晰,柔和阴影
后处理:清晰对焦,无晕影或文本伪影
"""
NEGATIVE_LOCK = """
无文本错误,无拼写错误,无水印,无贴纸,
无额外字符,无视觉噪点,无剧烈照明变化
"""
为每个场景构建角色锁定:
def build_character_lock(characters_in_scene):
lock = ""
for character in characters_in_scene:
lock += f"""
角色:{character['name']}
物种:{character['species']}
颜色:身体 {character['colors']['primary']}, 次要 {character['colors']['secondary']}
服装:{character['outfit']}
关键特征:{', '.join(character['features'])}
"""
return lock
步骤5:生成图像序列
使用多轮生成生成图像以确保一致性:
实现:
from pathlib import Path
import json
# 初始化跟踪
previous_image_id = None
image_files = []
# 生成每个场景
for scene in scene_plan:
print(f"生成场景 {scene['number']}: {scene['visual_description']}")
# 为此场景构建角色锁定
character_lock = build_character_lock(
[char_profiles[name] for name in scene['characters']]
)
# 构建完整图像提示
image_prompt = f"""
{STYLE_LOCK}
{character_lock}
场景描述:
{scene['visual_description']}
{NEGATIVE_LOCK}
"""
# 如果不是第一个场景,添加对先前场景的引用
if previous_image_id:
image_prompt += f"
为一致性参考先前场景:{previous_image_id}"
# 使用图像生成技能生成图像
# (这将调用图像生成技能)
# 为实现,使用适当模型(DALL-E 3 或 Gemini Pro)
result = generate_image(
prompt=image_prompt,
model="dall-e-3", # 或 gemini-3-pro-image-preview
size="1024x1024",
reference_image=previous_image_id
)
# 保存图像
filename = f"scene-{scene['number']:02d}.png"
save_image(result, filename)
image_files.append(filename)
# 为下一个场景引用跟踪
previous_image_id = result['image_id']
print(f" ✓ 保存:{filename}")
关键点:
- 场景0生成基础图像
- 场景1+为一致性参考先前场景
- 对每个提示应用STYLE_LOCK和CHARACTER_LOCK
- 使用顺序编号保存
步骤6:生成叙事音频
为每个场景创建声音配音:
实现:
from elevenlabs.client import ElevenLabs
client = ElevenLabs(api_key=os.environ['ELEVENLABS_API_KEY'])
audio_files = []
for scene in scene_plan:
print(f"为场景 {scene['number']} 生成音频")
# 准备对话输入
dialogue_input = {
"text": scene['narrative'],
"name": scene['speaker'],
"voice_id": scene['voice_id']
}
# 使用text_to_dialogue生成音频
audio = client.text_to_dialogue.convert(
inputs=[dialogue_input]
)
# 保存音频文件
filename = f"scene-{scene['number']:02d}.mp3"
with open(filename, 'wb') as f:
for chunk in audio:
f.write(chunk)
audio_files.append(filename)
print(f" ✓ 保存:{filename}")
叙事指南:
- 每个场景50-80词
- 使用情感标签:
[兴奋],[沉思],[困惑],[暂停] - 适当时包含音效:
[音效:门吱吱响] - 用暂停变化节奏
步骤7:连接音频
将所有场景音频组合成单个音轨:
实现:
import subprocess
# 构建ffmpeg连接命令
concat_filter = "concat=n={}:v=0:a=1[out]".format(len(audio_files))
inputs = []
for audio_file in audio_files:
inputs.extend(['-i', audio_file])
cmd = ['ffmpeg', '-y'] + inputs + [
'-filter_complex', concat_filter,
'-map', '[out]',
'full_audio.mp3'
]
subprocess.run(cmd, check=True)
print("✓ 音频连接:full_audio.mp3")
步骤8:组装最终视频
使用包含的 assemble_video.sh 脚本:
实现:
import subprocess
from pathlib import Path
# 准备命令
script_path = Path(__file__).parent / "scripts" / "assemble_video.sh"
cmd = [str(script_path), "full_audio.mp3"] + image_files
# 运行组装
subprocess.run(cmd, check=True)
# 输出将是 full_audio.mp4
print("✓ 视频创建:full_audio.mp4")
脚本详情:
- 基于总音频长度计算每个图像的相等时间
- 为每个图像创建视频片段
- 确保所有图像正好1080×1080(如果需要则填充)
- 连接片段
- 与音频轨道复用
- 输出高质量的H.264 MP4
步骤9:交付结果
提供给用户:
- 最终视频文件:
<故事名称>.mp4 - 场景分解: 每个场景的摘要
- 单独资源: 图像和音频文件(如果请求)
- 故事元数据: 角色档案、场景计划(如果请求)
示例输出:
✓ 视频故事创建:pyter-coding-adventure.mp4
场景:
0. 标题:"Pyter的编码冒险" (20s)
1. Pyter遇到错误 (18s)
2. Pyter意识到错误 (17s)
3. Pyter修复代码 (19s)
4. 代码运行成功 (16s)
5. Pyter庆祝 (15s)
总时长:1:45
分辨率:1080×1080
角色:Pyter Python(由Josh配音)
生成文件:
- pyter-coding-adventure.mp4(最终视频)
- scene-00.png 到 scene-05.png(图像)
- scene-00.mp3 到 scene-05.mp3(音频)
- full_audio.mp3(连接音频)
角色声音参考
ElevenLabs声音ID
叙述者:
- George(男性,中年,叙事):
JBFqnCBsd6RMkjVDRZzb - Rachel(女性,年轻,冷静):
21m00Tcm4TlvDq8ikWAM
年轻角色:
- Josh(男性,精力充沛):
TxGEqnHWrfWFTfGW9XjX - Bella(女性,表达性强):
EXAVITQu4vr4xnSDxMaL - Antoni(男性,友好):
ErXwobaYiN019PkySvjV - Elli(女性,情感丰富):
MF3mGyEYCl7XYWbV9V6O
成年角色:
- Adam(男性,权威):
pNInz6obpgDQGcFmaJgB - Domi(女性,自信):
AZnzlk1XvdvUeBnXmlld - Matilda(女性,温暖):
XrExE9yKIg1WjnnlVkGX
分配策略:
def assign_voice(character):
"""基于角色属性自动分配声音"""
# 检查显式分配
if 'voice_preference' in character:
return get_voice_id(character['voice_preference'])
# 基于属性自动分配
age = character.get('age', 'young')
gender = character.get('gender', 'male')
if age == 'young':
if gender == 'male':
return 'TxGEqnHWrfWFTfGW9XjX' # Josh
else:
return '21m00Tcm4TlvDq8ikWAM' # Rachel
else: # adult
if gender == 'male':
return 'pNInz6obpgDQGcFmaJgB' # Adam
else:
return 'XrExE9yKIg1WjnnlVkGX' # Matilda
示例故事生成
完整示例:“Pyter的第一个错误”
用户请求: “创建一个关于编码蛇修复他第一个错误的短故事”
步骤1:角色定义
pyter = {
"name": "Pyter Python",
"species": "友好蛇",
"colors": {"body": "#0B5FFF", "belly": "#FFB703"},
"outfit": "带π标志的白实验服",
"features": ["大眼睛棕色", "圆头", "愉快微笑"],
"personality": "热情学习者",
"voice_id": "TxGEqnHWrfWFTfGW9XjX" # Josh
}
步骤2:场景计划
scenes = [
{
"number": 0,
"visual": "Pyter带笔记本电脑,标题'Pyter的第一个错误'",
"narrative": "[愉快] 今天,Pyter Python将修复他的第一个编码错误!",
"speaker": "Narrator",
"voice_id": "JBFqnCBsd6RMkjVDRZzb"
},
{
"number": 1,
"visual": "Pyter盯着带有红色错误消息的屏幕",
"narrative": "[困惑] 等等...为什么我的代码不工作?[暂停] 计算机说有一个语法错误!",
"speaker": "Pyter",
"voice_id": "TxGEqnHWrfWFTfGW9XjX"
},
{
"number": 2,
"visual": "Pyter读Python书,沉思",
"narrative": "[沉思] 让我检查Python书...[暂停] 哦!我需要仔细看第5行。",
"speaker": "Pyter",
"voice_id": "TxGEqnHWrfWFTfGW9XjX"
},
{
"number": 3,
"visual": "Pyter指向屏幕特写,意识到",
"narrative": "[兴奋] 我找到了!我忘了关闭括号![暂停] 那就是错误!",
"speaker": "Pyter",
"voice_id": "TxGEqnHWrfWFTfGW9XjX"
},
{
"number": 4,
"visual": "屏幕显示'成功!'带绿色对勾",
"narrative": "[自豪] 我修复了它!我的代码现在完美运行!",
"speaker": "Pyter",
"voice_id": "TxGEqnHWrfWFTfGW9XjX"
},
{
"number": 5,
"visual": "Pyter庆祝,背景有五彩纸屑",
"narrative": "[温暖] 这就是Pyter学会每个程序员都会犯错误...而且没关系!",
"speaker": "Narrator",
"voice_id": "JBFqnCBsd6RMkjVDRZzb"
}
]
步骤3:生成(使用上述过程)
输出: pyters-first-bug.mp4 带6个场景,总长约90秒
需求
技能:
image-generation- 用于创建一致视觉场景elevenlabs- 用于角色声音配音
Python包:
pip install elevenlabs pillow
系统:
- Python 3.8+
- ffmpeg(用于视频组装)
- Bash shell(用于assemble_video.sh脚本)
- 2GB+ 空闲磁盘空间(用于临时文件)
API密钥:
- OpenAI或Google(用于图像生成)
- ElevenLabs(用于声音配音)
文件权限:
- 对
assemble_video.sh的执行权限
最佳实践
故事规划
-
保持简单:
- 从6个场景开始(1标题+5故事)
- 清晰开头、中间、结尾
- 第一个故事使用单个主要角色
-
角色一致性:
- 在开始前完全定义角色
- 使用独特视觉特征
- 在整个过程中保持服装/颜色
-
节奏:
- 每个场景15-20秒理想
- 使用暂停以增强戏剧效果
- 变化情感标签以增加表达性
视觉一致性
-
使用风格锁定:
- 对每个场景应用,无例外
- 不要在故事中途修改
- 自定义锁定应完整,非部分
-
角色锁定:
- 用十六进制代码指定颜色
- 列出3-5个独特特征
- 包括服装细节
-
多轮引用:
- 总是引用先前场景
- 提及“保持角色外观”
- 注明“相同照明和风格”
音频质量
-
叙事指南:
- 自然写作为演讲
- 适度使用情感标签(每个场景1-2个)
- 为节奏包含暂停
-
声音选择:
- 匹配声音到角色年龄/性格
- 保持叙述者声音中性
- 保持每个角色的声音一致性
-
音频测试:
- 首先生成一个场景测试
- 验证声音/情感匹配意图
- 在生成所有场景前调整
视频组装
-
文件组织:
- 使用一致命名(scene-XX.png/mp3)
- 保持平坦目录结构
- 组装后清理临时文件
-
质量设置:
- 默认1080×1080确保质量
- H.264基线配置文件以兼容
- AAC音频在192kbps
-
测试:
- 验证所有图像大小相同
- 检查音频文件有效
- 先用2-3个场景测试脚本
故障排除
视觉不一致
问题: 角色在场景间看起来不同
解决方案:
- 确保角色锁定应用于每个提示
- 验证先前图像被引用
- 添加“从先前场景保持确切角色外观”
- 使用更具体的颜色十六进制代码
音频问题
问题: 声音不匹配角色
解决方案:
- 验证voice_id正确
- 先用示例文本测试声音
- 检查角色声音分配逻辑
问题: 连接音频有间隙
解决方案:
- 确保所有音频文件是有效的MP3
- 检查ffmpeg连接过滤器语法
- 验证无缺失场景音频文件
视频组装错误
问题: 脚本失败,提示“文件未找到”
解决方案:
- 验证所有图像文件存在
- 检查音频文件路径
- 确保脚本有执行权限
问题: 视频中图像大小不同
解决方案:
- 验证所有图像是1080×1080
- 检查图像生成设置
- 脚本自动填充,但更喜欢精确大小
限制
-
场景数量:
- 实际限制:10-12个场景(视频长度约3分钟)
- 更多场景 = 更长生成时间
- 音频/视频文件大小考虑
-
角色复杂度:
- 推荐1-3个主要角色
- 太多角色 = 更难一致性
- 背景角色如果非详细可接受
-
视觉变化:
- 不能在故事中途改变风格
- 角色服装变化需要新角色锁定
- 主要场景变化(白天/夜晚)可能降低一致性
-
音频长度:
- 每个场景15-20秒理想
- 非常短的场景(<10秒)感觉匆忙
- 非常长的场景(>30秒)节奏慢
-
处理时间:
- 图像生成:每个场景30-60秒
- 音频生成:每个场景10-20秒
- 视频组装:总共30-60秒
- 总计:约10-15分钟用于6场景故事
相关技能
image-generation- 视觉生成必需elevenlabs- 声音配音必需python-plotting- 用于可视化故事分析scientific-writing- 用于编写叙事脚本
附加资源
- 图像生成技能: 参见
image-generation/SKILL.md - ElevenLabs技能: 参见
elevenlabs/SKILL.md - 风格锁定参考: 参见
references/style-locks.md - 叙事设计: 参见
references/narrative-design.md - 视频组装: 参见
references/video-assembly.md - 示例故事: 参见
examples/example-stories.md