Hypit 的流程不是「生成视频」,而是「编译视频」:源文件先被规划成不可变的构建定义,再由 Runtime 逐个满足需求,最后由 Provider 渲染。理解这一点,整套命令与目录结构才自洽。
这篇拆解六个阶段、三类源文件的分工、唯一会花钱的 build 命令,以及靠 build-record 显式复用的增量机制——包括它没有隐式缓存这个容易踩的坑。
最后是一个意外发现:Hypit 的本地渲染器并不自研,它直接依赖 HeyGen 开源的 HyperFrames 引擎,而且锁在了一个落后的版本上。
一句话:Hypit 的流程不是「生成视频」,而是「编译视频」。
它把视频当成一个有依赖图的编译产物:源码(.svml / .svs / .svrun)先被规划成一张不可变的构建定义,再由 Runtime 逐个满足其中的「需求」,最后由 Provider 去渲染和生成。理解这一点,整套命令、目录结构和「改一句台词只重跑一部分」的能力就全都自洽了。
以下每条都标了来源:源码指仓库文件可查证(给出路径),文档指项目自带说明,实测指我亲自跑过或复核过。这是上一篇拆解的续篇,那篇讲「它是什么」,这篇讲「它怎么跑」。
六个阶段,责任方各不相同
最容易误解的一点:coding agent 不负责渲染,也不负责生成画面。它只负责「写源码 + 决定用哪些服务 + 提交工作」。真正的执行在 Runtime 和 Provider 里。
| 阶段 | 责任方 | 输入 | 输出 | 落盘位置 |
|---|---|---|---|---|
| 1 参考解析 | agent + Skill | 参考视频 / 一句话需求 | 对参考的理解、镜头与节奏判断 | 项目笔记 |
| 2 编写 Source | agent | 上述理解 + 素材 | .svml / .svs / .svrun | 项目目录 |
| 3 规划 | Core | Source 闭包 | 不可变 BuildDefinition(含 Need 列表) | 内存 / Runtime |
| 4 满足 Need | Runtime + Provider | 单个 Need | 素材 / 计时 / 组件实例 | .hypit/runtimes/local |
| 5 渲染合成 | 本地 Provider | 合成图 + 时间轴 | 帧序列 → 编码视频 | 同上 |
| 6 导出 | CLI | Build id + 输出名 | 成片文件 | 你指定的路径 |
阶段 3–6 的产物统一记在 Build Result 里,项目内落在 .hypit/results(源码:packages/video-cli/src/distribution.ts:19),运行时数据在 .hypit/runtimes/local(同文件 :28)。实测
为什么要把视频拆成三个文件
这不是为了好看,是为了让「改一处」不牵动全局。
| 文件 | 声明什么 | 改它的后果 |
|---|---|---|
.svml | 内容与结构:Script 台词、画面组件、轨道、合成图 | 牵动依赖它的组件,但已产出的素材可复用 |
.svs | 外观参数表:命名 Recipe(布局、锚点、字号、动效) | 只改外观,不动内容,可单独重渲染 |
.svrun | 这次要产出什么、复用什么 | 不改变作品本身,只改变本次执行范围 |
一个真实的 .svrun 只有三行——实测(examples/interview/swap-host.svrun):
<?svml using="@hypit/run-markup@1"?>
<svrun version="1">
<author source="./swap-host.svml"/>
<target output="final.video"/>
</svrun>
而 .svs 是键值式的命名配方表,例如字幕框的位置与对齐都是一等参数——实测(examples/interview/recipes.svs):
caption.boy {
stack-order: 70;
x: 0.5; y: 0.5;
width: 0.88; height: 0.22;
anchor-x: center; anchor-y: center;
align: center;
}
命令级流程:只有 build 会花钱
官方文档对边界说得很直白(docs/quickstart/run.md):“Only build submits work.”——其他命令要么只读、要么只做诊断。文档
hypit runtime use hypit.runtime.json # 选定执行环境(一次)
hypit plan build.svrun # 只展示将要执行的工作,不执行
hypit build build.svrun --follow # 提交构建(这一步才花钱)
hypit get <build-id> --output final.video --to output/final.mp4
围绕这四步的辅助命令(packages/cli/src/output.ts:983-1022,源码):
check校验单个 Source 自洽;pricing读当前 Provider 的价目(只读,用来在花钱前算账)。builds/history/status --watch/logs/inspect:从存量 Result 里找东西、看进度、读失败证据。activity/cancel:查活跃构建、撤回构建。doctor:诊断外部环境(我实测过,见文末)。
一个重要的设计细节:提交与观察是解耦的。文档明确说「终端关闭或观察中断,与 Build 内部 Provider 请求失败是两件不同的事」(skills/hypit/references/production/builds.md)——文档。也就是说 --follow 断了不代表构建失败,用 hypit builds 还能把结果找回来。这对长时间渲染很实用。
最省钱的部分:Target、Candidate 与 build-record
这是整套流程里我认为工程价值最高的一节。
Run 源区分两个概念——文档(skills/hypit/references/production/runs.md):
- Target:这次要交付的输出(例如
final.video)。 - Candidate:途中需要、且可以外部提供的输出。
关键在于 Candidate 可以从历史 Build 结果里取:
<author source="./production.svml"/>
<target output="final.video"/>
<build-record id="kept-take" build="bld_..." output="opening-semantic.take"/>
<satisfy output="opening-semantic.take" candidate="kept-take"/>
官方对它的解释是:“changing a title can keep the performance and its timing while producing a new final video”——改标题可以保留已拍好的表演和它的时间,只产出新的成片。文档
但要提醒一个容易误判的点:Hypit 没有隐式缓存。源码里资源 id 是 res_<uuid> 形式,生成物的身份只做规范化(canonicalize),并不参与跨构建的自动命中;想复用就必须由作者在 Run 里显式写 build-record,而且 Forward 不复制字节。源码
含义很直接:省钱的开关是手动的。忘了写 build-record,改一个标题就会把整片重新生成一遍。这是这套设计里最需要养成习惯的地方,也是它相比「自动脏标记」方案的一个真实缺陷。
渲染:按帧区间求值,而不是「导出整片」
渲染也是可切片的——文档(skills/hypit/references/production/rendering.md):
<render:Video id="review" composition={main.composition} timeline={speech.timeline}
start-frame="240" end-frame-exclusive="360"/>
两个边界都指向原始节目的帧时钟,且必须在帧边界结束:240 到 360(不含)即 120 帧,30fps 下是第 8–12 秒。只渲染一个区间时,帧截图与源帧提取的开销都变小——这对「只看一眼第 8 秒」的审片场景很实用。
并发不是写死的。Provider 在自己的预留上限内按当前任务动态调整截图并发(文档原话:“the Provider adjusts capture concurrency within its reserved ceiling using the current job's work”),上限公式在 packages/provider-hyperframes-local/src/concurrency.ts:9-14:min(CPU-2, 内存/2/1.5GiB)——源码(上一篇文章里我证伪了「64 个 Chromium」,那正是这个公式在作者大机器上的取值)。
字幕、口播、动效各自怎么走
字幕:词锚点 → 计时 → 排版 → 呈现
链路是:Script 里的词级锚点(@name / @name! 等)→ 对齐产生时间 → 字幕包负责排版 → 屏幕呈现。相关包按职责分层:script(解析台词与锚点)、temporal / timeline-author(时间轴)、caption 与 caption-fine(常规与精细字幕)、whisperx(词级对齐)、fonts-open(字体)。源码
字幕的呈现参数走 Recipe,而不是内联写在内容里——上面 caption.boy 那段就是证据:字幕框的归一化坐标与对齐方式是可复用、可换主题的数据。
口播:A-roll 提供表演与时间
文档把 A-roll 定义为「提供一段口播表演及其本地计时的素材」(“A-roll is the performance supplying a spoken passage and its local timing”),并强调它的画面可以切走、可以缩进画中画,而声音与语义角色继续存在——文档(skills/hypit/SKILL.md)。语音生成走 Provider(内置厂商映射里包含 fishaudio、elevenlabs 等)。
动效:由词锚点事件触发,而非按秒触发
这是「词锚定」真正的消费侧。上一篇文章里我逐行验证过:@manifest! 一个锚点被音效、图标、闪光三处消费,三处都没有秒数。改台词、换语言,这三处跟着词走。实测
动效本身由 Recipe 的 appearance / motion 参数驱动(例如 recipes.motion.card),属于「声明式」而非「脚本式」动画。源码
它的本地渲染器,其实是别人的引擎
这是我做完流程拆解后最意外的一条发现,也修正了「Hyperframes 是 Hypit 内部件」的直觉。
Hypit 有四个名字里带 hyperframes 的包,很容易误以为是自研全家桶。实际是三层自研 + 一层上游引擎:
| 包 | 角色 | 归属 |
|---|---|---|
@hypit/hyperframes | 把 Composition 编译成 Hyperframes 文档 | Hypit 自研 |
@hypit/render-hyperframes | Surface / 能力层(自述「is not a renderer」) | Hypit 自研 |
@hypit/provider-hyperframes-local | 本地执行层 | Hypit 自研 |
hyperframes · @hyperframes/engine · @hyperframes/producer | 真正干活的渲染引擎 | HeyGen 开源 |
证据是硬的——Hypit 的本地 Provider 直接依赖外部包(packages/provider-hyperframes-local/package.json,实测):
"hyperframes": "0.7.101"
"@hyperframes/engine": "0.7.101"
"@hyperframes/producer": "0.7.101"
并且源码直接从上游引擎导入捕获会话:import { getCdpSession } from "@hyperframes/engine"(src/opaque-capture.ts:2,实测)。也就是说,Hypit 阶段 5 的渲染能力建构在 HeyGen 的 HyperFrames 之上。
上游是谁
我一手核实了这个项目(实测,2026-09-16):
- 仓库
heygen-com/hyperframes,Apache-2.0 许可,50,496 星 / 4,607 fork,2026-03-10 创建,当日仍在推送。 - 自我描述只有一句:“Write HTML. Render video. Built for agents.”
- npm 包
hyperframes最新 0.8.41,维护者邮箱vance@heygen.com(HeyGen 官方)。
0.7.101,上游已到 0.8.41——想自己玩这套渲染,直接对着 HeyGen 的引擎更划算。② 许可落差很大:HyperFrames 是干净的 Apache-2.0,而 Hypit 自身的修改版 Apache 禁止多租户与商业再分发。同样想「HTML 渲染成视频」,走上游没有这些限制。下一篇会把这个对比算清楚。
这套设计的优点与代价
| 设计 | 好处 | 代价 |
|---|---|---|
| 视频即编译产物 | 可切片、可复用、可校验 | 概念多(Run/Target/Candidate/Need/Build),上手陡 |
| 内容 / 外观 / 执行范围三分 | 改样式不重生成素材 | 要维护三类源文件与 Recipe 表 |
| 词锚定时间 | 改稿不用重对时间轴 | 语义锚点无法表达纯装饰性动效 |
| Provider 抽象 | 可换模型 / 可本地 / 可 BYOK | 接入成本高,无 *_API_KEY 约定 |
| 组件包按精确版本装 | 可复现 | 首次上手要逐个装,报错不告诉你装哪个 |
这是一套「为可维护性付费」的设计:它用更高的概念成本,换来了「改一处不用重做全片」。如果你只做一次性的视频,这套抽象是负担;如果你要做成百上千条变体,它是目前我见过最认真的回答。
顺带一提,它的 Studio 也只能在构建之后介入:hypit studio --run <build.svrun>——实测(hypit --help)。也就是说预览的对象是「已产出的构建」,不是「正在编辑的工程」。这是编译式架构的必然结果。
我没能验证的部分
- 没有跑过真实构建。本机的
hypit doctor只报0 diagnostics,但前提是先选 Runtime Profile;缺 ffmpeg 与付费模型,所以阶段 3–6 我只有源码与文档证据,没有端到端验收。实测 doctor有个小坑:未选 Profile 时它仍报「No problems found」,因为它只检查已选中的 Profile。别把它当成环境完整的证明。实测- 「Need」的完整类型清单我只在文档与包结构中确认了存在与职责,没有穷举。
- Build Result 清单文件的精确字段没有逐个核对。
下一篇我会把这三条主线拆开,和 Remotion、Hyperframes 等工具放在一起,研究一套通用、可落地的自动化剪辑流水线。