上一章用一支 AI MV 解释“长任务怎样拥有不会随进程消失的执行身份”。这一章换成九十秒动画解释视频《城市如何在夜间醒来》, 因为 OpenMontage 的 animated-explainer manifest 完整展示了 research、proposal、script、scene plan、assets、compose 与 publish。 两个案例承受同一类长周期生产压力,却不假装走同一条业务 pipeline。这里把镜头推近到制作现场:coding agent 怎样在仓库里读规则、 产出结构化工件、调用工具、接受审批并留下恢复点。

阅读契约。 全文始终跟踪“九十秒《城市如何在夜间醒来》动画解释视频”。读完后,应当能复述一次制作如何从 pipeline 与 manifest 进入 preflight, 再逐阶段读取 director skill、交接 artifact、按策略写 checkpoint,最后走到 render runtime 与 final review;还要能指出哪些约束由 Python 强制, 哪些只是 agent 必须遵守的仓库契约。

证据边界。 本文固定在 c36e412 源码快照。 YAML manifest 与 Markdown skills 是给 agent 的可读执行契约;只有文中明确链接到 Python 的 schema 校验、gate 拒绝、 文件写入、工具发现和 render routing 才称为代码强制。仓库里没有另一个 Python orchestrator 在后台自动扮演制片人。

一、一句 prompt 为什么不是生产系统

如果把任务直接写成“做一支九十秒《城市如何在夜间醒来》动画解释视频”,模型当然可以马上开始:先写几段旁白,再调图片或视频 API,最后拼成 MP4。 但生产过程里最贵的错误往往早于渲染发生:主题理解错了、三个创意方向其实只是同一个方向换标题、环境没有配置所选 provider、 用户以为会得到真实运动画面,系统却在失败后静默退化成图片平移。

先不要急着看源码名。把用户刚刚交来的任务想成一张还很粗糙的制作卡:输入只有题目、目标观众、几张霓虹城市参考图, 以及“解释电力、交通与夜间经济怎样让城市逐层亮起来”;输出希望是九十秒、有旁白、有字幕、能公开发布的横屏视频。 预算有限,引用的数据必须有来源,真正的运动画面不能在失败后悄悄变成静态幻灯片。 这张卡还不足以开工,因为它没有回答方向由谁确认、每一阶段交什么、哪项能力在当前机器上真实可用,以及什么证据能证明成片守住了承诺。

任务:九十秒《城市如何在夜间醒来》动画解释视频
已有输入:题目、目标观众、参考图、目标平台
用户承诺:有依据的解释、真实运动感、旁白、字幕、可发布
尚未决定:创意方向、provider、render runtime、成本
不可静默越过:提案、脚本、场景、素材、发布批准

1.1 先把五个高频词说成人话

Pipeline 是制作路线,例如“从零生成一支动画解释视频”,而不是某个模型。 Manifest 是这条路线的清单:先做什么、每一步读什么、交什么、能用哪些工具、什么时候必须停下来等人。 Director skill 是某一工种的工作手册,告诉 agent 怎样研究、提案、写脚本或审片。

Artifact 是阶段交接用的正式工件,例如研究简报、提案包、脚本、场景计划和素材清单;它们是文件中的项目事实,不是对话里一句稍后可能忘掉的承诺。 Checkpoint 则是进度记录:现在做到哪一阶段、状态是制作中、等待人还是已完成、这一步对应哪些工件。 如果 checkpoint 标着 awaiting_human,所谓 gate 就是“不收到批准便不能把它改成 completed”的那扇门。

1.2 真正要补齐的是责任,而不是 prompt 长度

一句 prompt 没有表达的责任一旦缺失会发生什么OpenMontage 放在哪里
当前阶段与输入工件返工时不知道该退回脚本、场景还是素材。pipeline_defs/*.yaml
创作决定与备选项换 provider、声音或渲染器后,只剩最后结果,没有原因。proposal_packetdecision_log
人工批准昂贵素材已经生成,用户才发现方向不对。manifest gate 与 checkpoint 状态
真实能力计划建立在未安装 runtime 或缺失 API key 上。ToolRegistry preflight
交付承诺render 成功被误当成产品完成。final_review 与 publish gate

OpenMontage 的 Rule Zero 先把入口收紧:所有制作必须选择 pipeline、读取 manifest、完成 preflight,再逐阶段读取对应的 director skill。 这条规则不是为了制造更多文件,而是把“模型临场发挥”改造成“agent 在显式生产合同中做判断”。

接下来只要一直追踪四个问题,密集的文件名就不会把主线冲散:

  1. 这一刻,agent 正在替谁做什么决定?
  2. 决定之前读了哪个工件,之后又写出哪个工件?
  3. 推进条件只是 Markdown 里的工作纪律,还是 Python 会拒绝违规状态?
  4. 如果会话现在消失,下一次从哪里知道已经做过什么?

二、核心心智模型:Agent 是控制平面,Python 是工具与持久化

仓库的 项目上下文 把架构写得非常直接:agent 承担编排、创作决策、review 与阶段推进;Python 负责可调用工具和持久化。 所以这里的 executive producer、director、reviewer 首先是 Markdown 中的角色协议,不是常驻 Python service。

OpenMontage 本身不是一个在后台自动转动的制片网站。典型操作现场是:用户把仓库交给一个能够读文件、调用工具的 coding-agent 会话, agent 根据 AGENT_GUIDE.md 打开 YAML 与 Markdown,显式调用 Python tool 和 checkpoint utility。 如果没有这样的 agent 主动推进,仓库里的文件不会自己从 research 跑到 publish;换一个会话后,新 agent 也必须先读项目目录才能恢复上下文。

“控制平面”在这里不是什么抽象云计算术语。它只是说:agent 决定下一步该读哪份规则、该让用户在什么地方做选择、该调用哪件工具, 以及一个失败应该返工还是升级给人。真正生成图片、合成音频、渲染视频和写入 checkpoint 的动作由 Python 工具完成。 agent 不需要亲自实现视频编解码,Python 也不替用户判断“城市逐层亮起”是不是比“追踪一个夜班司机”更适合解释这个题目。

OpenMontage 控制平面图,coding agent 读取 AGENT_GUIDE、pipeline_defs 和 skills,调用 ToolRegistry 中的工具,并把 artifacts、checkpoint 与 final.mp4 写回项目目录
智能判断留在 agent;机器可验证的边界下沉到 schema、checkpoint utility、tool contract 与 renderer。
AGENT_GUIDE.md
  -> 选择 animated-explainer
  -> 读取 pipeline_defs/animated-explainer.yaml
  -> ToolRegistry preflight,展示真实能力
  -> 读取当前 stage 的 director skill
  -> 产出 schema-valid artifact
  -> reviewer skill 自查
  -> checkpoint utility 持久化并执行 gate
  -> 进入下一 stage 或等待人工批准

把这段流程放回霓虹城市解释视频:agent 先从 manifest 看见当前是 proposal,于是读取 proposal director; director 要求它拿研究结果做出至少三个真正不同的方案,并把当前可用的 provider、成本和 runtime 说清楚。 agent 产出 proposal_packet,reviewer 检查三个方案是否只是换标题,checkpoint utility 再验证结构并把状态写成 awaiting_human。用户选择“电力、交通与夜间经济依次接管画面”之后,agent 才能把 proposal 重写为 completed 并进入 script。

这是一种很适合 coding agent 的分层。稳定规则可以版本化、review、被人和模型共同阅读;变化很快的 provider 通过工具注册表暴露; 只有“不能靠自觉”的部分才落到代码。代价也同样明确:如果 agent 没有读取规则、上下文漂移或平台不支持可靠的工具调用, Markdown 本身不会像进程内 orchestrator 那样主动夺回控制权。

2.1 固定合同与运行时现实必须分开

回答什么典型文件
生产合同必须经过哪些阶段,哪些工件与关口不可省。AGENT_GUIDE.mdpipeline_defs/
工作方法当前角色应如何研究、提案、写脚本、审片。skills/
能力现实这台机器此刻有哪些 provider、runtime 与依赖。tools/tool_registry.py
项目事实用户批准了什么、已经做完什么、产物在哪里。projects/<id>/ 下的 artifacts 与 checkpoints

2.2 一次阶段推进,其实只有六个动作

  1. 定位:从 checkpoint 找到第一个未完成阶段。
  2. 装载:读取 manifest、当前 director skill,以及上游已经批准的工件。
  3. 判断:由 agent 在工作手册约束下提出创意或执行选择。
  4. 执行:需要外部能力时,通过 registry 中的 Python tool 产生真实副作用。
  5. 取证:把结果写成 schema-valid artifact,并让 reviewer 对照本阶段标准检查。
  6. 交接:遵守 manifest 的 checkpoint 策略;需要持久化就写状态,有 gate 就等待人,否则进入下一阶段。

这六步也给出了系统的失效边界。前三步依赖 agent 忠实阅读并遵守仓库契约;artifact schema、checkpoint gate、原子写入和 render routing 才是 Python 能强制的部分。把所有 Markdown 都删掉,工具仍会存在,却没有制片方法;把所有 Python 都删掉,文字规则仍在,却没有可靠的验证、持久化和媒体执行面。

三、Manifest 不执行视频,它定义一条可审计的路线

对这支动画解释视频,主线就是 animated-explainer.yaml。 它把制作拆成 research → proposal → script → scene_plan → assets → edit → compose → publish, 并为每个阶段声明输入工件、产出、可用工具、review focus、success criteria 与人工批准策略。

OpenMontage animated-explainer 阶段路线,从 research、proposal、script、scene_plan、assets、edit、compose 到 publish,并标出工件和人工关口
Manifest 是阶段地图,不是执行引擎;真正读取地图并采取下一步的是 coding agent。

3.1 先按真实顺序走完整条路线

深挖某个类或字段之前,先把一次生产按时间顺序走完。下面每一行都回答同样四件事:这一站收到什么、agent 做什么、留下什么、 凭什么进入下一站。Preflight 发生在创意承诺之前;后面的章节再分别展开 checkpoint 与渲染边界。

时刻Agent 此刻做什么留下的项目事实推进条件
选择路线把明确的动画解释请求匹配到 animated-explainer,读取 manifest。project.json 标记项目与 pipeline。路线与目标一致。
Preflight发现真实工具、provider 配置与 composition runtime。给人看的 capability menu;尚未承诺创意。缺失能力被说明或补齐。
Research研究现有内容、数据、受众问题与可讲角度。research_brief来源、数据点与角度达到 manifest 标准。
Proposal把研究与能力现实变成三个不同方案、成本和生产计划。proposal_packetdecision_log用户批准 concept c2、provider 与 runtime。
Script把已批准方向写成九十秒、有时间与旁白表演提示的脚本。script用户批准措辞、时长与叙事。
Scene plan把脚本段落变成连续镜头与所需素材。scene_plan,包含 scene-04用户批准 storyboard。
Assets按 scene 调用图片、视频、TTS、音乐工具并记录成本。asset_manifest 与局部 checkpoint。素材存在、风格一致,用户批准 filmstrip。
Edit用 asset ID 排时间线、字幕、音乐与转场。edit_decisions无空档、引用有效、承诺被带到 compose。
Compose按批准的 runtime 渲染并检查真实输出。render_reportfinal_review、MP4。输出通过规定检查。
Publish准备视频、元数据与缩略图概念的交付包。publish_log用户最后批准发布。

这里最值得借鉴的不是阶段数量,而是每次交接都变成了有名字的 artifact。proposal 不是聊天里一句“就走方向 B”, 而是必须带至少三个 concept、production plan、成本估计和批准状态的 proposal_packet; compose 也不是只返回文件路径,而要同时产生 render_reportfinal_review。 下游读工件,不必依赖 agent 还记得上百轮聊天里的隐含决定。

3.2 Stage director 是“怎样做”,schema 是“至少长什么样”

Manifest 只告诉 agent 现在要产出 proposal;具体怎样给出互相不同的方向、怎样解释成本与质量权衡, 由 proposal director skill 指导。另一方面, proposal schema 把最低结构变成机器可验证条件,并要求显式记录 render_runtime

三层在一次 proposal 里这样协作:manifest 声明输入必须有 research_brief、输出必须有 proposal 与 decision log、而且这一站要等人; director skill 进一步要求方案在 hook、叙事结构和视觉方式上真正不同;schema 只检查结构与枚举值,不能判断三个标题是否只是同义改写。 最后并不是“仓库里放了一个 schema 文件”就自动安全: validate_checkpoint() 在写 checkpoint 时调用 artifact validator,缺少必需工件或字段才会实际被拒绝。

{
  "selected_concept": {
    "concept_id": "c2",
    "rationale": "用电力、交通与夜间经济三层解释城市怎样醒来"
  },
  "production_plan": {
    "pipeline": "animated-explainer",
    "renderer_family": "cinematic-trailer",
    "render_runtime": "hyperframes",
    "composition_mode": "atelier",
    "delivery_promise": {
      "promise_type": "motion_led",
      "motion_required": true,
      "tone_mode": "cinematic",
      "quality_floor": "presentable"
    }
  },
  "approval": {
    "status": "approved"
  }
}

这是教学用缩略形状,不是可直接提交的完整 artifact。关键在于把“方向”“技术 runtime”“创作方式”和“交付承诺”拆成不同字段: 后面如果 renderer 发生变化,就能判断它是合理实现调整,还是偷偷改变了用户批准的作品性质。

3.3 跟着同一个镜头穿过工件链

只看阶段名称仍然容易把 pipeline 想成一排互不相干的待办。更好的办法是盯住一个具体镜头。 假设研究发现电力、交通与夜间消费并不是同时启动,脚本把“城市分层醒来”写进第三段旁白;scene plan 再把这段旁白变成 scene-04,要求低机位、霓虹边缘光和向前推进的镜头。到了 assets,场景需要的背景、运动片段和旁白各自得到 asset ID; edit 只引用这些 ID 排时间线,compose 才把引用解析成文件并渲染。

research_brief
  -> 依据:城市夜间活动由多套系统分层支撑
script.sections[s03]
  -> 旁白:城市不是同时醒来,而是一层层点亮
scene_plan.scenes[scene-04]
  -> 18s–27s,neon,dolly_in,需要运动背景
asset_manifest.assets[neon-04]
  -> scene_id=scene-04,记录路径、provider、成本
edit_decisions.cuts[cut-04]
  -> source=neon-04,进入时间线
final_review
  -> 抽查中段画面、音轨、字幕与 motion promise

上面是帮助理解的 lineage,不是仓库里的真实项目记录;字段关系则来自 scriptscene planasset manifestedit decisions。 有了这条链,返工不再只是“我不喜欢这一段”:团队可以指出是研究依据不对、旁白不对、场景语言不对、素材失败,还是剪辑引用错了。

如果脚本改了,后面的场景和素材会自动作废吗?

当前快照没有展示一个自动计算工件依赖并级联失效的构建图。 get_next_stage() 只按 manifest 顺序寻找第一个没有 completed checkpoint 的阶段。若用户批准后又改脚本,agent 必须根据 send-back 规则重做受影响工件并重写 checkpoint; history/ 会保留被替换的正式版本。也就是说,它提供可追踪的返工材料,但不是自动依赖失效引擎。

四、Checkpoint 把昂贵创作变成可恢复的项目状态

init_project() 先建立项目目录与 project.json;需要持久化或设置 gate 的阶段再写 checkpoint_<stage>.json。 是否必写由 manifest 的 checkpoint_required 决定:这条 pipeline 的 research 是 false,proposal、script、scene_plan、 assets、edit、compose 与 publish 是 true。状态可以是 in_progressawaiting_humancompleted, 长阶段还可以把已完成场景写进 partial progress。

OpenMontage checkpoint 与人工关口图,in_progress 保存局部进度,awaiting_human 停在批准前,completed 进入下一阶段,旧版本进入 history,决定累积到 decision_log.json
checkpoint 既是恢复点,也是人机交接面;history/ 保留阶段重做与批准转换的旧版本。

4.1 先回放一次做到一半的中断

素材阶段最适合看 checkpoint 的价值,因为它既慢又贵。agent 进入 assets 时先写一份 in_progress; 每完成一个重要场景,就把已经完成的 scene ID 和尚未成为正式 artifact 的草稿放进 metadata.partial_progress。 如果生成到 scene-04 时会话消失,磁盘上至少留下“前四个场景已经成功”的事实,而不是只剩聊天窗口里一句“正在生成”。

进入 assets
  -> checkpoint_assets = in_progress
完成 scene-01 ... scene-04
  -> 刷新 partial_progress.completed_scene_ids
Agent 会话中断
  -> 新会话读取 get_next_stage(),仍得到 assets
  -> 读取 partial progress,跳过已完成场景
完成全部素材并通过 review
  -> checkpoint_assets = awaiting_human
用户批准 storyboard / filmstrip
  -> checkpoint_assets = completed
  -> 才进入 edit

这里恢复的是项目语义:哪些场景已经有可用素材、下一步应从哪里继续。它不保证某个进行中的云端生成请求能被原样接管; provider 是否已经扣费、是否接受了请求、回执是否丢失,仍需要工具自己的 operation ID、幂等策略或账本。 checkpoint 解决“下一个 agent 应该相信哪些项目事实”,不能替外部服务发明它没有提供的恢复语义。

恢复时真正读到的不是抽象的“进度”,而是类似下面这样的缩略记录:

{
  "project_id": "night-city-explainer",
  "pipeline_type": "animated-explainer",
  "stage": "assets",
  "status": "in_progress",
  "human_approval_required": true,
  "human_approved": false,
  "artifacts": {},
  "metadata": {
    "partial_progress": {
      "completed_scene_ids": ["scene-01", "scene-02", "scene-03", "scene-04"]
    }
  }
}

这是 shape-level 示例。正式 artifact 尚未完整时,checkpoint protocol 要求把草稿放在 metadata.partial_progress; 如果把一个已知 artifact 名写进 artifacts,即便状态仍是 in progress,它也会接受对应 schema 校验。 新会话先读 stage 与 status,再决定是继续局部工作、重新展示人工关口,还是寻找下一个阶段。

4.2 人工 gate 不只是 prompt 里的提醒

Animated explainer 的 proposal、script、scene_plan、assets 与 publish 都在 manifest 中声明人工批准。 write_checkpoint() 的 gate enforcement 会重新读取 manifest:如果一个 gated stage 没有 human_approved=True 却要写成 completed,代码直接抛错。 这就是“指令驱动但关键边界 fail closed”的具体例子。

“等待人工”也不是页面上的一行提示。正确协议是先写 awaiting_human,展示工件摘要、review 结果与已花成本,然后结束当前交互; 只有用户下一次明确批准,才能带着 human_approved=True 重写为 completed。这样“我本来准备问”与“用户真的批准过”在持久化状态里不会混成一件事。

proposal = in_progress
  -> proposal_packet 完成并通过结构校验
proposal = awaiting_human
  -> 展示三个 concept、runtime、成本与 reviewer 发现
用户要求修改
  -> 重写 proposal,decision_log 追加选择理由,再次 awaiting_human
用户明确批准
  -> proposal = completed, human_approved=true
  -> get_next_stage() 返回 script

若 agent 跳过等待直接写 completed
  -> CheckpointValidationError: GATE VIOLATION
  -> 项目仍停在 proposal,不会得到一份伪造的批准状态

4.3 原子写入、历史版本与 decision log 各自解决不同问题

当前 checkpoint 先序列化到临时文件,再通过 os.replace 原子替换;被覆盖的非 in_progress checkpoint 会复制进 history/。 与阶段状态分开的 decision_log 则保存每个重要选择的备选项、选中项、理由和用户批准。前者回答“做到哪里”,后者回答“为什么这样做”。

三者不要混用:原子替换防止一次写盘只写出半截 JSON;history/ 保存被正式状态覆盖的版本与 gate 转换; decision_log 则是跨阶段累积的选择账本。频繁刷新的 in_progress 是心跳和局部进度,不会每次都塞进 history, 否则生成一百个场景就会制造一百份没有审计价值的中间版本。

projects/night-city-explainer/
  project.json
  decision_log.json
  checkpoint_proposal.json
  checkpoint_script.json
  checkpoint_assets.json
  history/
    checkpoint_proposal_....json
  artifacts/
  assets/
  renders/
    final.mp4

get_next_stage() 会按 pipeline 顺序寻找第一个未完成阶段。新的 agent 会话因此能读取项目文件继续;但“能读文件继续”与“执行系统自动 replay”仍然是两种承诺, 第八节会专门把它们拆开。

五、Preflight 先问“真实能做什么”,再承诺创意

视频工具变化很快。同一个能力可能有本地模型、云 provider、多个价格与依赖;静态列一张工具表,很快就与机器现实脱节。 OpenMontage 的 discover() 遍历 tools package,注册实际存在的具体工具;selector 按 capability 聚合 provider,而不是让 director 绑定一个固定品牌。

OpenMontage capability preflight 图,ToolRegistry 自动发现 provider 工具,经 selector 和 provider_menu_summary 形成能力菜单,再约束 production plan 与 renderer 选择
Pipeline 描述需要什么能力,registry 报告当前环境拥有什么能力;proposal 只能在两者交集里承诺。

给人看的入口不是原始工具 dump,而是 provider_menu_summary(): 它汇总每类 capability 的 configured/total、可用与不可用 provider、setup offer、runtime warning,以及 Remotion、HyperFrames、FFmpeg 的可用性。这样 agent 可以在花钱之前告诉用户“当前能做什么、缺什么、降级会改变什么”。

ToolRegistry.discover()
  -> provider_menu_summary()
     composition_runtimes
     capabilities[]
     setup_offers[]
     runtime_warnings[]
  -> proposal_packet.production_plan
  -> 用户批准 provider / cost / runtime
  -> proposal -> script -> scene_plan -> assets

5.1 能力菜单怎样真实改变创意方案

provider_menu_summary() 的价值在于把工具注册表翻译成能做生产决定的菜单。下面不是作者机器的实时输出, 而是一份保持源码字段含义的缩略例子;configured/total 表示某类工具中有多少当前报告可用, setup offer 指出可以通过配置解决的缺口,runtime warning 则解释为什么某个看似存在的执行器还不能开工。

composition_runtimes
  remotion: true
  hyperframes: false
  ffmpeg: true

capabilities
  image_generation: configured 1 / total 3
  tts:              configured 1 / total 4
  video_generation: configured 0 / total 5

setup_offers
  video_generation: add provider API key

runtime_warnings
  hyperframes: required runtime not resolvable

假设提案原本想让每个城市街区都用真实生成视频,但 preflight 发现当前只有图片生成可用,视频 provider 没有配置; 同时 Remotion 可用,可以让静态素材在合成层产生排版、镜头和图形运动。agent 不能继续把“真实生成运动”写进承诺, 也不能偷偷把它解释成 Ken Burns。它要么帮助用户配置视频 provider,要么把方案改成以 motion graphics 为主,并明确说明作品性质、成本和质量会怎样变化。

创意需要:真实运动镜头 + 旁白 + 字幕
当前能力:图片可用,TTS 可用,视频生成不可用,Remotion 可用
不能做:按原承诺直接进入 assets
可以选:
  A. 配置视频 provider,保留 motion-led 承诺
  B. 改成 motion-graphics-led,重写承诺与成本
推进条件:用户看见差异并批准其中一条

Preflight 也有边界。discover() 能证明某个工具类被注册,工具报告的 status 能说明依赖或配置是否满足; 它不能保证远端 provider 此刻没有限流、账户有额度、生成内容一定合格。前者是开工前的能力事实,后者仍要在真实调用、ToolResult、 cost log 与 reviewer 中观察。把“可发现”写成“必成功”,只是把另一种静默假设搬进了系统。

六、先锁定渲染承诺,失败时也不准静默换引擎

OpenMontage 把三件常被混在一起的事拆开:delivery_promise 说作品答应交付什么; renderer_family 说创作语法;render_runtime 说真正运行 Remotion、HyperFrames 还是 FFmpeg。 composition_mode 又独立描述是用可复用模板,还是为单支作品手工搭建 atelier composition。

把它们翻成四个用户问题会更直观:最终答应我看到真实运动还是图文动画?画面采用电影预告、数据解释还是别的创作语法? 哪个技术引擎实际负责把时间线算成 MP4?这次是复用模板快速生产,还是为单支作品手工写一套 composition? 四个答案相关,却不能互相代替。尤其是 runtime,只是技术执行者;选择了 Remotion 并不自动保证作品具有运动感,也不自动证明它符合批准过的 visual approach。

仓库契约要求在 proposal 阶段把可用 runtime 摆给用户,并记录选择。 到真正渲染时, video_compose._render() 拒绝缺失或未知的 render_runtime,然后严格路由到对应实现。明确选择 FFmpeg 时不会自动“升级”; Remotion 失败时也不会悄悄退到另一条路径。

OpenMontage 渲染锁图,proposal_packet 中的 delivery promise、renderer family、render runtime 和 composition mode 传到 edit_decisions,再严格路由 remotion、hyperframes 或 ffmpeg,失败分支要求显式决策
fallback 不是技术细节:当它改变运动、质感或成本时,就是需要重新沟通并写入 decision log 的产品决定。

6.1 渲染失败之后,正确流程是什么

假设用户批准的是 Remotion atelier:每个场景为这支解释视频手工编排,而不是套用库存卡片。渲染时 Node 依赖缺失, 最省事的代码当然可以自动退到 FFmpeg,把静态图做平移缩放;但这样虽然更容易得到 MP4,却破坏了用户批准的运动语言。 _render() 的选择是返回失败和可选路径,把产品决定交还给 agent 与用户。

Remotion render 失败
  -> 不生成“看起来成功”的 FFmpeg 替代品
  -> 说明失败原因:依赖、工具 bug 或作品设计
  -> 展示选项:修复 Remotion / 明确降级 / 改用另一 runtime
  -> 解释每个选项对运动、成本、时间的影响
  -> 用户批准新路径
  -> decision_log 追加同一决策主题的新记录
  -> 更新工件并重新渲染

注意最后两步不是“把旧字段改掉”。decision_log 是追加历史;当 runtime、provider 或声音选择改变时, 新记录要沿用相同的 category 与 subject,让旧选择成为可见的被替代方案。这样 final review 才能判断实际运行的 runtime 是获批修订,还是无人知情的漂移。

6.2 为什么“可播放”仍然不是“可交付”

Render 前,工具会验证 cut、asset 与 scene plan;Render 后, 同一入口触发 final reviewfinal_review schema 要求记录容器与音轨、抽帧检查、音频抽查、字幕覆盖,以及实际 runtime 是否保持 proposal 的承诺。 如果 review 为 fail,ToolResult 也会失败,不能把文件包装成完成品。

机器实际采集了哪些证据

当前 Python 路径先用 ffprobe 读取容器、视频流、音轨、时长、分辨率与 codec;再在全片 10%、35%、65%、90% 的位置抽四帧, 用非常粗的文件大小启发式提示黑帧;音频检查通过 volumedetect 寻找近乎静音与可能削波;字幕检查则看字幕流, 或确认用于烧录的字幕源文件仍然存在。最后,它比较 proposal 与 edit 中的 runtime,并计算 delivery promise 的 motion 比例。

这些检查擅长抓“没有视频流”“完全无音轨”“时长离谱”“runtime 被换掉”一类可观测故障,却不等于 Python 真正看懂了作品。 例如 MP4 没有音轨会留下明确 issue;但如果音乐把旁白压得听不清,只要总体音量不低,简单统计仍可能通过。 人物是否变形、字幕是否挡住主体、节奏是否有情绪,也需要 reviewer agent 看抽帧或转录,最终仍应由人观看成片。

OpenMontage 成片验收边界图,render output 依次经过 technical probe、visual spotcheck、audio spotcheck、subtitle check 与 promise preservation,只有 pass 才进入 publish 和 final.mp4 交付
provider 成功、renderer 生成文件、final review 通过、用户允许发布,是四个不同状态。

6.3 四种“成功”必须分开

一次 provider 调用成功,只说明它返回了结果;render 成功,只说明输出文件存在且通过了合成入口的技术流程。 final_reviewpass 才表示技术探测、开中结尾抽帧、音频、字幕和承诺保持都没有阻断问题; 最后的 publish gate 还要等待用户决定是否采用并发布这份成片。

状态已经证明什么还不能声称什么
工具返回 success某次素材或处理调用产生了结果。整支视频已经成立。
Render output 存在时间线被执行,得到可探测的媒体文件。画面、声音、字幕与批准承诺都合格。
Final review pass真实输出通过了规定的自动取证与规则检查。语义质量完美,或用户已经采用并允许发布。
Publish gate completed用户批准交付,发布工件也已形成。外部平台永远不会转码、拒绝或下架。

这里还有一个值得保留的实现细节:schema 把 final review 分成 passrevisefail; 其中 revise 表示发现可修问题,按 agent contract 也不能包装成完成品,而 fail 会让当前 ToolResult 直接失败。 所以“代码自动阻断”与“agent 必须遵守 review 结论”仍然是两层约束,不能因为都叫 final review 就混为一谈。

七、这套设计真正聪明的地方:把判断放在正确的一侧

判断更适合放在哪里原因
哪个创意更适合这个题目与观众Agent + proposal director + 用户。需要语义、品味与协商,难以写成稳定 Python 分支。
提案至少包含哪些字段JSON Schema。结构完整性可以确定验证。
当前机器有哪些视频 providerToolRegistry。这是运行时事实,不能靠文档猜。
一个人工 gate 能否被标记完成Checkpoint utility。不能把不可越过的边界只写成建议。
素材风格是否一致Reviewer skill + 人。需要对作品做语义与视觉判断。
MP4 是否有音轨、时长与字幕证据Renderer + final review artifact。可以对真实产物取证。

这也解释了为什么 OpenMontage 不是“用 Markdown 代替代码”。它真正做的是把约束分级: 创作方法保留为可以进化的指令,结构合同放进 schema,环境事实交给 registry,越权风险交给 Python 拒绝, 最后再把真实输出送回 review。优雅之处不在于少写了一个 orchestrator class,而在于每种责任都有合适的证据形状。

7.1 为什么不能把所有约束都塞进同一种媒介

如果一切都写进 prompt,创作方法很好改,但字段完整、审批和写盘安全只能依赖模型自觉;如果一切都写成 Python 状态机, 结构更硬,却会把“这个镜头是否诚实”“三个方案是否真的不同”也伪装成确定分支。OpenMontage 的分层标准不是“能不能编码”, 而是“这个判断需要哪一种证据,失败时谁有权阻断”。

这也给普通 agent 项目一个检查方法:凡是会花钱、产生不可逆副作用、越过人工授权或改变用户承诺的动作,都不应只靠文字提醒; 凡是依赖语义、品味和上下文协商的判断,也不应为了看起来工程化而硬塞进布尔条件。中间的 artifact 让两侧可以交接: agent 给出有理由的选择,schema 与工具验证它能验证的部分,人再决定是否采用。

八、与 Temporal 的边界:文件恢复不是持久化执行

最关键的结论。 OpenMontage 当前快照没有使用 Temporal,也没有展示一个跨进程自动调度 stage、重放控制流、分发 task queue 的服务。 它的 checkpoint 让项目工作可恢复、可审计;Temporal 的 Event History 让执行控制流可由新的 Worker 重建。

8.1 同一次中断,两套系统各自记住什么

23:14  scene-04 素材生成完成
23:15  Agent 进程退出,下一场景尚未调用 provider

OpenMontage 留下:
  checkpoint_assets.json = in_progress
  partial_progress.completed_scene_ids = [scene-01 ... scene-04]
  已生成素材与选择理由

Temporal 留下:
  Workflow Event History 中已经调度、开始、完成或等待的事件
  Service 继续拥有 Workflow Execution
  新 Worker 可由历史重建控制流并继续取任务

OpenMontage 的下一会话要主动打开项目、解释 checkpoint,然后决定如何继续;没有会话时,文件不会自己调度 scene-05。 Temporal 则让 Workflow 在调用方和 Worker 都不在线时仍由 Service 持有,并在 Worker 回来后重建执行。 反过来,Temporal 的历史不会自动替视频团队保存“为什么选方案 B”“scene-04 的美术意图是什么”;这些仍属于业务 artifact 与 decision log。

故障或暂停OpenMontage 文件协议能提供什么Temporal durable execution 额外提供什么
Agent 会话结束下一会话读取 checkpoint、artifacts 与 decision log,继续下一个未完成阶段。Workflow Execution 独立于任何会话持续存在。
素材做到 scene-04 时失败in_progress partial progress 可记录已完成 scene,agent 据此跳过。Activity attempt、retry、timer 与完成事实由服务保存并重新调度。
人工批准等待一夜awaiting_human 文件保留关口,下一会话重新展示。Workflow 可持久等待消息,不需要业务自己唤醒会话并解释文件。
同一个外部生成调用回执丢失仍需 tool/provider 的幂等键与业务账本。Activity 也仍需外部幂等,但重试与执行历史由 durable runtime 管理。

8.2 组合时要先选择 durable unit

两者最自然的组合不是把 OpenMontage 的 director 改写成 Temporal 内部 DSL,而是保持层次: Temporal 或其他 durable controller 拥有整次制作的业务生命;OpenMontage agent 在某个 Workflow/Activity 边界内完成一段生产语义; provider 与对象存储拥有外部副作用和大文件。具体要把 durable unit 设成整支制作、一个 stage,还是一个昂贵 scene, 取决于重复成本、人工等待与可观测要求。

如果整支制作只是一个 Activity,接入简单,但九十秒解释视频的内部进度对 durable runtime 几乎不可见;如果每个 scene 都变成 Activity, 重试和并发更细,却要处理更多幂等键、媒体引用和状态转换。常见折中是 Workflow 持有阶段与人工等待,Activity 执行一次可重试的工具或生产单元, OpenMontage artifact 作为业务输入输出,checkpoint 继续服务 agent 恢复与人工审计。这个组合是设计建议,不是当前 OpenMontage 快照已经实现的主路径。

九、从 OpenMontage 带走的七条规则

  1. 先选择生产合同,再调用生成工具。入口必须落到 manifest,而不是临时脚本。
  2. 每次阶段交接都交 artifact。下游依赖结构化工件,不依赖长对话里的隐含记忆。
  3. 把人类批准写成状态,并在代码层拒绝越权。“记得问一下”不算 gate。
  4. 创意承诺必须建立在 preflight 的真实能力上。provider 与 runtime 是运行时事实。
  5. 重要决定保存备选项与理由。只记录最后选择,无法审计 fallback 是否改变了产品。
  6. 渲染器失败不能静默改变作品。runtime、composition mode 与 delivery promise 要分别锁定。
  7. 把生成、验证、采用、发布拆成不同状态。文件存在只是验收链的开始。

9.1 用一口气复述完整流程

用户带着题目、受众和目标平台来,agent 先选择 pipeline 并做 preflight;研究工件把题目变成有依据的角度,proposal 把角度、成本、provider、 runtime 与交付承诺摆给用户选择。批准后,script 把选中方向写成有时间的语言,scene plan 把语言变成可生产镜头, asset manifest 记录每个真实素材的来源、路径与成本,edit decisions 用 asset ID 组成时间线。renderer 只能按批准过的 runtime 执行; checkpoint 在每个关口保存状态,decision log 保存为什么;final review 检查真正生成的 MP4,publish gate 最后才允许交付。

如果在这条复述里删掉任何一环,都能说出一个具体代价:没有 preflight,创意可能建立在不存在的能力上;没有 artifact,阶段交接依赖聊天记忆; 没有 checkpoint,中断后不知道做到哪里;没有 decision log,fallback 只剩结果没有理由;没有 final review,可播放文件会冒充可交付作品。 这就是 OpenMontage 比“长一点的 prompt”多出来的系统性。

OpenMontage 最值得看的,不是它能连多少个模型,而是它给 coding agent 一间有账本、有工序、有门禁、有工具盘点、有验片室的工作室。 这套结构仍然依赖 agent 忠实执行指令,也还没有替代 durable runtime;但它把“做视频”从一次不可追溯的模型行为, 推进成了一条人和 agent 都能检查、暂停、恢复与修正的生产路线。

参考源码