把一篇小说自动转换成一支配旁白/角色对话配音的成片视频。一体化流程:小说→剧本转换+剧本校验(自然语言剧本,明确区分旁白/对话与说话人,先核实忠实于原文再往下走)→全局角色地点抽取(含声音设定与锁定视觉锚点)→全局角色/地点定妆图集中生成与校验→大场景切分(弹性时长,默认≤120秒,剧情连续性强时可放宽到≤240秒)→单大场景详细规划(旁白/对话按剧本行直接映射)→素材补全与差异化配音生成→角色/场景一致性校验+小场景视频生成与大场景内合成→最终拼接转场。每个阶段的具体操作步骤放在 references/ 下,进入某阶段前按需加载对应文件,完成后可从上下文中卸载。支持断点续跑、失败重试与 API Key 自动切换、跨 session 续接、以及用户对已生成内容提反馈后的定向回退重跑。当用户说"把这本小说做成视频"、"小说转视频"、"生成小说解说视频"时使用本 skill。
Resources
3Install
npx skillscat add onewaymyway/mini-agent/novel-video-studio Install via the SkillsCat registry.
小说转视频一体化 Skill (Novel Video Studio)
0. 这个 skill 怎么用
本 skill 把"小说转视频"的完整流程放在一个 skill 里,但不要求你一次性
读完所有细节。主文件(本文件)只讲两件事:
- 整体流程长什么样、各阶段产物/状态的契约是什么、阶段之间怎么衔接;
- 通用规则:什么时候必须跑校验脚本、失败了怎么办、用户要改内容时怎么
联动重跑、API 报错怎么处理。
每个阶段具体"这一步要做什么、跑哪个脚本、参数怎么传"的操作细节,
登记在上面 frontmatter 的 resources 里,对应 references/ 目录下的
文件,用 skill_resource_load(skill_name="novel-video-studio", resource_id=..., reason=...) 按需加载,读完执行完这一阶段就可以把这
份细节从上下文里卸载(不需要的时候不用一直带着,下次要用再加载一次
即可,文件不会变)。十份子资源分别对应九个阶段(阶段0——小说→剧本转换、
阶段1——剧本校验、阶段2——角色/地点抽取、阶段3——全局定妆图生成、
阶段4——大场景切分、阶段5——详细规划、阶段6——素材补全+配音、
阶段7——视频生成+合成、阶段8——最终拼接;其中阶段0/1合记在同一份script-conversion 子资源里,其余每个阶段各自一份)+ 两条贯穿性规范
(error-handling、revision-and-rollback),加载时机见各自description;也可以用 skill_resource_list 查看当前完整清单。
不要在还没进入某个阶段之前就把该阶段的子资源通读一遍——这样会造成
上下文浪费;也不要凭记忆猜测某阶段的脚本参数,进入该阶段前先加载
对应文件。
配套脚本统一放在 scripts/(不分子目录,直接按文件名调用):
.claude/skills/novel-video-studio/scripts/
├── check_script.py # 阶段1校验(剧本核查报告的机械把关)
├── check_entities.py # 阶段2校验
├── check_macro_scenes.py # 阶段4校验(时长弹性化:默认≤120秒建议值,≤240秒硬上限)
├── check_scene_detail.py # 阶段5校验
├── voice_mapping.py / tts_engine.py / synthesize_scene_audio.py
│ # 阶段6配音(synthesize_scene_audio.py 是入口)
├── check_assets_and_audio_v2.py # 阶段6校验
├── generate_scene_videos_v2.py # 阶段7生成小场景视频
├── check_consistency_report.py # 阶段7前置:机械校验 Agent 写的 consistency_report.yaml(覆盖完整性/全部pass/未过期),不做语义判断
├── compose_macro_scene.py # 阶段7大场景内合成
├── check_clips_v2.py # 阶段7校验
├── render_cover_title.py # 阶段3 Step B(可选):给 cover_bg.png 叠加标题文字产出 cover.png,本地 PIL 操作,不调用外部 API
├── compose_final_video_v2.py # 阶段8最终合成(若 cover.enabled,最前面新增独立封面片段,增加总时长)
├── check_project_state.py # 通用:查看项目当前进度到哪一步、下一步做什么
└── invalidate.py # 通用:用户改内容后,联动清理下游产物+回退状态阶段3没有独立的 check_*.py 脚本(当前未实现,由 Agent 直接读取global/characters.json/locations.json 逐条核对 asset_path,见references/02_global_assets.md)。
角色/地点定妆图依赖 gen_image_with_text skill,视频生成依赖gen_video_with_text skill,两者都是本 skill 的外部依赖(不重复实现,
按需调用即可)。
1. 整体流程与产物契约
阶段0 小说→剧本转换(script.md,全文一次性)
→ 阶段1 剧本校验(script_review.yaml,Agent逐场次核对剧情/对话/说话人/无臆造)
→ 阶段2 novel-entity-extractor(角色/地点抽取,含 voice_profile,基于 script.md 全文一次性)
→ 阶段3 全局角色/地点定妆图集中生成与校验(对阶段2产出的主表逐条生成,全部完成才能往下走)
→ 阶段4 大场景切分(macro_scene,以剧本场次为单位归并/细分,全文一次性,弹性时长)
→ 对每个大场景 macro_scene_XX 依次循环:
阶段5 详细规划(该场景) → 阶段6 素材补全(按需,仅新增实体/变体)+配音(该场景)
→ 阶段7 小场景视频生成+大场景内合成(该场景) → 向用户展示、等确认
→ 全部大场景 done 后,阶段8 最终拼接转场关键变化1(剧本化):阶段2、阶段4、阶段5现在读的都是阶段0产出的
结构化剧本 script.md,不再直接碰小说原文——原文里"叙事视角混杂对话、
引号嵌套动作描写"的非结构化特性是过去大场景/小场景切分反复出问题的
根因,剧本化把"区分旁白/对话/说话人"这件事一次性提前做对,下游阶段5
不再需要现场抠引号、识别动作描写混入。同时大场景时长约束从硬性 ≤120秒
改为弹性建议值(剧情连续性强时允许放宽到 ≤240秒),避免为了凑时长机械
切断连续戏剧张力。详见 next_doc/novel_video_studio_fix_plan_v5.md。
关键变化2(全局定妆图独立成阶段):角色/地点定妆图生成以前是
"素材+配音"阶段的第一步,按 asset_path 是否已生成去重、"用到哪个
大场景就顺带生成"。这个设计对阶段2主表之后才发现的新角色/新地点仍然
合理,但对阶段2一次性抽取出的、贯穿全书的角色/地点主表来说不够——
定妆图是保证角色/场景外观跨大场景一致的锚点资源,理应在角色/地点主表
定稿之后就集中生成、集中校验,而不是拖到"用到哪个大场景才发现没生成"。
现在阶段2产出完整主表后,先进入阶段3把全部角色/地点的定妆图一次性
生成完并逐条核对"一个不落",确认无误后才进入阶段4(大场景切分)。
阶段5详细规划中途回补出的新角色/地点、以及按需生成的外观变体定妆图,
仍然保留"用到哪个大场景就顺带生成"的按需逻辑,放在阶段6里处理,
详见 references/02_global_assets.md 与 references/05_assets_and_audio.md。
关键点:阶段5-7 是"以单个大场景为循环体"跑的,不是"全部大场景先
过完阶段5、再全部过阶段6、再全部过阶段7"的批处理流水线——用户需要
每做完一个大场景就能看到、检查这个场景的成片,而不是等到最后一刻才看
到第一份可检查的结果。阶段3(全局定妆图集中生成)不受这条规则约束,
它和阶段2一样是发生在循环开始之前的全局一次性操作;阶段6里"仅针对
新增实体/变体"的按需补生成同理,跟随触发它的那个大场景走,详见 §2.3。
目录结构:
novel_output/小说名_20260911/
├── novel_project.json # 全局配置(时长/画风/TTS方案/转场模式)
├── PROGRESS.md # 跨 session 记忆日志(决策/坑/用户要求),见 §3.3
├── script.md # 阶段0产出的剧本(旁白/对话/说话人结构化),阶段2/4/5的输入源
├── script_review.yaml # 阶段1 Agent 剧本核查结论(剧情覆盖/对话覆盖/说话人归属/无臆造)
├── global/
│ ├── characters.json # 角色库,含 voice_profile、asset_path
│ ├── locations.json # 地点库,含 asset_path
│ └── assets/ # character_*.png / location_*.png
│ # / cover_bg.png(可选,剧情向背景图,不含文字)
│ # / cover.png(可选,叠加标题文字后的成品,见 §1.1 cover.*)
├── macro_scenes.yaml # 大场景清单,status: pending→planned→done
├── macro_scene_01/
│ ├── scene_detail.yaml # 本大场景的小场景规划
│ ├── audio/ # narration_seg_<mid>_<i>.wav / dialogue_<charid>_<mid>_<i>.wav
│ ├── clips/ # <micro_id>.mp4,如 micro_01.mp4(不是 micro_scene_01.mp4)
│ ├── consistency_report.yaml # 阶段7 Step 0 Agent 语义核查结论(角色/地点/情节一致性),Step 0.5 校验其完整/全pass/未过期后才能生成视频
│ └── macro_scene_01.mp4 # 本大场景合成结果
├── macro_scene_02/ ...
└── video.mp4 # 最终产物1.1 各产物文件字段格式速查
下面把贯穿全流程、会被多个阶段读写的几份文件的完整字段列表集中列
一遍(各阶段 reference 里也有,但分散在各自文件里;这里是跨阶段的
权威速查表,字段类型/取值范围以这里为准,若某阶段文档描述与这里不一致
以这里为准并视为待修文档的 bug)。新建/修改这些文件时,只写下面列出
的字段,不要自造字段名或改变已有字段的类型,这是保持多阶段脚本之间
数据契约稳定的基础。
novel_project.json(阶段2创建,全程只读,除非用户明确要求改
项目级配置):
| 字段 | 类型 | 取值/说明 |
|---|---|---|
source_title |
string | 小说标题 |
target_duration_sec |
number | 目标总时长(秒) |
art_style |
string | 全书统一美术风格英文描述,阶段2 Step1 填 |
tts.engine |
string | "cosyvoice" | "edge-tts" |
tts.fallback |
string | 目前只实现 "edge-tts" |
tts.voice |
string | null | 旁白默认音色,null 时脚本按引擎默认值 |
bgm_enabled |
bool | 目前恒为 false(本版不接 BGM) |
transition_mode |
string | "cut" | "fade" |
transition_duration_sec |
number | fade 模式下的转场时长 |
orientation |
string | "landscape" | "portrait" |
aspect_ratio |
string | 如 "16:9"/"9:16" |
cover.enabled |
bool | 片头封面开关,默认 false(新功能,缺省即关闭),阶段2 Step0 询问 |
cover.title_text |
string | null | 封面标题文字,null 时用 source_title |
cover.duration_sec |
number | 封面片段时长(秒),默认 3.0 |
cover.layout |
string | "center"(默认,居中大字+蒙层)| "bottom_bar"(底部色块条)| "top_classic"(上方留白+描边字) |
global/characters.json 单条 characters[] 元素:id
(char_NN)、names (string[])、age_range ("child"|"teen"|"youth"|"middle_aged"|"elderly")、gender ("male"|"female")、nationality_or_ethnicity (string)、description_zh/description_en (string)、visual_anchor_en
(string,锁定视觉锚点:年龄段+性别+体型+发型发色+标志性穿着/显著特征
的一句话英文短语,阶段3生成定妆图时优先用它作为 prompt,阶段7每条prompt_en 也引用它保证角色外观跨场景不漂移)、voice_profile
(string)、first_appear (string)、relations (string[],如"char_02:挚友")、asset_path (string | null,阶段3首次批量生成
时回填,阶段6按需补生成新实体时同样回填)、face_reference_id
(预留字段,恒 null,未实现)、appearance_variants (object[],可选,
默认空数组,只有原文明确交代持续性外观变化——换装/变装/季节变化/
受伤等——才登记,见 01_entity_extraction.md Step 2.6;单条结构:variant_id (var_NN)、label_zh、trigger_zh (原文依据,必填)、visual_override_en (在不可变核心特征基础上覆盖服装/发型部分的完整
英文描述)、applies_scope (string[],生效的 macro_id/micro_id
列表)、asset_path (string | null,可选,阶段6按需生成))。
global/locations.json 单条 locations[] 元素:id (loc_NN)、name、location_type (string,如 "inn"/"forest")、era_setting
(string)、description_zh/description_en、visual_anchor_en
(string,同角色的锁定视觉锚点,建筑/环境类型+光照氛围+一两个标志性
视觉细节)、asset_path (string | null,阶段3首次批量生成时回填,
阶段6按需补生成新实体时同样回填)、appearance_variants (object[],
可选,结构同角色,用于登记同一地点持续性的环境变化——昼夜/天气/陈设等,asset_path 阶段6按需生成)。
script.md(阶段0产出,Markdown 自然语言剧本,全程只读,除非用户
明确要求改剧本内容并联动回退下游):按 ### 场次 N|地点·时间(内景/ 外景) 分段,段内是若干行 旁白:... 或 角色名:台词,格式规范见00_script_conversion.md。
script_review.yaml(阶段1由 Agent 写入,check_script.py
只读不写):checked_at、source_text_file、script_file、script_content_hash (对 script.md 全文算的 sha1 前12位,加sha1: 前缀,用于过期检测)、entries[],单条元素:scene_no
(number,对应 script.md 的场次编号)、status ("pass" |"fail")、checks (object,固定四个键 plot_coverage/dialogue_coverage/speaker_attribution/no_fabrication,各自取值"pass" | "fail")、notes (string,简要记录实际对照了原文哪些
段落/对话,不能是空话)。
macro_scenes.yaml 单条 macro_scenes[] 元素:id (macro_NN)、title、source_span (对应 script.md 的场次编号,如 "场次1" 或
合并多场次 "场次1-2")、raw_text (script.md 对应场次的剧本原文
逐字复制,不可改写——不是小说原文)、summary、estimated_duration_sec (number)、char_count (number)、uses_characters/uses_locations (id 数组)、status
("pending" → "planned" → "done",阶段5/7分别推进)。时长约束是
弹性的:默认建议 estimated_duration_sec ≤120秒,剧情连续性强时
允许放宽到 ≤240秒硬上限(超过240秒才是 check_macro_scenes.py 的
error,120-240秒之间只是 warning)。
macro_scene_XX/scene_detail.yaml 单条 micro_scenes[] 元素,
字段随阶段推进逐步补齐(同一份文件,不同阶段各自负责自己那部分字段,
不要覆盖其它阶段已写的字段):
| 字段 | 类型 | 由哪个阶段写入 | 取值/说明 |
|---|---|---|---|
id |
string | 阶段5 | micro_NN,全项目范围唯一 |
macro_id |
string | 阶段5 | 所属大场景 id |
uses_characters / uses_locations |
string[] | 阶段5 | 引用的全局库 id |
visual_hint |
string | 阶段5 | 画面提示(中文,供阶段7写 prompt_en 参考 + 阶段7 Agent 核查 prompt_en 是否符合情节的依据,需覆盖关键画面元素,不能空泛) |
character_variant_overrides / location_variant_overrides |
object | 不写 | 阶段5(可选) | {实体id: variant_id},只有该场景应使用某个已登记的 appearance_variants 变体时才写,不写则用默认 visual_anchor_en,见 04 文档 Step 4.5 |
content_blocks |
object[] | 阶段5 | 见下方 content_block 结构 |
duration_sec |
number | null | 阶段6回填 | 4-12 秒范围内,阶段5阶段写入时恒为 null |
prompt_en |
string | 阶段7(Agent 手写) | 阶段5不产出,脚本不代为生成,为空视频生成会直接失败;必须嵌入引用到的角色/地点 visual_anchor_en(或生效变体的 visual_override_en),见 06 文档"一致性铁律" |
video_mode |
string | 阶段7(Agent 手写) | "reference" | "keyframe" | "text",不设置时脚本按 "text" 处理 |
status |
string | 阶段7回写 | "pending" → "done" | "failed" |
macro_scene_XX/consistency_report.yaml(阶段7 Step 0 由 Agent
写入,check_consistency_report.py 只读不写):macro_id、checked_at、entries[],单条元素:micro_id、prompt_en_hash
(对当前 prompt_en 原文算的 sha1 前12位,加 sha1: 前缀,用于
过期检测)、status ("pass" | "fail")、checks (object,固定
四个键 character_appearance/location_appearance/cross_scene_drift/content_alignment,各自取值 "pass" |"fail")、notes (string,简要记录实际对照了哪些依据,不能是空话)。
详细写法和四项核查维度的定义见 06 文档 Step 0。
content_block 结构(content_blocks[] 单个元素):type
("narration" | "dialogue")、text (string,dialogue 必须原文
逐字摘录不可改写)、speaker (narration 恒 null,dialogue 必须是uses_characters 里的角色 id)。
音频文件命名(阶段6写入 macro_scene_XX/audio/,统一 .wav
后缀):narration_seg_<micro_id>_<block序号两位数>.wav(如narration_seg_micro_01_00.wav)、dialogue_<角色id>_<micro_id>_<block序号两位数>.wav(如dialogue_char_02_micro_01_01.wav);<block序号> 是该 micro_scene
的 content_blocks 列表下标,%02d 补零。
视频 clip 命名(阶段7写入 macro_scene_XX/clips/):<micro_id>.mp4(如 micro_01.mp4,不带 micro_scene_ 前缀)。
PROGRESS.md:不是结构化数据文件,是追加型 Markdown 日志,专门
装状态字段覆盖不到的跨 session 信息(决策/坑/用户要求),格式和写入
规则见 §3.3,新建/新 session 接手项目时都要处理它。
状态字段是阶段推进和"该不该回退"的唯一依据:
macro_scenes.yaml每条大场景:pending(未详细规划)→planned
(规划完成、待生成/待合成)→done(大场景视频已合成且校验通过);- 每个大场景
scene_detail.yaml里每个micro_scene:pending→done/failed(视频 clip 生成结果); - 顶层
video.mp4是否存在,代表整个项目是否已交付。
任何时候想知道"现在进度到哪、下一步该干什么",跑:
python .claude/skills/novel-video-studio/scripts/check_project_state.py <output_dir>输出里的 overall_stage/next_actions/warnings 直接告诉你该加载哪个
子资源(对照上面 frontmatter resources 里的 id)、该处理哪些大
场景,warnings 里如果出现"状态和磁盘不一致",先处理这个再继续往下走
(通常是上次执行中途被打断导致的)。
2. 通用规则(贯穿所有阶段)
2.1 先落盘,再校验,通不过不进入下一阶段
每个阶段的产物写完立刻落盘(不等用户确认),然后跑该阶段对应的check_*.py 脚本(阶段3例外,见 references/02_global_assets.md
里 Agent 自查的方式)。校验不通过,绝对不允许进入下一阶段,
也不允许把状态字段推进(比如 pending→planned)。不通过时回到本阶段
内部修正,重新写文件、重新校验,直到通过。这是保证整条流水线不带着
"看似完成实则有缺陷"的产物往下传的唯一手段。
特别地,阶段7"画面语义是否和角色/地点档案、和情节原文一致"这类判断
不能靠脚本做——同义词替换后语义已经变了、角色外观写反、在场人物对不
上原文这类问题机械关键词匹配既会漏检也会误报。这一类校验完全由
Agent 逐条语义核查完成,核查结论写入结构化的 consistency_report.yaml
报告文件;check_consistency_report.py 只做机械把关(报告是否覆盖
了本次全部场景、是否全部标记 pass、有没有在核查通过后 prompt_en 又被
改动过而报告没更新的"过期"情况),不做任何语义判断,也不能替代
Agent 的核查。四项核查维度、报告怎么写、脚本怎么把关,具体见子资源scene-video-generation Step 0/Step 0.5。
2.2 阶段间的"回补"是正常流程,不是异常
阶段5(详细规划)在处理某个大场景时,如果发现引用了阶段2没抽取到的
角色/地点,不是报错终止,而是:
- 加载子资源
entity-extraction(references/01_entity_extraction.md)
的"单点补抽取"模式,只处理当前这段原文,把新角色/地点并入global/characters.json/locations.json; - 加载子资源
assets-and-audio(references/05_assets_and_audio.md)
的 Step 1,只给这几个新增实体生成定妆图(阶段3已经批量生成过的
主表条目不受影响,不需要重新过一遍); - 回到阶段5继续规划,重新校验。
这个回补循环必须在阶段5内部闭环完成,不能把"引用了不存在的资源"这种
问题遗留到阶段6/7才发现——那样定位问题的成本高很多。类似地,阶段6如果
发现某个 micro_scene 配音后时长超过 12 秒硬限,也不是丢给阶段7硬着头皮
生成,而是回阶段5把这个小场景拆成两个,再重新走阶段6。
判断"该往前回退几步"的通用原则:定位到"是哪个阶段的产物不满足
下游契约",就回到那个阶段修正,而不是在下游阶段里硬编码补丁绕过去。
2.3 长篇/多大场景:必须逐个大场景端到端跑完,不要跨大场景批量处理
macro_scenes.yaml 有几个大场景,阶段5-7 就要跑几轮,且以"单个大
场景"为最小闭环单位:对某个 macro_scene_XX,依次做完
阶段5详细规划(该场景) → 阶段6配音(该场景, --macro-id macro_XX)
→ 阶段7小场景视频+大场景内合成(该场景, --macro-id macro_XX)
→ 该场景 status=done → 向用户展示这个大场景的结果,等待反馈/确认才能开始下一个大场景的阶段5。禁止的反模式:把全部大场景先一起跑完
阶段5(或阶段6的批量配音,不带 --macro-id),攒够所有大场景的配音后
再统一进入阶段7——这样用户要等到最后才能看到第一份可检查的成片,一旦
早期大场景的规划/人设/配音方向有问题,返工成本是按全部大场景计算的,
而不是一个大场景。
例外(这些确实是全局一次性资源,跟"逐场景处理"不矛盾):
- 阶段0的小说→剧本转换 + 阶段1剧本校验——全文一次性转换、一次性
核查,不能只处理部分内容(场次之间的剧情连贯性需要通盘把握); - 阶段2的角色/地点抽取——基于
script.md全文通读一次抽出全局角色
地点表,不能只看单个大场景(会漏掉后面场景才出现的角色); - 阶段3的全局角色/地点定妆图集中生成——对阶段2产出的主表逐条
生成,一次性做完并校验"全部完成",不按大场景拆分处理,也不允许
留着一部分等某个大场景用到才生成(这正是阶段3独立出来的原因,见references/02_global_assets.md); - 阶段4的大场景切分——同样需要看剧本全文划分边界;
- 阶段6 Step 1 里仅针对阶段5回补出的新角色/地点、以及被引用到的
外观变体的按需补生成——这部分不属于阶段3的批量生成范围,天然
应该在处理第一个用到该新实体/变体的大场景时顺带生成,之后其它大
场景直接复用,不用重复生成,也不需要、不应该攒到所有大场景一起
生成。
阶段6 Step 2(配音)、阶段7(视频生成+合成)默认都要带--macro-id macro_XX 只处理当前这一个大场景,不要在还没看到当前
大场景成片之前就去动下一个大场景。每跑完一个大场景的合成并校验通过、status=done 后,把这个大场景的产物(时长、配音引擎使用情况、任何
持续性失败场景)汇报给用户,用户确认没问题或提完修改意见处理完后,
再开始下一个大场景的阶段5。
用 check_project_state.py 随时查看整体进度(哪些大场景 done、哪个
正在处理)。这样任何一个大场景中途失败、需要重新调整,都不影响其它
已完成的大场景,也不会出现"配了十个场景的音,视频一个都还没生成,
用户完全看不到进展"的情况。
2.4 API 调用失败
跑到 synthesize_scene_audio.py(配音)、generate_scene_videos_v2.py
(视频生成)、或直接调用 gen_image_with_text(定妆图,阶段3的批量
生成 / 阶段6的按需补生成)这几步时,不可避免会遇到 API 报错(限流/
参数错误/网络问题)。处理规范见子资源 error-handling
(references/error_handling.md),核心原则一句话说完:限流类错误脚本会
自动切换 key 重试,不需要人工介入;非限流的参数类错误脚本重试几次后
会打印结构化错误直接放弃该条目并继续处理其它条目,需要 Agent 读懂错误
信息、去修对应的配置字段,然后只对失败的条目定向重跑,不要整体重跑,
也不要绕过错误直接忽略失败项。
2.5 用户反馈要求修改内容
用户看了中间产物或成片后要求"这句台词改一下"/"这个角色形象换一个"/
"这段画面重新生成",处理规范见子资源 revision-and-rollback
(references/revision_and_rollback.md)。
核心原则一句话说完:先改源头内容,再跑 invalidate.py 联动清空所有
依赖它的下游产物和状态,然后从被清空的那一层重新往后走,不允许只改
源头文件却不清理下游——那样下游文件会和新内容对不上(比如台词改了但
字幕、配音还是旧的)。
3. 项目启动与跨 session 续接
3.1 新项目
新项目从"进入阶段0"开始,向用户确认目标时长/横竖屏后立即写novel_project.json(细节见子资源 entity-extraction,references/01_entity_extraction.md Step 0),随后先做小说→剧本转换 +
剧本校验(子资源 script-conversion,references/00_script_conversion.md),script.md 校验通过后才进入阶段2(角色/地点抽取),抽取完的主表通过
阶段3(references/02_global_assets.md)集中生成定妆图并校验完成后,
才进入阶段4,再按上面的阶段顺序推进。同时在<output_dir>/PROGRESS.md 写入第一条记录(格式见 §3.3),作为这个
项目的跨 session 记忆起点。
3.2 新 session 接手已有项目——标准接手流程
任何一次新 session 里被要求"继续之前那个小说转视频项目"/"接着做",
不要凭对话上文的印象直接接着写产物(新 session 很可能完全没有
上文,或者上文只是一段被压缩过的摘要,细节不可靠),而是固定按下面
四步重新建立事实基础,再决定下一步做什么:
- 定位项目目录。用户明确给了
output_dir就直接用;没给的话,
扫描novel_output/*/novel_project.json,按目录 mtime 倒序列出候选
项目(source_title+ 路径),只有一个的话直接确认后使用,有多个
且用户没指明是哪本小说时,报清单让用户选一个,不要猜。 - 跑
check_project_state.py,拿到overall_stage/next_actions/warnings/每个大场景的status和micro_scenes明细——这是关于
"磁盘上实际完成到哪"的唯一权威事实来源,比对话记忆和上次的口头总结
都可靠。 - 读
<output_dir>/PROGRESS.md(如果存在,通常存在,见 §3.3),
补上check_project_state.py覆盖不到的"为什么"这一层:上次中断
前用户提过的定制要求、已知会反复出问题的坑(比如某个 TTS 引擎对某
类文本容易超时/失败)、上次做到一半时定下来但还没落到任何结构化
文件里的决定。这一步不能跳过——check_project_state.py只反映
机械状态(哪些文件存在、字段是否回填),反映不了"用户上次说这版
人设不满意但还没来得及改"这类还停留在对话里的信息,跳过这步很容易
在新 session 里重复上次已经被否掉的做法。 - 处理完
warnings里的状态/磁盘不一致后,才从next_actions
指向的阶段/大场景继续,不要在明知不一致的情况下继续往下堆产物
(常见于上次执行中途被打断:比如某个大场景scene_detail.yaml已经
写完但校验没跑完,macro_scenes.yaml里状态还停在pending)。
以上四步本身很轻量(一次目录扫描 + 一次脚本调用 + 一次小文件读取),
每次接手都固定跑一遍,比"信任上一个 session 留下的摘要"更可靠,也不
需要用户重新讲一遍项目背景(novel_project.json 里已经有的时长/画风
/横竖屏等配置,直接读,不要再问用户一遍)。
3.3 PROGRESS.md:弥补状态文件覆盖不到的跨 session 记忆
macro_scenes.yaml/scene_detail.yaml 等状态字段解决的是"做到哪一步
了",但解决不了"这一步是怎么做的决定、踩过什么坑、用户提过什么还没
落地的要求"——这些信息只存在于当时那次对话里,session 一断就丢失,
下一个 session 只能重新试错。PROGRESS.md 是专门装这类信息的追加型
纯文本日志,落在 <output_dir>/PROGRESS.md,格式:
## 2026-09-13 14:20
- 完成 macro_01 全流程(配音+视频+合成),用户确认通过,无需修改。
- 用户要求:全书人物对话语气偏克制,旁白可以更煽情一些——后续大场景
规划台词/旁白基调按这个来,不用每次都问。
- 已知坑:edge-tts 对超过 40 字的单段文本经常 300s 超时,配音时长的
content_blocks 尽量控制在 40 字以内,超过的提前在阶段5多拆一刀。
- 下一步:从 macro_02 开始阶段5详细规划。写入规则:
- 每完成一个大场景的端到端循环(§2.3)、或遇到一次需要记住的持续性
问题、或用户给出一条会影响后续大场景处理方式的要求时,追加一段
(不要改写/删除历史记录,这是日志不是状态文件); - 只记"下一个 session 需要知道、但不会体现在其它结构化文件里"的信息
——已经在macro_scenes.yaml/scene_detail.yaml里能看到的机械状态
不用重复记(比如"macro_01 已完成"这种check_project_state.py一
眼能看到的,除非附带了额外的、脚本看不出来的上下文才值得记,如上面
例子里"用户确认通过,无需修改"这句就是脚本状态之外的信息); - 每条前面带时间戳(
date命令或当前对话的实际日期),方便按时间线
回看;文件变长后不需要主动精简/归档,追加成本远低于误删有用信息的
风险,真的长到影响阅读时再考虑摘要归并最早的若干条。
check_project_state.py 现在会顺带把 PROGRESS.md 最后几条打印出来
(见脚本说明),接手项目时两者一起看,不需要分别手动读一遍。