mcpField
A
anibon-timestamper

anibon-timestamper

github.com/zeniththクリエイティブ・メディア190 回インストール1 日前に更新
リポジトリ

anibon-timestamper について

anibon-timestamperは、クラウドLLMでAnibon Officialの配信文字起こしからYouTube形式のトピック別タイムスタンプを作るスキルです。専用サブエージェントに並列で分担し、チャットや画面上の活動、トピック資料も活用できます。

主な機能

  1. 01サブエージェントによる並列分析
  2. 02文字起こしの準備と分割
  3. 03トピックのシグナルと境界の検出
  4. 04LiveChatと画面活動の任意追加
  5. 05チャットの盛り上がりによるムード案内

活用シーン

  1. 01Anibon Official配信のトピックに時刻を付与
  2. 02長い文字起こしを分割して処理
  3. 03チャットを基に配信場面の雰囲気を把握

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/zenithth/anibon-stream-synthesis --skill anibon-timestamper

インストール後、Claude Code で/anibon-timestamperを入力して読み込まれたか確認します。

同梱ファイル

SKILL.md と一緒にインストールされる参考資料やスクリプト。

.gitignore
docs/superpowers/specs/
2026-08-06-per-chunk-mood-segments-design.md
references/
anibon_emoji_dictionary.md
BUILD_WHISPERCPP_GUILD.md
INDEX.md
subagent-prompt-template.md
summarizer-subagent-guide.md
whisper_pc_spec_benchmark.md
references/stories/
.gitkeep
resources/
garbled_replacements.json
scripts/
align_live_chat.py
anibon-analyzer.py
build_subagent_prompts.py
detect_boundaries.py
detect_signals.py
dump_chunk_text.py
merge_timestamps.py
prepare_video.py
validate_mood.py
validate_part_coherence.py
tests/
__init__.py
test_align_live_chat.py
test_analyze_555_emoji.py
test_detect_signals.py
test_pack_timestamps.py
test_validate_tags.py

詳細

Anibon Timestamper(Frontier / Cloud Orchestrator)

クラウド/フロンティア LLM を使ってライブ配信の文字起こしを分析し、YouTube 形式のタイムスタンプを生成するルーティングオーケストレーターのスキルです。配信の種類を動的に検出し、専門のサブエージェントに並列で処理を委任します。

使用する場面

  • Anibon Official の「Boat」による配信や動画について、anibon-chunk-timestamper、anibon-summarizer、anibon-garbled-notes のサブエージェントを並列で実行し、フロンティア/クラウドモデルを使ってトピックラベルにタイムスタンプを付ける場合。
  • クラウド API の利用料をかけずに、単一 GPU(NVIDIA Tesla P100、LM Studio、Ollama、ローカル VLM)でローカル実行する場合は、代わりに anibon-timestamper-local を使用してください。

サブスキルと権限

必須(最初に実行): preparing-tools を読み込み、依存関係(yt-dlp、ffmpeg、python3)を確認します。 必須(サブエージェント実行時): 複数のサブエージェントで分析する場合は、batching-subagents-concurrency を読み込みます(1 バッチのターンにつき最大 10 個、Model: "flash")。大容量の外部メディアをダウンロードする場合や、時間のかかるウェブ取得を行う場合は、事前にユーザーの許可を求めてください。

状況に応じて読み込みます(必要な場合はプロンプトに追加してください):

  • anibon-world-identity — タイムスタンプ作成前に、参照資料と照合してゲーム名/キャラクター名や日本語の慣習・イベント用語を確認します
  • anibon-stream-activity — 画面上のゲームプレイ、ウェブカメラの映像や AFK 状態、視覚的な根拠を確認するための高解像度ストーリーボードサンプリング
  • anibon-local-transcription — YouTube に字幕がない場合の whisper.cpp フォールバック
  • whisper-corruption-recovery — Whisper の出力で繰り返しループや破損が検出された場合に、文字起こしを復旧します
  • anibon-livechat-analysis — LiveChat のリプレイ(または extract_visual_livechat.py による画面上のチャットの視覚的なフォールバック)を解析し、サブエージェントが配信者と視聴者の両方からライブ配信の内容を読み取れるようにします
  • antigravity-vision-proxy — 代名詞の指す対象が曖昧な場合、ゲームタイトルの確認、または未解決の文字起こしメモの確認のために、ストーリーボード(sb0)のフレームを調査します

パイプライン(上から順に実行)

以下のパスはすべてスキルのルートディレクトリ(skills/anibon-timestamper/)からの相対パスです。

0. 環境

script/ ディレクトリにすべてのヘルパースクリプトがあります。オプションを確認するには、<script>.py --help を実行してください。 作業ファイルは youtube_<video_id>_workspace/ に保存します。

1. 初期化

出力言語を確認します。resources/channels.json を参照して、チャンネル名と話者のニックネームを確認します。

  • 自身のチャンネル:Anibon Official(@anibonofficial)
  • 主な話者:Boat / Pu Boat(ปู่โบ๊ต / PhuBoat / โบ๊ต)

先に進む前に、文字起こしの話者表記が own_channel のニックネームに対応していることを確認します。

2. 動画の準備
python3 -X utf8 scripts/prepare_video.py "URL" --format xml --block 300 --overlap 30
# Talk-heavy → --block 600 --overlap 60

文字起こしをダウンロードし、<workspace>/chunks/chunk_*.xml を作成します。

重要

破損の確認: 文字起こしがローカル Whisper によるものである場合(または音声が 2 時間以上の場合)は、末尾の項目に繰り返しループがないか確認します。破損が見つかった場合(一致率が 0.5 を超える場合)は、whisper-corruption-recovery スキルを読み込み、分割して破損した末尾部分を再実行し、重複を除去して統合してからチャンク分割します。

3. 文字起こしのクリーンアップ(任意)

シグナル検出の前に、Thai-Whisper で文字起こしされた英語の文字化けを正規化します。このスクリプトは cleaning-auto-transcripts スキルにあります。timestamper スキルのルートから場所を解決してください。

python3 -X utf8 ../cleaning-auto-transcripts/scripts/clean_garbled_english.py --chunks ~/youtube_<id>_workspace/chunks/
3.5 LiveChat のダウンロードと位置合わせ(任意ですが推奨)

各サブエージェントが担当するチャンクについて、視聴者のチャットも渡します。これにより、発話とチャットの両方から状況や感情を読み取れます。ネットワークが必要です。VOD に live_chat 字幕がない場合は、この手順を省略し、文字起こしのみを使って感情を判断します。

# 1. Download LiveChat replay (network required)
yt-dlp --sub-langs live_chat --write-sub --skip-download "https://www.youtube.com/watch?v=<VIDEO_ID>" -o "<workspace>/%(id)s.%(ext)s"
# 2. Parse to coarse chunks + a seconds-prefixed raw event feed
python3 -X utf8 ../anibon-livechat-analysis/scripts/parse_live_chat.py <workspace>/<id>.live_chat.json --chunk-minutes 90 --raw-events <workspace>/livechat_events.txt -o <workspace>/livechat_90/
# 3. Align raw feed to transcript chunk windows
python3 -X utf8 scripts/align_live_chat.py --events <workspace>/livechat_events.txt --chunks <workspace>/chunks/ -o <workspace>/livechat/

出力:livechat/livechat_chunk_NN.txt(文字起こしの各チャンクに対応)と livechat/livechat_index.json。yt-dlp が live_chat 字幕なしと報告した場合も、処理を続けてください。サブエージェントは文字起こしのみを使うフォールバックに切り替えます。

編集済みライブ配信/.live_chat.json がない場合のフォールバック(チャットの映像抽出)

YouTube Studio でライブ配信がトリミングまたは編集されると、YouTube はチャットのリプレイトラック .live_chat.json を完全に削除するため、yt-dlp はチャット字幕を利用できないと報告します。

特に注目度の高い場面で視聴者の反応を失わないように、anibon-livechat-analysis から extract_visual_livechat.py を実行し、特定のピーク区間(ミームの連発、高額なスーパーチャット、視聴者との質疑応答、クライマックスでの反応など)に焼き込まれたチャットオーバーレイを OCR で読み取ります。

# 1. Spot-extract visual livechat for target chunk time range
python3 -X utf8 ../anibon-livechat-analysis/scripts/extract_visual_livechat.py \
  --video-id <VIDEO_ID> \
  --range HH:MM:SS-HH:MM:SS \
  -o <workspace>/visual_livechat_events.txt

# 2. Align visual raw event feed directly to chunk windows
python3 -X utf8 scripts/align_live_chat.py \
  --events <workspace>/visual_livechat_events.txt \
  --chunks <workspace>/chunks/ \
  -o <workspace>/livechat/

対象の区間ウィンドウについて <workspace>/livechat/livechat_chunk_NN.txt が生成され、全編の動画をダウンロードせずに、analyze_555.py(ステップ 3.7)とサブエージェントのプロンプト生成に直接反映されます。

3.6 アクティビティとウェブカメラのタイムラインを抽出する(任意ですが推奨)

画面上のゲームプレイ/ウェブ閲覧と、Pu Boat のウェブカメラへの映り込み/離席状態を記録し、発話と行動の食い違いによるエラーを防ぎます。

# 1. Download high-res storyboard (sb3 -> sb2 -> sb1 -> sb0)
yt-dlp -f "sb3/sb2/sb1/sb0" "https://www.youtube.com/watch?v=<VIDEO_ID>" -o "<workspace>/frames/storyboard.%(ext)s"
# 2. Unpack slides
python3 -X utf8 ../anibon-stream-activity/scripts/unpack_storyboard.py --mhtml "<workspace>/frames/storyboard*" --out-dir "<workspace>/frames/slides"
# 3. Extract continuous activity timeline
python3 -X utf8 ../anibon-stream-activity/scripts/extract_activity_timeline.py --slides-dir "<workspace>/frames/slides" -o "<workspace>/activity_timeline.json"
# 4. Align activity to chunk windows
python3 -X utf8 ../anibon-stream-activity/scripts/align_activity_timeline.py --timeline "<workspace>/activity_timeline.json" --chunks "<workspace>/chunks/" -o "<workspace>/activity/"

出力:<workspace>/activity/activity_chunk_NN.txt。画面上のゲーム/アプリの状態、ウェブカメラへの映り込み、身体的な表情を記録し、サブエージェントへの入力に使用します。

3.7 タイの笑い/ミームのピークを検出する(任意ですが推奨)

align_live_chat.py の後続処理です。タイの視聴者は笑いを 555.../xD/ฮา/ขำ で表します。短い時間内にこれらが集中すれば、ミーム/笑いのピークとみなします。自動文字起こしでは配信者が実際に笑っている様子を聞き取れない場合に、笑いを推測する唯一の信頼できる手段です。チャンクごとにムードの判定+タイ語のトーンガイダンスを出力します。ムードを伴う説明を書く際、サブエージェントのプロンプトではこれを必ず考慮する必要があります。

python3 -X utf8 scripts/analyze_555.py \
  --livechat ~/youtube_<id>_workspace/livechat/ \
  --index ~/youtube_<id>_workspace/livechat/livechat_index.json \
  --out ~/youtube_<id>_workspace/mood_555.json

出力:mood_555.json — チャンクから {density, n_markers, n_messages, peak_windows, pulse, mood, verdict, tone} への辞書です。tone フィールドには、そのチャンクのムードの種類に応じたタイ語のガイダンス(tone と推奨される verbs)が含まれます。判定結果は次のとおりです。

  • *_PULSE — 加重スコアがバーストの基準を満たしました。ムードに基づくガイダンスを使用します。MEME_PULSE(面白い)、CUTE_CUNNY_PULSE(かわいい)、SHOCK_HYPE_PULSE(盛り上がり)、DRAMA_NEWS_PULSE(ニュース/ドラマ)、ANGRY_OUTRAGE_PULSE(怒りのこもった発言)などがあります。
  • HOT/WARM — マーカーはありますが、バーストはありません。軽い冗談/活気のあるガイダンスを使用します。
  • QUIET — チャットが少なく、バーストもありません。中立とし、動詞は自由に選べます。

メモ

ムードはガイダンスであり、ルールではありません。 tone/verbs のヒントは、サブエージェントに「チャットがどのような雰囲気だったか」を伝えるものです。AI は引き続き、状況に応じて最初に使う動詞を選びます。PULSE チャンクのタイムスタンプが、場面を盛り上がりのないものとして記述している場合に限り、validate_mood.py(ステップ 11.5)が人間に確認を促します。

絵文字辞書の参照先:references/anibon_emoji_dictionary.md を参照してください。チャンネル限定のエモートマッピング(:_CunnyBoat:、:_MonkeyBoat:、:_Nerd:、:_shutup:、:Grind:、:_Ripfish:、:_noname:、:_What:、:_WOW:、:_Ahh:、:_Meh:、:_MoneyBoat:、:_Tahaan:、:_KonDee:、:_NeoSlim:、:_Slim:、:_BoatSOM:)の完全な一覧、および YouTube 共通のエモートからムードのピークの重み付けと安全性マスキングのトリガーへの対応表が記載されています。

チャンクに MEME_PULSE のフラグが付いているのに、サブエージェントがそのチャンクを平坦で事実のみの内容として記述する動詞から始めた場合、そのトーンの不一致が validate_mood.py(ステップ 11.5)によって確認対象として提示されます。

4. 分析(事前確認)

チャンクのカテゴリ、欠落部分、バイトサイズの概要をすばやく確認します。注:Windows では、コンソールの UnicodeEncodeError を避けるために -X utf8 を使用してください。

python3 -X utf8 scripts/anibon-analyzer.py ~/youtube_<id>_workspace/
5. シグナルを検出 → ナレッジファイルを照合

すべてのチャンクを対象に TF-IDF を適用します。各チャンクのキーワードをナレッジファイルと照合します。

python3 -X utf8 scripts/detect_signals.py \
  --chunks ~/youtube_<id>_workspace/chunks/ \
  --knowledge ./resources/knowledge.json \
  --output ~/youtube_<id>_workspace/signals.json

出力:signals.json — チャンクごとの重み付きシグナル:matched_files(df/weight を含む)、weighted_matched_files(順位付け済み)、best_file、primary_topic、confidence。

パスの規則: knowledge.json 内のすべての best_file/matched_files のパスは、timestamper スキルのルートからの相対パスです。references/stream/*.md は timestamper 内、../anibon-world-identity/references/*.md は world-identity スキル内、../reference/* は最上位のデータディレクトリ内を指します。すべてのパスは現在の cwd ではなく、timestamper のルートから解決してください。

希少性 = 主題: detect_signals.py は、照合した各キーワードに文書頻度の逆数(log(N/df))を使って重みを付けます。頻度の高い(df が大きい)単語は日常的なフィラーであり、スコアは 0 となり、無視されます。頻度の低い(希少な)単語が、その特徴的な主題です。 confidence が明確な場合に限り、best_file をサブエージェントに渡してください。そうでない場合は、ナレッジファイルを省略してください。

5.5 World-Identity を読み込む(シグナルがゲームの参照ファイルと一致する場合)

トリガー: signals.json の best_file/weighted_matched_files が ../anibon-world-identity/references/ 配下のファイルを指している場合(つまり、カットオフ後のゲーム/キャラクター名の場合)。これらの名前はサブエージェントが幻覚しやすいものです。

アクション: anibon-world-identity スキルを読み込みます。このスキルは、検証済みのタイ語音素 → 英語名の対応表を作成するため、references/INDEX.md → キャッシュ済みのストーリー参照資料 → 参照用 SRT → ウェブ検索フォールバックの順で処理します。まずゲーム DB を初期化します。

python3 ../anibon-world-identity/scripts/fetch_fgo_db.py --check --db "../reference/FGO and DATA/atlas_fgo.db" || \
  python3 ../anibon-world-identity/scripts/fetch_fgo_db.py --db "../reference/FGO and DATA/atlas_fgo.db"
python3 ../anibon-world-identity/scripts/fetch_ygo_db.py --check --db "../reference/Yu-Gi-Oh DATA/ygo_cards.db" || \
  python3 ../anibon-world-identity/scripts/fetch_ygo_db.py --db "../reference/Yu-Gi-Oh DATA/ygo_cards.db"

解決済みの名前対応表をステップ 7 のサブエージェントプロンプトに注入してください(シグナルがこれらのファイルのいずれかに一致するチャンクすべて)。これにより、[Story] の記録には音声から推測した名前ではなく、正式な名前が使われます。ストリームがカットオフ前のよく知られたコンテンツを扱っている場合は省略してください。

6. トピックの境界を検出する(任意)

明確なトピックの移り変わりがある長いストリームの場合に使用します。次を実行してください。

python3 -X utf8 scripts/detect_boundaries.py --chunks ~/youtube_<id>_workspace/chunks/ --signals ~/youtube_<id>_workspace/signals.json --output ~/youtube_<id>_workspace/boundaries.json

境界が不明瞭な場合は、signals.json から手動で特定します。matched_files が切り替わるチャンク(例:ゲーム→Genshin→FGO)を探してください。ステップ 9 の --break-at に指定するタイムスタンプを記録してください。

7. サブエージェントを起動する(並列処理)

チャンクを4~5個のグループ(各約20~25分)に分割します。各グループにつき、**anibon-chunk-timestamper**を1つ割り当てます。

build_subagent_prompts.pyを実行して、入力ファイルを準備します。

python3 -X utf8 scripts/build_subagent_prompts.py <workspace>

次のファイルが生成されます。

  • <workspace>/prompts_by_chunk/chunk_NN.txt:チャンクごとのプロンプトファイル(各約15~25KB)。チャンクの途中で切り詰められないよう、view_fileの46KBの表示上限を必ず下回ります。
  • <workspace>/prompts/group_NN.txt:チャンクファイルのURIとメタデータを記載したグループマニフェスト。

重要

厳密な同時実行バッチ処理(推奨サブエージェント数は4~6):APIの429 RESOURCE_EXHAUSTEDレート制限(特に、グループマニフェストが約50~100KBに達したときに発生する、ローリングTPMの枯渇)を防ぐため、サブエージェントは1ターンあたり4~6個ずつ制御されたバッチで起動します(最大8個)。各バッチが完了するのを待ってから、次のバッチを起動してください。

Flashモデル層を使用する:チャンクのタイムスタンプ作成用にinvoke_subagentを呼び出す際は、速度とレート制限の余裕を最大限に確保するため、必ずModel: "flash"を指定してください。

チャンクを順番に読む:サブエージェントには、割り当てられたfile://<workspace>/prompts_by_chunk/chunk_NN.txtファイルをview_fileで順番に読み込ませ、全文を切り詰めずに100%読み取れるようにしてください。

呼び出しパターン(グループごとに1つ):

invoke_subagent(
    "anibon-chunk-timestamper",
    Model="flash",
    prompt=f"""You are processing Group {g} (chunks {start} to {end}).
PREVIOUS GROUP LAST TOPIC: {prev_topic}
Read each chunk sequentially via view_file:
- file://{workspace}/prompts_by_chunk/chunk_00.txt
- file://{workspace}/prompts_by_chunk/chunk_01.txt
..."""
)

各チャンクのプロンプトには、以下が含まれます。

  • チャンクのXML/会話行
  • signals.jsonからの検出シグナル(best_file+primary_topic+confidence+順位付けされたweighted_matched_files)
  • チャンクごとのLiveChatログ(livechat/livechat_chunk_NN.txt)
  • チャンクごとの画面上の視覚的な活動ログ(activity/activity_chunk_NN.txt)
  • mood_555.jsonからのチャンクごとのムード判定+トーンのガイダンス

**重要:**エージェントはグループ内のチャンクを順番に読み込み、続きのチャンクはスキップします。 独自のトピック要約は挿入しないでください。挿入するのはシグナルデータのみです。

各anibon-chunk-timestamperは、プレーンテキストのタイムスタンプ行を返します(通常、各グループにつき2~4行)。

重要

ビジュアルAIと生成ツールの根拠付け: 配信者がビジュアルAI生成ツール(テキストから画像の生成、動画生成、クリエイティブ編集ツールなど)を試したり、それに反応したりする場合は、次の点を区別してください。

  1. 配信者が実際に指定したプロンプト。
  2. AIモデルが実際に画面上に生成したもの(または生成に失敗したか、拒否したか)。
  3. 配信者による、コメディ調、政治的、または誇張を交えた風刺的な酷評。 風刺的な酷評(例:「กลายเป็นคอมมิวนิสต์」、「เหมือนสลิ่ม」、「หลอนเป็นเปรต」)を、生成画像の文字どおりの説明として扱ってはいけません。必ず[Reaction]を使い、酷評を明確に配信者の発言として示してください(例:ปู่โบ๊ตเหวอ TH-AI ไม่รู้จัก Jane Doe เจนเป็นสาวไซเบอร์ยืนคู่ ครม. แซวยังกะรัฐบาลจีน)。

重要

タイムスタンプの開始位置と、何気ないたとえの扱いに関する注意:

  1. **話題の開始位置を基準にする:**タイムスタンプは、必ず話題の冒頭の文を開始位置としてください。重要な固有名詞がすでに発言された後の、文の途中を開始位置にしてはいけません(視聴者がその位置から再生して、文脈を把握できなくなるのを防ぐためです)。
  2. **何気ないたとえのルール:**番組の予算をFrierenと比較するなど、1~2文だけの何気ないたとえや皮肉を、タイムスタンプの主題にしてはいけません。主題(例:One Punch Man S3とJ.C.Staffの騒動)を必ず記載し、何気ない比較は括弧内に補足してください。
8. タイムスタンプを統合する

各サブエージェントの出力を個別のファイル(例:chunk_001.txt、chunk_002.txt)に書き込みます。その後、統合して時系列順に並べ替えます。

python3 scripts/merge_timestamps.py ~/youtube_<id>_workspace/chunk_*.txt \
  -o ~/youtube_<id>_workspace/all_timestamps.txt
8.5. ギャップを監査する(要約エージェントの前に必須)

ギャップ禁止の鉄則を徹底します。統合したリストに対して実行してください。ゼロ以外の終了コードが返された場合は、指摘されたチャンクごとに補完エージェントを起動します(再実行するチャンクchunk_NNがツールに表示されます)。その後、ステップ9に進む前に再統合してください。

python3 scripts/audit_gaps.py ~/youtube_<id>_workspace/all_timestamps.txt
# exit 0: no gaps. exit 1: prints e.g. "12m 01:13 -> 01:25 (fill around chunk_26)"
8.6 Whisper.cppに音声スライスを割り当て、文字化けしたメモを整理する(グラウンドトゥルース)

サブエージェントはスポッタートークン(- "token" @ HH:MM:SS (chunk_NN))のみを出力します。次の2つのフェーズを分けて実行してください。

ステップ 8.6a:まず whisper.cpp を実行する(オーケストレーターの音響的グラウンドトゥルース)
  1. チャンクのタイムスタンプ出力に含まれるスポッターのメモをすべて <workspace>/garbled_notes_raw/candidates.txt に抽出します。
  2. whisper_dispatcher.py を実行し、タイムスタンプをクラスタリングして、その場で音声を切り出し、ローカルの whisper.cpp を使って音響的なグラウンドトゥルースを文字起こしします。
# Slicing & Audio Resolution:
# Option A (Recommended): Direct on-the-fly slicing from stream URL (no multi-GB download required)
python3 scripts/whisper_dispatcher.py ~/youtube_<id>_workspace --video-url "https://www.youtube.com/watch?v=<VIDEO_ID>" --verbose

# Option B (Local audio stream copy at 150x-300x speed without full video download):
URL=$(yt-dlp --extractor-args "youtube:player_client=android" -g -f 18 "https://www.youtube.com/watch?v=<VIDEO_ID>")
ffmpeg -y -i "$URL" -vn -c:a copy ~/youtube_<id>_workspace/audio.m4a
python3 scripts/whisper_dispatcher.py ~/youtube_<id>_workspace --verbose

これにより、以下を含む <workspace>/garbled_notes.json が生成されます。

  • google_sentence:raw_transcript.th-orig.json3 から取得した、周辺文の正確な範囲です。
  • whisper_segment:whisper.cpp(-oj)による、時間が対応付けられた音響的な文のセグメントです。
  • correct:正規のターゲット(辞書から自動的に解決するか、サブエージェントが設定します)。

ヒント

切り出し失敗時の復旧(単一クラスタ): クラスタの切り出しに失敗した場合(例:一時的なストリームソケットのタイムアウトによる終了ステータス 187)、次のコマンドを実行します。

URL=$(yt-dlp --extractor-args "youtube:player_client=android" -g -f 18 "https://www.youtube.com/watch?v=<VIDEO_ID>")
ffmpeg -y -ss <start_sec> -t <duration_sec> -i "$URL" -ar 16000 -ac 1 -c:a pcm_s16le <workspace>/audio_slices/cluster_XXX_<start_sec>.wav
/Users/zenithth/whisper.cpp/build/bin/whisper-cli -m /Users/zenithth/whisper.cpp/models/ggml-large-v3-turbo.bin -f <workspace>/audio_slices/cluster_XXX_<start_sec>.wav -l th --output-txt

文字起こしテキストで garbled_notes.json を更新してから、anibon-garbled-notes に渡します。

ステップ 8.6b:後で anibon-garbled-notes サブエージェントを呼び出す(グラウンドトゥルースの照合と辞書の同期)

whisper_dispatcher.py による音響的な文字起こし結果を指定して、anibon-garbled-notes サブエージェントを呼び出します。

invoke_subagent(
    "anibon-garbled-notes",
    Model="flash",
    Prompt=f"""Analyze the whisper.cpp transcripts in {workspace}/garbled_notes.json.
Perform sentence-level word-by-word sequence alignment between google_sentence and whisper_segment to resolve phonetic noise slots.
Resolve canonical words, validate anti-cascade rules against raw_transcript.th-orig.json3,
update {workspace}/garbled_notes.json with confirmed targets, and run update_garbled_dictionary.py."""
)

サブエージェントは <workspace>/garbled_notes.json を更新し、次を実行します。

python3 ../cleaning-auto-transcripts/scripts/update_garbled_dictionary.py \
  --from-notes ~/youtube_<id>_workspace/garbled_notes.json \
  --workspace ~/youtube_<id>_workspace

# If /Users/zenithth/abss-dev exists, sync git working copy as well:
if [ -d "/Users/zenithth/abss-dev" ]; then
  python3 /Users/zenithth/abss-dev/skills/cleaning-auto-transcripts/scripts/update_garbled_dictionary.py \
    --from-notes ~/youtube_<id>_workspace/garbled_notes.json
fi

警告

macOS では --cookies-from-browser を避けてください:ブラウザーの Cookie を抽出すると、GUI のキーチェーンパスワード認証プロンプトが表示され、ヘッドレス/CLI のバックグラウンドエージェントタスクがハングします。認証情報なしで YouTube SABR HTTP 403 エラーを確実に回避するには、--extractor-args "youtube:player_client=android" を使用してください。

注意

連鎖的な誤りを防ぐためのグラウンドトゥルース確認:候補の文字化けメモは、必ず raw_transcript.th-orig.json3 と照合してください。候補に複数語からなるゲーム/アニメのタイトル(例:Yuri on Ice、Chaos Zero Nightmare、Where Winds Meet、SLAPP)がタイ語のテキストに埋め込まれている場合、それは過剰に強力なクリーナールールによる生成物です。クリーナーによる生成物を garbled_replacements.json に保存しないでください。

出力:

  • ~/youtube_<id>_workspace/garbled_notes.json — すべての候補(garbled、google_sentence、whisper_segment、correct、chunk、ts、cluster_span)
  • resources/garbled_replacements.json — 正規のグループ化されたマッピング(TargetWord: [patterns...])を、ルートとスキルのリソース間で自動同期します

辞書は共有されるため(resource_path() はプラグインのルートまでさかのぼります)、今後のすべてのストリームで新しいルールが自動的に読み込まれます。本当に解決できない固有名詞は、確認用に correct: null のままにします。

9. 最終組み立て — anibon-summarizer(wrap + pack を置き換えます)

ソート済みの all_timestamps.txt 全体を anibon-summarizer サブエージェントに渡します。このサブエージェントは、チャンクをまたぐ重複を削除し(3,500 バイトの上限による過剰な分割は行わずに)テーマの大きなまとまりごとにグループ化し、重要なタイムスタンプ(長時間のストリームでは約 40~60 個)を選別して、簡潔なパート見出しを書き、最終的な Markdown を生成します。

with open("all_timestamps.txt") as f:
    merged = f.read()

invoke_subagent(
    "anibon-summarizer",
    prompt=f"TIMESTAMPS:\n{merged}"
)

サブエージェントは output.md 全体を直接出力します。~/youtube_<id>_workspace/output.md に保存してください。

メモ

pack_timestamps.py は、オフライン/スクリプト実行の場合や、単一コメントのバイト数による分割を明示的に求められた場合にも引き続き利用できます。通常のセッションでは、話題の一貫性を保つため、テーマの大きなまとまりごとのグループ化と、重要なタイムスタンプの選別を優先してください。単一コメントのバイト数制限が明示的に求められた場合:

python3 scripts/pack_timestamps.py ~/youtube_<id>_workspace/all_timestamps.txt \
  --byte-limit 2900 \
  --title "<Stream Title>" \
  --output ~/youtube_<id>_workspace/output.md
python3 scripts/check_sections.py ~/youtube_<id>_workspace/output.md
10. セクションを検証する
python3 scripts/check_sections.py ~/youtube_<id>_workspace/output.md

確認項目:セクションごとのバイト上限 ✅、ASRの文字化けパターンがないこと。

11. トピックの一貫性を検証する(任意)

タイムスタンプのゲーム名をsignals.jsonの正解データと照合します。捏造された名前やパート間でのトピックの混入を検出します。

python3 scripts/validate_part_coherence.py ~/youtube_<id>_workspace/output.md \
  --signals ~/youtube_<id>_workspace/signals.json \
  --chunks ~/youtube_<id>_workspace/chunks/
11.5 ムードとチャットの盛り上がりを照合する(任意)

analyze_555.pyを補完するものです。各タイムスタンプに、そのチャンクのムード+タイ語のトーンに関するガイダンス(AIが使用できる動詞)を付与します。ガイダンスのみを提供し、動詞を却下することはありません。AIが自由に動詞を創作できます。レポートのみを出力し、終了コードは0です。PULSEの範囲にあるスタンプに、チャンクの勢いが反映されているかを確認するために使用します。

python3 scripts/validate_mood.py \
  --timestamps ~/youtube_<id>_workspace/all_timestamps.txt \
  --mood ~/youtube_<id>_workspace/mood_555.json \
  --livechat ~/youtube_<id>_workspace/livechat/ \
  --index ~/youtube_<id>_workspace/livechat/livechat_index.json
12. カタログ、アーカイブ、辞書の同期(シリーズ終了時の手順)

output.mdの生成と検証が完了した後(または複数パートのシリーズ実行が終了した後)、次の手順を行います。

  1. 文字化けメモを同期する:

    • すべてのパートのチャンクタイムスタンプ作成サブエージェントから、見つかったGARBLED_NOTES:をすべて収集します。
    • resources/garbled_replacements.jsonの"mappings"キー(data["mappings"])に取り込みます。ヘッダーのメタデータキーversion、description、noteは保持してください。
    • プラグインのルートとベンダー版の両方(resources/garbled_replacements.jsonとskills/anibon-timestamper/resources/garbled_replacements.json)を同期します。
    • 更新した辞書をGitHub(anibon-stream-synthesis.git)にコミットしてプッシュします。
  2. timestamp_workspaceに取り込む:

    • 必須のサブスキル: backing-up-timestampsを使用します。
    • python3 -X utf8 import_workspace.py ~/youtube_<id>_workspaceを実行します(または~/youtube_*_workspaceをまとめて処理します)。
    • 構造化されたトランスクリプト(transcripts/transcript_<id>.jsonl.gz)を自動でアーカイブし、extras/<id>/をコピーして、検索インデックス(python3 -m anibon_search build)を自動で再構築し、by_video_id/timestamp_<id>.mdとREADME.mdにエントリーをリンクします。
    • timestamp_workspaceをGitHub(ZenitHTH/timestamp-workspace.git)にコミットしてプッシュします。
  3. 生のワークスペースをアーカイブする:

    • 検証済みディレクトリを~/youtube_workspaces/backed_up/に移動します。
    • metadata/catalog.jsonのworkspace_pathを更新します。

出力形式

═══のセクションブロックを含む単一の.mdファイル:

# Title | ANIBON

═════════════════════════════════════════════════════
 Part 1: Section Header (⏱ เริ่ม: HH:MM:SS)
═════════════════════════════════════════════════════
HH:MM:SS - [Tag] Description
...

═════════════════════════════════════════════════════
 Part 2: Section Header (⏱ เริ่ม: HH:MM:SS)
═════════════════════════════════════════════════════
...

ヘルパースクリプト

スクリプト目的
scripts/prepare_video.pyダウンロード+クリーニング+チャンク分割
scripts/dump_chunk_text.pyサブエージェント向けに、XML チャンクから整形済みのクリーンなテキストを出力
scripts/anibon-analyzer.py事前確認:欠落、カテゴリ、バイトサイズ
scripts/detect_signals.pyTF-IDF シグナルとナレッジファイルのマッチング
scripts/detect_boundaries.pyトピックの境界を検出(Step 9 の --topic-json に入力)
scripts/align_live_chat.pyLiveChat イベントフィードを文字起こしチャンクごとのログに分割(Step 3.5)
scripts/analyze_555.py整列済み LiveChat からタイ語の笑い声/ミームの盛り上がりを検出 → チャンクごとのムード判定(Step 3.7)
scripts/validate_mood.pyムードを示すタイムスタンプが mood_555 の判定を反映しているか確認(Step 11.5)
../anibon-livechat-analysis/scripts/parse_live_chat.py.live_chat.json をイベントフィードに解析(Step 3.5)
../anibon-livechat-analysis/scripts/extract_visual_livechat.py.live_chat.json がない場合に、Gemini ビジョンプロキシを使って画面に焼き込まれた LiveChat を抽出(Step 3.5)
scripts/merge_timestamps.pyサブエージェントの出力を結合してソート
scripts/process_chunks_local.pyLM Studio/llama.cpp の OpenAI エンドポイントを使った、ローカル LLM によるチャンクの逐次処理
scripts/audit_gaps.py欠落の監査(ギャップなしルール)+欠落からチャンクへのマッピング(Step 8.5)
scripts/pack_timestamps.pyバイト数制限付きのセクション分割(--break-at、--topic-json に対応)
scripts/check_sections.pyバイト数の上限と ASR の文字化けを検証
scripts/validate_part_coherence.pyゲーム名とシグナルを相互参照
scripts/validate_tags.pyタイムスタンプのタグ語彙を検証
../cleaning-auto-transcripts/scripts/clean_garbled_english.pyThai-Whisper で文字化けした英語を正規化(Step 3)
../cleaning-auto-transcripts/scripts/clean_transcript.py生の json3 をクリーニング(prepare_video から呼び出し)
../anibon-world-identity/scripts/fetch_story_ref.pyストーリーのあらすじを取得/キャッシュ(ウェブ検索にはユーザーの同意が必要)
../anibon-world-identity/scripts/align_ref_timeline.py参照 SRT のタイムスタンプをチャンクに整列
../anibon-world-identity/scripts/fetch_fgo_db.py、fetch_ygo_db.pyカードデータベースを構築(World Identity)

厳守事項

  • 名前付きエージェントを使用する — ステップ 7 では、チャンクごとに必ず invoke_subagent("anibon-chunk-timestamper") を使用してください。ステップ 8.6 では必ず invoke_subagent("anibon-garbled-notes") を使用してください。ステップ 9 では必ず invoke_subagent("anibon-summarizer") を使用してください。汎用の Task/self エージェントで代用しないでください。
  • サブエージェントのバッチ処理を厳守する(4~6 件を推奨) — 4~6 件のサブエージェントを制御されたバッチで処理し、完了を待ってから次のバッチに進んでください(Model: "flash" の場合は最大 8 件)。負荷の高いプロンプト処理で、10 件以上のサブエージェントを同時に起動しないでください。
  • チャンクには FLASH モデル層を使用する — すばやく実行し、API のレート制限を回避するため、チャンクのタイムスタンプ作成サブエージェントには Model: "flash" を使用してください。
  • ヒューリスティック/正規表現によるフォールバックは禁止 — サブエージェントがエラーやレート制限に遭遇しても、ヒューリスティックなテキスト生成スクリプトにフォールバックしないでください。すべてのタイムスタンプは、実際のトランスクリプト XML と LiveChat ログを読み込む anibon-chunk-timestamper サブエージェントの実行によって生成する必要があります。
  • detect_signals.py を使用する — TF-IDF のみを使用してください。場当たり的な grep やインラインスキャンは禁止です。
  • チャンクを自分で読まない — XML はサブエージェントが読み込みます。signals.json のデータをプロンプトに注入してください。
  • ファイルは 1 つ — ═══ セクションブロックを含む単一の .md にしてください。part1.md は作成しないでください。
  • 空白区間を作らない — 無音であることを確認できている場合を除き、タイムスタンプの間隔は最大 10 分としてください。
  • イントロを細分化する — イントロが 10 分を超える場合は、3~5 分ごとのサブトピックのマイルストーンに分割してください。
  • 場当たり的なタイムスタンプ作成は禁止 — タイムスタンプは、サブエージェントのパイプライン → merge_timestamps.py → anibon-summarizer を通じてのみ作成してください。
  • 両方を読む — サブエージェントは、タイムスタンプを作成する前に、トランスクリプトのチャンク全体と、その LiveChat ログ(ある場合)の両方を読み込む必要があります。状況と感情は会話とチャットの両方から把握し、primary_topic だけで判断しないでください。
  • ムード判定を尊重する — mood_555.json がある場合は、各チャンクの tone のガイダンス(ムードと推奨される動詞)をサブエージェントのプロンプトに注入してください。AI は最初に選んだ動詞を維持できますが、チャットが笑いに包まれた場面や盛り上がりのピークを、淡々とした事実を表す動詞で平板化してはいけません。
  • ネタバレを避けるガチャの方針 — タイムスタンプの説明で、配信者がガチャに当たったか外れたかを明かさないでください。期待感、ピックアップ対象のバナーやキャラクター、チャットとのやり取りに焦点を当ててください。
  • 18+/RULE 34 のトーンを損なわない — 成人向けファンアートや Rule 34 に関する話題を、一般的なコメディタグ([Reaction] ฮา...)で軽く扱わないでください。成熟した、率直で正確な要約には [Talk] を使用してください。
  • 固有名詞とゲームタイトルの正確性を保つ — 誤認識メモを処理する際(ステップ 8.6)、正当な外部ゲームタイトル(例:MARVEL Tōkon: Fighting Souls、Alien: Isolation、TMNT: The Last Ronin)、ブランド名、一般的な英語表現(例:where we meet)をノイズパターンとして扱わないでください。確認できていない固有名詞は、一般的なタイ語表現に無理に置き換えず、correct: null のまま人による確認に回してください。
  • すべてのスタンプに固有名詞を含める — 各タイムスタンプには、その内容に固有の固有名詞を明記してください(例:Wuthering Waves、Zelda、น้องเนเน่ AGT、Bleach)。曖昧な代名詞(「このゲーム」「あの子」「この事務所」など)は使用しないでください。要約時に固有名詞を省略してはいけません。形容詞や感情的な補足を削ってください。

コメント