AIコーディングエージェントで解説スライド・動画を自動生成する “Contents Kit” システムを構築した
サンプル
AI コーディングエージェントである Claude Code や Cursor のエージェントモードを使用して、スライド動画を生成するシステムを構築しました。
この記事で紹介するために、サンプルとして Zenn について解説する動画を生成しました。
最初に最低限の情報を与えて、以降は AI (Cursor で Claude Opus 4.5 を使用) にすべて任せて作成しました。
生成したすべての動画
与えた情報
# カリキュラム情報
<!-- 以下の情報を手動で入力してください。AIに与える情報になります。 -->
## プロジェクト名
Zenn の基礎
## 概要
エンジニア向けの技術ブログサイトである Zenn の基礎を学びます。
## 対象者
Zenn を使ったことがない、または、見たことはあるけど投稿したことがないエンジニア
## 到達目標
- Zenn の特徴を理解できる
- Zenn の記事を閲覧できる
- Zenn の投稿方法を理解できる
これ以外の情報は、作成時にコーディングエージェントが Web 検索から取得しています。
想定読者
- AI を使って、業務の効率化を図りたい方
- スライドや動画の自動生成を行いたい方
この事例では教材の制作ですが、それ以外の一般的なプレゼン資料などにも活用できます
今回構築したシステム:Contents Kit について
背景
- 弊社は教育事業を行っており、e ラーニングのテキスト資料や動画教材を提供しています。
- 提供する教材コンテンツは、コンテンツ制作の担当メンバーが編集し制作しています。
- カリキュラムの設計、台本の作成、スライドの作成、読み上げ音声の収録、動画の作成といった一連の作業があり、膨大な時間がかかっています。
- AI を活用することで、コンテンツ制作の効率化を図りたい。
- これまでも AI の活用自体はしていたものの、都度 ChatGPT に投げるようなやり方であり、出力結果を管理する方法がなかったり、プロンプトをチームで共有する方法がなかったりといった問題がありました。
- ゆくゆくは完全に AI のみで生成できることを目指したい。
- 自動的に生成できれば、個人最適化された教材コンテンツを提供するといった発展的な使い方も実現できそうです。
対象となる教材
主に以下のような教材の作成を想定しています:
- eラーニングプラットフォーム向けの動画教材
- スライド資料とナレーション付き動画
- 扱うトピックとしては、AI、DX などの専門的な内容
動作方法
-
Git リポジトリをクローンしてローカルで動かす
- コンテンツ制作に関わるメンバーも Git や Cursor などのツールの知識があるため、わざわざ Web アプリケーションを構築したりせず、この方法にしています
-
Cursor でカスタムコマンドを実行して、各ステップの生成をする
- Claude Code でも実行できるようにしていますが、コンテンツ制作メンバーは Claude Code を使っておらず Cursor を使っているため、基本的に Cursor で実行しています
- なぜコーディングエージェントでコーディング以外のタスクをするのか、使うことのメリットは別記事で紹介しています
AI を活用したシステムを構築する前に
生成 AI を活用して業務を効率化するためには、まずはエキスパート(人間)が行っている作業を整理・理解し、言語化して説明できる状態になることが必要不可欠です。
AI を用いたワークフローを構築するうえでは、プロンプト設計が勝負です。人間が何を、どう判断しているのかをプロンプトに落とし込むことができれば、AI に任せることができます。
今回のフローを構築する際も、コンテンツ制作を行っているメンバーにヒアリングを行い、既存の制作工程を把握するところから始めました。
Contents Kit で実現している最終的なフローも、基本的に既存の制作工程を踏襲したものになっています。
ベースとなったアイデア:Spec Kit
リポジトリの構造を決めるうえで大いに参考にしたのが、Spec Kit です。
Spec Kit は、仕様駆動開発を行うためのツールであり、導入することで、Cursor や Claude Code で仕様の作成やそれを元にした実装を行うためのカスタムコマンドが提供されます。
このツールでも「シェルスクリプトで処理できるところはシェルスクリプトで処理する」「AI で実行するものをカスタムコマンドで実行する」という構成になっており、その構成を取り入れました。
リポジトリ構造
ワークフローの全体像
最終的なゴールは、スライドを表示しながらナレーションで解説する動画ファイルを生成することです。
Contents Kit は、大きく分けて 6 つのフェーズ で教材を生成します。
- カリキュラム設計の開始 (/00-start)
- 学習目標設計 (/01-goal-design)
- 台本作成 (/02-script)
- スライド作成 (/04-slide)
- 動画生成 (/05-video)
- 字幕生成 (/06-subtitle)
※途中でフェーズの統廃合を行ったため 03 が抜けた状態になっています。
それぞれのフェーズの中では、さらに以下の手順を踏みます:
- AI がコンテンツを生成
- あらかじめ定義したチェックリストを元に AI が自己レビュー。
- 自己レビューをクリアするまで AI が修正を繰り返す。
- 自己レビューをクリアしたコンテンツを人間が確認し、必要に応じて修正する。
- 内容が確定したら、次のステップに進む。
人間の確認のステップを飛ばせば、完全に AI に任せた教材生成も可能です。
技術スタック
主要な技術
| 技術 | 用途 |
|---|---|
| Cursor | エージェントモードでカスタムコマンドを実行する |
| Claude Code | Cursor と同様(いずれも使用可能。ただしCursorを使っているメンバーが多いため、基本的にCursorを使用) |
| Marp | Markdown ベースのスライド生成フレームワーク |
| Remotion | React コンポーネントから動画を生成するフレームワーク(※商用利用の場合有料ライセンスが必要) |
画像生成
| 技術 | 用途 |
|---|---|
| Nano Banana Pro (gemini-3-pro-image-preview) | スライド用画像・イラストの生成 |
| Gemini 3 Pro Preview | SVG 図版の生成 |
| Puppeteer | SVG 図版の表示確認 |
音声生成
| 技術 | 用途 |
|---|---|
| Gemini 2.5 Flash Preview TTS | テキスト読み上げ音声の生成 |
| ffmpeg | 音声ファイルのノイズ除去、ノーマライズ |
| MFA (Montreal Forced Aligner) | 字幕生成のための音声アライメント |
その他
- Git: ソースコードとコンテンツのバージョン管理
- Node.js: 依存パッケージの管理と実行環境
なぜこの構成なのか
Marp を選んだ理由
Marp とは、Markdown ベースでスライドを記述できるツールです。
通常(手作業で)、スライドを作る際は PowerPoint のような GUI アプリケーションが主流ですが、Marp であればテキストベースで記述できるため、AI にコンテンツを生成させることに向いています。また、Git 管理しやすいなどのメリットもあります。
Remotion を選んだ理由
Remotion は React コンポーネントを用いて、コードから動画を生成できます。
これにより、Marp で作成したスライドを HTML に変換し、それを Remotion で動画で出力するというフローが構築できます。
スライドのアニメーション制御や音声との同期も、コードで柔軟に制御できます。
Remotion の紹介記事としては以下の記事が非常に参考になりました。
Gemini API を選んだ理由
音声の生成と、図・イラストの生成に使用しています。
Gemini 2.5 Flash の TTS(テキスト読み上げ)は、日本語の自然な音声生成が可能で、しかも比較的低コストです。
また、画像生成も同じ API で統一できるため、開発がシンプルになります。
ただし、SVG による図の出力に関しては Gemini よりも Claude のほうが品質がよいため、画像生成モデルはもう少し検討の余地があります。
ディレクトリ構造
リポジトリのディレクトリ構成は以下のようになっています。
.
├── .claude/ # Claude Code カスタムコマンド定義
│ └── commands/
├── .cursor/ # Cursor カスタムコマンド(シンボリックリンク)
│ └── commands/
├── workflow/ # 処理を定義するファイル
│ ├── scripts/
│ │ ├── bash/ # Bash スクリプト(初期化、AI 呼び出し)
│ │ ├── node/ # TypeScript スクリプト(動画生成、解析)
│ │ └── prompts/ # AI プロンプトテンプレート
│ ├── templates/ # コンテンツテンプレート
│ │ ├── foo.md # foo コマンドのプロンプトテンプレート
│ │ └── foo.review.md # foo コマンドの自己レビューチェックリスト
│ └── marp/ # Marp 関連(テーマ、アイコン、コンポーネント)
├── output/ # 生成されたコンテンツ
│ └── <プロジェクト名>/
│ ├── 00-basic-info.md
│ ├── 01-goal-design.md
│ └── chapters/ # 章ごとのディレクトリ
│ ├── 01-イントロダクション/
│ │ ├── script.md
│ │ ├── slide.md
│ │ ├── slide.mp4
│ │ └── audio/
│ └── ...
└── reference/ # 参照用データ
各ディレクトリの役割詳細
.claude/commands/, .cursor/commands/
Claude Code や Cursor で実行できるカスタムコマンドを定義しています。各コマンドは、詳細なプロンプトとチェックリストを含んでおり、AI が適切なコンテンツを生成できるようにガイドします。
シンボリックリンクで共有することで、二重管理にならないようにします。
workflow/scripts/
-
bash/: ワークフローの各ステップを実行するシェルスクリプト -
node/: 動画生成や解析を行う TypeScript スクリプト -
prompts/: AI に渡すスタイル指定プロンプト(TTS、画像生成など)
workflow/templates/
各ステップで使用するテンプレートファイル。AI はこれらのテンプレートを埋めていく形でコンテンツを生成します。
workflow/marp/
-
icons/: スライドで使用する 200 個以上の SVG アイコン - カスタムコンポーネント定義(レイアウト、デザイン)
- Marp テーマ設定
output/
生成されたすべてのコンテンツを保存。
reference/
人間が制作した教材データ。評価システムで参照データとして使用する。
カスタムコマンドの設計
命名
カスタムコマンドには、 00-, 01-, 02- のように番号を先頭に付けています。
これにより、実行するべき順序をわかりやすくしています。
ステップの分割
1 つのコマンドだけで最終成果物である動画までを生成しようとするのではなく、ステップごとにカスタムコマンドを分割しています。
これにより、以下のようなメリットがあります:
- 各ステップで生成されたファイルを人間が確認し、必要に応じて修正できる
- コンテキストを分割することで、AI に与えるコンテキストが大きくなりすぎない
- 各ステップを独立してテスト可能になる
シェルスクリプトとの分担
カスタムコマンド(=AI が思考する処理)にすべてを入れるのではなく、シェルスクリプトで処理できる部分はシェルスクリプトに任せるようにしています。
例えば、「現在のブランチ名を取得する」「テンプレートファイルをコピーして配置する」などはシェルスクリプトで定義し、AI はそれを実行するだけにしています。
これにより、処理の安定性・実行時間を向上させることができます。
具体的な処理
カリキュラム設計の開始(/00-start)
-
/00-startコマンドを実行することで、シェルスクリプトを実行し、基本情報を入力するためのテンプレートファイルを生成します。 - このファイルだけは、人間が手作業で情報を入力し、どんなコンテンツを生成したいか指示します。
- 以下のような基本情報を記入します:
- カリキュラムのタイトル
- 対象とする学習者(想定読者)
- 到達目標
- 暫定カリキュラム
学習目標設計(/01-goal-design)
-
/01-goal-designコマンドを実行することで、前段で作成した基本情報を元に、カリキュラムの学習目標を詳細に設計します。 - あらかじめ用意されているテンプレートを埋める形で生成します。
AIによる自己レビューファイル
あらかじめ、学習目標を設計する際にどのような観点を守るべきかを定義しておき、チェックリスト形式のマークダウンファイルをテンプレートとして用意しておきます。
生成した学習目標ファイルをチェックリストに基づいてレビューさせ、チェックリストを満たすまでAIに自己改善を繰り返させることで、品質を担保できます。
Web 検索の利用
- カスタムコマンドのプロンプト中に「Web 検索を行ってください」という指示を入れることで、Web 検索を行わせることができます。
- なお、Cursor の場合、事前に有効化しておく必要があります。
- 「Cursor Settings」→「Agents」→「Web Search Tool」
台本作成(/02-script)
-
/02-scriptコマンドを実行することで、前段で作成した学習目標を元に、章構成と各スライドの読み上げ台本を作成します。 - この読み上げ台本は、スライド生成のもととなるデータでもあるため、単なる「読み上げテキスト」ではなく、スライド作成の詳細な設計書としても機能します。
- このステップについても同様にチェックリストを用意しており、自己レビューを行います。
スクリプトの形式
スクリプトは、SSML(Speech Synthesis Markup Language)形式で記述させています。
SSML とは、テキスト読み上げ時の発音や間の取り方を細かく指定できるマークアップ言語です。
スクリプト例:
<speak>
こんにちは。今日は<prosody rate="slow">セキュリティ</prosody>について学びます。
<break time="500ms"/>
まず、基本的な概念から見ていきましょう。
</speak>
このように記述することで、「セキュリティ」という単語をゆっくり読ませたり、500 ミリ秒の間を入れたりできます。
スライド作成(/04-slide)
-
/04-slideコマンドを実行することで、前段で作成した読み上げ台本を元に、スライドを作成します。 - 出力するのは Marp 形式のマークダウンファイルです。
- このステップについても同様にチェックリストを用意しており、自己レビューを行います。
Marp とは
Marp は、Markdown でスライドを書けるフレームワークです。
- テキストベースなので、AI がコンテンツを生成しやすい
- Git で差分管理が容易
- プレビューは VSCode や Cursor の Marp for VS Code 拡張機能で可能

Marp ファイルにおける読み上げ台本の記載
Marp では、スライド中に HTML コメントを入れることで、Presenter Notes(スピーカーノート)として扱われます。
この形式に則り、読み上げ台本を HTML コメントとして記述します。
ここの記述したテキストを後のステップの音声合成に使用するため、SSML 形式を使用すると読み上げの調整もある程度可能です。
# セキュリティの基本
<!-- こんにちは。今日はセキュリティの基本について学びます。 -->
---
# セキュリティの3要素
- 機密性
- 完全性
- 可用性
<!-- <speak>
セキュリティには、主に3つの要素があります。
<break time="300ms"/>
それぞれ、機密性、完全性、可用性と呼ばれています。
</speak> -->
スライドレイアウトの定義
「タイトルレイアウト」「2 カラムの比較用レイアウト」「項目の列挙用レイアウト」など、あらかじめ、スライドのレイアウトとして使えるパターンを slide-layouts.md として定義しておき、AI にはそれを元にスライドを作成させます。
CSS のテーマファイルを用いて自由にスタイリングできるため、自由度高く表現できます。
どのときにどのレイアウトを使うかの判断がうまくいかないパターンも多いため、判断の方法も与えています。
事前定義したフローチャートを与えることで、どのレイアウトを使うかを判断させています。
アイコンの使用
- アイコンが必要な箇所は、あらかじめ定義されたアイコンファイルから適切なものを選択させます。
- アイコンファイルをまとめて入れてあるディレクトリを読み込ませ、ファイル名を元に適切なものを選択させます。
- つまりアイコンが適切に選ばれない場合は、ファイル名に十分な情報が含まれているか見直す必要があります。
画像(イラスト・図解)の生成
スライド内で図解やイラストが必要な場合、AI は以下のように画像生成プロンプトを alt テキストの位置に記述します。
このプロンプトを元に、後のステップでスクリプトを実行することで、Gemini API を使って画像生成します。
(なお、今回のケースでは動画生成のみを想定しており、alt テキストとしての目的を持たないためこのような使い方をしています。)
<!-- イメージイラストの場合 -->

<!-- SVG の図解の場合 -->

スライド(マークダウンファイル)の作成後、別のスクリプトファイルで画像生成を行います。
このスクリプトファイルでは、
- マークダウンファイル内の画像パスを探索
- 画像ファイルが存在しない場合、Gemini API を使って画像生成を行います。
イラストの場合、生成 AI のモデルとしては、Nano Banana Pro (gemini-3-pro-image-preview) を使用しています。
一方、図解の場合、Gemini 3.0 (gemini-3-pro-preview) を使用しています。
SVG の出力では、一発でミスのない図ができあがることは稀で、要素が範囲外にはみ出したり、文字同士が重なったりしてしまいます。
これをある程度避けるため、 SVG 生成時は修正させるフローを組んでいます。
- SVG を生成
- Puppeteer を用いて SVG を PNG に変換
- PNG を Gemini API に送信し、レイアウトに問題がないかチェックさせる
- 問題があれば修正させ、ループを繰り返す(最大 3 回)
イラストのテイストについては、ベースとなるイラストを与えることでテイストを統一させます。
それに関しては別記事で紹介しています。
アニメーション制御
スライド内の要素に data-appear という属性を付けることで、順次表示のアニメーションを制御できるようにしています。
例:
<div data-appear="1">1つ目の要素</div>
<div data-appear="2">2つ目の要素</div>
Remotion でレンダリングする際、これらの要素がフェードインアニメーションで順次表示されるようにしています。
動画生成(/05-video)
-
/05-videoコマンドを実行することで、前段で作成したスライドを元に、動画を生成します。
- Marp のマークダウンファイルを marp-core を用いてパース。コメント(読み上げ台本)を抽出
- Gemini API を使って音声生成
- 音声ファイル+スライドから Remotion を使って動画生成。音声ファイルの長さに合わせて各スライドの表示時間を自動調整し、data-appear 属性でアニメーション制御を行う。
音声生成の詳細
使用モデル
gemini-2.5-flash-preview-tts
スタイルプロンプトの例
音声合成でもカスタム指示を付け加えることで、テイストを調整し、一貫性をもたせることができます。
講師が受講生に語りかけるような明瞭なトーン
穏やかで安定した感情、過度に興奮しない
以下のテキストを"1度だけ"読み上げてください:
出力ファイルの命名規則
出力した音声ファイルのファイル名は、読み上げテキストを MD5 でハッシュ化して生成します。
これにより、同じテキストなら既存の音声ファイルを再利用でき、テキストに変更があった場合は必ず新しい音声ファイルが生成されます。
ノイズ除去とノーマライズ
Gemini で生成した音声ファイルは、ノイズが多く、また、生成ごとに音量が異なるケースがあるため、生成後に調整します。
ffmpeg を用いてノイズ除去とノーマライズをかけてからファイル出力しています。
# 中間ファイルを作成(ノイズ除去前のWAV)
INTERMEDIATE_FILE="$(mktemp).wav"
# PCMからWAVへ変換
echo "PCMからWAVへ変換中..." >&2
ffmpeg -f ${pcmformat} -ar ${fs} -ac ${ch} -i ${input} "${INTERMEDIATE_FILE}"
# ノイズ除去とノーマライズを適用
echo "ノイズ除去とノーマライズを適用中..." >&2
ffmpeg -i "${INTERMEDIATE_FILE}" -af "afftdn=nf=-60,loudnorm=I=-20" "${output}"
Remotion の動画生成
Remotion は、React コンポーネントを動画フレームとしてレンダリングします。
- Marp を用いてスライドのマークダウンファイルを HTML + CSS に変換し、React コンポーネント内で表示
- 音声ファイルを読み込み、各スライドの表示時間を計算
-
data-appear属性に基づいてフェードインアニメーションを制御
字幕生成(/06-subtitle)
- このステップはオプションです。
-
/06-subtitleコマンドを実行すると、動画から字幕ファイル(SRT 形式)を生成します。
以下のような手順で字幕ファイルを生成します。
- 動画内の音声を抽出
- MFA (Montreal Forced Aligner) で音声と台本テキストをアライメント
- TextGrid 形式のファイルが生成されるので、これを SRT 形式に変換(自前の Python スクリプトで変換)
- SRT 形式の字幕ファイルを動画に埋め込む
MFA とは
MFA は、音声とテキストを照合して、各単語の発話時刻を推定するツールです。
セットアップは以下の記事を参考にしました。
(普段 Python をあまり使わないこともあり、かなり手こずりました。新しく参加するメンバーに使ってもらうのは大変なので、できれば別の方法を検討したいです)
なぜ MFA を使うのか:
本来は、音声生成時に時刻情報も取得したかったのですが、Gemini 2.5 Flash TTS では <mark> タグ(時刻マーカー)がサポートされていません。
そのため、生成後に MFA でアライメントを行う方法を採用しています。
将来的に Gemini が SSML の <mark> タグをサポートしてくれたら、MFA を使わずに字幕生成できるようになるかもしれません。
字幕ファイルの活用
生成された SRT ファイルは、以下の 2 つの方法で動画に追加できます。
1. クローズドキャプション(後から表示 ON/OFF 可能):
ffmpeg -i slide.mp4 -i subtitle.srt \
-map 0:v -map "0:a?" -map 1:0 \
-c:v copy -c:a copy -c:s mov_text \
-metadata:s:s:0 language=jpn -metadata:s:s:0 title="日本語" \
-movflags +faststart \
output_softsubs.mp4
2. オープンキャプション(動画に焼き込み):
ffmpeg -i slide.mp4 -vf "subtitles=subtitle.srt:charenc=UTF-8" \
-c:a copy \
output_burned.mp4
注意点: 焼き込みの場合、1 行の文字数を調整しないと画面外にはみ出すことがあります。
評価システム(/99-eval)
- 出力したコンテンツの評価システムを実装しています。
- 品質を評価するほか、同じテーマについてすでに過去に人間の手で制作したコンテンツと比較して、どのような点で差があるかを評価します。
評価項目
評価システムは、以下の 3 つの観点で正解データと生成データを比較します。
- 網羅性: 正解データの重要な要素がどれだけカバーされているか
- 品質: チェックリスト項目の達成度
- 表現品質: 文体の一貫性、難解な表現の有無
評価理由と、改善が必要な箇所の詳細を出力させることで、プロンプトの改善に役立てることができます。
システム活用の現状
- AI のみで動画を生成できるシステムを構築することはできましたが、コンテンツとしての品質が不十分であり、そのまま使っているわけではありません。
- 現状は、人間のワークフローをベースに、その一部を置き換えるような形で採用しています。
- 特に図解については、AI が作成した方針を元に人間が作成するフローとしています。
- この記事では省略しましたが、Figma にエクスポートして Figma 上で編集→画像でインポートというフローを構築し、それを利用しています。
課題
スライドレイアウト使用ルールの厳密化
スライド作成時、レイアウトの使用ルールに従ってくれないことが多々あります。
(今回サンプルとして作成した Zenn の基礎解説スライドでも、要素を順番に表示する data-appear 属性の使い方が正しくなく、アニメーションが使用されませんでした)
意図した通りに作成してもらえるよう、プロンプトを見直す必要がありそうです。
修正の際、プロンプトを足しすぎると逆にコンテキストが肥大化し、指示に従ってくれないことがあるため、生成プロンプトだけに依存しない仕組みを作る必要があるかもしれません。例えば、以下のような方法を検討するのも一案です。
- 最初からスライドを生成させるのではなく、厳密な JSON 形式のデータを生成させ、それを元にスライドに変換する
- チェックリストを用いて、AI に自己レビューさせる
- 構文チェックを行うリンターのようなものを導入する
生成速度の向上
一連の動画(5 ファイル程度)を生成するために、2-3 時間程度時間がかかります。
現状、並列処理を全く行っていないためなので、改善の余地があります。
図解のクオリティ
弊社で扱うテーマは、AI や DX、セキュリティなど、専門的な内容が多いため、理解を助けるための図解が必要です。
複雑な内容を図解で表現する場合、まだまだ AI が生成した図解の品質が高いとは言えません。
Gemini 3 など、新たに登場したモデルの検証も進める必要があります。
音声生成のクオリティ
Gemini による音声生成では、たまに日本語の発音がおかしいことがあります。
(例:執筆(しっぴつ)が「しゅっぴつ」と発音されたり、絶妙に言い間違いのような発音になることがある)
参考リンク集
参考記事
Discussion