keepwatching 是做什么的?
一个命名的短视频格式数据库,并且可以渲染。每个格式包含三个文件:一份渲染规范、一份陈述的假设以及测量数据——即使“尚无数据”。
使此技能有价值的规则:绝不虚构留存率数字。 每个关于格式性能的声明都来自 formats/<slug>/data.yml,并且每个声明都带有 n。如果 n: 0,诚实的回答是“未测试”——请如实说明。
设置——每次会话首先执行
此技能驱动一个实际的渲染器,该渲染器位于 keepwatching 仓库中。仅凭指令无法渲染任何内容,因此第一步始终是定位或安装该仓库。
# 1. 找到它。优先匹配第一个;如果都不存在,询问用户。
KW="${KEEPWATCHING_HOME:-}"
[ -z "$KW" ] && [ -d ./keepwatching ] && KW="$PWD/keepwatching"
[ -z "$KW" ] && [ -d "$HOME/keepwatching" ] && KW="$HOME/keepwatching"
[ -z "$KW" ] && { git clone https://github.com/msertdev/keepwatching "$HOME/keepwatching" && KW="$HOME/keepwatching"; }
cd "$KW"
# 2. 一次性安装。可安全重复运行;如果已完成则会自动跳过。
npm install && npm run setup
# 3. 在执行其他操作前确认库文件完整。
npx kw check
24 个画廊预览已提交,因此 npx kw gallery 可在几秒内显示页面,无需渲染任何内容。npm run setup 仅在渲染视频时需要。如果你更改了格式的 scene,其已提交的预览将过时——请使用 npx kw gallery --no-serve && git add site/previews 重新渲染并提交,否则 CI 的 kw previews check 将会失败。
kw check 显示 no problems 表示你已准备就绪。如果报告缺少字体,请再次运行 npm run setup——合成器拒绝使用后备字体渲染,因为替换的字体将无声地改变仓库曾测量过的每一帧。
两个实用说明。
- 在大多数代理框架中,Shell 变量在工具调用之间不会持久保存。 要么在每个命令中重新计算
KW,要么在每个命令前明确使用cd /absolute/path/to/keepwatching &&。不要假定$KW会存活。 npm run setup在冷机器上可能需要几分钟。 它会下载 Inter(约 30 MB)和无头 Chromium(约 150 MB)。在已经缓存两者的机器上,它会在几秒内完成。告知用户正在发生哪种情况,而不是看起来像卡住了。
此文件中的每条命令都假定仓库根目录是工作目录。 如果用户在其他项目中工作,请在仓库内渲染并将 MP4 复制回来;kw 无法在外部运行。
还有 npm run check,即 kw check 加上 tsc --noEmit。如果你曾编辑过 engine/ 下的任何内容,请使用此命令。
你可能被要求做的四件事
1. 为某个主题选择一个格式
npx kw list # 每个格式,附带其样本量和实测留存率
阅读 formats/<slug>/meta.yml 中的 hypothesis、useWhen 和 avoidWhen。将内容的形状与格式族匹配,而非主题。
下表中的每个格式当前都是
n = 0。 这些是基于陈述假设的起点,而非基于证据的胜出者。当你推荐一个时,请说明这一点。下表告诉你哪个格式声称适合某种形状的内容;它不会告诉你哪个表现更好。
| 内容类型… | 格式族 | 从…开始推荐 |
|---|---|---|
| 一个大数字 | stat-counter | stat-counter-rise |
| 值得等待的回报 | countdown | countdown-clock |
| 一组有序项 | ranking | ranking-suspense(完整度)或 ranking-reveal(分享) |
| 仅在数字上不同的两个选项 | comparison | split-compare |
| 两种状态下的同一主题 | reveal | before-after |
| 广为流传的虚假信念 | myth-fact | myth-vs-fact |
| 一句有力的陈述 | cold-open | cold-open-line |
| 具有真实阶段的过程 | progress | progress-bar |
| 真正逐步升级的值 | escalation | escalation-ladder |
| 循环的内容 | reverse | loop-seam |
当你推荐一个时,请引用它的 n。例如“ranking-suspense,尽管在此仓库中未测试(n=0)”是一个好的推荐。而“ranking-suspense 获得 68% 的留存率”则是编造的,除非 data.yml 中有此数据。
2. 渲染一个视频
复制一个格式,填充 data 块,然后渲染:
npx kw new my-clip --from=stat-counter-rise
# 编辑 formats/my-clip/format.json -> "data": { ... }
npx kw render my-clip # -> out/my-clip/master.mp4
# 可选:先在浏览器中预览,无需编码任何内容。
# 会阻塞直到你停止它,因此在无人值守运行时跳过。
npx kw preview my-clip
除非布局确实需要更改,否则只编辑 data。scene 数组就是格式;更改它会使该片段变成不同的格式,其测量数据也不再可转移。如果你确实更改了 scene,请升级 format.json 中的 version——变体 ID 由此派生。
更改时长——请阅读此内容,这是最常见的请求。
库内格式运行在 8–30 秒之间(24 个中有 21 个在 8 到 14 秒之间)。三个 30 秒的格式——cold-open-question、countdown-clock、stat-counter-rise——是进行中实验的层级,而非默认时长。任何格式尚未具有的时长需要编辑 scene,因此“只编辑 data”不适用。使用此规则:
将
canvas.durationSec和 scene 中的每个时间点乘以newDuration / oldDuration。然后查看结尾:缩放会使最终保持的画面按相同比例拉长,因此缩放因子越大,结尾越糟糕。
实际示例——stat-counter-rise 从 10 秒改为 30 秒,因子 3.0。以下是实际发布的数据,而非示例:
| 字段 | 10秒 | ×3 | 实际发布值 |
|---|---|---|---|
canvas.durationSec | 10 | 30 | 30 |
计数器 endSec | 6.4 | 19.2 | 21.0 |
柱状条 endSec | 6.4 | 19.2 | 21.0 |
unit at | 6.4 | 19.2 | 21.0 |
context at / in.at | 6.5 | 19.5 | 21.6 |
source at | — | — | 25.5 — 一个原先不存在的节拍 |
posterSec | 8.4 | 25.2 | 27.5 |
仅缩放导致最后一个节拍在 30 秒的片段中位于 19.5 秒:冻结卡牌持续 10.5 秒。 将其推至 21.6 秒仍然不够。当因子大约超过 2 时,算术不再是答案——该片段需要一个它原本没有的节拍。这里,在 25.5 秒处添加了一条引用行,该格式在 sources 中一直声明此内容但从未在屏幕上显示。最终保持:4 秒。
最终卡牌保持超过大约 3 秒就是死气沉沉,而结尾的死气沉沉是毁灭完成率的原因。kw frame0 在屏幕静止 5 秒时直接失败:3 秒是工艺建议,5 秒是硬性下限。它发现了三个已发布的格式,其结尾冻结了 6 秒。
约束和检查:
durationSec × fps必须是整数帧数。- 每当更改 scene 时,升级
format.json中的version——重新计时也算。一个刚搭建于0.1.0的脚手架,如果你重新计时,将变为0.2.0。变体 ID 由version加上规范的哈希值派生,这保证了未来的测量数据可归因。 npx kw check会捕获超出片段结尾的时间点,而npx kw frame0会捕获未绘制任何内容的首帧以及任何超过 5 秒的无运动运行。两者都无法告诉你一个片段是否枯燥——为此,请在渲染后打开out/<slug>/contact.png。它会均匀地在片段中采样六帧,因此近乎相同的图块一目了然。
来源。 kw new --from=<slug> 故意将脚手架重置为 sampleContent: placeholder 并丢弃父级的 sources——父级引用支持的是父级的数据,而不是你的。如果你在屏幕上放置了真实数字,请设置 sampleContent: sourced 并添加一个 sources: 条目,包含一个 URL 和它支持的确切声明。
一个有来源的数字只有在它展示时才是有来源的。 如果你将计数器动画到被引用的数字,请标记为 claim: "final",并且在 endSec 之前保持单位和任何诸如“正好”之类的词不显示——直到那时显示“计数中…”。计数期间可见的每个其他元素都需要 neutralWhileCounting: true。否则 kw check 会拒绝该格式。中间值真实正确(如“到目前为止的帧数”)的计数器标记为 claim: "running" 并豁免。
什么算作来源:读者可以打开并核对该数字的内容——原始论文或其永久记录(DOI、PubMed)、标准机构、统计机构、官方报告。不是列表文、不是总结这些内容的博客、也不是你自己的回忆。在 claim 字段中声明你为屏幕所做的任何四舍五入。kw check 验证 URL 和声明存在;它无法验证它们是否正确,因此这部分取决于你。如果你找不到来源,请改用填充文本——编造一个统计数据与编造留存率数字是同样的失败。
完整的 format.json 参考请参见 references/spec.md。
3. 进行 A/B 测试
两种格式,相同内容,同一天:
npx kw new test-a --from=cold-open-line
npx kw new test-b --from=redacted-reveal
# 在两个数据块中放入相同的副本
npx kw render test-a && npx kw render test-b
npx kw variant test-a # 记录此字符串到上传文件中
out/<slug>/variant.json 中的 variantId 是使结果可归因的关键。在上传时将其记录到已发布的视频中——如果你丢失了笔记,它也会写入 MP4 的元数据注释中。
4. 回读测量数据
# 从 YouTube Studio / TikTok 分析导出 CSV 到 measure/inbox/
npx kw measure # 导入 + 报告 -> measure/report.md
npx kw measure apply # 将结果写入 formats/*/data.yml
npx kw gallery # 重新渲染并重建画廊
无需 API,无需 OAuth。确切的导出步骤和 mapping.csv 格式请参见 references/measuring.md。
确定性是契约
相同的 format.json 在任何机器上永远产生相同的帧。这使得留存率曲线可以归因于格式而非渲染。编辑引擎时,切勿在合成中引入 Date.now()、Math.random()、requestAnimationFrame、CSS 过渡或 CSS 动画。npx kw render <slug> --check-determinism 会乱序重新寻找帧,并在任何像素移动时失败。
两个绝不可混合的测量数据
data.yml 有两个块,它们回答不同的问题:
| 块 | 问题 | 是否对库进行排名? |
|---|---|---|
format | 这种场景结构的表现如何? | 是 |
content_axis | 这种主题内容的表现如何? | 否 |
单个已发布的视频同时带有两个标签——它是一个倒计时并且它是关于身体的。取两者的平均值会产生一个不回答任何问题的数字,因此它们被分开存储、分开报告,并在画廊中以不同颜色显示。
当你报告结果时,请说明是哪一种:
- “
countdown-clock保留了 62%(n=6)”——格式声明。 - “个人/身体主题保留了 68%(n=9)”——轴声明。
- “个人/身体的倒计时保留了 68%”——两者都不是,除非你还说明有多少个视频、采用哪些格式、以及两者是混杂的。
measure/report.md 为每个轴打印一个 carried by 列。如果一个轴仅由一个格式承载,则该轴结果和该格式结果是同一个视频穿着两个标签;如实说明,而不是将它们呈现为两个独立的发现。
内容轴在 data/content-axes.yml 中声明,并通过 measure/mapping.csv 中可选的 content_axis 列附加到视频。它们是用户自己的轴,不是库的轴——添加他们实际测试的任何区别。
variant_id: not-in-library 标记一个已发布的视频,其格式不在本仓库中。它仅是一个内容轴样本:它永远不进入格式面板或任何 data.yml,并且 carried by 会命名它,以便差距可见。仓库级别的轴结果存在于 data/content-axis-results.yml 中,这就是为什么轴的测量可以在每个格式仍然为 n = 0 的情况下进行。不要猜测这种格式是什么——标签故意不提供任何信息。
诚实规则
这些不是样式偏好。如果它们失效,仓库就毫无价值。
n: 0意味着未测试。 切勿将其四舍五入为“有前景”或“很可能”。- n<5 只是一个方向,而不是结果。 说明你正在给出哪一个。
- 表现不佳的格式保留其数字并继续列出。 不要悄悄删除它们。
- 切勿手写
data.yml。 只有kw measure apply才能写入它。从记忆中打出的数字与编造的数字无法区分。 - 切勿将格式数字与轴数字混合。 见上表。
- 屏幕上的每个数字都需要来源。 你生成的片段要么带有
sampleContent: sourced和 URL,要么带有明显的填充文本。为演示编造一个看似合理的统计数据与编造留存率数字是同样的失败。 - 种子数据是一个创作者的一个频道。 它是一个起点,而不是平台的法则。当区别重要时,说明样本是谁的。
参考文件
references/spec.md—format.json元素和动画参考references/formats.md— 格式库,按族分类,包含假设references/measuring.md— CSV 导出、映射和报告循环references/authoring.md— 编写新的值得测量的格式