keepwatching 是做什麼的?
一個可渲染的命名短影音格式資料庫。每個格式由三個檔案組成:一份渲染規範(render spec)、一份陳述假設(stated hypothesis),以及測量結果——包括「尚無結果」的情況。
讓這個技能有用的規則:絕不憑空發明留存數字。 每個關於格式表現的主張都來自 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 個畫廊預覽已經提交(committed),因此 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——合成器拒絕使用後備字型進行渲染,因為替換的字型會默默地改變倉庫曾經測量過的每個影格。
兩個實用注意事項。
- 在大多數代理程式(agent harness)中,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 陣列就是格式本身;更改它會使剪輯變成不同的格式,其測量結果也將不再適用。如果你確實更改了場景,請在 format.json 中增加 version——變體 ID 由此衍生。
更改長度——請閱讀此項,這是最常見的要求。
庫中格式的長度在 8–30 秒 之間(24 個中有 21 個在 8 到 14 秒之間)。三個 30 秒的格式——cold-open-question、countdown-clock、stat-counter-rise——是正在進行的實驗的級別,而非預設長度。任何格式尚未擁有的長度都需要編輯場景,因此「只編輯 data」不適用於此。請使用這個規則:
將
canvas.durationSec以及場景中的每個時間設定乘以newDuration / oldDuration。然後觀察結尾:縮放會以相同倍率拉伸最後的停頓,因此倍率越大,結尾效果越差。
實際範例——將 stat-counter-rise 從 10 秒改為 30 秒,倍率 3.0。以下是實際出貨的數字,而非示例:
| 欄位 | 10 秒 | ×3 | 出貨值 |
|---|---|---|---|
canvas.durationSec | 10 | 30 | 30 |
counter endSec | 6.4 | 19.2 | 21.0 |
bar 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 秒仍然不夠。當倍率超過約兩倍時,算術就不再是答案了——剪輯需要一個它原本沒有的節奏點。這裡是一個在 25.5 秒處的引用行,這個格式一直聲稱在其 sources 中有它,但從未將其放在畫面上。最終停頓:4 秒。
最後一張卡片停留超過約 3 秒就是冷場,而結尾的冷場正是扼殺完成率的原因。kw frame0 會在畫面靜止超過 5 秒時直接失敗:3 秒是專業建議,5 秒是硬底線。它發現有三個格式已經以 6 秒的凍結結尾出貨。
約束與檢查:
durationSec × fps必須是整數影格數。- 每當你更改場景時,請在
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
# 在兩個 data 區塊中放入相同的文案
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 Analytics 匯出 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。
確定性(Determinism)是契約
相同的 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 會為每個軸線列印一個由…承載的欄位。如果一個軸線只曾經由一個格式承載,那麼該軸線結果和該格式結果是同一批影片貼了兩個標籤;請如實說明,而不是將它們作為兩個獨立發現來呈現。
內容軸線在 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,要嘛帶有placeholder和明顯的填充文字。為示範而發明一個看似合理的統計數據,與發明一個留存數字是同樣的失敗。 - 種子數據來自一個創作者的頻道。 它是一個起點,而不是平台的定律。當區別重要時,請說明樣本來自誰。
參考文件
references/spec.md——format.json元素與動畫參考references/formats.md——格式庫,按家族分類,附帶假設references/measuring.md——CSV 匯出、對應與報告循環references/authoring.md——編寫一個值得測量的新格式