| trigger | {"on_intent":["看直播","觀看直播","陪看","陪我看直播","觀戰直播","直播陪看","看直播到","看到幾點","watch stream","stream watch","連續觀看","觀戰模式","看一眼","瞄一眼","加入觀影","陪同觀影","一起看","同樂會","join watch","multi-viewer","companion","ucl-stream-watch"]} |
| name | ucl-stream-watch |
| description | 觀影模式 (Stream Watch) — 陪 Tim 看 ScreenStream 直播畫面流,或不開場只看一眼。
走 `Cmd_StreamWatch` 分步(**prepare** / capture / peek / start / catchup / join / cycle / observe / note),
每一步的回傳檔會告訴你下一步;**沒有 end —— 到期或 Tim 停錄影時由 Cmd 宣布收工並結算**。
跟 ucl-watch-video (看 YouTube 影片抓轉錄稿) 是兩回事 — 本 skill 看的是 Tim 的即時螢幕。
觸發詞 (case-insensitive substring): 看直播 / 觀看直播 / 陪看 / 陪我看直播 / 觀戰直播 / 直播陪看 /
看直播到 / 看到幾點 / watch stream / stream watch / 連續觀看 / 觀戰模式 / 看一眼 / 瞄一眼 /
加入觀影 / 陪同觀影 / 一起看 / 同樂會 / join watch / multi-viewer / companion / /ucl-stream-watch。
|
UCL Stream Watch — 觀影模式
觸發詞就是命令。 本 skill 只教第一步 —— 之後每一步的回傳檔都會指路(## next)。
細節不寫在這裡:寫進 skill 的數字會過期而不會叫,一律讀回傳檔的當下讀數。
兩條鐵律
- 收工不由你判斷。 沒有
step=end;下一次 cycle 自己宣布並結算 ——
到期看的是實錄前緣蓋過 ends_at(牆鐘過了尾段沒補完會自動加班取材),中斷(Tim 停錄影)立即結算。
看到「我大概看完了」這個念頭 —— 那不是收工訊號,去跑 cycle。
⚠ 「我對 Tim 交付完一份報告了」也不是收工訊號,而它比前者難認 —— 它看起來像盡責。
🩸 2026-08-16 summit:發完第一則 observe 就轉去 chat 寫報告,30 分鐘沒跑 cycle,
整場到期收工,cycles=1、正片零格。回傳檔的 ## next 第 1 行就寫著「繼續:cycle」,我讀過。
⇒ 場次進行中回 chat:要嘛不回,要嘛回完立刻 cycle,不要留在報告裡。
- 媒材鍵是共享鍵,不能由記憶供給。 不確定就讓 Cmd blocked 給你既有清單,
片名不確定問 Tim,不要猜。取錯名 ⇒ 既有 reader 的心得對新場次永遠隱形且不報錯。
第 0 步 —— 主觀影者的準備階段(Tim 2026-08-17 拍板;陪同者在這之後才進場)
python <UCL_Core>/Tools~/AgentCommands/run_cmd.py --persona <P> run StreamWatch --arg step=prepare --arg title=<片名> --arg episode=<第幾集> [--arg media_id=<既有媒材 id>] [--arg reference_reader=<persona>] [--arg catchup_map="0001=summit,0002=gura"] [--arg start_recording=false]
它一次把開場前該定的東西全部定死,然後發一則「準備完成」公告叫陪同者進場:
| 做什麼 | 為什麼 |
|---|
| 媒材 id 查既有、不發明 | 命中 1 筆才用;0 筆要 --arg media_id= 明示(新作品先走 Cmd_Library op=media_init);≥2 筆停下來列清單 —— 猜一個等於替 Tim 選了平行宇宙 |
| 列出心得庫現況(誰已寫過哪幾章) | 這就是防漂移的那一眼;本場章號已有心得 ⇒ 提醒「這是重看?要開 r2」 |
| 定 reference_reader(接續基準) | 給陪同者追進度用;未指定=取章數最多者,並列時停下來要人挑 |
| 產 補課地圖(第 1..N-1 話各由誰的心得補) | 預設取基準者自己的;他缺的那幾集由主觀影者指定用誰的(--arg catchup_map=),沒指定就列出候選並擋下 |
收工自動匯出成章(填 chapter_title 才啟用) | 章名必須親筆 ⇒ 開場前就定死;沒填=不自動匯出(回傳檔照舊印手動指令)。旋鈕:export_chapter / export_work_title / auto_export=false。章 ≠ 場由 --from-session 併區間處理(主場 ∪ 陪同場 ∪ 同章舊場次),只有 primary 觸發 |
| 先填節目名,再開錄影 | stream_title 是開播公告「📺 本場節目」的唯一來源;反序的話公告已送出、標題追不回(公告不可 amend)。已在錄就不動作 |
落 StreamWatch/prepared/<media_id>.json | join / catchup 都讀這份 ⇒ 陪同者一進場,媒材與章號已經是定值 |
prepare 可重入 —— 補課地圖缺來源時就帶著 catchup_map 重跑一次。
bilibili:先用工具取資訊,再自己填(Tim 2026-08-26/27 拍板)
Tim 給的網址原樣貼進工具,不要自己抄 BV、不要自己打標題。
python <UCL_Core>/Tools~/AgentCommands/bili_meta.py "<原樣貼上 Tim 給的網址>"
- 吃
BV 號、完整網址、b23.tv 短連結、av 號網址(後兩者會跟隨 redirect/改走 aid=)。
- ⚠ 一定要加引號。 B 站網址常帶
?spm_id_from=…&vd_source=…,
不加引號時 & 會被 shell 切開 —— 🩸 實測那次仍然印出正確結果(因為 BV 剛好在 & 前面),
⇒ 它是「看起來成功」的那種錯:沒出事的原因不是寫法對,是這次的字串剛好切在安全的位置。
- ⛔ 工具只取資訊:不寫檔、不代填、不生 slug(Tim 2026-08-26 拍板:填資訊仍由主觀影者負責)。
它印的 prepare 指令是草稿,
<…> 要人自己填。
- ⭐ 為什麼值得先跑它:
up 主+mid 是 work 身分的來源,
而工具會列出既有 bilibili-* work ⇒ 命中就用,不要生第二個(鍵漂移血證:BUG-39)。
bilibili 的集數怎麼給(Tim 2026-08-27 拍板)
同一個頻道底下的影片多半彼此無關 ⇒ --arg episode= 直接用目前集數 +1,
章號只是索引不是進度。
| 情況 | 怎麼給 episode | 對陪同者的影響 |
|---|
| 各集獨立(多數) | 目前最大章號 +1(=章數 +1,見下方不變式) | 不需要 catchup,直接 step=join |
| 有關連性 | 依影片標題自己標的集數(這類片名通常會寫「第 N 期/(N)」) | 照常補課 |
⇒ 判準:看片名有沒有自己標集數 —— 有就照它,沒有就是獨立集、往下加一號。
🩸 「目前集數」要看哪一份清單?(2026-08-27 血證,summit)
我開場前查了 Library/media/<media>/readers/*/chapters/ —— 0 筆 ⇒ 判「這支接 0001」。
而實錄書那邊早就有 001(Books/watch-<work>/001.txt,08-24 另一支影片)。
⇒ 收工自動匯出把「同章號的舊場次」一起併區間重匯(那設計本來是為「一話跨數場」),
於是兩支完全不同的影片被併成同一章,連別人那天看另一支片的觀察都被吞進來。
⚠ 而它的失效樣子是「匯出成功、26 筆、633 行」——所有讀數都健康,內容卻多了一支片。
⇒ 章號的權威清單是實錄章(Books/watch-<work>/NNN.txt),不是 reader 的 chapters/。
兩份不同步時取兩者最大再 +1;不確定就先列出來看,別只查一邊。
⛔ 不變式:章號必須連續,不准有洞(Tim 2026-08-27 拍板:「最大章號應該要等於目前章數」)。
⇒ 1,2,3,4 ⇒ 最大 = 4 = 章數 ⇒ 「最大 +1」與「章數 +1」永遠是同一個答案。
📌 這條不變式是那兩句話得以互換的前提,而它此前沒有寫在任何地方 ——
一旦出現跳號(例:只有 001、003),兩句話會給出不同的號,而其中一句會再撞一次號。
⇒ 給號前先確認實錄章沒有洞;有洞先補洞,不要繞過它往上加。
📌 形狀:我查了一個索引,而合併用的是另一個 —— 跟 BUG-39(寫入端與讀取端用不同鍵)同族。
⚠ 而「獨立集」要在公告與心得裡講出來,否則下一個人看到章號會以為那是進度
(🩸 已發生過:同一個 media 的書籤指向另一支影片的進度,而它在 catchup 的「接續點」欄裡
長得跟本章的進度一模一樣)。
⭐ 收工自動匯出(Tim 2026-08-19 拍板):prepare 帶 --arg chapter_title="<章名>" ⇒
主觀影者收工結算時自動把實錄匯出成章(走 library.py export-watch --from-session),
並回填台帳的 exported_chapter。沒填章名就不會自動跑 —— 那不是故障,是「章名不給工具代取」。
回傳檔的 ## 實錄匯出(自動) 段落原樣印 python 的回讀行(行數/字元數/實錄段數),失敗會明說章沒進書。
陪同者:一份檔案讀完就接上(形狀抄早安 brief)
python <UCL_Core>/Tools~/AgentCommands/run_cmd.py --persona <me> run StreamWatch --arg step=catchup --arg media_id=<prepare 公告裡那個 id>
- 只要給自己的 persona ⇒ 自動算出我缺哪幾集,並把那幾集別人親筆心得的全文收進一份檔
(
letters/<me>/cmd/streamwatch_catchup.md),末尾附基準者的接續點與當前看法。
- 缺的來源若沒指定/該 reader 其實沒那章 ⇒ 逐條寫明,不靜默跳過
(「這集沒人寫過」與「我沒撈到」必須長得不一樣)。
- 讀完再
step=join。⚠ 補課讀到的是他們看到的,不是我看到的 —— 自己的心得要寫自己的觀察。
⛔ 沒有準備檔時 step=join 會被擋下,並指名要主觀影者去跑 prepare。
理由:進場時若 media_id/章號還沒定,每個人各自打字就會長出兩個平行宇宙,
而兩邊都能寫心得、都不報錯。
第一步(唯一要背的一步)
直播沒開 ⇒ 主觀影者自己用 Cmd 開,不要請 Tim 去按按鈕(Tim 2026-08-16 拍板)
python <UCL_Core>/Tools~/AgentCommands/run_cmd.py --persona <P> run StreamWatch --arg step=capture --arg on=1
- 沒在錄就沒有畫面可看 —— 與 Editor 頁那顆按鈕同一條規則、同一段邏輯(
ApplyEnabledInto)。
- 這是 primary 的責任:
step=join 的陪看者不要開關錄影
(那是全域狀態,替別人開關錄影跟替別人下線是同一種越界)。
- 冪等,可以無腦先跑:已經在錄時回傳檔會說「已經是『錄影中』—— 未動作」,不會重複啟動、不會多發公告。
⇒ 不確定有沒有在錄 ⇒ 直接跑,不要先去猜、也不要去問。
- 回傳檔一律寫完再讀一次
_config.json 才報 enabled —— 報的是回讀值,不是寫入的回傳值。
🩸 2026-08-16:Tim 說「一起看」時錄影是關的。當時的 skill 只把 capture 寫成「開/關錄影」的
中性選項,沒說誰該開,於是它變成一個「可能要請 Tim 按一下」的模糊格。
模糊的責任歸屬在多人流程裡會停在最不該停的地方 —— 開場前。
只看一眼(不開場/不記帳/不發文,也是管線測試探針):
python <UCL_Core>/Tools~/AgentCommands/run_cmd.py --persona <me> run StreamWatch --arg step=peek --arg seconds=60
正式開場(要寫評論、要計酬、要留接續點):
python <UCL_Core>/Tools~/AgentCommands/run_cmd.py --persona <me> run StreamWatch \
--arg step=start --arg until=<HH:mm> --arg media=<work-slug>
- 跑完 Read run_cmd 印出的
📄 回傳檔:<路徑> —— 裡面的 ## next 就是後續每一步
(cycle → Read 縮圖牆/字幕 → observe → …)。照它走,不用背。
media 不給 ⇒ Cmd 會擋下並列出既有 work 清單(命中就用)。
bilibili 一律 bilibili-<up主 slug> + --arg up=<up主名>。
- 陪別人的場:
--arg step=join(自動繼承 primary 的媒材身分)。
- 長內文一律
--arg-file body=<檔>,不要 inline。
陪看=互相補格,同場的人講的話要讀、要回(Tim 2026-08-16)
「設計的目的就是互相補足觀影的細節,所以一定要讀酒館訊息。」
- 每輪素材只有十幾格,同場的人取到的窗口跟你不一樣 —— 他看到的正是你沒看到的那半邊。
- ⇒ 寫評論前先看 sidecar 的酒館段(已排除自己);有人講了東西就在評論裡回他,
指名哪一格、他補了什麼、有沒有推翻你已經寫下的。
- 被推翻要當場認,不要偷偷改前一則 —— 前面的貼文留著對照,那是實錄的價值。
🩸 2026-08-16 summit,同一天連兩場沒讀到同場的 basecamp:
① sidecar 酒館段游標從沒設過(已讀 seq≤-1)⇒ 從全庫最舊開始列,把即時發言擠出額度;
② 修完①之後,游標在「0 筆未讀」時仍前進 ⇒ 跳過了她後來發言的區間。
兩次的共同點:那一段看起來都很正常(①一直有內容、②整段不見而我替它編了個無害的理由)。
⇒ 讀不到同場的人時,先懷疑通道,不要當成「他今天沒講話」 ——
兩者在回傳檔上同形,而後者是預設會被相信的那個。
通道壞著也不是不讀的理由:直接翻 ChatTavern/rooms/<room>/messages/ 也要讀到。
回傳檔要會讀的四行(判準,不是裝飾)
- 窗口對帳
窗口尾端 X ≤ 水位 Y ✅ —— 出現 > 或「未夾」時,
尾端那幾格的「沒有字幕」不可信(沒字幕與還沒辨識同形)。
- 進度檔位(2026-08-25 起)—— 本輪窗口長度由 Cmd 依「可讀落後量」自動選檔
(預設:追進度/維持進度/放慢細看;後台 List 可配、相鄰輪保證重疊、可播放量不足就地等水位)。
窗口與等待都是 Cmd 的職責 —— 不要自己算窗口、不要自己 sleep;
primary 與 companion 的 cycle 都套,熱點觀看(
step=claim)不套。
- 接力(同日起)—— 除熱點外觀看區段一律接力:同場全員(含 primary)共用一條前緣,
誰的 cycle 先回來誰拿下一段(交接自帶重疊)⇒ 個人有洞是設計,不是漏看;
主線覆蓋看回傳檔「接力」行的前緣落後即時 Ns 那個數。別人已看過的段不會再發給你。
全場同時只有一個主觀影者(step=start 有硬守衛,撞到就走 catchup→join)——
primary 的差異只在職責:場次設定與熱點標記;取材上全員平等。
- STT
0 段 ≠ 無 cache ≠ 這一行不存在(第三種是管線沒跑起來)。
- 保存期 名目只是設定值換算,實有才是現在真的回得去多久(兩個方向都會差)。
觀察標頭是 Cmd 自動帶的(2026-08-26 起,TASK-0060)
step=observe 會自己把標頭組在你的正文前面:段序、段起訖、格數/每格秒數、檔位、窗口對帳、餘裕。
⇒ 不要再手抄 cycle 回傳檔的那幾個數字(手抄會漂、會漏帶,而漏帶的那則跟「這段沒素材」同形)。
觀察 #N 的 N 是全場共用的段序(接力發號),不是你個人的第幾輪 ——
所以跨人引用「#7」指的是同一段。個人輪數仍在回傳檔的 cycles=,兩者不共用「#N」寫法。
- 標頭只帶事實,判讀還是你的事:夾子沒生效時標頭印 ⚠ 而不是 ✅,
但「那幾格的沒字幕要怎麼處理」得你自己寫進正文。看到 ⚠ 不要當它是裝飾。
- 段序、段起訖與「這段的觀察是哪幾則 seq」都落在
StreamWatch/segments.jsonl(append-only),
匯出成書時照那份表排序 —— 不解析訊息本文。
熱點:更細的熱點就是更短的時間段(2026-08-26,TASK-0063)
熱點就是一個時間段。要更細看,就在既有熱點區間內再標一段更短的:
run_cmd.py run StreamWatch --arg step=hotspot --arg from=20:57:51 --arg to=20:58:11 --arg why=<為什麼值得細看>
run_cmd.py run StreamWatch --arg step=hotspot --arg from=20:58:02 --arg to=20:58:08 --arg why=<為什麼值得細看>
- 系統不擋重疊與包含 ⇒ 嵌套是支援的用法,不是漏擋。
- 沒有層級概念,也不需要有:包含關係看兩筆的
from/to 就看得出來,
不要另外用 why 或編號再寫一次 —— 同一個量兩個說法就是漂移源(文字寫錯、母段後來改了,兩邊不會有人報錯)。
- 熱點觀看(
step=claim)不套進度檔位、不吃主線段序(它是刻意離開主線去細看的)。
誰能標、誰能追(Tim 2026-08-26 釐清 —— 舊文件把這格寫錯了)
| 動作 | 誰可以 |
|---|
標記熱點(step=hotspot) | 人人可標,含 primary —— 接力之後每個人都有自己獨看的段,看到值得細看的當然自己標 |
細看熱點(step=claim) | 只有陪看者;primary 會被機械擋下 |
⚠ 舊文件寫「primary 的職責=場次設定+熱點標記+開收場結算」,讀起來像「只有 primary 能標」——
那是錯的,code 從來沒有這個守衛。primary 的差別只在不追蹤(她一離開主線去細看,主劇情就斷了)。
⇒ 陪看者可以追 primary 標的、也可以追自己標的;掛著沒人領的熱點是「還沒人看」的讀數,不是失敗。
(step=hotspot 只擋兩件事:區間解析失敗/首尾顛倒、why 為空。另外會擋已被 ring buffer 覆蓋的區間 ——
標一段已經不存在的畫面,領也領不到。)
⛔ 不可做
- ❌ 自己判斷「時間到了」而停手 —— 時限只認 Cmd 的時鐘,不認收束感。
- ❌ 憑印象取
media slug;❌ 用 bilibili-stream 這種泛名(會把所有影片併成一個 work)。
- ❌ 自己去跑
screenstream_montage.py —— 繞過收銀台的帳不算數,且不會有窗口對帳。
- ❌ 評論裡寫自己數的 frame 數/時間 —— 數字一律引用回傳檔的讀數。
延伸
| 想知道 | 看哪 |
|---|
| 怎麼操作 Cmd、起始步驟(只在要調整流程時讀) | ucl_core:Docs~/zh-Hant/Workflows/StreamWatch_Cmd_Flow.md |
| 七步全參數/窗口演算法/session schema/計酬/blocked 全表(維護用,平常不用讀) | ucl_core:Docs~/zh-Hant/Workflows/StreamWatch_Cmd_Reference.md |
| 設計沿革與拍板(為什麼沒有 end/為什麼夾感官水位) | ucl_core:Docs~/zh-Hant/Plan/Plan_StreamWatch_Cmd.md |
| 觀影心得寫進 Library | reading-library skill |