ずんだもんがAIニュースを5本読み上げるYouTube Shortsシリーズを、AIエージェント(Claude Code)と立ち上げました。ニュース集めから投稿までを6工程に分けた記録です。1本あたり約40分、費用はほぼゼロ。
できたもの: https://www.youtube.com/playlist?list=PLT9RqB3kcKYQ
コードは末尾の付録にまとめたので、まず全体像だけ読んでもらえれば十分です。
6工程を、上から下へ流す
人間がやるのは工程1だけです。どのニュースを選ぶか、その数字は信用できるか。ここは任せていません。
2本目に打ったプロンプト(Claude Code)
入力 — Claude Code に送ったプロンプト
02の制作に進んでほしい。説明文にサイトとXの紹介も入れて、Xにも動画付きで投稿してください。
出力 — エージェントが実行したこと
ニュース収集 → 5本選定 → 台本生成 → 音声7本 → 口パク → ミックス → 1,763枚レンダリング → 品質ゲート8項目 → 事実確認(誤り1件を修正して再レンダリング) → サムネ2種 → YouTube公開 → 縦サムネ設定 → 再生リスト追加 → X投稿
この1つで最後まで走りました。途中で止めたのはXの投稿先アカウントを聞かれた1回だけです。
1本目は8回、2本目は1回
1回で走るようになったのは2本目からです。1本目は作りながら直しました。
下の表は、AIエージェント(Claude Code)のチャット欄に実際に入力したプロンプトの全部です。コマンドではなく、この日本語をそのまま渡しています。★が修正の指示にあたります。
修正は6回。うち3回は同じ指摘でした。解決したのは、目で見るのをやめて測る仕組みを作ったときです。
そしてこの6回が、そのまま検証スクリプトになりました。2本目以降、同じ指摘は出ていません。
目視をやめる
エージェントは自分が作った動画を見られません。mp4は再生できず、1,700枚のフレームを全部は確認できず、音は聞けない。だから「間延びしている」の判断ができません。
そして人間の目も当てになりませんでした。重なりを直したつもりで、実機ではまだ重なっていた。重なっていた相手は動画の中ではなく、YouTubeのUI(右のボタン列、下のタイトル欄)だったからです。
赤い部分が、実機でボタンやタイトルに覆われる場所です。ここを自分で定義して、要素がはみ出していないかを機械に測らせます。
そこで、目で見ていた項目を数値に置き換えました。
測ってみると、見えていなかったものが出てきました。
数値になれば、エージェントは自分で回して自分で直せます。「91%」と出れば原因を探し、もう一度測って「14%」を確認する。人間を待ちません。
閾値に根拠は要りません。過去の動画を見比べて「これ以上止まると飽きる」と感じた線を、そのまま数字にしただけです。大事なのは、線が引かれていて、機械が毎回同じ厳しさで測れることです。
形式は測れる。中身は測れない
「中国の7ラボがClaudeを蒸留、1.5億回」と書いたことがあります。出典を確認すると、1.51億回はそのうち1社の数値でした。嘘ではないが、合計だと誤読される。
これは画面をどれだけ測っても出てきません。別のエージェントに出典URLを渡し、「この数字は出典のどこに書いてあるか」を1件ずつ答えさせています。
1本あたり40分
VOICEVOXもレンダリングも無料なので、費用は実質ゼロです。X投稿のAPI利用料が1回$0.200かかる程度でした。
自分の題材でやるなら
動画でなくても、スライドでも記事でもアプリ画面でも同じです。
- 不合格の条件を、先に数値で書く。 「良いもの」は定義できなくても「出せないもの」の線は引ける。3つで十分
- データと表現を分ける。 明日、内容だけ差し替えて2本目が作れるか
- 表現から時間を追い出す。 時刻を引数で受ければ、何度実行しても同じ絵になる
- 検証はコマンド1つ。 最後に合否の1行だけ出す。判断を挟ませない
- 事実は他人に検証させる。 「誇張はないか」ではなく「出典のどこにあるか」を聞く
残るのは、何を作るかとどこに線を引くかだけです。
付録: 実装
台本をデータで書く
1本のニュースが持つ情報を全部宣言します。ポイントは読み上げ用と表示用の2つの文を持つこと。音声合成には「ペーパーカット」と渡さないと読みを間違えますが、画面には「PaperCut」と出したい。1つの文では両立しません。
dict(
audience="社内にサーバーがある人へ", # 誰に関係あるか
summary="AIエージェントが395組織を突破", # 3秒で読める結論
figs=[{"n":"395組織","l":"侵害された組織(48カ国)"},
{"n":"7分","l":"侵入から管理者権限まで"}],
impact="PaperCutは今すぐパッチ適用を", # 読者がすべきこと
text="ペーパーカットの脆弱性を突く攻撃で…", # 読み上げ用(カタカナ)
caption="PaperCutの脆弱性を突くAI主導の攻撃で…", # 字幕用(正表記)
source_url="https://…", sourceType="調査",
)
この形にすると、2本目はデータを差し替えるだけです。HTMLには触っていません。
声は、変わった行だけ作り直す
本文のハッシュをファイル名に使い、同じものがあれば再利用します。
th = hashlib.sha1((ln["text"] + str(sid)).encode()).hexdigest()[:10]
f = AUD / f"l_{ln['idx']:03d}_{sp}_{th}.wav"
if f.exists():
sec = dur(f) # 本文が同じなら再利用
else:
vv(ln["text"], sid, f) # 変わった行だけ合成
事実確認で1行直したとき、再合成されたのは7本中1本でした。
画面から時間を追い出す
CSSアニメーションは使いません。「この時刻の画面」を返す関数を1つ置き、外から時刻を進めます。
window.__setTime = function(t){
$("pbar").style.width = (Math.min(t/TOTAL,1)*100) + "%";
$("bgfx").style.transform =
"translate("+(Math.sin(t*0.42)*230)+"px,"+(Math.cos(t*0.31)*300)+"px)";
};
for i in range(N):
pg.evaluate(f"window.__setTime({i/FPS:.4f})")
pg.screenshot(path=str(FR/f"f_{i:05d}.jpg"), type="jpeg", quality=88)
実時間に依存しないのでコマ落ちせず、何度実行しても同じ絵になります。そして検証側が好きな時刻の画面を再現できます。
画面の構造は、ブラウザに座標を聞けば図にできます。下はスクリプトが自動で描いたものです。
重なりは座標で判定する
def rects_overlap(a, b):
ox = min(a["right"], b["right"]) - max(a["left"], b["left"])
oy = min(a["bottom"], b["bottom"]) - max(a["top"], b["top"])
if ox > 0 and oy > 0:
return {"w": round(ox, 1), "h": round(oy, 1)}
return None
0.1秒刻みで全編に回します。500〜600回の判定が数十秒で終わり、出力は1行です。
ep02: 重なりなし(0.1秒刻み, 全587フレーム走査)
動画ではなくHTMLに対して走るので、mp4を作る前に判定できます。
実機UIの領域を定義する
# YouTube Shorts 実機UIの目安(1080x1920基準)
SAFE_RIGHT_W, SAFE_RIGHT_Y0, SAFE_RIGHT_Y1 = 220, 760, 1780 # 右のボタン列
SAFE_BOTTOM_H = 310 # 下のキャプション欄
止まり具合を測る
diffs = [float(np.abs(arrs[i]-arrs[i-1]).mean()/255*100)
for i in range(1, len(arrs))]
still_ratio = sum(1 for d in diffs if d < 0.5) / len(diffs)
404を取りこぼさない
requestfailed はネットワークエラーでしか発火しません。404は正常応答なので拾えない。
pg.on("response", lambda r: fails.append(r.url) if r.status >= 400 else None)
末尾に余韻を足す
フレーム数は整数に丸められます。48.117秒は1,443フレーム=48.10秒。音声の終端48.12秒に0.02秒足りず、-shortest が短い方に合わせていました。
TAIL = 0.7 # 最後の声にぴったり合わせると末尾が切れる
total += TAIL
判定の出力
$ python qc_video.py ep02
=== 品質ゲート(必須項目)===
尺 58.00s OK (Shorts上限60s)
音圧 LUFS -15.13 OK (-14±1.5)
冒頭0.5秒の輝度 61.0/255 OK
最長の静止 1.0s OK (3秒未満)
静止フレーム比率 6% OK (1/3未満)
判定: 全項目通過 — 投稿可
エージェントが読むのは、最後の1行だけです。
図つきの完全版(画面の構造図・セーフゾーンの実物・動画の埋め込み)はこちらです。
https://yuichi916.github.io/method/ai-agent-shorts.html
パイプライン一式(スクリプト・画面テンプレート・検証スクリプト・Claude Codeスキル)の配布も準備中です。






