六个维度、0-5 分、四档判定:OpenMontage 怎么防「会动的 PPT」
让一个 agent 「做条视频」,最常见的翻车不是报错,而是它交出来一串静态画面配上淡入淡出,看起来什么都对,就是不像视频。这种输出很难用一句「不好」去驳回——每一帧都合规,问题在整体。
OpenMontage 把这个失败模式单独抽成了一个模块:lib/slideshow_risk.py。它不是文档里的一段建议,是一个有维度、有分值、有判定门槛的打分函数。这篇就只讲这一个文件,以及它在流水线里卡在哪。
先看 docstring 原文
模块顶部的 docstring 把规则全写在明面上了,照抄如下:
Scores a video plan across 6 dimensions that reliably predict whether
the output will feel like a slideshow rather than directed video.
Each dimension is scored 0-5 (lower is better):
- repetition: same layouts/backgrounds/scene grammar recurring
- decorative_visuals: scenes decorate instead of communicate
- weak_motion: motion exists but has no narrative purpose
- weak_shot_intent: no explicit reason for framing or reveal rhythm
- typography_overreliance: too much of the video is text-first
- unsupported_cinematic_claims: cinematic label without structure
Verdict:
< 2.0: strong
< 3.0: acceptable
< 4.0: revise
>= 4.0: fail — should not proceed to compose
三件事一次说清:六个维度、每个维度 0-5 分、分越低越好,然后是四档判定。
「分越低越好」这一点值得先停一下。多数人看到 0-5 会下意识按「分越高越棒」去读,而这里打的是风险分——它衡量的是「这个方案有多像幻灯片」,不是「这个方案有多好」。看别人贴出来的分数时,方向搞反就会得出完全相反的结论。
六个维度分别在问什么
按 docstring 的字面译一遍,顺便说说每一条对应的是哪种常见毛病:
repetition(重复):相同的布局、背景、场景语法反复出现。也就是整条片子只有一套版式,换的只是里面的字。decorative_visuals(装饰性视觉):场景在装饰,而不是在传达。画面很满,但拿掉之后信息一点不少。weak_motion(弱动效):有动效,但没有叙事目的。注意它不等于「动得少」——缓慢推镜、粒子飘浮这类东西照样是动效,只是不承担叙事。weak_shot_intent(弱镜头意图):构图或揭示节奏没有明确理由。为什么这里是特写、为什么这个信息要留到第二段才出现,说不出来就是这一维在扣分。typography_overreliance(过度依赖排版):视频里太多东西是文字优先的。这一条几乎就是「会动的 PPT」的字面定义。unsupported_cinematic_claims(无支撑的电影感宣称):贴了 cinematic 标签,却没有相应的结构。
要补一句边界:这六个维度在文件里各自对应一个私有打分函数(_score_repetition、_score_decorative、_score_weak_motion、_score_weak_intent、_score_typography、_score_cinematic_claims)。这些函数的具体打分逻辑我们没有读过,所以本文只说每个维度在问什么问题,不说它凭什么给出 3 分还是 4 分。看到别处把这部分讲得很细的,建议直接回去读源码。
四档判定怎么读
四个门槛是 < 2.0 strong、< 3.0 acceptable、< 4.0 revise、>= 4.0 fail,最后一档后面还跟着一句 should not proceed to compose——判失败时不应进入合成阶段。
average 是六个维度分数的算术平均,返回时 round(average, 2)。这个实现细节直接决定了怎么解读一个分数:既然是平均,单独一个维度打满 5 分并不必然把整体推进 fail 档——按算术,只有一维 5 分、其余全 0,平均也只有约 0.83,仍落在 strong 里。反过来,六个维度各自平平无奇地拿 4 分,谁也不算「严重」,平均却是 4.0,直接判失败。
这不是设计好坏的问题,而是使用姿势的问题:这套判定拦的是「整体气质」,不是「单点事故」。 如果你真正担心的是某一个具体维度(比如全片文字优先),那就得去看返回结构里那一维的分,而不是只看总分。
空场景直接 5.0 fail
一个容易忽略但很能说明设计取向的边界:当传进去的 scenes 为空时,函数不做任何计算,直接返回 average: 5.0、verdict: "fail"、dimensions: {}。
也就是说,没有场景等于满分风险、直接判失败。这条对接自动化流程的人要留意:一个空计划不会得到「无法评估」这类中间态,它拿到的是最差档,因此也就进不了合成。
签名与返回结构
def score_slideshow_risk(
scenes: list[dict[str, Any]],
edit_decisions: dict[str, Any] | None = None,
renderer_family: str | None = None,
render_runtime: str | None = None,
) -> dict[str, Any]:
返回:
{
"average": float,
"verdict": str,
"dimensions": {dimension_name: {"score": float, "reason": str}},
"render_runtime": str | None,
}
有两处值得注意。
一是 dimensions 里每个维度带的不只是 score,还有一个 reason 字符串。这意味着这个分数在设计上是要被人读的——你能看到系统认为哪一维出了问题,而不是只拿到一个总分。
二是签名里的四个入参:场景列表、剪辑决策、渲染器 family、渲染运行时。没有交付承诺类型这一项。这一点下一节还会用到。
render_runtime 那段注释:为审计留的冗余参数
render_runtime 参数的 docstring 里有一段挺坦白的话,原文是:
Scoring logic is currently runtime-neutral — a text-heavy HyperFrames composition is just as slideshow-y as a text-heavy Remotion one — but the parameter is part of the contract so callers record runtime alongside family.
翻过来:评分逻辑当前对运行时是中立的——文字很重的 HyperFrames 合成,和文字很重的 Remotion 合成一样幻灯片化;但这个参数仍然是契约的一部分,好让调用方把 runtime 和 family 一起记录下来。
一个当前不参与计算的参数,因为审计需要而保留在签名里,并且在返回值里原样带出来。这条注释本身就是一处可核查的痕迹:它把「这个参数为什么在这儿」写进了源码,而不是留给读代码的人去猜。至于它在别处怎么被消费,那部分调用代码我们没有读,不做推断。
它卡在流水线的哪一步
README 的 Production Governance 一节把质量闸列成几条,其中 Pre-compose validation(合成前校验)明确写了三种阻断情形:违反交付承诺时阻断、幻灯片风险分为 critical 时阻断、渲染器 family 缺失时阻断,README 对这一步的说法是在浪费 GPU 时间之前抓住坏掉的计划。另一条 Slideshow risk scoring 则把六维分析概括为防止 README 所描述的「会动的 PowerPoint」式输出。
这里有一处措辞差异可以如实记下来:README 这一行用的词是 critical,而 lib/slideshow_risk.py 的 docstring 里四档判定的最差一档叫 fail。两处措辞不同,以仓库当前状态为准,我们不推断原因。
和交付承诺是两把尺子
同一批治理模块里还有 lib/delivery_promise.py,管的是「当初答应交付的是什么」。这两件事经常被混着说,但看一行就清楚它们不重叠。
PROMISE_RULES 里 motion_led 那一行是:still_fallback_allowed 为 False、requires_video_generation 为 True、min_motion_ratio 为 0.7,源码内联注释写的是至少 70% 的镜头必须是真实动效——视频或动画,不是 Remotion 幻灯片。而另有四种承诺类型(data_explainer、teacher_explainer、screen_demo、localization)的 min_motion_ratio 是 0.0,在规则层面完全允许零动效。(这张表共八种承诺类型,我们另有一篇专门逐行讲,这里只取和本文直接相关的这几行。)
分工由此就明确了:min_motion_ratio 管的是「有没有兑现动效比例的承诺」,是一个可核对的比例;幻灯片风险分管的是「成片整体像不像幻灯片」,是一个六维平均。 前面提过,score_slideshow_risk 的签名里并没有承诺类型这个入参——所以别指望「我这条本来就是教学片、承诺允许零动效」这件事会自动折抵幻灯片风险分。两把尺子各量各的。
一处「文档和代码对得上」的例子
本批看的三个仓库里,README 和实际代码对不上的地方不少。但幻灯片风险这一块是反过来的:README 说的是 6 个维度,lib/slideshow_risk.py 的 docstring 也是 6 个维度,而且比 README 更具体——README 没有写出 0-5 的分值区间,也没有写 2.0 / 3.0 / 4.0 这三个门槛。
对照着说,同一份 README 的架构图里 schemas/ 那行写的是 15 个 JSON Schema,而我们实读 schemas/ 目录数出 24 个 .json;TTS provider 一处写 5、架构图里写 4。两处不一致,以仓库当前状态为准,说到这里为止,我们不推断原因,也不拿它去评价这个项目。
可以看出的规律只是:对不上的多是计数类描述,核心治理数值(评分权重、预算默认值、幻灯片六维阈值)是逐项吻合的。 你要引用这个项目的数字,优先引后面这一类。
你能拿它做什么
最后说使用姿势。这六个维度即使不跑代码,也是一份质量相当高的审片提问清单:这条片子是不是只有一套版式换字;这些画面拿掉会不会少信息;动效有没有叙事职责;每个镜头说得出为什么是这个构图和这个揭示顺序;是不是文字优先;有没有嘴上说 cinematic 而结构上没有。
但要说清两件事。第一,这是一个打给系统看的风险分,写在代码里的是维度、分值和门槛,不是「跑一遍就不会做出幻灯片」的保证——README 自己把 proposal、script、scene plan、生成素材和 publish 这几道人类审批闸描述为强制而非建议,分数是给这些闸提供依据的输入,不是替代品。第二,我们没有安装或运行过这套系统,也没有让它给任何一个方案打过分,上面全部内容来自源码与文档的字面。
本文依据 OpenMontage 官方仓库(github.com/calesthio/OpenMontage)的 README、
AGENT_GUIDE.md、config.yaml、pipeline_defs/ 与 lib/ 下的治理模块整理,核对日 2026-08-09。
本文内容为仓库源码与文档口径,我们没有安装或运行过该系统,也没有调用过其中任何一个 provider API,
文中出现的成本数字均为项目方在 README 中自行标注的金额,非我们的实测结果。
该项目以 AGPL-3.0 发布,部分流水线在 manifest 中自标 stability: beta,请以仓库最新内容为准。